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
Powei Feng 7be9cdc7f8 Release Filament 1.50.2 2024-02-06 17:40:03 +00:00
Mathias Agopian af792e3d18 Resource allocator cache params can be set in Engine::Config
FIXES=[323386395]
2024-02-05 10:26:26 -08:00
Ben Doherty f93677548d Metal: schedule PresentDrawable for destruction on the main thread (#7535) 2024-02-01 10:45:58 -08:00
Mathias Agopian 1eff66e4ec wip: nullability attributes for backend 2024-02-01 10:36:42 -08:00
Mathias Agopian 34f8b9aa20 add nullability attributes to filament public APIs 2024-02-01 10:36:42 -08:00
Mathias Agopian 6d07443188 fixes builds fail when path to build directory contains spaces
fixes #7533
2024-02-01 10:35:06 -08:00
69 changed files with 1330 additions and 886 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"
+1 -1
View File
@@ -706,7 +706,7 @@ function(get_resgen_vars ARCHIVE_DIR ARCHIVE_NAME)
set(RESGEN_OUTPUTS "${OUTPUTS}" PARENT_SCOPE)
set(RESGEN_FLAGS -qx ${ARCHIVE_DIR} -p ${ARCHIVE_NAME} PARENT_SCOPE)
set(RESGEN_SOURCE "${ARCHIVE_DIR}/${ARCHIVE_NAME}${ASM_SUFFIX}.S" PARENT_SCOPE)
set(RESGEN_SOURCE_FLAGS "-I${ARCHIVE_DIR} ${ASM_ARCH_FLAG}" PARENT_SCOPE)
set(RESGEN_SOURCE_FLAGS "-I'${ARCHIVE_DIR}' ${ASM_ARCH_FLAG}" PARENT_SCOPE)
endif()
endfunction()
+3
View File
@@ -7,6 +7,9 @@ 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.3
## v1.50.2
@@ -484,7 +484,8 @@ 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 stereoscopicEyeCount) {
jlong jobSystemThreadCount, jlong stereoscopicEyeCount,
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
Engine::Config config = {
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
@@ -494,6 +495,8 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
.perFrameCommandsSizeMB = (uint32_t) perFrameCommandsSizeMB,
.jobSystemThreadCount = (uint32_t) jobSystemThreadCount,
.stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount,
.resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB,
.resourceAllocatorCacheMaxAge = (uint8_t) resourceAllocatorCacheMaxAge,
};
builder->config(&config);
}
@@ -211,7 +211,8 @@ public class Engine {
nSetBuilderConfig(mNativeBuilder, config.commandBufferSizeMB,
config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB,
config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB,
config.jobSystemThreadCount, config.stereoscopicEyeCount);
config.jobSystemThreadCount, config.stereoscopicEyeCount,
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge);
return this;
}
@@ -356,6 +357,18 @@ public class Engine {
* @see Engine#getMaxStereoscopicEyes
*/
public long stereoscopicEyeCount = 2;
/*
* Size in MiB of the frame graph texture cache. This should be adjusted based on the
* size of used render targets (typically the screen).
*/
public long resourceAllocatorCacheSizeMB = 64;
/*
* This value determines for how many frames are texture entries kept in the cache.
* The default value of 30 corresponds to about half a second at 60 fps.
*/
public long resourceAllocatorCacheMaxAge = 30;
}
private Engine(long nativeEngine, Config config) {
@@ -1227,7 +1240,8 @@ public class Engine {
private static native void nSetBuilderConfig(long nativeBuilder, long commandBufferSizeMB,
long perRenderPassArenaSizeMB, long driverHandleArenaSizeMB,
long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount,
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);
private static native long nBuilderBuild(long nativeBuilder);
+7 -1
View File
@@ -125,7 +125,13 @@ if [[ "${has_universal}" == "true" ]]; then
arch_output="${OUTPUT_PATH%.a}_${arch}.a"
arch_outputs+=("$arch_output")
combine_static_libs "$arch_output" $(find "$(pwd)/${archs_temp_dir}/${arch}" -iname '*.a')
archives=()
while IFS= read -r -d $'\0'; do
archives+=("$REPLY")
done < <(find "$(pwd)/${archs_temp_dir}/${arch}" -iname '*.a' -print0)
combine_static_libs "$arch_output" "$archives"
done
# Finally, combine the single-architecture archives into a universal binary.
+9 -5
View File
@@ -84,7 +84,7 @@ public:
*
* @return nullptr on failure, or a pointer to the newly created driver.
*/
virtual backend::Driver* createDriver(void* sharedContext,
virtual backend::Driver* UTILS_NULLABLE createDriver(void* UTILS_NULLABLE sharedContext,
const DriverConfig& driverConfig) noexcept = 0;
/**
@@ -102,7 +102,8 @@ public:
* cache.
*/
using InsertBlobFunc = utils::Invocable<
void(const void* key, size_t keySize, const void* value, size_t valueSize)>;
void(const void* UTILS_NONNULL key, size_t keySize,
const void* UTILS_NONNULL value, size_t valueSize)>;
/*
* RetrieveBlobFunc is an Invocable to an application-provided function that a
@@ -110,7 +111,8 @@ public:
* cache.
*/
using RetrieveBlobFunc = utils::Invocable<
size_t(const void* key, size_t keySize, void* value, size_t valueSize)>;
size_t(const void* UTILS_NONNULL key, size_t keySize,
void* UTILS_NONNULL value, size_t valueSize)>;
/**
* Sets the callback functions that the backend can use to interact with caching functionality
@@ -163,7 +165,8 @@ public:
* @param value pointer to the beginning of the value data that is to be inserted
* @param valueSize specifies the size in byte of the data pointed to by <value>
*/
void insertBlob(const void* key, size_t keySize, const void* value, size_t valueSize);
void insertBlob(const void* UTILS_NONNULL key, size_t keySize,
const void* UTILS_NONNULL value, size_t valueSize);
/**
* To retrieve the binary value associated with a given key from the cache, a
@@ -182,7 +185,8 @@ public:
* @return If the cache contains a value associated with the given key then the
* size of that binary value in bytes is returned. Otherwise 0 is returned.
*/
size_t retrieveBlob(const void* key, size_t keySize, void* value, size_t valueSize);
size_t retrieveBlob(const void* UTILS_NONNULL key, size_t keySize,
void* UTILS_NONNULL value, size_t valueSize);
private:
InsertBlobFunc mInsertBlob;
@@ -21,6 +21,8 @@
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <utils/compiler.h>
#include <stdint.h>
namespace filament::backend {
@@ -41,8 +43,8 @@ protected:
* Derived classes can use this to instantiate the default OpenGLDriver backend.
* This is typically called from your implementation of createDriver()
*/
static Driver* createDefaultDriver(OpenGLPlatform* platform,
void* sharedContext, const DriverConfig& driverConfig);
static Driver* UTILS_NULLABLE createDefaultDriver(OpenGLPlatform* UTILS_NONNULL platform,
void* UTILS_NULLABLE sharedContext, const DriverConfig& driverConfig);
~OpenGLPlatform() noexcept override;
@@ -69,7 +71,8 @@ public:
* @return The driver's SwapChain object.
*
*/
virtual SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) noexcept = 0;
virtual SwapChain* UTILS_NONNULL createSwapChain(
void* UTILS_NULLABLE nativeWindow, uint64_t flags) noexcept = 0;
/**
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
@@ -90,13 +93,14 @@ public:
* TODO: we need a more generic way of passing construction parameters
* A void* might be enough.
*/
virtual SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept = 0;
virtual SwapChain* UTILS_NULLABLE createSwapChain(
uint32_t width, uint32_t height, uint64_t flags) noexcept = 0;
/**
* Called by the driver to destroys the SwapChain
* @param swapChain SwapChain to be destroyed.
*/
virtual void destroySwapChain(SwapChain* swapChain) noexcept = 0;
virtual void destroySwapChain(SwapChain* UTILS_NONNULL swapChain) noexcept = 0;
/**
* Returns the set of buffers that must be preserved up to the call to commit().
@@ -109,7 +113,7 @@ public:
* @return buffer that must be preserved
* @see commit()
*/
virtual TargetBufferFlags getPreservedFlags(SwapChain* swapChain) noexcept;
virtual TargetBufferFlags getPreservedFlags(SwapChain* UTILS_NONNULL swapChain) noexcept;
/**
* Called by the driver to establish the default FBO. The default implementation returns 0.
@@ -123,14 +127,16 @@ public:
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
*/
virtual void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept = 0;
virtual void makeCurrent(
SwapChain* UTILS_NONNULL drawSwapChain,
SwapChain* UTILS_NONNULL readSwapChain) noexcept = 0;
/**
* Called by the driver once the current frame finishes drawing. Typically, this should present
* the drawSwapChain. This is for example where `eglMakeCurrent()` would be called.
* @param swapChain the SwapChain to present.
*/
virtual void commit(SwapChain* swapChain) noexcept = 0;
virtual void commit(SwapChain* UTILS_NONNULL swapChain) noexcept = 0;
/**
* Set the time the next committed buffer should be presented to the user at.
@@ -155,14 +161,14 @@ public:
*
* @return A Fence object. The default implementation returns nullptr.
*/
virtual Fence* createFence() noexcept;
virtual Fence* UTILS_NULLABLE createFence() noexcept;
/**
* Destroys a Fence object. The default implementation does nothing.
*
* @param fence Fence to destroy.
*/
virtual void destroyFence(Fence* fence) noexcept;
virtual void destroyFence(Fence* UTILS_NONNULL fence) noexcept;
/**
* Waits on a Fence.
@@ -172,7 +178,7 @@ public:
* @return Whether the fence signaled or timed out. See backend::FenceStatus.
* The default implementation always return backend::FenceStatus::ERROR.
*/
virtual backend::FenceStatus waitFence(Fence* fence, uint64_t timeout) noexcept;
virtual backend::FenceStatus waitFence(Fence* UTILS_NONNULL fence, uint64_t timeout) noexcept;
// --------------------------------------------------------------------------------------------
@@ -186,13 +192,13 @@ public:
* @param nativeStream The native stream, this parameter depends on the concrete implementation.
* @return A new Stream object.
*/
virtual Stream* createStream(void* nativeStream) noexcept;
virtual Stream* UTILS_NULLABLE createStream(void* UTILS_NULLABLE nativeStream) noexcept;
/**
* Destroys a Stream.
* @param stream Stream to destroy.
*/
virtual void destroyStream(Stream* stream) noexcept;
virtual void destroyStream(Stream* UTILS_NONNULL stream) noexcept;
/**
* The specified stream takes ownership of the texture (tname) object
@@ -202,20 +208,21 @@ public:
* @param stream Stream to take ownership of the texture
* @param tname GL texture id to "bind" to the Stream.
*/
virtual void attach(Stream* stream, intptr_t tname) noexcept;
virtual void attach(Stream* UTILS_NONNULL stream, intptr_t tname) noexcept;
/**
* Destroys the texture associated to the stream
* @param stream Stream to detach from its texture
*/
virtual void detach(Stream* stream) noexcept;
virtual void detach(Stream* UTILS_NONNULL stream) noexcept;
/**
* Updates the content of the texture attached to the stream.
* @param stream Stream to update
* @param timestamp Output parameter: Timestamp of the image bound to the texture.
*/
virtual void updateTexImage(Stream* stream, int64_t* timestamp) noexcept;
virtual void updateTexImage(Stream* UTILS_NONNULL stream,
int64_t* UTILS_NONNULL timestamp) noexcept;
// --------------------------------------------------------------------------------------------
@@ -228,13 +235,13 @@ public:
* implementation could just return { 0, GL_TEXTURE_2D } at this point. The actual
* values can be delayed until setExternalImage.
*/
virtual ExternalTexture *createExternalImageTexture() noexcept;
virtual ExternalTexture* UTILS_NULLABLE createExternalImageTexture() noexcept;
/**
* Destroys an external texture handle and associated data.
* @param texture a pointer to the handle to destroy.
*/
virtual void destroyExternalImage(ExternalTexture* texture) noexcept;
virtual void destroyExternalImage(ExternalTexture* UTILS_NONNULL texture) noexcept;
// called on the application thread to allow Filament to take ownership of the image
@@ -247,7 +254,7 @@ public:
* @param externalImage A token representing the platform's external image.
* @see destroyExternalImage
*/
virtual void retainExternalImage(void* externalImage) noexcept;
virtual void retainExternalImage(void* UTILS_NONNULL externalImage) noexcept;
/**
* Called to bind the platform-specific externalImage to an ExternalTexture.
@@ -261,7 +268,8 @@ public:
* @param texture an in/out pointer to ExternalTexture, id and target can be updated if necessary.
* @return true on success, false on error.
*/
virtual bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept;
virtual bool setExternalImage(void* UTILS_NONNULL externalImage,
ExternalTexture* UTILS_NONNULL texture) noexcept;
/**
* The method allows platforms to convert a user-supplied external image object into a new type
@@ -17,7 +17,10 @@
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_CIRCULARBUFFER_H
#define TNT_FILAMENT_BACKEND_PRIVATE_CIRCULARBUFFER_H
#include <utils/debug.h>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
@@ -37,28 +40,36 @@ public:
~CircularBuffer() noexcept;
// allocates 'size' bytes in the circular buffer and returns a pointer to the memory
// return the current head and moves it forward by size bytes
inline void* allocate(size_t size) noexcept {
static size_t getBlockSize() noexcept { return sPageSize; }
// Total size of circular buffer. This is a constant.
size_t size() const noexcept { return mSize; }
// Allocates `s` bytes in the circular buffer and returns a pointer to the memory. All
// allocations must not exceed size() bytes.
inline void* allocate(size_t s) noexcept {
// We can never allocate more that size().
assert_invariant(getUsed() + s <= size());
char* const cur = static_cast<char*>(mHead);
mHead = cur + size;
mHead = cur + s;
return cur;
}
// Total size of circular buffer
size_t size() const noexcept { return mSize; }
// returns true if the buffer is empty (e.g. after calling flush)
// Returns true if the buffer is empty, i.e.: no allocations were made since
// calling getBuffer();
bool empty() const noexcept { return mTail == mHead; }
void* getHead() const noexcept { return mHead; }
// Returns the size used since the last call to getBuffer()
size_t getUsed() const noexcept { return intptr_t(mHead) - intptr_t(mTail); }
void* getTail() const noexcept { return mTail; }
// call at least once every getRequiredSize() bytes allocated from the buffer
void circularize() noexcept;
static size_t getBlockSize() noexcept { return sPageSize; }
// Retrieves the current allocated range and frees it. It is the responsibility of the caller
// to make sure the returned range is no longer in use by the time allocate() allocates
// (size() - getUsed()) bytes.
struct Range {
void* tail;
void* head;
};
Range getBuffer() noexcept;
private:
void* alloc(size_t size) noexcept;
@@ -66,10 +77,10 @@ private:
// pointer to the beginning of the circular buffer (constant)
void* mData = nullptr;
int mUsesAshmem = -1;
int mAshmemFd = -1;
// size of the circular buffer (constant)
size_t mSize = 0;
size_t const mSize;
// pointer to the beginning of recorded data
void* mTail = nullptr;
@@ -33,7 +33,7 @@ namespace filament::backend {
* A producer-consumer command queue that uses a CircularBuffer as main storage
*/
class CommandBufferQueue {
struct Slice {
struct Range {
void* begin;
void* end;
};
@@ -46,7 +46,7 @@ class CommandBufferQueue {
mutable utils::Mutex mLock;
mutable utils::Condition mCondition;
mutable std::vector<Slice> mCommandBuffersToExecute;
mutable std::vector<Range> mCommandBuffersToExecute;
size_t mFreeSpace = 0;
size_t mHighWatermark = 0;
uint32_t mExitRequested = 0;
@@ -58,17 +58,20 @@ public:
CommandBufferQueue(size_t requiredSize, size_t bufferSize);
~CommandBufferQueue();
CircularBuffer& getCircularBuffer() { return mCircularBuffer; }
CircularBuffer& getCircularBuffer() noexcept { return mCircularBuffer; }
CircularBuffer const& getCircularBuffer() const noexcept { return mCircularBuffer; }
size_t getCapacity() const noexcept { return mRequiredSize; }
size_t getHighWatermark() const noexcept { return mHighWatermark; }
// wait for commands to be available and returns an array containing these commands
std::vector<Slice> waitForCommands() const;
std::vector<Range> waitForCommands() const;
// return the memory used by this command buffer to the circular buffer
// WARNING: releaseBuffer() must be called in sequence of the Slices returned by
// waitForCommands()
void releaseBuffer(Slice const& buffer);
void releaseBuffer(Range const& buffer);
// all commands buffers (Slices) written to this point are returned by waitForCommand(). This
// call blocks until the CircularBuffer has at least mRequiredSize bytes available.
@@ -213,6 +213,8 @@ public:
CommandStream(CommandStream const& rhs) noexcept = delete;
CommandStream& operator=(CommandStream const& rhs) noexcept = delete;
CircularBuffer const& getCircularBuffer() const noexcept { return mCurrentBuffer; }
public:
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
inline void methodName(paramsDecl) { \
@@ -231,7 +231,7 @@ private:
explicit Allocator(const utils::AreaPolicy::HeapArea& area);
// this is in fact always called with a constexpr size argument
[[nodiscard]] inline void* alloc(size_t size, size_t, size_t extra) 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, 16, extra);
else if (size <= mPool1.getSize()) p = mPool1.alloc(size, 16, extra);
+45 -26
View File
@@ -16,6 +16,14 @@
#include "private/backend/CircularBuffer.h"
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/architecture.h>
#include <utils/ashmem.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#if !defined(WIN32) && !defined(__EMSCRIPTEN__) && !defined(IOS)
# include <sys/mman.h>
# include <unistd.h>
@@ -24,23 +32,20 @@
# define HAS_MMAP 0
#endif
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <utils/architecture.h>
#include <utils/ashmem.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/Panic.h>
using namespace utils;
namespace filament::backend {
size_t CircularBuffer::sPageSize = arch::getPageSize();
CircularBuffer::CircularBuffer(size_t size) {
CircularBuffer::CircularBuffer(size_t size)
: mSize(size) {
mData = alloc(size);
mSize = size;
mTail = mData;
mHead = mData;
}
@@ -85,7 +90,7 @@ void* CircularBuffer::alloc(size_t size) noexcept {
MAP_PRIVATE, fd, (off_t)size);
if (vaddr_guard != MAP_FAILED && (vaddr_guard == (char*)vaddr_shadow + size)) {
// woo-hoo success!
mUsesAshmem = fd;
mAshmemFd = fd;
data = vaddr;
}
}
@@ -93,7 +98,7 @@ void* CircularBuffer::alloc(size_t size) noexcept {
}
}
if (UTILS_UNLIKELY(mUsesAshmem < 0)) {
if (UTILS_UNLIKELY(mAshmemFd < 0)) {
// ashmem failed
if (vaddr_guard != MAP_FAILED) {
munmap(vaddr_guard, size);
@@ -137,9 +142,9 @@ void CircularBuffer::dealloc() noexcept {
if (mData) {
size_t const BLOCK_SIZE = getBlockSize();
munmap(mData, mSize * 2 + BLOCK_SIZE);
if (mUsesAshmem >= 0) {
close(mUsesAshmem);
mUsesAshmem = -1;
if (mAshmemFd >= 0) {
close(mAshmemFd);
mAshmemFd = -1;
}
}
#else
@@ -149,23 +154,37 @@ void CircularBuffer::dealloc() noexcept {
}
void CircularBuffer::circularize() noexcept {
if (mUsesAshmem > 0) {
intptr_t const overflow = intptr_t(mHead) - (intptr_t(mData) + ssize_t(mSize));
if (overflow >= 0) {
assert_invariant(size_t(overflow) <= mSize);
mHead = (void *) (intptr_t(mData) + overflow);
#ifndef NDEBUG
memset(mData, 0xA5, size_t(overflow));
#endif
}
} else {
// Only circularize if mHead if in the second buffer.
if (intptr_t(mHead) - intptr_t(mData) > ssize_t(mSize)) {
CircularBuffer::Range CircularBuffer::getBuffer() noexcept {
Range const range{ .tail = mTail, .head = mHead };
char* const pData = static_cast<char*>(mData);
char const* const pEnd = pData + mSize;
char const* const pHead = static_cast<char const*>(mHead);
if (UTILS_UNLIKELY(pHead >= pEnd)) {
size_t const overflow = pHead - pEnd;
if (UTILS_LIKELY(mAshmemFd > 0)) {
assert_invariant(overflow <= mSize);
mHead = static_cast<void*>(pData + overflow);
// Data Tail End Head [virtual]
// v v v v
// +-------------:----+-----:--------------+
// | : | : |
// +-----:------------+--------------------+
// Head |<------ copy ------>| [physical]
} else {
// Data Tail End Head
// v v v v
// +-------------:----+-----:--------------+
// | : | : |
// +-----|------------+-----|--------------+
// |<---------------->|
// sliding window
mHead = mData;
}
}
mTail = mHead;
return range;
}
} // namespace filament::backend
+38 -24
View File
@@ -15,14 +15,25 @@
*/
#include "private/backend/CommandBufferQueue.h"
#include "private/backend/CircularBuffer.h"
#include "private/backend/CommandStream.h"
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/Systrace.h>
#include <utils/Mutex.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <utils/Systrace.h>
#include <utils/debug.h>
#include "private/backend/BackendUtils.h"
#include "private/backend/CommandStream.h"
#include <algorithm>
#include <mutex>
#include <iterator>
#include <utility>
#include <vector>
#include <stddef.h>
#include <stdint.h>
using namespace utils;
@@ -65,50 +76,53 @@ void CommandBufferQueue::flush() noexcept {
// always guaranteed to have enough space for the NoopCommand
new(circularBuffer.allocate(sizeof(NoopCommand))) NoopCommand(nullptr);
// end of this slice
void* const head = circularBuffer.getHead();
const size_t requiredSize = mRequiredSize;
// beginning of this slice
void* const tail = circularBuffer.getTail();
// get the current buffer
auto const [begin, end] = circularBuffer.getBuffer();
// size of this slice
uint32_t const used = uint32_t(intptr_t(head) - intptr_t(tail));
assert_invariant(circularBuffer.empty());
circularBuffer.circularize();
// size of the current buffer
size_t const used = std::distance(
static_cast<char const*>(begin), static_cast<char const*>(end));
std::unique_lock<utils::Mutex> lock(mLock);
mCommandBuffersToExecute.push_back({ tail, head });
mCommandBuffersToExecute.push_back({ begin, end });
mCondition.notify_one();
// circular buffer is too small, we corrupted the stream
ASSERT_POSTCONDITION(used <= mFreeSpace,
"Backend CommandStream overflow. Commands are corrupted and unrecoverable.\n"
"Please increase minCommandBufferSizeMB inside the Config passed to Engine::create.\n"
"Space used at this time: %u bytes",
(unsigned)used);
"Space used at this time: %u bytes, overflow: %u bytes",
(unsigned)used, unsigned(used - mFreeSpace));
// wait until there is enough space in the buffer
mFreeSpace -= used;
const size_t requiredSize = mRequiredSize;
if (UTILS_UNLIKELY(mFreeSpace < requiredSize)) {
#ifndef NDEBUG
size_t totalUsed = circularBuffer.size() - mFreeSpace;
mHighWatermark = std::max(mHighWatermark, totalUsed);
if (UTILS_UNLIKELY(totalUsed > requiredSize)) {
slog.d << "CommandStream used too much space: " << totalUsed
<< ", out of " << requiredSize << " (will block)" << io::endl;
}
size_t const totalUsed = circularBuffer.size() - mFreeSpace;
slog.d << "CommandStream used too much space (will block): "
<< "needed space " << requiredSize << " out of " << mFreeSpace
<< ", totalUsed=" << totalUsed << ", current=" << used
<< ", queue size=" << mCommandBuffersToExecute.size() << " buffers"
<< io::endl;
mHighWatermark = std::max(mHighWatermark, totalUsed);
#endif
mCondition.notify_one();
if (UTILS_LIKELY(mFreeSpace < requiredSize)) {
SYSTRACE_NAME("waiting: CircularBuffer::flush()");
mCondition.wait(lock, [this, requiredSize]() -> bool {
// TODO: on macOS, we need to call pumpEvents from time to time
return mFreeSpace >= requiredSize;
});
}
}
std::vector<CommandBufferQueue::Slice> CommandBufferQueue::waitForCommands() const {
std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() const {
if (!UTILS_HAS_THREADING) {
return std::move(mCommandBuffersToExecute);
}
@@ -123,7 +137,7 @@ std::vector<CommandBufferQueue::Slice> CommandBufferQueue::waitForCommands() con
return std::move(mCommandBuffersToExecute);
}
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Slice const& buffer) {
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Range const& buffer) {
std::lock_guard<utils::Mutex> const lock(mLock);
mFreeSpace += uintptr_t(buffer.end) - uintptr_t(buffer.begin);
mCondition.notify_one();
+2 -3
View File
@@ -84,8 +84,7 @@ public:
*
* @param engine Reference to the filament::Engine to associate this BufferObject with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
* @return pointer to the newly created object
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
@@ -93,7 +92,7 @@ public:
*
* @see IndexBuffer::setBuffer
*/
BufferObject* build(Engine& engine);
BufferObject* UTILS_NONNULL build(Engine& engine);
private:
friend class FBufferObject;
};
+1 -1
View File
@@ -306,7 +306,7 @@ public:
* @see setCustomProjection
* @see Engine::Config::stereoscopicEyeCount
*/
void setCustomEyeProjection(math::mat4 const* projection, size_t count,
void setCustomEyeProjection(math::mat4 const* UTILS_NONNULL projection, size_t count,
math::mat4 const& projectionForCulling, double near, double far);
/** Sets an additional matrix that scales the projection matrix.
+3 -4
View File
@@ -203,7 +203,7 @@ public:
*
* @return This Builder, for chaining calls
*/
Builder& toneMapper(const ToneMapper* toneMapper) noexcept;
Builder& toneMapper(ToneMapper const* UTILS_NULLABLE toneMapper) noexcept;
/**
* Selects the tone mapping operator to apply to the HDR color buffer as the last
@@ -473,10 +473,9 @@ public:
*
* @param engine Reference to the filament::Engine to associate this ColorGrading with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
* @return pointer to the newly created object.
*/
ColorGrading* build(Engine& engine);
ColorGrading* UTILS_NONNULL build(Engine& engine);
private:
friend class FColorGrading;
+23 -36
View File
@@ -39,27 +39,12 @@ namespace filament {
class UTILS_PUBLIC DebugRegistry : public FilamentAPI {
public:
/**
* Type of a property
*/
enum Type {
BOOL, INT, FLOAT, FLOAT2, FLOAT3, FLOAT4
};
/**
* Information about a property
*/
struct Property {
const char* name; //!< property name
Type type; //!< property type
};
/**
* Queries whether a property exists
* @param name The name of the property to query
* @return true if the property exists, false otherwise
*/
bool hasProperty(const char* name) const noexcept;
bool hasProperty(const char* UTILS_NONNULL name) const noexcept;
/**
* Queries the address of a property's data from its name
@@ -67,28 +52,30 @@ public:
* @return Address of the data of the \p name property
* @{
*/
void* getPropertyAddress(const char* name);
void* UTILS_NULLABLE getPropertyAddress(const char* UTILS_NONNULL name);
void const* getPropertyAddress(const char* name) const noexcept;
void const* UTILS_NULLABLE getPropertyAddress(const char* UTILS_NONNULL name) const noexcept;
template<typename T>
inline T* getPropertyAddress(const char* name) {
inline T* UTILS_NULLABLE getPropertyAddress(const char* UTILS_NONNULL name) {
return static_cast<T*>(getPropertyAddress(name));
}
template<typename T>
inline T const* getPropertyAddress(const char* name) const noexcept {
inline T const* UTILS_NULLABLE getPropertyAddress(const char* UTILS_NONNULL name) const noexcept {
return static_cast<T*>(getPropertyAddress(name));
}
template<typename T>
inline bool getPropertyAddress(const char* name, T** p) {
inline bool getPropertyAddress(const char* UTILS_NONNULL name,
T* UTILS_NULLABLE* UTILS_NONNULL p) {
*p = getPropertyAddress<T>(name);
return *p != nullptr;
}
template<typename T>
inline bool getPropertyAddress(const char* name, T* const* p) const noexcept {
inline bool getPropertyAddress(const char* UTILS_NONNULL name,
T* const UTILS_NULLABLE* UTILS_NONNULL p) const noexcept {
*p = getPropertyAddress<T>(name);
return *p != nullptr;
}
@@ -101,12 +88,12 @@ public:
* @return true if the operation was successful, false otherwise.
* @{
*/
bool setProperty(const char* name, bool v) noexcept;
bool setProperty(const char* name, int v) noexcept;
bool setProperty(const char* name, float v) noexcept;
bool setProperty(const char* name, math::float2 v) noexcept;
bool setProperty(const char* name, math::float3 v) noexcept;
bool setProperty(const char* name, math::float4 v) noexcept;
bool setProperty(const char* UTILS_NONNULL name, bool v) noexcept;
bool setProperty(const char* UTILS_NONNULL name, int v) noexcept;
bool setProperty(const char* UTILS_NONNULL name, float v) noexcept;
bool setProperty(const char* UTILS_NONNULL name, math::float2 v) noexcept;
bool setProperty(const char* UTILS_NONNULL name, math::float3 v) noexcept;
bool setProperty(const char* UTILS_NONNULL name, math::float4 v) noexcept;
/** @}*/
/**
@@ -116,20 +103,20 @@ public:
* @return true if the call was successful and \p v was updated
* @{
*/
bool getProperty(const char* name, bool* v) const noexcept;
bool getProperty(const char* name, int* v) const noexcept;
bool getProperty(const char* name, float* v) const noexcept;
bool getProperty(const char* name, math::float2* v) const noexcept;
bool getProperty(const char* name, math::float3* v) const noexcept;
bool getProperty(const char* name, math::float4* v) const noexcept;
bool getProperty(const char* UTILS_NONNULL name, bool* UTILS_NONNULL v) const noexcept;
bool getProperty(const char* UTILS_NONNULL name, int* UTILS_NONNULL v) const noexcept;
bool getProperty(const char* UTILS_NONNULL name, float* UTILS_NONNULL v) const noexcept;
bool getProperty(const char* UTILS_NONNULL name, math::float2* UTILS_NONNULL v) const noexcept;
bool getProperty(const char* UTILS_NONNULL name, math::float3* UTILS_NONNULL v) const noexcept;
bool getProperty(const char* UTILS_NONNULL name, math::float4* UTILS_NONNULL v) const noexcept;
/** @}*/
struct DataSource {
void const* data;
void const* UTILS_NULLABLE data;
size_t count;
};
DataSource getDataSource(const char* name) const noexcept;
DataSource getDataSource(const char* UTILS_NONNULL name) const noexcept;
struct FrameHistory {
using duration_ms = float;
+87 -72
View File
@@ -305,11 +305,23 @@ public:
* @see Engine::getMaxStereoscopicEyes
*/
uint8_t stereoscopicEyeCount = 2;
/*
* Size in MiB of the frame graph texture cache. This should be adjusted based on the
* size of used render targets (typically the screen).
*/
uint32_t resourceAllocatorCacheSizeMB = 64;
/*
* This value determines for how many frames are texture entries kept in the cache.
* The default value of 30 corresponds to about half a second at 60 fps.
*/
uint32_t resourceAllocatorCacheMaxAge = 30;
};
#if UTILS_HAS_THREADING
using CreateCallback = void(void* user, void* token);
using CreateCallback = void(void* UTILS_NULLABLE user, void* UTILS_NONNULL token);
#endif
/**
@@ -348,7 +360,7 @@ public:
*
* @return A reference to this Builder for chaining calls.
*/
Builder& platform(Platform* platform) noexcept;
Builder& platform(Platform* UTILS_NULLABLE platform) noexcept;
/**
* @param config A pointer to optional parameters to specify memory size
@@ -356,7 +368,7 @@ public:
*
* @return A reference to this Builder for chaining calls.
*/
Builder& config(const Config* config) noexcept;
Builder& config(const Config* UTILS_NULLABLE config) noexcept;
/**
* @param sharedContext A platform-dependant context used as a shared context
@@ -364,7 +376,7 @@ public:
*
* @return A reference to this Builder for chaining calls.
*/
Builder& sharedContext(void* sharedContext) noexcept;
Builder& sharedContext(void* UTILS_NULLABLE sharedContext) noexcept;
/**
* @param featureLevel The feature level at which initialize Filament.
@@ -379,7 +391,7 @@ public:
* @param callback Callback called once the engine is initialized and it is safe to
* call Engine::getEngine().
*/
void build(utils::Invocable<void(void* token)>&& callback) const;
void build(utils::Invocable<void(void* UTILS_NONNULL token)>&& callback) const;
#endif
/**
@@ -394,16 +406,17 @@ public:
* allocate the command buffer. If exceptions are disabled, this condition if
* fatal and this function will abort.
*/
Engine* build() const;
Engine* UTILS_NULLABLE build() const;
};
/**
* Backward compatibility helper to create an Engine.
* @see Builder
*/
static inline Engine* create(Backend backend = Backend::DEFAULT,
Platform* platform = nullptr, void* sharedContext = nullptr,
const Config* config = nullptr) {
static inline Engine* UTILS_NULLABLE create(Backend backend = Backend::DEFAULT,
Platform* UTILS_NULLABLE platform = nullptr,
void* UTILS_NULLABLE sharedContext = nullptr,
const Config* UTILS_NULLABLE config = nullptr) {
return Engine::Builder()
.backend(backend)
.platform(platform)
@@ -418,16 +431,18 @@ public:
* Backward compatibility helper to create an Engine asynchronously.
* @see Builder
*/
static inline void createAsync(CreateCallback callback, void* user,
static inline void createAsync(CreateCallback callback,
void* UTILS_NULLABLE user,
Backend backend = Backend::DEFAULT,
Platform* platform = nullptr, void* sharedContext = nullptr,
const Config* config = nullptr) {
Platform* UTILS_NULLABLE platform = nullptr,
void* UTILS_NULLABLE sharedContext = nullptr,
const Config* UTILS_NULLABLE config = nullptr) {
Engine::Builder()
.backend(backend)
.platform(platform)
.sharedContext(sharedContext)
.config(config)
.build([callback, user](void* token) {
.build([callback, user](void* UTILS_NONNULL token) {
callback(user, token);
});
}
@@ -444,7 +459,7 @@ public:
* allocate the command buffer. If exceptions are disabled, this condition if fatal and
* this function will abort.
*/
static Engine* getEngine(void* token);
static Engine* UTILS_NULLABLE getEngine(void* UTILS_NONNULL token);
#endif
@@ -474,7 +489,7 @@ public:
* \remark
* This method is thread-safe.
*/
static void destroy(Engine** engine);
static void destroy(Engine* UTILS_NULLABLE* UTILS_NULLABLE engine);
/**
* Destroy the Engine instance and all associated resources.
@@ -501,7 +516,7 @@ public:
* \remark
* This method is thread-safe.
*/
static void destroy(Engine* engine);
static void destroy(Engine* UTILS_NULLABLE engine);
/**
* Query the feature level supported by the selected backend.
@@ -649,11 +664,11 @@ public:
* `ANativeWindow*`.
* @param flags One or more configuration flags as defined in `SwapChain`.
*
* @return A pointer to the newly created SwapChain or nullptr if it couldn't be created.
* @return A pointer to the newly created SwapChain.
*
* @see Renderer.beginFrame()
*/
SwapChain* createSwapChain(void* nativeWindow, uint64_t flags = 0) noexcept;
SwapChain* UTILS_NONNULL createSwapChain(void* UTILS_NULLABLE nativeWindow, uint64_t flags = 0) noexcept;
/**
@@ -663,42 +678,42 @@ public:
* @param height Height of the drawing buffer in pixels.
* @param flags One or more configuration flags as defined in `SwapChain`.
*
* @return A pointer to the newly created SwapChain or nullptr if it couldn't be created.
* @return A pointer to the newly created SwapChain.
*
* @see Renderer.beginFrame()
*/
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags = 0) noexcept;
SwapChain* UTILS_NONNULL createSwapChain(uint32_t width, uint32_t height, uint64_t flags = 0) noexcept;
/**
* Creates a renderer associated to this engine.
*
* A Renderer is intended to map to a *window* on screen.
*
* @return A pointer to the newly created Renderer or nullptr if it couldn't be created.
* @return A pointer to the newly created Renderer.
*/
Renderer* createRenderer() noexcept;
Renderer* UTILS_NONNULL createRenderer() noexcept;
/**
* Creates a View.
*
* @return A pointer to the newly created View or nullptr if it couldn't be created.
* @return A pointer to the newly created View.
*/
View* createView() noexcept;
View* UTILS_NONNULL createView() noexcept;
/**
* Creates a Scene.
*
* @return A pointer to the newly created Scene or nullptr if it couldn't be created.
* @return A pointer to the newly created Scene.
*/
Scene* createScene() noexcept;
Scene* UTILS_NONNULL createScene() noexcept;
/**
* Creates a Camera component.
*
* @param entity Entity to add the camera component to.
* @return A pointer to the newly created Camera or nullptr if it couldn't be created.
* @return A pointer to the newly created Camera.
*/
Camera* createCamera(utils::Entity entity) noexcept;
Camera* UTILS_NONNULL createCamera(utils::Entity entity) noexcept;
/**
* Returns the Camera component of the given entity.
@@ -708,7 +723,7 @@ public:
* have a Camera component. The pointer is valid until destroyCameraComponent()
* is called or the entity itself is destroyed.
*/
Camera* getCameraComponent(utils::Entity entity) noexcept;
Camera* UTILS_NULLABLE getCameraComponent(utils::Entity entity) noexcept;
/**
* Destroys the Camera component associated with the given entity.
@@ -720,17 +735,17 @@ public:
/**
* Creates a Fence.
*
* @return A pointer to the newly created Fence or nullptr if it couldn't be created.
* @return A pointer to the newly created Fence.
*/
Fence* createFence() noexcept;
Fence* UTILS_NONNULL createFence() noexcept;
bool destroy(const BufferObject* p); //!< Destroys a BufferObject object.
bool destroy(const VertexBuffer* p); //!< Destroys an VertexBuffer object.
bool destroy(const Fence* p); //!< Destroys a Fence object.
bool destroy(const IndexBuffer* p); //!< Destroys an IndexBuffer object.
bool destroy(const SkinningBuffer* p); //!< Destroys a SkinningBuffer object.
bool destroy(const MorphTargetBuffer* p); //!< Destroys a MorphTargetBuffer object.
bool destroy(const IndirectLight* p); //!< Destroys an IndirectLight object.
bool destroy(const BufferObject* UTILS_NULLABLE p); //!< Destroys a BufferObject object.
bool destroy(const VertexBuffer* UTILS_NULLABLE p); //!< Destroys an VertexBuffer object.
bool destroy(const Fence* UTILS_NULLABLE p); //!< Destroys a Fence object.
bool destroy(const IndexBuffer* UTILS_NULLABLE p); //!< Destroys an IndexBuffer object.
bool destroy(const SkinningBuffer* UTILS_NULLABLE p); //!< Destroys a SkinningBuffer object.
bool destroy(const MorphTargetBuffer* UTILS_NULLABLE p); //!< Destroys a MorphTargetBuffer object.
bool destroy(const IndirectLight* UTILS_NULLABLE p); //!< Destroys an IndirectLight object.
/**
* Destroys a Material object
@@ -740,38 +755,38 @@ public:
* @exception utils::PreConditionPanic is thrown if some MaterialInstances remain.
* no-op if exceptions are disabled and some MaterialInstances remain.
*/
bool destroy(const Material* p);
bool destroy(const MaterialInstance* p); //!< Destroys a MaterialInstance object.
bool destroy(const Renderer* p); //!< Destroys a Renderer object.
bool destroy(const Scene* p); //!< Destroys a Scene object.
bool destroy(const Skybox* p); //!< Destroys a SkyBox object.
bool destroy(const ColorGrading* p); //!< Destroys a ColorGrading object.
bool destroy(const SwapChain* p); //!< Destroys a SwapChain object.
bool destroy(const Stream* p); //!< Destroys a Stream object.
bool destroy(const Texture* p); //!< Destroys a Texture object.
bool destroy(const RenderTarget* p); //!< Destroys a RenderTarget object.
bool destroy(const View* p); //!< Destroys a View object.
bool destroy(const InstanceBuffer* p); //!< Destroys an InstanceBuffer object.
void destroy(utils::Entity e); //!< Destroys all filament-known components from this entity
bool destroy(const Material* UTILS_NULLABLE p);
bool destroy(const MaterialInstance* UTILS_NULLABLE p); //!< Destroys a MaterialInstance object.
bool destroy(const Renderer* UTILS_NULLABLE p); //!< Destroys a Renderer object.
bool destroy(const Scene* UTILS_NULLABLE p); //!< Destroys a Scene object.
bool destroy(const Skybox* UTILS_NULLABLE p); //!< Destroys a SkyBox object.
bool destroy(const ColorGrading* UTILS_NULLABLE p); //!< Destroys a ColorGrading object.
bool destroy(const SwapChain* UTILS_NULLABLE p); //!< Destroys a SwapChain object.
bool destroy(const Stream* UTILS_NULLABLE p); //!< Destroys a Stream object.
bool destroy(const Texture* UTILS_NULLABLE p); //!< Destroys a Texture object.
bool destroy(const RenderTarget* UTILS_NULLABLE p); //!< Destroys a RenderTarget object.
bool destroy(const View* UTILS_NULLABLE p); //!< Destroys a View object.
bool destroy(const InstanceBuffer* UTILS_NULLABLE p); //!< Destroys an InstanceBuffer object.
void destroy(utils::Entity e); //!< Destroys all filament-known components from this entity
bool isValid(const BufferObject* p); //!< Tells whether a BufferObject object is valid
bool isValid(const VertexBuffer* p); //!< Tells whether an VertexBuffer object is valid
bool isValid(const Fence* p); //!< Tells whether a Fence object is valid
bool isValid(const IndexBuffer* p); //!< Tells whether an IndexBuffer object is valid
bool isValid(const SkinningBuffer* p); //!< Tells whether a SkinningBuffer object is valid
bool isValid(const MorphTargetBuffer* p); //!< Tells whether a MorphTargetBuffer object is valid
bool isValid(const IndirectLight* p); //!< Tells whether an IndirectLight object is valid
bool isValid(const Material* p); //!< Tells whether an IndirectLight object is valid
bool isValid(const Renderer* p); //!< Tells whether a Renderer object is valid
bool isValid(const Scene* p); //!< Tells whether a Scene object is valid
bool isValid(const Skybox* p); //!< Tells whether a SkyBox object is valid
bool isValid(const ColorGrading* p); //!< Tells whether a ColorGrading object is valid
bool isValid(const SwapChain* p); //!< Tells whether a SwapChain object is valid
bool isValid(const Stream* p); //!< Tells whether a Stream object is valid
bool isValid(const Texture* p); //!< Tells whether a Texture object is valid
bool isValid(const RenderTarget* p); //!< Tells whether a RenderTarget object is valid
bool isValid(const View* p); //!< Tells whether a View object is valid
bool isValid(const InstanceBuffer* p); //!< Tells whether an InstanceBuffer object is valid
bool isValid(const BufferObject* UTILS_NULLABLE p); //!< Tells whether a BufferObject object is valid
bool isValid(const VertexBuffer* UTILS_NULLABLE p); //!< Tells whether an VertexBuffer object is valid
bool isValid(const Fence* UTILS_NULLABLE p); //!< Tells whether a Fence object is valid
bool isValid(const IndexBuffer* UTILS_NULLABLE p); //!< Tells whether an IndexBuffer object is valid
bool isValid(const SkinningBuffer* UTILS_NULLABLE p); //!< Tells whether a SkinningBuffer object is valid
bool isValid(const MorphTargetBuffer* UTILS_NULLABLE p); //!< Tells whether a MorphTargetBuffer object is valid
bool isValid(const IndirectLight* UTILS_NULLABLE p); //!< Tells whether an IndirectLight object is valid
bool isValid(const Material* UTILS_NULLABLE p); //!< Tells whether an IndirectLight object is valid
bool isValid(const Renderer* UTILS_NULLABLE p); //!< Tells whether a Renderer object is valid
bool isValid(const Scene* UTILS_NULLABLE p); //!< Tells whether a Scene object is valid
bool isValid(const Skybox* UTILS_NULLABLE p); //!< Tells whether a SkyBox object is valid
bool isValid(const ColorGrading* UTILS_NULLABLE p); //!< Tells whether a ColorGrading object is valid
bool isValid(const SwapChain* UTILS_NULLABLE p); //!< Tells whether a SwapChain object is valid
bool isValid(const Stream* UTILS_NULLABLE p); //!< Tells whether a Stream object is valid
bool isValid(const Texture* UTILS_NULLABLE p); //!< Tells whether a Texture object is valid
bool isValid(const RenderTarget* UTILS_NULLABLE p); //!< Tells whether a RenderTarget object is valid
bool isValid(const View* UTILS_NULLABLE p); //!< Tells whether a View object is valid
bool isValid(const InstanceBuffer* UTILS_NULLABLE p); //!< Tells whether an InstanceBuffer object is valid
/**
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until
@@ -811,7 +826,7 @@ public:
*
* @return A pointer to the default Material instance (a singleton).
*/
const Material* getDefaultMaterial() const noexcept;
Material const* UTILS_NONNULL getDefaultMaterial() const noexcept;
/**
* Returns the resolved backend.
@@ -842,7 +857,7 @@ public:
* @return A pointer to the Platform object that was provided to Engine::create, or the
* Filament-created one.
*/
Platform* getPlatform() const noexcept;
Platform* UTILS_NULLABLE getPlatform() const noexcept;
/**
* Allocate a small amount of memory directly in the command stream. The allocated memory is
@@ -855,7 +870,7 @@ public:
* @note there is no need to destroy this buffer, it will be freed automatically when
* the current command buffer is executed.
*/
void* streamAlloc(size_t size, size_t alignment = alignof(double)) noexcept;
void* UTILS_NULLABLE streamAlloc(size_t size, size_t alignment = alignof(double)) noexcept;
/**
* Invokes one iteration of the render loop, used only on single-threaded platforms.
+1 -1
View File
@@ -76,7 +76,7 @@ public:
* @return FenceStatus::CONDITION_SATISFIED on success,
* FenceStatus::ERROR otherwise.
*/
static FenceStatus waitAndDestroy(Fence* fence, Mode mode = Mode::FLUSH);
static FenceStatus waitAndDestroy(Fence* UTILS_NONNULL fence, Mode mode = Mode::FLUSH);
protected:
// prevent heap allocation
+2 -2
View File
@@ -79,14 +79,14 @@ public:
* @param planes six plane equations encoded as in getNormalizedPlane() in
* left, right, bottom, top, far, near order
*/
void getNormalizedPlanes(math::float4 planes[6]) const noexcept;
void getNormalizedPlanes(math::float4 planes[UTILS_NONNULL 6]) const noexcept;
/**
* Returns all six frustum planes in left, right, bottom, top, far, near order
* @return six plane equations encoded as in getNormalizedPlane() in
* left, right, bottom, top, far, near order
*/
math::float4 const* getNormalizedPlanes() const noexcept { return mPlanes; }
math::float4 const* UTILS_NONNULL getNormalizedPlanes() const noexcept { return mPlanes; }
/**
* Returns whether a box intersects the frustum (i.e. is visible)
+2 -3
View File
@@ -89,8 +89,7 @@ public:
*
* @param engine Reference to the filament::Engine to associate this IndexBuffer with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
* @return pointer to the newly created object.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
@@ -98,7 +97,7 @@ public:
*
* @see IndexBuffer::setBuffer
*/
IndexBuffer* build(Engine& engine);
IndexBuffer* UTILS_NONNULL build(Engine& engine);
private:
friend class FIndexBuffer;
};
+10 -9
View File
@@ -116,7 +116,7 @@ public:
* @return This Builder, for chaining calls.
*
*/
Builder& reflections(Texture const* cubemap) noexcept;
Builder& reflections(Texture const* UTILS_NULLABLE cubemap) noexcept;
/**
* Sets the irradiance as Spherical Harmonics.
@@ -162,7 +162,7 @@ public:
* Because the coefficients are pre-scaled, `sh[0]` is the environment's
* average irradiance.
*/
Builder& irradiance(uint8_t bands, math::float3 const* sh) noexcept;
Builder& irradiance(uint8_t bands, math::float3 const* UTILS_NONNULL sh) noexcept;
/**
* Sets the irradiance from the radiance expressed as Spherical Harmonics.
@@ -194,7 +194,7 @@ public:
*
* @return This Builder, for chaining calls.
*/
Builder& radiance(uint8_t bands, math::float3 const* sh) noexcept;
Builder& radiance(uint8_t bands, math::float3 const* UTILS_NONNULL sh) noexcept;
/**
* Sets the irradiance as a cubemap.
@@ -213,7 +213,7 @@ public:
*
* @see irradiance(uint8_t bands, math::float3 const* sh)
*/
Builder& irradiance(Texture const* cubemap) noexcept;
Builder& irradiance(Texture const* UTILS_NULLABLE cubemap) noexcept;
/**
* (optional) Environment intensity.
@@ -249,7 +249,7 @@ public:
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/
IndirectLight* build(Engine& engine);
IndirectLight* UTILS_NONNULL build(Engine& engine);
private:
friend class FIndirectLight;
@@ -286,12 +286,12 @@ public:
/**
* Returns the associated reflection map, or null if it does not exist.
*/
Texture const* getReflectionsTexture() const noexcept;
Texture const* UTILS_NULLABLE getReflectionsTexture() const noexcept;
/**
* Returns the associated irradiance map, or null if it does not exist.
*/
Texture const* getIrradianceTexture() const noexcept;
Texture const* UTILS_NULLABLE getIrradianceTexture() const noexcept;
/**
* Helper to estimate the direction of the dominant light in the environment represented by
@@ -314,7 +314,7 @@ public:
* @see LightManager::Builder::direction()
* @see getColorEstimate()
*/
static math::float3 getDirectionEstimate(const math::float3 sh[9]) noexcept;
static math::float3 getDirectionEstimate(const math::float3 sh[UTILS_NONNULL 9]) noexcept;
/**
* Helper to estimate the color and relative intensity of the environment represented by
@@ -334,7 +334,8 @@ public:
* @see LightManager::Builder::intensity()
* @see getDirectionEstimate, getIntensity, setIntensity
*/
static math::float4 getColorEstimate(const math::float3 sh[9], math::float3 direction) noexcept;
static math::float4 getColorEstimate(const math::float3 sh[UTILS_NONNULL 9],
math::float3 direction) noexcept;
/** @deprecated use static versions instead */
+4 -3
View File
@@ -68,12 +68,12 @@ public:
* @param localTransforms an array of math::mat4f with length instanceCount, must remain
* valid until after build() is called
*/
Builder& localTransforms(math::mat4f const* localTransforms) noexcept;
Builder& localTransforms(math::mat4f const* UTILS_NULLABLE localTransforms) noexcept;
/**
* Creates the InstanceBuffer object and returns a pointer to it.
*/
InstanceBuffer* build(Engine& engine);
InstanceBuffer* UTILS_NONNULL build(Engine& engine);
private:
friend class FInstanceBuffer;
@@ -93,7 +93,8 @@ public:
* @param count the number of local transforms
* @param offset index of the first instance to set local transforms
*/
void setLocalTransforms(math::mat4f const* localTransforms, size_t count, size_t offset = 0);
void setLocalTransforms(math::mat4f const* UTILS_NONNULL localTransforms,
size_t count, size_t offset = 0);
protected:
// prevent heap allocation
+4 -4
View File
@@ -176,7 +176,7 @@ public:
* Retrieve the Entities of all the components of this manager.
* @return A list, in no particular order, of all the entities managed by this manager.
*/
utils::Entity const* getEntities() const noexcept;
utils::Entity const* UTILS_NONNULL getEntities() const noexcept;
/**
* Gets an Instance representing the Light component associated with the given Entity.
@@ -394,7 +394,7 @@ public:
* positions into
* @param cascades the number of shadow cascades, at most 4
*/
static void computeUniformSplits(float* splitPositions, uint8_t cascades);
static void computeUniformSplits(float* UTILS_NONNULL splitPositions, uint8_t cascades);
/**
* Utility method to compute ShadowOptions::cascadeSplitPositions according to a logarithmic
@@ -406,7 +406,7 @@ public:
* @param near the camera near plane
* @param far the camera far plane
*/
static void computeLogSplits(float* splitPositions, uint8_t cascades,
static void computeLogSplits(float* UTILS_NONNULL splitPositions, uint8_t cascades,
float near, float far);
/**
@@ -426,7 +426,7 @@ public:
* @param lambda a float in the range [0, 1] that interpolates between log and
* uniform split schemes
*/
static void computePracticalSplits(float* splitPositions, uint8_t cascades,
static void computePracticalSplits(float* UTILS_NONNULL splitPositions, uint8_t cascades,
float near, float far, float lambda);
};
+26 -26
View File
@@ -74,7 +74,7 @@ public:
*/
struct ParameterInfo {
//! Name of the parameter.
const char* name;
const char* UTILS_NONNULL name;
//! Whether the parameter is a sampler (texture).
bool isSampler;
//! Whether the parameter is a subpass type.
@@ -110,7 +110,7 @@ public:
* @param payload Pointer to the material data, must stay valid until build() is called.
* @param size Size of the material data pointed to by "payload" in bytes.
*/
Builder& package(const void* payload, size_t size);
Builder& package(const void* UTILS_NONNULL payload, size_t size);
template<typename T>
using is_supported_constant_parameter_t = typename std::enable_if<
@@ -132,11 +132,11 @@ public:
* in the material definition.
*/
template<typename T, typename = is_supported_constant_parameter_t<T>>
Builder& constant(const char* name, size_t nameLength, T value);
Builder& constant(const char* UTILS_NONNULL name, size_t nameLength, T value);
/** inline helper to provide the constant name as a null-terminated C string */
template<typename T, typename = is_supported_constant_parameter_t<T>>
inline Builder& constant(const char* name, T value) {
inline Builder& constant(const char* UTILS_NONNULL name, T value) {
return constant(name, strlen(name), value);
}
@@ -152,7 +152,7 @@ public:
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/
Material* build(Engine& engine);
Material* UTILS_NULLABLE build(Engine& engine);
private:
friend class FMaterial;
};
@@ -198,22 +198,22 @@ public:
*/
void compile(CompilerPriorityQueue priority,
UserVariantFilterMask variants,
backend::CallbackHandler* handler = nullptr,
utils::Invocable<void(Material*)>&& callback = {}) noexcept;
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
utils::Invocable<void(Material* UTILS_NONNULL)>&& callback = {}) noexcept;
inline void compile(CompilerPriorityQueue priority,
UserVariantFilterBit variants,
backend::CallbackHandler* handler = nullptr,
utils::Invocable<void(Material*)>&& callback = {}) noexcept {
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
utils::Invocable<void(Material* UTILS_NONNULL)>&& callback = {}) noexcept {
compile(priority, UserVariantFilterMask(variants), handler,
std::forward<utils::Invocable<void(Material*)>>(callback));
std::forward<utils::Invocable<void(Material* UTILS_NONNULL)>>(callback));
}
inline void compile(CompilerPriorityQueue priority,
backend::CallbackHandler* handler = nullptr,
utils::Invocable<void(Material*)>&& callback = {}) noexcept {
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
utils::Invocable<void(Material* UTILS_NONNULL)>&& callback = {}) noexcept {
compile(priority, UserVariantFilterBit::ALL, handler,
std::forward<utils::Invocable<void(Material*)>>(callback));
std::forward<utils::Invocable<void(Material* UTILS_NONNULL)>>(callback));
}
/**
@@ -225,13 +225,13 @@ public:
*
* @return A pointer to the new instance.
*/
MaterialInstance* createInstance(const char* name = nullptr) const noexcept;
MaterialInstance* UTILS_NONNULL createInstance(const char* UTILS_NULLABLE name = nullptr) const noexcept;
//! Returns the name of this material as a null-terminated string.
const char* getName() const noexcept;
const char* UTILS_NONNULL getName() const noexcept;
//! Returns the shading model of this material.
Shading getShading() const noexcept;
Shading getShading() const noexcept;
//! Returns the interpolation mode of this material. This affects how variables are interpolated.
Interpolation getInterpolation() const noexcept;
@@ -317,13 +317,13 @@ public:
*
* @return The number of parameters written to the parameters pointer.
*/
size_t getParameters(ParameterInfo* parameters, size_t count) const noexcept;
size_t getParameters(ParameterInfo* UTILS_NONNULL parameters, size_t count) const noexcept;
//! Indicates whether a parameter of the given name exists on this material.
bool hasParameter(const char* name) const noexcept;
bool hasParameter(const char* UTILS_NONNULL name) const noexcept;
//! Indicates whether an existing parameter is a sampler or not.
bool isSampler(const char* name) const noexcept;
bool isSampler(const char* UTILS_NONNULL name) const noexcept;
/**
* Sets the value of the given parameter on this material's default instance.
@@ -334,7 +334,7 @@ public:
* @see getDefaultInstance()
*/
template <typename T>
void setDefaultParameter(const char* name, T value) noexcept {
void setDefaultParameter(const char* UTILS_NONNULL name, T value) noexcept {
getDefaultInstance()->setParameter(name, value);
}
@@ -347,8 +347,8 @@ public:
*
* @see getDefaultInstance()
*/
void setDefaultParameter(const char* name, Texture const* texture,
TextureSampler const& sampler) noexcept {
void setDefaultParameter(const char* UTILS_NONNULL name,
Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) noexcept {
getDefaultInstance()->setParameter(name, texture, sampler);
}
@@ -361,7 +361,7 @@ public:
*
* @see getDefaultInstance()
*/
void setDefaultParameter(const char* name, RgbType type, math::float3 color) noexcept {
void setDefaultParameter(const char* UTILS_NONNULL name, RgbType type, math::float3 color) noexcept {
getDefaultInstance()->setParameter(name, type, color);
}
@@ -374,15 +374,15 @@ public:
*
* @see getDefaultInstance()
*/
void setDefaultParameter(const char* name, RgbaType type, math::float4 color) noexcept {
void setDefaultParameter(const char* UTILS_NONNULL name, RgbaType type, math::float4 color) noexcept {
getDefaultInstance()->setParameter(name, type, color);
}
//! Returns this material's default instance.
MaterialInstance* getDefaultInstance() noexcept;
MaterialInstance* UTILS_NONNULL getDefaultInstance() noexcept;
//! Returns this material's default instance.
MaterialInstance const* getDefaultInstance() const noexcept;
MaterialInstance const* UTILS_NONNULL getDefaultInstance() const noexcept;
protected:
// prevent heap allocation
+23 -18
View File
@@ -47,7 +47,7 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI {
using StringLiteralHelper = const char[N];
struct StringLiteral {
const char* data;
const char* UTILS_NONNULL data;
size_t size;
template<size_t N>
StringLiteral(StringLiteralHelper<N> const& s) noexcept // NOLINT(google-explicit-constructor)
@@ -95,17 +95,18 @@ public:
* @param name A name for the new MaterialInstance or nullptr to use the template's name
* @return A new MaterialInstance
*/
static MaterialInstance* duplicate(MaterialInstance const* other, const char* name = nullptr) noexcept;
static MaterialInstance* UTILS_NONNULL duplicate(MaterialInstance const* UTILS_NONNULL other,
const char* UTILS_NULLABLE name = nullptr) noexcept;
/**
* @return the Material associated with this instance
*/
Material const* getMaterial() const noexcept;
Material const* UTILS_NONNULL getMaterial() const noexcept;
/**
* @return the name associated with this instance
*/
const char* getName() const noexcept;
const char* UTILS_NONNULL getName() const noexcept;
/**
* Set a uniform by name
@@ -116,7 +117,7 @@ public:
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*/
template<typename T, typename = is_supported_parameter_t<T>>
void setParameter(const char* name, size_t nameLength, T const& value);
void setParameter(const char* UTILS_NONNULL name, size_t nameLength, T const& value);
/** inline helper to provide the name as a null-terminated string literal */
template<typename T, typename = is_supported_parameter_t<T>>
@@ -126,7 +127,7 @@ public:
/** inline helper to provide the name as a null-terminated C string */
template<typename T, typename = is_supported_parameter_t<T>>
inline void setParameter(const char* name, T const& value) {
inline void setParameter(const char* UTILS_NONNULL name, T const& value) {
setParameter<T>(name, strlen(name), value);
}
@@ -141,17 +142,19 @@ public:
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*/
template<typename T, typename = is_supported_parameter_t<T>>
void setParameter(const char* name, size_t nameLength, const T* values, size_t count);
void setParameter(const char* UTILS_NONNULL name, size_t nameLength,
const T* UTILS_NONNULL values, size_t count);
/** inline helper to provide the name as a null-terminated string literal */
template<typename T, typename = is_supported_parameter_t<T>>
inline void setParameter(StringLiteral name, const T* values, size_t count) {
inline void setParameter(StringLiteral name, const T* UTILS_NONNULL values, size_t count) {
setParameter<T>(name.data, name.size, values, count);
}
/** inline helper to provide the name as a null-terminated C string */
template<typename T, typename = is_supported_parameter_t<T>>
inline void setParameter(const char* name, const T* values, size_t count) {
inline void setParameter(const char* UTILS_NONNULL name,
const T* UTILS_NONNULL values, size_t count) {
setParameter<T>(name, strlen(name), values, count);
}
@@ -168,18 +171,18 @@ public:
* @param sampler Sampler parameters.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*/
void setParameter(const char* name, size_t nameLength,
Texture const* texture, TextureSampler const& sampler);
void setParameter(const char* UTILS_NONNULL name, size_t nameLength,
Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler);
/** inline helper to provide the name as a null-terminated string literal */
inline void setParameter(StringLiteral name,
Texture const* texture, TextureSampler const& sampler) {
Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) {
setParameter(name.data, name.size, texture, sampler);
}
/** inline helper to provide the name as a null-terminated C string */
inline void setParameter(const char* name,
Texture const* texture, TextureSampler const& sampler) {
inline void setParameter(const char* UTILS_NONNULL name,
Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) {
setParameter(name, strlen(name), texture, sampler);
}
@@ -194,7 +197,8 @@ public:
* @param color Array of read, green, blue channels values.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*/
void setParameter(const char* name, size_t nameLength, RgbType type, math::float3 color);
void setParameter(const char* UTILS_NONNULL name, size_t nameLength,
RgbType type, math::float3 color);
/** inline helper to provide the name as a null-terminated string literal */
inline void setParameter(StringLiteral name, RgbType type, math::float3 color) {
@@ -202,7 +206,7 @@ public:
}
/** inline helper to provide the name as a null-terminated C string */
inline void setParameter(const char* name, RgbType type, math::float3 color) {
inline void setParameter(const char* UTILS_NONNULL name, RgbType type, math::float3 color) {
setParameter(name, strlen(name), type, color);
}
@@ -217,7 +221,8 @@ public:
* @param color Array of read, green, blue and alpha channels values.
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
*/
void setParameter(const char* name, size_t nameLength, RgbaType type, math::float4 color);
void setParameter(const char* UTILS_NONNULL name, size_t nameLength,
RgbaType type, math::float4 color);
/** inline helper to provide the name as a null-terminated string literal */
inline void setParameter(StringLiteral name, RgbaType type, math::float4 color) {
@@ -225,7 +230,7 @@ public:
}
/** inline helper to provide the name as a null-terminated C string */
inline void setParameter(const char* name, RgbaType type, math::float4 color) {
inline void setParameter(const char* UTILS_NONNULL name, RgbaType type, math::float4 color) {
setParameter(name, strlen(name), type, color);
}
@@ -68,14 +68,13 @@ public:
*
* @param engine Reference to the filament::Engine to associate this MorphTargetBuffer with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
* @return pointer to the newly created object.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/
MorphTargetBuffer* build(Engine& engine);
MorphTargetBuffer* UTILS_NONNULL build(Engine& engine);
private:
friend class FMorphTargetBuffer;
};
@@ -95,7 +94,7 @@ public:
* @see setTangentsAt
*/
void setPositionsAt(Engine& engine, size_t targetIndex,
math::float3 const* positions, size_t count, size_t offset = 0);
math::float3 const* UTILS_NONNULL positions, size_t count, size_t offset = 0);
/**
* Updates positions for the given morph target.
@@ -110,7 +109,7 @@ public:
* @see setTangentsAt
*/
void setPositionsAt(Engine& engine, size_t targetIndex,
math::float4 const* positions, size_t count, size_t offset = 0);
math::float4 const* UTILS_NONNULL positions, size_t count, size_t offset = 0);
/**
* Updates tangents for the given morph target.
@@ -126,7 +125,7 @@ public:
* @see setPositionsAt
*/
void setTangentsAt(Engine& engine, size_t targetIndex,
math::short4 const* tangents, size_t count, size_t offset = 0);
math::short4 const* UTILS_NONNULL tangents, size_t count, size_t offset = 0);
/**
* Returns the vertex count of this MorphTargetBuffer.
+4 -5
View File
@@ -106,7 +106,7 @@ public:
* @param texture The associated texture object.
* @return A reference to this Builder for chaining calls.
*/
Builder& texture(AttachmentPoint attachment, Texture* texture) noexcept;
Builder& texture(AttachmentPoint attachment, Texture* UTILS_NULLABLE texture) noexcept;
/**
* Sets the mipmap level for a given attachment point.
@@ -138,10 +138,9 @@ public:
/**
* Creates the RenderTarget object and returns a pointer to it.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
* @return pointer to the newly created object.
*/
RenderTarget* build(Engine& engine);
RenderTarget* UTILS_NONNULL build(Engine& engine);
private:
friend class FRenderTarget;
@@ -152,7 +151,7 @@ public:
* @param attachment Attachment point
* @return A Texture object or nullptr if no texture is set for this attachment point
*/
Texture* getTexture(AttachmentPoint attachment) const noexcept;
Texture* UTILS_NULLABLE getTexture(AttachmentPoint attachment) const noexcept;
/**
* Returns the mipmap level set on the given attachment point
+61 -34
View File
@@ -129,7 +129,7 @@ public:
* Retrieve the Entities of all the components of this manager.
* @return A list, in no particular order, of all the entities managed by this manager.
*/
utils::Entity const* getEntities() const noexcept;
utils::Entity const* UTILS_NONNULL getEntities() const noexcept;
/**
* The transformation associated with a skinning joint.
@@ -190,9 +190,19 @@ public:
* @param maxIndex specifies the maximum index contained in the index buffer
* @param count number of indices to read (for triangles, this should be a multiple of 3)
*/
Builder& geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices, size_t offset, size_t minIndex, size_t maxIndex, size_t count) noexcept;
Builder& geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices, size_t offset, size_t count) noexcept; //!< \overload
Builder& geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices) noexcept; //!< \overload
Builder& geometry(size_t index, PrimitiveType type,
VertexBuffer* UTILS_NONNULL vertices,
IndexBuffer* UTILS_NONNULL indices,
size_t offset, size_t minIndex, size_t maxIndex, size_t count) noexcept;
Builder& geometry(size_t index, PrimitiveType type,
VertexBuffer* UTILS_NONNULL vertices,
IndexBuffer* UTILS_NONNULL indices,
size_t offset, size_t count) noexcept; //!< \overload
Builder& geometry(size_t index, PrimitiveType type,
VertexBuffer* UTILS_NONNULL vertices,
IndexBuffer* UTILS_NONNULL indices) noexcept; //!< \overload
/**
* Binds a material instance to the specified primitive.
@@ -209,7 +219,8 @@ public:
*
* @see Engine::setActiveFeatureLevel
*/
Builder& material(size_t index, MaterialInstance const* materialInstance) noexcept;
Builder& material(size_t index,
MaterialInstance const* UTILS_NONNULL materialInstance) noexcept;
/**
* The axis-aligned bounding box of the renderable.
@@ -357,7 +368,8 @@ public:
* @param count 0 to disable, otherwise the number of bone transforms (up to 255)
* @param offset offset in the SkinningBuffer
*/
Builder& skinning(SkinningBuffer* skinningBuffer, size_t count, size_t offset) noexcept;
Builder& skinning(SkinningBuffer* UTILS_NONNULL skinningBuffer,
size_t count, size_t offset) noexcept;
/**
@@ -375,8 +387,8 @@ public:
* @param boneCount 0 to disable, otherwise the number of bone transforms (up to 255)
* @param transforms the initial set of transforms (one for each bone)
*/
Builder& skinning(size_t boneCount, math::mat4f const* transforms) noexcept;
Builder& skinning(size_t boneCount, Bone const* bones) noexcept; //!< \overload
Builder& skinning(size_t boneCount, math::mat4f const* UTILS_NONNULL transforms) noexcept;
Builder& skinning(size_t boneCount, Bone const* UTILS_NONNULL bones) noexcept; //!< \overload
Builder& skinning(size_t boneCount) noexcept; //!< \overload
/**
@@ -405,7 +417,8 @@ public:
* @see VertexBuffer:Builder:advancedSkinning
*/
Builder& boneIndicesAndWeights(size_t primitiveIndex,
math::float2 const* indicesAndWeights, size_t count, size_t bonesPerVertex) noexcept;
math::float2 const* UTILS_NONNULL indicesAndWeights,
size_t count, size_t bonesPerVertex) noexcept;
/**
* Define bone indices and weights "pairs" for vertex skinning as a float2.
@@ -468,10 +481,11 @@ public:
* @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
*/
Builder& morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) noexcept;
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer,
size_t offset, size_t count) noexcept;
inline Builder& morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer) noexcept;
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept;
/**
* Sets the drawing order for blended primitives. The drawing order is either global or
@@ -538,7 +552,8 @@ public:
* the result of Engine::getMaxAutomaticInstances().
* @param instanceBuffer an InstanceBuffer containing at least instanceCount transforms
*/
Builder& instances(size_t instanceCount, InstanceBuffer* instanceBuffer) noexcept;
Builder& instances(size_t instanceCount,
InstanceBuffer* UTILS_NONNULL instanceBuffer) noexcept;
/**
* Adds the Renderable component to an entity.
@@ -565,18 +580,18 @@ public:
friend class FRenderPrimitive;
friend class FRenderableManager;
struct Entry {
VertexBuffer* vertices = nullptr;
IndexBuffer* indices = nullptr;
VertexBuffer* UTILS_NULLABLE vertices = nullptr;
IndexBuffer* UTILS_NULLABLE indices = nullptr;
size_t offset = 0;
size_t minIndex = 0;
size_t maxIndex = 0;
size_t count = 0;
MaterialInstance const* materialInstance = nullptr;
MaterialInstance const* UTILS_NULLABLE materialInstance = nullptr;
PrimitiveType type = PrimitiveType::TRIANGLES;
uint16_t blendOrder = 0;
bool globalBlendOrderEnabled = false;
struct {
MorphTargetBuffer* buffer = nullptr;
MorphTargetBuffer* UTILS_NULLABLE buffer = nullptr;
size_t offset = 0;
size_t count = 0;
} morphing;
@@ -694,8 +709,11 @@ public:
* Updates the bone transforms in the range [offset, offset + boneCount).
* The bones must be pre-allocated using Builder::skinning().
*/
void setBones(Instance instance, Bone const* transforms, size_t boneCount = 1, size_t offset = 0);
void setBones(Instance instance, math::mat4f const* transforms, size_t boneCount = 1, size_t offset = 0); //!< \overload
void setBones(Instance instance, Bone const* UTILS_NONNULL transforms,
size_t boneCount = 1, size_t offset = 0);
void setBones(Instance instance, math::mat4f const* UTILS_NONNULL transforms,
size_t boneCount = 1, size_t offset = 0); //!< \overload
/**
* Associates a region of a SkinningBuffer to a renderable instance
@@ -708,7 +726,7 @@ public:
* @param count Size of the region in bones, must be smaller or equal to 256.
* @param offset Start offset of the region in bones
*/
void setSkinningBuffer(Instance instance, SkinningBuffer* skinningBuffer,
void setSkinningBuffer(Instance instance, SkinningBuffer* UTILS_NONNULL skinningBuffer,
size_t count, size_t offset);
/**
@@ -723,24 +741,25 @@ public:
* @param offset Index of the first morph target weight to set at instance.
*/
void setMorphWeights(Instance instance,
float const* weights, size_t count, size_t offset = 0);
float const* UTILS_NONNULL weights, size_t count, size_t offset = 0);
/**
* Associates a MorphTargetBuffer to the given primitive.
*/
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count);
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, size_t offset, size_t count);
/**
* Utility method to change a MorphTargetBuffer to the given primitive
*/
inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer);
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer);
/**
* Get a MorphTargetBuffer to the given primitive or null if it doesn't exist.
*/
MorphTargetBuffer* getMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept;
MorphTargetBuffer* UTILS_NULLABLE getMorphTargetBufferAt(Instance instance,
uint8_t level, size_t primitiveIndex) const noexcept;
/**
* Gets the number of morphing in the given entity.
@@ -782,20 +801,22 @@ public:
* @see Engine::setActiveFeatureLevel
*/
void setMaterialInstanceAt(Instance instance,
size_t primitiveIndex, MaterialInstance const* materialInstance);
size_t primitiveIndex, MaterialInstance const* UTILS_NONNULL materialInstance);
/**
* Retrieves the material instance that is bound to the given primitive.
*/
MaterialInstance* getMaterialInstanceAt(Instance instance, size_t primitiveIndex) const noexcept;
MaterialInstance* UTILS_NULLABLE getMaterialInstanceAt(
Instance instance, size_t primitiveIndex) const noexcept;
/**
* Changes the geometry for the given primitive.
*
* \see Builder::geometry()
*/
void setGeometryAt(Instance instance, size_t primitiveIndex,
PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices,
void setGeometryAt(Instance instance, size_t primitiveIndex, PrimitiveType type,
VertexBuffer* UTILS_NONNULL vertices,
IndexBuffer* UTILS_NONNULL indices,
size_t offset, size_t count) noexcept;
/**
@@ -856,7 +877,9 @@ public:
template<typename VECTOR, typename INDEX,
typename = typename is_supported_vector_type<VECTOR>::type,
typename = typename is_supported_index_type<INDEX>::type>
static Box computeAABB(VECTOR const* vertices, INDEX const* indices, size_t count,
static Box computeAABB(
VECTOR const* UTILS_NONNULL vertices,
INDEX const* UTILS_NONNULL indices, size_t count,
size_t stride = sizeof(VECTOR)) noexcept;
protected:
@@ -864,21 +887,25 @@ protected:
~RenderableManager() = default;
};
RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer) noexcept {
RenderableManager::Builder& RenderableManager::Builder::morphing(
uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
return morphing(level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
void RenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer) {
void RenderableManager::setMorphTargetBufferAt(
Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) {
setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
template<typename VECTOR, typename INDEX, typename, typename>
Box RenderableManager::computeAABB(VECTOR const* vertices, INDEX const* indices, size_t count,
size_t stride) noexcept {
Box RenderableManager::computeAABB(
VECTOR const* UTILS_NONNULL vertices,
INDEX const* UTILS_NONNULL indices,
size_t count, size_t stride) noexcept {
math::float3 bmin(FLT_MAX);
math::float3 bmax(-FLT_MAX);
for (size_t i = 0; i < count; ++i) {
+7 -7
View File
@@ -181,14 +181,14 @@ public:
*
* @return A pointer to the Engine instance this Renderer is associated to.
*/
Engine* getEngine() noexcept;
Engine* UTILS_NONNULL getEngine() noexcept;
/**
* Get the Engine that created this Renderer.
*
* @return A constant pointer to the Engine instance this Renderer is associated to.
*/
inline Engine const* getEngine() const noexcept {
inline Engine const* UTILS_NONNULL getEngine() const noexcept {
return const_cast<Renderer *>(this)->getEngine();
}
@@ -264,7 +264,7 @@ public:
* @see
* endFrame()
*/
bool beginFrame(SwapChain* swapChain,
bool beginFrame(SwapChain* UTILS_NONNULL swapChain,
uint64_t vsyncSteadyClockTimeNano = 0u);
/**
@@ -335,7 +335,7 @@ public:
* beginFrame(), endFrame(), View
*
*/
void render(View const* view);
void render(View const* UTILS_NONNULL view);
/**
* Copy the currently rendered view to the indicated swap chain, using the
@@ -350,7 +350,7 @@ public:
* copyFrame() should be called after a frame is rendered using render()
* but before endFrame() is called.
*/
void copyFrame(SwapChain* dstSwapChain, Viewport const& dstViewport,
void copyFrame(SwapChain* UTILS_NONNULL dstSwapChain, Viewport const& dstViewport,
Viewport const& srcViewport, uint32_t flags = 0);
/**
@@ -490,7 +490,7 @@ public:
* readPixels() is intended for debugging and testing. It will impact performance significantly.
*
*/
void readPixels(RenderTarget* renderTarget,
void readPixels(RenderTarget* UTILS_NONNULL renderTarget,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer);
@@ -517,7 +517,7 @@ public:
* However, internally, renderStandaloneView() is highly multi-threaded to both improve
* performance in mitigate the call's latency.
*/
void renderStandaloneView(View const* view);
void renderStandaloneView(View const* UTILS_NONNULL view);
/**
+6 -6
View File
@@ -75,14 +75,14 @@ public:
*
* @param skybox The Skybox to use to fill untouched pixels, or nullptr to unset the Skybox.
*/
void setSkybox(Skybox* skybox) noexcept;
void setSkybox(Skybox* UTILS_NULLABLE skybox) noexcept;
/**
* Returns the Skybox associated with the Scene.
*
* @return The associated Skybox, or nullptr if there is none.
*/
Skybox* getSkybox() const noexcept;
Skybox* UTILS_NULLABLE getSkybox() const noexcept;
/**
* Set the IndirectLight to use when rendering the Scene.
@@ -93,7 +93,7 @@ public:
* @param ibl The IndirectLight to use when rendering the Scene or nullptr to unset.
* @see getIndirectLight
*/
void setIndirectLight(IndirectLight* ibl) noexcept;
void setIndirectLight(IndirectLight* UTILS_NULLABLE ibl) noexcept;
/**
* Get the IndirectLight or nullptr if none is set.
@@ -101,7 +101,7 @@ public:
* @return the the IndirectLight or nullptr if none is set
* @see setIndirectLight
*/
IndirectLight* getIndirectLight() const noexcept;
IndirectLight* UTILS_NULLABLE getIndirectLight() const noexcept;
/**
* Adds an Entity to the Scene.
@@ -120,7 +120,7 @@ public:
* @param entities Array containing entities to add to the scene.
* @param count Size of the entity array.
*/
void addEntities(const utils::Entity* entities, size_t count);
void addEntities(const utils::Entity* UTILS_NONNULL entities, size_t count);
/**
* Removes the Renderable from the Scene.
@@ -139,7 +139,7 @@ public:
* @param entities Array containing entities to remove from the scene.
* @param count Size of the entity array.
*/
void removeEntities(const utils::Entity* entities, size_t count);
void removeEntities(const utils::Entity* UTILS_NONNULL entities, size_t count);
/**
* Returns the total number of Entities in the Scene, whether alive or not.
+4 -5
View File
@@ -74,8 +74,7 @@ public:
*
* @param engine Reference to the filament::Engine to associate this SkinningBuffer with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
* @return pointer to the newly created object.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
@@ -83,7 +82,7 @@ public:
*
* @see SkinningBuffer::setBones
*/
SkinningBuffer* build(Engine& engine);
SkinningBuffer* UTILS_NONNULL build(Engine& engine);
private:
friend class FSkinningBuffer;
};
@@ -96,7 +95,7 @@ public:
* @param offset offset in elements (not bytes) in the SkinningBuffer (not in transforms)
* @see RenderableManager::setSkinningBuffer
*/
void setBones(Engine& engine, RenderableManager::Bone const* transforms,
void setBones(Engine& engine, RenderableManager::Bone const* UTILS_NONNULL transforms,
size_t count, size_t offset = 0);
/**
@@ -107,7 +106,7 @@ public:
* @param offset offset in elements (not bytes) in the SkinningBuffer (not in transforms)
* @see RenderableManager::setSkinningBuffer
*/
void setBones(Engine& engine, math::mat4f const* transforms,
void setBones(Engine& engine, math::mat4f const* UTILS_NONNULL transforms,
size_t count, size_t offset = 0);
/**
+5 -5
View File
@@ -92,7 +92,7 @@ public:
*
* @see Texture
*/
Builder& environment(Texture* cubemap) noexcept;
Builder& environment(Texture* UTILS_NONNULL cubemap) noexcept;
/**
* Indicates whether the sun should be rendered. The sun can only be
@@ -136,9 +136,9 @@ public:
*
* @param engine Reference to the filament::Engine to associate this Skybox with.
*
* @return pointer to the newly created object, or nullptr if the light couldn't be created.
* @return pointer to the newly created object.
*/
Skybox* build(Engine& engine);
Skybox* UTILS_NONNULL build(Engine& engine);
private:
friend class FSkybox;
@@ -172,9 +172,9 @@ public:
float getIntensity() const noexcept;
/**
* @return the associated texture, or null if it does not exist
* @return the associated texture
*/
Texture const* getTexture() const noexcept;
Texture const* UTILS_NONNULL getTexture() const noexcept;
protected:
// prevent heap allocation
+9 -6
View File
@@ -114,7 +114,7 @@ public:
*
* @return This Builder, for chaining calls.
*/
Builder& stream(void* stream) noexcept;
Builder& stream(void* UTILS_NULLABLE stream) noexcept;
/**
*
@@ -141,9 +141,9 @@ public:
*
* @param engine Reference to the filament::Engine to associate this Stream with.
*
* @return pointer to the newly created object, or nullptr if the stream couldn't be created.
* @return pointer to the newly created object.
*/
Stream* build(Engine& engine);
Stream* UTILS_NONNULL build(Engine& engine);
private:
friend class FStream;
@@ -171,11 +171,12 @@ public:
*
* @param image Pointer to AHardwareBuffer, casted to void* since this is a public header.
* @param callback This is triggered by Filament when it wishes to release the image.
* It callback tales two arguments: the AHardwareBuffer and the userdata.
* The callback tales two arguments: the AHardwareBuffer and the userdata.
* @param userdata Optional closure data. Filament will pass this into the callback when it
* releases the image.
*/
void setAcquiredImage(void* image, Callback callback, void* userdata) noexcept;
void setAcquiredImage(void* UTILS_NONNULL image,
Callback UTILS_NONNULL callback, void* UTILS_NULLABLE userdata) noexcept;
/**
* @see setAcquiredImage(void*, Callback, void*)
@@ -187,7 +188,9 @@ public:
* @param userdata Optional closure data. Filament will pass this into the callback when it
* releases the image.
*/
void setAcquiredImage(void* image, backend::CallbackHandler* handler, Callback callback, void* userdata) noexcept;
void setAcquiredImage(void* UTILS_NONNULL image,
backend::CallbackHandler* UTILS_NULLABLE handler,
Callback UTILS_NONNULL callback, void* UTILS_NULLABLE userdata) noexcept;
/**
* Updates the size of the incoming stream. Whether this value is used is
+5 -4
View File
@@ -152,7 +152,7 @@ class Engine;
class UTILS_PUBLIC SwapChain : public FilamentAPI {
public:
using FrameScheduledCallback = backend::FrameScheduledCallback;
using FrameCompletedCallback = utils::Invocable<void(SwapChain*)>;
using FrameCompletedCallback = utils::Invocable<void(SwapChain* UTILS_NONNULL)>;
/**
* Requests a SwapChain with an alpha channel.
@@ -236,7 +236,7 @@ public:
*/
static bool isSRGBSwapChainSupported(Engine& engine) noexcept;
void* getNativeWindow() const noexcept;
void* UTILS_NULLABLE getNativeWindow() const noexcept;
/**
* FrameScheduledCallback is a callback function that notifies an application when Filament has
@@ -269,7 +269,8 @@ public:
*
* @see PresentCallable
*/
void setFrameScheduledCallback(FrameScheduledCallback callback, void* user = nullptr);
void setFrameScheduledCallback(FrameScheduledCallback UTILS_NULLABLE callback,
void* UTILS_NULLABLE user = nullptr);
/**
* FrameCompletedCallback is a callback function that notifies an application when a frame's
@@ -290,7 +291,7 @@ public:
*
* @see CallbackHandler
*/
void setFrameCompletedCallback(backend::CallbackHandler* handler = nullptr,
void setFrameCompletedCallback(backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
FrameCompletedCallback&& callback = {}) noexcept;
+6 -7
View File
@@ -204,14 +204,13 @@ public:
*
* @param engine Reference to the filament::Engine to associate this Texture with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
* @return pointer to the newly created object.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/
Texture* build(Engine& engine);
Texture* UTILS_NONNULL build(Engine& engine);
/* no user serviceable parts below */
@@ -400,7 +399,7 @@ public:
* @see Builder::sampler()
*
*/
void setExternalImage(Engine& engine, void* image) noexcept;
void setExternalImage(Engine& engine, void* UTILS_NONNULL image) noexcept;
/**
* Specify the external image and plane to associate with this Texture. Typically the external
@@ -431,7 +430,7 @@ public:
* kCVPixelFormatType_420YpCbCr8BiPlanarFullRange images. On platforms
* other than iOS, this method is a no-op.
*/
void setExternalImage(Engine& engine, void* image, size_t plane) noexcept;
void setExternalImage(Engine& engine, void* UTILS_NONNULL image, size_t plane) noexcept;
/**
* Specify the external stream to associate with this Texture. Typically the external
@@ -450,7 +449,7 @@ public:
* @see Builder::sampler(), Stream
*
*/
void setExternalStream(Engine& engine, Stream* stream) noexcept;
void setExternalStream(Engine& engine, Stream* UTILS_NULLABLE stream) noexcept;
/**
* Generates all the mipmap levels automatically. This requires the texture to have a
@@ -500,7 +499,7 @@ public:
*/
void generatePrefilterMipmap(Engine& engine,
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets,
PrefilterOptions const* options = nullptr);
PrefilterOptions const* UTILS_NULLABLE options = nullptr);
/** @deprecated */
+2 -2
View File
@@ -140,7 +140,7 @@ public:
* Retrieve the Entities of all the components of this manager.
* @return A list, in no particular order, of all the entities managed by this manager.
*/
utils::Entity const* getEntities() const noexcept;
utils::Entity const* UTILS_NONNULL getEntities() const noexcept;
/**
* Enables or disable the accurate translation mode. Disabled by default.
@@ -224,7 +224,7 @@ public:
* @param count The maximum number of children to retrieve.
* @return The number of children written to the pointer.
*/
size_t getChildren(Instance i, utils::Entity* children, size_t count) const noexcept;
size_t getChildren(Instance i, utils::Entity* UTILS_NONNULL children, size_t count) const noexcept;
/**
* Returns an iterator to the Instance of the first child of the given parent.
+4 -4
View File
@@ -163,14 +163,13 @@ public:
*
* @param engine Reference to the filament::Engine to associate this VertexBuffer with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
* @return pointer to the newly created object.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/
VertexBuffer* build(Engine& engine);
VertexBuffer* UTILS_NONNULL build(Engine& engine);
private:
friend class FVertexBuffer;
@@ -209,7 +208,8 @@ public:
* and Builder::bufferCount() - 1.
* @param bufferObject The handle to the GPU data that will be used in this buffer slot.
*/
void setBufferObjectAt(Engine& engine, uint8_t bufferIndex, BufferObject const* bufferObject);
void setBufferObjectAt(Engine& engine, uint8_t bufferIndex,
BufferObject const* UTILS_NONNULL bufferObject);
protected:
// prevent heap allocation
+22 -20
View File
@@ -93,7 +93,7 @@ public:
* Sets the View's name. Only useful for debugging.
* @param name Pointer to the View's name. The string is copied.
*/
void setName(const char* name) noexcept;
void setName(const char* UTILS_NONNULL name) noexcept;
/**
* Returns the View's name
@@ -102,7 +102,7 @@ public:
*
* @attention Do *not* free the pointer or modify its content.
*/
const char* getName() const noexcept;
const char* UTILS_NULLABLE getName() const noexcept;
/**
* Set this View instance's Scene.
@@ -118,19 +118,19 @@ public:
* There is no reference-counting.
* Make sure to dissociate a Scene from all Views before destroying it.
*/
void setScene(Scene* scene);
void setScene(Scene* UTILS_NULLABLE scene);
/**
* Returns the Scene currently associated with this View.
* @return A pointer to the Scene associated to this View. nullptr if no Scene is set.
*/
Scene* getScene() noexcept;
Scene* UTILS_NULLABLE getScene() noexcept;
/**
* Returns the Scene currently associated with this View.
* @return A pointer to the Scene associated to this View. nullptr if no Scene is set.
*/
Scene const* getScene() const noexcept {
Scene const* UTILS_NULLABLE getScene() const noexcept {
return const_cast<View*>(this)->getScene();
}
@@ -145,7 +145,7 @@ public:
*
* @param renderTarget Render target associated with view, or nullptr for the swap chain.
*/
void setRenderTarget(RenderTarget* renderTarget) noexcept;
void setRenderTarget(RenderTarget* UTILS_NULLABLE renderTarget) noexcept;
/**
* Gets the offscreen render target associated with this view.
@@ -154,7 +154,7 @@ public:
*
* @see setRenderTarget
*/
RenderTarget* getRenderTarget() const noexcept;
RenderTarget* UTILS_NULLABLE getRenderTarget() const noexcept;
/**
* Sets the rectangular region to render to.
@@ -187,7 +187,7 @@ public:
* There is no reference-counting.
* Make sure to dissociate a Camera from all Views before destroying it.
*/
void setCamera(Camera* camera) noexcept;
void setCamera(Camera* UTILS_NONNULL camera) noexcept;
/**
* Returns the Camera currently associated with this View.
@@ -404,13 +404,13 @@ public:
* There is no reference-counting.
* Make sure to dissociate a ColorGrading from all Views before destroying it.
*/
void setColorGrading(ColorGrading* colorGrading) noexcept;
void setColorGrading(ColorGrading* UTILS_NULLABLE colorGrading) noexcept;
/**
* Returns the color grading transforms currently associated to this view.
* @return A pointer to the ColorGrading associated to this View.
*/
const ColorGrading* getColorGrading() const noexcept;
const ColorGrading* UTILS_NULLABLE getColorGrading() const noexcept;
/**
* Sets ambient occlusion options.
@@ -716,10 +716,10 @@ public:
bool isFrustumCullingEnabled() const noexcept;
//! debugging: sets the Camera used for rendering. It may be different from the culling camera.
void setDebugCamera(Camera* camera) noexcept;
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* getDirectionalLightCamera() const noexcept;
Camera const* UTILS_NULLABLE getDirectionalLightCamera() const noexcept;
/** Result of a picking query */
@@ -748,11 +748,12 @@ public:
/** User data for PickingQueryResultCallback */
struct PickingQuery {
// note: this is enough to store a std::function<> -- just saying...
void* storage[4];
void* UTILS_NULLABLE storage[4];
};
/** callback type used for picking queries. */
using PickingQueryResultCallback = void(*)(PickingQueryResult const& result, PickingQuery* pq);
using PickingQueryResultCallback =
void(*)(PickingQueryResult const& result, PickingQuery* UTILS_NONNULL pq);
/**
* Helper for creating a picking query from Foo::method, by pointer.
@@ -766,8 +767,8 @@ public:
* @param handler Handler to dispatch the callback or nullptr for the default handler.
*/
template<typename T, void(T::*method)(PickingQueryResult const&)>
void pick(uint32_t x, uint32_t y, T* instance,
backend::CallbackHandler* handler = nullptr) noexcept {
void pick(uint32_t x, uint32_t y, T* UTILS_NONNULL instance,
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr) noexcept {
PickingQuery& query = pick(x, y, [](PickingQueryResult const& result, PickingQuery* pq) {
(static_cast<T*>(pq->storage[0])->*method)(result);
}, handler);
@@ -787,7 +788,7 @@ public:
*/
template<typename T, void(T::*method)(PickingQueryResult const&)>
void pick(uint32_t x, uint32_t y, T instance,
backend::CallbackHandler* handler = nullptr) noexcept {
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr) noexcept {
static_assert(sizeof(instance) <= sizeof(PickingQuery::storage), "user data too large");
PickingQuery& query = pick(x, y, [](PickingQueryResult const& result, PickingQuery* pq) {
T* const that = static_cast<T*>(reinterpret_cast<void*>(pq->storage));
@@ -808,7 +809,7 @@ public:
*/
template<typename T>
void pick(uint32_t x, uint32_t y, T functor,
backend::CallbackHandler* handler = nullptr) noexcept {
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr) noexcept {
static_assert(sizeof(functor) <= sizeof(PickingQuery::storage), "functor too large");
PickingQuery& query = pick(x, y, handler,
(PickingQueryResultCallback)[](PickingQueryResult const& result, PickingQuery* pq) {
@@ -834,8 +835,9 @@ public:
* 8*sizeof(void*) bytes of user data. This user data is later accessible
* in the PickingQueryResultCallback callback 3rd parameter.
*/
PickingQuery& pick(uint32_t x, uint32_t y, backend::CallbackHandler* handler,
PickingQueryResultCallback callback) noexcept;
PickingQuery& pick(uint32_t x, uint32_t y,
backend::CallbackHandler* UTILS_NULLABLE handler,
PickingQueryResultCallback UTILS_NONNULL callback) noexcept;
/**
* Set the value of material global variables. There are up-to four such variable each of
+1 -1
View File
@@ -54,7 +54,7 @@ using LinearAllocatorArena = utils::Arena<
#endif
using ArenaScope = utils::ArenaScope<LinearAllocatorArena>;
using RootArenaScope = utils::ArenaScope<LinearAllocatorArena>;
} // namespace filament
+4 -3
View File
@@ -168,7 +168,8 @@ void Froxelizer::setProjection(const mat4f& projection,
}
bool Froxelizer::prepare(
FEngine::DriverApi& driverApi, ArenaScope& arena, filament::Viewport const& viewport,
FEngine::DriverApi& driverApi, RootArenaScope& rootArenaScope,
filament::Viewport const& viewport,
const mat4f& projection, float projectionNear, float projectionFar) noexcept {
setViewport(viewport);
setProjection(projection, projectionNear, projectionFar);
@@ -199,12 +200,12 @@ bool Froxelizer::prepare(
// light records per froxel (~256 KiB)
mLightRecords = {
arena.allocate<LightRecord>(getFroxelBufferEntryCount(), CACHELINE_SIZE),
rootArenaScope.allocate<LightRecord>(getFroxelBufferEntryCount(), CACHELINE_SIZE),
getFroxelBufferEntryCount() };
// froxel thread data (~256 KiB)
mFroxelShardedData = {
arena.allocate<FroxelThreadData>(GROUP_COUNT, CACHELINE_SIZE),
rootArenaScope.allocate<FroxelThreadData>(GROUP_COUNT, CACHELINE_SIZE),
uint32_t(GROUP_COUNT)
};
+1 -1
View File
@@ -110,7 +110,7 @@ public:
*
* return true if updateUniforms() needs to be called
*/
bool prepare(backend::DriverApi& driverApi, ArenaScope& arena, Viewport const& viewport,
bool prepare(backend::DriverApi& driverApi, RootArenaScope& rootArenaScope, Viewport const& viewport,
const math::mat4f& projection, float projectionNear, float projectionFar) noexcept;
Froxel getFroxelAt(size_t x, size_t y, size_t z) const noexcept;
+1 -1
View File
@@ -25,7 +25,7 @@ MaterialInstance* Material::createInstance(const char* name) const noexcept {
}
const char* Material::getName() const noexcept {
return downcast(this)->getName().c_str();
return downcast(this)->getName().c_str_safe();
}
Shading Material::getShading() const noexcept {
+18 -16
View File
@@ -414,7 +414,7 @@ void PostProcessManager::commitAndRender(FrameGraphResources::RenderPassInfo con
// ------------------------------------------------------------------------------------------------
PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph& fg,
RenderPass const& pass, uint8_t structureRenderFlags,
RenderPassBuilder const& passBuilder, uint8_t structureRenderFlags,
uint32_t width, uint32_t height,
StructurePassConfig const& config) noexcept {
@@ -466,17 +466,19 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
.clearFlags = TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH
});
},
[=, renderPass = pass](FrameGraphResources const& resources,
[=, passBuilder = passBuilder](FrameGraphResources const& resources,
auto const&, DriverApi&) mutable {
Variant structureVariant(Variant::DEPTH_VARIANT);
structureVariant.setPicking(config.picking);
auto out = resources.getRenderPassInfo();
renderPass.setRenderFlags(structureRenderFlags);
renderPass.setVariant(structureVariant);
renderPass.appendCommands(mEngine, RenderPass::CommandTypeFlags::SSAO);
renderPass.sortCommands(mEngine);
renderPass.execute(mEngine, resources.getPassName(), out.target, out.params);
passBuilder.renderFlags(structureRenderFlags);
passBuilder.variant(structureVariant);
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SSAO);
RenderPass const pass{ passBuilder.build(mEngine) };
RenderPass::execute(pass, mEngine, resources.getPassName(), out.target, out.params);
});
auto depth = structurePass->depth;
@@ -523,7 +525,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
// ------------------------------------------------------------------------------------------------
FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
RenderPass const& pass,
RenderPassBuilder const& passBuilder,
FrameHistory const& frameHistory,
CameraInfo const& cameraInfo,
PerViewUniforms& uniforms,
@@ -586,7 +588,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
},
[this, projection = cameraInfo.projection,
userViewMatrix = cameraInfo.getUserViewMatrix(), uvFromClipMatrix, historyProjection,
options, &uniforms, renderPass = pass]
options, &uniforms, passBuilder = passBuilder]
(FrameGraphResources const& resources, auto const& data, DriverApi& driver) mutable {
// set structure sampler
uniforms.prepareStructure(data.structure ?
@@ -607,17 +609,17 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
auto out = resources.getRenderPassInfo();
// Remove the HAS_SHADOWING RenderFlags, since it's irrelevant when rendering reflections
RenderPass::RenderFlags flags = renderPass.getRenderFlags();
flags &= ~RenderPass::HAS_SHADOWING;
renderPass.setRenderFlags(flags);
passBuilder.renderFlags(~RenderPass::HAS_SHADOWING, 0);
// use our special SSR variant, it can only be applied to object that have
// the SCREEN_SPACE ReflectionMode.
renderPass.setVariant(Variant{Variant::SPECIAL_SSR});
passBuilder.variant(Variant{ Variant::SPECIAL_SSR });
// generate all our drawing commands, except blended objects.
renderPass.appendCommands(mEngine, RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
renderPass.sortCommands(mEngine);
renderPass.execute(mEngine, resources.getPassName(), out.target, out.params);
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
RenderPass const pass{ passBuilder.build(mEngine) };
RenderPass::execute(pass, mEngine, resources.getPassName(), out.target, out.params);
});
return ssrPass->reflections;
+3 -2
View File
@@ -50,6 +50,7 @@ class FMaterialInstance;
class FrameGraph;
class PerViewUniforms;
class RenderPass;
class RenderPassBuilder;
struct CameraInfo;
class PostProcessManager {
@@ -99,12 +100,12 @@ public:
FrameGraphId<FrameGraphTexture> picking;
};
StructurePassOutput structure(FrameGraph& fg,
RenderPass const& pass, uint8_t structureRenderFlags,
RenderPassBuilder const& passBuilder, uint8_t structureRenderFlags,
uint32_t width, uint32_t height, StructurePassConfig const& config) noexcept;
// reflections pass
FrameGraphId<FrameGraphTexture> ssr(FrameGraph& fg,
RenderPass const& pass,
RenderPassBuilder const& passBuilder,
FrameHistory const& frameHistory,
CameraInfo const& cameraInfo,
PerViewUniforms& uniforms,
+279 -184
View File
@@ -19,17 +19,43 @@
#include "RenderPrimitive.h"
#include "ShadowMap.h"
#include "details/Camera.h"
#include "details/Material.h"
#include "details/MaterialInstance.h"
#include "details/View.h"
#include "components/RenderableManager.h"
#include <private/filament/EngineEnums.h>
#include <private/filament/UibStructs.h>
#include <private/filament/Variant.h>
#include <filament/MaterialEnums.h>
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/PipelineState.h>
#include "private/backend/CircularBuffer.h"
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/JobSystem.h>
#include <utils/Panic.h>
#include <utils/Slice.h>
#include <utils/Systrace.h>
#include <utils/Range.h>
#include <algorithm>
#include <functional>
#include <limits>
#include <utility>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
using namespace utils;
using namespace filament::math;
@@ -37,60 +63,103 @@ namespace filament {
using namespace backend;
RenderPass::RenderPass(FEngine& engine,
RenderPass::Arena& arena) noexcept
: mCommandArena(arena),
mCustomCommands(engine.getPerRenderPassAllocator()) {
RenderPassBuilder& RenderPassBuilder::customCommand(
FEngine& engine,
uint8_t channel,
RenderPass::Pass pass,
RenderPass::CustomCommand custom,
uint32_t order,
RenderPass::Executor::CustomCommandFn const& command) {
if (!mCustomCommands.has_value()) {
// construct the vector the first time
mCustomCommands.emplace(engine.getPerRenderPassArena());
}
mCustomCommands->emplace_back(channel, pass, custom, order, command);
return *this;
}
RenderPass::RenderPass(RenderPass const& rhs) = default;
RenderPass RenderPassBuilder::build(FEngine& engine) {
ASSERT_POSTCONDITION(mRenderableSoa, "RenderPassBuilder::geometry() hasn't been called");
assert_invariant(mScissorViewport.width <= std::numeric_limits<int32_t>::max());
assert_invariant(mScissorViewport.height <= std::numeric_limits<int32_t>::max());
return RenderPass{ engine, *this };
}
// ------------------------------------------------------------------------------------------------
RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexcept
: mRenderableSoa(*builder.mRenderableSoa),
mVisibleRenderables(builder.mVisibleRenderables),
mUboHandle(builder.mUboHandle),
mCameraPosition(builder.mCameraPosition),
mCameraForwardVector(builder.mCameraForwardVector),
mFlags(builder.mFlags),
mVariant(builder.mVariant),
mVisibilityMask(builder.mVisibilityMask),
mScissorViewport(builder.mScissorViewport),
mCustomCommands(engine.getPerRenderPassArena()) {
// compute the number of commands we need
updateSummedPrimitiveCounts(
const_cast<FScene::RenderableSoa&>(mRenderableSoa), mVisibleRenderables);
uint32_t commandCount =
FScene::getPrimitiveCount(mRenderableSoa, mVisibleRenderables.last);
const bool colorPass = bool(builder.mCommandTypeFlags & CommandTypeFlags::COLOR);
const bool depthPass = bool(builder.mCommandTypeFlags & CommandTypeFlags::DEPTH);
commandCount *= uint32_t(colorPass * 2 + depthPass);
commandCount += 1; // for the sentinel
uint32_t const customCommandCount =
builder.mCustomCommands.has_value() ? builder.mCustomCommands->size() : 0;
Command* const curr = builder.mArena.alloc<Command>(commandCount + customCommandCount);
assert_invariant(curr);
if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(curr))) {
static bool sLogOnce = true;
if (UTILS_UNLIKELY(sLogOnce)) {
sLogOnce = false;
PANIC_LOG("RenderPass arena is full, using slower system heap. Please increase "
"the appropriate constant (e.g. FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB).");
}
}
mCommandBegin = curr;
mCommandEnd = curr + commandCount + customCommandCount;
appendCommands(engine, { curr, commandCount }, builder.mCommandTypeFlags);
if (builder.mCustomCommands.has_value()) {
Command* p = curr + commandCount;
for (auto [channel, passId, command, order, fn]: builder.mCustomCommands.value()) {
appendCustomCommand(p++, channel, passId, command, order, fn);
}
}
// sort commands once we're done adding commands
sortCommands(builder.mArena);
if (engine.isAutomaticInstancingEnabled()) {
instanceify(engine, builder.mArena);
}
}
// this destructor is actually heavy because it inlines ~vector<>
RenderPass::~RenderPass() noexcept = default;
RenderPass::Command* RenderPass::append(size_t count) noexcept {
// this is like an "in-place" realloc(). Works only with LinearAllocator.
Command* const curr = mCommandArena.alloc<Command>(count);
assert_invariant(curr);
assert_invariant(mCommandBegin == nullptr || curr == mCommandEnd);
if (mCommandBegin == nullptr) {
mCommandBegin = mCommandEnd = curr;
}
mCommandEnd += count;
return curr;
}
void RenderPass::resize(size_t count) noexcept {
void RenderPass::resize(Arena& arena, size_t count) noexcept {
if (mCommandBegin) {
mCommandEnd = mCommandBegin + count;
mCommandArena.rewind(mCommandEnd);
arena.rewind(mCommandEnd);
}
}
void RenderPass::setGeometry(FScene::RenderableSoa const& soa, Range<uint32_t> vr,
backend::Handle<backend::HwBufferObject> uboHandle) noexcept {
mRenderableSoa = &soa;
mVisibleRenderables = vr;
mUboHandle = uboHandle;
}
void RenderPass::setCamera(const CameraInfo& camera) noexcept {
mCameraPosition = camera.getPosition();
mCameraForwardVector = camera.getForwardVector();
}
void RenderPass::setScissorViewport(backend::Viewport viewport) noexcept {
assert_invariant(viewport.width <= std::numeric_limits<int32_t>::max());
assert_invariant(viewport.height <= std::numeric_limits<int32_t>::max());
mScissorViewport = viewport;
}
void RenderPass::appendCommands(FEngine& engine, CommandTypeFlags const commandTypeFlags) noexcept {
void RenderPass::appendCommands(FEngine& engine,
Slice<Command> commands, CommandTypeFlags const commandTypeFlags) noexcept {
SYSTRACE_CALL();
SYSTRACE_CONTEXT();
assert_invariant(mRenderableSoa);
utils::Range<uint32_t> const vr = mVisibleRenderables;
// trace the number of visible renderables
SYSTRACE_VALUE32("visibleRenderables", vr.size());
@@ -104,17 +173,10 @@ void RenderPass::appendCommands(FEngine& engine, CommandTypeFlags const commandT
const FScene::VisibleMaskType visibilityMask = mVisibilityMask;
// up-to-date summed primitive counts needed for generateCommands()
FScene::RenderableSoa const& soa = *mRenderableSoa;
updateSummedPrimitiveCounts(const_cast<FScene::RenderableSoa&>(soa), vr);
FScene::RenderableSoa const& soa = mRenderableSoa;
// compute how much maximum storage we need for this pass
uint32_t commandCount = FScene::getPrimitiveCount(soa, vr.last);
// double the color pass for transparent objects that need to render twice
const bool colorPass = bool(commandTypeFlags & CommandTypeFlags::COLOR);
const bool depthPass = bool(commandTypeFlags & CommandTypeFlags::DEPTH);
commandCount *= uint32_t(colorPass * 2 + depthPass);
commandCount += 1; // for the sentinel
Command* const curr = append(commandCount);
Command* curr = commands.data();
size_t const commandCount = commands.size();
auto stereoscopicEyeCount =
renderFlags & IS_STEREOSCOPIC ? engine.getConfig().stereoscopicEyeCount : 1;
@@ -152,7 +214,8 @@ void RenderPass::appendCommands(FEngine& engine, CommandTypeFlags const commandT
}
}
void RenderPass::appendCustomCommand(uint8_t channel, Pass pass, CustomCommand custom, uint32_t order,
void RenderPass::appendCustomCommand(Command* commands,
uint8_t channel, Pass pass, CustomCommand custom, uint32_t order,
Executor::CustomCommandFn command) {
assert_invariant((uint64_t(order) << CUSTOM_ORDER_SHIFT) <= CUSTOM_ORDER_MASK);
@@ -168,11 +231,10 @@ void RenderPass::appendCustomCommand(uint8_t channel, Pass pass, CustomCommand c
cmd |= uint64_t(order) << CUSTOM_ORDER_SHIFT;
cmd |= uint64_t(index);
Command* const curr = append(1);
curr->key = cmd;
commands->key = cmd;
}
void RenderPass::sortCommands(FEngine& engine) noexcept {
void RenderPass::sortCommands(Arena& arena) noexcept {
SYSTRACE_NAME("sort and trim commands");
std::sort(mCommandBegin, mCommandEnd);
@@ -183,30 +245,20 @@ void RenderPass::sortCommands(FEngine& engine) noexcept {
return c.key != uint64_t(Pass::SENTINEL);
});
resize(uint32_t(last - mCommandBegin));
if (engine.isAutomaticInstancingEnabled()) {
instanceify(engine);
}
resize(arena, uint32_t(last - mCommandBegin));
}
void RenderPass::execute(FEngine& engine, const char* name,
void RenderPass::execute(RenderPass const& pass,
FEngine& engine, const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams params) const noexcept {
backend::RenderPassParams params) noexcept {
DriverApi& driver = engine.getDriverApi();
// this is a good time to flush the CommandStream, because we're about to potentially
// output a lot of commands. This guarantees here that we have at least
// FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB bytes (1MiB by default).
engine.flush();
driver.beginRenderPass(renderTarget, params);
getExecutor().execute(engine, name);
pass.getExecutor().execute(engine, name);
driver.endRenderPass();
}
void RenderPass::instanceify(FEngine& engine) noexcept {
void RenderPass::instanceify(FEngine& engine, Arena& arena) noexcept {
SYSTRACE_NAME("instanceify");
// instanceify works by scanning the **sorted** command stream, looking for repeat draw
@@ -262,7 +314,8 @@ void RenderPass::instanceify(FEngine& engine) noexcept {
// buffer large enough for all instances data
stagingBufferSize = sizeof(PerRenderableData) * (last - curr);
stagingBuffer = (PerRenderableData*)::malloc(stagingBufferSize);
uboData = mRenderableSoa->data<FScene::UBO>();
uboData = mRenderableSoa.data<FScene::UBO>();
assert_invariant(uboData);
}
// copy the ubo data to a staging buffer
@@ -315,7 +368,7 @@ void RenderPass::instanceify(FEngine& engine) noexcept {
return command.key == uint64_t(Pass::SENTINEL);
});
resize(uint32_t(lastCommand - mCommandBegin));
resize(arena, uint32_t(lastCommand - mCommandBegin));
}
assert_invariant(stagingBuffer == nullptr);
@@ -323,7 +376,7 @@ void RenderPass::instanceify(FEngine& engine) noexcept {
/* static */
UTILS_ALWAYS_INLINE // this function exists only to make the code more readable. we want it inlined.
UTILS_ALWAYS_INLINE // This function exists only to make the code more readable. we want it inlined.
inline // and we don't need it in the compilation unit
void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
FMaterialInstance const* const UTILS_RESTRICT mi, bool inverseFrontFaces) noexcept {
@@ -374,7 +427,7 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
/* static */
UTILS_NOINLINE
void RenderPass::generateCommands(uint32_t commandTypeFlags, Command* const commands,
void RenderPass::generateCommands(CommandTypeFlags commandTypeFlags, Command* const commands,
FScene::RenderableSoa const& soa, Range<uint32_t> range,
Variant variant, RenderFlags renderFlags,
FScene::VisibleMaskType visibilityMask, float3 cameraPosition, float3 cameraForward,
@@ -432,9 +485,9 @@ void RenderPass::generateCommands(uint32_t commandTypeFlags, Command* const comm
}
/* static */
template<uint32_t commandTypeFlags>
template<RenderPass::CommandTypeFlags commandTypeFlags>
UTILS_NOINLINE
RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags,
RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFlags extraFlags,
Command* UTILS_RESTRICT curr,
FScene::RenderableSoa const& UTILS_RESTRICT soa, Range<uint32_t> range,
Variant const variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask,
@@ -737,13 +790,13 @@ void RenderPass::updateSummedPrimitiveCounts(
// ------------------------------------------------------------------------------------------------
void RenderPass::Executor::overridePolygonOffset(backend::PolygonOffset const* polygonOffset) noexcept {
if ((mPolygonOffsetOverride = (polygonOffset != nullptr))) {
if ((mPolygonOffsetOverride = (polygonOffset != nullptr))) { // NOLINT(*-assignment-in-if-condition)
mPolygonOffset = *polygonOffset;
}
}
void RenderPass::Executor::overrideScissor(backend::Viewport const* scissor) noexcept {
if ((mScissorOverride = (scissor != nullptr))) {
if ((mScissorOverride = (scissor != nullptr))) { // NOLINT(*-assignment-in-if-condition)
mScissor = *scissor;
}
}
@@ -754,15 +807,20 @@ void RenderPass::Executor::overrideScissor(backend::Viewport const& scissor) noe
}
void RenderPass::Executor::execute(FEngine& engine, const char*) const noexcept {
execute(engine.getDriverApi(), mCommands.begin(), mCommands.end());
execute(engine, mCommands.begin(), mCommands.end());
}
UTILS_NOINLINE // no need to be inlined
void RenderPass::Executor::execute(backend::DriverApi& driver,
void RenderPass::Executor::execute(FEngine& engine,
const Command* first, const Command* last) const noexcept {
SYSTRACE_CALL();
SYSTRACE_CONTEXT();
DriverApi& driver = engine.getDriverApi();
size_t const capacity = engine.getMinCommandBufferSize();
CircularBuffer const& circularBuffer = driver.getCircularBuffer();
if (first != last) {
SYSTRACE_VALUE32("commandCount", last - first);
@@ -781,126 +839,163 @@ void RenderPass::Executor::execute(backend::DriverApi& driver,
FMaterial const* UTILS_RESTRICT ma = nullptr;
auto const* UTILS_RESTRICT pCustomCommands = mCustomCommands.data();
first--;
while (++first != last) {
assert_invariant(first->key != uint64_t(Pass::SENTINEL));
// Maximum space occupied in the CircularBuffer by a single `Command`. This must be
// reevaluated when the inner loop below adds DriverApi commands or when we change the
// CommandStream protocol. Currently, the maximum is 240 bytes, and we use 256 to be on
// the safer side.
size_t const maxCommandSizeInBytes = 256;
/*
* Be careful when changing code below, this is the hot inner-loop
*/
// Number of Commands that can be issued and guaranteed to fit in the current
// CircularBuffer allocation. In practice, we'll have tons of headroom especially if
// skinning and morphing aren't used. With a 2 MiB buffer (the default) a batch is
// 8192 commands (i.e. draw calls).
size_t const batchCommandCount = capacity / maxCommandSizeInBytes;
while(first != last) {
Command const* const batchLast = std::min(first + batchCommandCount, last);
if (UTILS_UNLIKELY((first->key & CUSTOM_MASK) != uint64_t(CustomCommand::PASS))) {
mi = nullptr; // custom command could change the currently bound MaterialInstance
uint32_t const index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT;
assert_invariant(index < mCustomCommands.size());
pCustomCommands[index]();
continue;
// actual number of commands we need to write (can be smaller than batchCommandCount)
size_t const commandCount = batchLast - first;
size_t const commandSizeInBytes = commandCount * maxCommandSizeInBytes;
// check we have enough capacity to write these commandCount commands, if not,
// request a new CircularBuffer allocation of `capacity` bytes.
if (UTILS_UNLIKELY(circularBuffer.getUsed() > capacity - commandSizeInBytes)) {
engine.flush(); // TODO: we should use a "fast" flush if possible
}
// primitiveHandle may be invalid if no geometry was set on the renderable.
if (UTILS_UNLIKELY(!first->primitive.primitiveHandle)) {
continue;
}
first--;
while (++first != batchLast) {
assert_invariant(first->key != uint64_t(Pass::SENTINEL));
// per-renderable uniform
const PrimitiveInfo info = first->primitive;
pipeline.rasterState = info.rasterState;
/*
* Be careful when changing code below, this is the hot inner-loop
*/
if (UTILS_UNLIKELY(mi != info.mi)) {
// this is always taken the first time
mi = info.mi;
ma = mi->getMaterial();
auto const& scissor = mi->getScissor();
if (UTILS_UNLIKELY(mi->hasScissor())) {
// scissor is set, we need to apply the offset/clip
// clang vectorizes this!
constexpr int32_t maxvali = std::numeric_limits<int32_t>::max();
const backend::Viewport scissorViewport = mScissorViewport;
// compute new left/bottom, assume no overflow
int32_t l = scissor.left + scissorViewport.left;
int32_t b = scissor.bottom + scissorViewport.bottom;
// compute right/top without overflowing, scissor.width/height guaranteed
// to convert to int32
int32_t r = (l > maxvali - int32_t(scissor.width)) ?
maxvali : l + int32_t(scissor.width);
int32_t t = (b > maxvali - int32_t(scissor.height)) ?
maxvali : b + int32_t(scissor.height);
// clip to the viewport
l = std::max(l, scissorViewport.left);
b = std::max(b, scissorViewport.bottom);
r = std::min(r, scissorViewport.left + int32_t(scissorViewport.width));
t = std::min(t, scissorViewport.bottom + int32_t(scissorViewport.height));
assert_invariant(r >= l && t >= b);
*pScissor = { l, b, uint32_t(r - l), uint32_t(t - b) };
} else {
// no scissor set (common case), 'scissor' has its default value, use that.
*pScissor = scissor;
if (UTILS_UNLIKELY((first->key & CUSTOM_MASK) != uint64_t(CustomCommand::PASS))) {
mi = nullptr; // custom command could change the currently bound MaterialInstance
uint32_t const index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT;
assert_invariant(index < mCustomCommands.size());
pCustomCommands[index]();
continue;
}
*pPipelinePolygonOffset = mi->getPolygonOffset();
pipeline.stencilState = mi->getStencilState();
mi->use(driver);
}
pipeline.program = ma->getProgram(info.materialVariant);
uint16_t const instanceCount = info.instanceCount & PrimitiveInfo::INSTANCE_COUNT_MASK;
auto getPerObjectUboHandle =
[this, &info, &instanceCount]() -> std::pair<Handle<backend::HwBufferObject>, uint32_t> {
if (info.instanceBufferHandle) {
// "hybrid" instancing -- instanceBufferHandle takes the place of the UBO
return { info.instanceBufferHandle, 0 };
// primitiveHandle may be invalid if no geometry was set on the renderable.
if (UTILS_UNLIKELY(!first->primitive.primitiveHandle)) {
continue;
}
bool const userInstancing =
(info.instanceCount & PrimitiveInfo::USER_INSTANCE_MASK) != 0u;
if (!userInstancing && instanceCount > 1) {
// automatic instancing
return { mInstancedUboHandle, info.index * sizeof(PerRenderableData) };
} else {
// manual instancing
return { mUboHandle, info.index * sizeof(PerRenderableData) };
// per-renderable uniform
const PrimitiveInfo info = first->primitive;
pipeline.rasterState = info.rasterState;
if (UTILS_UNLIKELY(mi != info.mi)) {
// this is always taken the first time
mi = info.mi;
assert_invariant(mi);
ma = mi->getMaterial();
auto const& scissor = mi->getScissor();
if (UTILS_UNLIKELY(mi->hasScissor())) {
// scissor is set, we need to apply the offset/clip
// clang vectorizes this!
constexpr int32_t maxvali = std::numeric_limits<int32_t>::max();
const backend::Viewport scissorViewport = mScissorViewport;
// compute new left/bottom, assume no overflow
int32_t l = scissor.left + scissorViewport.left;
int32_t b = scissor.bottom + scissorViewport.bottom;
// compute right/top without overflowing, scissor.width/height guaranteed
// to convert to int32
int32_t r = (l > maxvali - int32_t(scissor.width)) ?
maxvali : l + int32_t(scissor.width);
int32_t t = (b > maxvali - int32_t(scissor.height)) ?
maxvali : b + int32_t(scissor.height);
// clip to the viewport
l = std::max(l, scissorViewport.left);
b = std::max(b, scissorViewport.bottom);
r = std::min(r, scissorViewport.left + int32_t(scissorViewport.width));
t = std::min(t, scissorViewport.bottom + int32_t(scissorViewport.height));
assert_invariant(r >= l && t >= b);
*pScissor = { l, b, uint32_t(r - l), uint32_t(t - b) };
} else {
// no scissor set (common case), 'scissor' has its default value, use that.
*pScissor = scissor;
}
*pPipelinePolygonOffset = mi->getPolygonOffset();
pipeline.stencilState = mi->getStencilState();
mi->use(driver);
}
};
// bind per-renderable uniform block. there is no need to attempt to skip this command
// because the backends already do this.
auto const [perObjectUboHandle, offset] = getPerObjectUboHandle();
assert_invariant(perObjectUboHandle);
driver.bindBufferRange(BufferObjectBinding::UNIFORM,
+UniformBindingPoints::PER_RENDERABLE,
perObjectUboHandle,
offset,
sizeof(PerRenderableUib));
assert_invariant(ma);
pipeline.program = ma->getProgram(info.materialVariant);
if (UTILS_UNLIKELY(info.skinningHandle)) {
// note: we can't bind less than sizeof(PerRenderableBoneUib) due to glsl limitations
uint16_t const instanceCount =
info.instanceCount & PrimitiveInfo::INSTANCE_COUNT_MASK;
auto getPerObjectUboHandle =
[this, &info, &instanceCount]() -> std::pair<Handle<backend::HwBufferObject>, uint32_t> {
if (info.instanceBufferHandle) {
// "hybrid" instancing -- instanceBufferHandle takes the place of the UBO
return { info.instanceBufferHandle, 0 };
}
bool const userInstancing =
(info.instanceCount & PrimitiveInfo::USER_INSTANCE_MASK) != 0u;
if (!userInstancing && instanceCount > 1) {
// automatic instancing
return {
mInstancedUboHandle,
info.index * sizeof(PerRenderableData) };
} else {
// manual instancing
return { mUboHandle, info.index * sizeof(PerRenderableData) };
}
};
// Bind per-renderable uniform block. There is no need to attempt to skip this command
// because the backends already do this.
auto const [perObjectUboHandle, offset] = getPerObjectUboHandle();
assert_invariant(perObjectUboHandle);
driver.bindBufferRange(BufferObjectBinding::UNIFORM,
+UniformBindingPoints::PER_RENDERABLE_BONES,
info.skinningHandle,
info.skinningOffset * sizeof(PerRenderableBoneUib::BoneData),
sizeof(PerRenderableBoneUib));
// note: always bind the skinningTexture because the shader needs it.
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING,
info.skinningTexture);
// note: even if only skinning is enabled, binding morphTargetBuffer is needed.
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING,
info.morphTargetBuffer);
}
+UniformBindingPoints::PER_RENDERABLE,
perObjectUboHandle,
offset,
sizeof(PerRenderableUib));
if (UTILS_UNLIKELY(info.morphWeightBuffer)) {
// Instead of using a UBO per primitive, we could also have a single UBO for all
// primitives and use bindUniformBufferRange which might be more efficient.
driver.bindUniformBuffer(+UniformBindingPoints::PER_RENDERABLE_MORPHING,
info.morphWeightBuffer);
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING,
info.morphTargetBuffer);
// note: even if only morphing is enabled, binding skinningTexture is needed.
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING,
info.skinningTexture);
if (UTILS_UNLIKELY(info.skinningHandle)) {
// note: we can't bind less than sizeof(PerRenderableBoneUib) due to glsl limitations
driver.bindBufferRange(BufferObjectBinding::UNIFORM,
+UniformBindingPoints::PER_RENDERABLE_BONES,
info.skinningHandle,
info.skinningOffset * sizeof(PerRenderableBoneUib::BoneData),
sizeof(PerRenderableBoneUib));
// note: always bind the skinningTexture because the shader needs it.
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING,
info.skinningTexture);
// note: even if only skinning is enabled, binding morphTargetBuffer is needed.
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING,
info.morphTargetBuffer);
}
if (UTILS_UNLIKELY(info.morphWeightBuffer)) {
// Instead of using a UBO per primitive, we could also have a single UBO for all
// primitives and use bindUniformBufferRange which might be more efficient.
driver.bindUniformBuffer(+UniformBindingPoints::PER_RENDERABLE_MORPHING,
info.morphWeightBuffer);
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING,
info.morphTargetBuffer);
// note: even if only morphing is enabled, binding skinningTexture is needed.
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING,
info.skinningTexture);
}
driver.draw(pipeline, info.primitiveHandle, instanceCount);
}
}
driver.draw(pipeline, info.primitiveHandle, instanceCount);
// If the remaining space is less than half the capacity, we flush right away to
// allow some headroom for commands that might come later.
if (UTILS_UNLIKELY(circularBuffer.getUsed() > capacity / 2)) {
engine.flush();
}
}
+163 -102
View File
@@ -22,26 +22,38 @@
#include "details/Camera.h"
#include "details/Scene.h"
#include "backend/DriverApiForward.h"
#include <private/filament/Variant.h>
#include "private/filament/Variant.h"
#include "utils/BitmaskEnum.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/Allocator.h>
#include <utils/Range.h>
#include <utils/Slice.h>
#include <utils/architecture.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <math/mathfwd.h>
#include <functional>
#include <limits>
#include <optional>
#include <type_traits>
#include <tuple>
#include <vector>
#include <stddef.h>
#include <stdint.h>
namespace filament {
namespace backend {
class CommandBufferQueue;
}
class FMaterialInstance;
class RenderPassBuilder;
class RenderPass {
public:
@@ -171,7 +183,7 @@ public:
EPILOG = uint64_t(0x2) << CUSTOM_SHIFT
};
enum CommandTypeFlags : uint8_t {
enum class CommandTypeFlags : uint32_t {
COLOR = 0x1, // generate the color pass only
DEPTH = 0x2, // generate the depth pass only ( e.g. shadowmap)
@@ -191,7 +203,6 @@ public:
SCREEN_SPACE_REFLECTIONS = COLOR | FILTER_TRANSLUCENT_OBJECTS
};
/*
* The sorting material key is 32 bits and encoded as:
*
@@ -240,7 +251,6 @@ public:
uint32_t skinningOffset = 0; // 4 bytes
uint16_t instanceCount; // 2 bytes [MSb: user]
Variant materialVariant; // 1 byte
// uint8_t reserved[0] = {}; // 0 bytes
static const uint16_t USER_INSTANCE_MASK = 0x8000u;
static const uint16_t INSTANCE_COUNT_MASK = 0x7fffu;
@@ -253,7 +263,7 @@ public:
uint64_t reserved[1] = {}; // 8 bytes
bool operator < (Command const& rhs) const noexcept { return key < rhs.key; }
// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new (std::size_t, void* ptr) {
inline void* operator new (size_t, void* ptr) {
assert_invariant(ptr);
return ptr;
}
@@ -269,61 +279,31 @@ public:
// Arena used for commands
using Arena = utils::Arena<
utils::LinearAllocator, // note: can't change this allocator
utils::LinearAllocatorWithFallback,
utils::LockingPolicy::NoLock,
utils::TrackingPolicy::HighWatermark,
utils::AreaPolicy::StaticArea>;
/*
* Create a RenderPass.
* The Arena is used to allocate commands which are then owned by the Arena.
*/
RenderPass(FEngine& engine, Arena& arena) noexcept;
// RenderPass can only be moved
RenderPass(RenderPass&& rhs) = default;
// Copy the RenderPass as is. This can be used to create a RenderPass from a "template"
// by copying from an "empty" RenderPass.
RenderPass(RenderPass const& rhs);
// RenderPass can't be copied
RenderPass(RenderPass const& rhs) = delete;
RenderPass& operator=(RenderPass const& rhs) = delete;
RenderPass& operator=(RenderPass&& rhs) = delete;
// allocated commands ARE NOT freed, they're owned by the Arena
~RenderPass() noexcept;
// a box that both offsets the viewport and clips it
void setScissorViewport(backend::Viewport viewport) noexcept;
// specifies the geometry to generate commands for
void setGeometry(FScene::RenderableSoa const& soa, utils::Range<uint32_t> vr,
backend::Handle<backend::HwBufferObject> uboHandle) noexcept;
// specifies camera information (e.g. used for sorting commands)
void setCamera(const CameraInfo& camera) noexcept;
// flags controlling how commands are generated
void setRenderFlags(RenderFlags flags) noexcept { mFlags = flags; }
RenderFlags getRenderFlags() const noexcept { return mFlags; }
// variant to use
void setVariant(Variant variant) noexcept { mVariant = variant; }
// Sets the visibility mask, which is AND-ed against each Renderable's VISIBLE_MASK to determine
// if the renderable is visible for this pass.
// Defaults to all 1's, which means all renderables in this render pass will be rendered.
void setVisibilityMask(FScene::VisibleMaskType mask) noexcept { mVisibilityMask = mask; }
Command const* begin() const noexcept { return mCommandBegin; }
Command const* end() const noexcept { return mCommandEnd; }
bool empty() const noexcept { return begin() == end(); }
// This is the main function of this class, this appends commands to the pass using
// the current camera, geometry and flags set. This can be called multiple times if needed.
void appendCommands(FEngine& engine, CommandTypeFlags commandTypeFlags) noexcept;
// sorts and instanceify commands then trims sentinels
void sortCommands(FEngine& engine) noexcept;
// Helper to execute all the commands generated by this RenderPass
void execute(FEngine& engine, const char* name,
static void execute(RenderPass const& pass,
FEngine& engine, const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams params) const noexcept;
backend::RenderPassParams params) noexcept;
/*
* Executor holds the range of commands to execute for a given pass
@@ -331,6 +311,7 @@ public:
class Executor {
using CustomCommandFn = std::function<void()>;
friend class RenderPass;
friend class RenderPassBuilder;
// these fields are constant after creation
utils::Slice<Command> mCommands;
@@ -346,8 +327,7 @@ public:
Executor(RenderPass const* pass, Command const* b, Command const* e) noexcept;
void execute(backend::DriverApi& driver,
const Command* first, const Command* last) const noexcept;
void execute(FEngine& engine, const Command* first, const Command* last) const noexcept;
public:
Executor() = default;
@@ -366,37 +346,39 @@ public:
};
// returns a new executor for this pass
Executor getExecutor() {
return { this, mCommandBegin, mCommandEnd };
}
Executor getExecutor() const {
return { this, mCommandBegin, mCommandEnd };
}
// returns a new executor for this pass with a custom range
Executor getExecutor(Command const* b, Command const* e) {
return { this, b, e };
}
Executor getExecutor(Command const* b, Command const* e) const {
return { this, b, e };
}
// Appends a custom command.
void appendCustomCommand(uint8_t channel, Pass pass, CustomCommand custom, uint32_t order,
Executor::CustomCommandFn command);
private:
friend class FRenderer;
friend class RenderPassBuilder;
RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexcept;
Command* append(size_t count) noexcept;
void resize(size_t count) noexcept;
void instanceify(FEngine& engine) noexcept;
// This is the main function of this class, this appends commands to the pass using
// the current camera, geometry and flags set. This can be called multiple times if needed.
void appendCommands(FEngine& engine,
utils::Slice<Command> commands, CommandTypeFlags commandTypeFlags) noexcept;
// we choose the command count per job to minimize JobSystem overhead.
// on a Pixel 4, 2048 commands is about half a millisecond of processing.
// Appends a custom command.
void appendCustomCommand(Command* commands,
uint8_t channel, Pass pass, CustomCommand custom, uint32_t order,
Executor::CustomCommandFn command);
void resize(Arena& arena, size_t count) noexcept;
// sorts commands then trims sentinels
void sortCommands(Arena& arena) noexcept;
// instanceify commands then trims sentinels
void instanceify(FEngine& engine, Arena& arena) noexcept;
// We choose the command count per job to minimize JobSystem overhead.
// On a Pixel 4, 2048 commands is about half a millisecond of processing.
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_COUNT = 2048;
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_SIZE =
sizeof(Command) * JOBS_PARALLEL_FOR_COMMANDS_COUNT;
@@ -404,15 +386,15 @@ private:
static_assert(JOBS_PARALLEL_FOR_COMMANDS_SIZE % utils::CACHELINE_SIZE == 0,
"Size of Commands jobs must be multiple of a cache-line size");
static inline void generateCommands(uint32_t commandTypeFlags, Command* commands,
static inline void generateCommands(CommandTypeFlags commandTypeFlags, Command* commands,
FScene::RenderableSoa const& soa, utils::Range<uint32_t> range,
Variant variant, RenderFlags renderFlags,
FScene::VisibleMaskType visibilityMask,
math::float3 cameraPosition, math::float3 cameraForward,
uint8_t instancedStereoEyeCount) noexcept;
template<uint32_t commandTypeFlags>
static inline Command* generateCommandsImpl(uint32_t extraFlags, Command* curr,
template<RenderPass::CommandTypeFlags commandTypeFlags>
static inline Command* generateCommandsImpl(RenderPass::CommandTypeFlags extraFlags, Command* curr,
FScene::RenderableSoa const& soa, utils::Range<uint32_t> range,
Variant variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask,
math::float3 cameraPosition, math::float3 cameraForward,
@@ -424,50 +406,129 @@ private:
static void updateSummedPrimitiveCounts(
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
// a reference to the Engine, mostly to get to things like JobSystem
// Arena where all Commands are allocated. The Arena owns the commands.
Arena& mCommandArena;
FScene::RenderableSoa const& mRenderableSoa;
utils::Range<uint32_t> const mVisibleRenderables;
backend::Handle<backend::HwBufferObject> const mUboHandle;
math::float3 const mCameraPosition;
math::float3 const mCameraForwardVector;
RenderFlags const mFlags;
Variant const mVariant;
FScene::VisibleMaskType const mVisibilityMask;
backend::Viewport const mScissorViewport;
// Pointer to the first command
Command* mCommandBegin = nullptr;
// Pointer to one past the last command
Command* mCommandEnd = nullptr;
// the SOA containing the renderables we're interested in
FScene::RenderableSoa const* mRenderableSoa = nullptr;
// The range of visible renderables in the SOA above
utils::Range<uint32_t> mVisibleRenderables{};
// the UBO containing the data for the renderables
backend::Handle<backend::HwBufferObject> mUboHandle;
// a UBO for instanced primitives
backend::Handle<backend::HwBufferObject> mInstancedUboHandle;
// info about the camera
math::float3 mCameraPosition{};
math::float3 mCameraForwardVector{};
// info about the scene features (e.g.: has shadows, lighting, etc...)
RenderFlags mFlags{};
// Variant to use
Variant mVariant{};
// Additional visibility mask
FScene::VisibleMaskType mVisibilityMask = std::numeric_limits<FScene::VisibleMaskType>::max();
backend::Viewport mScissorViewport{ 0, 0,
std::numeric_limits<int32_t>::max(),
std::numeric_limits<int32_t>::max() };
// a vector for our custom commands
using CustomCommandVector = std::vector<Executor::CustomCommandFn,
utils::STLAllocator<Executor::CustomCommandFn, LinearAllocatorArena>>;
mutable CustomCommandVector mCustomCommands;
};
class RenderPassBuilder {
friend class RenderPass;
RenderPass::Arena& mArena;
RenderPass::CommandTypeFlags mCommandTypeFlags{};
backend::Viewport mScissorViewport{ 0, 0, INT32_MAX, INT32_MAX };
FScene::RenderableSoa const* mRenderableSoa = nullptr;
utils::Range<uint32_t> mVisibleRenderables{};
backend::Handle<backend::HwBufferObject> mUboHandle;
math::float3 mCameraPosition{};
math::float3 mCameraForwardVector{};
RenderPass::RenderFlags mFlags{};
Variant mVariant{};
FScene::VisibleMaskType mVisibilityMask = std::numeric_limits<FScene::VisibleMaskType>::max();
using CustomCommandRecord = std::tuple<
uint8_t,
RenderPass::Pass,
RenderPass::CustomCommand,
uint32_t,
RenderPass::Executor::CustomCommandFn>;
using CustomCommandContainer = std::vector<CustomCommandRecord,
utils::STLAllocator<CustomCommandRecord, LinearAllocatorArena>>;
// we make this optional because it's not used often, and we don't want to have
// to construct it by default.
std::optional<CustomCommandContainer> mCustomCommands;
public:
explicit RenderPassBuilder(RenderPass::Arena& arena) : mArena(arena) { }
RenderPassBuilder& commandTypeFlags(RenderPass::CommandTypeFlags commandTypeFlags) noexcept {
mCommandTypeFlags = commandTypeFlags;
return *this;
}
RenderPassBuilder& scissorViewport(backend::Viewport viewport) noexcept {
mScissorViewport = viewport;
return *this;
}
// specifies the geometry to generate commands for
RenderPassBuilder& geometry(FScene::RenderableSoa const& soa, utils::Range<uint32_t> vr,
backend::Handle<backend::HwBufferObject> uboHandle) noexcept {
mRenderableSoa = &soa;
mVisibleRenderables = vr;
mUboHandle = uboHandle;
return *this;
}
// Specifies camera information (e.g. used for sorting commands)
RenderPassBuilder& camera(const CameraInfo& camera) noexcept {
mCameraPosition = camera.getPosition();
mCameraForwardVector = camera.getForwardVector();
return *this;
}
// flags controlling how commands are generated
RenderPassBuilder& renderFlags(RenderPass::RenderFlags flags) noexcept {
mFlags = flags;
return *this;
}
// like above but allows to set specific flags
RenderPassBuilder& renderFlags(
RenderPass::RenderFlags mask, RenderPass::RenderFlags value) noexcept {
mFlags = (mFlags & mask) | (value & mask);
return *this;
}
// variant to use
RenderPassBuilder& variant(Variant variant) noexcept {
mVariant = variant;
return *this;
}
// Sets the visibility mask, which is AND-ed against each Renderable's VISIBLE_MASK to
// determine if the renderable is visible for this pass.
// Defaults to all 1's, which means all renderables in this render pass will be rendered.
RenderPassBuilder& visibilityMask(FScene::VisibleMaskType mask) noexcept {
mVisibilityMask = mask;
return *this;
}
RenderPassBuilder& customCommand(FEngine& engine,
uint8_t channel,
RenderPass::Pass pass,
RenderPass::CustomCommand custom,
uint32_t order,
const RenderPass::Executor::CustomCommandFn& command);
RenderPass build(FEngine& engine);
};
} // namespace filament
template<> struct utils::EnableBitMaskOperators<filament::RenderPass::CommandTypeFlags>
: public std::true_type {};
#endif // TNT_FILAMENT_RENDERPASS_H
-4
View File
@@ -228,10 +228,6 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
out.params.subpassMask = 1;
}
// this is a good time to flush the CommandStream, because we're about to potentially
// output a lot of commands. This guarantees here that we have at least
// FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB bytes (1MiB by default).
engine.flush();
driver.beginRenderPass(out.target, out.params);
passExecutor.execute(engine, resources.getPassName());
driver.endRenderPass();
+31 -15
View File
@@ -16,15 +16,30 @@
#include "ResourceAllocator.h"
#include "private/backend/DriverApi.h"
#include <filament/Engine.h>
#include "details/Texture.h"
#include <backend/DriverApiForward.h>
#include <backend/Handle.h>
#include <backend/TargetBufferInfo.h>
#include <backend/DriverEnums.h>
#include "private/backend/DriverApi.h"
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <array>
#include <algorithm>
#include <iterator>
#include <utility>
#include <stddef.h>
#include <stdint.h>
using namespace utils;
@@ -42,8 +57,7 @@ ResourceAllocator::AssociativeContainer<K, V, H>::AssociativeContainer() {
template<typename K, typename V, typename H>
UTILS_NOINLINE
ResourceAllocator::AssociativeContainer<K, V, H>::~AssociativeContainer() noexcept {
}
ResourceAllocator::AssociativeContainer<K, V, H>::~AssociativeContainer() noexcept = default;
template<typename K, typename V, typename H>
UTILS_NOINLINE
@@ -78,9 +92,9 @@ void ResourceAllocator::AssociativeContainer<K, V, H>::emplace(ARGS&& ... args)
ResourceAllocatorInterface::~ResourceAllocatorInterface() = default;
size_t ResourceAllocator::TextureKey::getSize() const noexcept {
size_t pixelCount = width * height * depth;
size_t const pixelCount = width * height * depth;
size_t size = pixelCount * FTexture::getFormatSize(format);
size_t s = std::max(uint8_t(1), samples);
size_t const s = std::max(uint8_t(1), samples);
if (s > 1) {
// if we have MSAA, we assume N times the storage
size *= s;
@@ -94,8 +108,10 @@ size_t ResourceAllocator::TextureKey::getSize() const noexcept {
return size;
}
ResourceAllocator::ResourceAllocator(DriverApi& driverApi) noexcept
: mBackend(driverApi) {
ResourceAllocator::ResourceAllocator(Engine::Config const& config, DriverApi& driverApi) noexcept
: mCacheCapacity(config.resourceAllocatorCacheSizeMB),
mCacheMaxAge(config.resourceAllocatorCacheMaxAge),
mBackend(driverApi) {
}
ResourceAllocator::~ResourceAllocator() noexcept {
@@ -112,7 +128,7 @@ void ResourceAllocator::terminate() noexcept {
}
}
RenderTargetHandle ResourceAllocator::createRenderTarget(const char* name,
RenderTargetHandle ResourceAllocator::createRenderTarget(const char*,
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
uint8_t samples, MRT color, TargetBufferInfo depth,
TargetBufferInfo stencil) noexcept {
@@ -181,7 +197,7 @@ void ResourceAllocator::destroyTexture(TextureHandle h) noexcept {
// move it to the cache
const TextureKey key = it->second;
uint32_t size = key.getSize();
uint32_t const size = key.getSize();
mTextureCache.emplace(key, TextureCachePayload{ h, mAge, size });
mCacheSize += size;
@@ -208,9 +224,9 @@ void ResourceAllocator::gc() noexcept {
auto& textureCache = mTextureCache;
for (auto it = textureCache.begin(); it != textureCache.end();) {
const size_t ageDiff = age - it->second.age;
if (ageDiff >= CACHE_MAX_AGE) {
if (ageDiff >= mCacheMaxAge) {
it = purge(it);
if (mCacheSize < CACHE_CAPACITY) {
if (mCacheSize < mCacheCapacity) {
// if we're not at capacity, only purge a single entry per gc, trying to
// avoid a burst of work.
break;
@@ -220,7 +236,7 @@ void ResourceAllocator::gc() noexcept {
}
}
if (UTILS_UNLIKELY(mCacheSize >= CACHE_CAPACITY)) {
if (UTILS_UNLIKELY(mCacheSize >= mCacheCapacity)) {
// make a copy of our CacheContainer to a vector
using Vector = FixedCapacityVector<std::pair<TextureKey, TextureCachePayload>>;
auto cache = Vector::with_capacity(textureCache.size());
@@ -233,14 +249,14 @@ void ResourceAllocator::gc() noexcept {
// now remove entries until we're at capacity
auto curr = cache.begin();
while (mCacheSize >= CACHE_CAPACITY) {
while (mCacheSize >= mCacheCapacity) {
// by construction this entry must exist
purge(textureCache.find(curr->first));
++curr;
}
// Since we're sorted already, reset the oldestAge of the whole system
size_t oldestAge = cache.front().second.age;
size_t const oldestAge = cache.front().second.age;
for (auto& it : textureCache) {
it.second.age -= oldestAge;
}
+8 -4
View File
@@ -17,6 +17,8 @@
#ifndef TNT_FILAMENT_RESOURCEALLOCATOR_H
#define TNT_FILAMENT_RESOURCEALLOCATOR_H
#include <filament/Engine.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/TargetBufferInfo.h>
@@ -27,7 +29,9 @@
#include <array>
#include <vector>
#include <utility>
#include <stddef.h>
#include <stdint.h>
namespace filament {
@@ -62,7 +66,8 @@ protected:
class ResourceAllocator final : public ResourceAllocatorInterface {
public:
explicit ResourceAllocator(backend::DriverApi& driverApi) noexcept;
explicit ResourceAllocator(
Engine::Config const& config, backend::DriverApi& driverApi) noexcept;
~ResourceAllocator() noexcept override;
void terminate() noexcept;
@@ -89,9 +94,8 @@ public:
void gc() noexcept;
private:
// TODO: these should be settings of the engine
static constexpr size_t CACHE_CAPACITY = 64u << 20u; // 64 MiB
static constexpr size_t CACHE_MAX_AGE = 30u;
size_t const mCacheCapacity;
size_t const mCacheMaxAge;
struct TextureKey {
const char* name; // doesn't participate in the hash
+1 -1
View File
@@ -163,7 +163,7 @@ public:
FCamera const& getCamera() const noexcept { return *mCamera; }
// use only for debugging
FCamera const& getDebugCamera() const noexcept { return *mDebugCamera; }
FCamera const* getDebugCamera() const noexcept { return mDebugCamera; }
// Update SceneInfo struct for a given light
static void updateSceneInfoDirectional(const math::mat4f& Mv, FScene const& scene,
+20 -12
View File
@@ -19,14 +19,21 @@
#include "RenderPass.h"
#include "ShadowMap.h"
#include "details/DebugRegistry.h"
#include "details/Texture.h"
#include "details/View.h"
#include <fg/FrameGraph.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <new>
#include <memory>
namespace filament {
using namespace backend;
@@ -128,7 +135,8 @@ void ShadowMapManager::addShadowMap(size_t lightIndex, bool spotlight,
}
FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameGraph& fg,
RenderPass const& pass, FView& view, CameraInfo const& mainCameraInfo,
RenderPassBuilder const& passBuilder,
FView& view, CameraInfo const& mainCameraInfo,
float4 const& userTime) noexcept {
const float moment2 = std::numeric_limits<half>::max();
@@ -206,8 +214,8 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
builder.sideEffect();
},
[this, &engine, &view, vsmShadowOptions,
scene, mainCameraInfo, userTime, passTemplate = pass](
FrameGraphResources const&, auto const& data, DriverApi& driver) {
scene, mainCameraInfo, userTime, passBuilder = passBuilder](
FrameGraphResources const&, auto const& data, DriverApi& driver) mutable {
// Note: we could almost parallel_for the loop below, the problem currently is
// that updatePrimitivesLod() updates temporary global state.
@@ -262,19 +270,20 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
cameraInfo, scene->getRenderableData(), entry.range);
// generate and sort the commands for rendering the shadow map
RenderPass pass(passTemplate);
pass.setCamera(cameraInfo);
pass.setVisibilityMask(entry.visibilityMask);
pass.setGeometry(scene->getRenderableData(),
entry.range, scene->getRenderableUBO());
pass.appendCommands(engine, RenderPass::SHADOW);
pass.sortCommands(engine);
RenderPass const pass = passBuilder
.camera(cameraInfo)
.visibilityMask(entry.visibilityMask)
.geometry(scene->getRenderableData(),
entry.range, scene->getRenderableUBO())
.commandTypeFlags(RenderPass::CommandTypeFlags::SHADOW)
.build(engine);
entry.executor = pass.getExecutor();
if (!view.hasVSM()) {
auto const* options = shadowMap.getShadowOptions();
const PolygonOffset polygonOffset = { // handle reversed Z
PolygonOffset const polygonOffset = { // handle reversed Z
.slope = -options->polygonOffsetSlope,
.constant = -options->polygonOffsetConstant
};
@@ -395,7 +404,6 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
auto rt = resources.getRenderPassInfo(data.rt);
engine.flush();
driver.beginRenderPass(rt.target, rt.params);
entry.shadowMap->bind(driver);
entry.executor.overrideScissor(entry.shadowMap->getScissor());
+4 -2
View File
@@ -43,6 +43,7 @@ namespace filament {
class FView;
class FrameGraph;
class RenderPass;
class RenderPassBuilder;
struct ShadowMappingUniforms {
math::float4 cascadeSplits;
@@ -86,7 +87,8 @@ public:
FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData) noexcept;
// Renders all the shadow maps.
FrameGraphId<FrameGraphTexture> render(FEngine& engine, FrameGraph& fg, RenderPass const& pass,
FrameGraphId<FrameGraphTexture> render(FEngine& engine, FrameGraph& fg,
RenderPassBuilder const& passBuilder,
FView& view, CameraInfo const& mainCameraInfo, math::float4 const& userTime) noexcept;
// valid after calling update() above
@@ -100,7 +102,7 @@ public:
// for debugging only
FCamera const* getDirectionalLightCamera() const noexcept {
return &getShadowMap(0).getDebugCamera();
return getShadowMap(0).getDebugCamera();
}
private:
+2 -1
View File
@@ -28,7 +28,8 @@ void Stream::setAcquiredImage(void* image, Callback callback, void* userdata) no
downcast(this)->setAcquiredImage(image, callback, userdata);
}
void Stream::setAcquiredImage(void* image, backend::CallbackHandler* handler, Callback callback, void* userdata) noexcept {
void Stream::setAcquiredImage(void* image,
backend::CallbackHandler* handler, Callback callback, void* userdata) noexcept {
downcast(this)->setAcquiredImage(image, handler, callback, userdata);
}
+4
View File
@@ -34,6 +34,10 @@ class FEngine;
class FDebugRegistry : public DebugRegistry {
public:
enum Type {
BOOL, INT, FLOAT, FLOAT2, FLOAT3, FLOAT4
};
FDebugRegistry() noexcept;
void registerProperty(std::string_view name, bool* p) noexcept {
+11 -9
View File
@@ -198,7 +198,7 @@ FEngine::FEngine(Engine::Builder const& builder) :
mCommandBufferQueue(
builder->mConfig.minCommandBufferSizeMB * MiB,
builder->mConfig.commandBufferSizeMB * MiB),
mPerRenderPassAllocator(
mPerRenderPassArena(
"FEngine::mPerRenderPassAllocator",
builder->mConfig.perRenderPassArenaSizeMB * MiB),
mHeapAllocator("FEngine::mHeapAllocator", AreaPolicy::NullArea{}),
@@ -252,7 +252,7 @@ void FEngine::init() {
slog.i << "FEngine feature level: " << int(mActiveFeatureLevel) << io::endl;
mResourceAllocator = new ResourceAllocator(driverApi);
mResourceAllocator = new ResourceAllocator(mConfig, driverApi);
mFullScreenTriangleVb = downcast(VertexBuffer::Builder()
.vertexCount(3)
@@ -709,7 +709,9 @@ const FMaterial* FEngine::getSkyboxMaterial() const noexcept {
template<typename T>
inline T* FEngine::create(ResourceList<T>& list, typename T::Builder const& builder) noexcept {
T* p = mHeapAllocator.make<T>(*this, builder);
list.insert(p);
if (UTILS_UNLIKELY(p)) { // this should never happen
list.insert(p);
}
return p;
}
@@ -771,7 +773,7 @@ FRenderTarget* FEngine::createRenderTarget(const RenderTarget::Builder& builder)
FRenderer* FEngine::createRenderer() noexcept {
FRenderer* p = mHeapAllocator.make<FRenderer>(*this);
if (p) {
if (UTILS_UNLIKELY(p)) { // should never happen
mRenderers.insert(p);
}
return p;
@@ -780,7 +782,7 @@ FRenderer* FEngine::createRenderer() noexcept {
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
const FMaterialInstance* other, const char* name) noexcept {
FMaterialInstance* p = mHeapAllocator.make<FMaterialInstance>(*this, other, name);
if (p) {
if (UTILS_UNLIKELY(p)) { // should never happen
auto pos = mMaterialInstances.emplace(material, "MaterialInstance");
pos.first->second.insert(p);
}
@@ -793,7 +795,7 @@ FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
FScene* FEngine::createScene() noexcept {
FScene* p = mHeapAllocator.make<FScene>(*this);
if (p) {
if (UTILS_UNLIKELY(p)) { // should never happen
mScenes.insert(p);
}
return p;
@@ -801,7 +803,7 @@ FScene* FEngine::createScene() noexcept {
FView* FEngine::createView() noexcept {
FView* p = mHeapAllocator.make<FView>(*this);
if (p) {
if (UTILS_UNLIKELY(p)) { // should never happen
mViews.insert(p);
}
return p;
@@ -809,7 +811,7 @@ FView* FEngine::createView() noexcept {
FFence* FEngine::createFence() noexcept {
FFence* p = mHeapAllocator.make<FFence>(*this);
if (p) {
if (UTILS_UNLIKELY(p)) { // should never happen
std::lock_guard const guard(mFenceListLock);
mFences.insert(p);
}
@@ -825,7 +827,7 @@ FSwapChain* FEngine::createSwapChain(void* nativeWindow, uint64_t flags) noexcep
getDriverApi().setupExternalImage(nativeWindow);
}
FSwapChain* p = mHeapAllocator.make<FSwapChain>(*this, nativeWindow, flags);
if (p) {
if (UTILS_UNLIKELY(p)) { // should never happen
mSwapChains.insert(p);
}
return p;
+13 -13
View File
@@ -58,17 +58,6 @@
#include <filament/Texture.h>
#include <filament/VertexBuffer.h>
#if FILAMENT_ENABLE_MATDBG
#include <matdbg/DebugServer.h>
#else
namespace filament {
namespace matdbg {
class DebugServer;
using MaterialKey = uint32_t;
} // namespace matdbg
} // namespace filament
#endif
#include <utils/compiler.h>
#include <utils/Allocator.h>
#include <utils/JobSystem.h>
@@ -78,8 +67,19 @@ using MaterialKey = uint32_t;
#include <memory>
#include <new>
#include <random>
#include <thread>
#include <type_traits>
#include <unordered_map>
#if FILAMENT_ENABLE_MATDBG
#include <matdbg/DebugServer.h>
#else
namespace filament::matdbg {
class DebugServer;
using MaterialKey = uint32_t;
} // namespace filament::matdbg
#endif
namespace filament {
class Renderer;
@@ -142,7 +142,7 @@ public:
// the per-frame Area is used by all Renderer, so they must run in sequence and
// have freed all allocated memory when done. If this needs to change in the future,
// we'll simply have to use separate Areas (for instance).
LinearAllocatorArena& getPerRenderPassAllocator() noexcept { return mPerRenderPassAllocator; }
LinearAllocatorArena& getPerRenderPassArena() noexcept { return mPerRenderPassArena; }
// Material IDs...
uint32_t getMaterialId() const noexcept { return mMaterialId++; }
@@ -508,7 +508,7 @@ private:
uint32_t mFlushCounter = 0;
LinearAllocatorArena mPerRenderPassAllocator;
RootArenaScope::Arena mPerRenderPassArena;
HeapAllocatorArena mHeapAllocator;
utils::JobSystem mJobSystem;
+1 -1
View File
@@ -269,7 +269,7 @@ const char* FMaterialInstance::getName() const noexcept {
// the instance's CString rather than calling empty(). This allows instances to override the
// parent material's name with a blank string.
if (mName.data() == nullptr) {
return mMaterial->getName().c_str();
return mMaterial->getName().c_str_safe();
}
return mName.c_str();
}
+58 -35
View File
@@ -16,6 +16,9 @@
#include "details/Renderer.h"
#include "Allocators.h"
#include "DebugRegistry.h"
#include "FrameHistory.h"
#include "PostProcessManager.h"
#include "RendererUtils.h"
#include "RenderPass.h"
@@ -28,21 +31,40 @@
#include "details/Texture.h"
#include "details/View.h"
#include <filament/Camera.h>
#include <filament/Fence.h>
#include <filament/Options.h>
#include <filament/Renderer.h>
#include <backend/DriverEnums.h>
#include <backend/DriverApiForward.h>
#include <backend/Handle.h>
#include <backend/PixelBufferDescriptor.h>
#include "fg/FrameGraph.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphResources.h"
#include "fg/FrameGraphTexture.h"
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/mat4.h>
#include <utils/compiler.h>
#include <utils/JobSystem.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <utils/Systrace.h>
#include <utils/vector.h>
#include <utils/debug.h>
#include <chrono>
#include <limits>
#include <utility>
#include <stddef.h>
#include <stdint.h>
// this helps visualize what dynamic-scaling is doing
#define DEBUG_DYNAMIC_SCALING false
@@ -62,8 +84,7 @@ FRenderer::FRenderer(FEngine& engine) :
mHdrQualityMedium(TextureFormat::R11F_G11F_B10F),
mHdrQualityHigh(TextureFormat::RGB16F),
mIsRGB8Supported(false),
mUserEpoch(engine.getEngineEpoch()),
mPerRenderPassArena(engine.getPerRenderPassAllocator())
mUserEpoch(engine.getEngineEpoch())
{
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.renderer.doFrameCapture",
@@ -442,7 +463,7 @@ void FRenderer::render(FView const* view) {
if (UTILS_LIKELY(view && view->getScene())) {
if (mViewRenderedCount) {
// this is a good place to kick the GPU, since we've rendered a View before,
// This is a good place to kick the GPU, since we've rendered a View before,
// and we're about to render another one.
mEngine.getDriverApi().flush();
}
@@ -452,17 +473,17 @@ void FRenderer::render(FView const* view) {
}
void FRenderer::renderInternal(FView const* view) {
// per-renderpass data
ArenaScope rootArena(mPerRenderPassArena);
FEngine& engine = mEngine;
JobSystem& js = engine.getJobSystem();
// per-renderpass data
RootArenaScope rootArenaScope(engine.getPerRenderPassArena());
// create a root job so no other job can escape
JobSystem& js = engine.getJobSystem();
auto *rootJob = js.setRootJob(js.createJob());
// execute the render pass
renderJob(rootArena, const_cast<FView&>(*view));
renderJob(rootArenaScope, const_cast<FView&>(*view));
// make sure to flush the command buffer
engine.flush();
@@ -471,7 +492,7 @@ void FRenderer::renderInternal(FView const* view) {
js.runAndWait(rootJob);
}
void FRenderer::renderJob(ArenaScope& arena, FView& view) {
void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
FEngine& engine = mEngine;
JobSystem& js = engine.getJobSystem();
FEngine::DriverApi& driver = engine.getDriverApi();
@@ -636,7 +657,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
xvp.bottom = int32_t(guardBand);
}
view.prepare(engine, driver, arena, svp, cameraInfo, getShaderUserTime(), needsAlphaChannel);
view.prepare(engine, driver, rootArenaScope, svp, cameraInfo, getShaderUserTime(), needsAlphaChannel);
view.prepareUpscaler(scale, taaOptions, dsrOptions);
@@ -649,8 +670,10 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// Allocate some space for our commands in the per-frame Arena, and use that space as
// an Arena for commands. All this space is released when we exit this method.
size_t const perFrameCommandsSize = engine.getPerFrameCommandsSize();
void* const arenaBegin = arena.allocate(perFrameCommandsSize, CACHELINE_SIZE);
void* const arenaBegin = rootArenaScope.allocate(perFrameCommandsSize, CACHELINE_SIZE);
void* const arenaEnd = pointermath::add(arenaBegin, perFrameCommandsSize);
// This arena *must* stay valid until all commands have been processed
RenderPass::Arena commandArena("Command Arena", { arenaBegin, arenaEnd });
RenderPass::RenderFlags renderFlags = 0;
@@ -658,8 +681,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
if (view.isFrontFaceWindingInverted()) renderFlags |= RenderPass::HAS_INVERSE_FRONT_FACES;
if (view.hasInstancedStereo()) renderFlags |= RenderPass::IS_STEREOSCOPIC;
RenderPass pass(engine, commandArena);
pass.setRenderFlags(renderFlags);
RenderPassBuilder passBuilder(commandArena);
passBuilder.renderFlags(renderFlags);
Variant variant;
variant.setDirectionalLighting(view.hasDirectionalLight());
@@ -682,10 +705,10 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
if (view.needsShadowMap()) {
Variant shadowVariant(Variant::DEPTH_VARIANT);
shadowVariant.setVsm(view.getShadowType() == ShadowType::VSM);
RenderPass shadowPass(pass);
shadowPass.setVariant(shadowVariant);
auto shadows = view.renderShadowMaps(engine, fg, cameraInfo, mShaderUserTime, shadowPass);
auto shadows = view.renderShadowMaps(engine, fg, cameraInfo, mShaderUserTime,
RenderPassBuilder{ commandArena }
.renderFlags(renderFlags)
.variant(shadowVariant));
blackboard["shadows"] = shadows;
}
@@ -771,8 +794,9 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
view.updatePrimitivesLod(engine, cameraInfo,
scene.getRenderableData(), view.getVisibleRenderables());
pass.setCamera(cameraInfo);
pass.setGeometry(scene.getRenderableData(), view.getVisibleRenderables(), scene.getRenderableUBO());
passBuilder.camera(cameraInfo);
passBuilder.geometry(scene.getRenderableData(),
view.getVisibleRenderables(), scene.getRenderableUBO());
// view set-ups that need to happen before rendering
fg.addTrivialSideEffectPass("Prepare View Uniforms",
@@ -818,7 +842,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// This is normally used by SSAO and contact-shadows
// TODO: the scaling should depends on all passes that need the structure pass
const auto [structure, picking_] = ppm.structure(fg, pass, renderFlags, svp.width, svp.height, {
const auto [structure, picking_] = ppm.structure(fg,
passBuilder, renderFlags, svp.width, svp.height, {
.scale = aoOptions.resolution,
.picking = view.hasPicking()
});
@@ -876,7 +901,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// screen-space reflections pass
if (ssReflectionsOptions.enabled) {
auto reflections = ppm.ssr(fg, pass,
auto reflections = ppm.ssr(fg, passBuilder,
view.getFrameHistory(), cameraInfo,
view.getPerViewUniforms(),
structure,
@@ -894,10 +919,15 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// --------------------------------------------------------------------------------------------
// Color passes
// this makes the viewport relative to xvp
// FIXME: we should use 'vp' when rendering directly into the swapchain, but that's hard to
// know at this point. This will usually be the case when post-process is disabled.
// FIXME: we probably should take the dynamic scaling into account too
passBuilder.scissorViewport(hasPostProcess ? xvp : vp);
// This one doesn't need to be a FrameGraph pass because it always happens by construction
// (i.e. it won't be culled, unless everything is culled), so no need to complexify things.
pass.setVariant(variant);
pass.appendCommands(engine, RenderPass::COLOR);
passBuilder.variant(variant);
// color-grading as subpass is done either by the color pass or the TAA pass if any
auto colorGradingConfigForColor = colorGradingConfig;
@@ -905,7 +935,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
if (colorGradingConfigForColor.asSubpass) {
// append color grading subpass after all other passes
pass.appendCustomCommand(3,
passBuilder.customCommand(engine, 3,
RenderPass::Pass::BLENDED,
RenderPass::CustomCommand::EPILOG,
0, [&ppm, &driver, colorGradingConfigForColor]() {
@@ -913,7 +943,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
});
} else if (colorGradingConfig.customResolve) {
// append custom resolve subpass after all other passes
pass.appendCustomCommand(3,
passBuilder.customCommand(engine, 3,
RenderPass::Pass::BLENDED,
RenderPass::CustomCommand::EPILOG,
0, [&ppm, &driver]() {
@@ -921,16 +951,9 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
});
}
// sort commands once we're done adding commands
pass.sortCommands(engine);
// this makes the viewport relative to xvp
// FIXME: we should use 'vp' when rendering directly into the swapchain, but that's hard to
// know at this point. This will usually be the case when post-process is disabled.
// FIXME: we probably should take the dynamic scaling into account too
pass.setScissorViewport(hasPostProcess ? xvp : vp);
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::COLOR);
RenderPass const pass{ passBuilder.build(engine) };
FrameGraphTexture::Descriptor const desc = {
.width = config.physicalViewport.width,
+1 -4
View File
@@ -163,7 +163,7 @@ private:
}
void renderInternal(FView const* view);
void renderJob(ArenaScope& arena, FView& view);
void renderJob(RootArenaScope& rootArenaScope, FView& view);
// keep a reference to our engine
FEngine& mEngine;
@@ -187,9 +187,6 @@ private:
backend::TargetBufferFlags mClearFlags{};
tsl::robin_set<FRenderTarget*> mPreviousRenderTargets;
std::function<void()> mBeginFrameInternal;
// per-frame arena for this Renderer
LinearAllocatorArena& mPerRenderPassArena;
};
FILAMENT_DOWNCAST(Renderer)
+5 -5
View File
@@ -53,7 +53,7 @@ FScene::~FScene() noexcept = default;
void FScene::prepare(utils::JobSystem& js,
LinearAllocatorArena& allocator,
RootArenaScope& rootArenaScope,
mat4 const& worldTransform,
bool shadowReceiversAreCasters) noexcept {
// TODO: can we skip this in most cases? Since we rely on indices staying the same,
@@ -64,7 +64,7 @@ void FScene::prepare(utils::JobSystem& js,
SYSTRACE_CONTEXT();
// This will reset the allocator upon exiting
ArenaScope const arena(allocator);
ArenaScope<RootArenaScope::Arena> localArenaScope(rootArenaScope.getArena());
FEngine& engine = mEngine;
EntityManager const& em = engine.getEntityManager();
@@ -85,10 +85,10 @@ void FScene::prepare(utils::JobSystem& js,
utils::STLAllocator< LightContainerData, LinearAllocatorArena >, false>;
RenderableInstanceContainer renderableInstances{
RenderableInstanceContainer::with_capacity(entities.size(), allocator) };
RenderableInstanceContainer::with_capacity(entities.size(), localArenaScope.getArena()) };
LightInstanceContainer lightInstances{
LightInstanceContainer::with_capacity(entities.size(), allocator) };
LightInstanceContainer::with_capacity(entities.size(), localArenaScope.getArena()) };
SYSTRACE_NAME_BEGIN("InstanceLoop");
@@ -454,7 +454,7 @@ void FScene::terminate(FEngine&) {
mRenderableViewUbh.clear();
}
void FScene::prepareDynamicLights(const CameraInfo& camera, ArenaScope&,
void FScene::prepareDynamicLights(const CameraInfo& camera,
Handle<HwBufferObject> lightUbh) noexcept {
FEngine::DriverApi& driver = mEngine.getDriverApi();
FLightManager const& lcm = mEngine.getLightManager();
+4 -2
View File
@@ -31,6 +31,8 @@
#include <filament/Box.h>
#include <filament/Scene.h>
#include <math/mathfwd.h>
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/Slice.h>
@@ -70,12 +72,12 @@ public:
~FScene() noexcept;
void terminate(FEngine& engine);
void prepare(utils::JobSystem& js, LinearAllocatorArena& allocator,
void prepare(utils::JobSystem& js, RootArenaScope& rootArenaScope,
math::mat4 const& worldTransform, bool shadowReceiversAreCasters) noexcept;
void prepareVisibleRenderables(utils::Range<uint32_t> visibleRenderables) noexcept;
void prepareDynamicLights(const CameraInfo& camera, ArenaScope& arena,
void prepareDynamicLights(const CameraInfo& camera,
backend::Handle<backend::HwBufferObject> lightUbh) noexcept;
backend::Handle<backend::HwBufferObject> getRenderableUBO() const noexcept {
+4 -2
View File
@@ -86,11 +86,13 @@ void FStream::terminate(FEngine& engine) noexcept {
engine.getDriverApi().destroyStream(mStreamHandle);
}
void FStream::setAcquiredImage(void* image, Callback callback, void* userdata) noexcept {
void FStream::setAcquiredImage(void* image,
Callback callback, void* userdata) noexcept {
mEngine.getDriverApi().setAcquiredImage(mStreamHandle, image, nullptr, callback, userdata);
}
void FStream::setAcquiredImage(void* image, CallbackHandler* handler, Callback callback, void* userdata) noexcept {
void FStream::setAcquiredImage(void* image,
CallbackHandler* handler, Callback callback, void* userdata) noexcept {
mEngine.getDriverApi().setAcquiredImage(mStreamHandle, image, handler, callback, userdata);
}
+29 -23
View File
@@ -341,8 +341,7 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
mNeedsShadowMap = any(shadowTechnique & ShadowMapManager::ShadowTechnique::SHADOW_MAP);
}
void FView::prepareLighting(FEngine& engine, ArenaScope& arena,
CameraInfo const& cameraInfo) noexcept {
void FView::prepareLighting(FEngine& engine, CameraInfo const& cameraInfo) noexcept {
SYSTRACE_CALL();
SYSTRACE_CONTEXT();
@@ -354,7 +353,7 @@ void FView::prepareLighting(FEngine& engine, ArenaScope& arena,
*/
if (hasDynamicLighting()) {
scene->prepareDynamicLights(cameraInfo, arena, mLightUbh);
scene->prepareDynamicLights(cameraInfo, mLightUbh);
}
// here the array of visible lights has been shrunk to CONFIG_MAX_LIGHT_COUNT
@@ -427,7 +426,7 @@ CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept {
return { *camera, mat4{ rotation } * mat4::translation(translation) };
}
void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootArenaScope,
filament::Viewport viewport, CameraInfo cameraInfo,
float4 const& userTime, bool needsAlphaChannel) noexcept {
@@ -465,7 +464,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
* Gather all information needed to render this scene. Apply the world origin to all
* objects in the scene.
*/
scene->prepare(js, arena.getAllocator(),
scene->prepare(js, rootArenaScope,
cameraInfo.worldTransform,
hasVSM());
@@ -475,14 +474,22 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
JobSystem::Job* froxelizeLightsJob = nullptr;
JobSystem::Job* prepareVisibleLightsJob = nullptr;
if (scene->getLightData().size() > FScene::DIRECTIONAL_LIGHTS_COUNT) {
size_t const lightCount = scene->getLightData().size();
if (lightCount > FScene::DIRECTIONAL_LIGHTS_COUNT) {
// create and start the prepareVisibleLights job
// note: this job updates LightData (non const)
// allocate a scratch buffer for distances outside the job below, so we don't need
// to use a locked allocator; the downside is that we need to account for the worst case.
size_t const positionalLightCount = lightCount - FScene::DIRECTIONAL_LIGHTS_COUNT;
float* const distances = rootArenaScope.allocate<float>(
(positionalLightCount + 3u) & ~3u, CACHELINE_SIZE);
prepareVisibleLightsJob = js.runAndRetain(js.createJob(nullptr,
[&engine, &arena, &viewMatrix = cameraInfo.view, &cullingFrustum,
[&engine, distances, positionalLightCount, &viewMatrix = cameraInfo.view, &cullingFrustum,
&lightData = scene->getLightData()]
(JobSystem&, JobSystem::Job*) {
FView::prepareVisibleLights(engine.getLightManager(), arena,
FView::prepareVisibleLights(engine.getLightManager(),
{ distances, distances + positionalLightCount },
viewMatrix, cullingFrustum, lightData);
}));
}
@@ -530,7 +537,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
// As soon as prepareVisibleLight finishes, we can kick-off the froxelization
if (hasDynamicLighting()) {
auto& froxelizer = mFroxelizer;
if (froxelizer.prepare(driver, arena, viewport,
if (froxelizer.prepare(driver, rootArenaScope, viewport,
cameraInfo.projection, cameraInfo.zn, cameraInfo.zf)) {
// TODO: might be more consistent to do this in prepareLighting(), but it's not
// strictly necessary
@@ -645,7 +652,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
* Relies on FScene::prepare() and prepareVisibleLights()
*/
prepareLighting(engine, arena, cameraInfo);
prepareLighting(engine, cameraInfo);
/*
* Update driver state
@@ -850,7 +857,8 @@ void FView::cullRenderables(JobSystem&,
functor(0, renderableData.size());
}
void FView::prepareVisibleLights(FLightManager const& lcm, ArenaScope& rootArena,
void FView::prepareVisibleLights(FLightManager const& lcm,
utils::Slice<float> scratch,
mat4f const& viewMatrix, Frustum const& frustum,
FScene::LightSoa& lightData) noexcept {
SYSTRACE_CALL();
@@ -918,28 +926,25 @@ void FView::prepareVisibleLights(FLightManager const& lcm, ArenaScope& rootArena
* - This helps our limited numbers of spot-shadow as well.
*/
ArenaScope arena(rootArena.getAllocator());
size_t const size = visibleLightCount;
// number of point/spotlights
size_t const positionalLightCount = size - FScene::DIRECTIONAL_LIGHTS_COUNT;
size_t const positionalLightCount = visibleLightCount - FScene::DIRECTIONAL_LIGHTS_COUNT;
if (positionalLightCount) {
// always allocate at least 4 entries, because the vectorized loops below rely on that
float* const UTILS_RESTRICT distances =
arena.allocate<float>((size + 3u) & ~3u, CACHELINE_SIZE);
assert_invariant(positionalLightCount <= scratch.size());
// pre-compute the lights' distance to the camera, for sorting below
// - we don't skip the directional light, because we don't care, it's ignored during sorting
float* const UTILS_RESTRICT distances = scratch.data();
float4 const* const UTILS_RESTRICT spheres = lightData.data<FScene::POSITION_RADIUS>();
computeLightCameraDistances(distances, viewMatrix, spheres, size);
computeLightCameraDistances(distances, viewMatrix, spheres, visibleLightCount);
// skip directional light
Zip2Iterator<FScene::LightSoa::iterator, float*> b = { lightData.begin(), distances };
std::sort(b + FScene::DIRECTIONAL_LIGHTS_COUNT, b + size,
std::sort(b + FScene::DIRECTIONAL_LIGHTS_COUNT, b + visibleLightCount,
[](auto const& lhs, auto const& rhs) { return lhs.second < rhs.second; });
}
// drop excess lights
lightData.resize(std::min(size, CONFIG_MAX_LIGHT_COUNT + FScene::DIRECTIONAL_LIGHTS_COUNT));
lightData.resize(std::min(visibleLightCount,
CONFIG_MAX_LIGHT_COUNT + FScene::DIRECTIONAL_LIGHTS_COUNT));
}
// These methods need to exist so clang honors the __restrict__ keyword, which in turn
@@ -972,8 +977,9 @@ void FView::updatePrimitivesLod(FEngine& engine, const CameraInfo&,
}
FrameGraphId<FrameGraphTexture> FView::renderShadowMaps(FEngine& engine, FrameGraph& fg,
CameraInfo const& cameraInfo, float4 const& userTime, RenderPass const& pass) noexcept {
return mShadowMapManager.render(engine, fg, pass, *this, cameraInfo, userTime);
CameraInfo const& cameraInfo, float4 const& userTime,
RenderPassBuilder const& passBuilder) noexcept {
return mShadowMapManager.render(engine, fg, passBuilder, *this, cameraInfo, userTime);
}
void FView::commitFrameHistory(FEngine& engine) noexcept {
+6 -5
View File
@@ -88,7 +88,7 @@ public:
// note: viewport/cameraInfo are passed by value to make it clear that prepare cannot
// keep references on them that would outlive the scope of prepare() (e.g. with JobSystem).
void prepare(FEngine& engine, backend::DriverApi& driver, ArenaScope& arena,
void prepare(FEngine& engine, backend::DriverApi& driver, RootArenaScope& rootArenaScope,
filament::Viewport viewport, CameraInfo cameraInfo,
math::float4 const& userTime, bool needsAlphaChannel) noexcept;
@@ -144,7 +144,7 @@ public:
void prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableData,
FScene::LightSoa const& lightData, CameraInfo const& cameraInfo) noexcept;
void prepareLighting(FEngine& engine, ArenaScope& arena, CameraInfo const& cameraInfo) noexcept;
void prepareLighting(FEngine& engine, CameraInfo const& cameraInfo) noexcept;
void prepareSSAO(backend::Handle<backend::HwTexture> ssao) const noexcept;
void prepareSSR(backend::Handle<backend::HwTexture> ssr, bool disableSSR,
@@ -176,7 +176,7 @@ public:
FrameGraphId<FrameGraphTexture> renderShadowMaps(FEngine& engine, FrameGraph& fg,
CameraInfo const& cameraInfo, math::float4 const& userTime,
RenderPass const& pass) noexcept;
RenderPassBuilder const& passBuilder) noexcept;
void updatePrimitivesLod(
FEngine& engine, const CameraInfo& camera,
@@ -460,7 +460,8 @@ private:
void prepareVisibleRenderables(utils::JobSystem& js,
Frustum const& frustum, FScene::RenderableSoa& renderableData) const noexcept;
static void prepareVisibleLights(FLightManager const& lcm, ArenaScope& rootArena,
static void prepareVisibleLights(FLightManager const& lcm,
utils::Slice<float> scratch,
math::mat4f const& viewMatrix, Frustum const& frustum,
FScene::LightSoa& lightData) noexcept;
@@ -490,7 +491,7 @@ private:
FScene* mScene = nullptr;
// The camera set by the user, used for culling and viewing
FCamera* mCullingCamera = nullptr;
FCamera* /* UTILS_NONNULL */ mCullingCamera = nullptr; // FIXME: should alaways be non-null
// The optional (debug) camera, used only for viewing
FCamera* mViewingCamera = nullptr;
+88 -26
View File
@@ -30,6 +30,7 @@
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>
#include <vector>
namespace utils {
@@ -43,14 +44,14 @@ static inline P* add(P* a, T b) noexcept {
template <typename P>
static inline P* align(P* p, size_t alignment) noexcept {
// alignment must be a power-of-two
assert(alignment && !(alignment & alignment-1));
assert_invariant(alignment && !(alignment & alignment-1));
return (P*)((uintptr_t(p) + alignment - 1) & ~(alignment - 1));
}
template <typename P>
static inline P* align(P* p, size_t alignment, size_t offset) noexcept {
P* const r = align(add(p, offset), alignment);
assert(r >= add(p, offset));
assert_invariant(r >= add(p, offset));
return r;
}
@@ -89,20 +90,19 @@ public:
// branch-less allocation
void* const p = pointermath::align(current(), alignment, extra);
void* const c = pointermath::add(p, size);
bool success = c <= end();
bool const success = c <= end();
set_current(success ? c : current());
return success ? p : nullptr;
}
// API specific to this allocator
void *getCurrent() UTILS_RESTRICT noexcept {
return current();
}
// free memory back to the specified point
void rewind(void* p) UTILS_RESTRICT noexcept {
assert(p>=mBegin && p<end());
assert_invariant(p >= mBegin && p < end());
set_current(p);
}
@@ -122,16 +122,21 @@ public:
void swap(LinearAllocator& rhs) noexcept;
void *base() noexcept { return mBegin; }
void const *base() const noexcept { return mBegin; }
void free(void*, size_t) UTILS_RESTRICT noexcept { }
private:
protected:
void* end() UTILS_RESTRICT noexcept { return pointermath::add(mBegin, mSize); }
void const* end() const UTILS_RESTRICT noexcept { return pointermath::add(mBegin, mSize); }
void* current() UTILS_RESTRICT noexcept { return pointermath::add(mBegin, mCur); }
void const* current() const UTILS_RESTRICT noexcept { return pointermath::add(mBegin, mCur); }
private:
void set_current(void* p) UTILS_RESTRICT noexcept {
mCur = uint32_t(uintptr_t(p) - uintptr_t(mBegin));
}
void* mBegin = nullptr;
uint32_t mSize = 0;
uint32_t mCur = 0;
@@ -152,9 +157,7 @@ public:
explicit HeapAllocator(const AREA&) { }
// our allocator concept
void* alloc(size_t size, size_t alignment = alignof(std::max_align_t), size_t extra = 0) {
// this allocator doesn't support 'extra'
assert(extra == 0);
void* alloc(size_t size, size_t alignment = alignof(std::max_align_t)) {
return aligned_alloc(size, alignment);
}
@@ -171,6 +174,50 @@ public:
void swap(HeapAllocator&) noexcept { }
};
/* ------------------------------------------------------------------------------------------------
* LinearAllocatorWithFallback
*
* This is a LinearAllocator that falls back to a HeapAllocator when allocation fail. The Heap
* allocator memory is freed only when the LinearAllocator is reset or destroyed.
* ------------------------------------------------------------------------------------------------
*/
class LinearAllocatorWithFallback : private LinearAllocator, private HeapAllocator {
std::vector<void*> mHeapAllocations;
public:
LinearAllocatorWithFallback(void* begin, void* end) noexcept
: LinearAllocator(begin, end) {
}
template <typename AREA>
explicit LinearAllocatorWithFallback(const AREA& area)
: LinearAllocatorWithFallback(area.begin(), area.end()) {
}
~LinearAllocatorWithFallback() noexcept {
LinearAllocatorWithFallback::reset();
}
void* alloc(size_t size, size_t alignment = alignof(std::max_align_t));
void *getCurrent() noexcept {
return LinearAllocator::getCurrent();
}
void rewind(void* p) noexcept {
if (p >= LinearAllocator::base() && p < LinearAllocator::end()) {
LinearAllocator::rewind(p);
}
}
void reset() noexcept;
void free(void*, size_t) noexcept { }
bool isHeapAllocation(void* p) const noexcept {
return p < LinearAllocator::base() || p >= LinearAllocator::end();
}
};
// ------------------------------------------------------------------------------------------------
class FreeList {
@@ -186,13 +233,13 @@ public:
Node* const head = mHead;
mHead = head ? head->next : nullptr;
// this could indicate a use after free
assert(!mHead || mHead >= mBegin && mHead < mEnd);
assert_invariant(!mHead || mHead >= mBegin && mHead < mEnd);
return head;
}
void push(void* p) noexcept {
assert(p);
assert(p >= mBegin && p < mEnd);
assert_invariant(p);
assert_invariant(p >= mBegin && p < mEnd);
// TODO: assert this is one of our pointer (i.e.: it's address match one of ours)
Node* const head = static_cast<Node*>(p);
head->next = mHead;
@@ -229,7 +276,7 @@ public:
AtomicFreeList& operator=(const FreeList& rhs) = delete;
void* pop() noexcept {
Node* const storage = mStorage;
Node* const pStorage = mStorage;
HeadPtr currentHead = mHead.load();
while (currentHead.offset >= 0) {
@@ -237,8 +284,8 @@ public:
// 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.
Node* const next = storage[currentHead.offset].next.load(std::memory_order_relaxed);
const HeadPtr newHead{ next ? int32_t(next - storage) : -1, currentHead.tag + 1 };
Node* const pNext = pStorage[currentHead.offset].next.load(std::memory_order_relaxed);
const HeadPtr newHead{ pNext ? int32_t(pNext - pStorage) : -1, currentHead.tag + 1 };
// In the rare case that the other thread that raced ahead of us already returned the
// same mHead we just loaded, but it now has a different "next" value, the tag field will not
// match, and compare_exchange_weak will fail and prevent that particular race condition.
@@ -246,18 +293,18 @@ public:
// This assert needs to occur after we have validated that there was no race condition
// Otherwise, next might already contain application data, if another thread
// raced ahead of us after we loaded mHead, but before we loaded mHead->next.
assert(!next || next >= storage);
assert_invariant(!pNext || pNext >= pStorage);
break;
}
}
void* p = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr;
assert(!p || p >= storage);
void* p = (currentHead.offset >= 0) ? (pStorage + currentHead.offset) : nullptr;
assert_invariant(!p || p >= pStorage);
return p;
}
void push(void* p) noexcept {
Node* const storage = mStorage;
assert(p && p >= storage);
assert_invariant(p && p >= storage);
Node* const node = static_cast<Node*>(p);
HeadPtr currentHead = mHead.load();
HeadPtr newHead = { int32_t(node - storage), currentHead.tag + 1 };
@@ -330,9 +377,9 @@ public:
// our allocator concept
void* alloc(size_t size = ELEMENT_SIZE,
size_t alignment = ALIGNMENT, size_t offset = OFFSET) noexcept {
assert(size <= ELEMENT_SIZE);
assert(alignment <= ALIGNMENT);
assert(offset == OFFSET);
assert_invariant(size <= ELEMENT_SIZE);
assert_invariant(alignment <= ALIGNMENT);
assert_invariant(offset == OFFSET);
return mFreeList.pop();
}
@@ -587,23 +634,36 @@ public:
// 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 = alignof(std::max_align_t), size_t extra = 0) noexcept {
void* alloc(size_t size, size_t alignment, size_t extra) noexcept {
std::lock_guard<LockingPolicy> guard(mLock);
void* p = mAllocator.alloc(size, alignment, extra);
mListener.onAlloc(p, size, alignment, extra);
return p;
}
void* alloc(size_t size, size_t alignment = alignof(std::max_align_t)) noexcept {
std::lock_guard<LockingPolicy> guard(mLock);
void* p = mAllocator.alloc(size, alignment);
mListener.onAlloc(p, size, alignment, 0);
return p;
}
// Allocate an array of trivially destructible objects
// for safety, we disable the object-based alloc method if the object type is not
// trivially destructible, since free() won't call the destructor and this is allocating
// an array.
template <typename T,
typename = typename std::enable_if<std::is_trivially_destructible<T>::value>::type>
T* alloc(size_t count, size_t alignment = alignof(T), size_t extra = 0) noexcept {
T* alloc(size_t count, size_t alignment, size_t extra) noexcept {
return (T*)alloc(count * sizeof(T), alignment, extra);
}
template <typename T,
typename = typename std::enable_if<std::is_trivially_destructible<T>::value>::type>
T* alloc(size_t count, size_t alignment = alignof(T)) noexcept {
return (T*)alloc(count * sizeof(T), alignment);
}
// return memory pointed by p to the arena
// (actual behaviour may depend on allocator provided)
void free(void* p) noexcept {
@@ -720,6 +780,8 @@ class ArenaScope {
}
public:
using Arena = ARENA;
explicit ArenaScope(ARENA& allocator)
: mArena(allocator), mRewind(allocator.getCurrent()) {
}
@@ -771,7 +833,7 @@ public:
}
// use with caution
ARENA& getAllocator() noexcept { return mArena; }
ARENA& getArena() noexcept { return mArena; }
private:
ARENA& mArena;
+8
View File
@@ -179,6 +179,14 @@
# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 0
#endif
#if defined(__clang__)
#define UTILS_NONNULL _Nonnull
#define UTILS_NULLABLE _Nullable
#else
#define UTILS_NONNULL
#define UTILS_NULLABLE
#endif
#if defined(_MSC_VER)
// MSVC does not support loop unrolling hints
# define UTILS_UNROLL
+43 -15
View File
@@ -16,6 +16,8 @@
#include <utils/Allocator.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <algorithm>
@@ -52,6 +54,29 @@ void LinearAllocator::swap(LinearAllocator& rhs) noexcept {
std::swap(mCur, rhs.mCur);
}
// ------------------------------------------------------------------------------------------------
// LinearAllocatorWithFallback
// ------------------------------------------------------------------------------------------------
void* LinearAllocatorWithFallback::alloc(size_t size, size_t alignment) {
void* p = LinearAllocator::alloc(size, alignment);
if (UTILS_UNLIKELY(!p)) {
p = HeapAllocator::alloc(size, alignment);
mHeapAllocations.push_back(p);
}
assert_invariant(p);
return p;
}
void LinearAllocatorWithFallback::reset() noexcept {
LinearAllocator::reset();
for (auto* p : mHeapAllocations) {
HeapAllocator::free(p);
}
mHeapAllocations.clear();
}
// ------------------------------------------------------------------------------------------------
// FreeList
// ------------------------------------------------------------------------------------------------
@@ -61,8 +86,8 @@ FreeList::Node* FreeList::init(void* begin, void* end,
{
void* const p = pointermath::align(begin, alignment, extra);
void* const n = pointermath::align(pointermath::add(p, elementSize), alignment, extra);
assert(p >= begin && p < end);
assert(n >= begin && n < end && n > p);
assert_invariant(p >= begin && p < end);
assert_invariant(n >= begin && n < end && n > p);
const size_t d = uintptr_t(n) - uintptr_t(p);
const size_t num = (uintptr_t(end) - uintptr_t(p)) / d;
@@ -77,8 +102,8 @@ FreeList::Node* FreeList::init(void* begin, void* end,
cur->next = next;
cur = next;
}
assert(cur < end);
assert(pointermath::add(cur, d) <= end);
assert_invariant(cur < end);
assert_invariant(pointermath::add(cur, d) <= end);
cur->next = nullptr;
return head;
}
@@ -97,13 +122,13 @@ AtomicFreeList::AtomicFreeList(void* begin, void* end,
{
#ifdef __ANDROID__
// on some platform (e.g. web) this returns false. we really only care about mobile though.
assert(mHead.is_lock_free());
assert_invariant(mHead.is_lock_free());
#endif
void* const p = pointermath::align(begin, alignment, extra);
void* const n = pointermath::align(pointermath::add(p, elementSize), alignment, extra);
assert(p >= begin && p < end);
assert(n >= begin && n < end && n > p);
assert_invariant(p >= begin && p < end);
assert_invariant(n >= begin && n < end && n > p);
const size_t d = uintptr_t(n) - uintptr_t(p);
const size_t num = (uintptr_t(end) - uintptr_t(p)) / d;
@@ -119,8 +144,8 @@ AtomicFreeList::AtomicFreeList(void* begin, void* end,
cur->next = next;
cur = next;
}
assert(cur < end);
assert(pointermath::add(cur, d) <= end);
assert_invariant(cur < end);
assert_invariant(pointermath::add(cur, d) <= end);
cur->next = nullptr;
mHead.store({ int32_t(head - mStorage), 0 });
@@ -148,22 +173,25 @@ TrackingPolicy::HighWatermark::~HighWatermark() noexcept {
}
void TrackingPolicy::HighWatermark::onFree(void* p, size_t size) noexcept {
assert(mCurrent >= size);
// FIXME: this code is incorrect with LinearAllocators because free() is a no-op for them
assert_invariant(mCurrent >= size);
mCurrent -= uint32_t(size);
}
void TrackingPolicy::HighWatermark::onReset() noexcept {
// we should never be here if mBase is nullptr because compilation would have failed when
// Arena::onReset() tries to call the underlying allocator's onReset()
assert(mBase);
assert_invariant(mBase);
mCurrent = 0;
}
void TrackingPolicy::HighWatermark::onRewind(void const* addr) noexcept {
// we should never be here if mBase is nullptr because compilation would have failed when
// Arena::onRewind() tries to call the underlying allocator's onReset()
assert(mBase);
assert(addr >= mBase);
mCurrent = uint32_t(uintptr_t(addr) - uintptr_t(mBase));
assert_invariant(mBase);
// for LinearAllocatorWithFallback we could get pointers outside the range
if (addr >= mBase && addr < pointermath::add(mBase, mSize)) {
mCurrent = uint32_t(uintptr_t(addr) - uintptr_t(mBase));
}
}
// ------------------------------------------------------------------------------------------------
@@ -183,7 +211,7 @@ void TrackingPolicy::Debug::onFree(void* p, size_t size) noexcept {
void TrackingPolicy::Debug::onReset() noexcept {
// we should never be here if mBase is nullptr because compilation would have failed when
// Arena::onReset() tries to call the underlying allocator's onReset()
assert(mBase);
assert_invariant(mBase);
memset(mBase, 0xec, mSize);
}