diff --git a/README.md b/README.md index f2e6856085d..9fc2f823a5a 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.51.2' + implementation 'com.google.android.filament:filament-android:1.51.3' } ``` @@ -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.51.2' +pod 'Filament', '~> 1.51.3' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d60e6d45843..8cb9157a994 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -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.51.3 + + ## v1.51.2 - engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()` diff --git a/android/gradle.properties b/android/gradle.properties index c677c8262ab..187e11e9c1f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.51.2 +VERSION_NAME=1.51.3 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/docs/Materials.md.html b/docs/Materials.md.html index 1e63bcef0da..d34b5f5ef4e 100644 --- a/docs/Materials.md.html +++ b/docs/Materials.md.html @@ -1397,7 +1397,7 @@ : `string` Value -: Any of `opaque`, `transparent`, `fade`, `add`, `masked`, `multiply`, `screen`. Defaults to `opaque`. +: Any of `opaque`, `transparent`, `fade`, `add`, `masked`, `multiply`, `screen`, `custom`. Defaults to `opaque`. Description : Defines how/if the rendered object is blended with the content of the render target. @@ -1420,6 +1420,7 @@ of the material's output defines whether a fragment is discarded or not. Additionally, ALPHA_TO_COVERAGE is enabled for non-translucent views. See the maskThreshold section for more information. + - **Custom**: blending is enabled. But the blending function is user specified. See `blendFunction`. !!! Note When `blending` is set to `masked`, alpha to coverage is automatically enabled for the material. @@ -1432,6 +1433,36 @@ } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Blending and transparency: blendFunction + +Type +: `object` + +Fields +: `srcRGB`, `srcA`, `dstRGB`, `dstA` + +Description +: - *srcRGB*: source function applied to the RGB channels + - *srcA*: source function applied to the alpha channel + - *srcRGB*: destination function applied to the RGB channels + - *srcRGB*: destination function applied to the alpha channel + The values possible for each functions are one of `zero`, `one`, `srcColor`, `oneMinusSrcColor`, + `dstColor`, `oneMinusDstColor`, `srcAlpha`, `oneMinusSrcAlpha`, `dstAlpha`, + `oneMinusDstAlpha`, `srcAlphaSaturate` + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON +material { + blending : custom, + blendFunction : + { + srcRGB: one, + srcA: one, + dstRGB: oneMinusSrcColor, + dstA: oneMinusSrcAlpha + } + } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ### Blending and transparency: postLightingBlending Type diff --git a/filament/backend/include/private/backend/CommandStream.h b/filament/backend/include/private/backend/CommandStream.h index 985fa5fcd6e..f722794b52c 100644 --- a/filament/backend/include/private/backend/CommandStream.h +++ b/filament/backend/include/private/backend/CommandStream.h @@ -134,7 +134,7 @@ struct CommandType { public: template - static inline void execute(M&& method, D&& driver, CommandBase* base, intptr_t* next) noexcept { + static inline void execute(M&& method, D&& driver, CommandBase* base, intptr_t* next) { Command* self = static_cast(base); *next = align(sizeof(Command)); #if DEBUG_COMMAND_STREAM @@ -168,7 +168,7 @@ struct CommandType { class CustomCommand : public CommandBase { std::function mCommand; - static void execute(Driver&, CommandBase* base, intptr_t* next) noexcept; + static void execute(Driver&, CommandBase* base, intptr_t* next); public: inline CustomCommand(CustomCommand&& rhs) = default; inline explicit CustomCommand(std::function cmd) diff --git a/filament/backend/include/private/backend/Driver.h b/filament/backend/include/private/backend/Driver.h index 4b30bdad77a..527052378e6 100644 --- a/filament/backend/include/private/backend/Driver.h +++ b/filament/backend/include/private/backend/Driver.h @@ -76,7 +76,7 @@ class Driver { // the fn function will execute a batch of driver commands // this gives the driver a chance to wrap their execution in a meaningful manner // the default implementation simply calls fn - virtual void execute(std::function const& fn) noexcept; + virtual void execute(std::function const& fn); // This is called on debug build, or when enabled manually on the backend thread side. virtual void debugCommandBegin(CommandStream* cmds, diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h index 578ce5d6f1a..bcf4bfa902f 100644 --- a/filament/backend/include/private/backend/HandleAllocator.h +++ b/filament/backend/include/private/backend/HandleAllocator.h @@ -65,7 +65,7 @@ class HandleAllocator { * */ template - Handle allocateAndConstruct(ARGS&& ... args) noexcept { + Handle allocateAndConstruct(ARGS&& ... args) { Handle h{ allocateHandle() }; D* addr = handle_cast(h); new(addr) D(std::forward(args)...); @@ -97,7 +97,7 @@ class HandleAllocator { */ template typename std::enable_if_t, D>* - destroyAndConstruct(Handle const& handle, ARGS&& ... args) noexcept { + destroyAndConstruct(Handle const& handle, ARGS&& ... args) { assert_invariant(handle); D* addr = handle_cast(const_cast&>(handle)); assert_invariant(addr); @@ -163,7 +163,7 @@ class HandleAllocator { inline typename std::enable_if_t< std::is_pointer_v && std::is_base_of_v>, Dp> - handle_cast(Handle& handle) noexcept { + handle_cast(Handle& handle) { assert_invariant(handle); auto [p, tag] = handleToPointer(handle.getId()); @@ -185,7 +185,7 @@ class HandleAllocator { inline typename std::enable_if_t< std::is_pointer_v && std::is_base_of_v>, Dp> - handle_cast(Handle const& handle) noexcept { + handle_cast(Handle const& handle) { return handle_cast(const_cast&>(handle)); } @@ -317,7 +317,7 @@ class HandleAllocator { return (id & HANDLE_HEAP_FLAG) == 0u; } - HandleBase::HandleId allocateHandleSlow(size_t size) noexcept; + HandleBase::HandleId allocateHandleSlow(size_t size); void deallocateHandleSlow(HandleBase::HandleId id, size_t size) noexcept; // We inline this because it's just 4 instructions in the fast case diff --git a/filament/backend/src/CommandStream.cpp b/filament/backend/src/CommandStream.cpp index 29bb2184575..69730d688c8 100644 --- a/filament/backend/src/CommandStream.cpp +++ b/filament/backend/src/CommandStream.cpp @@ -149,7 +149,7 @@ void CommandType::Command::log() noexcept { // ------------------------------------------------------------------------------------------------ -void CustomCommand::execute(Driver&, CommandBase* base, intptr_t* next) noexcept { +void CustomCommand::execute(Driver&, CommandBase* base, intptr_t* next) { *next = CustomCommand::align(sizeof(CustomCommand)); static_cast(base)->mCommand(); static_cast(base)->~CustomCommand(); diff --git a/filament/backend/src/Driver.cpp b/filament/backend/src/Driver.cpp index ef165d7667b..f33f855d9f4 100644 --- a/filament/backend/src/Driver.cpp +++ b/filament/backend/src/Driver.cpp @@ -214,7 +214,7 @@ size_t Driver::getElementTypeSize(ElementType type) noexcept { Driver::~Driver() noexcept = default; -void Driver::execute(std::function const& fn) noexcept { +void Driver::execute(std::function const& fn) { fn(); } diff --git a/filament/backend/src/HandleAllocator.cpp b/filament/backend/src/HandleAllocator.cpp index fb58cb1b588..a5a9e7c98c8 100644 --- a/filament/backend/src/HandleAllocator.cpp +++ b/filament/backend/src/HandleAllocator.cpp @@ -107,7 +107,7 @@ void* HandleAllocator::handleToPointerSlow(HandleBase::HandleId id) } template -HandleBase::HandleId HandleAllocator::allocateHandleSlow(size_t size) noexcept { +HandleBase::HandleId HandleAllocator::allocateHandleSlow(size_t size) { void* p = ::malloc(size); std::unique_lock lock(mLock); diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index f0e23ccb033..f99a6b63a66 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1634,7 +1634,7 @@ return; } - ASSERT_PRECONDITION(bool(functions), "Attempting to bind an invalid Metal program."); + functions.validate(); auto [fragment, vertex] = functions.getRasterFunctions(); diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 196d1bd2e66..81cc54fb830 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -724,10 +724,19 @@ void OpenGLDriver::createTextureR(Handle th, SamplerType target, uint GLenum internalFormat = getInternalFormat(format); assert_invariant(internalFormat); - if (UTILS_UNLIKELY(usage & TextureUsage::PROTECTED)) { + if (any(usage & TextureUsage::PROTECTED)) { // renderbuffers don't have a protected mode, so we need to use a texture // because protected textures are only supported on GLES 3.2, MSAA will be available. usage |= TextureUsage::SAMPLEABLE; + } else if (any(usage & TextureUsage::UPLOADABLE)) { + // if we have the uploadable flag, we also need to use a texture + usage |= TextureUsage::SAMPLEABLE; + } else if (target != SamplerType::SAMPLER_2D) { + // renderbuffers can only be 2D + usage |= TextureUsage::SAMPLEABLE; + } else if (levels > 1) { + // renderbuffers can't have mip levels + usage |= TextureUsage::SAMPLEABLE; } auto& gl = mContext; @@ -803,13 +812,6 @@ void OpenGLDriver::createTextureR(Handle th, SamplerType target, uint textureStorage(t, w, h, depth, bool(usage & TextureUsage::PROTECTED)); } } else { - assert_invariant(any(usage & ( - TextureUsage::COLOR_ATTACHMENT | - TextureUsage::DEPTH_ATTACHMENT | - TextureUsage::STENCIL_ATTACHMENT))); - assert_invariant(levels == 1); - assert_invariant(target == SamplerType::SAMPLER_2D); - assert_invariant(none(usage & TextureUsage::PROTECTED)); t->gl.internalFormat = internalFormat; t->gl.target = GL_RENDERBUFFER; glGenRenderbuffers(1, &t->gl.id); diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index 6dad88c082b..100148101d5 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -268,13 +268,13 @@ class OpenGLDriver final : public DriverBase { HandleAllocatorGL mHandleAllocator; template - Handle initHandle(ARGS&& ... args) noexcept { + Handle initHandle(ARGS&& ... args) { return mHandleAllocator.allocateAndConstruct(std::forward(args) ...); } template typename std::enable_if::value, D>::type* - construct(Handle const& handle, ARGS&& ... args) noexcept { + construct(Handle const& handle, ARGS&& ... args) { return mHandleAllocator.destroyAndConstruct(handle, std::forward(args) ...); } @@ -288,7 +288,7 @@ class OpenGLDriver final : public DriverBase { typename std::enable_if_t< std::is_pointer_v && std::is_base_of_v>, Dp> - handle_cast(Handle& handle) noexcept { + handle_cast(Handle& handle) { return mHandleAllocator.handle_cast(handle); } @@ -296,7 +296,7 @@ class OpenGLDriver final : public DriverBase { inline typename std::enable_if_t< std::is_pointer_v && std::is_base_of_v>, Dp> - handle_cast(Handle const& handle) noexcept { + handle_cast(Handle const& handle) { return mHandleAllocator.handle_cast(handle); } diff --git a/filament/src/MaterialParser.cpp b/filament/src/MaterialParser.cpp index 3b492bd6a4f..5427835a282 100644 --- a/filament/src/MaterialParser.cpp +++ b/filament/src/MaterialParser.cpp @@ -277,6 +277,16 @@ bool MaterialParser::getBlendingMode(BlendingMode* value) const noexcept { return mImpl.getFromSimpleChunk(ChunkType::MaterialBlendingMode, reinterpret_cast(value)); } +bool MaterialParser::getCustomBlendFunction(std::array* value) const noexcept { + uint32_t blendFunctions = 0; + bool const result = mImpl.getFromSimpleChunk(ChunkType::MaterialBlendFunction, &blendFunctions); + (*value)[0] = BlendFunction((blendFunctions >> 24) & 0xFF); + (*value)[1] = BlendFunction((blendFunctions >> 16) & 0xFF); + (*value)[2] = BlendFunction((blendFunctions >> 8) & 0xFF); + (*value)[3] = BlendFunction((blendFunctions >> 0) & 0xFF); + return result; +} + bool MaterialParser::getMaskThreshold(float* value) const noexcept { return mImpl.getFromSimpleChunk(ChunkType::MaterialMaskThreshold, value); } diff --git a/filament/src/MaterialParser.h b/filament/src/MaterialParser.h index 955c8b2a152..638e93d5662 100644 --- a/filament/src/MaterialParser.h +++ b/filament/src/MaterialParser.h @@ -102,6 +102,7 @@ class MaterialParser { bool getShading(Shading*) const noexcept; bool getBlendingMode(BlendingMode*) const noexcept; + bool getCustomBlendFunction(std::array*) const noexcept; bool getMaskThreshold(float*) const noexcept; bool getAlphaToCoverageSet(bool*) const noexcept; bool getAlphaToCoverage(bool*) const noexcept; diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 3f21ce872d4..3fbd7f41444 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -450,11 +450,8 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph .levels = uint8_t(levelCount), .format = isES2 ? TextureFormat::DEPTH24 : TextureFormat::DEPTH32F }); - // workaround: since we have levels, this implies SAMPLEABLE (because of the gl - // backend, which implements non-sampleables with renderbuffers, which don't have levels). - // (should the gl driver revert to textures, in that case?) data.depth = builder.write(data.depth, - FrameGraphTexture::Usage::DEPTH_ATTACHMENT | FrameGraphTexture::Usage::SAMPLEABLE); + FrameGraphTexture::Usage::DEPTH_ATTACHMENT); if (config.picking) { data.picking = builder.createTexture("Picking Buffer", { diff --git a/filament/src/RendererUtils.cpp b/filament/src/RendererUtils.cpp index 8c63be153eb..3e1cafdbc9d 100644 --- a/filament/src/RendererUtils.cpp +++ b/filament/src/RendererUtils.cpp @@ -16,17 +16,35 @@ #include "RendererUtils.h" +#include "PostProcessManager.h" + #include "details/Engine.h" #include "details/View.h" #include "fg/FrameGraph.h" #include "fg/FrameGraphId.h" #include "fg/FrameGraphResources.h" +#include "fg/FrameGraphTexture.h" + +#include +#include +#include + +#include +#include +#include +#include #include #include #include +#include +#include + +#include +#include + namespace filament { using namespace backend; @@ -153,16 +171,9 @@ FrameGraphId RendererUtils::colorPass( data.depth = builder.read(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT); data.color = builder.write(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.depth = builder.write(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT); if (engine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW) { - // Add sampleable usage flag for depth in multiview rendering, othewise it's - // treated as renderbuffer in the backend and crashed. - data.depth = builder.write(data.depth, - FrameGraphTexture::Usage::DEPTH_ATTACHMENT | - FrameGraphTexture::Usage::SAMPLEABLE); layerCount = engine.getConfig().stereoscopicEyeCount; - } else { - data.depth = builder.write(data.depth, - FrameGraphTexture::Usage::DEPTH_ATTACHMENT); } /* diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 66bf9ee67b2..5deb07bd51b 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -323,12 +323,8 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder) parser->getMaskThreshold(&mMaskThreshold); } - // The fade blending mode only affects shading. For proper sorting we need to - // treat this blending mode as a regular transparent blending operation. - if (UTILS_UNLIKELY(mBlendingMode == BlendingMode::FADE)) { - mRenderBlendingMode = BlendingMode::TRANSPARENT; - } else { - mRenderBlendingMode = mBlendingMode; + if (mBlendingMode == BlendingMode::CUSTOM) { + parser->getCustomBlendFunction(&mCustomBlendFunctions); } if (mShading == Shading::UNLIT) { @@ -381,6 +377,12 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder) mRasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_COLOR; mRasterState.depthWrite = false; break; + case BlendingMode::CUSTOM: + mRasterState.blendFunctionSrcRGB = mCustomBlendFunctions[0]; + mRasterState.blendFunctionSrcAlpha = mCustomBlendFunctions[1]; + mRasterState.blendFunctionDstRGB = mCustomBlendFunctions[2]; + mRasterState.blendFunctionDstAlpha = mCustomBlendFunctions[3]; + mRasterState.depthWrite = false; } bool depthWriteSet = false; diff --git a/filament/src/details/Material.h b/filament/src/details/Material.h index 85e7932a0c8..9bdd30ae01f 100644 --- a/filament/src/details/Material.h +++ b/filament/src/details/Material.h @@ -127,7 +127,6 @@ class FMaterial : public Material { Shading getShading() const noexcept { return mShading; } Interpolation getInterpolation() const noexcept { return mInterpolation; } BlendingMode getBlendingMode() const noexcept { return mBlendingMode; } - BlendingMode getRenderBlendingMode() const noexcept { return mRenderBlendingMode; } VertexDomain getVertexDomain() const noexcept { return mVertexDomain; } MaterialDomain getMaterialDomain() const noexcept { return mMaterialDomain; } CullingMode getCullingMode() const noexcept { return mCullingMode; } @@ -223,13 +222,13 @@ class FMaterial : public Material { mutable std::array, VARIANT_COUNT> mCachedPrograms; backend::RasterState mRasterState; - BlendingMode mRenderBlendingMode = BlendingMode::OPAQUE; TransparencyMode mTransparencyMode = TransparencyMode::DEFAULT; bool mIsVariantLit = false; backend::FeatureLevel mFeatureLevel = backend::FeatureLevel::FEATURE_LEVEL_1; Shading mShading = Shading::UNLIT; BlendingMode mBlendingMode = BlendingMode::OPAQUE; + std::array mCustomBlendFunctions = {}; Interpolation mInterpolation = Interpolation::SMOOTH; VertexDomain mVertexDomain = VertexDomain::OBJECT; MaterialDomain mMaterialDomain = MaterialDomain::SURFACE; diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 3ed06a44db5..8543885f0a3 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.51.2" + spec.version = "1.51.3" 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.51.2/filament-v1.51.2-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.3/filament-v1.51.3-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/libs/filabridge/include/filament/MaterialChunkType.h b/libs/filabridge/include/filament/MaterialChunkType.h index 6cff20039a1..27d50c1683a 100644 --- a/libs/filabridge/include/filament/MaterialChunkType.h +++ b/libs/filabridge/include/filament/MaterialChunkType.h @@ -59,6 +59,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t { MaterialFeatureLevel = charTo64bitNum("MAT_FEAT"), MaterialShading = charTo64bitNum("MAT_SHAD"), MaterialBlendingMode = charTo64bitNum("MAT_BLEN"), + MaterialBlendFunction = charTo64bitNum("MAT_BLFN"), MaterialTransparencyMode = charTo64bitNum("MAT_TRMD"), MaterialMaskThreshold = charTo64bitNum("MAT_THRS"), MaterialShadowMultiplier = charTo64bitNum("MAT_SHML"), diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index ebe2ccce20e..9f348481895 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -80,6 +80,8 @@ enum class BlendingMode : uint8_t { MULTIPLY, //! material brightens what's behind it SCREEN, + //! custom blending function + CUSTOM, }; /** diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index 2e2d096309f..4b66965d746 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -227,6 +227,7 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { using ShaderQuality = filament::ShaderQuality; using BlendingMode = filament::BlendingMode; + using BlendFunction = filament::backend::BlendFunction; using Shading = filament::Shading; using Interpolation = filament::Interpolation; using VertexDomain = filament::VertexDomain; @@ -407,6 +408,15 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { */ MaterialBuilder& blending(BlendingMode blending) noexcept; + /** + * Set the blend function for this material. blending must be et to CUSTOM. + */ + MaterialBuilder& customBlendFunctions( + BlendFunction srcRGB, + BlendFunction srcA, + BlendFunction dstRGB, + BlendFunction dstA) noexcept; + /** * Set the blending mode of the post-lighting color for this material. * Only OPAQUE, TRANSPARENT and ADD are supported, the default is TRANSPARENT. @@ -828,6 +838,7 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1; BlendingMode mBlendingMode = BlendingMode::OPAQUE; BlendingMode mPostLightingBlendingMode = BlendingMode::TRANSPARENT; + std::array mCustomBlendFunctions = {}; CullingMode mCullingMode = CullingMode::BACK; Shading mShading = Shading::LIT; MaterialDomain mMaterialDomain = MaterialDomain::SURFACE; diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 746c49caf3c..109d6e43da5 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -388,6 +388,16 @@ MaterialBuilder& MaterialBuilder::blending(BlendingMode blending) noexcept { return *this; } +MaterialBuilder& MaterialBuilder::customBlendFunctions( + BlendFunction srcRGB, BlendFunction srcA, + BlendFunction dstRGB, BlendFunction dstA) noexcept { + mCustomBlendFunctions[0] = srcRGB; + mCustomBlendFunctions[1] = srcA; + mCustomBlendFunctions[2] = dstRGB; + mCustomBlendFunctions[3] = dstA; + return *this; +} + MaterialBuilder& MaterialBuilder::postLightingBlending(BlendingMode blending) noexcept { mPostLightingBlendingMode = blending; return *this; @@ -1510,6 +1520,16 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo& container.emplace(ChunkType::MaterialDoubleSided, mDoubleSided); container.emplace(ChunkType::MaterialBlendingMode, static_cast(mBlendingMode)); + + if (mBlendingMode == BlendingMode::CUSTOM) { + uint32_t const blendFunctions = + (uint32_t(mCustomBlendFunctions[0]) << 24) | + (uint32_t(mCustomBlendFunctions[1]) << 16) | + (uint32_t(mCustomBlendFunctions[2]) << 8) | + (uint32_t(mCustomBlendFunctions[3]) << 0); + container.emplace< uint32_t >(ChunkType::MaterialBlendFunction, blendFunctions); + } + container.emplace(ChunkType::MaterialTransparencyMode, static_cast(mTransparencyMode)); container.emplace(ChunkType::MaterialReflectionMode, diff --git a/libs/filamat/src/shaders/ShaderGenerator.cpp b/libs/filamat/src/shaders/ShaderGenerator.cpp index af4f1198de9..92508566857 100644 --- a/libs/filamat/src/shaders/ShaderGenerator.cpp +++ b/libs/filamat/src/shaders/ShaderGenerator.cpp @@ -157,6 +157,9 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(utils::io::sstream& case BlendingMode::SCREEN: CodeGenerator::generateDefine(out, "BLEND_MODE_SCREEN", true); break; + case BlendingMode::CUSTOM: + CodeGenerator::generateDefine(out, "BLEND_MODE_CUSTOM", true); + break; } switch (material.postLightingBlendingMode) { @@ -175,6 +178,9 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(utils::io::sstream& case BlendingMode::SCREEN: CodeGenerator::generateDefine(out, "POST_LIGHTING_BLEND_MODE_SCREEN", true); break; + case BlendingMode::CUSTOM: + CodeGenerator::generateDefine(out, "POST_LIGHTING_BLEND_MODE_CUSTOM", true); + break; default: break; } diff --git a/libs/gltfio/src/UbershaderProvider.cpp b/libs/gltfio/src/UbershaderProvider.cpp index 8196273f232..7e184f648d4 100644 --- a/libs/gltfio/src/UbershaderProvider.cpp +++ b/libs/gltfio/src/UbershaderProvider.cpp @@ -338,6 +338,7 @@ const char* toString(BlendingMode blendingMode) noexcept { case BlendingMode::FADE: return "fade"; case BlendingMode::MULTIPLY: return "multiply"; case BlendingMode::SCREEN: return "screen"; + case BlendingMode::CUSTOM: return "custom"; } } diff --git a/libs/matdbg/src/CommonWriter.h b/libs/matdbg/src/CommonWriter.h index 6d2afc1ee4e..9e49df9a862 100644 --- a/libs/matdbg/src/CommonWriter.h +++ b/libs/matdbg/src/CommonWriter.h @@ -65,6 +65,7 @@ const char* toString(BlendingMode blendingMode) noexcept { case BlendingMode::FADE: return "fade"; case BlendingMode::MULTIPLY: return "multiply"; case BlendingMode::SCREEN: return "screen"; + case BlendingMode::CUSTOM: return "custom"; } return "--"; } diff --git a/libs/uberz/src/WritableArchive.cpp b/libs/uberz/src/WritableArchive.cpp index d7fface9472..09e601f8684 100644 --- a/libs/uberz/src/WritableArchive.cpp +++ b/libs/uberz/src/WritableArchive.cpp @@ -76,6 +76,8 @@ static string_view readBlendingMode(string_view cursor, BlendingMode* blending) *blending = BlendingMode::MULTIPLY; } else if (sz = readPrefix("screen"sv, cursor); sz > 0) { *blending = BlendingMode::SCREEN; + } else if (sz = readPrefix("custom"sv, cursor); sz > 0) { + *blending = BlendingMode::CUSTOM; } return { cursor.data(), sz }; } diff --git a/libs/utils/include/utils/Panic.h b/libs/utils/include/utils/Panic.h index b4ec032c81b..c658da4b14f 100644 --- a/libs/utils/include/utils/Panic.h +++ b/libs/utils/include/utils/Panic.h @@ -250,42 +250,71 @@ namespace utils { */ class UTILS_PUBLIC Panic { public: + + using PanicHandlerCallback = void(*)(void* user, Panic const& panic); + + /** + * Sets a user-defined handler for the Panic. If exceptions are enabled, the concrete Panic + * object will be thrown upon return; moreover it is acceptable to throw from the provided + * callback, but it is unsafe to throw the Panic object itself, since it's just an interface. + * It is also acceptable to abort from the callback. If exceptions are not enabled, std::abort() + * will be automatically called upon return. + * + * The PanicHandlerCallback can be called from any thread. + * + * Caveat: this API can misbehave if is used as a static library in multiple translation units, + * some of these translation units might not see the callback. + * + * @param handler pointer to the user defined handler for the Panic + * @param user user pointer given back to the callback + */ + static void setPanicHandler(PanicHandlerCallback handler, void* user) noexcept; + + virtual ~Panic() noexcept; /** - * @return a detailed description of the error + * @return a formatted and detailed description of the error including all available + * information. * @see std::exception */ virtual const char* what() const noexcept = 0; /** - * Get the function name where the panic was detected + * Get the reason string for the panic + * @return a C string containing the reason for the panic + */ + virtual const char* getReason() const noexcept = 0; + + /** + * Get the function name where the panic was detected. On debug build the fully qualified + * function name is returned; on release builds only the function name is. * @return a C string containing the function name where the panic was detected */ virtual const char* getFunction() const noexcept = 0; /** - * Get the file name where the panic was detected + * Get the file name where the panic was detected. Only available on debug builds. * @return a C string containing the file name where the panic was detected */ virtual const char* getFile() const noexcept = 0; /** - * Get the line number in the file where the panic was detected + * Get the line number in the file where the panic was detected. Only available on debug builds. * @return an integer containing the line number in the file where the panic was detected */ virtual int getLine() const noexcept = 0; /** - * Logs this exception to the system-log + * Get the CallStack when the panic was detected if available. + * @return the CallStack when the panic was detected */ - virtual void log() const noexcept = 0; + virtual const CallStack& getCallStack() const noexcept = 0; /** - * Get the CallStack when the panic was detected - * @return the CallStack when the panic was detected + * Logs this exception to the system-log */ - virtual const CallStack& getCallStack() const noexcept = 0; + virtual void log() const noexcept = 0; }; // ----------------------------------------------------------------------------------------------- @@ -305,6 +334,7 @@ class UTILS_PUBLIC TPanic : public Panic { const char* what() const noexcept override; // Panic interface + const char* getReason() const noexcept override; const char* getFunction() const noexcept override; const char* getFile() const noexcept override; int getLine() const noexcept override; diff --git a/libs/utils/include/utils/debug.h b/libs/utils/include/utils/debug.h index 4c118725177..0f6ecdb27e1 100644 --- a/libs/utils/include/utils/debug.h +++ b/libs/utils/include/utils/debug.h @@ -20,6 +20,7 @@ #include namespace utils { +UTILS_PUBLIC void panic(const char *func, const char * file, int line, const char *assertion) noexcept; } // namespace filament diff --git a/libs/utils/src/Panic.cpp b/libs/utils/src/Panic.cpp index 023a79b2477..addec296c10 100644 --- a/libs/utils/src/Panic.cpp +++ b/libs/utils/src/Panic.cpp @@ -14,17 +14,64 @@ * limitations under the License. */ +#include #include +#include +#include -#include -#include #include #include +#include +#include +#include +#include -#include +#include +#include +#include +#include namespace utils { +// ------------------------------------------------------------------------------------------------ + +class UserPanicHandler { + struct CallBack { + Panic::PanicHandlerCallback handler = nullptr; + void* user = nullptr; + void call(Panic const& panic) const noexcept { + if (UTILS_UNLIKELY(handler)) { + handler(user, panic); + } + } + }; + + mutable std::mutex mLock{}; + CallBack mCallBack{}; + + CallBack getCallback() const noexcept { + std::lock_guard const lock(mLock); + return mCallBack; + } + +public: + static UserPanicHandler& get() noexcept { + static UserPanicHandler data; + return data; + } + + void call(Panic const& panic) const noexcept { + getCallback().call(panic); + } + + void set(Panic::PanicHandlerCallback handler, void* user) noexcept { + std::lock_guard const lock(mLock); + mCallBack = { handler, user }; + } +}; + +// ------------------------------------------------------------------------------------------------ + static std::string formatString(const char* format, va_list args) noexcept { std::string reason; @@ -35,10 +82,12 @@ static std::string formatString(const char* format, va_list args) noexcept { if (n >= 0) { ++n; // for the nul-terminating char - char* buf = new char[n]; - vsnprintf(buf, size_t(n), format, args); - reason.assign(buf); - delete [] buf; + char* const buf = new(std::nothrow) char[n]; + if (buf) { + vsnprintf(buf, size_t(n), format, args); + reason.assign(buf); + delete [] buf; + } } return reason; } @@ -63,8 +112,16 @@ static std::string panicString( #endif } +// ------------------------------------------------------------------------------------------------ + Panic::~Panic() noexcept = default; +void Panic::setPanicHandler(PanicHandlerCallback handler, void* user) noexcept { + UserPanicHandler::get().set(handler, user); +} + +// ------------------------------------------------------------------------------------------------ + template TPanic::TPanic(std::string reason) : m_reason(std::move(reason)) { @@ -88,6 +145,11 @@ const char* TPanic::what() const noexcept { return m_msg.c_str(); } +template +const char* TPanic::getReason() const noexcept { + return m_reason.c_str(); +} + template const char* TPanic::getFunction() const noexcept { return m_function; @@ -135,13 +197,22 @@ template void TPanic::panic(char const* function, char const* file, int line, const char* format, ...) { va_list args; va_start(args, format); - std::string reason(formatString(format, args)); + std::string const reason(formatString(format, args)); va_end(args); T e(function, formatFile(file), line, reason); + + // always log the Panic at the point it is detected e.log(); + + // Call the user provided handler + UserPanicHandler::get().call(e); + + // if exceptions are enabled, throw now. #ifdef __EXCEPTIONS - throw e; + throw e; #endif + + // and finally abort if we somehow get here std::abort(); } @@ -152,7 +223,7 @@ namespace details { void panicLog(char const* function, char const* file, int line, const char* format, ...) noexcept { va_list args; va_start(args, format); - std::string reason(formatString(format, args)); + std::string const reason(formatString(format, args)); va_end(args); const std::string msg = panicString("" /* no extra message */, diff --git a/tools/matc/src/matc/ParametersProcessor.cpp b/tools/matc/src/matc/ParametersProcessor.cpp index 77a74fa9f93..828597e51a6 100644 --- a/tools/matc/src/matc/ParametersProcessor.cpp +++ b/tools/matc/src/matc/ParametersProcessor.cpp @@ -671,6 +671,7 @@ static bool processBlending(MaterialBuilder& builder, const JsonishValue& value) { "fade", MaterialBuilder::BlendingMode::FADE }, { "multiply", MaterialBuilder::BlendingMode::MULTIPLY }, { "screen", MaterialBuilder::BlendingMode::SCREEN }, + { "custom", MaterialBuilder::BlendingMode::CUSTOM }, }; auto jsonString = value.toJsonString(); if (!isStringValidEnum(strToEnum, jsonString->getString())) { @@ -681,6 +682,52 @@ static bool processBlending(MaterialBuilder& builder, const JsonishValue& value) return true; } +static bool processBlendFunction(MaterialBuilder& builder, const JsonishValue& value) { + static const std::unordered_map strToEnum{ + { "zero", MaterialBuilder::BlendFunction::ZERO }, + { "one", MaterialBuilder::BlendFunction::ONE }, + { "srcColor", MaterialBuilder::BlendFunction::SRC_COLOR }, + { "oneMinusSrcColor", MaterialBuilder::BlendFunction::ONE_MINUS_SRC_COLOR }, + { "dstColor", MaterialBuilder::BlendFunction::DST_COLOR }, + { "oneMinusDstColor", MaterialBuilder::BlendFunction::ONE_MINUS_DST_COLOR }, + { "srcAlpha", MaterialBuilder::BlendFunction::SRC_ALPHA }, + { "oneMinusSrcAlpha", MaterialBuilder::BlendFunction::ONE_MINUS_SRC_ALPHA }, + { "dstAlpha", MaterialBuilder::BlendFunction::DST_ALPHA }, + { "oneMinusDstAlpha", MaterialBuilder::BlendFunction::ONE_MINUS_DST_ALPHA }, + { "srcAlphaSaturate", MaterialBuilder::BlendFunction::SRC_ALPHA_SATURATE }, + }; + + if (value.getType() != JsonishValue::Type::OBJECT) { + std::cerr << "blendFunction must be an OBJECT." << std::endl; + } + + JsonishObject const* const jsonObject = value.toJsonObject(); + + MaterialBuilder::BlendFunction srcRGB, srcA, dstRGB, dstA; + std::pair functions[] = { + { "srcRGB", &srcRGB }, + { "srcA", &srcA }, + { "dstRGB", &dstRGB }, + { "dstA", &dstA }, + }; + + for (auto&& entry : functions) { + const char* key = entry.first; + const JsonishValue* v = jsonObject->getValue(key); + if (!v) { + std::cerr << "blendFunction: entry without '" << key << "' key." << std::endl; + return false; + } + if (v->getType() != JsonishValue::STRING) { + std::cerr << "blendFunction: '" << key << "' value must be STRING." << std::endl; + return false; + } + *entry.second = stringToEnum(strToEnum, v->toJsonString()->getString()); + } + builder.customBlendFunctions(srcRGB, srcA, dstRGB, dstA); + return true; +} + static bool processPostLightingBlending(MaterialBuilder& builder, const JsonishValue& value) { static const std::unordered_map strToEnum { { "add", MaterialBuilder::BlendingMode::ADD }, @@ -1194,6 +1241,7 @@ ParametersProcessor::ParametersProcessor() { mParameters["variables"] = { &processVariables, Type::ARRAY }; mParameters["requires"] = { &processRequires, Type::ARRAY }; mParameters["blending"] = { &processBlending, Type::STRING }; + mParameters["blendFunction"] = { &processBlendFunction, Type::OBJECT }; mParameters["postLightingBlending"] = { &processPostLightingBlending, Type::STRING }; mParameters["vertexDomain"] = { &processVertexDomain, Type::STRING }; mParameters["culling"] = { &processCulling, Type::STRING }; diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index fed31ce1407..87da0121452 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -369,8 +369,8 @@ using EntityVector = std::vector; register_vector("RegistryKeys"); register_vector("EntityVector"); -register_vector("AssetInstanceVector"); -register_vector("MaterialInstanceVector"); +register_vector>("AssetInstanceVector"); +register_vector>("MaterialInstanceVector"); // CORE FILAMENT CLASSES // --------------------- diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 62fb8be635a..b114a397027 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.51.2", + "version": "1.51.3", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js",