Merge branch 'main' into ma/descriptor-sets

This commit is contained in:
Mathias Agopian
2024-08-28 11:45:02 -07:00
63 changed files with 506 additions and 215 deletions
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.54.0'
implementation 'com.google.android.filament:filament-android:1.54.1'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.54.0'
pod 'Filament', '~> 1.54.1'
```
### Snapshots
+4
View File
@@ -7,6 +7,10 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.54.2
- Add a `name` API to Filament objects for debugging handle use-after-free assertions
## v1.54.1
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.54.0
VERSION_NAME=1.54.1
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1
View File
@@ -61,6 +61,7 @@ set(SRCS
src/Engine.cpp
src/Exposure.cpp
src/Fence.cpp
src/FilamentBuilder.cpp
src/FrameInfo.cpp
src/FrameSkipper.cpp
src/Froxelizer.cpp
@@ -1150,7 +1150,7 @@ struct RasterState {
bool inverseFrontFaces : 1; // 31
//! padding, must be 0
bool depthClamp : 1; // 32
uint8_t padding : 1; // 32
};
uint32_t u = 0;
};
@@ -162,6 +162,10 @@ DECL_DRIVER_API_0(finish)
// reset state tracking, if the driver does any state tracking (e.g. GL)
DECL_DRIVER_API_0(resetState)
DECL_DRIVER_API_N(setDebugTag,
backend::HandleBase::HandleId, handleId,
utils::CString, tag)
/*
* Creating driver objects
* -----------------------
@@ -354,7 +358,6 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isDepthStencilBlitSupported, backend::TextureFormat, format)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
@@ -20,11 +20,12 @@
#include <backend/Handle.h>
#include <utils/Allocator.h>
#include <utils/CString.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <tsl/robin_map.h>
@@ -173,8 +174,10 @@ public:
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
auto const pNode = static_cast<typename Allocator::Node*>(p);
uint8_t const expectedAge = pNode[-1].age;
FILAMENT_CHECK_POSTCONDITION(expectedAge == age) <<
"use-after-free of Handle with id=" << handle.getId();
// getHandleTag() is only called if the check fails.
FILAMENT_CHECK_POSTCONDITION(expectedAge == age)
<< "use-after-free of Handle with id=" << handle.getId()
<< ", tag=" << getHandleTag(handle.getId()).c_str_safe();
}
}
@@ -201,6 +204,34 @@ public:
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
}
void associateTagToHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
// TODO: for now, only pool handles check for use-after-free, so we only keep tags for
// those
if (isPoolHandle(id)) {
// Truncate the tag's age to N bits.
constexpr uint8_t N = 2; // support a history of 4 tags
constexpr uint8_t mask = (1 << N) - 1;
uint8_t const age = (id & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
uint8_t const newAge = age & mask;
uint32_t const key = (id & ~HANDLE_AGE_MASK) | (newAge << HANDLE_AGE_SHIFT);
// This line is the costly part. In the future, we could potentially use a custom
// allocator.
mDebugTags[key] = std::move(tag);
}
}
utils::CString getHandleTag(HandleBase::HandleId id) const noexcept {
if (!isPoolHandle(id)) {
return "(no tag)";
}
if (auto pos = mDebugTags.find(id); pos != mDebugTags.end()) {
return pos->second;
}
return "(no tag)";
}
private:
template<typename D>
@@ -363,6 +394,7 @@ private:
// Below is only used when running out of space in the HandleArena
mutable utils::Mutex mLock;
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
tsl::robin_map<HandleBase::HandleId, utils::CString> mDebugTags;
HandleBase::HandleId mId = 0;
bool mUseAfterFreeCheckDisabled = false;
};
+3
View File
@@ -80,6 +80,9 @@ HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size,
bool disableUseAfterFreeCheck) noexcept
: mHandleArena(name, size, disableUseAfterFreeCheck),
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) {
// Reserve initial space for debug tags. This prevents excessive calls to malloc when the first
// few tags are set.
mDebugTags.reserve(512);
}
template <size_t P0, size_t P1, size_t P2>
+2
View File
@@ -160,6 +160,8 @@ public:
size_t size, bool forceGpuBuffer = false);
~MetalBuffer();
[[nodiscard]] bool wasAllocationSuccessful() const noexcept { return mBuffer || mCpuBuffer; }
MetalBuffer(const MetalBuffer& rhs) = delete;
MetalBuffer& operator=(const MetalBuffer& rhs) = delete;
+2 -2
View File
@@ -56,8 +56,8 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
mBuffer = { [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate],
TrackedMetalBuffer::Type::GENERIC };
}
FILAMENT_CHECK_POSTCONDITION(mBuffer)
<< "Could not allocate Metal buffer of size " << size << ".";
// mBuffer might fail to be allocated. Clients can check for this by calling
// wasAllocationSuccessful().
}
MetalBuffer::~MetalBuffer() {
@@ -166,7 +166,6 @@ struct MetalContext {
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
CullModeStateTracker cullModeState;
WindingStateTracker windingState;
DepthClampStateTracker depthClampState;
Handle<HwRenderPrimitive> currentRenderPrimitive;
// State caches.
+3 -2
View File
@@ -58,7 +58,6 @@ class MetalDriver final : public DriverBase {
public:
static Driver* create(MetalPlatform* platform, const Platform::DriverConfig& driverConfig);
void runAtNextTick(const std::function<void()>& fn) noexcept;
private:
@@ -75,10 +74,12 @@ private:
/*
* Tasks run regularly on the driver thread.
* Not thread-safe; tasks are run from the driver thead and must be enqueued from the driver
* thread.
*/
void runAtNextTick(const std::function<void()>& fn) noexcept;
void executeTickOps() noexcept;
std::vector<std::function<void()>> mTickOps;
std::mutex mTickOpsLock;
// Tasks regularly executed on the driver thread after a command buffer has completed
struct DeferredTask {
+30 -17
View File
@@ -405,17 +405,40 @@ void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
// No actual GPU memory is allocated here, so no need to check for allocation success.
}
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
auto elementSize = (uint8_t) getElementTypeSize(elementType);
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
auto elementSize = (uint8_t)getElementTypeSize(elementType);
auto* indexBuffer =
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
auto& buffer = indexBuffer->buffer;
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!buffer.wasAllocationSuccessful())) {
const size_t byteCount = buffer.getSize();
runAtNextTick([byteCount, this, ibh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal index buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(ibh.getId()).c_str_safe();
});
}
}
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
auto* bufferObject =
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!bufferObject->getBuffer()->wasAllocationSuccessful())) {
runAtNextTick([byteCount, this, boh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(boh.getId()).c_str_safe();
});
}
}
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
@@ -1061,10 +1084,6 @@ bool MetalDriver::isProtectedTexturesSupported() {
return false;
}
bool MetalDriver::isDepthClampSupported() {
return true;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -1961,13 +1980,6 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
[mContext->currentRenderPassEncoder setFrontFacingWinding:winding];
}
// depth clip mode
MTLDepthClipMode depthClipMode = rs.depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
mContext->depthClampState.updateState(depthClipMode);
if (mContext->depthClampState.stateChanged()) {
[mContext->currentRenderPassEncoder setDepthClipMode:depthClipMode];
}
// Set the depth-stencil state, if a state change is needed.
DepthStencilState depthState;
if (depthAttachment) {
@@ -2311,16 +2323,17 @@ void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
void MetalDriver::resetState(int) {
}
void MetalDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
}
void MetalDriver::runAtNextTick(const std::function<void()>& fn) noexcept {
std::lock_guard<std::mutex> const lock(mTickOpsLock);
mTickOps.push_back(fn);
}
void MetalDriver::executeTickOps() noexcept {
std::vector<std::function<void()>> ops;
mTickOpsLock.lock();
std::swap(ops, mTickOps);
mTickOpsLock.unlock();
for (const auto& f : ops) {
f();
}
@@ -253,10 +253,6 @@ void MetalSwapChain::present() {
}
}
#ifndef FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD
#define FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD 1
#endif
class PresentDrawableData {
public:
PresentDrawableData() = delete;
@@ -275,14 +271,10 @@ public:
[that->mDrawable present];
}
#if FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD == 1
// mDrawable is acquired on the driver thread. Typically, we would release this object on
// the same thread, but after receiving consistent crash reports from within
// [CAMetalDrawable dealloc], we suspect this object requires releasing on the main thread.
dispatch_async(dispatch_get_main_queue(), ^{ cleanupAndDestroy(that); });
#else
that->mDriver->runAtNextTick([that]() { cleanupAndDestroy(that); });
#endif
}
private:
-1
View File
@@ -379,7 +379,6 @@ using SamplerStateCache = StateCache<SamplerState, id<MTLSamplerState>, SamplerS
using CullModeStateTracker = StateTracker<MTLCullMode>;
using WindingStateTracker = StateTracker<MTLWinding>;
using DepthClampStateTracker = StateTracker<MTLDepthClipMode>;
// Argument encoder
+3 -4
View File
@@ -208,10 +208,6 @@ bool NoopDriver::isProtectedTexturesSupported() {
return true;
}
bool NoopDriver::isDepthClampSupported() {
return false;
}
bool NoopDriver::isWorkaroundNeeded(Workaround) {
return false;
}
@@ -413,4 +409,7 @@ void NoopDriver::bindDescriptorSet(
backend::DescriptorSetOffsetArray&& offsets) {
}
void NoopDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
}
} // namespace filament
@@ -676,7 +676,6 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
#ifndef __EMSCRIPTEN__
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
#endif
ext->EXT_depth_clamp = exts.has("GL_EXT_depth_clamp"sv);
ext->EXT_discard_framebuffer = exts.has("GL_EXT_discard_framebuffer"sv);
#ifndef __EMSCRIPTEN__
ext->EXT_disjoint_timer_query = exts.has("GL_EXT_disjoint_timer_query"sv);
@@ -747,7 +746,6 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
ext->EXT_color_buffer_half_float = true; // Assumes core profile.
ext->EXT_clip_cull_distance = true;
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
ext->EXT_depth_clamp = true;
ext->EXT_discard_framebuffer = false;
ext->EXT_disjoint_timer_query = true;
ext->EXT_multisampled_render_to_texture = false;
+3 -5
View File
@@ -224,9 +224,8 @@ public:
bool EXT_color_buffer_float;
bool EXT_color_buffer_half_float;
bool EXT_debug_marker;
bool EXT_depth_clamp;
bool EXT_discard_framebuffer;
bool EXT_disjoint_timer_query;
bool EXT_discard_framebuffer;
bool EXT_multisampled_render_to_texture2;
bool EXT_multisampled_render_to_texture;
bool EXT_protected_textures;
@@ -244,10 +243,10 @@ public:
bool KHR_parallel_shader_compile;
bool KHR_texture_compression_astc_hdr;
bool KHR_texture_compression_astc_ldr;
bool OES_EGL_image_external_essl3;
bool OES_depth24;
bool OES_depth_texture;
bool OES_depth24;
bool OES_packed_depth_stencil;
bool OES_EGL_image_external_essl3;
bool OES_rgb8_rgba8;
bool OES_standard_derivatives;
bool OES_texture_npot;
@@ -623,7 +622,6 @@ constexpr size_t OpenGLContext::getIndexForCap(GLenum cap) noexcept { //NOLINT
#ifdef BACKEND_OPENGL_VERSION_GL
case GL_PROGRAM_POINT_SIZE: index = 10; break;
#endif
case GL_DEPTH_CLAMP: index = 11; break;
default: break;
}
assert_invariant(index < state.enables.caps.size());
+4 -12
View File
@@ -462,14 +462,6 @@ void OpenGLDriver::setRasterState(RasterState rs) noexcept {
} else {
gl.disable(GL_SAMPLE_ALPHA_TO_COVERAGE);
}
if (gl.ext.EXT_depth_clamp) {
if (rs.depthClamp) {
gl.enable(GL_DEPTH_CLAMP);
} else {
gl.disable(GL_DEPTH_CLAMP);
}
}
}
void OpenGLDriver::setStencilState(StencilState ss) noexcept {
@@ -2254,10 +2246,6 @@ bool OpenGLDriver::isProtectedTexturesSupported() {
return getContext().ext.EXT_protected_textures;
}
bool OpenGLDriver::isDepthClampSupported() {
return getContext().ext.EXT_depth_clamp;
}
bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -2349,6 +2337,10 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
// Updating driver objects
// ------------------------------------------------------------------------------------------------
void OpenGLDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
}
void OpenGLDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh,
uint32_t index, Handle<HwBufferObject> boh) {
DEBUG_MARKER()
-6
View File
@@ -201,12 +201,6 @@ using namespace glext;
# define GL_CLIP_DISTANCE1 0x3001
#endif
#if defined(GL_EXT_depth_clamp)
# define GL_DEPTH_CLAMP GL_DEPTH_CLAMP_EXT
#else
# define GL_DEPTH_CLAMP 0x864F
#endif
#if defined(GL_KHR_debug)
# define GL_DEBUG_OUTPUT GL_DEBUG_OUTPUT_KHR
# define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR
@@ -125,10 +125,6 @@ public:
return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE;
}
inline bool isDepthClampSupported() const noexcept {
return mPhysicalDeviceFeatures.depthClamp == VK_TRUE;
}
inline bool isDebugMarkersSupported() const noexcept {
return mDebugMarkersSupported;
}
+4 -6
View File
@@ -1038,10 +1038,6 @@ bool VulkanDriver::isProtectedTexturesSupported() {
return false;
}
bool VulkanDriver::isDepthClampSupported() {
return mContext.isDepthClampSupported();
}
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU: {
@@ -1827,8 +1823,6 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha),
.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0),
.rasterizationSamples = rt->getSamples(),
.depthClamp = rasterState.depthClamp,
.reserved = 0,
.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass),
.colorBlendOp = rasterState.blendEquationRGB,
.alphaBlendOp = rasterState.blendEquationAlpha,
@@ -2020,6 +2014,10 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
void VulkanDriver::resetState(int) {
}
void VulkanDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mResourceAllocator.associateHandle(handleId, std::move(tag));
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<VulkanDriver>;
@@ -184,7 +184,6 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
vkRaster.polygonMode = VK_POLYGON_MODE_FILL;
vkRaster.cullMode = raster.cullMode;
vkRaster.frontFace = raster.frontFace;
vkRaster.depthClampEnable = raster.depthClamp;
vkRaster.depthBiasEnable = raster.depthBiasEnable;
vkRaster.depthBiasConstantFactor = raster.depthBiasConstantFactor;
vkRaster.depthBiasClamp = 0.0f;
@@ -90,9 +90,7 @@ public:
VkBlendFactor srcAlphaBlendFactor : 5;
VkBlendFactor dstAlphaBlendFactor : 5;
VkColorComponentFlags colorWriteMask : 4;
uint8_t rasterizationSamples : 4;// offset = 4 bytes
uint8_t depthClamp : 1;
uint8_t reserved : 3;
uint8_t rasterizationSamples; // offset = 4 bytes
uint8_t colorTargetCount; // offset = 5 bytes
BlendEquation colorBlendOp : 4; // offset = 6 bytes
BlendEquation alphaBlendOp : 4;
@@ -105,6 +105,10 @@ public:
mHandleAllocatorImpl.deallocate(handle, obj);
}
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
}
private:
AllocatorImpl mHandleAllocatorImpl;
@@ -325,7 +325,6 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
// We could simply enable all supported features, but since that may have performance
// consequences let's just enable the features we need.
VkPhysicalDeviceFeatures enabledFeatures{
.depthClamp = features.depthClamp,
.samplerAnisotropy = features.samplerAnisotropy,
.textureCompressionETC2 = features.textureCompressionETC2,
.textureCompressionBC = features.textureCompressionBC,
+16 -1
View File
@@ -54,7 +54,7 @@ public:
using BufferDescriptor = backend::BufferDescriptor;
using BindingType = backend::BufferObjectBinding;
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -78,6 +78,21 @@ public:
*/
Builder& bindingType(BindingType bindingType) noexcept;
/**
* Associate an optional name with this BufferObject for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this BufferObject
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the BufferObject and returns a pointer to it. After creation, the buffer
* object is uninitialized. Use BufferObject::setBuffer() to initialize it.
+17
View File
@@ -19,6 +19,7 @@
#include <utils/compiler.h>
#include <utils/PrivateImplementation.h>
#include <utils/CString.h>
#include <stddef.h>
@@ -54,6 +55,22 @@ public:
template<typename T>
using BuilderBase = utils::PrivateImplementation<T>;
void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept;
template <typename Builder>
class UTILS_PUBLIC BuilderNameMixin {
public:
Builder& name(const char* name, size_t len) noexcept {
builderMakeName(mName, name, len);
return static_cast<Builder&>(*this);
}
utils::CString const& getName() const noexcept { return mName; }
private:
utils::CString mName;
};
} // namespace filament
#endif // TNT_FILAMENT_FILAMENTAPI_H
+16 -1
View File
@@ -59,7 +59,7 @@ public:
UINT = uint8_t(backend::ElementType::UINT), //!< 32-bit indices
};
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -83,6 +83,21 @@ public:
*/
Builder& bufferType(IndexType indexType) noexcept;
/**
* Associate an optional name with this IndexBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this IndexBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the IndexBuffer object and returns a pointer to it. After creation, the index
* buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer.
+16 -1
View File
@@ -38,7 +38,7 @@ class UTILS_PUBLIC InstanceBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
@@ -70,6 +70,21 @@ public:
*/
Builder& localTransforms(math::mat4f const* UTILS_NULLABLE localTransforms) noexcept;
/**
* Associate an optional name with this InstanceBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this InstanceBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the InstanceBuffer object and returns a pointer to it.
*/
+16 -1
View File
@@ -39,7 +39,7 @@ class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -63,6 +63,21 @@ public:
*/
Builder& count(size_t count) noexcept;
/**
* Associate an optional name with this MorphTargetBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this MorphTargetBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the MorphTargetBuffer object and returns a pointer to it.
*
+16 -1
View File
@@ -39,7 +39,7 @@ class UTILS_PUBLIC SkinningBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -69,6 +69,21 @@ public:
*/
Builder& initialize(bool initialize = true) noexcept;
/**
* Associate an optional name with this SkinningBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this SkinningBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the SkinningBuffer object and returns a pointer to it.
*
+16 -1
View File
@@ -94,7 +94,7 @@ public:
*
* To create a NATIVE stream, call the <pre>stream</pre> method on the builder.
*/
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -136,6 +136,21 @@ public:
*/
Builder& height(uint32_t height) noexcept;
/**
* Associate an optional name with this Stream for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this Stream
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the Stream object and returns a pointer to it.
*
+16 -1
View File
@@ -112,7 +112,7 @@ public:
//! Use Builder to construct a Texture object instance
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -202,6 +202,21 @@ public:
*/
Builder& swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept;
/**
* Associate an optional name with this Texture for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this Texture
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the Texture object and returns a pointer to it.
*
+16 -1
View File
@@ -61,7 +61,7 @@ public:
using AttributeType = backend::ElementType;
using BufferDescriptor = backend::BufferDescriptor;
class Builder : public BuilderBase<BuilderDetails> {
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
@@ -158,6 +158,21 @@ public:
*/
Builder& advancedSkinning(bool enabled) noexcept;
/**
* Associate an optional name with this VertexBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this VertexBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
*/
// Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; // inherited
/**
* Creates the VertexBuffer object and returns a pointer to it.
*
+31
View File
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <filament/FilamentAPI.h>
#include <algorithm>
namespace filament {
void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept {
if (!name) {
return;
}
size_t const length = std::min(len, size_t { 128u });
outName = utils::CString(name, length);
}
} // namespace filament
+1
View File
@@ -3204,6 +3204,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
assert_invariant(dst);
assert_invariant(srcDesc.format == dstDesc.format);
assert_invariant(srcDesc.width == dstDesc.width && srcDesc.height == dstDesc.height);
assert_invariant(srcDesc.samples > 1 && dstDesc.samples <= 1);
driver.resolve(
dst, dstSubDesc.level, dstSubDesc.layer,
src, srcSubDesc.level, srcSubDesc.layer);
+2 -9
View File
@@ -449,8 +449,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
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, bool hasDepthClamp) noexcept {
FMaterialInstance const* const UTILS_RESTRICT mi, bool inverseFrontFaces) noexcept {
FMaterial const * const UTILS_RESTRICT ma = mi->getMaterial();
variant = Variant::filterVariant(variant, ma->isVariantLit());
@@ -491,7 +490,6 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
cmdDraw.info.rasterState.colorWrite = mi->isColorWriteEnabled();
cmdDraw.info.rasterState.depthWrite = mi->isDepthWriteEnabled();
cmdDraw.info.rasterState.depthFunc = mi->getDepthFunc();
cmdDraw.info.rasterState.depthClamp = hasDepthClamp;
cmdDraw.info.materialVariant = variant;
// we keep "RasterState::colorWrite" to the value set by material (could be disabled)
}
@@ -589,9 +587,6 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
bool const hasInstancedStereo =
renderFlags & IS_INSTANCED_STEREOSCOPIC;
bool const hasDepthClamp =
renderFlags & HAS_DEPTH_CLAMP;
float const cameraPositionDotCameraForward = dot(cameraPosition, cameraForward);
auto const* const UTILS_RESTRICT soaWorldAABBCenter = soa.data<FScene::WORLD_AABB_CENTER>();
@@ -612,7 +607,6 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
cmd.info.rasterState.depthWrite = true;
cmd.info.rasterState.depthFunc = RasterState::DepthFunc::GE;
cmd.info.rasterState.alphaToCoverage = false;
cmd.info.rasterState.depthClamp = hasDepthClamp;
}
for (uint32_t i = range.first; i < range.last; ++i) {
@@ -728,8 +722,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
// morphing.morphTargetBuffer->getHwHandle() : SamplerGroupHandle{};
if constexpr (isColorPass) {
RenderPass::setupColorCommand(cmd, renderableVariant, mi,
inverseFrontFaces, hasDepthClamp);
RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces);
const bool blendPass = Pass(cmd.key & PASS_MASK) == Pass::BLENDED;
if (blendPass) {
// TODO: at least for transparent objects, AABB should be per primitive
+1 -2
View File
@@ -285,7 +285,6 @@ public:
static constexpr RenderFlags HAS_SHADOWING = 0x01;
static constexpr RenderFlags HAS_INVERSE_FRONT_FACES = 0x02;
static constexpr RenderFlags IS_INSTANCED_STEREOSCOPIC = 0x04;
static constexpr RenderFlags HAS_DEPTH_CLAMP = 0x08;
// Arena used for commands
using Arena = utils::Arena<
@@ -454,7 +453,7 @@ private:
uint8_t instancedStereoEyeCount) noexcept;
static void setupColorCommand(Command& cmdDraw, Variant variant,
FMaterialInstance const* mi, bool inverseFrontFaces, bool hasDepthClamp) noexcept;
FMaterialInstance const* mi, bool inverseFrontFaces) noexcept;
static void updateSummedPrimitiveCounts(
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
+42 -40
View File
@@ -40,6 +40,7 @@
#include <utils/Panic.h>
#include <algorithm>
#include <optional>
#include <utility>
#include <stddef.h>
@@ -50,8 +51,9 @@ namespace filament {
using namespace backend;
using namespace math;
FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
RendererUtils::ColorPassOutput RendererUtils::colorPass(
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
ColorPassInput const& colorPassInput,
FrameGraphTexture::Descriptor const& colorBufferDesc,
ColorPassConfig const& config, PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass::Executor passExecutor) noexcept {
@@ -67,8 +69,6 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
FrameGraphId<FrameGraphTexture> structure;
};
Blackboard& blackboard = fg.getBlackboard();
auto& colorPass = fg.addPass<ColorPassData>(name,
[&](FrameGraph::Builder& builder, ColorPassData& data) {
@@ -76,21 +76,21 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
TargetBufferFlags clearStencilFlags = config.clearFlags & TargetBufferFlags::STENCIL;
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
data.color = blackboard.get<FrameGraphTexture>("color");
data.depth = blackboard.get<FrameGraphTexture>("depth");
data.color = colorPassInput.linearColor;
data.depth = colorPassInput.depth;
data.shadows = colorPassInput.shadows;
data.ssao = colorPassInput.ssao;
// Screen-space reflection or refractions
if (config.hasScreenSpaceReflectionsOrRefractions) {
data.ssr = blackboard.get<FrameGraphTexture>("ssr");
data.ssr = colorPassInput.ssr;
if (data.ssr) {
data.ssr = builder.sample(data.ssr);
}
}
if (config.hasContactShadows) {
data.structure = blackboard.get<FrameGraphTexture>("structure");
data.structure = colorPassInput.structure;
assert_invariant(data.structure);
data.structure = builder.sample(data.structure);
}
@@ -153,6 +153,8 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
}
if (colorGradingConfig.asSubpass) {
assert_invariant(config.msaa <= 1);
assert_invariant(colorBufferDesc.samples <= 1);
data.output = builder.createTexture("Tonemapped Buffer", {
.width = colorBufferDesc.width,
.height = colorBufferDesc.height,
@@ -194,7 +196,6 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
.samples = config.msaa,
.layerCount = static_cast<uint8_t>(colorBufferDesc.depth),
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags});
blackboard["depth"] = data.depth;
},
[=, passExecutor = std::move(passExecutor), &view, &engine](FrameGraphResources const& resources,
ColorPassData const& data, DriverApi& driver) {
@@ -260,24 +261,21 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
}
);
// when color grading is done as a subpass, the output of the color-pass is the ldr buffer
auto output = colorGradingConfig.asSubpass ? colorPass->output : colorPass->color;
blackboard["color"] = output;
return output;
return {
.linearColor = colorPass->color,
.tonemappedColor = colorPass->output, // can be null
.depth = colorPass->depth
};
}
std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
std::optional<RendererUtils::ColorPassOutput> RendererUtils::refractionPass(
FrameGraph& fg, FEngine& engine, FView const& view,
ColorPassInput colorPassInput,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass const& pass) noexcept {
auto& blackboard = fg.getBlackboard();
auto input = blackboard.get<FrameGraphTexture>("color");
FrameGraphId<FrameGraphTexture> output;
// find the first refractive object in channel 2
RenderPass::Command const* const refraction = std::partition_point(pass.begin(), pass.end(),
[](auto const& command) {
@@ -292,23 +290,21 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
// if there wasn't any refractive object, just skip everything below.
if (UTILS_UNLIKELY(hasScreenSpaceRefraction)) {
PostProcessManager& ppm = engine.getPostProcessManager();
// clear the color/depth buffers, which will orphan (and cull) the color pass
input.clear();
blackboard.remove("color");
blackboard.remove("depth");
assert_invariant(!colorPassInput.linearColor);
assert_invariant(!colorPassInput.depth);
config.hasScreenSpaceReflectionsOrRefractions = true;
input = RendererUtils::colorPass(fg, "Color Pass (opaque)", engine, view, {
PostProcessManager& ppm = engine.getPostProcessManager();
auto const opaquePassOutput = RendererUtils::colorPass(fg,
"Color Pass (opaque)", engine, view, colorPassInput, {
// When rendering the opaques, we need to conserve the sample buffer,
// so create a config that specifies the sample count.
.width = config.physicalViewport.width,
.height = config.physicalViewport.height,
.samples = config.msaa,
.format = config.hdrFormat
}, config, { .asSubpass = false },
},
config, { .asSubpass = false, .customResolve = false },
pass.getExecutor(pass.begin(), refraction));
@@ -317,13 +313,16 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
// in bound because only the descriptor 0 (frame uniforms) matters, and it's
// present in both.
PostProcessManager::generateMipmapSSR(ppm, fg,
input, ssrConfig.refraction, true, ssrConfig);
opaquePassOutput.linearColor,
ssrConfig.refraction,
true, ssrConfig);
// Now we're doing the refraction pass proper.
// This uses the same framebuffer (color and depth) used by the opaque pass. This happens
// automatically because these are set in the Blackboard (they were set by the opaque
// pass). For this reason, `desc` below is only used in colorPass() for the width and
// height.
// This uses the same framebuffer (color and depth) used by the opaque pass.
// For this reason, the `colorBufferDesc` parameter of colorPass() below is only used for
// the width and height.
colorPassInput.linearColor = opaquePassOutput.linearColor;
colorPassInput.depth = opaquePassOutput.depth;
// Since we're reusing the existing target we don't want to clear any of its buffer.
// Important: if this target ended up being an imported target, then the clearFlags
@@ -331,22 +330,25 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
// and we'd end up clearing the opaque pass. This scenario never happens because it is
// prevented in Renderer.cpp's final blit.
config.clearFlags = TargetBufferFlags::NONE;
output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view, {
auto transparentPassOutput = RendererUtils::colorPass(fg, "Color Pass (transparent)",
engine, view, colorPassInput, {
.width = config.physicalViewport.width,
.height = config.physicalViewport.height },
config, colorGradingConfig, pass.getExecutor(refraction, pass.end()));
config, colorGradingConfig,
pass.getExecutor(refraction, pass.end()));
if (config.msaa > 1 && !colorGradingConfig.asSubpass) {
// We need to do a resolve here because later passes (such as color grading or DoF) will
// need to sample from 'output'. However, because we have MSAA, we know we're not
// sampleable. And this is because in the SSR case, we had to use a renderbuffer to
// conserve the multi-sample buffer.
output = ppm.resolve(fg, "Resolved Color Buffer", output, { .levels = 1 });
transparentPassOutput.linearColor = ppm.resolve(fg, "Resolved Color Buffer",
transparentPassOutput.linearColor, { .levels = 1 });
}
} else {
output = input;
return transparentPassOutput;
}
return { output, hasScreenSpaceRefraction };
return std::nullopt;
}
UTILS_NOINLINE
+24 -2
View File
@@ -21,14 +21,19 @@
#include "RenderPass.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphTexture.h"
#include <filament/Viewport.h>
#include <backend/DriverEnums.h>
#include <backend/PixelBufferDescriptor.h>
#include <math/vec2.h>
#include <math/vec4.h>
#include <stdint.h>
#include <optional>
#include <utility>
namespace filament {
@@ -71,15 +76,32 @@ public:
bool screenSpaceReflectionHistoryNotReady;
};
static FrameGraphId<FrameGraphTexture> colorPass(
struct ColorPassInput {
FrameGraphId<FrameGraphTexture> linearColor;
FrameGraphId<FrameGraphTexture> tonemappedColor;
FrameGraphId<FrameGraphTexture> depth;
FrameGraphId<FrameGraphTexture> shadows;
FrameGraphId<FrameGraphTexture> ssao;
FrameGraphId<FrameGraphTexture> ssr;
FrameGraphId<FrameGraphTexture> structure;
};
struct ColorPassOutput {
FrameGraphId<FrameGraphTexture> linearColor;
FrameGraphId<FrameGraphTexture> tonemappedColor;
FrameGraphId<FrameGraphTexture> depth;
};
static ColorPassOutput colorPass(
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
ColorPassInput const& colorPassInput,
FrameGraphTexture::Descriptor const& colorBufferDesc,
ColorPassConfig const& config,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass::Executor passExecutor) noexcept;
static std::pair<FrameGraphId<FrameGraphTexture>, bool> refractionPass(
static std::optional<RendererUtils::ColorPassOutput> refractionPass(
FrameGraph& fg, FEngine& engine, FView const& view,
ColorPassInput colorPassInput,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
PostProcessManager::ColorGradingConfig colorGradingConfig,
+6 -6
View File
@@ -829,13 +829,13 @@ ShadowMap::Corners ShadowMap::computeFrustumCorners(
Corners const csViewFrustumCorners = {
.vertices = {
{ -1, -1, far },
{ 1, -1, far },
{ -1, 1, far },
{ 1, 1, far },
{ 1, -1, far },
{ -1, 1, far },
{ 1, 1, far },
{ -1, -1, near },
{ 1, -1, near },
{ -1, 1, near },
{ 1, 1, near },
{ 1, -1, near },
{ -1, 1, near },
{ 1, 1, near },
}
};
+5 -26
View File
@@ -66,15 +66,15 @@ namespace filament {
using namespace backend;
using namespace math;
ShadowMapManager::ShadowMapManager(FEngine& engine)
: mIsDepthClampSupported(engine.getDriverApi().isDepthClampSupported()) {
// do this only if depth-clamp is available
static constexpr bool USE_DEPTH_CLAMP = false;
ShadowMapManager::ShadowMapManager(FEngine& engine) {
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.shadowmap.visualize_cascades",
&engine.debug.shadowmap.visualize_cascades);
debugRegistry.registerProperty("d.shadowmap.disable_light_frustum_align",
&engine.debug.shadowmap.disable_light_frustum_align);
debugRegistry.registerProperty("d.shadowmap.depth_clamp",
&engine.debug.shadowmap.depth_clamp);
}
ShadowMapManager::~ShadowMapManager() {
@@ -367,22 +367,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// generate and sort the commands for rendering the shadow map
RenderPass::RenderFlags renderPassFlags{};
if (view.isFrontFaceWindingInverted()) {
renderPassFlags |= RenderPass::HAS_INVERSE_FRONT_FACES;
}
bool const canUseDepthClamp =
!view.hasVSM() &&
mIsDepthClampSupported &&
engine.debug.shadowmap.depth_clamp;
if (canUseDepthClamp) {
renderPassFlags |= RenderPass::HAS_DEPTH_CLAMP;
}
RenderPass const pass = passBuilder
.renderFlags(renderPassFlags)
.camera(cameraInfo)
.visibilityMask(entry.visibilityMask)
.geometry(scene->getRenderableData(), entry.range)
@@ -652,14 +637,8 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
cameraInfo.zf = -nearFarPlanes[i + 1];
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
bool const canUseDepthClamp =
!view.hasVSM() &&
mIsDepthClampSupported &&
engine.debug.shadowmap.depth_clamp;
auto shaderParameters = shadowMap.updateDirectional(engine,
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo,
canUseDepthClamp);
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo, USE_DEPTH_CLAMP);
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
-1
View File
@@ -232,7 +232,6 @@ private:
ShadowMapCacheContainer mShadowMapCache;
uint32_t mDirectionalShadowMapCount = 0;
uint32_t mSpotShadowMapCount = 0;
bool const mIsDepthClampSupported;
bool mInitialized = false;
ShadowMap& getShadowMap(size_t index) noexcept {
@@ -590,6 +590,9 @@ void FRenderableManager::create(
// full size of the UBO.
instances.handle = driver.createBufferObject(sizeof(PerRenderableUib),
BufferObjectBinding::UNIFORM, backend::BufferUsage::DYNAMIC);
if (auto name = instances.buffer->getName(); !name.empty()) {
driver.setDebugTag(instances.handle.getId(), std::move(name));
}
}
const uint32_t boneCount = builder->mSkinningBoneCount;
+5
View File
@@ -20,6 +20,8 @@
#include "FilamentAPI-impl.h"
#include <utils/CString.h>
namespace filament {
struct BufferObject::BuilderDetails {
@@ -56,6 +58,9 @@ FBufferObject::FBufferObject(FEngine& engine, const BufferObject::Builder& build
FEngine::DriverApi& driver = engine.getDriverApi();
mHandle = driver.createBufferObject(builder->mByteCount, builder->mBindingType,
backend::BufferUsage::STATIC);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), std::move(name));
}
}
void FBufferObject::terminate(FEngine& engine) {
-1
View File
@@ -617,7 +617,6 @@ public:
bool focus_shadowcasters = true;
bool visualize_cascades = false;
bool disable_light_frustum_align = false;
bool depth_clamp = true;
float dzn = -1.0f;
float dzf = 1.0f;
float display_shadow_texture_scale = 0.25f;
+5
View File
@@ -20,6 +20,8 @@
#include "FilamentAPI-impl.h"
#include <utils/CString.h>
namespace filament {
struct IndexBuffer::BuilderDetails {
@@ -58,6 +60,9 @@ FIndexBuffer::FIndexBuffer(FEngine& engine, const IndexBuffer::Builder& builder)
(backend::ElementType)builder->mIndexType,
uint32_t(builder->mIndexCount),
backend::BufferUsage::STATIC);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), std::move(name));
}
}
void FIndexBuffer::terminate(FEngine& engine) {
+2 -1
View File
@@ -61,7 +61,8 @@ InstanceBuffer* InstanceBuffer::Builder::build(Engine& engine) {
// ------------------------------------------------------------------------------------------------
FInstanceBuffer::FInstanceBuffer(FEngine& engine, const Builder& builder) {
FInstanceBuffer::FInstanceBuffer(FEngine& engine, const Builder& builder)
: mName(builder.getName()) {
mInstanceCount = builder->mInstanceCount;
mLocalTransforms.reserve(mInstanceCount);
+4
View File
@@ -25,6 +25,7 @@
#include <math/mat4.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
namespace filament {
@@ -46,10 +47,13 @@ public:
void prepare(FEngine& engine, math::mat4f rootTransform, const PerRenderableData& ubo,
backend::Handle<backend::HwBufferObject> handle);
utils::CString const& getName() const noexcept { return mName; }
private:
friend class RenderableManager;
utils::FixedCapacityVector<math::mat4f> mLocalTransforms;
utils::CString mName;
size_t mInstanceCount;
};
+1
View File
@@ -610,6 +610,7 @@ Program FMaterial::getProgramWithVariants(
void FMaterial::createAndCacheProgram(Program&& p, Variant variant) const noexcept {
auto program = mEngine.getDriverApi().createProgram(std::move(p));
mEngine.getDriverApi().setDebugTag(program.getId(), mName);
assert_invariant(program);
mCachedPrograms[variant.key] = program;
}
@@ -72,6 +72,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material)
mUniforms = UniformBuffer(material->getUniformInterfaceBlock().getSize());
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
BufferObjectBinding::UNIFORM, backend::BufferUsage::STATIC);
driver.setDebugTag(mUbHandle.getId(), material->getName());
}
// set the UBO, always descriptor 0
@@ -136,6 +137,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
mUniforms.setUniforms(other->getUniformBuffer());
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
BufferObjectBinding::UNIFORM, backend::BufferUsage::DYNAMIC);
driver.setDebugTag(mUbHandle.getId(), material->getName());
}
// set the UBO, always descriptor 0
@@ -25,6 +25,8 @@
#include <math/mat4.h>
#include <math/norm.h>
#include <utils/CString.h>
namespace filament {
using namespace backend;
@@ -123,6 +125,11 @@ FMorphTargetBuffer::FMorphTargetBuffer(FEngine& engine, const Builder& builder)
getHeight(mVertexCount),
mCount,
TextureUsage::DEFAULT);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mPbHandle.getId(), name);
driver.setDebugTag(mTbHandle.getId(), std::move(name));
}
}
void FMorphTargetBuffer::terminate(FEngine& engine) {
+48 -25
View File
@@ -656,16 +656,16 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// on qualcomm hardware -- we might need a backend dependent toggle at some point
const PostProcessManager::ColorGradingConfig colorGradingConfig{
.asSubpass =
hasColorGrading &&
msaaSampleCount <= 1 &&
!bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled &&
driver.isFrameBufferFetchSupported() &&
hasColorGrading &&
!bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled &&
!engine.debug.renderer.disable_subpasses,
.customResolve =
msaaOptions.customResolve &&
msaaSampleCount > 1 &&
hasColorGrading &&
driver.isFrameBufferFetchMultiSampleSupported() &&
msaaOptions.customResolve &&
hasColorGrading &&
!engine.debug.renderer.disable_subpasses,
.translucent = needsAlphaChannel,
.fxaa = hasFXAA,
@@ -674,6 +674,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
TextureFormat::RGBA8 : getLdrFormat(needsAlphaChannel)
};
// by construction (msaaSampleCount) both asSubpass and customResolve can't be true
assert_invariant(colorGradingConfig.asSubpass + colorGradingConfig.customResolve < 2);
// whether we're scaled at all
bool scaled = any(notEqual(scale, float2(1.0f)));
@@ -696,13 +699,12 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
CameraInfo cameraInfo = view.computeCameraInfo(engine);
// when colorgrading-as-subpass is active, we know that many other effects are disabled
// such as dof, bloom. Moreover, if fxaa and scaling are not enabled, we're essentially in
// a very fast rendering path -- in this case, we would need an extra blit to "resolve" the
// buffer padding (because there are no other pass that can do it as a side effect).
// In this case, it is better to skip the padding, which won't be helping much.
const bool noBufferPadding = (colorGradingConfig.asSubpass && !hasFXAA && !scaled)
|| engine.debug.renderer.disable_buffer_padding;
// If fxaa and scaling are not enabled, we're essentially in a very fast rendering path -- in
// this case, we would need an extra blit to "resolve" the buffer padding (because there are no
// other pass that can do it as a side effect). In this case, it is better to skip the padding,
// which won't be helping much.
const bool noBufferPadding
= (!hasFXAA && !scaled) || engine.debug.renderer.disable_buffer_padding;
// guardBand must be a multiple of 16 to guarantee the same exact rendering up to 4 mip levels.
float const guardBand = guardBandOptions.enabled ? 16.0f : 0.0f;
@@ -948,7 +950,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
.scale = aoOptions.resolution,
.picking = view.hasPicking()
});
blackboard["structure"] = structure;
const auto picking = picking_;
@@ -1003,7 +1004,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
ssReflectionsOptions.enabled ? TextureFormat::RGBA16F : TextureFormat::R11F_G11F_B10F,
view.getCameraUser().getFieldOfView(Camera::Fov::VERTICAL), config.scale);
config.ssrLodOffset = ssrConfig.lodOffset;
blackboard["ssr"] = ssrConfig.ssr;
// --------------------------------------------------------------------------------------------
// screen-space reflections pass
@@ -1108,24 +1108,39 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
);
// the color pass itself + color-grading as subpass if needed
auto colorPassOutput = RendererUtils::colorPass(fg, "Color Pass", mEngine, view,
auto colorPassOutput = RendererUtils::colorPass(fg, "Color Pass", mEngine, view, {
.shadows = blackboard.get<FrameGraphTexture>("shadows"),
.ssao = blackboard.get<FrameGraphTexture>("ssao"),
.ssr = ssrConfig.ssr,
.structure = structure
},
colorBufferDesc, config, colorGradingConfigForColor, pass.getExecutor());
if (view.isScreenSpaceRefractionEnabled() && !pass.empty()) {
// this cancels the colorPass() call above if refraction is active.
// the color pass + refraction + color-grading as subpass if needed
const auto [output, enabled] = RendererUtils::refractionPass(fg, mEngine, view,
// This cancels the colorPass() call above if refraction is active.
// The color pass + refraction + color-grading as subpass if needed
auto const output = RendererUtils::refractionPass(fg, mEngine, view, {
.shadows = blackboard.get<FrameGraphTexture>("shadows"),
.ssao = blackboard.get<FrameGraphTexture>("ssao"),
.ssr = ssrConfig.ssr,
.structure = structure
},
config, ssrConfig, colorGradingConfigForColor, pass);
colorPassOutput = output;
hasScreenSpaceRefraction = enabled;
hasScreenSpaceRefraction = output.has_value();
if (hasScreenSpaceRefraction) {
colorPassOutput = output.value();
}
}
if (colorGradingConfig.customResolve) {
assert_invariant(fg.getDescriptor(colorPassOutput.linearColor).samples <= 1);
// TODO: we have to "uncompress" (i.e. detonemap) the color buffer here because it's used
// by many other passes (Bloom, TAA, DoF, etc...). We could make this more
// efficient by using ARM_shader_framebuffer_fetch. We use a load/store (i.e.
// subpass) here because it's more convenient.
colorPassOutput = ppm.customResolveUncompressPass(fg, colorPassOutput);
colorPassOutput.linearColor =
ppm.customResolveUncompressPass(fg, colorPassOutput.linearColor);
}
// export the color buffer if screen-space reflections are enabled
@@ -1141,7 +1156,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// The "output" of this pass is going to be used during the next frame as
// an "import".
builder.sideEffect();
data.history = builder.sample(colorPassOutput); // FIXME: an access must be declared for detach(), why?
// we can't use colorPassOutput here because it could be tonemapped
data.history = builder.sample(colorPassOutput.linearColor); // FIXME: an access must be declared for detach(), why?
}, [&view, projection](FrameGraphResources const& resources, auto const& data,
backend::DriverApi&) {
auto& history = view.getFrameHistory();
@@ -1151,14 +1168,20 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
});
}
FrameGraphId<FrameGraphTexture> input = colorPassOutput;
// this is the output of the color pass / input to post processing,
// this is only used later for comparing it with the output after post-processing
FrameGraphId<FrameGraphTexture> const postProcessInput = colorGradingConfig.asSubpass ?
colorPassOutput.tonemappedColor :
colorPassOutput.linearColor;
// input can change below
FrameGraphId<FrameGraphTexture> input = postProcessInput;
// Resolve depth -- which might be needed because of TAA or DoF. This pass will be culled
// if the depth is not used below or if the depth is not MS (e.g. it could have been
// auto-resolved).
// In practice, this is used on Vulkan and older Metal devices.
auto depth = blackboard.get<FrameGraphTexture>("depth");
depth = ppm.resolve(fg, "Resolved Depth Buffer", depth, { .levels = 1 });
auto depth = ppm.resolve(fg, "Resolved Depth Buffer", colorPassOutput.depth, { .levels = 1 });
// Debug: CSM visualisation
if (UTILS_UNLIKELY(engine.debug.shadowmap.visualize_cascades &&
@@ -1274,7 +1297,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// The intermediate buffer is accomplished with a "fake" opaqueBlit (i.e. blit) operation.
const bool outputIsSwapChain =
(input == colorPassOutput) && (viewRenderTarget == mRenderTargetHandle);
(input == postProcessInput) && (viewRenderTarget == mRenderTargetHandle);
if (mightNeedFinalBlit) {
if (blendModeTranslucent ||
xvp != svp ||
+7 -1
View File
@@ -25,8 +25,9 @@
#include <math/half.h>
#include <math/mat4.h>
#include <string.h>
#include <utils/CString.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
@@ -81,6 +82,11 @@ FSkinningBuffer::FSkinningBuffer(FEngine& engine, const Builder& builder)
BufferObjectBinding::UNIFORM,
BufferUsage::DYNAMIC);
if (auto name = builder.getName(); !name.empty()) {
// TODO: We should also tag the texture created inside createIndicesAndWeightsHandle.
driver.setDebugTag(mHandle.getId(), std::move(name));
}
if (builder->mInitialize) {
// initialize the bones to identity (before rounding up)
auto* out = driver.allocatePod<PerRenderableBoneUib::BoneData>(mBoneCount);
+5 -1
View File
@@ -23,10 +23,10 @@
#include <backend/PixelBufferDescriptor.h>
#include <utils/CString.h>
#include <utils/Panic.h>
#include <filament/Stream.h>
namespace filament {
using namespace backend;
@@ -80,6 +80,10 @@ FStream::FStream(FEngine& engine, const Builder& builder) noexcept
} else {
mStreamHandle = engine.getDriverApi().createStreamAcquired();
}
if (auto name = builder.getName(); !name.empty()) {
engine.getDriverApi().setDebugTag(mStreamHandle.getId(), std::move(name));
}
}
void FStream::terminate(FEngine& engine) noexcept {
+5
View File
@@ -252,7 +252,12 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) {
mHandle = driver.importTexture(builder->mImportedId,
mTarget, mLevelCount, mFormat, mSampleCount, mWidth, mHeight, mDepth, mUsage);
}
mHandleForSampling = mHandle;
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), std::move(name));
}
}
// frees driver resources, object becomes invalid
+10 -1
View File
@@ -29,6 +29,7 @@
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
@@ -259,7 +260,9 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
mBufferCount, mDeclaredAttributes.count(), mAttributes);
mHandle = driver.createVertexBuffer(mVertexCount, mVertexBufferInfoHandle);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), name);
}
// calculate buffer sizes
size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {};
@@ -287,6 +290,9 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
if (!mBufferObjects[i]) {
BufferObjectHandle bo = driver.createBufferObject(bufferSizes[i],
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(bo.getId(), name);
}
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
@@ -303,6 +309,9 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
if (!mBufferObjects[i]) {
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(bo.getId(), name);
}
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.54.0"
spec.version = "1.54.1"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.54.0/filament-v1.54.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.54.1/filament-v1.54.1-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
+7 -1
View File
@@ -56,7 +56,13 @@ bool operator==(const MaterialKey& k1, const MaterialKey& k2) {
(k1.volumeThicknessUV == k2.volumeThicknessUV) &&
(k1.hasSheen == k2.hasSheen) &&
(k1.hasIOR == k2.hasIOR) &&
(k1.hasVolume == k2.hasVolume);
(k1.hasVolume == k2.hasVolume) &&
(k1.hasSpecular == k2.hasSpecular) &&
(k1.hasSpecularTexture == k2.hasSpecularTexture) &&
(k1.hasSpecularColorTexture == k2.hasSpecularColorTexture) &&
(k1.specularTextureUV == k2.specularTextureUV) &&
(k1.specularColorTextureUV == k2.specularColorTextureUV)
;
}
// Filament supports up to 2 UV sets. glTF has arbitrary texcoord set indices, but it allows
+28 -2
View File
@@ -259,6 +259,24 @@ MaterialInstance* UbershaderProvider::createMaterialInstance(MaterialKey* config
mi->setParameter("occlusionUvMatrix", identity);
mi->setParameter("emissiveUvMatrix", identity);
// set to -1 all possibly optional parameters
for (auto const* param : {
"clearCoatIndex",
"clearCoatRoughnessIndex",
"clearCoatNormalIndex",
"sheenColorIndex",
"sheenRoughnessIndex",
"volumeThicknessUvMatrix",
"volumeThicknessIndex",
"transmissionIndex",
"specularIndex",
"specularColorIndex",
}) {
if (material->hasParameter(param)) {
mi->setParameter(param, -1);
}
}
if (config->hasClearCoat) {
mi->setParameter("clearCoatIndex",
getUvIndex(config->clearCoatUV, config->hasClearCoatTexture));
@@ -340,10 +358,10 @@ MaterialInstance* UbershaderProvider::createMaterialInstance(MaterialKey* config
mi->setParameter("sheenRoughnessMap", mDummyTexture, sampler);
}
if (mi->getMaterial()->hasParameter("ior")) {
if (material->hasParameter("ior")) {
mi->setParameter("ior", 1.5f);
}
if (mi->getMaterial()->hasParameter("reflectance")) {
if (material->hasParameter("reflectance")) {
mi->setParameter("reflectance", 0.5f);
}
@@ -355,6 +373,14 @@ MaterialInstance* UbershaderProvider::createMaterialInstance(MaterialKey* config
mi->setParameter("specularColorMap", mDummyTexture, sampler);
}
if (material->hasParameter("specularColorFactor")) {
mi->setParameter("specularColorFactor", float3(1.0f));
}
if (material->hasParameter("specularStrength")) {
mi->setParameter("specularStrength", 1.0f);
}
return mi;
}
-2
View File
@@ -905,8 +905,6 @@ int main(int argc, char** argv) {
debug.getPropertyAddress<bool>("d.shadowmap.focus_shadowcasters"));
ImGui::Checkbox("Disable light frustum alignment",
debug.getPropertyAddress<bool>("d.shadowmap.disable_light_frustum_align"));
ImGui::Checkbox("Depth clamp",
debug.getPropertyAddress<bool>("d.shadowmap.depth_clamp"));
bool debugDirectionalShadowmap;
if (debug.getProperty("d.shadowmap.debug_directional_shadowmap",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.54.0",
"version": "1.54.1",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",