From f4d87ad526e0e7e327c74d4cc39a6c449c00db8d Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 16 Sep 2024 22:24:29 -0700 Subject: [PATCH 1/5] prepare rendering loop for multithreading The goal of this PR is to get closer to being able to run all FrameGraph passes in parallel. To achieve this we need all data consumed by the "execute" closure of the FrameGraph passes to be immutable or thread-safe. We also need the passes to never use the Engine's global `DriverAPi&` object. Specifically in this PR, we turn as many objects to `const` as possible without major changes, and we pass the `DriverApi&` object as parameter to render passes. This work is far from being complete. So we also annotate with FIXMEs all the places we can identify will be problematic (there are probably others). The main remaining issues are: - main allocator is not thread-safe - some places take a non-const View, Scene or Engine - lazy allocation of materials and material instance usages are not thread-safe. This PR shouldn't change any behavior. --- filament/src/PostProcessManager.cpp | 15 +++--- filament/src/RenderPass.cpp | 61 +++++++++------------- filament/src/RenderPass.h | 33 +++++------- filament/src/RendererUtils.cpp | 2 +- filament/src/ShadowMap.cpp | 6 +-- filament/src/ShadowMap.h | 4 +- filament/src/ShadowMapManager.cpp | 40 +++++++------- filament/src/ShadowMapManager.h | 12 ++--- filament/src/details/Engine.h | 15 +++++- filament/src/details/Material.cpp | 3 +- filament/src/details/Renderer.cpp | 16 +++--- filament/src/details/View.cpp | 4 +- filament/src/details/View.h | 6 +-- filament/src/ds/ShadowMapDescriptorSet.cpp | 6 +-- filament/src/ds/ShadowMapDescriptorSet.h | 4 +- 15 files changed, 115 insertions(+), 112 deletions(-) diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index ec10f0abda..90c743e584 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -519,16 +519,17 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph Variant structureVariant(Variant::DEPTH_VARIANT); structureVariant.setPicking(config.picking); - auto out = resources.getRenderPassInfo(); - bindPostProcessDescriptorSet(driver); passBuilder.renderFlags(structureRenderFlags); passBuilder.variant(structureVariant); passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SSAO); - RenderPass const pass{ passBuilder.build(mEngine) }; - RenderPass::execute(pass, mEngine, resources.getPassName(), out.target, out.params); + RenderPass const pass{ passBuilder.build(mEngine, driver) }; + auto out = resources.getRenderPassInfo(); + driver.beginRenderPass(out.target, out.params); + pass.getExecutor().execute(mEngine, driver); + driver.endRenderPass(); }); auto depth = structurePass->depth; @@ -678,8 +679,10 @@ FrameGraphId PostProcessManager::ssr(FrameGraph& fg, // generate all our drawing commands, except blended objects. passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS); - RenderPass const pass{ passBuilder.build(mEngine) }; - RenderPass::execute(pass, mEngine, resources.getPassName(), out.target, out.params); + RenderPass const pass{ passBuilder.build(mEngine, driver) }; + driver.beginRenderPass(out.target, out.params); + pass.getExecutor().execute(mEngine, driver); + driver.endRenderPass(); }); return ssrPass->reflections; diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 9498184c86..251d00e89e 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -68,7 +68,6 @@ namespace filament { using namespace backend; RenderPassBuilder& RenderPassBuilder::customCommand( - FEngine& engine, uint8_t channel, RenderPass::Pass pass, RenderPass::CustomCommand custom, @@ -76,17 +75,17 @@ RenderPassBuilder& RenderPassBuilder::customCommand( RenderPass::Executor::CustomCommandFn const& command) { if (!mCustomCommands.has_value()) { // construct the vector the first time - mCustomCommands.emplace(engine.getPerRenderPassArena()); + mCustomCommands.emplace(); } mCustomCommands->emplace_back(channel, pass, custom, order, command); return *this; } -RenderPass RenderPassBuilder::build(FEngine& engine) { +RenderPass RenderPassBuilder::build(FEngine const& engine, backend::DriverApi& driver) const { assert_invariant(mRenderableSoa); assert_invariant(mScissorViewport.width <= std::numeric_limits::max()); assert_invariant(mScissorViewport.height <= std::numeric_limits::max()); - return RenderPass{ engine, *this }; + return RenderPass{ engine, driver, *this }; } // ------------------------------------------------------------------------------------------------ @@ -107,11 +106,11 @@ void RenderPass::DescriptorSetHandleDeleter::operator()( // ------------------------------------------------------------------------------------------------ -RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexcept +RenderPass::RenderPass(FEngine const& engine, backend::DriverApi& driver, + RenderPassBuilder const& builder) noexcept : mRenderableSoa(*builder.mRenderableSoa), mColorPassDescriptorSet(builder.mColorPassDescriptorSet), - mScissorViewport(builder.mScissorViewport), - mCustomCommands(engine.getPerRenderPassArena()) { + mScissorViewport(builder.mScissorViewport) { // compute the number of commands we need updateSummedPrimitiveCounts( @@ -127,10 +126,12 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce uint32_t const customCommandCount = builder.mCustomCommands.has_value() ? builder.mCustomCommands->size() : 0; + // FIXME: builder.mArena must be thread safe eventually Command* const commandBegin = builder.mArena.alloc(commandCount + customCommandCount); Command* commandEnd = commandBegin + (commandCount + customCommandCount); assert_invariant(commandBegin); + // FIXME: builder.mArena must be thread safe eventually if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(commandBegin))) { static bool sLogOnce = true; if (UTILS_UNLIKELY(sLogOnce)) { @@ -150,6 +151,7 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce builder.mCameraForwardVector); if (builder.mCustomCommands.has_value()) { + mCustomCommands.reserve(customCommandCount); Command* p = commandBegin + commandCount; for (auto const& [channel, passId, command, order, fn]: builder.mCustomCommands.value()) { appendCustomCommand(p++, channel, passId, command, order, fn); @@ -166,7 +168,9 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce stereoscopicEyeCount *= engine.getConfig().stereoscopicEyeCount; } commandEnd = resize(builder.mArena, - instanceify(engine, commandBegin, commandEnd, stereoscopicEyeCount)); + instanceify(driver, + engine.getPerRenderableDescriptorSetLayout().getHandle(), + commandBegin, commandEnd, stereoscopicEyeCount)); } // these are `const` from this point on... @@ -182,7 +186,7 @@ RenderPass::Command* RenderPass::resize(Arena& arena, Command* const last) noexc return last; } -void RenderPass::appendCommands(FEngine& engine, +void RenderPass::appendCommands(FEngine const& engine, Slice commands, utils::Range const vr, CommandTypeFlags const commandTypeFlags, @@ -283,17 +287,8 @@ RenderPass::Command* RenderPass::sortCommands( return last; } -void RenderPass::execute(RenderPass const& pass, - FEngine& engine, const char* name, - backend::Handle renderTarget, - backend::RenderPassParams params) noexcept { - DriverApi& driver = engine.getDriverApi(); - driver.beginRenderPass(renderTarget, params); - pass.getExecutor().execute(engine, name); - driver.endRenderPass(); -} - -RenderPass::Command* RenderPass::instanceify(FEngine& engine, +RenderPass::Command* RenderPass::instanceify(backend::DriverApi& driver, + DescriptorSetLayoutHandle perRenderableDescriptorSetLayoutHandle, Command* curr, Command* const last, int32_t eyeCount) const noexcept { SYSTRACE_NAME("instanceify"); @@ -352,8 +347,6 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine, if (UTILS_UNLIKELY(instanceCount > 1)) { drawCallsSavedCount += instanceCount - 1; - auto& driver = engine.getDriverApi(); - // allocate our staging buffer only if needed if (UTILS_UNLIKELY(!stagingBuffer)) { // Create a temporary UBO for holding the per-renderable data of each primitive, @@ -382,8 +375,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine, // in this case we would need to preserve the default descriptor-set content). // This has the same lifetime as the UBO (see above). mInstancedDescriptorSetHandle = DescriptorSetSharedHandle{ - driver.createDescriptorSet( - engine.getPerRenderableDescriptorSetLayout().getHandle()), + driver.createDescriptorSet(perRenderableDescriptorSetLayoutHandle), driver }; driver.updateDescriptorSetBuffer(mInstancedDescriptorSetHandle, @@ -418,10 +410,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine, if (UTILS_UNLIKELY(firstSentinel)) { //slog.d << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of " // << count << io::endl; - // we have instanced primitives - DriverApi& driver = engine.getDriverApi(); - // copy our instanced ubo data driver.updateBufferObjectUnsynchronized(mInstancedUboHandle, { stagingBuffer, sizeof(PerRenderableData) * instancedPrimitiveOffset, @@ -866,8 +855,8 @@ void RenderPass::Executor::overrideScissor(backend::Viewport const& scissor) noe mScissor = scissor; } -void RenderPass::Executor::execute(FEngine& engine, const char*) const noexcept { - execute(engine, mCommands.begin(), mCommands.end()); +void RenderPass::Executor::execute(FEngine const& engine, backend::DriverApi& driver) const noexcept { + execute(engine, driver, mCommands.begin(), mCommands.end()); } UTILS_NOINLINE // no need to be inlined @@ -893,14 +882,12 @@ backend::Viewport RenderPass::Executor::applyScissorViewport( } UTILS_NOINLINE // no need to be inlined -void RenderPass::Executor::execute(FEngine& engine, - const Command* first, const Command* last) const noexcept { +void RenderPass::Executor::execute(FEngine const& engine, backend::DriverApi& driver, + Command const* first, Command const* last) const noexcept { SYSTRACE_CALL(); SYSTRACE_CONTEXT(); - DriverApi& driver = engine.getDriverApi(); - size_t const capacity = engine.getMinCommandBufferSize(); CircularBuffer const& circularBuffer = driver.getCircularBuffer(); @@ -965,7 +952,9 @@ void RenderPass::Executor::execute(FEngine& engine, // check we have enough capacity to write these commandCount commands, if not, // request a new CircularBuffer allocation of `capacity` bytes. if (UTILS_UNLIKELY(circularBuffer.getUsed() > capacity - commandSizeInBytes)) { - engine.flush(); // TODO: we should use a "fast" flush if possible + // FIXME: eventually we can't flush here because this will be a secondary + // command buffer. We will need another solution for overflows. + const_cast(engine).flush(); } first--; @@ -1093,7 +1082,9 @@ void RenderPass::Executor::execute(FEngine& engine, // If the remaining space is less than half the capacity, we flush right away to // allow some headroom for commands that might come later. if (UTILS_UNLIKELY(circularBuffer.getUsed() > capacity / 2)) { - engine.flush(); + // FIXME: eventually we can't flush here because this will be a secondary + // command buffer. + const_cast(engine).flush(); } } } diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h index a11d89e9c2..c48bd9be44 100644 --- a/filament/src/RenderPass.h +++ b/filament/src/RenderPass.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -309,13 +310,6 @@ public: Command const* end() const noexcept { return mCommandEnd; } bool empty() const noexcept { return begin() == end(); } - // Helper to execute all the commands generated by this RenderPass - static void execute(RenderPass const& pass, - FEngine& engine, const char* name, - backend::Handle renderTarget, - backend::RenderPassParams params) noexcept; - - class BufferObjectHandleDeleter { std::reference_wrapper driver; public: @@ -364,7 +358,8 @@ public: Executor(RenderPass const& pass, Command const* b, Command const* e) noexcept; - void execute(FEngine& engine, const Command* first, const Command* last) const noexcept; + void execute(FEngine const& engine, backend::DriverApi& driver, + Command const* first, Command const* last) const noexcept; static backend::Viewport applyScissorViewport( backend::Viewport const& scissorViewport, @@ -389,7 +384,7 @@ public: void overrideScissor(backend::Viewport const& scissor) noexcept; - void execute(FEngine& engine, const char* name) const noexcept; + void execute(FEngine const& engine, backend::DriverApi& driver) const noexcept; }; // returns a new executor for this pass @@ -404,11 +399,12 @@ public: private: friend class FRenderer; friend class RenderPassBuilder; - RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexcept; + RenderPass(FEngine const& engine, backend::DriverApi& driver, + RenderPassBuilder const& builder) noexcept; // This is the main function of this class, this appends commands to the pass using // the current camera, geometry and flags set. This can be called multiple times if needed. - void appendCommands(FEngine& engine, + void appendCommands(FEngine const& engine, utils::Slice commands, utils::Range visibleRenderables, CommandTypeFlags commandTypeFlags, @@ -430,7 +426,8 @@ private: Command* begin, Command* end) noexcept; // instanceify commands then trims sentinels - RenderPass::Command* instanceify(FEngine& engine, + RenderPass::Command* instanceify(backend::DriverApi& driver, + backend::DescriptorSetLayoutHandle perRenderableDescriptorSetLayoutHandle, Command* begin, Command* end, int32_t eyeCount) const noexcept; @@ -470,8 +467,7 @@ private: mutable BufferObjectSharedHandle mInstancedUboHandle; // ubo for instanced primitives mutable DescriptorSetSharedHandle mInstancedDescriptorSetHandle; // a descriptor-set to hold the ubo // a vector for our custom commands - using CustomCommandVector = std::vector>; + using CustomCommandVector = utils::FixedCapacityVector; mutable CustomCommandVector mCustomCommands; }; @@ -497,11 +493,10 @@ class RenderPassBuilder { uint32_t, RenderPass::Executor::CustomCommandFn>; - using CustomCommandContainer = std::vector>; + using CustomCommandContainer = std::vector; // we make this optional because it's not used often, and we don't want to have - // to construct it by default. + // to construct it by default. For the same reason we use a std::vector<> std::optional mCustomCommands; public: @@ -569,14 +564,14 @@ public: return *this; } - RenderPassBuilder& customCommand(FEngine& engine, + RenderPassBuilder& customCommand( uint8_t channel, RenderPass::Pass pass, RenderPass::CustomCommand custom, uint32_t order, const RenderPass::Executor::CustomCommandFn& command); - RenderPass build(FEngine& engine); + RenderPass build(FEngine const& engine, backend::DriverApi& driver) const; }; diff --git a/filament/src/RendererUtils.cpp b/filament/src/RendererUtils.cpp index 2e57af691f..5b671dda97 100644 --- a/filament/src/RendererUtils.cpp +++ b/filament/src/RendererUtils.cpp @@ -251,7 +251,7 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass( } driver.beginRenderPass(out.target, out.params); - passExecutor.execute(engine, resources.getPassName()); + passExecutor.execute(engine, driver); driver.endRenderPass(); // color pass is typically heavy, and we don't have much CPU work left after diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index fd5d314580..35fa104dcd 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -1361,8 +1361,8 @@ math::float4 ShadowMap::getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) // ------------------------------------------------------------------------------------------------ void ShadowMap::prepareCamera(Transaction const& transaction, - FEngine& engine, const CameraInfo& cameraInfo) noexcept { - ShadowMapDescriptorSet::prepareCamera(transaction, engine, cameraInfo); + backend::DriverApi& driver, const CameraInfo& cameraInfo) noexcept { + ShadowMapDescriptorSet::prepareCamera(transaction, driver, cameraInfo); ShadowMapDescriptorSet::prepareLodBias(transaction, 0.0f); } @@ -1372,7 +1372,7 @@ void ShadowMap::prepareViewport(Transaction const& transaction, } void ShadowMap::prepareTime(Transaction const& transaction, - FEngine& engine, math::float4 const& userTime) noexcept { + FEngine const& engine, math::float4 const& userTime) noexcept { ShadowMapDescriptorSet::prepareTime(transaction, engine, userTime); } diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h index ed113f0a3a..cf131d4f64 100644 --- a/filament/src/ShadowMap.h +++ b/filament/src/ShadowMap.h @@ -193,11 +193,11 @@ public: using Transaction = ShadowMapDescriptorSet::Transaction; static void prepareCamera(Transaction const& transaction, - FEngine& engine, const CameraInfo& cameraInfo) noexcept; + backend::DriverApi& driver, const CameraInfo& cameraInfo) noexcept; static void prepareViewport(Transaction const& transaction, backend::Viewport const& viewport) noexcept; static void prepareTime(Transaction const& transaction, - FEngine& engine, math::float4 const& userTime) noexcept; + FEngine const& engine, math::float4 const& userTime) noexcept; static void prepareShadowMapping(Transaction const& transaction, bool highPrecision) noexcept; static ShadowMapDescriptorSet::Transaction open(backend::DriverApi& driver) noexcept; diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index 763e026db3..158259bdcf 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -314,9 +314,10 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG // "read" from one of its resource (only writes), so the FrameGraph culls it. builder.sideEffect(); }, - [this, &engine, &view, vsmShadowOptions, - scene, mainCameraInfo, userTime, passBuilder = passBuilder]( - FrameGraphResources const&, auto const& data, DriverApi& driver) mutable { + [=, passBuilder = passBuilder, + &engine = const_cast(engine), // FIXME: we want this const + &view = const_cast(view)] + (FrameGraphResources const&, auto const& data, DriverApi& driver) mutable { // Note: we could almost parallel_for the loop below, the problem currently is // that updatePrimitivesLod() updates temporary global state. @@ -363,7 +364,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG const CameraInfo cameraInfo{ shadowMap.getCamera(), mainCameraInfo }; auto transaction = ShadowMap::open(driver); - ShadowMap::prepareCamera(transaction, engine, cameraInfo); + ShadowMap::prepareCamera(transaction, driver, cameraInfo); ShadowMap::prepareViewport(transaction, shadowMap.getViewport()); ShadowMap::prepareTime(transaction, engine, userTime); ShadowMap::prepareShadowMapping(transaction, @@ -371,8 +372,8 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG shadowMap.commit(transaction, engine, driver); // updatePrimitivesLod must be run before RenderPass::appendCommands. - view.updatePrimitivesLod(engine, - cameraInfo, scene->getRenderableData(), entry.range); + FView::updatePrimitivesLod(scene->getRenderableData(), + engine, cameraInfo, entry.range); // generate and sort the commands for rendering the shadow map @@ -394,7 +395,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG .visibilityMask(entry.visibilityMask) .geometry(scene->getRenderableData(), entry.range) .commandTypeFlags(RenderPass::CommandTypeFlags::SHADOW) - .build(engine); + .build(engine, driver); entry.executor = pass.getExecutor(); @@ -525,7 +526,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG if (entry.shadowMap->hasVisibleShadows()) { entry.shadowMap->bind(driver); entry.executor.overrideScissor(entry.shadowMap->getScissor()); - entry.executor.execute(engine, "Shadow Pass"); + entry.executor.execute(engine, driver); } driver.endRenderPass(); }); @@ -791,9 +792,10 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engin } } -void ShadowMapManager::cullSpotShadowMap(ShadowMap const& shadowMap, FEngine& engine, FView& view, +void ShadowMapManager::cullSpotShadowMap(ShadowMap const& shadowMap, + FEngine const& engine, FView const& view, FScene::RenderableSoa& renderableData, utils::Range range, - FScene::LightSoa& lightData) noexcept { + FScene::LightSoa const& lightData) noexcept { auto& lcm = engine.getLightManager(); const size_t lightIndex = shadowMap.getLightIndex(); @@ -881,23 +883,23 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap, } } -void ShadowMapManager::cullPointShadowMap(ShadowMap const& shadowMap, FView& view, +void ShadowMapManager::cullPointShadowMap(ShadowMap const& shadowMap, FView const& view, FScene::RenderableSoa& renderableData, utils::Range range, - FScene::LightSoa& lightData) noexcept { + FScene::LightSoa const& lightData) noexcept { - const uint8_t face = shadowMap.getFace(); - const size_t lightIndex = shadowMap.getLightIndex(); + uint8_t const face = shadowMap.getFace(); + size_t const lightIndex = shadowMap.getLightIndex(); // compute the frustum for this light // for spotlights, we cull shadow casters first because we already know the frustum, // this will help us find better near/far plane later - const auto position = lightData.elementAt(lightIndex).xyz; - const auto radius = lightData.elementAt(lightIndex).w; + auto const position = lightData.elementAt(lightIndex).xyz; + auto const radius = lightData.elementAt(lightIndex).w; // compute shadow map frustum for culling - const mat4f Mv = ShadowMap::getPointLightViewMatrix(TextureCubemapFace(face), position); - const mat4f Mp = mat4f::perspective(90.0f, 1.0f, 0.01f, radius); - const Frustum frustum{ math::highPrecisionMultiply(Mp, Mv) }; + mat4f const Mv = ShadowMap::getPointLightViewMatrix(TextureCubemapFace(face), position); + mat4f const Mp = mat4f::perspective(90.0f, 1.0f, 0.01f, radius); + Frustum const frustum{ math::highPrecisionMultiply(Mp, Mv) }; // Cull shadow casters float3 const* worldAABBCenter = renderableData.data(); diff --git a/filament/src/ShadowMapManager.h b/filament/src/ShadowMapManager.h index 0d4b68324d..258c27dd6a 100644 --- a/filament/src/ShadowMapManager.h +++ b/filament/src/ShadowMapManager.h @@ -165,17 +165,17 @@ private: FScene::LightSoa& lightData, ShadowMap::SceneInfo const& sceneInfo) noexcept; static void cullSpotShadowMap(ShadowMap const& map, - FEngine& engine, FView& view, + FEngine const& engine, FView const& view, FScene::RenderableSoa& renderableData, utils::Range range, - FScene::LightSoa& lightData) noexcept; + FScene::LightSoa const& lightData) noexcept; void preparePointShadowMap(ShadowMap& map, FEngine& engine, FView& view, CameraInfo const& mainCameraInfo, FScene::LightSoa& lightData) noexcept; - static void cullPointShadowMap(ShadowMap const& shadowMap, FView& view, + static void cullPointShadowMap(ShadowMap const& shadowMap, FView const& view, FScene::RenderableSoa& renderableData, utils::Range range, - FScene::LightSoa& lightData) noexcept; + FScene::LightSoa const& lightData) noexcept; static void updateSpotVisibilityMasks( uint8_t visibleLayers, @@ -253,8 +253,8 @@ private: return const_cast(this)->getCascadedShadowMap(); } - utils::Slice getSpotShadowMaps() noexcept { - ShadowMap* const p = &getShadowMap(CONFIG_MAX_SHADOW_CASCADES); + utils::Slice getSpotShadowMaps() const noexcept { + ShadowMap const* const p = &getShadowMap(CONFIG_MAX_SHADOW_CASCADES); return { p, mSpotShadowMapCount }; } }; diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index 272bafd4db..712b103038 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -208,10 +208,18 @@ public: return mRenderableManager; } + FRenderableManager const& getRenderableManager() const noexcept { + return mRenderableManager; + } + FLightManager& getLightManager() noexcept { return mLightManager; } + FLightManager const& getLightManager() const noexcept { + return mLightManager; + } + FCameraManager& getCameraManager() noexcept { return mCameraManager; } @@ -423,8 +431,11 @@ public: bool execute(); - utils::JobSystem& getJobSystem() noexcept { - return mJobSystem; + utils::JobSystem& getJobSystem() const noexcept { + // JobSystem is thread-safe, and it's always okay to return a non-const one, + // it's conceptually the same as if we were holding a non-const reference, as opposed + // to by-value class attribute. + return const_cast(mJobSystem); } std::default_random_engine& getRandomEngine() { diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 90f65b6401..6f39b21773 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -346,8 +346,8 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder, FMaterial::~FMaterial() noexcept = default; void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantValue) noexcept { + DriverApi& driverApi = mEngine.getDriverApi(); if (mMaterialDomain == MaterialDomain::SURFACE) { - DriverApi& driverApi = mEngine.getDriverApi(); auto& cachedPrograms = mCachedPrograms; for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) { Variant const variant(k); @@ -377,7 +377,6 @@ void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantV } } } else if (mMaterialDomain == MaterialDomain::POST_PROCESS) { - DriverApi& driverApi = mEngine.getDriverApi(); auto& cachedPrograms = mCachedPrograms; for (size_t k = 0, n = POST_PROCESS_VARIANT_COUNT; k < n; ++k) { if ((k & variantMask) == variantValue) { diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index 02fe8b70f4..296fe60602 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -921,8 +921,8 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { // updatePrimitivesLod must be run before appendCommands and once for each set // of RenderPass::setCamera / RenderPass::setGeometry calls. - view.updatePrimitivesLod(engine, cameraInfo, - scene.getRenderableData(), view.getVisibleRenderables()); + FView::updatePrimitivesLod(scene.getRenderableData(), + engine, cameraInfo, view.getVisibleRenderables()); passBuilder.camera(cameraInfo); passBuilder.geometry(scene.getRenderableData(), view.getVisibleRenderables()); @@ -1025,8 +1025,10 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { passBuilder.variant(pickingVariant); passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::DEPTH); - RenderPass const pass{ passBuilder.build(mEngine) }; - RenderPass::execute(pass, mEngine, resources.getPassName(), out.target, out.params); + RenderPass const pass{ passBuilder.build(mEngine, driver) }; + driver.beginRenderPass(out.target, out.params); + pass.getExecutor().execute(mEngine, driver); + driver.endRenderPass(); }); picking = pickingRenderPass->picking; } @@ -1130,7 +1132,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { if (colorGradingConfigForColor.asSubpass) { // append color grading subpass after all other passes - passBuilder.customCommand(engine, 3, + passBuilder.customCommand(3, RenderPass::Pass::BLENDED, RenderPass::CustomCommand::EPILOG, 0, [&ppm, &driver, colorGradingConfigForColor]() { @@ -1138,7 +1140,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { }); } else if (colorGradingConfig.customResolve) { // append custom resolve subpass after all other passes - passBuilder.customCommand(engine, 3, + passBuilder.customCommand(3, RenderPass::Pass::BLENDED, RenderPass::CustomCommand::EPILOG, 0, [&ppm, &driver]() { @@ -1156,7 +1158,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { passBuilder.renderFlags(renderFlags); } - RenderPass const pass{ passBuilder.build(engine) }; + RenderPass const pass{ passBuilder.build(engine, driver) }; FrameGraphTexture::Descriptor colorBufferDesc = { .width = config.physicalViewport.width, diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index a50b05c8e4..e7414f2a19 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -1065,8 +1065,8 @@ inline void FView::computeLightCameraDistances( } } -void FView::updatePrimitivesLod(FEngine& engine, const CameraInfo&, - FScene::RenderableSoa& renderableData, Range visible) noexcept { +void FView::updatePrimitivesLod(FScene::RenderableSoa& renderableData, + FEngine const& engine, CameraInfo const&, Range visible) noexcept { FRenderableManager const& rcm = engine.getRenderableManager(); for (uint32_t const index : visible) { uint8_t const level = 0; // TODO: pick the proper level of detail diff --git a/filament/src/details/View.h b/filament/src/details/View.h index ff61732a68..e8ffafd0f9 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -203,9 +203,9 @@ public: CameraInfo const& cameraInfo, math::float4 const& userTime, RenderPassBuilder const& passBuilder) noexcept; - void updatePrimitivesLod( - FEngine& engine, const CameraInfo& camera, - FScene::RenderableSoa& renderableData, Range visible) noexcept; + static void updatePrimitivesLod(FScene::RenderableSoa& renderableData, + FEngine const& engine, CameraInfo const& camera, + Range visible) noexcept; void setShadowingEnabled(bool enabled) noexcept { mShadowingEnabled = enabled; } diff --git a/filament/src/ds/ShadowMapDescriptorSet.cpp b/filament/src/ds/ShadowMapDescriptorSet.cpp index ae6aacaaed..da7f1c777c 100644 --- a/filament/src/ds/ShadowMapDescriptorSet.cpp +++ b/filament/src/ds/ShadowMapDescriptorSet.cpp @@ -61,7 +61,7 @@ PerViewUib& ShadowMapDescriptorSet::edit(Transaction const& transaction) noexcep } void ShadowMapDescriptorSet::prepareCamera(Transaction const& transaction, - FEngine& engine, const CameraInfo& camera) noexcept { + backend::DriverApi& driver, const CameraInfo& camera) noexcept { mat4f const& viewFromWorld = camera.view; mat4f const& worldFromView = camera.model; mat4f const& clipFromView = camera.projection; @@ -85,7 +85,7 @@ void ShadowMapDescriptorSet::prepareCamera(Transaction const& transaction, // with a clip-space of [-w, w] ==> z' = -z // with a clip-space of [0, w] ==> z' = (w - z)/2 - s.clipControl = engine.getDriverApi().getClipSpaceParams(); + s.clipControl = driver.getClipSpaceParams(); } void ShadowMapDescriptorSet::prepareLodBias(Transaction const& transaction, float bias) noexcept { @@ -103,7 +103,7 @@ void ShadowMapDescriptorSet::prepareViewport(Transaction const& transaction, } void ShadowMapDescriptorSet::prepareTime(Transaction const& transaction, - FEngine& engine, math::float4 const& userTime) noexcept { + FEngine const& engine, math::float4 const& userTime) noexcept { auto& s = edit(transaction); const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1'000'000'000; const float fraction = float(double(oneSecondRemainder) / 1'000'000'000.0); diff --git a/filament/src/ds/ShadowMapDescriptorSet.h b/filament/src/ds/ShadowMapDescriptorSet.h index c1f55c5c89..e280330be0 100644 --- a/filament/src/ds/ShadowMapDescriptorSet.h +++ b/filament/src/ds/ShadowMapDescriptorSet.h @@ -56,7 +56,7 @@ public: void terminate(backend::DriverApi& driver); static void prepareCamera(Transaction const& transaction, - FEngine& engine, const CameraInfo& camera) noexcept; + backend::DriverApi& driver, const CameraInfo& camera) noexcept; static void prepareLodBias(Transaction const& transaction, float bias) noexcept; @@ -65,7 +65,7 @@ public: backend::Viewport const& viewport) noexcept; static void prepareTime(Transaction const& transaction, - FEngine& engine, math::float4 const& userTime) noexcept; + FEngine const& engine, math::float4 const& userTime) noexcept; static void prepareShadowMapping(Transaction const& transaction, bool highPrecision) noexcept; From c2177ab86a1d6d2f2b649f53f1c7c99b4f253bb8 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 29 Oct 2024 10:21:13 -0700 Subject: [PATCH 2/5] vk: implement descriptor set unbind (#8236) --- filament/backend/src/vulkan/VulkanDriver.cpp | 8 ++++++-- .../src/vulkan/caching/VulkanDescriptorSetManager.cpp | 4 ++++ .../src/vulkan/caching/VulkanDescriptorSetManager.h | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 492f0c24b9..dc1c3f6983 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1767,8 +1767,12 @@ void VulkanDriver::bindDescriptorSet( backend::DescriptorSetHandle dsh, backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) { - VulkanDescriptorSet* set = mResourceAllocator.handle_cast(dsh); - mDescriptorSetManager.bind(setIndex, set, std::move(offsets)); + if (dsh) { + VulkanDescriptorSet* set = mResourceAllocator.handle_cast(dsh); + mDescriptorSetManager.bind(setIndex, set, std::move(offsets)); + } else { + mDescriptorSetManager.unbind(setIndex); + } } void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { diff --git a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp index 28bf06dc2d..0533b58e0f 100644 --- a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp +++ b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp @@ -369,6 +369,10 @@ void VulkanDescriptorSetManager::bind(uint8_t setIndex, VulkanDescriptorSet* set mStashedSets[setIndex] = set; } +void VulkanDescriptorSetManager::unbind(uint8_t setIndex) { + mStashedSets[setIndex] = nullptr; +} + void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, DescriptorSetMask const& setMask) { // setMask indicates the set of descriptor sets the driver wants to bind, curMask is the diff --git a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h index 75e8afda61..2735e5062b 100644 --- a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h +++ b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h @@ -61,6 +61,8 @@ public: void bind(uint8_t setIndex, VulkanDescriptorSet* set, backend::DescriptorSetOffsetArray&& offsets); + void unbind(uint8_t setIndex); + void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, DescriptorSetMask const& setMask); From d1998c1b6704a1ed8d7a13acc9eef9988eaa4243 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Tue, 29 Oct 2024 10:53:24 -0700 Subject: [PATCH 3/5] Metal: unbind descriptor set with null handle (#8237) --- filament/backend/src/metal/MetalDriver.mm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 8528bb77d0..f196f58a9f 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1794,6 +1794,16 @@ void MetalDriver::bindDescriptorSet( backend::DescriptorSetHandle dsh, backend::descriptor_set_t set, backend::DescriptorSetOffsetArray&& offsets) { + + if (UTILS_UNLIKELY(!dsh)) { + DEBUG_LOG("bindDescriptorSet(dsh = null, set = %d, offsets = [])\n", set); + mContext->currentDescriptorSets[set] = nullptr; + mContext->vertexDescriptorBindings.setBuffer(nil, 0, set); + mContext->fragmentDescriptorBindings.setBuffer(nil, 0, set); + mContext->dynamicOffsets.setOffsets(set, nullptr, 0); + return; + } + auto descriptorSet = handle_cast(dsh); const size_t dynamicBindings = descriptorSet->layout->getDynamicOffsetCount(); utils::FixedCapacityVector offsetsVector(dynamicBindings, 0); From ecedbad5ba65e9a3cb2888ffad6afe4da9903e28 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 28 Oct 2024 16:33:57 -0700 Subject: [PATCH 4/5] allow bindDescriptorSet() to take a null handle --- filament/backend/src/opengl/OpenGLDriver.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index a606d09087..2154a26964 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -3875,6 +3875,14 @@ void OpenGLDriver::bindDescriptorSet( backend::DescriptorSetHandle dsh, backend::descriptor_set_t set, backend::DescriptorSetOffsetArray&& offsets) { + + if (UTILS_UNLIKELY(!dsh)) { + mBoundDescriptorSets[set].dsh = dsh; + mInvalidDescriptorSetBindings.set(set, true); + mInvalidDescriptorSetBindingOffsets.set(set, true); + return; + } + // handle_cast<> here also serves to validate the handle (it actually cannot return nullptr) GLDescriptorSet const* const ds = handle_cast(dsh); if (ds) { From eb1b30c7b1b3ef39c8582c0f8b48d08ba64ddced Mon Sep 17 00:00:00 2001 From: Serge Metral Date: Tue, 29 Oct 2024 21:32:18 +0000 Subject: [PATCH 5/5] vk: protected content platform (#8226) Adding the basic mechanisms for creating and managing the protected state. We also create the queue and get it. Finally we allocate protected memory. Also removed some of the shared context logic --- .../backend/platforms/VulkanPlatform.h | 25 ++++ filament/backend/src/vulkan/VulkanContext.h | 26 +++- filament/backend/src/vulkan/VulkanDriver.cpp | 3 +- filament/backend/src/vulkan/VulkanHandles.cpp | 3 + filament/backend/src/vulkan/VulkanHandles.h | 2 + .../backend/src/vulkan/VulkanSwapChain.cpp | 10 +- filament/backend/src/vulkan/VulkanSwapChain.h | 3 + filament/backend/src/vulkan/VulkanTexture.cpp | 16 ++- filament/backend/src/vulkan/VulkanTexture.h | 9 +- .../src/vulkan/platform/VulkanPlatform.cpp | 127 +++++++++++++++--- .../platform/VulkanPlatformSwapChainImpl.cpp | 13 +- .../platform/VulkanPlatformSwapChainImpl.h | 9 ++ 12 files changed, 206 insertions(+), 40 deletions(-) diff --git a/filament/backend/include/backend/platforms/VulkanPlatform.h b/filament/backend/include/backend/platforms/VulkanPlatform.h index 2bbe1983f2..91189310d5 100644 --- a/filament/backend/include/backend/platforms/VulkanPlatform.h +++ b/filament/backend/include/backend/platforms/VulkanPlatform.h @@ -88,6 +88,7 @@ public: VkFormat colorFormat = VK_FORMAT_UNDEFINED; VkFormat depthFormat = VK_FORMAT_UNDEFINED; VkExtent2D extent = {0, 0}; + bool isProtected = false; }; struct ImageSyncData { @@ -199,6 +200,13 @@ public: */ virtual bool hasResized(SwapChainPtr handle); + /** + * Check if the surface is protected. + * @param handle The handle returned by createSwapChain() + * @return Whether the swapchain is protected + */ + virtual bool isProtected(SwapChainPtr handle); + /** * Carry out a recreation of the swapchain. * @param handle The handle returned by createSwapChain() @@ -267,6 +275,23 @@ public: */ VkQueue getGraphicsQueue() const noexcept; + /** + * @return The family index of the protected graphics queue selected for the + * Vulkan backend. + */ + uint32_t getProtectedGraphicsQueueFamilyIndex() const noexcept; + + /** + * @return The index of the protected graphics queue (if there are multiple + * graphics queues) selected for the Vulkan backend. + */ + uint32_t getProtectedGraphicsQueueIndex() const noexcept; + + /** + * @return The protected queue that was selected for the Vulkan backend. + */ + VkQueue getProtectedGraphicsQueue() const noexcept; + private: static ExtensionSet getSwapchainInstanceExtensions(); diff --git a/filament/backend/src/vulkan/VulkanContext.h b/filament/backend/src/vulkan/VulkanContext.h index e7a1814196..68c44dfdce 100644 --- a/filament/backend/src/vulkan/VulkanContext.h +++ b/filament/backend/src/vulkan/VulkanContext.h @@ -93,6 +93,9 @@ struct VulkanRenderPass { struct VulkanContext { public: inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const { + if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) { + assert_invariant(isProtectedMemorySupported() == true); + } for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) { if (flags & 1) { if ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) { @@ -113,19 +116,19 @@ public: } inline VkPhysicalDeviceLimits const& getPhysicalDeviceLimits() const noexcept { - return mPhysicalDeviceProperties.limits; + return mPhysicalDeviceProperties.properties.limits; } inline uint32_t getPhysicalDeviceVendorId() const noexcept { - return mPhysicalDeviceProperties.vendorID; + return mPhysicalDeviceProperties.properties.vendorID; } inline bool isImageCubeArraySupported() const noexcept { - return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE; + return mPhysicalDeviceFeatures.features.imageCubeArray == VK_TRUE; } inline bool isDepthClampSupported() const noexcept { - return mPhysicalDeviceFeatures.depthClamp == VK_TRUE; + return mPhysicalDeviceFeatures.features.depthClamp == VK_TRUE; } inline bool isDebugMarkersSupported() const noexcept { @@ -141,21 +144,30 @@ public: } inline bool isClipDistanceSupported() const noexcept { - return mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE; + return mPhysicalDeviceFeatures.features.shaderClipDistance == VK_TRUE; } inline bool isLazilyAllocatedMemorySupported() const noexcept { return mLazilyAllocatedMemorySupported; } + inline bool isProtectedMemorySupported() const noexcept { + return mProtectedMemorySupported; + } + private: VkPhysicalDeviceMemoryProperties mMemoryProperties = {}; - VkPhysicalDeviceProperties mPhysicalDeviceProperties = {}; - VkPhysicalDeviceFeatures mPhysicalDeviceFeatures = {}; + VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 + }; + VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 + }; bool mDebugMarkersSupported = false; bool mDebugUtilsSupported = false; bool mMultiviewEnabled = false; bool mLazilyAllocatedMemorySupported = false; + bool mProtectedMemorySupported = false; VkFormatList mDepthStencilFormats; VkFormatList mBlittableDepthStencilFormats; diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index dc1c3f6983..567a34bc53 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -977,8 +977,7 @@ bool VulkanDriver::isSRGBSwapChainSupported() { } bool VulkanDriver::isProtectedContentSupported() { - // the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag is not supported - return false; + return mContext.isProtectedMemorySupported(); } bool VulkanDriver::isStereoSupported() { diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 954dd77f5e..43c05c6904 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -256,6 +256,7 @@ VulkanRenderTarget::VulkanRenderTarget() : HwRenderTarget(0, 0), VulkanResource(VulkanResourceType::RENDER_TARGET), mOffscreen(false), + mProtected(false), mResources(nullptr), mInfo(std::make_unique()) { mInfo->rpkey.samples = mInfo->fbkey.samples = 1; @@ -267,6 +268,7 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) { VkExtent2D const extent = swapChain.getExtent(); width = extent.width; height = extent.height; + mProtected = swapChain.isProtected(); VulkanAttachment color = {.texture = swapChain.getCurrentColor()}; mInfo->attachments = {color}; @@ -303,6 +305,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica : HwRenderTarget(width, height), VulkanResource(VulkanResourceType::RENDER_TARGET), mOffscreen(true), + mProtected(false), mResources(handleAllocator), mInfo(std::make_unique()) { diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index b74b90b89c..60d3702a72 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -306,6 +306,7 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource { inline bool hasDepth() const { return mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX; } inline bool isSwapChain() const { return !mOffscreen; } + inline bool isProtected() const { return mProtected; } void bindToSwapChain(VulkanSwapChain& surf); @@ -328,6 +329,7 @@ private: int8_t msaaIndex = UNDEFINED_INDEX; }; bool const mOffscreen; + bool mProtected; VulkanAcquireOnlyResourceManager mResources; std::unique_ptr mInfo; diff --git a/filament/backend/src/vulkan/VulkanSwapChain.cpp b/filament/backend/src/vulkan/VulkanSwapChain.cpp index 4989b925a3..bf9fd353cd 100644 --- a/filament/backend/src/vulkan/VulkanSwapChain.cpp +++ b/filament/backend/src/vulkan/VulkanSwapChain.cpp @@ -63,14 +63,20 @@ void VulkanSwapChain::update() { mColors.reserve(bundle.colors.size()); VkDevice const device = mPlatform->getDevice(); + TextureUsage depthUsage = TextureUsage::DEPTH_ATTACHMENT; + TextureUsage colorUsage = TextureUsage::COLOR_ATTACHMENT; + if (bundle.isProtected) { + depthUsage |= TextureUsage::PROTECTED; + colorUsage |= TextureUsage::PROTECTED; + } for (auto const color: bundle.colors) { mColors.push_back(std::make_unique(device, mAllocator, mCommands, mHandleAllocator, color, bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height, - TextureUsage::COLOR_ATTACHMENT, mStagePool, true /* heap allocated */)); + colorUsage, mStagePool, true /* heap allocated */)); } mDepth = std::make_unique(device, mAllocator, mCommands, mHandleAllocator, bundle.depth, bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height, - TextureUsage::DEPTH_ATTACHMENT, mStagePool, true /* heap allocated */); + depthUsage, mStagePool, true /* heap allocated */); mExtent = bundle.extent; } diff --git a/filament/backend/src/vulkan/VulkanSwapChain.h b/filament/backend/src/vulkan/VulkanSwapChain.h index b3e49c044e..acb88c56b9 100644 --- a/filament/backend/src/vulkan/VulkanSwapChain.h +++ b/filament/backend/src/vulkan/VulkanSwapChain.h @@ -74,6 +74,9 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource { return mExtent; } + inline bool isProtected() noexcept { + return mPlatform->isProtected(swapChain); + } private: static constexpr int IMAGE_READY_SEMAPHORE_COUNT = FVK_MAX_COMMAND_BUFFERS; diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index 2358d6ea0d..a526310118 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -147,12 +147,14 @@ inline VulkanLayout getDefaultLayoutImpl(VkImageUsageFlags vkusage) { VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format, - VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout) + VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout, + bool isProtected) : VulkanResource(VulkanResourceType::HEAP_ALLOCATED), mVkFormat(format), mViewType(viewType), mFullViewRange{filament::backend::getImageAspect(format), 0, levels, 0, layerCount}, mDefaultLayout(defaultLayout), + mIsProtected(isProtected), mStagePool(stagePool), mDevice(device), mAllocator(allocator), @@ -183,7 +185,8 @@ VulkanTexture::VulkanTexture( mState(handleAllocator->initHandle( device, allocator, commands, stagePool, format, imgutil::getViewType(SamplerType::SAMPLER_2D), 1, 1, - getDefaultLayoutImpl(tusage))) { + getDefaultLayoutImpl(tusage), + any(usage& TextureUsage::PROTECTED))) { auto* const state = getSharedState(); state->mTextureImage = image; mPrimaryViewRange = state->mFullViewRange; @@ -201,7 +204,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, mAllocator(handleAllocator), mState(handleAllocator->initHandle(device, allocator, commands, stagePool, backend::getVkFormat(tformat), imgutil::getViewType(target), levels, - getLayerCount(target, depth), VulkanLayout::UNDEFINED)) { + getLayerCount(target, depth), VulkanLayout::UNDEFINED, any(usage& TextureUsage::PROTECTED))) { auto* const state = getSharedState(); // Create an appropriately-sized device-only VkImage, but do not fill it yet. @@ -232,6 +235,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, imageInfo.arrayLayers = depth * 6; imageInfo.extent.depth = 1; } + if (any(usage & TextureUsage::PROTECTED)) { + imageInfo.flags |= VK_IMAGE_CREATE_PROTECTED_BIT; + } if (any(usage & TextureUsage::BLIT_SRC)) { imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; @@ -331,7 +337,8 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, const VkFlags requiredMemoryFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | - (useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U); + (useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U) | + (state->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U); uint32_t memoryTypeIndex = context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags); @@ -416,6 +423,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP || target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1)); auto* const state = getSharedState(); + assert_invariant(state->mIsProtected == false); const PixelBufferDescriptor* hostData = &data; PixelBufferDescriptor reshapedData; diff --git a/filament/backend/src/vulkan/VulkanTexture.h b/filament/backend/src/vulkan/VulkanTexture.h index acc5884670..5165b1cec7 100644 --- a/filament/backend/src/vulkan/VulkanTexture.h +++ b/filament/backend/src/vulkan/VulkanTexture.h @@ -35,7 +35,7 @@ class VulkanResourceAllocator; struct VulkanTextureState : public VulkanResource { VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels, - uint8_t layerCount, VulkanLayout defaultLayout); + uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected); struct ImageViewKey { VkImageSubresourceRange range; // 4 * 5 bytes @@ -70,6 +70,8 @@ struct VulkanTextureState : public VulkanResource { VkImage mTextureImage = VK_NULL_HANDLE; VulkanLayout mDefaultLayout; + bool mIsProtected = false; + // Track the image layout of each subresource using a sparse range map. utils::RangeMap mSubresourceLayouts; @@ -179,6 +181,11 @@ struct VulkanTexture : public HwTexture, VulkanResource { return state->mIsTransientAttachment; } + bool getIsProtected() const { + VulkanTextureState const* state = getSharedState(); + return state->mIsProtected; + } + bool transitionLayout(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range, VulkanLayout newLayout); diff --git a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp index 6ddfb94bd3..c894e18bff 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp +++ b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp @@ -300,10 +300,10 @@ VkInstance createInstance(ExtensionSet const& requiredExts) { } VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, - VkPhysicalDeviceFeatures const& features, uint32_t graphicsQueueFamilyIndex, - ExtensionSet const& deviceExtensions) { + VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex, + uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions) { VkDevice device; - VkDeviceQueueCreateInfo deviceQueueCreateInfo[1] = {}; + VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = {}; const float queuePriority[] = {1.0f}; VkDeviceCreateInfo deviceCreateInfo = {}; FixedCapacityVector requestExtensions; @@ -314,22 +314,30 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, for (auto const& ext: deviceExtensions) { requestExtensions.push_back(ext.data()); } - deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - deviceQueueCreateInfo->queueFamilyIndex = graphicsQueueFamilyIndex; - deviceQueueCreateInfo->queueCount = 1; - deviceQueueCreateInfo->pQueuePriorities = &queuePriority[0]; + deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCreateInfo[0].queueFamilyIndex = graphicsQueueFamilyIndex; + deviceQueueCreateInfo[0].queueCount = 1; + deviceQueueCreateInfo[0].pQueuePriorities = &queuePriority[0]; + // Protected queue + deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCreateInfo[1].flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT; + deviceQueueCreateInfo[1].queueFamilyIndex = protectedGraphicsQueueFamilyIndex; + deviceQueueCreateInfo[1].queueCount = 1; + deviceQueueCreateInfo[1].pQueuePriorities = &queuePriority[0]; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceCreateInfo.queueCreateInfoCount = 1; + bool const hasProtectedQueue = protectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX; + deviceCreateInfo.queueCreateInfoCount = hasProtectedQueue ? 2 : 1; deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo; // We could simply enable all supported features, but since that may have performance // consequences let's just enable the features we need. VkPhysicalDeviceFeatures enabledFeatures{ - .depthClamp = features.depthClamp, - .samplerAnisotropy = features.samplerAnisotropy, - .textureCompressionETC2 = features.textureCompressionETC2, - .textureCompressionBC = features.textureCompressionBC, - .shaderClipDistance = features.shaderClipDistance, + .depthClamp = features.features.depthClamp, + .samplerAnisotropy = features.features.samplerAnisotropy, + .textureCompressionETC2 = features.features.textureCompressionETC2, + .textureCompressionBC = features.features.textureCompressionBC, + .shaderClipDistance = features.features.shaderClipDistance, }; deviceCreateInfo.pEnabledFeatures = &enabledFeatures; @@ -360,6 +368,16 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, pNext = &multiview; } + VkPhysicalDeviceProtectedMemoryFeatures protectedMemory = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES, + }; + if (hasProtectedQueue) { + // Enable protected memory, if requested. + protectedMemory.protectedMemory = VK_TRUE; + protectedMemory.pNext = pNext; + pNext = &protectedMemory; + } + deviceCreateInfo.pNext = pNext; VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device); @@ -406,13 +424,13 @@ FixedCapacityVector getPhysicalDeviceQueueFamilyPropert return queueFamiliesProperties; } -uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice) { +uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice, VkQueueFlags flags) { const FixedCapacityVector queueFamiliesProperties = getPhysicalDeviceQueueFamilyPropertiesHelper(physicalDevice); uint32_t graphicsQueueFamilyIndex = INVALID_VK_INDEX; for (uint32_t j = 0; j < queueFamiliesProperties.size(); ++j) { VkQueueFamilyProperties props = queueFamiliesProperties[j]; - if (props.queueCount != 0 && props.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + if (props.queueCount != 0 && props.queueFlags & flags) { graphicsQueueFamilyIndex = j; break; } @@ -473,7 +491,8 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance, // Does the device have any command queues that support graphics? // In theory, we should also ensure that the device supports presentation of our // particular VkSurface, but we don't have a VkSurface yet, so we'll skip this requirement. - if (identifyGraphicsQueueFamilyIndex(candidateDevice) == INVALID_VK_INDEX) { + if (identifyGraphicsQueueFamilyIndex(candidateDevice, VK_QUEUE_GRAPHICS_BIT) + == INVALID_VK_INDEX) { continue; } @@ -584,6 +603,9 @@ struct VulkanPlatformPrivate { uint32_t mGraphicsQueueFamilyIndex = INVALID_VK_INDEX; uint32_t mGraphicsQueueIndex = INVALID_VK_INDEX; VkQueue mGraphicsQueue = VK_NULL_HANDLE; + uint32_t mProtectedGraphicsQueueFamilyIndex = INVALID_VK_INDEX; + uint32_t mProtectedGraphicsQueueIndex = INVALID_VK_INDEX; + VkQueue mProtectedGraphicsQueue = VK_NULL_HANDLE; VulkanContext mContext = {}; // We use a map to both map a handle (i.e. SwapChainPtr) to the concrete type and also to @@ -686,22 +708,45 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, printDeviceInfo(mImpl->mInstance, mImpl->mPhysicalDevice); + VkPhysicalDeviceProtectedMemoryFeatures queryProtectedMemoryFeatures = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES, + }; + // Chain to the pNext linked list + queryProtectedMemoryFeatures.pNext = context.mPhysicalDeviceFeatures.pNext; + context.mPhysicalDeviceFeatures.pNext = &queryProtectedMemoryFeatures; + VkPhysicalDeviceProtectedMemoryProperties protectedMemoryProperties = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES, + }; + protectedMemoryProperties.pNext = context.mPhysicalDeviceProperties.pNext; + context.mPhysicalDeviceProperties.pNext = &protectedMemoryProperties; + // Initialize the following fields: physicalDeviceProperties, memoryProperties, // physicalDeviceFeatures, graphicsQueueFamilyIndex. - vkGetPhysicalDeviceProperties(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties); - vkGetPhysicalDeviceFeatures(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures); + vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties); + vkGetPhysicalDeviceFeatures2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures); vkGetPhysicalDeviceMemoryProperties(mImpl->mPhysicalDevice, &context.mMemoryProperties); mImpl->mGraphicsQueueFamilyIndex = mImpl->mGraphicsQueueFamilyIndex == INVALID_VK_INDEX - ? identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice) + ? identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice, + VK_QUEUE_GRAPHICS_BIT) : mImpl->mGraphicsQueueFamilyIndex; assert_invariant(mImpl->mGraphicsQueueFamilyIndex != INVALID_VK_INDEX); + // We know we need to allocate the protected version of the VK objects + context.mProtectedMemorySupported = + static_cast(queryProtectedMemoryFeatures.protectedMemory); + if (context.mProtectedMemorySupported) { + mImpl->mProtectedGraphicsQueueFamilyIndex + = identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice, + (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT)); + assert_invariant(mImpl->mProtectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX); + } + // Only enable shaderClipDistance if we are doing instanced stereoscopic rendering. - if (context.mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE + if (context.mPhysicalDeviceFeatures.features.shaderClipDistance == VK_TRUE && driverConfig.stereoscopicType != StereoscopicType::INSTANCED) { - context.mPhysicalDeviceFeatures.shaderClipDistance = VK_FALSE; + context.mPhysicalDeviceFeatures.features.shaderClipDistance = VK_FALSE; } // At this point, we should have a family index that points to a family that has > 0 queues for @@ -711,6 +756,11 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, mImpl->mGraphicsQueueIndex = mImpl->mGraphicsQueueIndex == INVALID_VK_INDEX ? 0 : mImpl->mGraphicsQueueIndex; + // Applying the same logic to the protected queue index (Not sure about shared context and protection) + mImpl->mProtectedGraphicsQueueIndex + = mImpl->mProtectedGraphicsQueueIndex == INVALID_VK_INDEX ? 0 : + mImpl->mProtectedGraphicsQueueIndex; + ExtensionSet deviceExts; // If using a shared context, we do not assume any extensions. if (!mImpl->mSharedContext) { @@ -727,7 +777,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, mImpl->mDevice = mImpl->mDevice == VK_NULL_HANDLE ? createLogicalDevice(mImpl->mPhysicalDevice, - context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex, deviceExts) + context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex, + mImpl->mProtectedGraphicsQueueFamilyIndex, deviceExts) : mImpl->mDevice; assert_invariant(mImpl->mDevice != VK_NULL_HANDLE); @@ -735,6 +786,15 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, &mImpl->mGraphicsQueue); assert_invariant(mImpl->mGraphicsQueue != VK_NULL_HANDLE); + if (context.mProtectedMemorySupported) { + VkDeviceQueueInfo2 info = {}; + info.queueFamilyIndex = mImpl->mProtectedGraphicsQueueFamilyIndex; + info.queueIndex = mImpl->mProtectedGraphicsQueueIndex; + info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2; + info.flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT; + vkGetDeviceQueue2(mImpl->mDevice, &info, &mImpl->mProtectedGraphicsQueue); + } + // Store the extension support in the context context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME); context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME); @@ -800,6 +860,10 @@ bool VulkanPlatform::hasResized(SwapChainPtr handle) { SWAPCHAIN_RET_FUNC(hasResized, handle, ) } +bool VulkanPlatform::isProtected(SwapChainPtr handle) { + SWAPCHAIN_RET_FUNC(isProtected, handle, ) +} + VkResult VulkanPlatform::recreate(SwapChainPtr handle) { SWAPCHAIN_RET_FUNC(recreate, handle, ) } @@ -829,6 +893,13 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags, flags |= SWAP_CHAIN_CONFIG_ENABLE_XCB; } + if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) { + if (!mImpl->mContext.mProtectedMemorySupported) { + utils::slog.w << "protected swapchain requested, but VulkanPlatform does not support it" + << utils::io::endl; + } + } + auto [surface, fallbackExtent] = createVkSurfaceKHR(nativeWindow, mImpl->mInstance, flags); // The VulkanPlatformSurfaceSwapChain now `owns` the surface. VulkanPlatformSurfaceSwapChain* swapchain = new VulkanPlatformSurfaceSwapChain(mImpl->mContext, @@ -862,6 +933,18 @@ VkQueue VulkanPlatform::getGraphicsQueue() const noexcept { return mImpl->mGraphicsQueue; } +uint32_t VulkanPlatform::getProtectedGraphicsQueueFamilyIndex() const noexcept { + return mImpl->mProtectedGraphicsQueueFamilyIndex; +} + +uint32_t VulkanPlatform::getProtectedGraphicsQueueIndex() const noexcept { + return mImpl->mProtectedGraphicsQueueIndex; +} + +VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept { + return mImpl->mProtectedGraphicsQueue; +} + #undef SWAPCHAIN_RET_FUNC }// namespace filament::backend diff --git a/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp b/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp index d8cace97a6..3037f64af7 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp +++ b/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp @@ -125,7 +125,8 @@ VulkanPlatformSurfaceSwapChain::VulkanPlatformSurfaceSwapChain(VulkanContext con mSurface(surface), mFallbackExtent(fallbackExtent), mUsesRGB((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0), - mHasStencil((flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0) { + mHasStencil((flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0), + mIsProtected((flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) != 0) { assert_invariant(surface); create(); } @@ -212,6 +213,8 @@ VkResult VulkanPlatformSurfaceSwapChain::create() { VkSwapchainCreateInfoKHR const createInfo{ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .flags = mIsProtected ? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR : + VkSwapchainCreateFlagsKHR(0), .surface = mSurface, .minImageCount = desiredImageCount, .imageFormat = surfaceFormat.format, @@ -246,13 +249,15 @@ VkResult VulkanPlatformSurfaceSwapChain::create() { mSwapChainBundle.depthFormat = selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil); mSwapChainBundle.depth = createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat); + mSwapChainBundle.isProtected = mIsProtected; FVK_LOGI << "vkCreateSwapchain" << ": " << mSwapChainBundle.extent.width << "x" << mSwapChainBundle.extent.height << ", " << surfaceFormat.format << ", " << surfaceFormat.colorSpace << ", " << "swapchain-size=" << mSwapChainBundle.colors.size() << ", " << "identity-transform=" << (caps.currentTransform == 1) << ", " - << "depth=" << mSwapChainBundle.depthFormat + << "depth=" << mSwapChainBundle.depthFormat << ", " + << "protected=" << mSwapChainBundle.isProtected << io::endl; VkSemaphoreCreateInfo const semaphoreCreateInfo = { @@ -317,6 +322,10 @@ bool VulkanPlatformSurfaceSwapChain::hasResized() { return !equivalent(mSwapChainBundle.extent, perceivedExtent); } +bool VulkanPlatformSurfaceSwapChain::isProtected() { + return mIsProtected; +} + // Non-virtual override VkResult VulkanPlatformSurfaceSwapChain::recreate() { destroy(); diff --git a/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.h b/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.h index bcad1ccb0e..2a715a210c 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.h +++ b/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.h @@ -64,6 +64,11 @@ struct VulkanPlatformSwapChainImpl : public Platform::SwapChain { return false; } + // Non-virtual override-able method + bool isProtected() { + return false; + } + protected: // Non-virtual override-able method void destroy(); @@ -97,6 +102,9 @@ struct VulkanPlatformSurfaceSwapChain : public VulkanPlatformSwapChainImpl { // Non-virtual override-able method bool hasResized(); + // Non-virtual override-able method + bool isProtected(); + protected: // Non-virtual override-able method void destroy(); @@ -117,6 +125,7 @@ private: bool mUsesRGB = false; bool mHasStencil = false; + bool mIsProtected = false; bool mSuboptimal; };