From 0605e9fe82b706f89d9725605f14cbab45f479ad Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Wed, 6 Mar 2024 13:42:30 -0800 Subject: [PATCH 01/16] Fix a warning (#7634) This fixes a warning for 6601c7c2b5a05c5e41920fa86de74c65ae430ba8 --- tools/matc/src/matc/ParametersProcessor.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/matc/src/matc/ParametersProcessor.cpp b/tools/matc/src/matc/ParametersProcessor.cpp index 498ea75a35c..77a74fa9f93 100644 --- a/tools/matc/src/matc/ParametersProcessor.cpp +++ b/tools/matc/src/matc/ParametersProcessor.cpp @@ -1283,6 +1283,11 @@ bool ParametersProcessor::process(filamat::MaterialBuilder& builder, const std:: case JsonishValue::Type::STRING: var = std::make_unique(value); break; + default: + std::cerr << "Unsupported type: \"" + << JsonishValue::typeToString(mParameters.at(key).rootAssert) + << "\"" << std::endl; + return false; } auto fPointer = mParameters[key].callback; From 434c226e8a7061fd08b57a903de95ac3cd03c5ce Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 6 Mar 2024 14:40:30 -0800 Subject: [PATCH 02/16] IBLs were mirrored when using IBLPrefilter cmgen mirrors environment maps by default so that the reflection map appears un-mirrored. IBLPrefilter didn't do that. EquirectangularToCubemap now takes a Config parameter that allows to specify the mirroring, which is enabled by default. FIXES=[320856413] --- .../IBLPrefilterContext.h | 14 +++++++++- libs/iblprefilter/src/IBLPrefilterContext.cpp | 27 ++++++++++++++++--- .../src/materials/equirectToCube.mat | 19 +++++++------ .../src/materials/iblprefilter.mat | 7 ++--- 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h b/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h index 815ff613ffb..903c42589f2 100644 --- a/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h +++ b/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h @@ -88,12 +88,23 @@ class UTILS_PUBLIC IBLPrefilterContext { */ class EquirectangularToCubemap { public: + + struct Config { + bool mirror = true; //!< mirror the source horizontally + }; + /** - * Creates a EquirectangularToCubemap processor. + * Creates a EquirectangularToCubemap processor using the default Config * @param context IBLPrefilterContext to use */ explicit EquirectangularToCubemap(IBLPrefilterContext& context); + /** + * Creates a EquirectangularToCubemap processor using the provided Config + * @param context IBLPrefilterContext to use + */ + EquirectangularToCubemap(IBLPrefilterContext& context, Config const& config); + /** * Destroys all GPU resources created during initialization. */ @@ -125,6 +136,7 @@ class UTILS_PUBLIC IBLPrefilterContext { private: IBLPrefilterContext& mContext; filament::Material* mEquirectMaterial = nullptr; + Config mConfig{}; }; /** diff --git a/libs/iblprefilter/src/IBLPrefilterContext.cpp b/libs/iblprefilter/src/IBLPrefilterContext.cpp index fc66e250eb9..33705b140df 100644 --- a/libs/iblprefilter/src/IBLPrefilterContext.cpp +++ b/libs/iblprefilter/src/IBLPrefilterContext.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -29,12 +30,22 @@ #include #include -#include +#include + +#include #include +#include #include -#include -#include +#include +#include + +#include +#include +#include + +#include +#include #include "generated/resources/iblprefilter_materials.h" @@ -167,13 +178,19 @@ IBLPrefilterContext& IBLPrefilterContext::operator=(IBLPrefilterContext&& rhs) n // ------------------------------------------------------------------------------------------------ IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap( - IBLPrefilterContext& context) : mContext(context) { + IBLPrefilterContext& context, + IBLPrefilterContext::EquirectangularToCubemap::Config const& config) + : mContext(context), mConfig(config) { Engine& engine = mContext.mEngine; mEquirectMaterial = Material::Builder().package( IBLPREFILTER_MATERIALS_EQUIRECTTOCUBE_DATA, IBLPREFILTER_MATERIALS_EQUIRECTTOCUBE_SIZE).build(engine); } +IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap( + IBLPrefilterContext& context) : EquirectangularToCubemap(context, {}) { +} + IBLPrefilterContext::EquirectangularToCubemap::~EquirectangularToCubemap() noexcept { Engine& engine = mContext.mEngine; engine.destroy(mEquirectMaterial); @@ -257,6 +274,8 @@ Texture* IBLPrefilterContext::EquirectangularToCubemap::operator()( .texture(RenderTarget::AttachmentPoint::COLOR1, outCube) .texture(RenderTarget::AttachmentPoint::COLOR2, outCube); + mi->setParameter("mirror", mConfig.mirror ? -1.0f : 1.0f); + for (size_t i = 0; i < 2; i++) { mi->setParameter("side", i == 0 ? 1.0f : -1.0f); diff --git a/libs/iblprefilter/src/materials/equirectToCube.mat b/libs/iblprefilter/src/materials/equirectToCube.mat index b3b9bee9fb0..eb147998adf 100644 --- a/libs/iblprefilter/src/materials/equirectToCube.mat +++ b/libs/iblprefilter/src/materials/equirectToCube.mat @@ -10,6 +10,11 @@ material { type : float, name : side, precision: medium + }, + { + type : float, + name : mirror, + precision: medium } ], outputs : [ @@ -51,7 +56,7 @@ highp vec2 toEquirect(const highp vec3 s) { highp float xf = atan(s.x, s.z) * (1.0 / PI); // range [-1.0, 1.0] highp float yf = asin(s.y) * (2.0 / PI); // range [-1.0, 1.0] xf = (xf + 1.0) * 0.5; // range [0, 1.0] - yf = (1.0 - yf) * 0.5; // range [0, 1.0] + yf = (yf + 1.0) * 0.5; // range [0, 1.0] return vec2(xf, yf); } @@ -62,18 +67,16 @@ mediump vec3 sampleEquirect(mediump sampler2D equirect, const highp vec3 r) { void postProcess(inout PostProcessInputs postProcess) { highp vec2 uv = variable_vertex.xy; // interpolated at pixel's center - highp vec2 p = vec2( - uv.x * 2.0 - 1.0, - 1.0 - uv.y * 2.0); + highp vec2 p = uv * 2.0 - 1.0; float side = materialParams.side; + float mirror = materialParams.mirror; highp float l = inversesqrt(p.x * p.x + p.y * p.y + 1.0); // compute the view (and normal, since v = n) direction for each face - highp vec3 rx = vec3( side, p.y, side * -p.x); - highp vec3 ry = vec3( p.x, side, side * -p.y); - highp vec3 rz = vec3(side * p.x, p.y, side); - + highp vec3 rx = vec3( side * mirror, p.y, side * -p.x); + highp vec3 ry = vec3( p.x * mirror, side, side * -p.y); + highp vec3 rz = vec3(side * p.x * mirror, p.y, side); postProcess.outx = sampleEquirect(materialParams_equirect, rx * l); postProcess.outy = sampleEquirect(materialParams_equirect, ry * l); diff --git a/libs/iblprefilter/src/materials/iblprefilter.mat b/libs/iblprefilter/src/materials/iblprefilter.mat index d9fa379a8e0..aa0c25c1d67 100644 --- a/libs/iblprefilter/src/materials/iblprefilter.mat +++ b/libs/iblprefilter/src/materials/iblprefilter.mat @@ -122,9 +122,10 @@ void postProcess(inout PostProcessInputs postProcess) { float side = materialParams.side; // compute the view (and normal, since v = n) direction for each face - vec3 rx = normalize(vec3( side, -p.y, side * -p.x)); - vec3 ry = normalize(vec3( p.x, side, side * p.y)); - vec3 rz = normalize(vec3(side * p.x, -p.y, side)); + float l = inversesqrt(p.x * p.x + p.y * p.y + 1.0); + vec3 rx = vec3( side, p.y, side * -p.x) * l; + vec3 ry = vec3( p.x, side, side * -p.y) * l; + vec3 rz = vec3(side * p.x, p.y, side) * l; // random rotation around r mediump float a = 2.0 * PI * random(gl_FragCoord.xy); From b9a33b7d3ec0e1a8d86ca8094e0c5ca72742ccbf Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 6 Mar 2024 17:09:04 -0800 Subject: [PATCH 03/16] Try fixing windows artifact output again (#7637) The [previous] change assumed that the shell is powershell, but the shell is actually commands (cmd). The [previous] change assumed we're in the root directory. This assumption is probably correct [ref]. So we keep that change. [ref]: https://github.com/google/filament/blob/main/build/windows/build-github.bat#L134 [previous]: 373c5710b11bec4ee27e094b255e4eb4c4ed9915 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bf7211a1039..2312795605b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -205,7 +205,7 @@ jobs: TAG: ${{ steps.git_ref.outputs.tag }} run: | build\windows\build-github.bat release - move out\filament-windows.tgz out\filament-$Env:TAG-windows.tgz + move out\filament-windows.tgz out\filament-%TAG%-windows.tgz shell: cmd - uses: actions/github-script@v6 env: From e1973978aee31dc6965dc335352f19b3964d213e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 7 Mar 2024 14:00:21 -0800 Subject: [PATCH 04/16] Set the protected attribute on EGLImage We set the attribute based on the usage bits of the underlaying AHardwareBuffer. --- .../opengl/platforms/PlatformEGLAndroid.cpp | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp b/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp index b61bbeab9a7..81d390397f2 100644 --- a/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp +++ b/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp @@ -14,21 +14,34 @@ * limitations under the License. */ +#include +#include +#include #include #include "opengl/GLUtils.h" #include "ExternalStreamManagerAndroid.h" #include - -#include -#include +#include #include +#include #include +#include +#include + #include +#include + +#include + +#include +#include +#include + // We require filament to be built with an API 19 toolchain, before that, OpenGLES 3.0 didn't exist // Actually, OpenGL ES 3.0 was added to API 18, but API 19 is the better target and // the minimum for Jetpack at the time of this comment. @@ -165,14 +178,28 @@ int PlatformEGLAndroid::getOSVersion() const noexcept { AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) noexcept { // Convert the AHardwareBuffer to EGLImage. - EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID((const AHardwareBuffer*)source.image); + AHardwareBuffer const* const pHardwareBuffer = (const AHardwareBuffer*)source.image; + + EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(pHardwareBuffer); if (!clientBuffer) { slog.e << "Unable to get EGLClientBuffer from AHardwareBuffer." << io::endl; return {}; } - // Note that this cannot be used to stream protected video (for now) because we do not set EGL_PROTECTED_CONTENT_EXT. - EGLint attrs[] = { EGL_NONE, EGL_NONE }; - EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); + + PlatformEGL::Config attributes; + + if (__builtin_available(android 26, *)) { + AHardwareBuffer_Desc desc; + AHardwareBuffer_describe(pHardwareBuffer, &desc); + bool const isProtectedContent = + desc.usage & AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT; + if (isProtectedContent) { + attributes[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE; + } + } + + EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay, + EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attributes.data()); if (eglImage == EGL_NO_IMAGE_KHR) { slog.e << "eglCreateImageKHR returned no image." << io::endl; return {}; From 23a8efd3dc893f3cea20a9dd7c00e18a2b4cba1b Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 7 Mar 2024 17:15:24 -0800 Subject: [PATCH 05/16] vk: clean up debug tools (#7635) - Fix broken resource leak print out. - Add sampler name to debugUtils when enabled --- .../backend/src/vulkan/VulkanCommands.cpp | 4 +-- filament/backend/src/vulkan/VulkanConstants.h | 20 +++++++++----- filament/backend/src/vulkan/VulkanDriver.cpp | 26 ++++++++++++------- filament/backend/src/vulkan/VulkanHandles.cpp | 12 +++++++-- filament/backend/src/vulkan/VulkanHandles.h | 12 +++++++++ .../src/vulkan/VulkanPipelineCache.cpp | 8 ++++++ .../src/vulkan/VulkanResourceAllocator.h | 18 ++++++------- .../backend/src/vulkan/VulkanResources.cpp | 1 + filament/backend/src/vulkan/VulkanResources.h | 2 ++ filament/backend/src/vulkan/VulkanTexture.cpp | 2 +- 10 files changed, 76 insertions(+), 29 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanCommands.cpp b/filament/backend/src/vulkan/VulkanCommands.cpp index f5b8e9029ab..21d5944cbd9 100644 --- a/filament/backend/src/vulkan/VulkanCommands.cpp +++ b/filament/backend/src/vulkan/VulkanCommands.cpp @@ -435,8 +435,8 @@ void VulkanCommands::popGroupMarker() { auto const [marker, startTime] = mGroupMarkers->pop(); auto const endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration diff = endTime - startTime; - utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms\n" - << utils::io::flush; + utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms" + << utils::io::endl; #else mGroupMarkers->pop(); #endif diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h index a2b62bd34c8..5856e391641 100644 --- a/filament/backend/src/vulkan/VulkanConstants.h +++ b/filament/backend/src/vulkan/VulkanConstants.h @@ -63,11 +63,12 @@ // Enable the debug utils extension if it is available. #define FVK_DEBUG_DEBUG_UTILS 0x00008000 +#define FVK_DEBUG_RESOURCE_LEAK 0x00010000 + // Usefaul default combinations #define FVK_DEBUG_EVERYTHING 0xFFFFFFFF #define FVK_DEBUG_PERFORMANCE \ - FVK_DEBUG_SYSTRACE | \ - FVK_DEBUG_GROUP_MARKERS + FVK_DEBUG_SYSTRACE #define FVK_DEBUG_CORRECTNESS \ FVK_DEBUG_VALIDATION | \ @@ -80,14 +81,12 @@ FVK_DEBUG_PRINT_GROUP_MARKERS #ifndef NDEBUG -#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_DEBUG_UTILS | FVK_DEBUG_VALIDATION) +#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE) #else #define FVK_DEBUG_FLAGS 0 #endif -#define FVK_ENABLED(flags) ((FVK_DEBUG_FLAGS) & (flags)) -#define FVK_ENABLED_BOOL(flags) ((bool) FVK_ENABLED(flags)) - +#define FVK_ENABLED(flags) (((FVK_DEBUG_FLAGS) & (flags)) == (flags)) // Group marker only works only if validation or debug utils is enabled since it uses // vkCmd(Begin/End)DebugUtilsLabelEXT or vkCmdDebugMarker(Begin/End)EXT @@ -107,6 +106,15 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION)); // end dependcy checks +// Shorthand for combination of enabled debug flags +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_TEXTURE) +#define FVK_ENABLED_DEBUG_SAMPLER_NAME 1 +#else +#define FVK_ENABLED_DEBUG_SAMPLER_NAME 0 +#endif + +// end shorthands + #if FVK_ENABLED(FVK_DEBUG_SYSTRACE) #include diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index c1e8f4c0dee..990e47de9e3 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -359,6 +359,11 @@ void VulkanDriver::collectGarbage() { mCommands->gc(); mStagePool.gc(); mFramebufferCache.gc(); + +#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK) + mResourceAllocator.print(); +#endif + FVK_SYSTRACE_END(); } void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) { @@ -1731,13 +1736,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r commands->acquire(prim.indexBuffer); commands->acquire(prim.vertexBuffer); - // If this is a debug build, validate the current shader. -#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) - if (program->bundle.vertex == VK_NULL_HANDLE || program->bundle.fragment == VK_NULL_HANDLE) { - utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl; - } -#endif - // Update the VK raster state. const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget; @@ -1787,6 +1785,10 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r auto const& bindingToSamplerIndex = program->getBindingToSamplerIndex(); VulkanPipelineCache::UsageFlags usage = program->getUsage(); +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + auto const& bindingToName = program->getBindingToName(); +#endif + UTILS_NOUNROLL for (uint8_t binding = 0; binding < VulkanPipelineCache::SAMPLER_BINDING_COUNT; binding++) { uint16_t const indexPair = bindingToSamplerIndex[binding]; @@ -1819,15 +1821,21 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r // matching characteristics. (e.g. if the missing texture is a 3D texture) if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) { #if FVK_ENABLED(FVK_DEBUG_TEXTURE) - utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'"; + utils::slog.w << "Uninitialized texture bound to '" << bindingToName[binding] << "'"; utils::slog.w << " in material '" << program->name.c_str() << "'"; - utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl; + utils::slog.w << " at binding point " << +binding << utils::io::endl; #endif texture = mEmptyTexture.get(); } SamplerParams const& samplerParams = boundSampler->s; VkSampler const vksampler = mSamplerCache.getSampler(samplerParams); + +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SAMPLER, + reinterpret_cast(vksampler), bindingToName[binding].c_str()); +#endif + VkImageView imageView = VK_NULL_HANDLE; VkImageSubresourceRange const range = texture->getPrimaryViewRange(); if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) && diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 77121150824..60f59513aa5 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -99,6 +99,10 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept #endif } +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + auto& bindingToName = mInfo->bindingToName; +#endif + auto& groupInfo = builder.getSamplerGroupInfo(); auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex; auto& usage = mInfo->usage; @@ -109,12 +113,16 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept uint32_t const binding = samplers[i].binding; bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i); usage = VulkanPipelineCache::getUsageFlags(binding, group.stageFlags, usage); + +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + bindingToName[binding] = samplers[i].name.c_str(); +#endif } } #if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) - utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex - << ", " << bundle.fragment << ")" << utils::io::endl; + utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << modules[0] + << ", " << modules[1] << ")" << utils::io::endl; #endif } diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 9e1d51b5373..f031f1c56f7 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -64,6 +64,12 @@ struct VulkanProgram : public HwProgram, VulkanResource { return mInfo->bindingToSamplerIndex; } +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + inline utils::FixedCapacityVector const& getBindingToName() const { + return mInfo->bindingToName; + } +#endif + private: // TODO: handle compute shaders. // The expected order of shaders - from frontend to backend - is vertex, fragment, compute. @@ -80,6 +86,12 @@ struct VulkanProgram : public HwProgram, VulkanResource { // We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit. utils::FixedCapacityVector bindingToSamplerIndex; VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE }; + +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + // We store the sampler name mapped from binding index (only for debug purposes). + utils::FixedCapacityVector bindingToName; +#endif + }; PipelineInfo* mInfo; diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.cpp b/filament/backend/src/vulkan/VulkanPipelineCache.cpp index 5b96344c0c8..2cb474d7bb4 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.cpp +++ b/filament/backend/src/vulkan/VulkanPipelineCache.cpp @@ -563,6 +563,14 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept { mPipelineRequirements.shaders[0] = program->getVertexShader(); mPipelineRequirements.shaders[1] = program->getFragmentShader(); + + // If this is a debug build, validate the current shader. +#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) + if (mPipelineRequirements.shaders[0] == VK_NULL_HANDLE || + mPipelineRequirements.shaders[1] == VK_NULL_HANDLE) { + utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl; + } +#endif } void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept { diff --git a/filament/backend/src/vulkan/VulkanResourceAllocator.h b/filament/backend/src/vulkan/VulkanResourceAllocator.h index 222c7be9360..ac71f0ece09 100644 --- a/filament/backend/src/vulkan/VulkanResourceAllocator.h +++ b/filament/backend/src/vulkan/VulkanResourceAllocator.h @@ -17,6 +17,7 @@ #ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H #define TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H +#include "VulkanConstants.h" #include "VulkanHandles.h" #include @@ -29,18 +30,17 @@ namespace filament::backend { -// RESOURCE_TYPE_COUNT matches the count of enum VulkanResourceType. -#define RESOURCE_TYPE_COUNT 12 -#define DEBUG_RESOURCE_LEAKS 0 +#define RESOURCE_TYPE_COUNT (static_cast(VulkanResourceType::END_TYPE)) +#define DEBUG_RESOURCE_LEAKS FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK) #if DEBUG_RESOURCE_LEAKS - #define TRACK_INCREMENT() \ - if (!IS_MANAGED_TYPE(obj->mType)) { \ - mDebugOnlyResourceCount[static_cast(obj->mType)]++; \ + #define TRACK_INCREMENT() \ + if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \ + mDebugOnlyResourceCount[static_cast(obj->getType())]++; \ } - #define TRACK_DECREMENT() \ - if (!IS_MANAGED_TYPE(obj->mType)) { \ - mDebugOnlyResourceCount[static_cast(obj->mType)]--; \ + #define TRACK_DECREMENT() \ + if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \ + mDebugOnlyResourceCount[static_cast(obj->getType())]--; \ } #else // No-op diff --git a/filament/backend/src/vulkan/VulkanResources.cpp b/filament/backend/src/vulkan/VulkanResources.cpp index bbc84722397..a0bfd794352 100644 --- a/filament/backend/src/vulkan/VulkanResources.cpp +++ b/filament/backend/src/vulkan/VulkanResources.cpp @@ -70,6 +70,7 @@ void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType t // destruction. case VulkanResourceType::FENCE: case VulkanResourceType::HEAP_ALLOCATED: + case VulkanResourceType::END_TYPE: break; } } diff --git a/filament/backend/src/vulkan/VulkanResources.h b/filament/backend/src/vulkan/VulkanResources.h index 8364c232b01..dd40cbb3ab2 100644 --- a/filament/backend/src/vulkan/VulkanResources.h +++ b/filament/backend/src/vulkan/VulkanResources.h @@ -51,6 +51,8 @@ enum class VulkanResourceType : uint8_t { // Below are resources that are managed manually (i.e. not ref counted). FENCE, HEAP_ALLOCATED, + + END_TYPE, // A placeholder }; #define IS_HEAP_ALLOC_TYPE(f) \ diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index 16ab0a2eccf..e595f3bec93 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -159,7 +159,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, imageInfo.samples = (VkSampleCountFlagBits) samples; VkResult error = vkCreateImage(mDevice, &imageInfo, VKALLOC, &mTextureImage); - if (error || FVK_ENABLED_BOOL(FVK_DEBUG_TEXTURE)) { + if (error || FVK_ENABLED(FVK_DEBUG_TEXTURE)) { utils::slog.d << "vkCreateImage: " << "image = " << mTextureImage << ", " << "result = " << error << ", " From c3057e17bb43554d214dea730b03c42bbb316606 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 7 Mar 2024 16:02:15 -0800 Subject: [PATCH 06/16] add post-lighting mix factor support This adds a new material property (float postLightingMixFactor) which is used to mix the original color with the post-lighting blended color. The default value is 1.0, which keeps the current behavior. FIXES=[328498606] --- NEW_RELEASE_NOTES.md | 2 + .../include/filament/MaterialEnums.h | 3 +- .../filamat/include/filamat/MaterialBuilder.h | 3 +- libs/filamat/src/Enums.cpp | 53 ++++++++++--------- libs/filamat/src/shaders/CodeGenerator.cpp | 53 ++++++++++--------- libs/filamat/tests/test_filamat.cpp | 2 + shaders/src/main.fs | 12 +++-- shaders/src/material_inputs.fs | 2 + 8 files changed, 71 insertions(+), 59 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 4a1a9c7fa7e..82631f524ae 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,3 +7,5 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut + +- materials: add support for post-lighting mix factor (b/328498606) [⚠️ **New Material Version**] diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index f98c707e1c3..4c4d3da332b 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -201,7 +201,7 @@ enum class ReflectionMode : uint8_t { // can't really use std::underlying_type::type because the driver takes a uint32_t using AttributeBitset = utils::bitset32; -static constexpr size_t MATERIAL_PROPERTIES_COUNT = 26; +static constexpr size_t MATERIAL_PROPERTIES_COUNT = 27; enum class Property : uint8_t { BASE_COLOR, //!< float4, all shading models ROUGHNESS, //!< float, lit shading models only @@ -223,6 +223,7 @@ enum class Property : uint8_t { EMISSIVE, //!< float4, all shading models NORMAL, //!< float3, all shading models only, except unlit POST_LIGHTING_COLOR, //!< float4, all shading models + POST_LIGHTING_MIX_FACTOR,//!< float, all shading models CLIP_SPACE_TRANSFORM, //!< mat4, vertex shader only ABSORPTION, //!< float3, how much light is absorbed by the material TRANSMISSION, //!< float, how much light is refracted through the material diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index 541153112cf..2e2d096309f 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -410,7 +410,8 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { /** * Set the blending mode of the post-lighting color for this material. * Only OPAQUE, TRANSPARENT and ADD are supported, the default is TRANSPARENT. - * This setting requires the material property "postLightingColor" to be set. + * This setting requires the material properties "postLightingColor" and + * "postLightingMixFactor" to be set. */ MaterialBuilder& postLightingBlending(BlendingMode blending) noexcept; diff --git a/libs/filamat/src/Enums.cpp b/libs/filamat/src/Enums.cpp index 01c4011c7ef..583d376f951 100644 --- a/libs/filamat/src/Enums.cpp +++ b/libs/filamat/src/Enums.cpp @@ -21,32 +21,33 @@ namespace filamat { std::unordered_map Enums::mStringToProperty = { - { "baseColor", Property::BASE_COLOR }, - { "roughness", Property::ROUGHNESS }, - { "metallic", Property::METALLIC }, - { "reflectance", Property::REFLECTANCE }, - { "ambientOcclusion", Property::AMBIENT_OCCLUSION }, - { "clearCoat", Property::CLEAR_COAT }, - { "clearCoatRoughness", Property::CLEAR_COAT_ROUGHNESS }, - { "clearCoatNormal", Property::CLEAR_COAT_NORMAL }, - { "anisotropy", Property::ANISOTROPY }, - { "anisotropyDirection", Property::ANISOTROPY_DIRECTION }, - { "thickness", Property::THICKNESS }, - { "subsurfacePower", Property::SUBSURFACE_POWER }, - { "subsurfaceColor", Property::SUBSURFACE_COLOR }, - { "sheenColor", Property::SHEEN_COLOR }, - { "sheenRoughness", Property::SHEEN_ROUGHNESS }, - { "glossiness", Property::GLOSSINESS }, - { "specularColor", Property::SPECULAR_COLOR }, - { "emissive", Property::EMISSIVE }, - { "normal", Property::NORMAL }, - { "postLightingColor", Property::POST_LIGHTING_COLOR }, - { "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM }, - { "absorption", Property::ABSORPTION }, - { "transmission", Property::TRANSMISSION }, - { "ior", Property::IOR }, - { "microThickness", Property::MICRO_THICKNESS }, - { "bentNormal", Property::BENT_NORMAL }, + { "baseColor", Property::BASE_COLOR }, + { "roughness", Property::ROUGHNESS }, + { "metallic", Property::METALLIC }, + { "reflectance", Property::REFLECTANCE }, + { "ambientOcclusion", Property::AMBIENT_OCCLUSION }, + { "clearCoat", Property::CLEAR_COAT }, + { "clearCoatRoughness", Property::CLEAR_COAT_ROUGHNESS }, + { "clearCoatNormal", Property::CLEAR_COAT_NORMAL }, + { "anisotropy", Property::ANISOTROPY }, + { "anisotropyDirection", Property::ANISOTROPY_DIRECTION }, + { "thickness", Property::THICKNESS }, + { "subsurfacePower", Property::SUBSURFACE_POWER }, + { "subsurfaceColor", Property::SUBSURFACE_COLOR }, + { "sheenColor", Property::SHEEN_COLOR }, + { "sheenRoughness", Property::SHEEN_ROUGHNESS }, + { "glossiness", Property::GLOSSINESS }, + { "specularColor", Property::SPECULAR_COLOR }, + { "emissive", Property::EMISSIVE }, + { "normal", Property::NORMAL }, + { "postLightingColor", Property::POST_LIGHTING_COLOR }, + { "postLightingMixFactor", Property::POST_LIGHTING_MIX_FACTOR }, + { "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM }, + { "absorption", Property::ABSORPTION }, + { "transmission", Property::TRANSMISSION }, + { "ior", Property::IOR }, + { "microThickness", Property::MICRO_THICKNESS }, + { "bentNormal", Property::BENT_NORMAL }, }; template <> diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index c242eee6d5b..13c6bd07686 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -1115,32 +1115,33 @@ io::sstream& CodeGenerator::generateShaderReflections(utils::io::sstream& out, S char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) noexcept { using Property = MaterialBuilder::Property; switch (property) { - case Property::BASE_COLOR: return "BASE_COLOR"; - case Property::ROUGHNESS: return "ROUGHNESS"; - case Property::METALLIC: return "METALLIC"; - case Property::REFLECTANCE: return "REFLECTANCE"; - case Property::AMBIENT_OCCLUSION: return "AMBIENT_OCCLUSION"; - case Property::CLEAR_COAT: return "CLEAR_COAT"; - case Property::CLEAR_COAT_ROUGHNESS: return "CLEAR_COAT_ROUGHNESS"; - case Property::CLEAR_COAT_NORMAL: return "CLEAR_COAT_NORMAL"; - case Property::ANISOTROPY: return "ANISOTROPY"; - case Property::ANISOTROPY_DIRECTION: return "ANISOTROPY_DIRECTION"; - case Property::THICKNESS: return "THICKNESS"; - case Property::SUBSURFACE_POWER: return "SUBSURFACE_POWER"; - case Property::SUBSURFACE_COLOR: return "SUBSURFACE_COLOR"; - case Property::SHEEN_COLOR: return "SHEEN_COLOR"; - case Property::SHEEN_ROUGHNESS: return "SHEEN_ROUGHNESS"; - case Property::GLOSSINESS: return "GLOSSINESS"; - case Property::SPECULAR_COLOR: return "SPECULAR_COLOR"; - case Property::EMISSIVE: return "EMISSIVE"; - case Property::NORMAL: return "NORMAL"; - case Property::POST_LIGHTING_COLOR: return "POST_LIGHTING_COLOR"; - case Property::CLIP_SPACE_TRANSFORM: return "CLIP_SPACE_TRANSFORM"; - case Property::ABSORPTION: return "ABSORPTION"; - case Property::TRANSMISSION: return "TRANSMISSION"; - case Property::IOR: return "IOR"; - case Property::MICRO_THICKNESS: return "MICRO_THICKNESS"; - case Property::BENT_NORMAL: return "BENT_NORMAL"; + case Property::BASE_COLOR: return "BASE_COLOR"; + case Property::ROUGHNESS: return "ROUGHNESS"; + case Property::METALLIC: return "METALLIC"; + case Property::REFLECTANCE: return "REFLECTANCE"; + case Property::AMBIENT_OCCLUSION: return "AMBIENT_OCCLUSION"; + case Property::CLEAR_COAT: return "CLEAR_COAT"; + case Property::CLEAR_COAT_ROUGHNESS: return "CLEAR_COAT_ROUGHNESS"; + case Property::CLEAR_COAT_NORMAL: return "CLEAR_COAT_NORMAL"; + case Property::ANISOTROPY: return "ANISOTROPY"; + case Property::ANISOTROPY_DIRECTION: return "ANISOTROPY_DIRECTION"; + case Property::THICKNESS: return "THICKNESS"; + case Property::SUBSURFACE_POWER: return "SUBSURFACE_POWER"; + case Property::SUBSURFACE_COLOR: return "SUBSURFACE_COLOR"; + case Property::SHEEN_COLOR: return "SHEEN_COLOR"; + case Property::SHEEN_ROUGHNESS: return "SHEEN_ROUGHNESS"; + case Property::GLOSSINESS: return "GLOSSINESS"; + case Property::SPECULAR_COLOR: return "SPECULAR_COLOR"; + case Property::EMISSIVE: return "EMISSIVE"; + case Property::NORMAL: return "NORMAL"; + case Property::POST_LIGHTING_COLOR: return "POST_LIGHTING_COLOR"; + case Property::POST_LIGHTING_MIX_FACTOR: return "POST_LIGHTING_MIX_FACTOR"; + case Property::CLIP_SPACE_TRANSFORM: return "CLIP_SPACE_TRANSFORM"; + case Property::ABSORPTION: return "ABSORPTION"; + case Property::TRANSMISSION: return "TRANSMISSION"; + case Property::IOR: return "IOR"; + case Property::MICRO_THICKNESS: return "MICRO_THICKNESS"; + case Property::BENT_NORMAL: return "BENT_NORMAL"; } } diff --git a/libs/filamat/tests/test_filamat.cpp b/libs/filamat/tests/test_filamat.cpp index 91e4a9adeb3..1d63fc15dcd 100644 --- a/libs/filamat/tests/test_filamat.cpp +++ b/libs/filamat/tests/test_filamat.cpp @@ -701,6 +701,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerOutputFactor) { void material(inout MaterialInputs material) { prepareMaterial(material); material.postLightingColor = vec4(1.0); + material.postLightingMixFactor = 0.5; } )"); @@ -712,6 +713,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerOutputFactor) { glslTools.findProperties(ShaderStage::FRAGMENT, shaderCode, properties); MaterialBuilder::PropertyList expected{ false }; expected[size_t(filamat::MaterialBuilder::Property::POST_LIGHTING_COLOR)] = true; + expected[size_t(filamat::MaterialBuilder::Property::POST_LIGHTING_MIX_FACTOR)] = true; EXPECT_TRUE(PropertyListsMatch(expected, properties)); } diff --git a/shaders/src/main.fs b/shaders/src/main.fs index 375960515ea..1bc74372c13 100644 --- a/shaders/src/main.fs +++ b/shaders/src/main.fs @@ -6,17 +6,19 @@ layout(location = 0) out vec4 fragColor; #if defined(MATERIAL_HAS_POST_LIGHTING_COLOR) void blendPostLightingColor(const MaterialInputs material, inout vec4 color) { + vec4 blend = color; #if defined(POST_LIGHTING_BLEND_MODE_OPAQUE) - color = material.postLightingColor; + blend = material.postLightingColor; #elif defined(POST_LIGHTING_BLEND_MODE_TRANSPARENT) - color = material.postLightingColor + color * (1.0 - material.postLightingColor.a); + blend = material.postLightingColor + color * (1.0 - material.postLightingColor.a); #elif defined(POST_LIGHTING_BLEND_MODE_ADD) - color += material.postLightingColor; + blend += material.postLightingColor; #elif defined(POST_LIGHTING_BLEND_MODE_MULTIPLY) - color *= material.postLightingColor; + blend *= material.postLightingColor; #elif defined(POST_LIGHTING_BLEND_MODE_SCREEN) - color += material.postLightingColor * (1.0 - color); + blend += material.postLightingColor * (1.0 - color); #endif + color = mix(color, blend, material.postLightingMixFactor); } #endif diff --git a/shaders/src/material_inputs.fs b/shaders/src/material_inputs.fs index 3844df9f2bc..53d4ade1ecf 100644 --- a/shaders/src/material_inputs.fs +++ b/shaders/src/material_inputs.fs @@ -66,6 +66,7 @@ struct MaterialInputs { #if defined(MATERIAL_HAS_POST_LIGHTING_COLOR) vec4 postLightingColor; + float postLightingMixFactor; #endif #if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) && !defined(SHADING_MODEL_UNLIT) @@ -153,6 +154,7 @@ void initMaterial(out MaterialInputs material) { #if defined(MATERIAL_HAS_POST_LIGHTING_COLOR) material.postLightingColor = vec4(0.0); + material.postLightingMixFactor = 1.0; #endif #if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) && !defined(SHADING_MODEL_UNLIT) From d07168f49cff6b1f6ace355417706f8503aad5e0 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Fri, 8 Mar 2024 10:32:38 -0800 Subject: [PATCH 07/16] Metal: change shader compilation pool size to 1 (#7639) --- filament/backend/src/metal/MetalShaderCompiler.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/backend/src/metal/MetalShaderCompiler.mm b/filament/backend/src/metal/MetalShaderCompiler.mm index 7af9c65944e..713133b1db4 100644 --- a/filament/backend/src/metal/MetalShaderCompiler.mm +++ b/filament/backend/src/metal/MetalShaderCompiler.mm @@ -77,7 +77,7 @@ bool isReady() const noexcept { } void MetalShaderCompiler::init() noexcept { - const uint32_t poolSize = 2; + const uint32_t poolSize = 1; mCompilerThreadPool.init(poolSize, []() {}, []() {}); } From f8973d53d65c485123ee9e814fa7b47beee93ac1 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Fri, 8 Mar 2024 12:26:53 -0800 Subject: [PATCH 08/16] Consolidate materials for feature level (#7640) Some default materials such as defaultMaterial and skybox have discrete material file for feature level 0. Combine these materials as one utilizing the `-P` option of matc. --- filament/CMakeLists.txt | 42 +++++++-------- filament/src/materials/defaultMaterial0.mat | 12 ----- filament/src/materials/skybox.mat | 6 ++- filament/src/materials/skybox0.mat | 60 --------------------- 4 files changed, 25 insertions(+), 95 deletions(-) delete mode 100644 filament/src/materials/defaultMaterial0.mat delete mode 100644 filament/src/materials/skybox0.mat diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 698c139332e..25318ec0fae 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -252,8 +252,8 @@ set(MATERIAL_SRCS ) set(MATERIAL_FL0_SRCS - src/materials/defaultMaterial0.mat - src/materials/skybox0.mat + src/materials/defaultMaterial.mat + src/materials/skybox.mat ) # Embed the binary resource blob for materials. @@ -316,32 +316,30 @@ foreach (mat_src ${MATERIAL_SRCS}) get_filename_component(fullname "${mat_src}" ABSOLUTE) set(output_path "${MATERIAL_DIR}/${localname}.filamat") - add_custom_command( - OUTPUT ${output_path} - COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname} - MAIN_DEPENDENCY ${fullname} - DEPENDS matc - COMMENT "Compiling material ${mat_src} to ${output_path}" - ) - list(APPEND MATERIAL_BINS ${output_path}) -endforeach() - -if (FILAMENT_ENABLE_FEATURE_LEVEL_0) - foreach (mat_src ${MATERIAL_FL0_SRCS}) - get_filename_component(localname "${mat_src}" NAME_WE) - get_filename_component(fullname "${mat_src}" ABSOLUTE) - set(output_path "${MATERIAL_DIR}/${localname}.filamat") - + list(FIND MATERIAL_FL0_SRCS ${mat_src} index) + if (${index} GREATER -1 AND FILAMENT_ENABLE_FEATURE_LEVEL_0) + string(REGEX REPLACE "[.]filamat$" "0.filamat" output_path0 ${output_path}) + add_custom_command( + OUTPUT ${output_path} ${output_path0} + COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname} + COMMAND matc ${MATC_BASE_FLAGS} -PfeatureLevel=0 -o ${output_path0} ${fullname} + MAIN_DEPENDENCY ${fullname} + DEPENDS matc + COMMENT "Compiling material ${fullname} to ${output_path} and ${output_path0}" + ) + list(APPEND MATERIAL_BINS ${output_path0}) + else () add_custom_command( OUTPUT ${output_path} COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname} MAIN_DEPENDENCY ${fullname} DEPENDS matc - COMMENT "Compiling material ${mat_src} to ${output_path}" + COMMENT "Compiling material ${fullname} to ${output_path}" ) - list(APPEND MATERIAL_BINS ${output_path}) - endforeach () -endif () + endif () + + list(APPEND MATERIAL_BINS ${output_path}) +endforeach() # Additional dependencies on included files for materials diff --git a/filament/src/materials/defaultMaterial0.mat b/filament/src/materials/defaultMaterial0.mat deleted file mode 100644 index 914d29c3d1d..00000000000 --- a/filament/src/materials/defaultMaterial0.mat +++ /dev/null @@ -1,12 +0,0 @@ -material { - name : "Filament Default Material", - shadingModel : unlit, - featureLevel: 0 -} - -fragment { - void material(inout MaterialInputs material) { - prepareMaterial(material); - material.baseColor.rgb = vec3(0.8); - } -} diff --git a/filament/src/materials/skybox.mat b/filament/src/materials/skybox.mat index 1d36108a275..d692fc238cf 100644 --- a/filament/src/materials/skybox.mat +++ b/filament/src/materials/skybox.mat @@ -35,10 +35,14 @@ fragment { if (materialParams.constantColor != 0) { sky = materialParams.color; } else { +#if MATERIAL_FEATURE_LEVEL == 0 + sky = vec4(textureCube(materialParams_skybox, variable_eyeDirection.xyz).rgb, 1.0); +#else sky = vec4(textureLod(materialParams_skybox, variable_eyeDirection.xyz, 0.0).rgb, 1.0); +#endif sky.rgb *= frameUniforms.iblLuminance; } - if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0f) { + if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0) { vec3 direction = normalize(variable_eyeDirection.xyz); // Assume the sun is a sphere vec3 sun = frameUniforms.lightColorIntensity.rgb * diff --git a/filament/src/materials/skybox0.mat b/filament/src/materials/skybox0.mat deleted file mode 100644 index 2891e32f4b3..00000000000 --- a/filament/src/materials/skybox0.mat +++ /dev/null @@ -1,60 +0,0 @@ -material { - name : Skybox, - parameters : [ - { - type : int, - name : showSun - }, - { - type : int, - name : constantColor - }, - { - type : samplerCubemap, - name : skybox - }, - { - type : float4, - name : color - } - ], - variables : [ - eyeDirection - ], - vertexDomain : device, - depthWrite : false, - shadingModel : unlit, - variantFilter : [ skinning, shadowReceiver, vsm ], - culling: none, - featureLevel: 0 -} - -fragment { - void material(inout MaterialInputs material) { - prepareMaterial(material); - vec4 sky; - if (materialParams.constantColor != 0) { - sky = materialParams.color; - } else { - sky = vec4(textureCube(materialParams_skybox, variable_eyeDirection.xyz).rgb, 1.0); - sky.rgb *= frameUniforms.iblLuminance; - } - if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0) { - vec3 direction = normalize(variable_eyeDirection.xyz); - // Assume the sun is a sphere - vec3 sun = frameUniforms.lightColorIntensity.rgb * - (frameUniforms.lightColorIntensity.a * (4.0 * PI)); - float cosAngle = dot(direction, frameUniforms.lightDirection); - float x = (cosAngle - frameUniforms.sun.x) * frameUniforms.sun.z; - float gradient = pow(1.0 - saturate(x), frameUniforms.sun.w); - sky.rgb = sky.rgb + gradient * sun; - } - material.baseColor = sky; - } -} - -vertex { - void materialVertex(inout MaterialVertexInputs material) { - material.eyeDirection.xyz = material.worldPosition.xyz; - } -} From cac4d2aa9455cc289972cb3d80bf004754466b95 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 14 Feb 2024 00:20:50 -0800 Subject: [PATCH 09/16] Modernize draw API. PipelineState now holds a handle to a HwVertexBufferInfo. DriverAPI::draw() is now technically deprecated and replaced by the more efficient draw2(), which only takes an index offset, index count and instance count. The Pipeline to use is now specified with a new API bindPipeline() and the primitive to use with bindRenderPrimitive(). This allows clients to reuse RenderPrimitives and ultimately Pipelines. This change reduces CPU usage significantly on Metal and Vulkan, by reducing the need to lookup for a pipeline at every draw call. The application, however, must be a "good citizen" by reusing MaterialInstance and RenderPrimitive as much as possible. We do have RenderPrimitive cache however, so reusing the same VertexBuffer and associated parameters also works. --- .../backend/include/backend/DriverEnums.h | 20 ++- .../backend/include/backend/PipelineState.h | 11 +- .../include/private/backend/DriverAPI.inc | 12 ++ .../include/private/backend/HandleAllocator.h | 6 +- filament/backend/src/metal/MetalContext.h | 1 + filament/backend/src/metal/MetalDriver.mm | 87 ++++++++---- filament/backend/src/metal/MetalHandles.h | 32 +++-- filament/backend/src/metal/MetalHandles.mm | 44 +++--- filament/backend/src/noop/NoopDriver.cpp | 9 ++ filament/backend/src/opengl/OpenGLDriver.cpp | 52 +++++--- filament/backend/src/opengl/OpenGLDriver.h | 5 + filament/backend/src/vulkan/VulkanDriver.cpp | 125 +++++++++++------- filament/backend/src/vulkan/VulkanDriver.h | 4 - filament/backend/src/vulkan/VulkanHandles.cpp | 100 ++++++-------- filament/backend/src/vulkan/VulkanHandles.h | 70 +++++----- .../backend/src/vulkan/VulkanPipelineCache.h | 17 ++- filament/src/PostProcessManager.cpp | 6 +- filament/src/RenderPass.cpp | 39 +++++- filament/src/RenderPrimitive.cpp | 33 ++--- filament/src/RenderPrimitive.h | 7 +- filament/src/details/VertexBuffer.h | 2 + 21 files changed, 411 insertions(+), 271 deletions(-) diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index c2deaf547c7..6b0424ca063 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -1186,11 +1186,27 @@ struct StencilState { //! Stencil operations for front-facing polygons StencilOperations front = { - .stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff }; + .stencilFunc = StencilFunction::A, + .stencilOpStencilFail = StencilOperation::KEEP, + .padding0 = 0, + .stencilOpDepthFail = StencilOperation::KEEP, + .stencilOpDepthStencilPass = StencilOperation::KEEP, + .padding1 = 0, + .ref = 0, + .readMask = 0xff, + .writeMask = 0xff }; //! Stencil operations for back-facing polygons StencilOperations back = { - .stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff }; + .stencilFunc = StencilFunction::A, + .stencilOpStencilFail = StencilOperation::KEEP, + .padding0 = 0, + .stencilOpDepthFail = StencilOperation::KEEP, + .stencilOpDepthStencilPass = StencilOperation::KEEP, + .padding1 = 0, + .ref = 0, + .readMask = 0xff, + .writeMask = 0xff }; //! Whether stencil-buffer writes are enabled bool stencilWrite = false; diff --git a/filament/backend/include/backend/PipelineState.h b/filament/backend/include/backend/PipelineState.h index 04001345d8b..220d04bbf26 100644 --- a/filament/backend/include/backend/PipelineState.h +++ b/filament/backend/include/backend/PipelineState.h @@ -29,10 +29,13 @@ namespace filament::backend { //! \privatesection struct PipelineState { - Handle program; - RasterState rasterState; - StencilState stencilState; - PolygonOffset polygonOffset; + Handle program; // 4 + Handle vertexBufferInfo; // 4 + RasterState rasterState; // 4 + StencilState stencilState; // 12 + PolygonOffset polygonOffset; // 8 + PrimitiveType primitiveType = PrimitiveType::TRIANGLES; // 1 + uint8_t padding[3] = {}; // 3 }; } // namespace filament::backend diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 05170ac8d42..ac1dc256c8c 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -493,6 +493,17 @@ DECL_DRIVER_API_N(blit, math::uint2, srcOrigin, math::uint2, size) +DECL_DRIVER_API_N(bindPipeline, + backend::PipelineState, state) + +DECL_DRIVER_API_N(bindRenderPrimitive, + backend::RenderPrimitiveHandle, rph) + +DECL_DRIVER_API_N(draw2, + uint32_t, indexOffset, + uint32_t, indexCount, + uint32_t, instanceCount) + DECL_DRIVER_API_N(draw, backend::PipelineState, state, backend::RenderPrimitiveHandle, rph, @@ -500,6 +511,7 @@ DECL_DRIVER_API_N(draw, uint32_t, indexCount, uint32_t, instanceCount) + DECL_DRIVER_API_N(dispatchCompute, backend::ProgramHandle, program, math::uint3, workGroupCount) diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h index 14e70fd06e3..53bab210777 100644 --- a/filament/backend/include/private/backend/HandleAllocator.h +++ b/filament/backend/include/private/backend/HandleAllocator.h @@ -37,9 +37,9 @@ #include #include -#define HandleAllocatorGL HandleAllocator<24, 64, 136> // ~3640 / pool / MiB -#define HandleAllocatorVK HandleAllocator<80, 176, 320> // ~1820 / pool / MiB -#define HandleAllocatorMTL HandleAllocator<48, 160, 592> // ~1310 / pool / MiB +#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB +#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB +#define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB namespace filament::backend { diff --git a/filament/backend/src/metal/MetalContext.h b/filament/backend/src/metal/MetalContext.h index c98419b1672..33b8f92c829 100644 --- a/filament/backend/src/metal/MetalContext.h +++ b/filament/backend/src/metal/MetalContext.h @@ -99,6 +99,7 @@ struct MetalContext { std::array ssboState; CullModeStateTracker cullModeState; WindingStateTracker windingState; + Handle currentRenderPrimitive; // State caches. DepthStencilStateCache depthStencilStateCache; diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index e291ab60fd2..a47c57a30fa 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -48,24 +48,26 @@ // For reference on a 64-bits machine in Release mode: // MetalTimerQuery : 16 few // HwStream : 24 few + // MetalRenderPrimitive : 24 many + // MetalVertexBuffer : 32 moderate + // -- less than or equal 32 bytes // MetalIndexBuffer : 40 moderate // MetalFence : 48 few // MetalBufferObject : 48 many // -- less than or equal 48 bytes // MetalSamplerGroup : 112 few - // MetalProgram : 144 moderate + // MetalProgram : 152 moderate // MetalTexture : 152 moderate - // MetalVertexBuffer : 152 moderate - // -- less than or equal 160 bytes // MetalSwapChain : 184 few // MetalRenderTarget : 272 few - // MetalRenderPrimitive : 584 many - // -- less than or equal to 592 bytes + // MetalVertexBufferInfo : 552 moderate + // -- less than or equal to 552 bytes utils::slog.d << "\nMetalSwapChain: " << sizeof(MetalSwapChain) << "\nMetalBufferObject: " << sizeof(MetalBufferObject) << "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer) + << "\nMetalVertexBufferInfo: " << sizeof(MetalVertexBufferInfo) << "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer) << "\nMetalSamplerGroup: " << sizeof(MetalSamplerGroup) << "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive) @@ -1602,11 +1604,13 @@ } } -void MetalDriver::draw(PipelineState ps, Handle rph, - uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { +void MetalDriver::bindPipeline(PipelineState ps) { ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr, - "Attempted to draw without a valid command encoder."); - auto primitive = handle_cast(rph); + "bindPipeline() without a valid command encoder."); + + MetalVertexBufferInfo const* const vbi = + handle_cast(ps.vertexBufferInfo); + auto program = handle_cast(ps.program); const auto& rs = ps.rasterState; @@ -1620,7 +1624,7 @@ return; } - functions.validate(); + ASSERT_PRECONDITION(bool(functions), "Attempting to bind an invalid Metal program."); auto [fragment, vertex] = functions.getRasterFunctions(); @@ -1647,7 +1651,7 @@ MetalPipelineState const pipelineState { .vertexFunction = vertex, .fragmentFunction = fragment, - .vertexDescription = primitive->vertexDescription, + .vertexDescription = vbi->vertexDescription, .colorAttachmentPixelFormat = { colorPixelFormat[0], colorPixelFormat[1], @@ -1741,20 +1745,6 @@ mContext->currentPolygonOffset = ps.polygonOffset; } - // Bind uniform buffers. - MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil }; - NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 }; - - enumerateBoundBuffers(BufferObjectBinding::UNIFORM, - [&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer, - uint32_t index) { - uniformsToBind[index] = buffer; - offsets[index] = state.offset; - }); - MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder, - UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT, - uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT); - // Bind sampler groups (argument buffers). for (size_t s = 0; s < Program::SAMPLER_BINDING_COUNT; s++) { MetalSamplerGroup* const samplerGroup = mContext->samplerBindings[s]; @@ -1785,19 +1775,29 @@ atIndex:(SAMPLER_GROUP_BINDING_START + s)]; } } +} - // Bind the user vertex buffers. +void MetalDriver::bindRenderPrimitive(Handle rph) { + ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr, + "bindRenderPrimitive() without a valid command encoder."); + // Bind the user vertex buffers. MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {}; size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {}; size_t maxBufferIndex = 0; + MetalRenderPrimitive const* const primitive = handle_cast(rph); + MetalVertexBufferInfo const* const vbi = + handle_cast(primitive->vertexBuffer->vbih); + + mContext->currentRenderPrimitive = rph; + auto vb = primitive->vertexBuffer; - for (auto m : primitive->bufferMapping) { + for (auto m : vbi->bufferMapping) { assert_invariant( m.bufferArgumentIndex >= USER_VERTEX_BUFFER_BINDING_START && m.bufferArgumentIndex < USER_VERTEX_BUFFER_BINDING_START + MAX_VERTEX_BUFFER_COUNT); - size_t vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START; + size_t const vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START; vertexBuffers[vertexBufferIndex] = vb->buffers[m.sourceBufferIndex]; maxBufferIndex = std::max(maxBufferIndex, vertexBufferIndex); } @@ -1812,6 +1812,27 @@ [mContext->currentRenderPassEncoder setVertexBytes:bytes length:16 atIndex:ZERO_VERTEX_BUFFER_BINDING]; +} + +void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { + ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr, + "draw() without a valid command encoder."); + + // Bind uniform buffers. + MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil }; + NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 }; + + enumerateBoundBuffers(BufferObjectBinding::UNIFORM, + [&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer, + uint32_t index) { + uniformsToBind[index] = buffer; + offsets[index] = state.offset; + }); + MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder, + UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT, + uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT); + + auto primitive = handle_cast(mContext->currentRenderPrimitive); MetalIndexBuffer* indexBuffer = primitive->indexBuffer; @@ -1825,6 +1846,16 @@ instanceCount:instanceCount]; } +void MetalDriver::draw(PipelineState ps, Handle rph, + uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { + MetalRenderPrimitive const* const rp = handle_cast(rph); + ps.primitiveType = rp->type; + ps.vertexBufferInfo = rp->vertexBuffer->vbih; + bindPipeline(ps); + bindRenderPrimitive(rph); + draw2(indexOffset, indexCount, instanceCount); +} + void MetalDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) { ASSERT_PRECONDITION(!isInRenderPass(mContext), "dispatchCompute must be called outside of a render pass."); diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 534d75044ae..fea6f0947f9 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -149,7 +149,21 @@ struct MetalVertexBufferInfo : public HwVertexBufferInfo { MetalVertexBufferInfo(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes); - AttributeArray attributes; + // This struct is used to create the pipeline description to describe vertex assembly. + VertexDescription vertexDescription = {}; + + struct Entry { + uint8_t sourceBufferIndex = 0; + uint8_t stride = 0; + // maps to -> + uint8_t bufferArgumentIndex = 0; + + Entry(uint8_t sourceBufferIndex, uint8_t stride, uint8_t bufferArgumentIndex) + : sourceBufferIndex(sourceBufferIndex), + stride(stride), + bufferArgumentIndex(bufferArgumentIndex) {} + }; + utils::FixedCapacityVector bufferMapping; }; struct MetalVertexBuffer : public HwVertexBuffer { @@ -176,22 +190,6 @@ struct MetalRenderPrimitive : public HwRenderPrimitive { MetalVertexBuffer* vertexBuffer = nullptr; MetalIndexBuffer* indexBuffer = nullptr; - - // This struct is used to create the pipeline description to describe vertex assembly. - VertexDescription vertexDescription = {}; - - struct Entry { - uint8_t sourceBufferIndex = 0; - uint8_t stride = 0; - // maps to -> - uint8_t bufferArgumentIndex = 0; - - Entry(uint8_t sourceBufferIndex, uint8_t stride, uint8_t bufferArgumentIndex) - : sourceBufferIndex(sourceBufferIndex), - stride(stride), - bufferArgumentIndex(bufferArgumentIndex) {} - }; - utils::FixedCapacityVector bufferMapping; }; class MetalProgram : public HwProgram { diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index eef4087cc52..18d83118253 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -343,27 +343,9 @@ void presentDrawable(bool presentFrame, void* user) { MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes) : HwVertexBufferInfo(bufferCount, attributeCount), - attributes(attributes) { -} + bufferMapping(utils::FixedCapacityVector::with_capacity(MAX_VERTEX_BUFFER_COUNT)) { -MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, - uint32_t vertexCount, uint32_t bufferCount, Handle vbih) - : HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) { -} - -MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize, - uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount), - buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { } - -MetalRenderPrimitive::MetalRenderPrimitive() - : bufferMapping(utils::FixedCapacityVector::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {} - -void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi, - MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) { - this->vertexBuffer = vertexBuffer; - this->indexBuffer = indexBuffer; - - const size_t attributeCount = vbi->attributes.size(); + const size_t maxAttributeCount = attributes.size(); auto& mapping = bufferMapping; mapping.clear(); @@ -403,8 +385,8 @@ void presentDrawable(bool presentFrame, void* user) { } }; - for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) { - const auto& attribute = vbi->attributes[attributeIndex]; + for (uint32_t attributeIndex = 0; attributeIndex < maxAttributeCount; attributeIndex++) { + const auto& attribute = attributes[attributeIndex]; // If the attribute is unused, bind it to the zero buffer. It's a Metal error for a shader // to read from missing vertex attributes. @@ -431,6 +413,24 @@ void presentDrawable(bool presentFrame, void* user) { } } +MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, + uint32_t vertexCount, uint32_t bufferCount, Handle vbih) + : HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) { +} + +MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize, + uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount), + buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { } + +MetalRenderPrimitive::MetalRenderPrimitive() { +} + +void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi, + MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) { + this->vertexBuffer = vertexBuffer; + this->indexBuffer = indexBuffer; +} + MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept : HwProgram(program.getName()), mContext(context) { diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 9f5a7b644d9..7a150a3e74a 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -350,6 +350,15 @@ void NoopDriver::blit( math::uint2 size) { } +void NoopDriver::bindPipeline(PipelineState pipelineState) { +} + +void NoopDriver::bindRenderPrimitive(Handle rph) { +} + +void NoopDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { +} + void NoopDriver::draw(PipelineState pipelineState, Handle rph, uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { } diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index a4a0ea2b3f0..cb6958a253a 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -94,10 +94,10 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform, // GLSamplerGroup : 16 few // GLSwapChain : 16 few // GLTimerQuery : 16 few - // GLRenderPrimitive : 20 many // GLFence : 24 few - // -- less than or equal 24 bytes + // GLRenderPrimitive : 32 many // GLBufferObject : 32 many + // -- less than or equal 32 bytes // OpenGLProgram : 56 moderate // GLTexture : 64 moderate // -- less than or equal 64 bytes @@ -518,10 +518,12 @@ void OpenGLDriver::createRenderPrimitiveR(Handle rph, GLIndexBuffer const* const ib = handle_cast(ibh); assert_invariant(ib->elementSize == 2 || ib->elementSize == 4); - GLRenderPrimitive* rp = handle_cast(rph); + GLVertexBuffer* const vb = handle_cast(vbh); + GLRenderPrimitive* const rp = handle_cast(rph); rp->gl.indicesSize = (ib->elementSize == 4u) ? 4u : 2u; rp->gl.vertexBufferWithObjects = vbh; rp->type = pt; + rp->vbih = vb->vbih; // create a name for this VAO in the current context gl.procs.genVertexArrays(1, &rp->gl.vao[gl.contextIndex]); @@ -3784,39 +3786,42 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel) #endif } -void OpenGLDriver::draw(PipelineState state, Handle rph, - uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { +void OpenGLDriver::bindPipeline(PipelineState state) { DEBUG_MARKER() auto& gl = mContext; - + setRasterState(state.rasterState); + setStencilState(state.stencilState); + gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant); OpenGLProgram* const p = handle_cast(state.program); + mValidProgram = useProgram(p); +} - bool const success = useProgram(p); - if (UTILS_UNLIKELY(!success)) { - // Avoid fatal (or cascading) errors that can occur during the draw call when the program - // is invalid. The shader compile error has already been dumped to the console at this - // point, so it's fine to simply return early. - return; - } +void OpenGLDriver::bindRenderPrimitive(Handle rph) { + DEBUG_MARKER() + auto& gl = mContext; GLRenderPrimitive* const rp = handle_cast(rph); // Gracefully do nothing if the render primitive has not been set up. VertexBufferHandle vb = rp->gl.vertexBufferWithObjects; if (UTILS_UNLIKELY(!vb)) { + mBoundRenderPrimitive = nullptr; return; } - gl.bindVertexArray(&rp->gl); - // If necessary, mutate the bindings in the VAO. + gl.bindVertexArray(&rp->gl); GLVertexBuffer const* const glvb = handle_cast(vb); updateVertexArrayObject(rp, glvb); - setRasterState(state.rasterState); - setStencilState(state.stencilState); + mBoundRenderPrimitive = rp; +} - gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant); +void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { + GLRenderPrimitive const* const rp = mBoundRenderPrimitive; + if (UTILS_UNLIKELY(!rp || !mValidProgram)) { + return; + } if (UTILS_LIKELY(instanceCount <= 1)) { glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(), @@ -3842,6 +3847,17 @@ void OpenGLDriver::scissor(Viewport scissor) { setScissor(scissor); } +void OpenGLDriver::draw(PipelineState state, Handle rph, + uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { + DEBUG_MARKER() + GLRenderPrimitive* const rp = handle_cast(rph); + state.primitiveType = rp->type; + state.vertexBufferInfo = rp->vbih; + bindPipeline(state); + bindRenderPrimitive(rph); + draw2(indexOffset, indexCount, instanceCount); +} + void OpenGLDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) { getShaderCompilerService().tick(); diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index 7ef5bc4f204..ccaddeefbf6 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -142,6 +142,7 @@ class OpenGLDriver final : public DriverBase { struct GLRenderPrimitive : public HwRenderPrimitive { using HwRenderPrimitive::HwRenderPrimitive; OpenGLContext::RenderPrimitive gl; + Handle vbih; }; struct GLTexture : public HwTexture { @@ -366,6 +367,10 @@ class OpenGLDriver final : public DriverBase { GLboolean mRenderPassDepthWrite{}; GLboolean mRenderPassStencilWrite{}; + GLRenderPrimitive const* mBoundRenderPrimitive = nullptr; + bool mValidProgram = false; + + void clearWithRasterPipe(TargetBufferFlags clearFlags, math::float4 const& linearColor, GLfloat depth, GLint stencil) noexcept; diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 990e47de9e3..05e85a6d48b 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -264,26 +264,28 @@ Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& cont #if 0 // this is useful for development, but too verbose even for debug builds // For reference on a 64-bits machine in Release mode: - // VulkanSamplerGroup : 24 few + // VulkanSamplerGroup : 16 few // HwStream : 24 few - // VulkanFence : 40 few - // VulkanProgram : 40 moderate - // VulkanIndexBuffer : 72 moderate - // VulkanBufferObject : 72 many - // -- less than or equal 80 bytes - // VulkanRenderPrimitive : 104 many - // VulkanSwapChain : 112 few - // VulkanTimerQuery : 168 few - // -- less than or equal 176 bytes - // VulkanTexture : 232 moderate - // VulkanVertexBuffer : 312 moderate - // VulkanRenderTarget : 320 few - // -- less than or equal to 320 bytes + // VulkanFence : 32 few + // VulkanProgram : 32 moderate + // VulkanIndexBuffer : 64 moderate + // VulkanBufferObject : 64 many + // -- less than or equal 64 bytes + // VulkanRenderPrimitive : 72 many + // VulkanVertexBufferInfo : 96 moderate + // VulkanSwapChain : 104 few + // VulkanTimerQuery : 160 few + // -- less than or equal 160 bytes + // VulkanVertexBuffer : 192 moderate + // VulkanTexture : 224 moderate + // VulkanRenderTarget : 312 few + // -- less than or equal to 312 bytes utils::slog.d << "\nVulkanSwapChain: " << sizeof(VulkanSwapChain) << "\nVulkanBufferObject: " << sizeof(VulkanBufferObject) << "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer) + << "\nVulkanVertexBufferInfo: " << sizeof(VulkanVertexBufferInfo) << "\nVulkanIndexBuffer: " << sizeof(VulkanIndexBuffer) << "\nVulkanSamplerGroup: " << sizeof(VulkanSamplerGroup) << "\nVulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive) @@ -416,9 +418,9 @@ void VulkanDriver::createSamplerGroupR(Handle sbh, uint32_t coun void VulkanDriver::createRenderPrimitiveR(Handle rph, Handle vbh, Handle ibh, PrimitiveType pt) { - auto rp = mResourceAllocator.construct(rph, &mResourceAllocator); + auto rp = mResourceAllocator.construct(rph, + &mResourceAllocator, pt, vbh, ibh); mResourceManager.acquire(rp); - VulkanDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh); } void VulkanDriver::destroyRenderPrimitive(Handle rph) { @@ -987,7 +989,6 @@ void VulkanDriver::setVertexBufferObject(Handle vbh, uint32_t in auto vb = mResourceAllocator.handle_cast(vbh); auto bo = mResourceAllocator.handle_cast(boh); assert_invariant(bo->bindingType == BufferObjectBinding::VERTEX); - vb->setBuffer(mResourceAllocator, bo, index); } @@ -1465,14 +1466,6 @@ void VulkanDriver::nextSubpass(int) { } } -void VulkanDriver::setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt, - Handle vbh, Handle ibh) { - auto primitive = mResourceAllocator.handle_cast(rph); - primitive->setBuffers(mResourceAllocator.handle_cast(vbh), - mResourceAllocator.handle_cast(ibh)); - primitive->setPrimitiveType(pt); -} - void VulkanDriver::makeCurrent(Handle drawSch, Handle readSch) { FVK_SYSTRACE_CONTEXT(); FVK_SYSTRACE_START("makeCurrent"); @@ -1718,14 +1711,13 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers, FVK_SYSTRACE_END(); } -void VulkanDriver::draw(PipelineState pipelineState, Handle rph, - uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { +void VulkanDriver::bindPipeline(PipelineState pipelineState) { FVK_SYSTRACE_CONTEXT(); FVK_SYSTRACE_START("draw"); VulkanCommandBuffer* commands = &mCommands->get(); - VkCommandBuffer cmdbuffer = commands->buffer(); - const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast(rph); + const VulkanVertexBufferInfo& vbi = + *mResourceAllocator.handle_cast(pipelineState.vertexBufferInfo); Handle programHandle = pipelineState.program; RasterState rasterState = pipelineState.rasterState; @@ -1733,8 +1725,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r auto* program = mResourceAllocator.handle_cast(programHandle); commands->acquire(program); - commands->acquire(prim.indexBuffer); - commands->acquire(prim.vertexBuffer); // Update the VK raster state. const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget; @@ -1760,20 +1750,19 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r .depthBiasSlopeFactor = depthOffset.slope }; + // unfortunately in Vulkan the topology is per pipeline + VkPrimitiveTopology const topology = + VulkanPipelineCache::getPrimitiveTopology(pipelineState.primitiveType); + // Declare fixed-size arrays that get passed to the pipeCache and to vkCmdBindVertexBuffers. - VulkanVertexBufferInfo const* const vbi = - mResourceAllocator.handle_cast(prim.vertexBuffer->vbih); - uint32_t const bufferCount = vbi->attributes.size(); - VkVertexInputAttributeDescription const* attribDesc = prim.vertexBuffer->getAttribDescriptions(); - VkVertexInputBindingDescription const* bufferDesc = prim.vertexBuffer->getBufferDescriptions(); - VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers(); - VkDeviceSize const* offsets = prim.vertexBuffer->getOffsets(); + VkVertexInputAttributeDescription const* attribDesc = vbi.getAttribDescriptions(); + VkVertexInputBindingDescription const* bufferDesc = vbi.getBufferDescriptions(); // Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls. mPipelineCache.bindProgram(program); mPipelineCache.bindRasterState(vulkanRasterState); - mPipelineCache.bindPrimitiveTopology(prim.primitiveTopology); - mPipelineCache.bindVertexArray(attribDesc, bufferDesc, bufferCount); + mPipelineCache.bindPrimitiveTopology(topology); + mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi.getAttributeCount()); // Query the program for the mapping from (SamplerGroupBinding,Offset) to (SamplerBinding), // where "SamplerBinding" is the integer in the GLSL, and SamplerGroupBinding is the abstract @@ -1859,19 +1848,33 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r mPipelineCache.bindSamplers(samplerInfo, samplerTextures, usage); - // Bind new descriptor sets if they need to change. - // If descriptor set allocation failed, skip the draw call and bail. No need to emit an error - // message since the validation layers already do so. - if (!mPipelineCache.bindDescriptors(cmdbuffer)) { - return; - } - // Bind a new pipeline if the pipeline state changed. // If allocation failed, skip the draw call and bail. We do not emit an error since the // validation layer will already do so. if (!mPipelineCache.bindPipeline(commands)) { return; } + FVK_SYSTRACE_END(); +} + +void VulkanDriver::bindRenderPrimitive(Handle rph) { + FVK_SYSTRACE_CONTEXT(); + FVK_SYSTRACE_START("bindRenderPrimitive"); + + VulkanCommandBuffer* commands = &mCommands->get(); + VkCommandBuffer cmdbuffer = commands->buffer(); + const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast(rph); + commands->acquire(prim.indexBuffer); + commands->acquire(prim.vertexBuffer); + + // This *must* match the VulkanVertexBufferInfo that was bound in bindPipeline(). But we want + // to allow to call this before bindPipeline(), so the validation can only happen in draw() + VulkanVertexBufferInfo const* const vbi = + mResourceAllocator.handle_cast(prim.vertexBuffer->vbih); + + uint32_t const bufferCount = vbi->getAttributeCount(); + VkDeviceSize const* offsets = vbi->getOffsets(); + VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers(); // Next bind the vertex buffers and index buffer. One potential performance improvement is to // avoid rebinding these if they are already bound, but since we do not (yet) support subranges @@ -1880,15 +1883,43 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0, prim.indexBuffer->indexType); + FVK_SYSTRACE_END(); +} + +void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { + FVK_SYSTRACE_CONTEXT(); + FVK_SYSTRACE_START("draw2"); + + VulkanCommandBuffer* commands = &mCommands->get(); + VkCommandBuffer cmdbuffer = commands->buffer(); + + // Bind new descriptor sets if they need to change. + // If descriptor set allocation failed, skip the draw call and bail. No need to emit an error + // message since the validation layers already do so. + if (!mPipelineCache.bindDescriptors(cmdbuffer)) { + return; + } + // Finally, make the actual draw call. TODO: support subranges const uint32_t firstIndex = indexOffset; const int32_t vertexOffset = 0; const uint32_t firstInstId = 0; vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId); + FVK_SYSTRACE_END(); } +void VulkanDriver::draw(PipelineState state, Handle rph, + uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) { + VulkanRenderPrimitive* const rp = mResourceAllocator.handle_cast(rph); + state.primitiveType = rp->type; + state.vertexBufferInfo = rp->vertexBuffer->vbih; + bindPipeline(state); + bindRenderPrimitive(rph); + draw2(indexOffset, indexCount, instanceCount); +} + void VulkanDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) { // FIXME: implement me } diff --git a/filament/backend/src/vulkan/VulkanDriver.h b/filament/backend/src/vulkan/VulkanDriver.h index 8e311b9bcc8..d6398f8cd32 100644 --- a/filament/backend/src/vulkan/VulkanDriver.h +++ b/filament/backend/src/vulkan/VulkanDriver.h @@ -101,10 +101,6 @@ class VulkanDriver final : public DriverBase { VulkanDriver& operator=(VulkanDriver const&) = delete; private: - inline void setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt, - Handle vbh, - Handle ibh); - void collectGarbage(); VulkanPlatform* mPlatform = nullptr; diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 60f59513aa5..48dc56a2b16 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -289,32 +289,21 @@ uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) co return count; } -VulkanVertexBufferInfo::VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, - AttributeArray const& attributes) - : HwVertexBufferInfo(bufferCount, attributeCount), - VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO), - attributes(attributes) { -} - -VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, - VulkanResourceAllocator* allocator, uint32_t vertexCount, Handle vbih) - : HwVertexBuffer(vertexCount), - VulkanResource(VulkanResourceType::VERTEX_BUFFER), - vbih(vbih), - mResources(allocator) { - - VulkanVertexBufferInfo const* const vbi = allocator->handle_cast(vbih); - mInfo = new PipelineInfo(vbi->attributes.size()); - - auto attribDesc = mInfo->mSoa.data(); - auto bufferDesc = mInfo->mSoa.data(); - auto offsets = mInfo->mSoa.data(); - auto attribToBufferIndex = mInfo->mSoa.data(); - std::fill(mInfo->mSoa.begin(), - mInfo->mSoa.end(), -1); - - for (uint32_t attribIndex = 0; attribIndex < vbi->attributes.size(); attribIndex++) { - Attribute attrib = vbi->attributes[attribIndex]; +VulkanVertexBufferInfo::VulkanVertexBufferInfo( + uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes) + : HwVertexBufferInfo(bufferCount, attributeCount), + VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO), + mInfo(attributes.size()) { + + auto attribDesc = mInfo.mSoa.data(); + auto bufferDesc = mInfo.mSoa.data(); + auto offsets = mInfo.mSoa.data(); + auto attribToBufferIndex = mInfo.mSoa.data(); + std::fill(mInfo.mSoa.begin(), + mInfo.mSoa.end(), -1); + + for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) { + Attribute attrib = attributes[attribIndex]; bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET; bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED; VkFormat vkformat = getVkFormat(attrib.type, isNormalized, isInteger); @@ -326,7 +315,7 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& // expects to receive floats or ints. if (attrib.buffer == Attribute::BUFFER_UNUSED) { vkformat = isInteger ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_SNORM; - attrib = vbi->attributes[0]; + attrib = attributes[0]; } offsets[attribIndex] = attrib.offset; attribDesc[attribIndex] = { @@ -342,18 +331,23 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& } } -VulkanVertexBuffer::~VulkanVertexBuffer() { - delete mInfo; +VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, + VulkanResourceAllocator* allocator, + uint32_t vertexCount, Handle vbih) + : HwVertexBuffer(vertexCount), + VulkanResource(VulkanResourceType::VERTEX_BUFFER), + vbih(vbih), + mBuffers(MAX_VERTEX_BUFFER_COUNT), // TODO: can we do better here? + mResources(allocator) { } void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator, VulkanBufferObject* bufferObject, uint32_t index) { - VulkanVertexBufferInfo const* const vbi = const_cast(allocator).handle_cast(vbih); - size_t count = vbi->attributes.size(); - auto vkbuffers = mInfo->mSoa.data(); - auto attribToBuffer = mInfo->mSoa.data(); + size_t const count = vbi->getAttributeCount(); + VkBuffer* const vkbuffers = getVkBuffers(); + int8_t const* const attribToBuffer = vbi->getAttributeToBuffer(); for (uint8_t attribIndex = 0; attribIndex < count; attribIndex++) { if (attribToBuffer[attribIndex] == static_cast(index)) { vkbuffers[attribIndex] = bufferObject->buffer.getGpuBuffer(); @@ -369,35 +363,6 @@ VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount), bindingType(bindingType) {} -void VulkanRenderPrimitive::setPrimitiveType(PrimitiveType pt) { - this->type = pt; - switch (pt) { - case PrimitiveType::POINTS: - primitiveTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST; - break; - case PrimitiveType::LINES: - primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; - break; - case PrimitiveType::LINE_STRIP: - primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; - break; - case PrimitiveType::TRIANGLES: - primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - break; - case PrimitiveType::TRIANGLE_STRIP: - primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; - break; - } -} - -void VulkanRenderPrimitive::setBuffers(VulkanVertexBuffer* vertexBuffer, - VulkanIndexBuffer* indexBuffer) { - this->vertexBuffer = vertexBuffer; - this->indexBuffer = indexBuffer; - mResources.acquire(vertexBuffer); - mResources.acquire(indexBuffer); -} - VulkanTimerQuery::VulkanTimerQuery(std::tuple indices) : VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY), mStartingQueryIndex(std::get<0>(indices)), @@ -429,4 +394,15 @@ bool VulkanTimerQuery::isCompleted() noexcept { VulkanTimerQuery::~VulkanTimerQuery() = default; +VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator, + PrimitiveType pt, Handle vbh, Handle ibh) + : VulkanResource(VulkanResourceType::RENDER_PRIMITIVE), + mResources(resourceAllocator) { + type = pt; + vertexBuffer = resourceAllocator->handle_cast(vbh); + indexBuffer = resourceAllocator->handle_cast(ibh); + mResources.acquire(vertexBuffer); + mResources.acquire(indexBuffer); +} + } // namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index f031f1c56f7..7c65915d51f 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -144,61 +144,69 @@ struct VulkanBufferObject; struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource { VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes); - AttributeArray attributes; -}; - -struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource { - VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, - VulkanResourceAllocator* allocator, - uint32_t vertexCount, Handle vbih); - - ~VulkanVertexBuffer(); - - void setBuffer(VulkanResourceAllocator const& allocator, - VulkanBufferObject* bufferObject, uint32_t index); - inline VkVertexInputAttributeDescription const* getAttribDescriptions() { - return mInfo->mSoa.data(); + inline VkVertexInputAttributeDescription const* getAttribDescriptions() const { + return mInfo.mSoa.data(); } - inline VkVertexInputBindingDescription const* getBufferDescriptions() { - return mInfo->mSoa.data(); + inline VkVertexInputBindingDescription const* getBufferDescriptions() const { + return mInfo.mSoa.data(); } - inline VkBuffer const* getVkBuffers() const { - return mInfo->mSoa.data(); + inline int8_t const* getAttributeToBuffer() const { + return mInfo.mSoa.data(); } inline VkDeviceSize const* getOffsets() const { - return mInfo->mSoa.data(); + return mInfo.mSoa.data(); } - Handle vbih; + size_t getAttributeCount() const noexcept { + return mInfo.mSoa.size(); + } private: struct PipelineInfo { - PipelineInfo(size_t size) - : mSoa(size /* capacity */) { + PipelineInfo(size_t size) : mSoa(size /* capacity */) { mSoa.resize(size); } // These correspond to the index of the element in the SoA static constexpr uint8_t ATTRIBUTE_DESCRIPTION = 0; static constexpr uint8_t BUFFER_DESCRIPTION = 1; - static constexpr uint8_t VK_BUFFER = 2; - static constexpr uint8_t OFFSETS = 3; - static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 4; + static constexpr uint8_t OFFSETS = 2; + static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 3; utils::StructureOfArrays< VkVertexInputAttributeDescription, VkVertexInputBindingDescription, - VkBuffer, VkDeviceSize, int8_t > mSoa; }; - PipelineInfo* mInfo; + PipelineInfo mInfo; +}; + +struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource { + VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, + VulkanResourceAllocator* allocator, + uint32_t vertexCount, Handle vbih); + + void setBuffer(VulkanResourceAllocator const& allocator, + VulkanBufferObject* bufferObject, uint32_t index); + + inline VkBuffer const* getVkBuffers() const { + return mBuffers.data(); + } + + inline VkBuffer* getVkBuffers() { + return mBuffers.data(); + } + + Handle vbih; +private: + utils::FixedCapacityVector mBuffers; FixedSizeVulkanResourceManager mResources; }; @@ -231,19 +239,15 @@ struct VulkanSamplerGroup : public HwSamplerGroup, VulkanResource { }; struct VulkanRenderPrimitive : public HwRenderPrimitive, VulkanResource { - VulkanRenderPrimitive(VulkanResourceAllocator* allocator) - : VulkanResource(VulkanResourceType::RENDER_PRIMITIVE), - mResources(allocator) {} + VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator, + PrimitiveType pt, Handle vbh, Handle ibh); ~VulkanRenderPrimitive() { mResources.clear(); } - void setPrimitiveType(PrimitiveType pt); - void setBuffers(VulkanVertexBuffer* vertexBuffer, VulkanIndexBuffer* indexBuffer); VulkanVertexBuffer* vertexBuffer = nullptr; VulkanIndexBuffer* indexBuffer = nullptr; - VkPrimitiveTopology primitiveTopology; private: // Keep references to the vertex buffer and the index buffer. diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.h b/filament/backend/src/vulkan/VulkanPipelineCache.h index b2acb37ec27..9888c59fb88 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.h +++ b/filament/backend/src/vulkan/VulkanPipelineCache.h @@ -191,10 +191,19 @@ class VulkanPipelineCache : public CommandBufferObserver { mDummyTargetInfo.imageView = imageView; } - // Acquires a resource to be bound to the current pipeline. The ownership of the resource - // will be transferred to the corresponding pipeline when pipeline is bound. - void acquireResource(VulkanResource* resource) { - mPipelineBoundResources.acquire(resource); + static VkPrimitiveTopology getPrimitiveTopology(PrimitiveType pt) noexcept { + switch (pt) { + case PrimitiveType::POINTS: + return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + case PrimitiveType::LINES: + return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + case PrimitiveType::LINE_STRIP: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; + case PrimitiveType::TRIANGLES: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + case PrimitiveType::TRIANGLE_STRIP: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + } } private: diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 3a1b2b18a38..461acba3a20 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -41,6 +41,7 @@ #include "details/Material.h" #include "details/MaterialInstance.h" #include "details/Texture.h" +#include "details/VertexBuffer.h" #include "generated/resources/materials.h" @@ -190,8 +191,9 @@ std::pair FMaterial* const material = getMaterial(engine); material->prepareProgram(Variant{ variantKey }); return {{ - .program = material->getProgram(Variant{ variantKey }), - .rasterState = material->getRasterState() }, + .program = material->getProgram(Variant{ variantKey }), + .vertexBufferInfo = engine.getFullScreenVertexBuffer()->getVertexBufferInfoHandle(), + .rasterState = material->getRasterState() }, material->getDefaultInstance()->getScissor() }; } diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 016c9976a4f..de606809703 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -55,6 +55,7 @@ #include #include #include +#include using namespace utils; using namespace filament::math; @@ -866,6 +867,10 @@ void RenderPass::Executor::execute(FEngine& engine, .polygonOffset = mPolygonOffset, }; + PipelineState currentPipeline{}; + Handle currentPrimitiveHandle{}; + bool rebindPipeline = true; + FMaterialInstance const* UTILS_RESTRICT mi = nullptr; FMaterial const* UTILS_RESTRICT ma = nullptr; auto const* UTILS_RESTRICT pCustomCommands = mCustomCommands.data(); @@ -918,7 +923,9 @@ void RenderPass::Executor::execute(FEngine& engine, // per-renderable uniform PrimitiveInfo const info = first->primitive; pipeline.rasterState = info.rasterState; - //pipeline.vertexBufferInfo = info.vertexBufferInfo; + pipeline.vertexBufferInfo = info.primitive->getVertexBufferInfoHandle(); + pipeline.primitiveType = info.primitive->getPrimitiveType(); + assert_invariant(pipeline.vertexBufferInfo); if (UTILS_UNLIKELY(mi != info.primitive->getMaterialInstance())) { // this is always taken the first time @@ -940,6 +947,11 @@ void RenderPass::Executor::execute(FEngine& engine, } pipeline.stencilState = mi->getStencilState(); mi->use(driver); + + // FIXME: MaterialInstance changed (not necessarily the program though), + // however, texture bindings may have changed and currently we need to + // rebind the pipeline when that happens. + rebindPipeline = true; } assert_invariant(ma); @@ -989,6 +1001,10 @@ void RenderPass::Executor::execute(FEngine& engine, // note: even if only skinning is enabled, binding morphTargetBuffer is needed. driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING, info.morphTargetBuffer); + + // FIXME: Currently we need to rebind the PipelineState when texture or + // UBO binding change. + rebindPipeline = true; } if (UTILS_UNLIKELY(info.morphWeightBuffer)) { @@ -1001,10 +1017,27 @@ void RenderPass::Executor::execute(FEngine& engine, // note: even if only morphing is enabled, binding skinningTexture is needed. driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING, info.skinningTexture); + + // FIXME: Currently we need to rebind the PipelineState when texture or + // UBO binding change. + rebindPipeline = true; + } + + if (rebindPipeline || + (memcmp(&pipeline, ¤tPipeline, sizeof(PipelineState)) != 0)) { + rebindPipeline = false; + currentPipeline = pipeline; + driver.bindPipeline(pipeline); + } + + if (info.primitive->getHwHandle() != currentPrimitiveHandle) { + currentPrimitiveHandle = info.primitive->getHwHandle(); + driver.bindRenderPrimitive(info.primitive->getHwHandle()); } - driver.draw(pipeline, info.primitive->getHwHandle(), - info.primitive->getIndexOffset(), info.primitive->getIndexCount(), + driver.draw2( + info.primitive->getIndexOffset(), + info.primitive->getIndexCount(), instanceCount); } } diff --git a/filament/src/RenderPrimitive.cpp b/filament/src/RenderPrimitive.cpp index 6a523b8b6e8..8bd5e5ad573 100644 --- a/filament/src/RenderPrimitive.cpp +++ b/filament/src/RenderPrimitive.cpp @@ -16,14 +16,18 @@ #include "RenderPrimitive.h" -#include "details/Engine.h" +#include +#include + #include "details/IndexBuffer.h" -#include "details/Material.h" +#include "details/MaterialInstance.h" #include "details/VertexBuffer.h" +#include +#include + #include -#include #include namespace filament { @@ -39,18 +43,7 @@ void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverAp if (entry.indices && entry.vertices) { FVertexBuffer* vertexBuffer = downcast(entry.vertices); FIndexBuffer* indexBuffer = downcast(entry.indices); - - AttributeBitset const enabledAttributes = vertexBuffer->getDeclaredAttributes(); - - auto const& ebh = vertexBuffer->getHwHandle(); - auto const& ibh = indexBuffer->getHwHandle(); - - mHandle = factory.create(driver, ebh, ibh, entry.type); - - mPrimitiveType = entry.type; - mIndexOffset = entry.offset; - mIndexCount = entry.count; - mEnabledAttributes = enabledAttributes; + set(factory, driver, entry.type, vertexBuffer, indexBuffer, entry.offset, entry.count); } } @@ -62,17 +55,19 @@ void FRenderPrimitive::terminate(HwRenderPrimitiveFactory& factory, backend::Dri void FRenderPrimitive::set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver, RenderableManager::PrimitiveType type, - FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset, size_t count) noexcept { + FVertexBuffer* vertexBuffer, FIndexBuffer* indexBuffer, + size_t offset, size_t count) noexcept { if (mHandle) { factory.destroy(driver, mHandle); } - AttributeBitset const enabledAttributes = vertices->getDeclaredAttributes(); + AttributeBitset const enabledAttributes = vertexBuffer->getDeclaredAttributes(); - auto const& ebh = vertices->getHwHandle(); - auto const& ibh = indices->getHwHandle(); + auto const& ebh = vertexBuffer->getHwHandle(); + auto const& ibh = indexBuffer->getHwHandle(); mHandle = factory.create(driver, ebh, ibh, type); + mVertexBufferInfoHandle = vertexBuffer->getVertexBufferInfoHandle(); mPrimitiveType = type; mIndexOffset = offset; diff --git a/filament/src/RenderPrimitive.h b/filament/src/RenderPrimitive.h index bc1c897484a..07d7431f021 100644 --- a/filament/src/RenderPrimitive.h +++ b/filament/src/RenderPrimitive.h @@ -45,14 +45,15 @@ class FRenderPrimitive { void set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver, RenderableManager::PrimitiveType type, - FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset, + FVertexBuffer* vertexBuffer, FIndexBuffer* indexBuffer, size_t offset, size_t count) noexcept; // frees driver resources, object becomes invalid void terminate(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver); const FMaterialInstance* getMaterialInstance() const noexcept { return mMaterialInstance; } - backend::Handle getHwHandle() const noexcept { return mHandle; } + backend::RenderPrimitiveHandle getHwHandle() const noexcept { return mHandle; } + backend::VertexBufferInfoHandle getVertexBufferInfoHandle() const { return mVertexBufferInfoHandle; } uint32_t getIndexOffset() const noexcept { return mIndexOffset; } uint32_t getIndexCount() const noexcept { return mIndexCount; } @@ -76,7 +77,7 @@ class FRenderPrimitive { struct { FMaterialInstance const* mMaterialInstance = nullptr; backend::Handle mHandle = {}; - UTILS_UNUSED uint8_t padding[4]= {}; + backend::Handle mVertexBufferInfoHandle = {}; uint32_t mIndexOffset = 0; uint32_t mIndexCount = 0; }; diff --git a/filament/src/details/VertexBuffer.h b/filament/src/details/VertexBuffer.h index 56c5f5bb61c..759c7fe0198 100644 --- a/filament/src/details/VertexBuffer.h +++ b/filament/src/details/VertexBuffer.h @@ -52,6 +52,8 @@ class FVertexBuffer : public VertexBuffer { VertexBufferHandle getHwHandle() const noexcept { return mHandle; } + VertexBufferInfoHandle getVertexBufferInfoHandle() const { return mVertexBufferInfoHandle; } + size_t getVertexCount() const noexcept; AttributeBitset getDeclaredAttributes() const noexcept { From 0c48f4083618c795a4b72797a4c3e1f9440564fd Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Fri, 8 Mar 2024 15:18:43 -0800 Subject: [PATCH 10/16] vk: add /usr/local/lib to rpath on macos (#7643) For reasons unknown, after upgrading to XCode 15.3, dlopen can no longer find libvulkan.1.dylib. We fix it by explicitly adding /usr/local/lib to rpath for macos. --- samples/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 00a17efee6f..9b7804e94f3 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -222,6 +222,12 @@ function(add_demo NAME) target_link_libraries(${NAME} PRIVATE sample-resources filamentapp) target_compile_options(${NAME} PRIVATE ${COMPILER_FLAGS}) set_target_properties(${NAME} PROPERTIES FOLDER Samples) + + # This is needed after XCode 15.3 + if (APPLE) + set_target_properties(${NAME} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE) + set_target_properties(${NAME} PROPERTIES INSTALL_RPATH /usr/local/lib) + endif() endfunction() if (NOT ANDROID) From c1dfd8553dc5ac8d610224503c114244c0c33f43 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Mon, 11 Mar 2024 21:38:10 +0000 Subject: [PATCH 11/16] Release Filament 1.50.6 --- NEW_RELEASE_NOTES.md | 2 -- README.md | 4 ++-- RELEASE_NOTES.md | 4 ++++ android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 82631f524ae..4a1a9c7fa7e 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,5 +7,3 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut - -- materials: add support for post-lighting mix factor (b/328498606) [⚠️ **New Material Version**] diff --git a/README.md b/README.md index 38792b39368..3bff570f9c5 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.50.5' + implementation 'com.google.android.filament:filament-android:1.50.6' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.50.5' +pod 'Filament', '~> 1.50.6' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 64ab3f47697..e3b32ecc38d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -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.51.0 + +- materials: add support for post-lighting mix factor (b/328498606) [⚠️ **New Material Version**] + ## v1.50.6 - Add new API `SwapChain::getFrameScheduledCallback` diff --git a/android/gradle.properties b/android/gradle.properties index 0c82646753c..5dd1904abe2 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.50.5 +VERSION_NAME=1.50.6 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 29df33ce0f0..e92cedb1b49 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.50.5" + spec.version = "1.50.6" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.50.5/filament-v1.50.5-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.50.6/filament-v1.50.6-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/web/filament-js/package.json b/web/filament-js/package.json index ff16aeadc9d..582f75eec43 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.50.5", + "version": "1.50.6", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From f63296fc18f176546a747a02c139ea8a98f85aa2 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Mon, 11 Mar 2024 21:38:38 +0000 Subject: [PATCH 12/16] Bump version to 1.51.0 --- README.md | 4 ++-- android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3bff570f9c5..8a4adea8f24 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.50.6' + implementation 'com.google.android.filament:filament-android:1.51.0' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.50.6' +pod 'Filament', '~> 1.51.0' ``` ### Snapshots diff --git a/android/gradle.properties b/android/gradle.properties index 5dd1904abe2..535a3002648 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.50.6 +VERSION_NAME=1.51.0 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index e92cedb1b49..513d6c19752 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.50.6" + spec.version = "1.51.0" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.50.6/filament-v1.50.6-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.0/filament-v1.51.0-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/web/filament-js/package.json b/web/filament-js/package.json index 582f75eec43..2b2c4d8af55 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.50.6", + "version": "1.51.0", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From 4ad07e25d49ac44392245c86995010996b01e5ff Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Tue, 12 Mar 2024 12:10:43 -0700 Subject: [PATCH 13/16] Move SwapChain flags into separate file (#7654) --- .../com/google/android/filament/Engine.java | 26 ++--- .../google/android/filament/SwapChain.java | 72 +------------- .../android/filament/SwapChainFlags.java | 97 +++++++++++++++++++ .../android/filament/android/UiHelper.java | 4 +- 4 files changed, 117 insertions(+), 82 deletions(-) create mode 100644 android/filament-android/src/main/java/com/google/android/filament/SwapChainFlags.java diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java index a858330eb4a..aee4c2b34aa 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java @@ -654,7 +654,7 @@ public long getMaxStereoscopicEyes() { */ @NonNull public SwapChain createSwapChain(@NonNull Object surface) { - return createSwapChain(surface, SwapChain.CONFIG_DEFAULT); + return createSwapChain(surface, SwapChainFlags.CONFIG_DEFAULT); } /** @@ -662,15 +662,15 @@ public SwapChain createSwapChain(@NonNull Object surface) { * * @param surface on Android, must be an instance of {@link android.view.Surface} * - * @param flags configuration flags, see {@link SwapChain} + * @param flags configuration flags, see {@link SwapChainFlags} * * @return a newly created {@link SwapChain} object * * @exception IllegalStateException can be thrown if the SwapChain couldn't be created * - * @see SwapChain#CONFIG_DEFAULT - * @see SwapChain#CONFIG_TRANSPARENT - * @see SwapChain#CONFIG_READABLE + * @see SwapChainFlags#CONFIG_DEFAULT + * @see SwapChainFlags#CONFIG_TRANSPARENT + * @see SwapChainFlags#CONFIG_READABLE * */ @NonNull @@ -688,21 +688,22 @@ public SwapChain createSwapChain(@NonNull Object surface, long flags) { * * @param width width of the rendering buffer * @param height height of the rendering buffer - * @param flags configuration flags, see {@link SwapChain} + * @param flags configuration flags, see {@link SwapChainFlags} * * @return a newly created {@link SwapChain} object * * @exception IllegalStateException can be thrown if the SwapChain couldn't be created * - * @see SwapChain#CONFIG_DEFAULT - * @see SwapChain#CONFIG_TRANSPARENT - * @see SwapChain#CONFIG_READABLE + * @see SwapChainFlags#CONFIG_DEFAULT + * @see SwapChainFlags#CONFIG_TRANSPARENT + * @see SwapChainFlags#CONFIG_READABLE * */ @NonNull public SwapChain createSwapChain(int width, int height, long flags) { if (width >= 0 && height >= 0) { - long nativeSwapChain = nCreateSwapChainHeadless(getNativeObject(), width, height, flags); + long nativeSwapChain = + nCreateSwapChainHeadless(getNativeObject(), width, height, flags); if (nativeSwapChain == 0) throw new IllegalStateException("Couldn't create SwapChain"); return new SwapChain(nativeSwapChain, null); } @@ -714,11 +715,12 @@ public SwapChain createSwapChain(int width, int height, long flags) { * * @param surface a properly initialized {@link NativeSurface} * - * @param flags configuration flags, see {@link SwapChain} + * @param flags configuration flags, see {@link SwapChainFlags} * * @return a newly created {@link SwapChain} object * - * @exception IllegalStateException can be thrown if the {@link SwapChain} couldn't be created + * @exception IllegalStateException can be thrown if the {@link SwapChainFlags} couldn't be + * created */ @NonNull public SwapChain createSwapChainFromNativeSurface(@NonNull NativeSurface surface, long flags) { diff --git a/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java b/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java index e3d1cf43aad..db47d215635 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java +++ b/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java @@ -68,72 +68,6 @@ public class SwapChain { private final Object mSurface; private long mNativeObject; - public static final long CONFIG_DEFAULT = 0x0; - - /** - * This flag indicates that the SwapChain must be allocated with an - * alpha-channel. - */ - public static final long CONFIG_TRANSPARENT = 0x1; - - /** - * This flag indicates that the SwapChain may be used as a source surface - * for reading back render results. This config must be set when creating - * any SwapChain that will be used as the source for a blit operation. - * - * @see Renderer#copyFrame - */ - public static final long CONFIG_READABLE = 0x2; - - /** - * Indicates that the native X11 window is an XCB window rather than an XLIB window. - * This is ignored on non-Linux platforms and in builds that support only one X11 API. - */ - public static final long CONFIG_ENABLE_XCB = 0x4; - - /** - * Indicates that the SwapChain must automatically perform linear to sRGB encoding. - * - * This flag is ignored if isSRGBSwapChainSupported() is false. - * - * When using this flag, post-processing should be disabled. - * - * @see SwapChain#isSRGBSwapChainSupported - * @see View#setPostProcessingEnabled - */ - public static final long CONFIG_SRGB_COLORSPACE = 0x10; - - /** - * Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer. - * - * This flag is necessary when using View::setStencilBufferEnabled and rendering directly into - * the SwapChain (when post-processing is disabled). - * - * The specific format of the stencil buffer depends on platform support. The following pixel - * formats are tried, in order of preference: - * - * Depth only (without CONFIG_HAS_STENCIL_BUFFER): - * - DEPTH32F - * - DEPTH24 - * - * Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER): - * - DEPTH32F_STENCIL8 - * - DEPTH24F_STENCIL8 - * - * Note that enabling the stencil buffer may hinder depth precision and should only be used if - * necessary. - * - * @see View#setStencilBufferEnabled - * @see View#setPostProcessingEnabled - */ - public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20; - - /** - * The SwapChain contains protected content. Only supported when isProtectedContentSupported() - * is true. - */ - public static final long CONFIG_PROTECTED_CONTENT = 0x40; - SwapChain(long nativeSwapChain, Object surface) { mNativeObject = nativeSwapChain; mSurface = surface; @@ -145,17 +79,19 @@ public class SwapChain { * * @param engine A reference to the filament Engine * @return true if CONFIG_PROTECTED_CONTENT is supported, false otherwise. + * @see SwapChainFlags#CONFIG_PROTECTED_CONTENT */ public static boolean isProtectedContentSupported(@NonNull Engine engine) { return nIsProtectedContentSupported(engine.getNativeObject()); } /** - * Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag. + * Return whether createSwapChain supports the CONFIG_SRGB_COLORSPACE flag. * The default implementation returns false. * * @param engine A reference to the filament Engine - * @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise. + * @return true if CONFIG_SRGB_COLORSPACE is supported, false otherwise. + * @see SwapChainFlags#CONFIG_SRGB_COLORSPACE */ public static boolean isSRGBSwapChainSupported(@NonNull Engine engine) { return nIsSRGBSwapChainSupported(engine.getNativeObject()); diff --git a/android/filament-android/src/main/java/com/google/android/filament/SwapChainFlags.java b/android/filament-android/src/main/java/com/google/android/filament/SwapChainFlags.java new file mode 100644 index 00000000000..daef2dd15f2 --- /dev/null +++ b/android/filament-android/src/main/java/com/google/android/filament/SwapChainFlags.java @@ -0,0 +1,97 @@ +/* + * 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. + */ + +package com.google.android.filament; + +// Note: SwapChainFlags is kept separate from SwapChain so that UiHelper does not need to depend +// on SwapChain. This allows clients to use UiHelper without requiring all of Filament's Java +// classes. + +/** + * Flags that a SwapChain can be created with to control behavior. + * + * @see Engine#createSwapChain + * @see Engine#createSwapChainFromNativeSurface + */ +public final class SwapChainFlags { + + public static final long CONFIG_DEFAULT = 0x0; + + /** + * This flag indicates that the SwapChain must be allocated with an + * alpha-channel. + */ + public static final long CONFIG_TRANSPARENT = 0x1; + + /** + * This flag indicates that the SwapChain may be used as a source surface + * for reading back render results. This config must be set when creating + * any SwapChain that will be used as the source for a blit operation. + * + * @see Renderer#copyFrame + */ + public static final long CONFIG_READABLE = 0x2; + + /** + * Indicates that the native X11 window is an XCB window rather than an XLIB window. + * This is ignored on non-Linux platforms and in builds that support only one X11 API. + */ + public static final long CONFIG_ENABLE_XCB = 0x4; + + /** + * Indicates that the SwapChain must automatically perform linear to sRGB encoding. + * + * This flag is ignored if isSRGBSwapChainSupported() is false. + * + * When using this flag, post-processing should be disabled. + * + * @see SwapChain#isSRGBSwapChainSupported + * @see View#setPostProcessingEnabled + */ + public static final long CONFIG_SRGB_COLORSPACE = 0x10; + + /** + * Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer. + * + * This flag is necessary when using View::setStencilBufferEnabled and rendering directly into + * the SwapChain (when post-processing is disabled). + * + * The specific format of the stencil buffer depends on platform support. The following pixel + * formats are tried, in order of preference: + * + * Depth only (without CONFIG_HAS_STENCIL_BUFFER): + * - DEPTH32F + * - DEPTH24 + * + * Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER): + * - DEPTH32F_STENCIL8 + * - DEPTH24F_STENCIL8 + * + * Note that enabling the stencil buffer may hinder depth precision and should only be used if + * necessary. + * + * @see View#setStencilBufferEnabled + * @see View#setPostProcessingEnabled + */ + public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20; + + /** + * The SwapChain contains protected content. Only supported when isProtectedContentSupported() + * is true. + */ + public static final long CONFIG_PROTECTED_CONTENT = 0x40; +} + diff --git a/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java b/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java index 451c09a33be..4d6f3a140f1 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java +++ b/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java @@ -27,7 +27,7 @@ import android.view.SurfaceView; import android.view.TextureView; -import com.google.android.filament.SwapChain; +import com.google.android.filament.SwapChainFlags; /** * UiHelper is a simple class that can manage either a SurfaceView, TextureView, or a SurfaceHolder @@ -538,7 +538,7 @@ public void setMediaOverlay(boolean overlay) { * the options set on this UiHelper. */ public long getSwapChainFlags() { - return isOpaque() ? SwapChain.CONFIG_DEFAULT : SwapChain.CONFIG_TRANSPARENT; + return isOpaque() ? SwapChainFlags.CONFIG_DEFAULT : SwapChainFlags.CONFIG_TRANSPARENT; } /** From 5ca7f415130f511dc73e57d6ac043f5901d46a28 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Tue, 12 Mar 2024 13:06:43 -0700 Subject: [PATCH 14/16] Metal: respect disableParallelShaderCompile config (#7659) --- filament/backend/include/backend/Platform.h | 2 +- filament/backend/src/metal/MetalDriver.mm | 7 +- .../backend/src/metal/MetalShaderCompiler.h | 16 +++-- .../backend/src/metal/MetalShaderCompiler.mm | 71 ++++++++++--------- filament/include/filament/Engine.h | 2 +- 5 files changed, 57 insertions(+), 41 deletions(-) diff --git a/filament/backend/include/backend/Platform.h b/filament/backend/include/backend/Platform.h index b89fb98f7fd..1d85568a883 100644 --- a/filament/backend/include/backend/Platform.h +++ b/filament/backend/include/backend/Platform.h @@ -56,7 +56,7 @@ class UTILS_PUBLIC Platform { /** * Set to `true` to forcibly disable parallel shader compilation in the backend. - * Currently only honored by the GL backend. + * Currently only honored by the GL and Metal backends. */ bool disableParallelShaderCompile = false; }; diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index a47c57a30fa..8dfbb27c51b 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -180,7 +180,10 @@ mContext->eventListener = [[MTLSharedEventListener alloc] initWithDispatchQueue:queue]; } - mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this); + const MetalShaderCompiler::Mode compilerMode = driverConfig.disableParallelShaderCompile + ? MetalShaderCompiler::Mode::SYNCHRONOUS + : MetalShaderCompiler::Mode::ASYNCHRONOUS; + mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this, compilerMode); mContext->shaderCompiler->init(); #if defined(FILAMENT_METAL_PROFILING) @@ -788,7 +791,7 @@ } bool MetalDriver::isParallelShaderCompileSupported() { - return true; + return mContext->shaderCompiler->isParallelShaderCompileSupported(); } bool MetalDriver::isDepthStencilResolveSupported() { diff --git a/filament/backend/src/metal/MetalShaderCompiler.h b/filament/backend/src/metal/MetalShaderCompiler.h index 5d967081e49..f0b827ad6ac 100644 --- a/filament/backend/src/metal/MetalShaderCompiler.h +++ b/filament/backend/src/metal/MetalShaderCompiler.h @@ -41,6 +41,11 @@ class MetalShaderCompiler { struct MetalProgramToken; public: + enum class Mode { + SYNCHRONOUS, // synchronous shader compilation + ASYNCHRONOUS // asynchronous shader compilation + }; + class MetalFunctionBundle { public: using Raster = std::tuple, id>; @@ -110,7 +115,7 @@ class MetalShaderCompiler { using program_token_t = std::shared_ptr; - explicit MetalShaderCompiler(id device, MetalDriver& driver); + explicit MetalShaderCompiler(id device, MetalDriver& driver, Mode mode); MetalShaderCompiler(MetalShaderCompiler const& rhs) = delete; MetalShaderCompiler(MetalShaderCompiler&& rhs) = delete; @@ -120,15 +125,15 @@ class MetalShaderCompiler { void init() noexcept; void terminate() noexcept; - // Creates a program asynchronously + bool isParallelShaderCompileSupported() const noexcept; + + // Creates a program, either synchronously or asynchronously, depending on the Mode + // MetalShaderCompiler was constructed with. program_token_t createProgram(utils::CString const& name, Program&& program); // Returns the functions, blocking if necessary. The Token is destroyed and becomes invalid. MetalFunctionBundle getProgram(program_token_t& token); - // Destroys a valid token and all associated resources. Used to "cancel" a program compilation. - static void terminate(program_token_t& token); - void notifyWhenAllProgramsAreReady( CallbackHandler* handler, CallbackHandler::Callback callback, void* user); @@ -138,6 +143,7 @@ class MetalShaderCompiler { CompilerThreadPool mCompilerThreadPool; id mDevice; CallbackManager mCallbackManager; + Mode mMode; }; } // namespace filament::backend diff --git a/filament/backend/src/metal/MetalShaderCompiler.mm b/filament/backend/src/metal/MetalShaderCompiler.mm index 713133b1db4..7c2cffdb103 100644 --- a/filament/backend/src/metal/MetalShaderCompiler.mm +++ b/filament/backend/src/metal/MetalShaderCompiler.mm @@ -70,22 +70,31 @@ bool isReady() const noexcept { MetalShaderCompiler::MetalProgramToken::~MetalProgramToken() = default; -MetalShaderCompiler::MetalShaderCompiler(id device, MetalDriver& driver) +MetalShaderCompiler::MetalShaderCompiler(id device, MetalDriver& driver, Mode mode) : mDevice(device), - mCallbackManager(driver) { + mCallbackManager(driver), + mMode(mode) { } void MetalShaderCompiler::init() noexcept { const uint32_t poolSize = 1; - mCompilerThreadPool.init(poolSize, []() {}, []() {}); + if (mMode == Mode::ASYNCHRONOUS) { + mCompilerThreadPool.init(poolSize, []() {}, []() {}); + } } void MetalShaderCompiler::terminate() noexcept { - mCompilerThreadPool.terminate(); + if (mMode == Mode::ASYNCHRONOUS) { + mCompilerThreadPool.terminate(); + } mCallbackManager.terminate(); } +bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept { + return mMode == Mode::ASYNCHRONOUS; +} + /* static */ MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::compileProgram( const Program& program, id device) { std::array, Program::SHADER_TYPE_COUNT> functions = { nil }; @@ -180,14 +189,26 @@ bool isReady() const noexcept { token->handle = mCallbackManager.get(); - CompilerPriorityQueue const priorityQueue = program.getPriorityQueue(); - mCompilerThreadPool.queue(priorityQueue, token, - [this, name, device = mDevice, program = std::move(program), token]() { - MetalFunctionBundle compiledProgram = compileProgram(program, device); + switch (mMode) { + case Mode::ASYNCHRONOUS: { + CompilerPriorityQueue const priorityQueue = program.getPriorityQueue(); + mCompilerThreadPool.queue(priorityQueue, token, + [this, name, device = mDevice, program = std::move(program), token]() { + MetalFunctionBundle compiledProgram = compileProgram(program, device); + token->set(compiledProgram); + mCallbackManager.put(token->handle); + }); + + break; + } - token->set(compiledProgram); - mCallbackManager.put(token->handle); - }); + case Mode::SYNCHRONOUS: { + MetalFunctionBundle compiledProgram = compileProgram(program, mDevice); + token->set(compiledProgram); + mCallbackManager.put(token->handle); + break; + } + } return token; } @@ -195,35 +216,21 @@ bool isReady() const noexcept { MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::getProgram(program_token_t& token) { assert_invariant(token); - if (!token->isReady()) { - auto job = mCompilerThreadPool.dequeue(token); - if (job) { - job(); + if (mMode == Mode::ASYNCHRONOUS) { + if (!token->isReady()) { + auto job = mCompilerThreadPool.dequeue(token); + if (job) { + job(); + } } } + assert_invariant(token->isReady()); MetalShaderCompiler::MetalFunctionBundle program = token->get(); - token = nullptr; - return program; } -/* static */ void MetalShaderCompiler::terminate(program_token_t& token) { - assert_invariant(token); - - auto job = token->compiler.mCompilerThreadPool.dequeue(token); - if (!job) { - // The job is being executed right now (or has already executed). - token->wait(); - } else { - // The job has not executed yet. - token->compiler.mCallbackManager.put(token->handle); - } - - token.reset(); -} - void MetalShaderCompiler::notifyWhenAllProgramsAreReady( CallbackHandler* handler, CallbackHandler::Callback callback, void* user) { mCallbackManager.setCallback(handler, callback, user); diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 6eb1916f7ab..04d71259b7c 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -300,7 +300,7 @@ class UTILS_PUBLIC Engine { /** * Set to `true` to forcibly disable parallel shader compilation in the backend. - * Currently only honored by the GL backend. + * Currently only honored by the GL and Metal backends. */ bool disableParallelShaderCompile = false; From 6dd85c6530fbc2f13d1aa543f8ad7388d6c8c3f7 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Tue, 12 Mar 2024 14:29:49 -0700 Subject: [PATCH 15/16] Remove erroneous assertion (#7661) --- filament/backend/src/metal/MetalShaderCompiler.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/filament/backend/src/metal/MetalShaderCompiler.mm b/filament/backend/src/metal/MetalShaderCompiler.mm index 7c2cffdb103..7741ae54489 100644 --- a/filament/backend/src/metal/MetalShaderCompiler.mm +++ b/filament/backend/src/metal/MetalShaderCompiler.mm @@ -224,7 +224,9 @@ bool isReady() const noexcept { } } } - assert_invariant(token->isReady()); + + // The job isn't guaranteed to have finished yet. We may have failed to dequeue it above, + // which means it's currently running. In that case get() will block until it finishes. MetalShaderCompiler::MetalFunctionBundle program = token->get(); token = nullptr; From de6df6dc0ebea83d70b3e944536867f25ed90874 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Thu, 14 Mar 2024 10:40:26 -0700 Subject: [PATCH 16/16] Bump MATERIAL_VERSION to 51 --- libs/filabridge/include/filament/MaterialEnums.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index 4c4d3da332b..ebe2ccce20e 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -28,7 +28,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 50; +static constexpr size_t MATERIAL_VERSION = 51; /** * Supported shading models