Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0404be5127 | ||
|
|
a784fb7d8b | ||
|
|
8459cbc91c | ||
|
|
344c04a2d8 | ||
|
|
55e2691f3f | ||
|
|
7be9cdc7f8 | ||
|
|
af792e3d18 | ||
|
|
f93677548d | ||
|
|
1eff66e4ec | ||
|
|
34f8b9aa20 | ||
|
|
6d07443188 |
@@ -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
@@ -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()
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -54,7 +54,7 @@ using LinearAllocatorArena = utils::Arena<
|
||||
|
||||
#endif
|
||||
|
||||
using ArenaScope = utils::ArenaScope<LinearAllocatorArena>;
|
||||
using RootArenaScope = utils::ArenaScope<LinearAllocatorArena>;
|
||||
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user