From 3e02d5016e7587e8095935c1f43bc3a1c40e3f06 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 6 Nov 2024 14:04:57 -0800 Subject: [PATCH 01/20] vk: fix uninitialized param (#8253) --- .../backend/src/vulkan/caching/VulkanDescriptorSetManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h index 2735e5062b1..1bbf6e29b04 100644 --- a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h +++ b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h @@ -94,7 +94,7 @@ class VulkanDescriptorSetManager { struct { VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; DescriptorSetMask setMask; - DescriptorSetArray boundSets; + DescriptorSetArray boundSets = {}; } mLastBoundInfo; }; From b9768394a1b9a6d6be988072e1287ba393c680e1 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Thu, 7 Nov 2024 11:23:30 -0800 Subject: [PATCH 02/20] Metal: remove cached pipeline states with deleted programs (#8243) --- filament/backend/src/metal/MetalDriver.mm | 15 +++++++++++++-- filament/backend/src/metal/MetalHandles.h | 1 + filament/backend/src/metal/MetalHandles.mm | 4 ++++ .../backend/src/metal/MetalShaderCompiler.h | 4 ++++ filament/backend/src/metal/MetalState.h | 18 +++++++++++++++++- 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 29a0f6fed90..8e49c90dd7d 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -797,9 +797,20 @@ } void MetalDriver::destroyProgram(Handle ph) { - if (ph) { - destruct_handle(ph); + if (UTILS_UNLIKELY(!ph)) { + return; + } + // Remove any cached pipeline states that refer to these programs. + auto* metalProgram = handle_cast(ph); + const auto& functions = metalProgram->getFunctionsIfPresent(); + if (UTILS_LIKELY(functions.isRaster())) { // only raster pipelines are cached + const auto& raster = functions.getRasterFunctions(); + mContext->pipelineStateCache.removeIf([&](const MetalPipelineState& state) { + const auto& [fragment, vertex] = raster; + return state.fragmentFunction == fragment || state.vertexFunction == vertex; + }); } + destruct_handle(ph); } void MetalDriver::destroyTexture(Handle th) { diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 0c02b213edb..3758de5842a 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -202,6 +202,7 @@ class MetalProgram : public HwProgram { MetalProgram(MetalContext& context, Program&& program) noexcept; const MetalShaderCompiler::MetalFunctionBundle& getFunctions(); + const MetalShaderCompiler::MetalFunctionBundle& getFunctionsIfPresent() const; private: void initialize(); diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 443d4435a91..60cf484c86e 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -495,6 +495,10 @@ static void func(void* user) { return mFunctionBundle; } +const MetalShaderCompiler::MetalFunctionBundle& MetalProgram::getFunctionsIfPresent() const { + return mFunctionBundle; +} + void MetalProgram::initialize() { if (!mToken) { return; diff --git a/filament/backend/src/metal/MetalShaderCompiler.h b/filament/backend/src/metal/MetalShaderCompiler.h index f0b827ad6ac..dfb29f47b08 100644 --- a/filament/backend/src/metal/MetalShaderCompiler.h +++ b/filament/backend/src/metal/MetalShaderCompiler.h @@ -83,6 +83,10 @@ class MetalShaderCompiler { return std::get(mPrograms); } + bool isRaster() const { return std::holds_alternative(mPrograms); } + + bool isCompute() const { return std::holds_alternative(mPrograms); } + static MetalFunctionBundle none() { return MetalFunctionBundle(None{}); } diff --git a/filament/backend/src/metal/MetalState.h b/filament/backend/src/metal/MetalState.h index 92d5a3319c2..a9e9e76fff4 100644 --- a/filament/backend/src/metal/MetalState.h +++ b/filament/backend/src/metal/MetalState.h @@ -28,7 +28,9 @@ #include #include + #include +#include namespace filament { namespace backend { @@ -160,6 +162,8 @@ template> class StateCache { + using MapType = tsl::robin_map; + public: StateCache() = default; @@ -169,6 +173,18 @@ class StateCache { void setDevice(id device) noexcept { mDevice = device; } + void removeIf(utils::Invocable fn) noexcept { + typename MapType::const_iterator it = mStateCache.begin(); + while (it != mStateCache.end()) { + const auto& [key, _] = *it; + if (UTILS_UNLIKELY(fn(key))) { + it = mStateCache.erase(it); + } else { + ++it; + } + } + } + MetalType getOrCreateState(const StateType& state) noexcept { assert_invariant(mDevice); @@ -196,7 +212,7 @@ class StateCache { StateCreator creator; id mDevice = nil; - tsl::robin_map mStateCache; + MapType mStateCache; }; From 2b7f325cc70a66107947366a28bede491a513a56 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 7 Nov 2024 13:55:20 -0800 Subject: [PATCH 03/20] renderdiff: Use single lib for osmesa (#8255) Previously, we linked against libOSMesa through the linker and then used dlopen to link against libGL. This is not how libOSMesa is intended to be used. Instead, we use dlopen on libOSMesa (via bluegl), which then will map the correct GL methods for us, and for the OSMesa functions, we also map those functions from libOSMesa (instead of relying on compile-time linker). --- filament/backend/CMakeLists.txt | 4 -- .../backend/platforms/PlatformOSMesa.h | 3 +- .../src/opengl/platforms/PlatformOSMesa.cpp | 61 +++++++++++++++++-- libs/bluegl/CMakeLists.txt | 2 + libs/bluegl/src/BlueGLLinuxOSMesa.cpp | 59 ++++++++++++++++++ 5 files changed, 118 insertions(+), 11 deletions(-) create mode 100644 libs/bluegl/src/BlueGLLinuxOSMesa.cpp diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index 0a07c82836d..9a70983d794 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -378,10 +378,6 @@ set(LINUX_LINKER_OPTIMIZATION_FLAGS if (LINUX AND FILAMENT_SUPPORTS_OSMESA) set(OSMESA_COMPILE_FLAGS -I${FILAMENT_OSMESA_PATH}/include/GL) - set(OSMESA_LINKER_FLAGS - -Wl,-L${FILAMENT_OSMESA_PATH}/lib/x86_64-linux-gnu/ - -lOSMesa - ) endif() if (MSVC) diff --git a/filament/backend/include/backend/platforms/PlatformOSMesa.h b/filament/backend/include/backend/platforms/PlatformOSMesa.h index bcb82195a4c..a1a72fc6f77 100644 --- a/filament/backend/include/backend/platforms/PlatformOSMesa.h +++ b/filament/backend/include/backend/platforms/PlatformOSMesa.h @@ -21,7 +21,7 @@ #include "bluegl/BlueGL.h" -#include "osmesa.h" +#include #include #include @@ -56,6 +56,7 @@ class PlatformOSMesa : public OpenGLPlatform { private: OSMesaContext mContext; + void* mOsMesaApi = nullptr; }; } // namespace filament::backend diff --git a/filament/backend/src/opengl/platforms/PlatformOSMesa.cpp b/filament/backend/src/opengl/platforms/PlatformOSMesa.cpp index 9a34577dc1c..fdeccfe110f 100644 --- a/filament/backend/src/opengl/platforms/PlatformOSMesa.cpp +++ b/filament/backend/src/opengl/platforms/PlatformOSMesa.cpp @@ -19,6 +19,7 @@ #include #include +#include #include namespace filament::backend { @@ -41,13 +42,56 @@ struct OSMesaSwapchain { std::unique_ptr buffer; }; -} // anonymous namespace +struct OSMesaAPI { +private: + using CreateContextFunc = OSMesaContext (*)(GLenum format, OSMesaContext); + using DestroyContextFunc = GLboolean (*)(OSMesaContext); + using MakeCurrentFunc = GLboolean (*)(OSMesaContext ctx, void* buffer, GLenum type, + GLsizei width, GLsizei height); + using GetProcAddressFunc = OSMESAproc (*)(const char* funcName); + +public: + CreateContextFunc OSMesaCreateContext; + DestroyContextFunc OSMesaDestroyContext; + MakeCurrentFunc OSMesaMakeCurrent; + GetProcAddressFunc OSMesaGetProcAddress; + + OSMesaAPI() { + constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"}; + for (char const* libName: libraryNames) { + mLib = dlopen(libName, RTLD_GLOBAL | RTLD_NOW); + if (mLib) { + break; + } + } + FILAMENT_CHECK_PRECONDITION(mLib) + << "Unable to dlopen libOSMesa to create a software GL context"; + + OSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress"); + + OSMesaCreateContext = (CreateContextFunc) OSMesaGetProcAddress("OSMesaCreateContext"); + OSMesaDestroyContext = + (DestroyContextFunc) OSMesaGetProcAddress("OSMesaDestroyContext"); + OSMesaMakeCurrent = (MakeCurrentFunc) OSMesaGetProcAddress("OSMesaMakeCurrent"); + } + + ~OSMesaAPI() { + dlclose(mLib); + } +private: + void* mLib = nullptr; +}; + +}// anonymous namespace Driver* PlatformOSMesa::createDriver(void* const sharedGLContext, const DriverConfig& driverConfig) noexcept { + OSMesaAPI* api = new OSMesaAPI(); + mOsMesaApi = api; + FILAMENT_CHECK_PRECONDITION(sharedGLContext == nullptr) << "shared GL context is not supported with PlatformOSMesa"; - mContext = OSMesaCreateContext(GL_RGBA, NULL); + mContext = api->OSMesaCreateContext(GL_RGBA, NULL); // We need to do a no-op makecurrent here so that the context will be in a correct state before // any GL calls. @@ -62,7 +106,11 @@ Driver* PlatformOSMesa::createDriver(void* const sharedGLContext, } void PlatformOSMesa::terminate() noexcept { - OSMesaDestroyContext(mContext); + OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi; + api->OSMesaDestroyContext(mContext); + delete api; + mOsMesaApi = nullptr; + bluegl::unbind(); } @@ -84,11 +132,12 @@ void PlatformOSMesa::destroySwapChain(Platform::SwapChain* swapChain) noexcept { bool PlatformOSMesa::makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept { + OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi; OSMesaSwapchain* impl = (OSMesaSwapchain*) drawSwapChain; - auto result = OSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(), BACKING_GL_TYPE, - impl->width, impl->height); - FILAMENT_CHECK_POSTCONDITION(result) << "OSMesaMakeCurrent failed!"; + auto result = api->OSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(), + BACKING_GL_TYPE, impl->width, impl->height); + FILAMENT_CHECK_POSTCONDITION(result == GL_TRUE) << "OSMesaMakeCurrent failed!"; return true; } diff --git a/libs/bluegl/CMakeLists.txt b/libs/bluegl/CMakeLists.txt index 9ce75c177dd..704432cd15a 100644 --- a/libs/bluegl/CMakeLists.txt +++ b/libs/bluegl/CMakeLists.txt @@ -36,6 +36,8 @@ elseif (APPLE AND NOT IOS) elseif(LINUX) if(FILAMENT_SUPPORTS_EGL_ON_LINUX) set(SRCS ${SRCS} src/BlueGLLinuxEGL.cpp) + elseif(FILAMENT_SUPPORTS_OSMESA) + set(SRCS ${SRCS} src/BlueGLLinuxOSMesa.cpp) else() set(SRCS ${SRCS} src/BlueGLLinux.cpp) endif() diff --git a/libs/bluegl/src/BlueGLLinuxOSMesa.cpp b/libs/bluegl/src/BlueGLLinuxOSMesa.cpp new file mode 100644 index 00000000000..182f5b2a528 --- /dev/null +++ b/libs/bluegl/src/BlueGLLinuxOSMesa.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +namespace bluegl { + +namespace { +using ProcAddressFunc = void*(*)(char const* funcName); +} + +struct Driver { + ProcAddressFunc OSMesaGetProcAddress; + void* library; +} g_driver = {nullptr, nullptr}; + +bool initBinder() { + constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"}; + for (char const* name : libraryNames) { + g_driver.library = dlopen(name, RTLD_GLOBAL | RTLD_NOW); + if (g_driver.library) { + break; + } + } + if (!g_driver.library) { + return false; + } + + g_driver.OSMesaGetProcAddress = (ProcAddressFunc) + dlsym(g_driver.library, "OSMesaGetProcAddress"); + + return g_driver.OSMesaGetProcAddress; +} + +void* loadFunction(const char* name) { + return (void*) g_driver.OSMesaGetProcAddress(name); +} + +void shutdownBinder() { + dlclose(g_driver.library); + memset(&g_driver, 0, sizeof(g_driver)); +} + +} // namespace bluegl From 7f7d4d89ea6b9ca7a4617d5a2070f6f51e42dff3 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 7 Nov 2024 14:25:24 -0800 Subject: [PATCH 04/20] fix some missing highp qualifiers BUGS=[377751005] --- shaders/src/shadowing.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shaders/src/shadowing.fs b/shaders/src/shadowing.fs index 50f44e26c1d..061aec25a3b 100644 --- a/shaders/src/shadowing.fs +++ b/shaders/src/shadowing.fs @@ -19,7 +19,7 @@ float sampleDepth(const mediump sampler2DArrayShadow map, const highp vec4 scissorNormalized, - const uint layer, highp vec2 uv, float depth) { + const uint layer, highp vec2 uv, highp float depth) { // clamp needed for directional lights and/or large kernels uv = clamp(uv, scissorNormalized.xy, scissorNormalized.zw); @@ -51,7 +51,7 @@ float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map, highp vec2 texelSize = vec2(1.0) / size; // CastaƱo, 2013, "Shadow Mapping Summary Part 1" - float depth = position.z; + highp float depth = position.z; // clamp position to avoid overflows below, which cause some GPUs to abort position.xy = clamp(position.xy, vec2(-1.0), vec2(2.0)); From b9906527025e1636b65ffbff362565347a3777ec Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 8 Nov 2024 00:19:28 -0800 Subject: [PATCH 05/20] assert if post-processing is used at Feature Level 0 We assert in Renderer::render(View*) as early as possible. The current behavior can result in a SEG FAULT which is harder to debug. --- filament/backend/src/opengl/OpenGLDriver.cpp | 2 +- filament/src/details/Renderer.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 521b23ac634..883100e2997 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -1673,7 +1673,7 @@ void OpenGLDriver::createSwapChainR(Handle sch, void* nativeWindow, // See if we need the emulated rec709 output conversion if (UTILS_UNLIKELY(mContext.isES2())) { - sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE && + sc->rec709 = ((flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) && !mPlatform.isSRGBSwapChainSupported()); } } diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index 296fe606021..eec97677948 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -584,6 +584,10 @@ void FRenderer::render(FView const* view) { void FRenderer::renderInternal(FView const* view) { FEngine& engine = mEngine; + FILAMENT_CHECK_PRECONDITION(!view->hasPostProcessPass() || + engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1)) + << "post-processing is not supported at FEATURE_LEVEL_0"; + // per-renderpass data RootArenaScope rootArenaScope(engine.getPerRenderPassArena()); From 61c4df95039b71b1c66d55b18209e86f7a7812f8 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 8 Nov 2024 13:00:51 -0800 Subject: [PATCH 06/20] fix uninitialized variable on ES2 devices On ES2 devices (or in forceES2 mode), we emulate the sRGB swapchain in the shader if the h/w doesn't support it. In that case, the emulation is controlled by a uniform that technically lives in the frameUniforms block. However, the frameUniforms buffer is not updated, instead, the uniform is manually set. Unfortunately, the UBO emulation overrides it with the uninitialized variable. BUGS=[377913730] --- .../filament/hellotriangle/MainActivity.kt | 12 +++++-- filament/backend/src/opengl/OpenGLContext.cpp | 18 +++++----- filament/backend/src/opengl/OpenGLDriver.cpp | 33 +++++++++---------- filament/backend/src/opengl/OpenGLProgram.cpp | 4 ++- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/android/samples/sample-hello-triangle/src/main/java/com/google/android/filament/hellotriangle/MainActivity.kt b/android/samples/sample-hello-triangle/src/main/java/com/google/android/filament/hellotriangle/MainActivity.kt index ed6a507a766..a799acac52c 100644 --- a/android/samples/sample-hello-triangle/src/main/java/com/google/android/filament/hellotriangle/MainActivity.kt +++ b/android/samples/sample-hello-triangle/src/main/java/com/google/android/filament/hellotriangle/MainActivity.kt @@ -112,7 +112,13 @@ class MainActivity : Activity() { } private fun setupFilament() { - engine = Engine.Builder().featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build() + val config = Engine.Config() + //config.forceGLES2Context = true + + engine = Engine.Builder() + .config(config) + .featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0) + .build() renderer = engine.createRenderer() scene = engine.createScene() view = engine.createView() @@ -123,7 +129,9 @@ class MainActivity : Activity() { scene.skybox = Skybox.Builder().color(0.035f, 0.035f, 0.035f, 1.0f).build(engine) // post-processing is not supported at feature level 0 - view.isPostProcessingEnabled = false + if (engine.activeFeatureLevel == Engine.FeatureLevel.FEATURE_LEVEL_0) { + view.isPostProcessingEnabled = false + } // Tell the view which camera we want to use view.camera = camera diff --git a/filament/backend/src/opengl/OpenGLContext.cpp b/filament/backend/src/opengl/OpenGLContext.cpp index 826994c3656..fc76f5e38d8 100644 --- a/filament/backend/src/opengl/OpenGLContext.cpp +++ b/filament/backend/src/opengl/OpenGLContext.cpp @@ -614,7 +614,7 @@ FeatureLevel OpenGLContext::resolveFeatureLevel(GLint major, GLint minor, featureLevel = FeatureLevel::FEATURE_LEVEL_2; if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT && gets.max_combined_texture_image_units >= - (MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) { + (MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) { featureLevel = FeatureLevel::FEATURE_LEVEL_3; } } @@ -623,15 +623,13 @@ FeatureLevel OpenGLContext::resolveFeatureLevel(GLint major, GLint minor, # ifndef IOS // IOS is guaranteed to have ES3.x else if (UTILS_UNLIKELY(major == 2)) { // Runtime OpenGL version is ES 2.x -# if defined(BACKEND_OPENGL_LEVEL_GLES30) - // mandatory extensions (all supported by Mali-400 and Adreno 304) - assert_invariant(exts.OES_depth_texture); - assert_invariant(exts.OES_depth24); - assert_invariant(exts.OES_packed_depth_stencil); - assert_invariant(exts.OES_rgb8_rgba8); - assert_invariant(exts.OES_standard_derivatives); - assert_invariant(exts.OES_texture_npot); -# endif + // note: mandatory extensions (all supported by Mali-400 and Adreno 304) + // OES_depth_texture + // OES_depth24 + // OES_packed_depth_stencil + // OES_rgb8_rgba8 + // OES_standard_derivatives + // OES_texture_npot featureLevel = FeatureLevel::FEATURE_LEVEL_0; } # endif // IOS diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 883100e2997..676b539be7e 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -394,24 +394,21 @@ void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept { } bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept { - if (UTILS_UNLIKELY(mBoundProgram == p)) { - // program didn't change, don't do anything. - return true; - } - - // compile/link the program if needed and call glUseProgram - bool const success = p->use(this, mContext); - assert_invariant(success == p->isValid()); - - if (success) { - // TODO: we could even improve this if the program could tell us which of the descriptors - // bindings actually changed. In practice, it is likely that set 0 or 1 might not - // change often. - decltype(mInvalidDescriptorSetBindings) changed; - changed.setValue((1 << MAX_DESCRIPTOR_SET_COUNT) - 1); - mInvalidDescriptorSetBindings |= changed; - - mBoundProgram = p; + bool success = true; + if (mBoundProgram != p) { + // compile/link the program if needed and call glUseProgram + success = p->use(this, mContext); + assert_invariant(success == p->isValid()); + if (success) { + // TODO: we could even improve this if the program could tell us which of the descriptors + // bindings actually changed. In practice, it is likely that set 0 or 1 might not + // change often. + decltype(mInvalidDescriptorSetBindings) changed; + changed.setValue((1 << MAX_DESCRIPTOR_SET_COUNT) - 1); + mInvalidDescriptorSetBindings |= changed; + + mBoundProgram = p; + } } if (UTILS_UNLIKELY(mContext.isES2() && success)) { diff --git a/filament/backend/src/opengl/OpenGLProgram.cpp b/filament/backend/src/opengl/OpenGLProgram.cpp index 5f3c8d50802..b4552d5c21c 100644 --- a/filament/backend/src/opengl/OpenGLProgram.cpp +++ b/filament/backend/src/opengl/OpenGLProgram.cpp @@ -255,7 +255,9 @@ void OpenGLProgram::updateUniforms( for (size_t i = 0, c = records.uniforms.size(); i < c; i++) { Program::Uniform const& u = records.uniforms[i]; GLint const loc = records.locations[i]; - if (loc < 0) { + // mRec709Location is special, it is handled by setRec709ColorSpace() and the corresponding + // entry in `buffer` is typically not initialized, so we skip it. + if (loc < 0 || loc == mRec709Location) { continue; } // u.offset is in 'uint32_t' units From 96443fa1fd9a68ca6a51882513006fd15637a19a Mon Sep 17 00:00:00 2001 From: 1558287830 <1558287830@qq.com> Date: Tue, 12 Nov 2024 14:49:12 +0800 Subject: [PATCH 07/20] vk: fix stage pool gc logic (#8260) * vk: fix stage pool gc logic * Update NEW_RELEASE_NOTES.md --------- Co-authored-by: linkunhai --- NEW_RELEASE_NOTES.md | 1 + filament/backend/src/vulkan/VulkanStagePool.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 4a1a9c7fa7e..71416f7f695 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,3 +7,4 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut +- vk: fix stage pool gc logic diff --git a/filament/backend/src/vulkan/VulkanStagePool.cpp b/filament/backend/src/vulkan/VulkanStagePool.cpp index 067b16afd84..8fb2abf79c2 100644 --- a/filament/backend/src/vulkan/VulkanStagePool.cpp +++ b/filament/backend/src/vulkan/VulkanStagePool.cpp @@ -37,6 +37,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) { if (iter != mFreeStages.end()) { auto stage = iter->second; mFreeStages.erase(iter); + stage->lastAccessed = mCurrentFrame; mUsedStages.insert(stage); return stage; } @@ -74,6 +75,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi for (auto image : mFreeImages) { if (image->format == vkformat && image->width == width && image->height == height) { mFreeImages.erase(image); + image->lastAccessed = mCurrentFrame; mUsedImages.insert(image); return image; } From 0108d66aa1bf64322b7cf9a6258349b139836314 Mon Sep 17 00:00:00 2001 From: Serge Metral Date: Tue, 12 Nov 2024 17:10:03 +0000 Subject: [PATCH 08/20] vk: Implement protected content - Cache the render buffer in the render pass strucutre - Enable the proteded path in the Render Target - Make sure we build the protected version of the depth/color in the swap chaing - Minor cleanup/restrucuring of the code --- filament/backend/src/vulkan/VulkanBlitter.cpp | 9 +-- filament/backend/src/vulkan/VulkanContext.h | 2 + filament/backend/src/vulkan/VulkanDriver.cpp | 66 +++++++++++-------- filament/backend/src/vulkan/VulkanHandles.cpp | 6 +- filament/backend/src/vulkan/VulkanHandles.h | 6 +- .../platform/VulkanPlatformSwapChainImpl.cpp | 21 ++++-- .../platform/VulkanPlatformSwapChainImpl.h | 2 +- 7 files changed, 68 insertions(+), 44 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanBlitter.cpp b/filament/backend/src/vulkan/VulkanBlitter.cpp index db68b94b58b..85f83d8287b 100644 --- a/filament/backend/src/vulkan/VulkanBlitter.cpp +++ b/filament/backend/src/vulkan/VulkanBlitter.cpp @@ -126,8 +126,7 @@ struct BlitterUniforms { }// anonymous namespace VulkanBlitter::VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept - : mPhysicalDevice(physicalDevice), - mCommands(commands) {} + : mPhysicalDevice(physicalDevice), mCommands(commands) {} void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) { @@ -151,7 +150,8 @@ void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) { } #endif - VulkanCommandBuffer& commands = mCommands->get(); + VulkanCommandBuffer& commands = dst.texture->getIsProtected() ? + mCommands->getProtected() : mCommands->get(); commands.acquire(src.texture); commands.acquire(dst.texture); resolveFast(&commands, aspect, src, dst); @@ -176,7 +176,8 @@ void VulkanBlitter::blit(VkFilter filter, #endif // src and dst should have the same aspect here VkImageAspectFlags const aspect = src.texture->getImageAspect(); - VulkanCommandBuffer& commands = mCommands->get(); + VulkanCommandBuffer& commands = dst.texture->getIsProtected() ? + mCommands->getProtected() : mCommands->get(); commands.acquire(src.texture); commands.acquire(dst.texture); blitFast(&commands, aspect, filter, src, dst, srcRectPair, dstRectPair); diff --git a/filament/backend/src/vulkan/VulkanContext.h b/filament/backend/src/vulkan/VulkanContext.h index 68c44dfdcee..902cb33ac7a 100644 --- a/filament/backend/src/vulkan/VulkanContext.h +++ b/filament/backend/src/vulkan/VulkanContext.h @@ -82,6 +82,8 @@ class VulkanTimestamps { }; struct VulkanRenderPass { + // Between the begin and end command render pass we cache the command buffer + VulkanCommandBuffer* commandBuffer; VulkanRenderTarget* renderTarget; VkRenderPass renderPass; RenderPassParams params; diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index bb72633af65..e77efb602ed 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -201,8 +201,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex mResourceManager(&mResourceAllocator), mThreadSafeResourceManager(&mResourceAllocator), mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(), - mPlatform->getGraphicsQueueFamilyIndex(), - nullptr, 0, &mContext, &mResourceAllocator), + mPlatform->getGraphicsQueueFamilyIndex(), mPlatform->getProtectedGraphicsQueue(), + mPlatform->getProtectedGraphicsQueueFamilyIndex(), &mContext, &mResourceAllocator), mPipelineLayoutCache(mPlatform->getDevice(), &mResourceAllocator), mPipelineCache(mPlatform->getDevice(), mAllocator), mStagePool(mAllocator, &mCommands), @@ -682,7 +682,12 @@ void VulkanDriver::destroyRenderTarget(Handle rth) { } void VulkanDriver::createFenceR(Handle fh, int) { - VulkanCommandBuffer* cmdbuf = &mCommands.get(); + VulkanCommandBuffer* cmdbuf; + if (mCurrentRenderPass.commandBuffer) { + cmdbuf = mCurrentRenderPass.commandBuffer; + } else { + cmdbuf = &mCommands.get(); + } mResourceAllocator.construct(fh, cmdbuf->getFenceStatus()); } @@ -695,6 +700,13 @@ void VulkanDriver::createSwapChainR(Handle sch, void* nativeWindow, auto swapChain = mResourceAllocator.construct(sch, mPlatform, mContext, mAllocator, &mCommands, &mResourceAllocator, mStagePool, nativeWindow, flags); mResourceManager.acquire(swapChain); + + if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) { + if (!isProtectedContentSupported()) { + utils::slog.w << "protected swapchain requested, but Vulkan does not support it" + << utils::io::endl; + } + } } void VulkanDriver::createSwapChainHeadlessR(Handle sch, uint32_t width, @@ -1236,6 +1248,9 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP VkExtent2D const& extent = rt->getExtent(); assert_invariant(rt == mDefaultRenderTarget || extent.width > 0 && extent.height > 0); + VulkanCommandBuffer* commandBuffer = rt->isProtected() ? + &mCommands.getProtected() : &mCommands.get(); + // Filament has the expectation that the contents of the swap chain are not preserved on the // first render pass. Note however that its contents are often preserved on subsequent render // passes, due to multiple views. @@ -1260,8 +1275,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP // If that's the case, we need to change the layout of the texture to DEPTH_SAMPLER, which is a // more general layout. Otherwise, we prefer the DEPTH_ATTACHMENT layout, which is optimal for // the non-sampling case. - VulkanCommandBuffer& commands = mCommands.get(); - VkCommandBuffer const cmdbuffer = commands.buffer(); + VkCommandBuffer const cmdbuffer = commandBuffer->buffer(); // Scissor is reset with each render pass // This also takes care of VUID-vkCmdDrawIndexed-None-07832. @@ -1296,7 +1310,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP fbkey.renderPass = renderPass; fbkey.layers = 1; - rt->emitBarriersBeginRenderPass(commands); + rt->emitBarriersBeginRenderPass(*commandBuffer); VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey); @@ -1310,7 +1324,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP #endif // The current command buffer now has references to the render target and its attachments. - commands.acquire(rt); + commandBuffer->acquire(rt); // Populate the structures required for vkCmdBeginRenderPass. VkRenderPassBeginInfo renderPassInfo { @@ -1369,6 +1383,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP vkCmdSetViewport(cmdbuffer, 0, 1, &viewport); mCurrentRenderPass = { + .commandBuffer = commandBuffer, .renderTarget = rt, .renderPass = renderPassInfo.renderPass, .params = params, @@ -1379,8 +1394,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP void VulkanDriver::endRenderPass(int) { FVK_SYSTRACE_SCOPE(); - VulkanCommandBuffer& commands = mCommands.get(); - VkCommandBuffer cmdbuffer = commands.buffer(); + VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer(); vkCmdEndRenderPass(cmdbuffer); VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget; @@ -1388,11 +1402,13 @@ void VulkanDriver::endRenderPass(int) { // Since we might soon be sampling from the render target that we just wrote to, we need a // pipeline barrier between framebuffer writes and shader reads. - rt->emitBarriersEndRenderPass(commands); + rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer); mRenderPassFboInfo = {}; mCurrentRenderPass.renderTarget = nullptr; mCurrentRenderPass.renderPass = VK_NULL_HANDLE; + + mCurrentRenderPass.commandBuffer = nullptr; } void VulkanDriver::nextSubpass(int) { @@ -1403,7 +1419,8 @@ void VulkanDriver::nextSubpass(int) { assert_invariant(renderTarget); assert_invariant(mCurrentRenderPass.params.subpassMask); - vkCmdNextSubpass(mCommands.get().buffer(), VK_SUBPASS_CONTENTS_INLINE); + vkCmdNextSubpass(mCurrentRenderPass.commandBuffer->buffer(), + VK_SUBPASS_CONTENTS_INLINE); mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass, ++mCurrentRenderPass.currentSubpass); @@ -1446,9 +1463,8 @@ void VulkanDriver::commit(Handle sch) { void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant value) { assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants"); - VulkanCommands* commands = &mCommands; - mBoundPipeline.program->writePushConstant(commands, mBoundPipeline.pipelineLayout, stage, index, - value); + mBoundPipeline.program->writePushConstant(mCurrentRenderPass.commandBuffer, + mBoundPipeline.pipelineLayout, stage, index, value); } void VulkanDriver::insertEventMarker(char const* string) { @@ -1628,7 +1644,6 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers, void VulkanDriver::bindPipeline(PipelineState const& pipelineState) { FVK_SYSTRACE_SCOPE(); - VulkanCommandBuffer* commands = &mCommands.get(); const VulkanVertexBufferInfo& vbi = *mResourceAllocator.handle_cast(pipelineState.vertexBufferInfo); @@ -1637,7 +1652,7 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) { PolygonOffset const& depthOffset = pipelineState.polygonOffset; auto* program = mResourceAllocator.handle_cast(programHandle); - commands->acquire(program); + mCurrentRenderPass.commandBuffer->acquire(program); // Update the VK raster state. const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget; @@ -1701,17 +1716,16 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) { }; mPipelineCache.bindLayout(pipelineLayout); - mPipelineCache.bindPipeline(commands); + mPipelineCache.bindPipeline(mCurrentRenderPass.commandBuffer); } void VulkanDriver::bindRenderPrimitive(Handle rph) { FVK_SYSTRACE_SCOPE(); - VulkanCommandBuffer* commands = &mCommands.get(); - VkCommandBuffer cmdbuffer = commands->buffer(); + VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer(); const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast(rph); - commands->acquire(prim.indexBuffer); - commands->acquire(prim.vertexBuffer); + mCurrentRenderPass.commandBuffer->acquire(prim.indexBuffer); + mCurrentRenderPass.commandBuffer->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() @@ -1744,11 +1758,10 @@ void VulkanDriver::bindDescriptorSet( void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { FVK_SYSTRACE_SCOPE(); + VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer(); - VulkanCommandBuffer& commands = mCommands.get(); - VkCommandBuffer cmdbuffer = commands.buffer(); - - mDescriptorSetManager.commit(&commands, mBoundPipeline.pipelineLayout, + mDescriptorSetManager.commit(mCurrentRenderPass.commandBuffer, + mBoundPipeline.pipelineLayout, mBoundPipeline.descriptorSetMask); // Finally, make the actual draw call. TODO: support subranges @@ -1774,8 +1787,7 @@ void VulkanDriver::dispatchCompute(Handle program, math::uint3 workGr } void VulkanDriver::scissor(Viewport scissorBox) { - VulkanCommandBuffer& commands = mCommands.get(); - VkCommandBuffer cmdbuffer = commands.buffer(); + VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer(); // TODO: it's a common case that scissor() is called with (0, 0, maxint, maxint) // we should maybe have a fast path for this and avoid vkCmdSetScissor() if possible diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 9fda018943b..a2df51edc29 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -164,9 +164,9 @@ PushConstantDescription::PushConstantDescription(backend::Program const& program } } -void PushConstantDescription::write(VulkanCommands* commands, VkPipelineLayout layout, +void PushConstantDescription::write(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) { - VulkanCommandBuffer* cmdbuf = &(commands->get()); + uint32_t binaryValue = 0; UTILS_UNUSED_IN_RELEASE auto const& types = mTypes[(uint8_t) stage]; if (std::holds_alternative(value)) { @@ -340,6 +340,8 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica continue; } + mProtected |= texture->getIsProtected(); + attachments.push_back(attachment); mInfo->colors.set(index); diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 1dc3141fd5a..4b573796c79 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -185,7 +185,7 @@ struct PushConstantDescription { uint32_t getVkRangeCount() const noexcept { return mRangeCount; } - void write(VulkanCommands* commands, VkPipelineLayout layout, backend::ShaderStage stage, + void write(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value); private: @@ -218,9 +218,9 @@ struct VulkanProgram : public HwProgram, VulkanResource { return mInfo->pushConstantDescription.getVkRanges(); } - inline void writePushConstant(VulkanCommands* commands, VkPipelineLayout layout, + inline void writePushConstant(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) { - mInfo->pushConstantDescription.write(commands, layout, stage, index, value); + mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value); } #if FVK_ENABLED_DEBUG_SAMPLER_NAME diff --git a/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp b/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp index 3037f64af74..ed9ba2281b9 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp +++ b/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp @@ -29,7 +29,8 @@ namespace filament::backend { namespace { std::tuple createImageAndMemory(VulkanContext const& context, - VkDevice device, VkExtent2D extent, VkFormat format) { + VkDevice device, VkExtent2D extent, VkFormat format, + bool isProtected) { bool const isDepth = isVkDepthFormat(format); // Filament expects blit() to work with any texture, so we almost always set these usage flags // (see copyFrame() and readPixels()). @@ -38,6 +39,7 @@ std::tuple createImageAndMemory(VulkanContext const& co VkImageCreateInfo imageInfo { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .flags = isProtected ? VK_IMAGE_CREATE_PROTECTED_BIT : VkImageCreateFlags(0), .imageType = VK_IMAGE_TYPE_2D, .format = format, .extent = {extent.width, extent.height, 1}, @@ -58,8 +60,11 @@ std::tuple createImageAndMemory(VulkanContext const& co VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(device, image, &memReqs); + const VkFlags requiredMemoryFlags = + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + (isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U); uint32_t memoryTypeIndex - = context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + = context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags); FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES) << "VulkanPlatformSwapChainImpl: unable to find a memory type that meets requirements."; @@ -110,8 +115,9 @@ void VulkanPlatformSwapChainImpl::destroy() { mSwapChainBundle.colors.clear(); } -VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format) { - auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format); +VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format, + bool isProtected) { + auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format, isProtected); mMemory.insert({image, memory}); return image; } @@ -248,7 +254,8 @@ VkResult VulkanPlatformSurfaceSwapChain::create() { mSwapChainBundle.colorFormat = surfaceFormat.format; mSwapChainBundle.depthFormat = selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil); - mSwapChainBundle.depth = createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat); + mSwapChainBundle.depth = createImage(mSwapChainBundle.extent, + mSwapChainBundle.depthFormat, mIsProtected); mSwapChainBundle.isProtected = mIsProtected; FVK_LOGI << "vkCreateSwapchain" @@ -356,13 +363,13 @@ VulkanPlatformHeadlessSwapChain::VulkanPlatformHeadlessSwapChain(VulkanContext c images.reserve(HEADLESS_SWAPCHAIN_SIZE); images.resize(HEADLESS_SWAPCHAIN_SIZE); for (size_t i = 0; i < HEADLESS_SWAPCHAIN_SIZE; ++i) { - images[i] = createImage(extent, mSwapChainBundle.colorFormat); + images[i] = createImage(extent, mSwapChainBundle.colorFormat, false); } bool const hasStencil = (flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0; mSwapChainBundle.depthFormat = selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), hasStencil); - mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat); + mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat, false); } VulkanPlatformHeadlessSwapChain::~VulkanPlatformHeadlessSwapChain() { diff --git a/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.h b/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.h index 2a715a210c2..ff1179d4ec3 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.h +++ b/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.h @@ -73,7 +73,7 @@ struct VulkanPlatformSwapChainImpl : public Platform::SwapChain { // Non-virtual override-able method void destroy(); - VkImage createImage(VkExtent2D extent, VkFormat format); + VkImage createImage(VkExtent2D extent, VkFormat format, bool isProtected); VulkanContext const& mContext; VkDevice mDevice; From bc65135c512f90d9b99442e9e0ff66bf9eec15b2 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 12 Nov 2024 10:33:00 -0800 Subject: [PATCH 09/20] vk: refactor resource ref-counting (#8254) Continuing from PR #8220. Here we change all of the references from the old ref-counting ways to the new ref-counting structs and mechanisms. There should be no functional change. --- filament/backend/CMakeLists.txt | 3 - .../backend/src/vulkan/VulkanAsyncHandles.cpp | 3 +- .../backend/src/vulkan/VulkanAsyncHandles.h | 13 +- filament/backend/src/vulkan/VulkanBlitter.cpp | 4 +- .../backend/src/vulkan/VulkanCommands.cpp | 37 +- filament/backend/src/vulkan/VulkanCommands.h | 24 +- filament/backend/src/vulkan/VulkanConstants.h | 4 +- filament/backend/src/vulkan/VulkanContext.cpp | 15 +- filament/backend/src/vulkan/VulkanContext.h | 16 +- filament/backend/src/vulkan/VulkanDriver.cpp | 495 +++++++++--------- filament/backend/src/vulkan/VulkanDriver.h | 19 +- filament/backend/src/vulkan/VulkanHandles.cpp | 122 ++--- filament/backend/src/vulkan/VulkanHandles.h | 109 ++-- .../src/vulkan/VulkanPipelineCache.cpp | 2 +- .../backend/src/vulkan/VulkanPipelineCache.h | 4 +- .../backend/src/vulkan/VulkanReadPixels.cpp | 9 +- .../backend/src/vulkan/VulkanReadPixels.h | 7 +- .../src/vulkan/VulkanResourceAllocator.h | 136 ----- .../backend/src/vulkan/VulkanResources.cpp | 81 --- filament/backend/src/vulkan/VulkanResources.h | 313 ----------- .../backend/src/vulkan/VulkanStagePool.cpp | 3 +- filament/backend/src/vulkan/VulkanStagePool.h | 5 +- .../backend/src/vulkan/VulkanSwapChain.cpp | 37 +- filament/backend/src/vulkan/VulkanSwapChain.h | 30 +- filament/backend/src/vulkan/VulkanTexture.cpp | 242 ++++----- filament/backend/src/vulkan/VulkanTexture.h | 77 +-- .../caching/VulkanDescriptorSetManager.cpp | 70 +-- .../caching/VulkanDescriptorSetManager.h | 40 +- .../caching/VulkanPipelineLayoutCache.cpp | 11 +- .../caching/VulkanPipelineLayoutCache.h | 4 +- .../src/vulkan/memory/ResourceManager.cpp | 11 +- .../src/vulkan/memory/ResourceManager.h | 1 - 32 files changed, 649 insertions(+), 1298 deletions(-) delete mode 100644 filament/backend/src/vulkan/VulkanResourceAllocator.h delete mode 100644 filament/backend/src/vulkan/VulkanResources.cpp delete mode 100644 filament/backend/src/vulkan/VulkanResources.h diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index 9a70983d794..c44643a3f56 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -221,9 +221,6 @@ if (FILAMENT_SUPPORTS_VULKAN) src/vulkan/VulkanSwapChain.h src/vulkan/VulkanReadPixels.cpp src/vulkan/VulkanReadPixels.h - src/vulkan/VulkanResourceAllocator.h - src/vulkan/VulkanResources.cpp - src/vulkan/VulkanResources.h src/vulkan/VulkanTexture.cpp src/vulkan/VulkanTexture.h src/vulkan/VulkanUtility.cpp diff --git a/filament/backend/src/vulkan/VulkanAsyncHandles.cpp b/filament/backend/src/vulkan/VulkanAsyncHandles.cpp index 46d6ed38bf6..82963b05c4d 100644 --- a/filament/backend/src/vulkan/VulkanAsyncHandles.cpp +++ b/filament/backend/src/vulkan/VulkanAsyncHandles.cpp @@ -19,8 +19,7 @@ namespace filament::backend { VulkanTimerQuery::VulkanTimerQuery(std::tuple indices) - : VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY), - mStartingQueryIndex(std::get<0>(indices)), + : mStartingQueryIndex(std::get<0>(indices)), mStoppingQueryIndex(std::get<1>(indices)) {} void VulkanTimerQuery::setFence(std::shared_ptr fence) noexcept { diff --git a/filament/backend/src/vulkan/VulkanAsyncHandles.h b/filament/backend/src/vulkan/VulkanAsyncHandles.h index 4e7920fd4da..eb626871ae3 100644 --- a/filament/backend/src/vulkan/VulkanAsyncHandles.h +++ b/filament/backend/src/vulkan/VulkanAsyncHandles.h @@ -21,7 +21,7 @@ #include "DriverBase.h" -#include "VulkanResources.h" +#include "vulkan/memory/Resource.h" #include #include @@ -47,17 +47,12 @@ struct VulkanCmdFence { std::atomic status; }; -struct VulkanFence : public HwFence, VulkanResource { - VulkanFence() : VulkanResource(VulkanResourceType::FENCE) {} - - explicit VulkanFence(std::shared_ptr fence) - : VulkanResource(VulkanResourceType::FENCE), - fence(fence) {} - +struct VulkanFence : public HwFence, fvkmemory::ThreadSafeResource { + VulkanFence() {} std::shared_ptr fence; }; -struct VulkanTimerQuery : public HwTimerQuery, VulkanThreadSafeResource { +struct VulkanTimerQuery : public HwTimerQuery, fvkmemory::ThreadSafeResource { explicit VulkanTimerQuery(std::tuple indices); ~VulkanTimerQuery(); diff --git a/filament/backend/src/vulkan/VulkanBlitter.cpp b/filament/backend/src/vulkan/VulkanBlitter.cpp index 85f83d8287b..bcb786e63ae 100644 --- a/filament/backend/src/vulkan/VulkanBlitter.cpp +++ b/filament/backend/src/vulkan/VulkanBlitter.cpp @@ -151,7 +151,7 @@ void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) { #endif VulkanCommandBuffer& commands = dst.texture->getIsProtected() ? - mCommands->getProtected() : mCommands->get(); + mCommands->getProtected() : mCommands->get(); commands.acquire(src.texture); commands.acquire(dst.texture); resolveFast(&commands, aspect, src, dst); @@ -177,7 +177,7 @@ void VulkanBlitter::blit(VkFilter filter, // src and dst should have the same aspect here VkImageAspectFlags const aspect = src.texture->getImageAspect(); VulkanCommandBuffer& commands = dst.texture->getIsProtected() ? - mCommands->getProtected() : mCommands->get(); + mCommands->getProtected() : mCommands->get(); commands.acquire(src.texture); commands.acquire(dst.texture); blitFast(&commands, aspect, filter, src, dst, srcRectPair, dstRectPair); diff --git a/filament/backend/src/vulkan/VulkanCommands.cpp b/filament/backend/src/vulkan/VulkanCommands.cpp index c3548d5cbbc..3dd8fd95a60 100644 --- a/filament/backend/src/vulkan/VulkanCommands.cpp +++ b/filament/backend/src/vulkan/VulkanCommands.cpp @@ -110,14 +110,13 @@ bool VulkanGroupMarkers::empty() const noexcept { #endif // FVK_DEBUG_GROUP_MARKERS -VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VulkanResourceAllocator* allocator, - VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected) +VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VkDevice device, VkQueue queue, + VkCommandPool pool, bool isProtected) : mContext(context), mMarkerCount(0), isProtected(isProtected), mDevice(device), mQueue(queue), - mResourceManager(allocator), mBuffer(createCommandBuffer(device, pool)), mFenceStatus(std::make_shared(VK_INCOMPLETE)) { VkSemaphoreCreateInfo sci{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; @@ -134,7 +133,7 @@ VulkanCommandBuffer::~VulkanCommandBuffer() { void VulkanCommandBuffer::reset() noexcept { mMarkerCount = 0; - mResourceManager.clear(); + mResources.clear(); mWaitSemaphores.clear(); // Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence @@ -257,8 +256,8 @@ VkSemaphore VulkanCommandBuffer::submit() { return mSubmission; } -CommandBufferPool::CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator, - VkDevice device, VkQueue queue, uint8_t queueFamilyIndex, bool isProtected) +CommandBufferPool::CommandBufferPool(VulkanContext* context, VkDevice device, VkQueue queue, + uint8_t queueFamilyIndex, bool isProtected) : mDevice(device), mRecording(INVALID) { VkCommandPoolCreateInfo createInfo = { @@ -271,8 +270,8 @@ CommandBufferPool::CommandBufferPool(VulkanContext* context, VulkanResourceAlloc vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool); for (size_t i = 0; i < CAPACITY; ++i) { - mBuffers.emplace_back(std::make_unique(context, allocator, device, - queue, mPool, isProtected)); + mBuffers.emplace_back( + std::make_unique(context, device, queue, mPool, isProtected)); } } @@ -370,16 +369,12 @@ void CommandBufferPool::insertEvent(char const* marker) { } VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex, - VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context, - VulkanResourceAllocator* allocator) + VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context) : mDevice(device), mProtectedQueue(protectedQueue), mProtectedQueueFamilyIndex(protectedQueueFamilyIndex), - mAllocator(allocator), mContext(context), - mPool(std::make_unique(context, allocator, device, queue, queueFamilyIndex, - false)) { -} + mPool(std::make_unique(context, device, queue, queueFamilyIndex, false)) {} void VulkanCommands::terminate() { mPool.reset(); @@ -395,14 +390,19 @@ VulkanCommandBuffer& VulkanCommands::getProtected() { assert_invariant(mProtectedQueue != VK_NULL_HANDLE); if (!mProtectedPool) { - mProtectedPool = std::make_unique(mContext, mAllocator, mDevice, - mProtectedQueue, mProtectedQueueFamilyIndex, true); + mProtectedPool = std::make_unique(mContext, mDevice, mProtectedQueue, + mProtectedQueueFamilyIndex, true); } auto& ret = mProtectedPool->getRecording(); return ret; } bool VulkanCommands::flush() { + // It's possible to call flush and wait at "terminate", in which case, we'll just return. + if (!mPool && !mProtectedPool) { + return false; + } + VkSemaphore dependency = mInjectedDependency; VkSemaphore lastSubmit = mLastSubmit; bool hasFlushed = false; @@ -434,6 +434,11 @@ bool VulkanCommands::flush() { } void VulkanCommands::wait() { + // It's possible to call flush and wait at "terminate", in which case, we'll just return. + if (!mPool && !mProtectedPool) { + return; + } + FVK_SYSTRACE_CONTEXT(); FVK_SYSTRACE_START("commands::wait"); diff --git a/filament/backend/src/vulkan/VulkanCommands.h b/filament/backend/src/vulkan/VulkanCommands.h index 25b839ec49e..e7354fbe6bd 100644 --- a/filament/backend/src/vulkan/VulkanCommands.h +++ b/filament/backend/src/vulkan/VulkanCommands.h @@ -23,8 +23,8 @@ #include "VulkanAsyncHandles.h" #include "VulkanConstants.h" -#include "VulkanResources.h" #include "VulkanUtility.h" +#include "vulkan/memory/ResourcePointer.h" #include #include @@ -39,6 +39,8 @@ namespace filament::backend { +using namespace fvkmemory; + struct VulkanContext; #if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS) @@ -65,7 +67,7 @@ class VulkanGroupMarkers { // DriverApi fence object and should not be destroyed until both the DriverApi object is freed and // we're done waiting on the most recent submission of the given command buffer. struct VulkanCommandBuffer { - VulkanCommandBuffer(VulkanContext* mContext, VulkanResourceAllocator* allocator, + VulkanCommandBuffer(VulkanContext* mContext, VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected); VulkanCommandBuffer(VulkanCommandBuffer const&) = delete; @@ -73,13 +75,10 @@ struct VulkanCommandBuffer { ~VulkanCommandBuffer(); - inline void acquire(VulkanResource* resource) { - mResourceManager.acquire(resource); + inline void acquire(fvkmemory::resource_ptr resource) { + mResources.push_back(resource); } - inline void acquire(VulkanAcquireOnlyResourceManager* srcResources) { - mResourceManager.acquireAll(srcResources); - } void reset() noexcept; inline void insertWait(VkSemaphore sem) { @@ -119,21 +118,20 @@ struct VulkanCommandBuffer { bool const isProtected; VkDevice mDevice; VkQueue mQueue; - VulkanAcquireOnlyResourceManager mResourceManager; CappedArray mWaitSemaphores; VkCommandBuffer mBuffer; VkSemaphore mSubmission; VkFence mFence; std::shared_ptr mFenceStatus; - + std::vector> mResources; }; struct CommandBufferPool { using ActiveBuffers = utils::bitset32; static constexpr int8_t INVALID = -1; - CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator, VkDevice device, - VkQueue queue, uint8_t queueFamilyIndex, bool isProtected); + CommandBufferPool(VulkanContext* context, VkDevice device, VkQueue queue, + uint8_t queueFamilyIndex, bool isProtected); ~CommandBufferPool(); VulkanCommandBuffer& getRecording(); @@ -189,8 +187,7 @@ struct CommandBufferPool { class VulkanCommands { public: VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex, - VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context, - VulkanResourceAllocator* allocator); + VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context); void terminate(); @@ -241,7 +238,6 @@ class VulkanCommands { VkQueue const mProtectedQueue; // For defered initialization if/when we need protected content uint32_t const mProtectedQueueFamilyIndex; - VulkanResourceAllocator* mAllocator; VulkanContext* mContext; std::unique_ptr mPool; diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h index c291a0c4f13..79b388a7136 100644 --- a/filament/backend/src/vulkan/VulkanConstants.h +++ b/filament/backend/src/vulkan/VulkanConstants.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 The Android Open Source Project * -* Licensed under the Apache License, Version 2.0 (the "License"); + * 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 * @@ -63,7 +63,7 @@ #define FVK_DEBUG_SHADER_MODULE 0x00000800 #define FVK_DEBUG_READ_PIXELS 0x00001000 #define FVK_DEBUG_PIPELINE_CACHE 0x00002000 -#define FVK_DEBUG_ALLOCATION 0x00004000 +#define FVK_DEBUG_STAGING_ALLOCATION 0x00004000 // Enable the debug utils extension if it is available. #define FVK_DEBUG_DEBUG_UTILS 0x00008000 diff --git a/filament/backend/src/vulkan/VulkanContext.cpp b/filament/backend/src/vulkan/VulkanContext.cpp index 576fff38e17..7c11a47551f 100644 --- a/filament/backend/src/vulkan/VulkanContext.cpp +++ b/filament/backend/src/vulkan/VulkanContext.cpp @@ -25,15 +25,11 @@ #include #include -#include #include // for std::max using namespace bluevk; -using utils::FixedCapacityVector; - - namespace { } // end anonymous namespace @@ -113,7 +109,7 @@ void VulkanTimestamps::clearQuery(uint32_t queryIndex) { } void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands, - VulkanTimerQuery* query) { + fvkmemory::resource_ptr query) { uint32_t const index = query->getStartingQueryIndex(); auto const cmdbuffer = commands->buffer(); @@ -125,19 +121,20 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands, } void VulkanTimestamps::endQuery(VulkanCommandBuffer const* commands, - VulkanTimerQuery const* query) { + fvkmemory::resource_ptr query) { uint32_t const index = query->getStoppingQueryIndex(); vkCmdWriteTimestamp(commands->buffer(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index); } -VulkanTimestamps::QueryResult VulkanTimestamps::getResult(VulkanTimerQuery const* query) { +VulkanTimestamps::QueryResult VulkanTimestamps::getResult( + fvkmemory::resource_ptr query) { uint32_t const index = query->getStartingQueryIndex(); QueryResult result; size_t const dataSize = result.size() * sizeof(uint64_t); VkDeviceSize const stride = sizeof(uint64_t) * 2; VkResult vkresult = - vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) result.data(), - stride, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT); + vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) result.data(), stride, + VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT); FILAMENT_CHECK_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY) << "vkGetQueryPoolResults error: " << static_cast(vkresult); if (vkresult == VK_NOT_READY) { diff --git a/filament/backend/src/vulkan/VulkanContext.h b/filament/backend/src/vulkan/VulkanContext.h index 902cb33ac7a..67c4e52a9b7 100644 --- a/filament/backend/src/vulkan/VulkanContext.h +++ b/filament/backend/src/vulkan/VulkanContext.h @@ -21,6 +21,8 @@ #include "VulkanImageUtility.h" #include "VulkanUtility.h" +#include "vulkan/memory/ResourcePointer.h" + #include #include #include @@ -41,10 +43,10 @@ struct VulkanTimerQuery; struct VulkanCommandBuffer; struct VulkanAttachment { - VulkanTexture* texture = nullptr; + fvkmemory::resource_ptr texture; uint8_t level = 0; uint8_t layerCount = 1; - uint16_t layer = 0; + uint8_t layer = 0; bool isDepth() const; VkImage getImage() const; @@ -70,9 +72,11 @@ class VulkanTimestamps { std::tuple getNextQuery(); void clearQuery(uint32_t queryIndex); - void beginQuery(VulkanCommandBuffer const* commands, VulkanTimerQuery* query); - void endQuery(VulkanCommandBuffer const* commands, VulkanTimerQuery const* query); - QueryResult getResult(VulkanTimerQuery const* query); + void beginQuery(VulkanCommandBuffer const* commands, + fvkmemory::resource_ptr query); + void endQuery(VulkanCommandBuffer const* commands, + fvkmemory::resource_ptr query); + QueryResult getResult(fvkmemory::resource_ptr query); private: VkDevice mDevice; @@ -84,7 +88,7 @@ class VulkanTimestamps { struct VulkanRenderPass { // Between the begin and end command render pass we cache the command buffer VulkanCommandBuffer* commandBuffer; - VulkanRenderTarget* renderTarget; + fvkmemory::resource_ptr renderTarget; VkRenderPass renderPass; RenderPassParams params; int currentSubpass; diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index e77efb602ed..9bfac220f4e 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -26,6 +26,8 @@ #include "VulkanHandles.h" #include "VulkanMemory.h" #include "VulkanTexture.h" +#include "memory/ResourceManager.h" +#include "memory/ResourcePointer.h" #include @@ -194,23 +196,21 @@ Dispatcher VulkanDriver::getDispatcher() const noexcept { VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& context, Platform::DriverConfig const& driverConfig) noexcept : mPlatform(platform), + mResourceManager(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck), mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(), mPlatform->getDevice())), mContext(context), - mResourceAllocator(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck), - mResourceManager(&mResourceAllocator), - mThreadSafeResourceManager(&mResourceAllocator), mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(), mPlatform->getProtectedGraphicsQueue(), - mPlatform->getProtectedGraphicsQueueFamilyIndex(), &mContext, &mResourceAllocator), - mPipelineLayoutCache(mPlatform->getDevice(), &mResourceAllocator), + mPlatform->getProtectedGraphicsQueueFamilyIndex(), &mContext), + mPipelineLayoutCache(mPlatform->getDevice()), mPipelineCache(mPlatform->getDevice(), mAllocator), mStagePool(mAllocator, &mCommands), mFramebufferCache(mPlatform->getDevice()), mSamplerCache(mPlatform->getDevice()), mBlitter(mPlatform->getPhysicalDevice(), &mCommands), mReadPixels(mPlatform->getDevice()), - mDescriptorSetManager(mPlatform->getDevice(), &mResourceAllocator), + mDescriptorSetManager(mPlatform->getDevice(), &mResourceManager), mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported), mStereoscopicType(driverConfig.stereoscopicType) { @@ -300,11 +300,10 @@ void VulkanDriver::terminate() { // to those commands are no longer referenced. finish(0); - // Command buffers should come first since it might have commands depending on resources that - // are about to be destroyed. - mCommands.terminate(); + mCurrentSwapChain = {}; + mDefaultRenderTarget = {}; + mBoundPipeline = {}; - mResourceManager.clear(); mTimestamps.reset(); mBlitter.terminate(); @@ -313,6 +312,8 @@ void VulkanDriver::terminate() { // Allow the stage pool to clean up. mStagePool.gc(); + mCommands.terminate(); + mStagePool.terminate(); mPipelineCache.terminate(); mFramebufferCache.reset(); @@ -320,8 +321,11 @@ void VulkanDriver::terminate() { mDescriptorSetManager.terminate(); mPipelineLayoutCache.terminate(); + // Before terminating ResourceManager, we must make sure all of the resource_ptrs have been unset. + mResourceManager.terminate(); + #if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK) - mResourceAllocator.print(); + mResourceManager.print(); #endif vmaDestroyAllocator(mAllocator); @@ -348,15 +352,17 @@ void VulkanDriver::tick(int) { // been destroyed for safe destruction, due to outstanding command buffers and triple buffering. void VulkanDriver::collectGarbage() { FVK_SYSTRACE_SCOPE(); - // Command buffers need to be submitted and completed before other resources can be gc'd. And - // its gc() function carrys out the *wait*. + // Command buffers need to be submitted and completed before other resources can be gc'd. mCommands.gc(); + mDescriptorSetManager.clearHistory(); mStagePool.gc(); mFramebufferCache.gc(); mPipelineCache.gc(); + mResourceManager.gc(); + #if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK) - mResourceAllocator.print(); + mResourceManager.print(); #endif } void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, @@ -388,13 +394,10 @@ void VulkanDriver::updateDescriptorSetBuffer( backend::BufferObjectHandle boh, uint32_t offset, uint32_t size) { - FVK_SYSTRACE_CONTEXT(); - FVK_SYSTRACE_START("updateDescriptorSetBuffer"); - - VulkanDescriptorSet* set = mResourceAllocator.handle_cast(dsh); - VulkanBufferObject* obj = mResourceAllocator.handle_cast(boh); - mDescriptorSetManager.updateBuffer(set, binding, obj, offset, size); - FVK_SYSTRACE_END(); + FVK_SYSTRACE_SCOPE(); + auto set = resource_ptr::cast(&mResourceManager, dsh); + auto buffer = resource_ptr::cast(&mResourceManager, boh); + mDescriptorSetManager.updateBuffer(set, binding, buffer, offset, size); } void VulkanDriver::updateDescriptorSetTexture( @@ -402,13 +405,12 @@ void VulkanDriver::updateDescriptorSetTexture( backend::descriptor_binding_t binding, backend::TextureHandle th, SamplerParams params) { - FVK_SYSTRACE_CONTEXT(); - FVK_SYSTRACE_START("updateDescriptorSetTexture"); - VulkanDescriptorSet* set = mResourceAllocator.handle_cast(dsh); - VulkanTexture* texture = mResourceAllocator.handle_cast(th); + FVK_SYSTRACE_SCOPE(); + auto set = resource_ptr::cast(&mResourceManager, dsh); + auto texture = resource_ptr::cast(&mResourceManager, th); + VkSampler const vksampler = mSamplerCache.getSampler(params); mDescriptorSetManager.updateSampler(set, binding, texture, vksampler); - FVK_SYSTRACE_END(); } void VulkanDriver::flush(int) { @@ -428,129 +430,117 @@ void VulkanDriver::finish(int dummy) { void VulkanDriver::createRenderPrimitiveR(Handle rph, Handle vbh, Handle ibh, PrimitiveType pt) { - auto rp = mResourceAllocator.construct(rph, - &mResourceAllocator, pt, vbh, ibh); - mResourceManager.acquire(rp); + auto vb = resource_ptr::cast(&mResourceManager, vbh); + auto ib = resource_ptr::cast(&mResourceManager, ibh); + auto ptr = resource_ptr::make(&mResourceManager, rph, pt, vb, ib); + ptr.inc(); } void VulkanDriver::destroyRenderPrimitive(Handle rph) { if (!rph) { return; } - auto rp = mResourceAllocator.handle_cast(rph); - mResourceManager.release(rp); + auto ptr = resource_ptr::cast(&mResourceManager, rph); + ptr.dec(); } void VulkanDriver::createVertexBufferInfoR(Handle vbih, uint8_t bufferCount, uint8_t attributeCount, AttributeArray attributes) { - auto vbi = mResourceAllocator.construct(vbih, - bufferCount, attributeCount, attributes); - mResourceManager.acquire(vbi); + auto vbi = resource_ptr::make(&mResourceManager, vbih, bufferCount, + attributeCount, attributes); + vbi.inc(); } void VulkanDriver::destroyVertexBufferInfo(Handle vbih) { if (!vbih) { return; } - auto vbi = mResourceAllocator.handle_cast(vbih); - mResourceManager.release(vbi); + auto vbi = resource_ptr::cast(&mResourceManager, vbih); + vbi.dec(); } - -void VulkanDriver::createVertexBufferR(Handle vbh, - uint32_t vertexCount, Handle vbih) { - auto vertexBuffer = mResourceAllocator.construct(vbh, - mContext, mStagePool, &mResourceAllocator, vertexCount, vbih); - mResourceManager.acquire(vertexBuffer); +void VulkanDriver::createVertexBufferR(Handle vbh, uint32_t vertexCount, + Handle vbih) { + auto vbi = resource_ptr::cast(&mResourceManager, vbih); + auto vb = resource_ptr::make(&mResourceManager, vbh, mContext, mStagePool, + vertexCount, vbi); + vb.inc(); } void VulkanDriver::destroyVertexBuffer(Handle vbh) { if (!vbh) { return; } - auto vertexBuffer = mResourceAllocator.handle_cast(vbh); - mResourceManager.release(vertexBuffer); + auto vb = resource_ptr::cast(&mResourceManager, vbh); + vb.dec(); } void VulkanDriver::createIndexBufferR(Handle ibh, ElementType elementType, uint32_t indexCount, BufferUsage usage) { auto elementSize = (uint8_t) getElementTypeSize(elementType); - auto indexBuffer = mResourceAllocator.construct(ibh, mAllocator, mStagePool, + auto ib = resource_ptr::make(&mResourceManager, ibh, mAllocator, mStagePool, elementSize, indexCount); - mResourceManager.acquire(indexBuffer); + ib.inc(); } void VulkanDriver::destroyIndexBuffer(Handle ibh) { if (!ibh) { return; } - auto indexBuffer = mResourceAllocator.handle_cast(ibh); - mResourceManager.release(indexBuffer); + auto ib = resource_ptr::cast(&mResourceManager, ibh); + ib.dec(); } void VulkanDriver::createBufferObjectR(Handle boh, uint32_t byteCount, BufferObjectBinding bindingType, BufferUsage usage) { - auto bufferObject = mResourceAllocator.construct(boh, mAllocator, - mStagePool, byteCount, bindingType); - mResourceManager.acquire(bufferObject); + auto bo = resource_ptr::make(&mResourceManager, boh, mAllocator, mStagePool, + byteCount, bindingType); + bo.inc(); } void VulkanDriver::destroyBufferObject(Handle boh) { if (!boh) { return; } - auto bufferObject = mResourceAllocator.handle_cast(boh); - mResourceManager.release(bufferObject); + auto bo = resource_ptr::cast(&mResourceManager, boh); + bo.dec(); } void VulkanDriver::createTextureR(Handle th, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage usage) { - FVK_SYSTRACE_CONTEXT(); - FVK_SYSTRACE_START("createTexture"); + FVK_SYSTRACE_SCOPE(); + auto texture = resource_ptr::make(&mResourceManager, th, mPlatform->getDevice(), + mPlatform->getPhysicalDevice(), mContext, mAllocator, &mResourceManager, &mCommands, + target, levels, format, samples, w, h, depth, usage, mStagePool); - auto vktexture = mResourceAllocator.construct(th, mPlatform->getDevice(), - mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator, - target, levels, - format, samples, w, h, depth, usage, mStagePool); - mResourceManager.acquire(vktexture); + // Do transition to default layout. + VulkanCommandBuffer& commandsBuf = mCommands.get(); + auto const& primaryViewRange = texture->getPrimaryViewRange(); + auto const defaultLayout = texture->getDefaultLayout(); + texture->transitionLayout(&commandsBuf, primaryViewRange, defaultLayout); - FVK_SYSTRACE_END(); + texture.inc(); } -//void VulkanDriver::createTextureSwizzledR(Handle th, SamplerType target, uint8_t levels, -// TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, -// TextureUsage usage, -// TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) { -// TextureSwizzle swizzleArray[] = {r, g, b, a}; -// const VkComponentMapping swizzleMap = getSwizzleMap(swizzleArray); -// auto vktexture = mResourceAllocator.construct(th, mPlatform->getDevice(), -// mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator, -// target, levels, format, samples, w, h, depth, usage, mStagePool, -// false /*heap allocated */, swizzleMap); -// mResourceManager.acquire(vktexture); -//} - void VulkanDriver::createTextureViewR(Handle th, Handle srch, uint8_t baseLevel, uint8_t levelCount) { - VulkanTexture const* src = mResourceAllocator.handle_cast(srch); - auto vktexture = mResourceAllocator.construct(th, mPlatform->getDevice(), - mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator, - src, baseLevel, levelCount); - mResourceManager.acquire(vktexture); + auto src = resource_ptr::cast(&mResourceManager, srch); + auto texture = resource_ptr::make(&mResourceManager, th, mPlatform->getDevice(), + mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, src, baseLevel, + levelCount); + texture.inc(); } void VulkanDriver::createTextureViewSwizzleR(Handle th, Handle srch, backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b, backend::TextureSwizzle a) { - TextureSwizzle const swizzleArray[] = {r, g, b, a}; - VkComponentMapping const swizzle = getSwizzleMap(swizzleArray); - - VulkanTexture const* src = mResourceAllocator.handle_cast(srch); - auto vktexture = mResourceAllocator.construct(th, mPlatform->getDevice(), - mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator, - src, swizzle); - mResourceManager.acquire(vktexture); + TextureSwizzle const swizzleArray[] = {r, g, b, a}; + VkComponentMapping const swizzle = getSwizzleMap(swizzleArray); + auto src = resource_ptr::cast(&mResourceManager, srch); + auto texture = resource_ptr::make(&mResourceManager, th, mPlatform->getDevice(), + mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, src, swizzle); + texture.inc(); } void VulkanDriver::createTextureExternalImageR(Handle th, backend::TextureFormat format, @@ -573,39 +563,36 @@ void VulkanDriver::destroyTexture(Handle th) { if (!th) { return; } - auto texture = mResourceAllocator.handle_cast(th); - mResourceManager.release(texture); + auto texture = resource_ptr::cast(&mResourceManager, th); + texture.dec(); } void VulkanDriver::createProgramR(Handle ph, Program&& program) { - FVK_SYSTRACE_CONTEXT(); - FVK_SYSTRACE_START("createProgram"); - auto vkprogram - = mResourceAllocator.construct(ph, mPlatform->getDevice(), program); - mResourceManager.acquire(vkprogram); - FVK_SYSTRACE_END(); + FVK_SYSTRACE_SCOPE(); + auto vprogram = resource_ptr::make(&mResourceManager, ph, mPlatform->getDevice(), + program); + vprogram.inc(); } void VulkanDriver::destroyProgram(Handle ph) { if (!ph) { return; } - auto vkprogram = mResourceAllocator.handle_cast(ph); - mResourceManager.release(vkprogram); + auto vprogram = resource_ptr::cast(&mResourceManager, ph); + vprogram.dec(); } void VulkanDriver::createDefaultRenderTargetR(Handle rth, int) { - assert_invariant(mDefaultRenderTarget == nullptr); - VulkanRenderTarget* renderTarget = mResourceAllocator.construct(rth); + assert_invariant(!mDefaultRenderTarget); + auto renderTarget = resource_ptr::make(&mResourceManager, rth); mDefaultRenderTarget = renderTarget; - mResourceManager.acquire(renderTarget); } void VulkanDriver::createRenderTargetR(Handle rth, TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) { - FVK_SYSTRACE_CONTEXT(); - FVK_SYSTRACE_START("createRenderTarget"); + + FVK_SYSTRACE_SCOPE(); UTILS_UNUSED_IN_RELEASE math::vec2 tmin = {std::numeric_limits::max()}; UTILS_UNUSED_IN_RELEASE math::vec2 tmax = {0}; @@ -615,10 +602,10 @@ void VulkanDriver::createRenderTargetR(Handle rth, for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { if (color[i].handle) { colorTargets[i] = { - .texture = mResourceAllocator.handle_cast(color[i].handle), + .texture = resource_ptr::cast(&mResourceManager, color[i].handle), .level = color[i].level, .layerCount = layerCount, - .layer = color[i].layer, + .layer = (uint8_t) color[i].layer, }; UTILS_UNUSED_IN_RELEASE VkExtent2D extent = colorTargets[i].getExtent2D(); tmin = { std::min(tmin.x, extent.width), std::min(tmin.y, extent.height) }; @@ -630,10 +617,10 @@ void VulkanDriver::createRenderTargetR(Handle rth, VulkanAttachment depthStencil[2] = {}; if (depth.handle) { depthStencil[0] = { - .texture = mResourceAllocator.handle_cast(depth.handle), + .texture = resource_ptr::cast(&mResourceManager, depth.handle), .level = depth.level, .layerCount = layerCount, - .layer = depth.layer, + .layer = (uint8_t) depth.layer, }; UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[0].getExtent2D(); tmin = { std::min(tmin.x, extent.width), std::min(tmin.y, extent.height) }; @@ -643,10 +630,10 @@ void VulkanDriver::createRenderTargetR(Handle rth, if (stencil.handle) { depthStencil[1] = { - .texture = mResourceAllocator.handle_cast(stencil.handle), + .texture = resource_ptr::cast(&mResourceManager, stencil.handle), .level = stencil.level, .layerCount = layerCount, - .layer = stencil.layer, + .layer = (uint8_t) stencil.layer, }; UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[1].getExtent2D(); tmin = { std::min(tmin.x, extent.width), std::min(tmin.y, extent.height) }; @@ -660,13 +647,10 @@ void VulkanDriver::createRenderTargetR(Handle rth, assert_invariant(tmin == tmax); assert_invariant(tmin.x >= width && tmin.y >= height); - auto renderTarget = mResourceAllocator.construct(rth, - mPlatform->getDevice(), mPlatform->getPhysicalDevice(), mContext, mAllocator, - &mCommands, &mResourceAllocator, width, height, samples, colorTargets, depthStencil, - mStagePool, layerCount); - mResourceManager.acquire(renderTarget); - - FVK_SYSTRACE_END(); + auto rt = resource_ptr::make(&mResourceManager, rth, mPlatform->getDevice(), + mPlatform->getPhysicalDevice(), mContext, &mResourceManager, mAllocator, &mCommands, + width, height, samples, colorTargets, depthStencil, mStagePool, layerCount); + rt.inc(); } void VulkanDriver::destroyRenderTarget(Handle rth) { @@ -674,11 +658,12 @@ void VulkanDriver::destroyRenderTarget(Handle rth) { return; } - VulkanRenderTarget* rt = mResourceAllocator.handle_cast(rth); + auto rt = resource_ptr::cast(&mResourceManager, rth); if (UTILS_UNLIKELY(rt == mDefaultRenderTarget)) { - mDefaultRenderTarget = nullptr; + mDefaultRenderTarget = {}; + } else { + rt.dec(); } - mResourceManager.release(rt); } void VulkanDriver::createFenceR(Handle fh, int) { @@ -688,7 +673,11 @@ void VulkanDriver::createFenceR(Handle fh, int) { } else { cmdbuf = &mCommands.get(); } - mResourceAllocator.construct(fh, cmdbuf->getFenceStatus()); + // Note at this point, the fence has already been constructed via createFenceS, so we just tag + // it with appropriate VulkanCmdFence, which is associated with the current, recording command + // buffer. + auto fence = resource_ptr::cast(&mResourceManager, fh); + fence->fence = cmdbuf->getFenceStatus(); } void VulkanDriver::createSwapChainR(Handle sch, void* nativeWindow, uint64_t flags) { @@ -697,16 +686,15 @@ void VulkanDriver::createSwapChainR(Handle sch, void* nativeWindow, << utils::io::endl; flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE); } - auto swapChain = mResourceAllocator.construct(sch, mPlatform, mContext, - mAllocator, &mCommands, &mResourceAllocator, mStagePool, nativeWindow, flags); - mResourceManager.acquire(swapChain); - if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) { if (!isProtectedContentSupported()) { - utils::slog.w << "protected swapchain requested, but Vulkan does not support it" + FVK_LOGW << "protected swapchain requested, but Platform does not support it" << utils::io::endl; } } + auto swapChain = resource_ptr::make(&mResourceManager, sch, mPlatform, + mContext, &mResourceManager, mAllocator, &mCommands, mStagePool, nativeWindow, flags); + swapChain.inc(); } void VulkanDriver::createSwapChainHeadlessR(Handle sch, uint32_t width, @@ -717,10 +705,10 @@ void VulkanDriver::createSwapChainHeadlessR(Handle sch, uint32_t wi flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE); } assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions."); - auto swapChain = mResourceAllocator.construct(sch, mPlatform, mContext, - mAllocator, &mCommands, &mResourceAllocator, mStagePool, - nullptr, flags, VkExtent2D{width, height}); - mResourceManager.acquire(swapChain); + auto swapChain = resource_ptr::make(&mResourceManager, sch, mPlatform, + mContext, &mResourceManager, mAllocator, &mCommands, mStagePool, nullptr, flags, + VkExtent2D{width, height}); + swapChain.inc(); } void VulkanDriver::createTimerQueryR(Handle tqh, int) { @@ -729,123 +717,117 @@ void VulkanDriver::createTimerQueryR(Handle tqh, int) { void VulkanDriver::createDescriptorSetLayoutR(Handle dslh, backend::DescriptorSetLayout&& info) { - VulkanDescriptorSetLayout* layout = mResourceAllocator.construct( - dslh, info); - - // This will create a VkDescriptorSetLayout (which is cached) for this object. + auto layout = resource_ptr::make(&mResourceManager, dslh, info); mDescriptorSetManager.initVkLayout(layout); - mResourceManager.acquire(layout); + layout.inc(); } void VulkanDriver::createDescriptorSetR(Handle dsh, Handle dslh) { - FVK_SYSTRACE_CONTEXT(); - FVK_SYSTRACE_START("createDescriptorSet"); - - auto layout = mResourceAllocator.handle_cast(dslh); - mDescriptorSetManager.createSet(dsh, layout); - - auto set = mResourceAllocator.handle_cast(dsh); - mResourceManager.acquire(set); - - FVK_SYSTRACE_END(); + FVK_SYSTRACE_SCOPE(); + fvkmemory::resource_ptr layout = + fvkmemory::resource_ptr::cast(&mResourceManager, dslh); + auto set = mDescriptorSetManager.createSet(dsh, layout); + set.inc(); } Handle VulkanDriver::createVertexBufferInfoS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createVertexBufferS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createIndexBufferS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createBufferObjectS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createTextureS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createTextureViewS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createTextureViewSwizzleS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createTextureExternalImageS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createTextureExternalImagePlaneS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::importTextureS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createRenderPrimitiveS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createProgramS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createDefaultRenderTargetS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createRenderTargetS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createFenceS() noexcept { - return mResourceAllocator.initHandle(); + auto handle = mResourceManager.allocHandle(); + auto fence = resource_ptr::make(&mResourceManager, handle); + fence.inc(); + return handle; } Handle VulkanDriver::createSwapChainS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createSwapChainHeadlessS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createTimerQueryS() noexcept { // The handle must be constructed here, as a synchronous call to getTimerQueryValue might happen // before createTimerQueryR is executed. - Handle tqh - = mResourceAllocator.initHandle(mTimestamps->getNextQuery()); - auto query = mResourceAllocator.handle_cast(tqh); - mThreadSafeResourceManager.acquire(query); - return tqh; + auto query = resource_ptr::construct(&mResourceManager, + mTimestamps->getNextQuery()); + query.inc(); + return Handle(query.id()); } Handle VulkanDriver::createDescriptorSetLayoutS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } Handle VulkanDriver::createDescriptorSetS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceManager.allocHandle(); } void VulkanDriver::destroySwapChain(Handle sch) { if (!sch) { return; } - VulkanSwapChain* swapChain = mResourceAllocator.handle_cast(sch); + auto swapChain = resource_ptr::cast(&mResourceManager, sch); if (mCurrentSwapChain == swapChain) { - mCurrentSwapChain = nullptr; + mCurrentSwapChain = {}; } - mResourceManager.release(swapChain); + swapChain.dec(); } void VulkanDriver::destroyStream(Handle sh) { @@ -855,19 +837,18 @@ void VulkanDriver::destroyTimerQuery(Handle tqh) { if (!tqh) { return; } - auto vtq = mResourceAllocator.handle_cast(tqh); - mThreadSafeResourceManager.release(vtq); + auto vtq = resource_ptr::cast(&mResourceManager, tqh); + vtq.dec(); } void VulkanDriver::destroyDescriptorSetLayout(Handle dslh) { - VulkanDescriptorSetLayout* layout = mResourceAllocator.handle_cast(dslh); - mResourceManager.release(layout); + auto layout = resource_ptr::cast(&mResourceManager, dslh); + layout.dec(); } void VulkanDriver::destroyDescriptorSet(Handle dsh) { - mDescriptorSetManager.destroySet(dsh); - VulkanDescriptorSet* set = mResourceAllocator.handle_cast(dsh); - mResourceManager.release(set); + auto set = resource_ptr::cast(&mResourceManager, dsh); + set.dec(); } Handle VulkanDriver::createStreamNative(void* nativeStream) { @@ -893,11 +874,14 @@ void VulkanDriver::updateStreams(CommandStream* driver) { } void VulkanDriver::destroyFence(Handle fh) { - mResourceAllocator.destruct(fh); + auto fence = resource_ptr::cast(&mResourceManager, fh); + fence.dec(); } FenceStatus VulkanDriver::getFenceStatus(Handle fh) { - auto& cmdfence = mResourceAllocator.handle_cast(fh)->fence; + auto fence = resource_ptr::cast(&mResourceManager, fh); + + auto& cmdfence = fence->fence; if (!cmdfence) { // If wait is called before a fence actually exists, we return timeout. This matches the // current behavior in OpenGLDriver, but we should eventually reconsider a different error @@ -911,10 +895,11 @@ FenceStatus VulkanDriver::getFenceStatus(Handle fh) { return FenceStatus::CONDITION_SATISFIED; } + // Two other states are possible: - // - VK_INCOMPLETE: the corresponding buffer has not yet been submitted. - // - VK_NOT_READY: the buffer has been submitted but not yet signaled. - // In either case, we return TIMEOUT_EXPIRED to indicate the fence has not been signaled. + // - VK_INCOMPLETE: the corresponding buffer has not yet been submitted. + // - VK_NOT_READY: the buffer has been submitted but not yet signaled. + // In either case, we return TIMEOUT_EXPIRED to indicate the fence has not been signaled. return FenceStatus::TIMEOUT_EXPIRED; } @@ -1100,16 +1085,16 @@ size_t VulkanDriver::getMaxUniformBufferSize() { void VulkanDriver::setVertexBufferObject(Handle vbh, uint32_t index, Handle boh) { - auto vb = mResourceAllocator.handle_cast(vbh); - auto bo = mResourceAllocator.handle_cast(boh); + auto vb = resource_ptr::cast(&mResourceManager, vbh); + auto bo = resource_ptr::cast(&mResourceManager, boh); assert_invariant(bo->bindingType == BufferObjectBinding::VERTEX); - vb->setBuffer(mResourceAllocator, bo, index); + vb->setBuffer(bo, index); } void VulkanDriver::updateIndexBuffer(Handle ibh, BufferDescriptor&& p, uint32_t byteOffset) { VulkanCommandBuffer& commands = mCommands.get(); - auto ib = mResourceAllocator.handle_cast(ibh); + auto ib = resource_ptr::cast(&mResourceManager, ibh); commands.acquire(ib); ib->buffer.loadFromCpu(commands.buffer(), p.buffer, byteOffset, p.size); @@ -1120,7 +1105,7 @@ void VulkanDriver::updateBufferObject(Handle boh, BufferDescript uint32_t byteOffset) { VulkanCommandBuffer& commands = mCommands.get(); - auto bo = mResourceAllocator.handle_cast(boh); + auto bo = resource_ptr::cast(&mResourceManager, boh); commands.acquire(bo); bo->buffer.loadFromCpu(commands.buffer(), bd.buffer, byteOffset, bd.size); @@ -1130,11 +1115,10 @@ void VulkanDriver::updateBufferObject(Handle boh, BufferDescript void VulkanDriver::updateBufferObjectUnsynchronized(Handle boh, BufferDescriptor&& bd, uint32_t byteOffset) { VulkanCommandBuffer& commands = mCommands.get(); - auto bo = mResourceAllocator.handle_cast(boh); + auto bo = resource_ptr::cast(&mResourceManager, boh); commands.acquire(bo); // TODO: implement unsynchronized version bo->buffer.loadFromCpu(commands.buffer(), bd.buffer, byteOffset, bd.size); - mResourceManager.acquire(bo); scheduleDestroy(std::move(bd)); } @@ -1150,8 +1134,8 @@ void VulkanDriver::resetBufferObject(Handle boh) { void VulkanDriver::update3DImage(Handle th, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth, PixelBufferDescriptor&& data) { - mResourceAllocator.handle_cast(th)->updateImage(data, width, height, depth, - xoffset, yoffset, zoffset, level); + auto texture = resource_ptr::cast(&mResourceManager, th); + texture->updateImage(data, width, height, depth, xoffset, yoffset, zoffset, level); scheduleDestroy(std::move(data)); } @@ -1159,7 +1143,7 @@ void VulkanDriver::setupExternalImage(void* image) { } TimerQueryResult VulkanDriver::getTimerQueryValue(Handle tqh, uint64_t* elapsedTime) { - VulkanTimerQuery* vtq = mResourceAllocator.handle_cast(tqh); + auto vtq = resource_ptr::cast(&mResourceManager, tqh); if (!vtq->isCompleted()) { return TimerQueryResult::NOT_READY; } @@ -1197,7 +1181,7 @@ void VulkanDriver::setExternalStream(Handle th, Handle sh) } void VulkanDriver::generateMipmaps(Handle th) { - auto* const t = mResourceAllocator.handle_cast(th); + auto t = resource_ptr::cast(&mResourceManager, th); assert_invariant(t); int32_t layerCount = int32_t(t->depth); @@ -1206,9 +1190,10 @@ void VulkanDriver::generateMipmaps(Handle th) { layerCount *= 6; } + assert_invariant(layerCount < 1 << (sizeof(VulkanAttachment::layerCount) * 8)); + // FIXME: the loop below can perform many layout transitions and back. We should be // able to optimize that. - uint8_t level = 0; int32_t srcw = int32_t(t->width); int32_t srch = int32_t(t->height); @@ -1220,12 +1205,18 @@ void VulkanDriver::generateMipmaps(Handle th) { // TODO: there should be a way to do this using layerCount in vkBlitImage // TODO: vkBlitImage should be able to handle 3D textures too - for (int32_t layer = 0; layer < layerCount; layer++) { - mBlitter.blit(VK_FILTER_LINEAR, - { .texture = t, .level = uint8_t(level + 1), .layer = (uint16_t)layer }, - dstOffsets, - { .texture = t, .level = uint8_t(level ), .layer = (uint16_t)layer }, - srcOffsets); + for (uint8_t layer = 0; layer < layerCount; layer++) { + VulkanAttachment dst { + .level = uint8_t(level + 1), + .layer = layer, + }; + dst.texture = t; + VulkanAttachment src { + .level = uint8_t(level), + .layer = layer, + }; + src.texture = t; + mBlitter.blit(VK_FILTER_LINEAR, dst, dstOffsets, src, srcOffsets); } level++; @@ -1244,11 +1235,12 @@ void VulkanDriver::compilePrograms(CompilerPriorityQueue priority, void VulkanDriver::beginRenderPass(Handle rth, const RenderPassParams& params) { FVK_SYSTRACE_SCOPE(); - VulkanRenderTarget* const rt = mResourceAllocator.handle_cast(rth); - VkExtent2D const& extent = rt->getExtent(); + auto rt = resource_ptr::cast(&mResourceManager, rth); + VkExtent2D const extent = rt->getExtent(); + assert_invariant(rt == mDefaultRenderTarget || extent.width > 0 && extent.height > 0); - VulkanCommandBuffer* commandBuffer = rt->isProtected() ? + VulkanCommandBuffer* commandBuffer = rt->isProtected() ? &mCommands.getProtected() : &mCommands.get(); // Filament has the expectation that the contents of the swap chain are not preserved on the @@ -1256,7 +1248,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP // passes, due to multiple views. TargetBufferFlags discardStart = params.flags.discardStart; if (rt->isSwapChain()) { - VulkanSwapChain* sc = mCurrentSwapChain; + fvkmemory::resource_ptr sc = mCurrentSwapChain; assert_invariant(sc); if (sc->isFirstRenderPass()) { discardStart |= TargetBufferFlags::COLOR; @@ -1295,6 +1287,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP // Create the VkRenderPass or fetch it from cache. + VulkanFboCache::RenderPassKey rpkey = rt->getRenderPassKey(); rpkey.clear = clearVal; rpkey.discardStart = discardStart; @@ -1397,7 +1390,7 @@ void VulkanDriver::endRenderPass(int) { VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer(); vkCmdEndRenderPass(cmdbuffer); - VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget; + auto rt = mCurrentRenderPass.renderTarget; assert_invariant(rt); // Since we might soon be sampling from the render target that we just wrote to, we need a @@ -1405,7 +1398,7 @@ void VulkanDriver::endRenderPass(int) { rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer); mRenderPassFboInfo = {}; - mCurrentRenderPass.renderTarget = nullptr; + mCurrentRenderPass.renderTarget = {}; mCurrentRenderPass.renderPass = VK_NULL_HANDLE; mCurrentRenderPass.commandBuffer = nullptr; @@ -1415,18 +1408,18 @@ void VulkanDriver::nextSubpass(int) { FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.currentSubpass == 0) << "Only two subpasses are currently supported."; - VulkanRenderTarget* renderTarget = mCurrentRenderPass.renderTarget; + auto renderTarget = mCurrentRenderPass.renderTarget; assert_invariant(renderTarget); assert_invariant(mCurrentRenderPass.params.subpassMask); - vkCmdNextSubpass(mCurrentRenderPass.commandBuffer->buffer(), + vkCmdNextSubpass(mCurrentRenderPass.commandBuffer->buffer(), VK_SUBPASS_CONTENTS_INLINE); mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass, ++mCurrentRenderPass.currentSubpass); if (mCurrentRenderPass.params.subpassMask & 0x1) { - VulkanAttachment subpassInput = renderTarget->getColor0(); + VulkanAttachment& subpassInput = renderTarget->getColor0(); mDescriptorSetManager.updateInputAttachment({}, subpassInput); } } @@ -1436,8 +1429,10 @@ void VulkanDriver::makeCurrent(Handle drawSch, Handle ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch, "Vulkan driver does not support distinct draw/read swap chains."); - VulkanSwapChain* swapChain = mCurrentSwapChain - = mResourceAllocator.handle_cast(drawSch); + + resource_ptr swapChain = + resource_ptr::cast(&mResourceManager, drawSch); + mCurrentSwapChain = swapChain; bool resized = false; swapChain->acquire(resized); @@ -1447,14 +1442,14 @@ void VulkanDriver::makeCurrent(Handle drawSch, Handle } if (UTILS_LIKELY(mDefaultRenderTarget)) { - mDefaultRenderTarget->bindToSwapChain(*swapChain); + mDefaultRenderTarget->bindToSwapChain(swapChain); } } void VulkanDriver::commit(Handle sch) { FVK_SYSTRACE_SCOPE(); - VulkanSwapChain* swapChain = mResourceAllocator.handle_cast(sch); + auto swapChain = resource_ptr::cast(&mResourceManager, sch); // Present the backbuffer after the most recent command buffer submission has finished. swapChain->present(); @@ -1463,7 +1458,8 @@ void VulkanDriver::commit(Handle sch) { void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant value) { assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants"); - mBoundPipeline.program->writePushConstant(mCurrentRenderPass.commandBuffer, + assert_invariant(mCurrentRenderPass.commandBuffer && "Should be called within a renderpass"); + mBoundPipeline.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(), mBoundPipeline.pipelineLayout, stage, index, value); } @@ -1496,7 +1492,7 @@ void VulkanDriver::stopCapture(int) {} void VulkanDriver::readPixels(Handle src, uint32_t x, uint32_t y, uint32_t width, uint32_t height, PixelBufferDescriptor&& pbd) { - VulkanRenderTarget* srcTarget = mResourceAllocator.handle_cast(src); + auto srcTarget = resource_ptr::cast(&mResourceManager, src); mCommands.flush(); mReadPixels.run( srcTarget, x, y, width, height, mPlatform->getGraphicsQueueFamilyIndex(), @@ -1523,8 +1519,9 @@ void VulkanDriver::resolve( FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE) << "resolve() cannot be invoked inside a render pass."; - auto* const srcTexture = mResourceAllocator.handle_cast(src); - auto* const dstTexture = mResourceAllocator.handle_cast(dst); + auto srcTexture = resource_ptr::cast(&mResourceManager, src); + auto dstTexture = resource_ptr::cast(&mResourceManager, dst); + assert_invariant(srcTexture); assert_invariant(dstTexture); @@ -1565,8 +1562,8 @@ void VulkanDriver::blit( FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE) << "blit() cannot be invoked inside a render pass."; - auto* const srcTexture = mResourceAllocator.handle_cast(src); - auto* const dstTexture = mResourceAllocator.handle_cast(dst); + auto srcTexture = resource_ptr::cast(&mResourceManager, src); + auto dstTexture = resource_ptr::cast(&mResourceManager, dst); FILAMENT_CHECK_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST)) << "texture doesn't have BLIT_DST"; @@ -1614,8 +1611,8 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers, srcRect.left >= 0 && srcRect.bottom >= 0 && dstRect.left >= 0 && dstRect.bottom >= 0) << "Source and destination rects must be positive."; - VulkanRenderTarget* dstTarget = mResourceAllocator.handle_cast(dst); - VulkanRenderTarget* srcTarget = mResourceAllocator.handle_cast(src); + auto dstTarget = resource_ptr::cast(&mResourceManager, dst); + auto srcTarget = resource_ptr::cast(&mResourceManager, src); VkFilter const vkfilter = (filter == SamplerMagFilter::NEAREST) ? VK_FILTER_NEAREST : VK_FILTER_LINEAR; @@ -1643,19 +1640,19 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers, void VulkanDriver::bindPipeline(PipelineState const& pipelineState) { FVK_SYSTRACE_SCOPE(); - - const VulkanVertexBufferInfo& vbi = - *mResourceAllocator.handle_cast(pipelineState.vertexBufferInfo); + auto commands = mCurrentRenderPass.commandBuffer; + auto vbi = resource_ptr::cast(&mResourceManager, + pipelineState.vertexBufferInfo); Handle programHandle = pipelineState.program; RasterState const& rasterState = pipelineState.rasterState; PolygonOffset const& depthOffset = pipelineState.polygonOffset; - auto* program = mResourceAllocator.handle_cast(programHandle); - mCurrentRenderPass.commandBuffer->acquire(program); + auto program = resource_ptr::cast(&mResourceManager, programHandle); + commands->acquire(program); // Update the VK raster state. - const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget; + auto rt = mCurrentRenderPass.renderTarget; VulkanPipelineCache::RasterState const vulkanRasterState{ .cullMode = getCullMode(rasterState.culling), @@ -1684,14 +1681,14 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) { VulkanPipelineCache::getPrimitiveTopology(pipelineState.primitiveType); // Declare fixed-size arrays that get passed to the pipeCache and to vkCmdBindVertexBuffers. - VkVertexInputAttributeDescription const* attribDesc = vbi.getAttribDescriptions(); - VkVertexInputBindingDescription const* bufferDesc = vbi.getBufferDescriptions(); + 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(topology); - mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi.getAttributeCount()); + mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi->getAttributeCount()); auto& setLayouts = pipelineState.pipelineLayout.setLayout; VulkanDescriptorSetLayout::DescriptorSetLayoutArray layoutList; @@ -1701,7 +1698,8 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) { if (!handle) { return VK_NULL_HANDLE; } - auto layout = mResourceAllocator.handle_cast(handle); + auto layout = + resource_ptr::cast(&mResourceManager, handle); layoutCount++; return layout->getVkLayout(); }); @@ -1722,26 +1720,25 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) { void VulkanDriver::bindRenderPrimitive(Handle rph) { FVK_SYSTRACE_SCOPE(); - VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer(); - const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast(rph); - mCurrentRenderPass.commandBuffer->acquire(prim.indexBuffer); - mCurrentRenderPass.commandBuffer->acquire(prim.vertexBuffer); + VulkanCommandBuffer* commands = mCurrentRenderPass.commandBuffer; + VkCommandBuffer cmdbuffer = commands->buffer(); + auto prim = resource_ptr::cast(&mResourceManager, rph); + commands->acquire(prim); // 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); + auto vbi = prim->vertexBuffer->vbi; uint32_t const bufferCount = vbi->getAttributeCount(); VkDeviceSize const* offsets = vbi->getOffsets(); - VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers(); + 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 // it would be rare for a client to make consecutive draw calls with the same render primitive. vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets); - vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0, - prim.indexBuffer->indexType); + vkCmdBindIndexBuffer(cmdbuffer, prim->indexBuffer->buffer.getGpuBuffer(), 0, + prim->indexBuffer->indexType); } void VulkanDriver::bindDescriptorSet( @@ -1749,7 +1746,7 @@ void VulkanDriver::bindDescriptorSet( backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) { if (dsh) { - VulkanDescriptorSet* set = mResourceAllocator.handle_cast(dsh); + auto set = resource_ptr::cast(&mResourceManager, dsh); mDescriptorSetManager.bind(setIndex, set, std::move(offsets)); } else { mDescriptorSetManager.unbind(setIndex); @@ -1760,7 +1757,7 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins FVK_SYSTRACE_SCOPE(); VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer(); - mDescriptorSetManager.commit(mCurrentRenderPass.commandBuffer, + mDescriptorSetManager.commit(mCurrentRenderPass.commandBuffer, mBoundPipeline.pipelineLayout, mBoundPipeline.descriptorSetMask); @@ -1774,9 +1771,9 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins 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); + auto rp = resource_ptr::cast(&mResourceManager, rph); state.primitiveType = rp->type; - state.vertexBufferInfo = rp->vertexBuffer->vbih; + state.vertexBufferInfo = Handle(rp->vertexBuffer->vbi.id()); bindPipeline(state); bindRenderPrimitive(rph); draw2(indexOffset, indexCount, instanceCount); @@ -1810,19 +1807,19 @@ void VulkanDriver::scissor(Viewport scissorBox) { .extent = { uint32_t(r - l), uint32_t(t - b) } }; - VulkanRenderTarget const* rt = mCurrentRenderPass.renderTarget; + auto rt = mCurrentRenderPass.renderTarget; rt->transformClientRectToPlatform(&scissor); vkCmdSetScissor(cmdbuffer, 0, 1, &scissor); } void VulkanDriver::beginTimerQuery(Handle tqh) { - VulkanTimerQuery* vtq = mResourceAllocator.handle_cast(tqh); + auto vtq = resource_ptr::cast(&mResourceManager, tqh); mTimestamps->beginQuery(&(mCommands.get()), vtq); } void VulkanDriver::endTimerQuery(Handle tqh) { - VulkanTimerQuery* vtq = mResourceAllocator.handle_cast(tqh); - mTimestamps->endQuery(&(mCommands.get()), vtq); + auto vtq = resource_ptr::cast(&mResourceManager, tqh); + mTimestamps->endQuery(&(mCommands.get()), vtq); } void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept { @@ -1854,7 +1851,7 @@ void VulkanDriver::resetState(int) { } void VulkanDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) { - mResourceAllocator.associateHandle(handleId, std::move(tag)); + mResourceManager.associateHandle(handleId, std::move(tag)); } // explicit instantiation of the Dispatcher diff --git a/filament/backend/src/vulkan/VulkanDriver.h b/filament/backend/src/vulkan/VulkanDriver.h index 7dd95209342..e304b8df4a6 100644 --- a/filament/backend/src/vulkan/VulkanDriver.h +++ b/filament/backend/src/vulkan/VulkanDriver.h @@ -24,13 +24,14 @@ #include "VulkanHandles.h" #include "VulkanPipelineCache.h" #include "VulkanReadPixels.h" -#include "VulkanResourceAllocator.h" #include "VulkanSamplerCache.h" #include "VulkanStagePool.h" #include "VulkanUtility.h" #include "backend/DriverEnums.h" #include "caching/VulkanDescriptorSetManager.h" #include "caching/VulkanPipelineLayoutCache.h" +#include "memory/ResourceManager.h" +#include "memory/ResourcePointer.h" #include "DriverBase.h" #include "private/backend/Driver.h" @@ -77,6 +78,9 @@ class VulkanDriver final : public DriverBase { #endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) private: + template + using resource_ptr = fvkmemory::resource_ptr; + static constexpr uint8_t MAX_SAMPLER_BINDING_COUNT = Program::SAMPLER_BINDING_COUNT; void debugCommandBegin(CommandStream* cmds, bool synchronous, @@ -113,21 +117,16 @@ class VulkanDriver final : public DriverBase { void collectGarbage(); VulkanPlatform* mPlatform = nullptr; + fvkmemory::ResourceManager mResourceManager; std::unique_ptr mTimestamps; - VulkanSwapChain* mCurrentSwapChain = nullptr; - VulkanRenderTarget* mDefaultRenderTarget = nullptr; + resource_ptr mCurrentSwapChain; + resource_ptr mDefaultRenderTarget; VulkanRenderPass mCurrentRenderPass = {}; VmaAllocator mAllocator = VK_NULL_HANDLE; VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE; VulkanContext mContext = {}; - VulkanResourceAllocator mResourceAllocator; - VulkanResourceManager mResourceManager; - - // Used for resources that are created synchronously and used and destroyed on the backend - // thread. - VulkanThreadSafeResourceManager mThreadSafeResourceManager; VulkanCommands mCommands; VulkanPipelineLayoutCache mPipelineLayoutCache; @@ -142,7 +141,7 @@ class VulkanDriver final : public DriverBase { // This is necessary for us to write to push constants after binding a pipeline. struct { - VulkanProgram* program; + resource_ptr program; VkPipelineLayout pipelineLayout; DescriptorSetMask descriptorSetMask; } mBoundPipeline = {}; diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index a2df51edc29..dd64779a902 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -22,8 +22,8 @@ #include "VulkanDriver.h" #include "VulkanMemory.h" -#include "VulkanResourceAllocator.h" #include "VulkanUtility.h" +#include "vulkan/memory/ResourcePointer.h" #include "spirv/VulkanSpirvUtils.h" #include @@ -105,9 +105,10 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) { return mask; } -VulkanTexture* initMsaaTexture(VulkanTexture* texture, VkDevice device, +fvkmemory::resource_ptr initMsaaTexture( + fvkmemory::resource_ptr texture, VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator, - VulkanCommands* commands, VulkanResourceAllocator* handleAllocator, uint8_t levels, + VulkanCommands* commands, fvkmemory::ResourceManager* resManager, uint8_t levels, uint8_t samples, VulkanStagePool& stagePool) { assert_invariant(texture); auto msTexture = texture->getSidecar(); @@ -117,10 +118,9 @@ VulkanTexture* initMsaaTexture(VulkanTexture* texture, VkDevice device, const TextureUsage usage = texture->usage & TextureUsage::ALL_ATTACHMENTS; assert_invariant(static_cast(usage) != 0U); - msTexture = new VulkanTexture(device, physicalDevice, context, allocator, commands, - handleAllocator, texture->target, levels, - texture->format, samples, texture->width, texture->height, texture->depth, usage, - stagePool, true /* heap allocated */); + msTexture = resource_ptr::construct(resManager, device, physicalDevice, + context, allocator, resManager, commands, texture->target, levels, texture->format, + samples, texture->width, texture->height, texture->depth, usage, stagePool); texture->setSidecar(msTexture); } return msTexture; @@ -128,19 +128,18 @@ VulkanTexture* initMsaaTexture(VulkanTexture* texture, VkDevice device, } // anonymous namespace -VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout const& layout) - : VulkanResource(VulkanResourceType::DESCRIPTOR_SET_LAYOUT), - bitmask(fromBackendLayout(layout)), - count(Count::fromLayoutBitmask(bitmask)) {} - -void VulkanDescriptorSet::acquire(VulkanTexture* texture) { - mResources.acquire(texture); +void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr texture) { + mResources.push_back(texture); } -void VulkanDescriptorSet::acquire(VulkanBufferObject* bufferObject) { - mResources.acquire(bufferObject); +void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr obj) { + mResources.push_back(obj); } +VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout const& layout) + : bitmask(fromBackendLayout(layout)), + count(Count::fromLayoutBitmask(bitmask)) {} + PushConstantDescription::PushConstantDescription(backend::Program const& program) noexcept { mRangeCount = 0; for (auto stage : { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) { @@ -164,7 +163,7 @@ PushConstantDescription::PushConstantDescription(backend::Program const& program } } -void PushConstantDescription::write(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout, +void PushConstantDescription::write(VkCommandBuffer cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) { uint32_t binaryValue = 0; @@ -182,13 +181,12 @@ void PushConstantDescription::write(VulkanCommandBuffer* cmdbuf, VkPipelineLayou int const ival = std::get(value); binaryValue = *reinterpret_cast(&ival); } - vkCmdPushConstants(cmdbuf->buffer(), layout, getVkStage(stage), index * ENTRY_SIZE, ENTRY_SIZE, + vkCmdPushConstants(cmdbuf, layout, getVkStage(stage), index * ENTRY_SIZE, ENTRY_SIZE, &binaryValue); } VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept : HwProgram(builder.getName()), - VulkanResource(VulkanResourceType::PROGRAM), mInfo(new(std::nothrow) PipelineInfo(builder)), mDevice(device) { @@ -256,23 +254,24 @@ VulkanProgram::~VulkanProgram() { // Creates a special "default" render target (i.e. associated with the swap chain) 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; } -void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) { +VulkanRenderTarget::~VulkanRenderTarget() = default; + +void VulkanRenderTarget::bindToSwapChain(fvkmemory::resource_ptr swapchain) { assert_invariant(!mOffscreen); - VkExtent2D const extent = swapChain.getExtent(); + VkExtent2D const extent = swapchain->getExtent(); width = extent.width; height = extent.height; - mProtected = swapChain.isProtected(); + mProtected = swapchain->isProtected(); - VulkanAttachment color = {.texture = swapChain.getCurrentColor()}; + VulkanAttachment color = {}; + color.texture = swapchain->getCurrentColor(); mInfo->attachments = {color}; auto& fbkey = mInfo->fbkey; @@ -284,33 +283,30 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) { fbkey.color[0] = color.getImageView(); fbkey.resolve[0] = VK_NULL_HANDLE; - VulkanAttachment depth = {}; - rpkey.depthFormat = depth.getFormat(); - fbkey.depth = VK_NULL_HANDLE; - - if (swapChain.getDepth()) { - depth = {.texture = swapChain.getDepth()}; + if (swapchain->getDepth()) { + VulkanAttachment depth = {}; + depth.texture = swapchain->getDepth(); mInfo->attachments.push_back(depth); mInfo->depthIndex = 1; rpkey.depthFormat = depth.getFormat(); fbkey.depth = depth.getImageView(); + } else { + rpkey.depthFormat = VK_FORMAT_UNDEFINED; + fbkey.depth = VK_NULL_HANDLE; } mInfo->colors.set(0); } VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice, - VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, - VulkanResourceAllocator* handleAllocator, uint32_t width, uint32_t height, uint8_t samples, - VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT], + VulkanContext const& context, fvkmemory::ResourceManager* resourceManager, + VmaAllocator allocator, VulkanCommands* commands, uint32_t width, uint32_t height, + uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT], VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount) : HwRenderTarget(width, height), - VulkanResource(VulkanResourceType::RENDER_TARGET), mOffscreen(true), mProtected(false), - mResources(handleAllocator), mInfo(std::make_unique()) { - auto& depth = depthStencil[0]; // Constrain the sample count according to both kinds of sample count masks obtained from @@ -349,14 +345,11 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica fbkey.color[index] = attachment.getImageView(); fbkey.resolve[index] = VK_NULL_HANDLE; - mResources.acquire(attachment.texture); - if (samples > 1) { VulkanAttachment msaaAttachment = {}; if (texture->samples == 1) { auto msaaTexture = initMsaaTexture(texture, device, physicalDevice, context, - allocator, commands, handleAllocator, - ((VulkanTexture const*) texture)->levels, samples, stagePool); + allocator, commands, resourceManager, texture->levels, samples, stagePool); if (msaaTexture && msaaTexture->isTransientAttachment()) { rpkey.usesLazilyAllocatedMemory |= (1 << index); } @@ -377,7 +370,6 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica } fbkey.color[index] = msaaAttachment.getImageView(); msaa.push_back(msaaAttachment); - mResources.acquire(msaaAttachment.texture); } } @@ -390,19 +382,18 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica auto depthTexture = depth.texture; mInfo->depthIndex = (uint8_t) attachments.size(); attachments.push_back(depth); - mResources.acquire(depthTexture); fbkey.depth = depth.getImageView(); if (samples > 1) { mInfo->msaaDepthIndex = mInfo->depthIndex; if (depthTexture->samples == 1) { // MSAA depth texture must have the mipmap count of 1 uint8_t const msLevel = 1; - // Create sidecar MSAA texture for the depth attachment if it does not already exist. - auto msaa = initMsaaTexture(depthTexture, device, physicalDevice, context, allocator, - commands, handleAllocator, msLevel, samples, stagePool); + // Create sidecar MSAA texture for the depth attachment if it does not already + // exist. + auto msaa = initMsaaTexture(depthTexture, device, physicalDevice, context, + allocator, commands, resourceManager, msLevel, samples, stagePool); mInfo->msaaDepthIndex = (uint8_t) attachments.size(); attachments.push_back({ .texture = msaa, .layerCount = layerCount }); - mResources.acquire(msaa); } } } @@ -493,9 +484,7 @@ void VulkanRenderTarget::emitBarriersEndRenderPass(VulkanCommandBuffer& commands 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(); @@ -533,19 +522,15 @@ VulkanVertexBufferInfo::VulkanVertexBufferInfo( } VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, - VulkanResourceAllocator* allocator, - uint32_t vertexCount, Handle vbih) + uint32_t vertexCount, fvkmemory::resource_ptr vbi) : HwVertexBuffer(vertexCount), - VulkanResource(VulkanResourceType::VERTEX_BUFFER), - vbih(vbih), - mBuffers(MAX_VERTEX_BUFFER_COUNT), // TODO: can we do better here? - mResources(allocator) { + vbi(vbi), + // TODO: Seems a bit wasteful. can we do better here? + mBuffers(MAX_VERTEX_BUFFER_COUNT) { } -void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator, - VulkanBufferObject* bufferObject, uint32_t index) { - VulkanVertexBufferInfo const* const vbi = - const_cast(allocator).handle_cast(vbih); +void VulkanVertexBuffer::setBuffer(fvkmemory::resource_ptr bufferObject, + uint32_t index) { size_t const count = vbi->getAttributeCount(); VkBuffer* const vkbuffers = getVkBuffers(); int8_t const* const attribToBuffer = vbi->getAttributeToBuffer(); @@ -554,25 +539,20 @@ void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator, vkbuffers[attribIndex] = bufferObject->buffer.getGpuBuffer(); } } - mResources.acquire(bufferObject); + mResources.push_back(bufferObject); } VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool, uint32_t byteCount, BufferObjectBinding bindingType) : HwBufferObject(byteCount), - VulkanResource(VulkanResourceType::BUFFER_OBJECT), buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount), bindingType(bindingType) {} -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); -} +VulkanRenderPrimitive::VulkanRenderPrimitive(PrimitiveType pt, + fvkmemory::resource_ptr vb, + fvkmemory::resource_ptr ib) + : HwRenderPrimitive{.type = pt}, + vertexBuffer(vb), + indexBuffer(ib) {} } // namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 4b573796c79..32509852283 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -20,14 +20,14 @@ // This needs to be at the top #include "DriverBase.h" +#include "VulkanAsyncHandles.h" #include "VulkanBuffer.h" #include "VulkanFboCache.h" -#include "VulkanResources.h" #include "VulkanSwapChain.h" #include "VulkanTexture.h" #include "VulkanUtility.h" +#include "vulkan/memory/Resource.h" -#include #include #include @@ -35,6 +35,8 @@ #include #include +#include + namespace filament::backend { namespace { @@ -55,7 +57,7 @@ inline uint8_t collapsedCount(Bitmask const& mask) { class VulkanTimestamps; struct VulkanBufferObject; -struct VulkanDescriptorSetLayout : public VulkanResource, HwDescriptorSetLayout { +struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Resource { static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT = 4; static constexpr uint8_t MAX_BINDINGS = 25; @@ -129,21 +131,19 @@ struct VulkanDescriptorSetLayout : public VulkanResource, HwDescriptorSetLayout VkDescriptorSetLayout mVkLayout = VK_NULL_HANDLE; }; -struct VulkanDescriptorSet : public VulkanResource, HwDescriptorSet { +struct VulkanDescriptorSet : public HwDescriptorSet, fvkmemory::Resource { public: // Because we need to recycle descriptor sets not used, we allow for a callback that the "Pool" // can use to repackage the vk handle. using OnRecycle = std::function; - VulkanDescriptorSet(VulkanResourceAllocator* allocator, VkDescriptorSet rawSet, + VulkanDescriptorSet(VkDescriptorSet rawSet, UniformBufferBitmask const& dynamicUboMask, uint8_t uniqueDynamicUboCount, OnRecycle&& onRecycleFn) - : VulkanResource(VulkanResourceType::DESCRIPTOR_SET), - vkSet(rawSet), + : vkSet(rawSet), dynamicUboMask(dynamicUboMask), uniqueDynamicUboCount(uniqueDynamicUboCount), - mResources(allocator), mOnRecycleFn(std::move(onRecycleFn)) {} ~VulkanDescriptorSet() { @@ -160,9 +160,8 @@ struct VulkanDescriptorSet : public VulkanResource, HwDescriptorSet { return &mOffsets; } - void acquire(VulkanTexture* texture); - - void acquire(VulkanBufferObject* texture); + void acquire(fvkmemory::resource_ptr texture); + void acquire(fvkmemory::resource_ptr buffer); VkDescriptorSet const vkSet; UniformBufferBitmask const dynamicUboMask; @@ -170,7 +169,7 @@ struct VulkanDescriptorSet : public VulkanResource, HwDescriptorSet { private: backend::DescriptorSetOffsetArray mOffsets; - VulkanAcquireOnlyResourceManager mResources; + std::vector> mResources; OnRecycle mOnRecycleFn; }; @@ -178,14 +177,11 @@ using PushConstantNameArray = utils::FixedCapacityVector; using PushConstantNameByStage = std::array; struct PushConstantDescription { - explicit PushConstantDescription(backend::Program const& program) noexcept; VkPushConstantRange const* getVkRanges() const noexcept { return mRanges; } - uint32_t getVkRangeCount() const noexcept { return mRangeCount; } - - void write(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage, + void write(VkCommandBuffer cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value); private: @@ -196,12 +192,10 @@ struct PushConstantDescription { uint32_t mRangeCount; }; -struct VulkanProgram : public HwProgram, VulkanResource { - +struct VulkanProgram : public HwProgram, fvkmemory::Resource { using BindingList = CappedArray; VulkanProgram(VkDevice device, Program const& builder) noexcept; - ~VulkanProgram(); inline VkShaderModule getVertexShader() const { @@ -218,7 +212,7 @@ struct VulkanProgram : public HwProgram, VulkanResource { return mInfo->pushConstantDescription.getVkRanges(); } - inline void writePushConstant(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout, + inline void writePushConstant(VkCommandBuffer cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) { mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value); } @@ -255,16 +249,16 @@ struct VulkanProgram : public HwProgram, VulkanResource { // // We use private inheritance to shield clients from the width / height fields in HwRenderTarget, // which are not representative when this is the default render target. -struct VulkanRenderTarget : private HwRenderTarget, VulkanResource { +struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource { // Creates an offscreen render target. VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice, - VulkanContext const& context, VmaAllocator allocator, - VulkanCommands* commands, - VulkanResourceAllocator* handleAllocator, - uint32_t width, uint32_t height, + VulkanContext const& context, fvkmemory::ResourceManager* resourceManager, + VmaAllocator allocator, VulkanCommands* commands, uint32_t width, uint32_t height, uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT], VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount); + ~VulkanRenderTarget(); + // Creates a special "default" render target (i.e. associated with the swap chain) explicit VulkanRenderTarget(); @@ -276,12 +270,12 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource { return {width, height}; } - inline VulkanAttachment const& getColor0() const { + inline VulkanAttachment& getColor0() const { assert_invariant(mInfo->colors[0]); return mInfo->attachments[0]; } - inline VulkanAttachment const& getDepth() const { + inline VulkanAttachment& getDepth() const { assert_invariant(hasDepth()); if (mInfo->fbkey.samples == 1) { return mInfo->attachments[mInfo->depthIndex]; @@ -308,7 +302,7 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource { inline bool isSwapChain() const { return !mOffscreen; } inline bool isProtected() const { return mProtected; } - void bindToSwapChain(VulkanSwapChain& surf); + void bindToSwapChain(fvkmemory::resource_ptr swapchain); void emitBarriersBeginRenderPass(VulkanCommandBuffer& commands); @@ -331,13 +325,12 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource { bool const mOffscreen; bool mProtected; - VulkanAcquireOnlyResourceManager mResources; std::unique_ptr mInfo; }; struct VulkanBufferObject; -struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource { +struct VulkanVertexBufferInfo : public HwVertexBufferInfo, fvkmemory::Resource { VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes); @@ -384,33 +377,24 @@ struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource { 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); +struct VulkanVertexBuffer : public HwVertexBuffer, fvkmemory::Resource { + VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, uint32_t vertexCount, + fvkmemory::resource_ptr vbi); + void setBuffer(fvkmemory::resource_ptr bufferObject, uint32_t index); - inline VkBuffer const* getVkBuffers() const { - return mBuffers.data(); - } - - inline VkBuffer* getVkBuffers() { - return mBuffers.data(); - } + inline VkBuffer const* getVkBuffers() const { return mBuffers.data(); } + inline VkBuffer* getVkBuffers() { return mBuffers.data(); } + fvkmemory::resource_ptr vbi; - Handle vbih; private: utils::FixedCapacityVector mBuffers; - FixedSizeVulkanResourceManager mResources; + std::vector> mResources; }; -struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource { +struct VulkanIndexBuffer : public HwIndexBuffer, fvkmemory::Resource { VulkanIndexBuffer(VmaAllocator allocator, VulkanStagePool& stagePool, uint8_t elementSize, uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount), - VulkanResource(VulkanResourceType::INDEX_BUFFER), buffer(allocator, stagePool, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, elementSize * indexCount), indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32) {} @@ -418,7 +402,7 @@ struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource { const VkIndexType indexType; }; -struct VulkanBufferObject : public HwBufferObject, VulkanResource { +struct VulkanBufferObject : public HwBufferObject, fvkmemory::Resource { VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool, uint32_t byteCount, BufferObjectBinding bindingType); @@ -426,28 +410,13 @@ struct VulkanBufferObject : public HwBufferObject, VulkanResource { const BufferObjectBinding bindingType; }; -struct VulkanSamplerGroup : public HwSamplerGroup, VulkanResource { - // NOTE: we have to use out-of-line allocation here because the size of a Handle<> is limited - std::unique_ptr sb;// FIXME: this shouldn't depend on filament::SamplerGroup - explicit VulkanSamplerGroup(size_t size) noexcept - : VulkanResource(VulkanResourceType::SAMPLER_GROUP), - sb(new SamplerGroup(size)) {} -}; - -struct VulkanRenderPrimitive : public HwRenderPrimitive, VulkanResource { - VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator, - PrimitiveType pt, Handle vbh, Handle ibh); +struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource { + VulkanRenderPrimitive(PrimitiveType pt, fvkmemory::resource_ptr vb, + fvkmemory::resource_ptr ib); + ~VulkanRenderPrimitive() = default; - ~VulkanRenderPrimitive() { - mResources.clear(); - } - - VulkanVertexBuffer* vertexBuffer = nullptr; - VulkanIndexBuffer* indexBuffer = nullptr; - -private: - // Keep references to the vertex buffer and the index buffer. - FixedSizeVulkanResourceManager<2> mResources; + fvkmemory::resource_ptr vertexBuffer; + fvkmemory::resource_ptr indexBuffer; }; inline constexpr VkBufferUsageFlagBits getBufferObjectUsage( diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.cpp b/filament/backend/src/vulkan/VulkanPipelineCache.cpp index dd730fdab73..4cf98cef15a 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.cpp +++ b/filament/backend/src/vulkan/VulkanPipelineCache.cpp @@ -241,7 +241,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value(); } -void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept { +void VulkanPipelineCache::bindProgram(fvkmemory::resource_ptr program) noexcept { mPipelineRequirements.shaders[0] = program->getVertexShader(); mPipelineRequirements.shaders[1] = program->getFragmentShader(); diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.h b/filament/backend/src/vulkan/VulkanPipelineCache.h index 702d27f245f..c0500fbb8be 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.h +++ b/filament/backend/src/vulkan/VulkanPipelineCache.h @@ -19,7 +19,6 @@ #include "VulkanCommands.h" #include "VulkanMemory.h" -#include "VulkanResources.h" #include "VulkanUtility.h" #include @@ -44,7 +43,6 @@ namespace filament::backend { struct VulkanProgram; struct VulkanBufferObject; struct VulkanTexture; -class VulkanResourceAllocator; // VulkanPipelineCache manages a cache of descriptor sets and pipelines. // @@ -122,7 +120,7 @@ class VulkanPipelineCache { void bindPipeline(VulkanCommandBuffer* commands); // Each of the following methods are fast and do not make Vulkan calls. - void bindProgram(VulkanProgram* program) noexcept; + void bindProgram(fvkmemory::resource_ptr program) noexcept; void bindRasterState(const RasterState& rasterState) noexcept; void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept; void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept; diff --git a/filament/backend/src/vulkan/VulkanReadPixels.cpp b/filament/backend/src/vulkan/VulkanReadPixels.cpp index c7d95d96bec..48df6c72227 100644 --- a/filament/backend/src/vulkan/VulkanReadPixels.cpp +++ b/filament/backend/src/vulkan/VulkanReadPixels.cpp @@ -117,9 +117,10 @@ void VulkanReadPixels::terminate() noexcept { VulkanReadPixels::VulkanReadPixels(VkDevice device) : mDevice(device) {} -void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint32_t const y, - uint32_t const width, uint32_t const height, uint32_t const graphicsQueueFamilyIndex, - PixelBufferDescriptor&& pbd, SelecteMemoryFunction const& selectMemoryFunc, +void VulkanReadPixels::run(fvkmemory::resource_ptr srcTarget, uint32_t const x, + uint32_t const y, uint32_t const width, uint32_t const height, + uint32_t const graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd, + SelecteMemoryFunction const& selectMemoryFunc, OnReadCompleteFunction const& readCompleteFunc) { assert_invariant(mDevice != VK_NULL_HANDLE); @@ -143,7 +144,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint VkCommandPool& cmdpool = mCommandPool; - VulkanTexture* srcTexture = srcTarget->getColor0().texture; + fvkmemory::resource_ptr srcTexture = srcTarget->getColor0().texture; assert_invariant(srcTexture); VkFormat const srcFormat = srcTexture->getVkFormat(); bool const swizzle diff --git a/filament/backend/src/vulkan/VulkanReadPixels.h b/filament/backend/src/vulkan/VulkanReadPixels.h index e5dd2a31bb9..23699537d31 100644 --- a/filament/backend/src/vulkan/VulkanReadPixels.h +++ b/filament/backend/src/vulkan/VulkanReadPixels.h @@ -17,6 +17,7 @@ #ifndef TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H #define TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H +#include "vulkan/memory/ResourcePointer.h" #include "private/backend/Driver.h" #include @@ -72,9 +73,9 @@ class VulkanReadPixels { void terminate() noexcept; - void run(VulkanRenderTarget* srcTarget, uint32_t x, uint32_t y, uint32_t width, uint32_t height, - uint32_t graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd, - SelecteMemoryFunction const& selectMemoryFunc, + void run(fvkmemory::resource_ptr srcTarget, uint32_t x, uint32_t y, + uint32_t width, uint32_t height, uint32_t graphicsQueueFamilyIndex, + PixelBufferDescriptor&& pbd, SelecteMemoryFunction const& selectMemoryFunc, OnReadCompleteFunction const& readCompleteFunc); // This method will block until all of the in-flight requests are complete. diff --git a/filament/backend/src/vulkan/VulkanResourceAllocator.h b/filament/backend/src/vulkan/VulkanResourceAllocator.h deleted file mode 100644 index 572aeed4f78..00000000000 --- a/filament/backend/src/vulkan/VulkanResourceAllocator.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -#ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H -#define TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H - -#include "VulkanConstants.h" -#include "VulkanHandles.h" - -#include - -#include -#include - -#include -#include - -namespace filament::backend { - -#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_HEAP_ALLOC_TYPE(obj->getType())) { \ - mDebugOnlyResourceCount[static_cast(obj->getType())]++; \ - } - #define TRACK_DECREMENT() \ - if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \ - mDebugOnlyResourceCount[static_cast(obj->getType())]--; \ - } -#else - // No-op - #define TRACK_INCREMENT() - #define TRACK_DECREMENT() -#endif - -class VulkanResourceAllocator { -public: - using AllocatorImpl = HandleAllocatorVK; - VulkanResourceAllocator(size_t arenaSize, bool disableUseAfterFreeCheck) - : mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck) -#if DEBUG_RESOURCE_LEAKS - , mDebugOnlyResourceCount(RESOURCE_TYPE_COUNT) { - std::memset(mDebugOnlyResourceCount.data(), 0, sizeof(size_t) * RESOURCE_TYPE_COUNT); - } -#else - {} -#endif - - template - inline Handle initHandle(ARGS&&... args) noexcept { - auto handle = mHandleAllocatorImpl.allocateAndConstruct(std::forward(args)...); - auto obj = handle_cast(handle); - obj->initResource(handle.getId()); - TRACK_INCREMENT(); - return handle; - } - - template - inline Handle allocHandle() noexcept { - return mHandleAllocatorImpl.allocate(); - } - - template - inline typename std::enable_if::value, D>::type* construct( - Handle const& handle, ARGS&&... args) noexcept { - auto obj = mHandleAllocatorImpl.construct(handle, std::forward(args)...); - obj->initResource(handle.getId()); - TRACK_INCREMENT(); - return obj; - } - - template - inline typename std::enable_if_t< - std::is_pointer_v && std::is_base_of_v>, Dp> - handle_cast(Handle& handle) noexcept { - return mHandleAllocatorImpl.handle_cast(handle); - } - - template - inline typename std::enable_if_t< - std::is_pointer_v && std::is_base_of_v>, Dp> - handle_cast(Handle const& handle) noexcept { - return mHandleAllocatorImpl.handle_cast(handle); - } - - template - inline void destruct(Handle handle) noexcept { - auto obj = handle_cast(handle); - TRACK_DECREMENT(); - mHandleAllocatorImpl.deallocate(handle, obj); - } - - inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept { - mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag)); - } - -private: - AllocatorImpl mHandleAllocatorImpl; - -#if DEBUG_RESOURCE_LEAKS -public: - void print() { - FVK_LOGD << "Resource Allocator state (debug only)" << utils::io::endl; - for (size_t i = 0; i < RESOURCE_TYPE_COUNT; i++) { - FVK_LOGD << "[" << i << "]=" << mDebugOnlyResourceCount[i] << utils::io::endl; - } - FVK_LOGD << "+++++++++++++++++++++++++++++++++++++" << utils::io::endl; - } -private: - utils::FixedCapacityVector mDebugOnlyResourceCount; -#endif - -}; - -#undef TRACK_INCREMENT -#undef TRACK_DECREMENT -#undef DEBUG_RESOURCE_LEAKS - -} // namespace filament::backend - -#endif // TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H diff --git a/filament/backend/src/vulkan/VulkanResources.cpp b/filament/backend/src/vulkan/VulkanResources.cpp deleted file mode 100644 index deb3a3168e6..00000000000 --- a/filament/backend/src/vulkan/VulkanResources.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "VulkanResources.h" -#include "VulkanHandles.h" -#include "VulkanResourceAllocator.h" -#include "VulkanPipelineCache.h" - -namespace filament::backend { - -void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType type, - HandleBase::HandleId id) { - - if (IS_HEAP_ALLOC_TYPE(type)) { - return; - } - - switch (type) { - case VulkanResourceType::BUFFER_OBJECT: - allocator->destruct(Handle(id)); - break; - case VulkanResourceType::INDEX_BUFFER: - allocator->destruct(Handle(id)); - break; - case VulkanResourceType::PROGRAM: - allocator->destruct(Handle(id)); - break; - case VulkanResourceType::RENDER_TARGET: - allocator->destruct(Handle(id)); - break; - case VulkanResourceType::SAMPLER_GROUP: - allocator->destruct(Handle(id)); - break; - case VulkanResourceType::SWAP_CHAIN: - allocator->destruct(Handle(id)); - break; - case VulkanResourceType::TEXTURE: - allocator->destruct(Handle(id)); - break; - case VulkanResourceType::TIMER_QUERY: - allocator->destruct(Handle(id)); - break; - case VulkanResourceType::VERTEX_BUFFER_INFO: - allocator->destruct(Handle(id)); - break; - case VulkanResourceType::VERTEX_BUFFER: - allocator->destruct(Handle(id)); - break; - case VulkanResourceType::RENDER_PRIMITIVE: - allocator->destruct(Handle(id)); - break; - case VulkanResourceType::DESCRIPTOR_SET_LAYOUT: - allocator->destruct(Handle(id)); - break; - case VulkanResourceType::DESCRIPTOR_SET: - allocator->destruct(Handle(id)); - break; - - // If the resource is heap allocated, then the resource manager just skip refcounted - // destruction. - case VulkanResourceType::FENCE: - case VulkanResourceType::HEAP_ALLOCATED: - case VulkanResourceType::END_TYPE: - break; - } -} - -} // namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanResources.h b/filament/backend/src/vulkan/VulkanResources.h deleted file mode 100644 index cbfb70ab608..00000000000 --- a/filament/backend/src/vulkan/VulkanResources.h +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (C) 2023 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. - */ - -#ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCES_H -#define TNT_FILAMENT_BACKEND_VULKANRESOURCES_H - -#include "VulkanUtility.h" - -#include - -#include -#include -#include - -#include -#include - -namespace filament::backend { - -class VulkanResourceAllocator; -struct VulkanThreadSafeResource; - -// Subclasses of VulkanResource must provide this enum in their construction. -enum class VulkanResourceType : uint8_t { - BUFFER_OBJECT = 0, - INDEX_BUFFER = 1, - PROGRAM = 2, - RENDER_TARGET = 3, - SAMPLER_GROUP = 4, - SWAP_CHAIN = 5, - RENDER_PRIMITIVE = 6, - TEXTURE = 7, - TIMER_QUERY = 8, - VERTEX_BUFFER = 9, - VERTEX_BUFFER_INFO = 10, - DESCRIPTOR_SET_LAYOUT = 11, - DESCRIPTOR_SET = 12, - - // Below are resources that are managed manually (i.e. not ref counted). - FENCE = 13, - HEAP_ALLOCATED = 14, - - END_TYPE = 15, // A placeholder -}; - -#define IS_HEAP_ALLOC_TYPE(f) \ - (f == VulkanResourceType::FENCE || f == VulkanResourceType::HEAP_ALLOCATED) - - -// This is a ref-counting base class that tracks how many references of this resource exist. This -// class is paired with VulkanResourceManagerImpl which is responsible for incrementing or -// decrementing the count. Once mRefCount == 0, VulkanResourceManagerImpl will also call the -// appropriate destructor. VulkanCommandBuffer, VulkanDriver, and composite structure like -// VulkanRenderPrimitive are owners of VulkanResourceManagerImpl instances. -struct VulkanResourceBase { -protected: - explicit VulkanResourceBase(VulkanResourceType type) - : mRefCount(IS_HEAP_ALLOC_TYPE(type) ? 1 : 0), - mType(uint32_t(type)), - mHandleId(0) { - } - -private: - inline VulkanResourceType getType() const noexcept { - return VulkanResourceType(mType); - } - - inline HandleBase::HandleId getId() const noexcept { - return mHandleId; - } - - inline void initResource(HandleBase::HandleId id) noexcept { - mHandleId = id; - } - - inline void ref() noexcept { - if (IS_HEAP_ALLOC_TYPE(getType())) { - return; - } - assert_invariant(mRefCount < ((1<<24) - 1)); - ++mRefCount; - } - - inline void deref() noexcept { - if (IS_HEAP_ALLOC_TYPE(getType())) { - return; - } - assert_invariant(mRefCount > 0); - --mRefCount; - } - - inline size_t refcount() const noexcept { - return mRefCount; - } - - uint32_t mRefCount : 24; // 16M is enough for the refcount - uint32_t mType : 8; // must be uint32_t or MSVC doesn't pack it. no codegen impact w/ clang. - HandleBase::HandleId mHandleId; - - friend struct VulkanThreadSafeResource; - friend class VulkanResourceAllocator; - - template - friend class VulkanResourceManagerImpl; -}; - -static_assert(sizeof(VulkanResourceBase) == 8, "VulkanResourceBase should be 8 bytes"); - -struct VulkanThreadSafeResource { -protected: - explicit VulkanThreadSafeResource(VulkanResourceType type) - : mImpl(type) {} - -private: - inline VulkanResourceType getType() { - return mImpl.getType(); - } - - inline HandleBase::HandleId getId() { - return mImpl.getId(); - } - - inline void initResource(HandleBase::HandleId id) noexcept { - std::unique_lock lock(mMutex); - mImpl.initResource(id); - } - - inline void ref() noexcept { - std::unique_lock lock(mMutex); - mImpl.ref(); - } - - inline void deref() noexcept { - std::unique_lock lock(mMutex); - mImpl.deref(); - } - - inline size_t refcount() noexcept { - std::unique_lock lock(mMutex); - return mImpl.refcount(); - } - - utils::Mutex mMutex; - VulkanResourceBase mImpl; - - friend class VulkanResourceAllocator; - template - friend class VulkanResourceManagerImpl; -}; - -using VulkanResource = VulkanResourceBase; - -namespace { - -// When the size of the resource set is known to be small, (for example for VulkanRenderPrimitive), -// we just use a std::array to back the set. -template -using FixedCapacityResourceSet = CappedArray; - -// robin_set/map are useful for sets that are acquire only and the set will be iterated when the set -// is cleared. -using FastIterationResourceSet = tsl::robin_set; - -// unoredered_set is used in the general case where insert/erase can occur at will. This is useful -// for the basic object ownership count - i.e. VulkanDriver. -using ResourceSet = std::unordered_set; - -using ThreadSafeResourceSet = std::unordered_set; - -} // anonymous namespace - -class VulkanResourceAllocator; - -#define LOCK_IF_NEEDED() \ - if constexpr (std::is_base_of_v) { \ - mMutex->lock(); \ - } - -#define UNLOCK_IF_NEEDED() \ - if constexpr (std::is_base_of_v) { \ - mMutex->unlock(); \ - } - -void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType type, - HandleBase::HandleId id); - -template -class VulkanResourceManagerImpl { -public: - explicit VulkanResourceManagerImpl(VulkanResourceAllocator* allocator) - : mAllocator(allocator) { - if constexpr (std::is_base_of_v) { - mMutex = std::make_unique(); - } - } - - VulkanResourceManagerImpl(const VulkanResourceManagerImpl& other) = delete; - void operator=(const VulkanResourceManagerImpl& other) = delete; - VulkanResourceManagerImpl(const VulkanResourceManagerImpl&& other) = delete; - void operator=(const VulkanResourceManagerImpl&& other) = delete; - - ~VulkanResourceManagerImpl() { - clear(); - } - - inline void acquire(ResourceType* resource) { - if (IS_HEAP_ALLOC_TYPE(resource->getType())) { - return; - } - - LOCK_IF_NEEDED(); - if (mResources.find(resource) != mResources.end()) { - UNLOCK_IF_NEEDED(); - return; - } - mResources.insert(resource); - UNLOCK_IF_NEEDED(); - resource->ref(); - } - - // Transfers ownership from one resource set to another - template - inline void acquireAll(VulkanResourceManagerImpl* srcResources) { - copyAll(srcResources); - srcResources->clear(); - } - - // Transfers ownership from one resource set to another - template - inline void copyAll(VulkanResourceManagerImpl* srcResources) { - LOCK_IF_NEEDED(); - for (auto iter = srcResources->mResources.begin(); iter != srcResources->mResources.end(); - iter++) { - acquire(*iter); - } - UNLOCK_IF_NEEDED(); - } - - inline void release(ResourceType* resource) { - if (IS_HEAP_ALLOC_TYPE(resource->getType())) { - return; - } - - LOCK_IF_NEEDED(); - auto resItr = mResources.find(resource); - if (resItr == mResources.end()) { - UNLOCK_IF_NEEDED(); - return; - } - mResources.erase(resItr); - UNLOCK_IF_NEEDED(); - derefImpl(resource); - } - - inline void clear() { - LOCK_IF_NEEDED(); - for (auto iter = mResources.begin(); iter != mResources.end(); iter++) { - derefImpl(*iter); - } - mResources.clear(); - UNLOCK_IF_NEEDED(); - } - - inline size_t size() { - return mResources.size(); - } - -private: - inline void derefImpl(ResourceType* resource) { - resource->deref(); - if (resource->refcount() != 0) { - return; - } - deallocateResource(mAllocator, resource->getType(), resource->getId()); - } - - VulkanResourceAllocator* mAllocator; - SetType mResources; - std::unique_ptr mMutex; - - template friend class VulkanResourceManagerImpl; -}; - -using VulkanAcquireOnlyResourceManager - = VulkanResourceManagerImpl; -using VulkanResourceManager = VulkanResourceManagerImpl; - -template -using FixedSizeVulkanResourceManager = - VulkanResourceManagerImpl>; - -using VulkanThreadSafeResourceManager - = VulkanResourceManagerImpl; - -#undef LOCK_IF_NEEDED -#undef UNLOCK_IF_NEEDED - -} // namespace filament::backend - -#endif // TNT_FILAMENT_BACKEND_VULKANRESOURCES_H diff --git a/filament/backend/src/vulkan/VulkanStagePool.cpp b/filament/backend/src/vulkan/VulkanStagePool.cpp index 8fb2abf79c2..cae1e85d257 100644 --- a/filament/backend/src/vulkan/VulkanStagePool.cpp +++ b/filament/backend/src/vulkan/VulkanStagePool.cpp @@ -16,6 +16,7 @@ #include "VulkanStagePool.h" +#include "VulkanCommands.h" #include "VulkanConstants.h" #include "VulkanImageUtility.h" #include "VulkanMemory.h" @@ -60,7 +61,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) { UTILS_UNUSED_IN_RELEASE VkResult result = vmaCreateBuffer(mAllocator, &bufferInfo, &allocInfo, &stage->buffer, &stage->memory, nullptr); -#if FVK_ENABLED(FVK_DEBUG_ALLOCATION) +#if FVK_ENABLED(FVK_DEBUG_STAGING_ALLOCATION) if (result != VK_SUCCESS) { FVK_LOGE << "Allocation error: " << result << utils::io::endl; } diff --git a/filament/backend/src/vulkan/VulkanStagePool.h b/filament/backend/src/vulkan/VulkanStagePool.h index d2aacae5013..6c8e307ff3f 100644 --- a/filament/backend/src/vulkan/VulkanStagePool.h +++ b/filament/backend/src/vulkan/VulkanStagePool.h @@ -17,8 +17,7 @@ #ifndef TNT_FILAMENT_BACKEND_VULKANSTAGEPOOL_H #define TNT_FILAMENT_BACKEND_VULKANSTAGEPOOL_H -#include "VulkanCommands.h" -#include "VulkanContext.h" +#include "backend/DriverEnums.h" #include "VulkanMemory.h" #include @@ -26,6 +25,8 @@ namespace filament::backend { +class VulkanCommands; + // Immutable POD representing a shared CPU-GPU staging area. struct VulkanStage { VmaAllocation memory; diff --git a/filament/backend/src/vulkan/VulkanSwapChain.cpp b/filament/backend/src/vulkan/VulkanSwapChain.cpp index bf9fd353cd8..c1df76e76db 100644 --- a/filament/backend/src/vulkan/VulkanSwapChain.cpp +++ b/filament/backend/src/vulkan/VulkanSwapChain.cpp @@ -15,6 +15,8 @@ */ #include "VulkanSwapChain.h" + +#include "VulkanCommands.h" #include "VulkanTexture.h" #include @@ -26,19 +28,18 @@ using namespace utils; namespace filament::backend { VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context, - VmaAllocator allocator, VulkanCommands* commands, VulkanResourceAllocator* handleAllocator, - VulkanStagePool& stagePool, - void* nativeWindow, uint64_t flags, VkExtent2D extent) - : VulkanResource(VulkanResourceType::SWAP_CHAIN), - mPlatform(platform), + fvkmemory::ResourceManager* resourceManager, VmaAllocator allocator, + VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow, uint64_t flags, + VkExtent2D extent) + : mPlatform(platform), + mResourceManager(resourceManager), mCommands(commands), mAllocator(allocator), - mHandleAllocator(handleAllocator), mStagePool(stagePool), mHeadless(extent.width != 0 && extent.height != 0 && !nativeWindow), mFlushAndWaitOnResize(platform->getCustomization().flushAndWaitOnWindowResize), mTransitionSwapChainImageLayoutForPresent( - platform->getCustomization().transitionSwapChainImageLayoutForPresent), + platform->getCustomization().transitionSwapChainImageLayoutForPresent), mAcquired(false), mIsFirstRenderPass(true) { swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent); @@ -53,6 +54,9 @@ VulkanSwapChain::~VulkanSwapChain() { mCommands->flush(); mCommands->wait(); + mColors = {}; + mDepth = {}; + mPlatform->destroy(swapChain); } @@ -70,13 +74,16 @@ void VulkanSwapChain::update() { 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, - colorUsage, mStagePool, true /* heap allocated */)); + auto colorTexture = fvkmemory::resource_ptr::construct(mResourceManager, + device, mAllocator, mResourceManager, mCommands, color, bundle.colorFormat, 1, + bundle.extent.width, bundle.extent.height, TextureUsage::COLOR_ATTACHMENT, + mStagePool); + mColors.push_back(colorTexture); } - mDepth = std::make_unique(device, mAllocator, mCommands, mHandleAllocator, - bundle.depth, bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height, - depthUsage, mStagePool, true /* heap allocated */); + + mDepth = fvkmemory::resource_ptr::construct(mResourceManager, device, mAllocator, + mResourceManager, mCommands, bundle.depth, bundle.depthFormat, 1, bundle.extent.width, + bundle.extent.height, TextureUsage::DEPTH_ATTACHMENT, mStagePool); mExtent = bundle.extent; } @@ -95,7 +102,7 @@ void VulkanSwapChain::present() { } mCommands->flush(); - + // call the image ready wait function if (mExplicitImageReadyWait != nullptr) { mExplicitImageReadyWait(swapChain); @@ -135,7 +142,7 @@ void VulkanSwapChain::acquire(bool& resized) { VkResult const result = mPlatform->acquire(swapChain, &imageSyncData); mCurrentSwapIndex = imageSyncData.imageIndex; mExplicitImageReadyWait = imageSyncData.explicitImageReadyWait; - FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) + FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) << "Cannot acquire in swapchain."; if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) { mCommands->injectDependency(imageSyncData.imageReadySemaphore); diff --git a/filament/backend/src/vulkan/VulkanSwapChain.h b/filament/backend/src/vulkan/VulkanSwapChain.h index acb88c56b95..bf1c26945bb 100644 --- a/filament/backend/src/vulkan/VulkanSwapChain.h +++ b/filament/backend/src/vulkan/VulkanSwapChain.h @@ -19,9 +19,9 @@ #include "DriverBase.h" -#include "VulkanCommands.h" #include "VulkanContext.h" -#include "VulkanResources.h" +#include "VulkanTexture.h" +#include "vulkan/memory/Resource.h" #include @@ -36,14 +36,14 @@ namespace filament::backend { struct VulkanHeadlessSwapChain; struct VulkanSurfaceSwapChain; -class VulkanResourceAllocator; +class VulkanCommands; // A wrapper around the platform implementation of swapchain. -struct VulkanSwapChain : public HwSwapChain, VulkanResource { - VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context, VmaAllocator allocator, - VulkanCommands* commands, VulkanResourceAllocator* handleAllocator, - VulkanStagePool& stagePool, - void* nativeWindow, uint64_t flags, VkExtent2D extent = {0, 0}); +struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource { + VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context, + fvkmemory::ResourceManager* resourceManager, VmaAllocator allocator, + VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow, + uint64_t flags, VkExtent2D extent = {0, 0}); ~VulkanSwapChain(); @@ -51,15 +51,15 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource { void acquire(bool& reized); - inline VulkanTexture* getCurrentColor() const noexcept { + fvkmemory::resource_ptr getCurrentColor() const noexcept { uint32_t const imageIndex = mCurrentSwapIndex; FILAMENT_CHECK_PRECONDITION( imageIndex != VulkanPlatform::ImageSyncData::INVALID_IMAGE_INDEX); - return mColors[imageIndex].get(); + return mColors[imageIndex]; } - inline VulkanTexture* getDepth() const noexcept { - return mDepth.get(); + inline fvkmemory::resource_ptr getDepth() const noexcept { + return mDepth; } inline bool isFirstRenderPass() const noexcept { @@ -83,9 +83,9 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource { void update(); VulkanPlatform* mPlatform; + fvkmemory::ResourceManager* mResourceManager; VulkanCommands* mCommands; VmaAllocator mAllocator; - VulkanResourceAllocator* const mHandleAllocator; VulkanStagePool& mStagePool; bool const mHeadless; bool const mFlushAndWaitOnResize; @@ -93,8 +93,8 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource { // We create VulkanTextures based on VkImages. VulkanTexture has facilities for doing layout // transitions, which are useful here. - utils::FixedCapacityVector> mColors; - std::unique_ptr mDepth; + utils::FixedCapacityVector> mColors; + fvkmemory::resource_ptr mDepth; VkExtent2D mExtent; uint32_t mCurrentSwapIndex; std::function mExplicitImageReadyWait = nullptr; diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index a526310118e..105ae8072d4 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -14,10 +14,11 @@ * limitations under the License. */ +#include "VulkanCommands.h" #include "VulkanMemory.h" -#include "VulkanResourceAllocator.h" #include "VulkanTexture.h" #include "VulkanUtility.h" +#include "vulkan/memory/ResourcePointer.h" #include #include @@ -149,8 +150,7 @@ VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected) - : VulkanResource(VulkanResourceType::HEAP_ALLOCATED), - mVkFormat(format), + : mVkFormat(format), mViewType(viewType), mFullViewRange{filament::backend::getImageAspect(format), 0, levels, 0, layerCount}, mDefaultLayout(defaultLayout), @@ -161,61 +161,42 @@ VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator, mCommands(commands), mIsTransientAttachment(false) {} -VulkanTextureState* VulkanTexture::getSharedState() { - VulkanTextureState* state = mAllocator->handle_cast(mState); - return state; -} - -VulkanTextureState const* VulkanTexture::getSharedState() const { - VulkanTextureState const* state = mAllocator->handle_cast(mState); - return state; -} - // Constructor for internally passed VkImage -VulkanTexture::VulkanTexture( - VkDevice device, VmaAllocator allocator, VulkanCommands* commands, - VulkanResourceAllocator* handleAllocator, - VkImage image, VkFormat format, uint8_t samples, uint32_t width, uint32_t height, - TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated) - : HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED, - tusage), - VulkanResource( - heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE), - mAllocator(handleAllocator), - mState(handleAllocator->initHandle( - device, allocator, commands, stagePool, - format, imgutil::getViewType(SamplerType::SAMPLER_2D), 1, 1, - getDefaultLayoutImpl(tusage), - any(usage& TextureUsage::PROTECTED))) { - auto* const state = getSharedState(); - state->mTextureImage = image; - mPrimaryViewRange = state->mFullViewRange; +VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator, + fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image, + VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage, + VulkanStagePool& stagePool) + : HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED, + tusage), + mState(fvkmemory::resource_ptr::construct(resourceManager, device, + allocator, commands, stagePool, format, imgutil::getViewType(SamplerType::SAMPLER_2D), + 1, 1, getDefaultLayoutImpl(tusage), any(usage & TextureUsage::PROTECTED))) { + mState->mTextureImage = image; + mPrimaryViewRange = mState->mFullViewRange; } // Constructor for user facing texture VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, - VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, - VulkanResourceAllocator* handleAllocator, SamplerType target, uint8_t levels, - TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, - TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated) + VulkanContext const& context, VmaAllocator allocator, + fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, SamplerType target, + uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, + uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool) : HwTexture(target, levels, samples, w, h, depth, tformat, tusage), - VulkanResource( - heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE), - mAllocator(handleAllocator), - mState(handleAllocator->initHandle(device, allocator, commands, stagePool, - backend::getVkFormat(tformat), imgutil::getViewType(target), levels, - getLayerCount(target, depth), VulkanLayout::UNDEFINED, any(usage& TextureUsage::PROTECTED))) { - auto* const state = getSharedState(); - + mState(fvkmemory::resource_ptr::construct(resourceManager, device, + allocator, commands, stagePool, backend::getVkFormat(tformat), + imgutil::getViewType(target), levels, getLayerCount(target, depth), + VulkanLayout::UNDEFINED, any(usage & TextureUsage::PROTECTED))) { // Create an appropriately-sized device-only VkImage, but do not fill it yet. - VkImageCreateInfo imageInfo{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D, - .format = state->mVkFormat, - .extent = {w, h, depth}, - .mipLevels = levels, - .arrayLayers = 1, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = 0}; + VkImageCreateInfo imageInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D, + .format = mState->mVkFormat, + .extent = {w, h, depth}, + .mipLevels = levels, + .arrayLayers = 1, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = 0, + }; if (target == SamplerType::SAMPLER_CUBEMAP) { imageInfo.arrayLayers = 6; imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; @@ -254,7 +235,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, none(tusage & ~TextureUsage::ALL_ATTACHMENTS) && // Usage contains at least one attachment flag. any(tusage & TextureUsage::ALL_ATTACHMENTS); - state->mIsTransientAttachment = useTransientAttachment; + mState->mIsTransientAttachment = useTransientAttachment; const VkImageUsageFlags transientFlag = useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U; @@ -299,7 +280,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, // any kind of attachment (color or depth). const auto& limits = context.getPhysicalDeviceLimits(); if (imageInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) { - samples = reduceSampleCount(samples, isVkDepthFormat(state->mVkFormat) + samples = reduceSampleCount(samples, isVkDepthFormat(mState->mVkFormat) ? limits.sampledImageDepthSampleCounts : limits.sampledImageColorSampleCounts); } @@ -313,12 +294,12 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, this->samples = samples; imageInfo.samples = (VkSampleCountFlagBits) samples; - VkResult error = vkCreateImage(state->mDevice, &imageInfo, VKALLOC, &state->mTextureImage); + VkResult error = vkCreateImage(mState->mDevice, &imageInfo, VKALLOC, &mState->mTextureImage); if (error || FVK_ENABLED(FVK_DEBUG_TEXTURE)) { FVK_LOGD << "vkCreateImage: " - << "image = " << state->mTextureImage << ", " + << "image = " << mState->mTextureImage << ", " << "result = " << error << ", " - << "handle = " << utils::io::hex << state->mTextureImage << utils::io::dec << ", " + << "handle = " << utils::io::hex << mState->mTextureImage << utils::io::dec << ", " << "extent = " << w << "x" << h << "x"<< depth << ", " << "mipLevels = " << int(levels) << ", " << "TextureUsage = " << static_cast(usage) << ", " @@ -327,18 +308,18 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, << "type = " << imageInfo.imageType << ", " << "flags = " << imageInfo.flags << ", " << "target = " << static_cast(target) <<", " - << "format = " << state->mVkFormat << utils::io::endl; + << "format = " << mState->mVkFormat << utils::io::endl; } FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create image."; // Allocate memory for the VkImage and bind it. VkMemoryRequirements memReqs = {}; - vkGetImageMemoryRequirements(state->mDevice, state->mTextureImage, &memReqs); + vkGetImageMemoryRequirements(mState->mDevice, mState->mTextureImage, &memReqs); const VkFlags requiredMemoryFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | (useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U) | - (state->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U); + (mState->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U); uint32_t memoryTypeIndex = context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags); @@ -350,37 +331,29 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, .allocationSize = memReqs.size, .memoryTypeIndex = memoryTypeIndex, }; - error = vkAllocateMemory(state->mDevice, &allocInfo, nullptr, &state->mTextureImageMemory); + error = vkAllocateMemory(mState->mDevice, &allocInfo, nullptr, &mState->mTextureImageMemory); FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to allocate image memory."; - error = vkBindImageMemory(state->mDevice, state->mTextureImage, state->mTextureImageMemory, 0); + error = vkBindImageMemory(mState->mDevice, mState->mTextureImage, mState->mTextureImageMemory, + 0); FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to bind image."; // Spec out the "primary" VkImageView that shaders use to sample from the image. - mPrimaryViewRange = state->mFullViewRange; + mPrimaryViewRange = mState->mFullViewRange; // Go ahead and create the primary image view. - getImageView(mPrimaryViewRange, state->mViewType, mSwizzle); + getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle); - VulkanCommandBuffer& commandsBuf = state->mCommands->get(); - commandsBuf.acquire(this); - - auto const defaultLayout = state->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage); - transitionLayout(&commandsBuf, mPrimaryViewRange, defaultLayout); + mState->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage); } // Constructor for creating a texture view VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, - VulkanResourceAllocator* handleAllocator, VulkanTexture const* src, uint8_t baseLevel, + fvkmemory::resource_ptr src, uint8_t baseLevel, uint8_t levelCount) : HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth, - src->format, src->usage), - VulkanResource(VulkanResourceType::TEXTURE), - mAllocator(handleAllocator) { + src->format, src->usage) { mState = src->mState; - auto* state = getSharedState(); - - state->refs++; mPrimaryViewRange = src->mPrimaryViewRange; mPrimaryViewRange.baseMipLevel = src->mPrimaryViewRange.baseMipLevel + baseLevel; mPrimaryViewRange.levelCount = levelCount; @@ -389,31 +362,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, // Constructor for creating a texture view with swizzle VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, - VulkanResourceAllocator* handleAllocator, VulkanTexture const* src, - VkComponentMapping swizzle) + fvkmemory::resource_ptr src, VkComponentMapping swizzle) : HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth, - src->format, src->usage), - VulkanResource(VulkanResourceType::TEXTURE), - mAllocator(handleAllocator) { + src->format, src->usage) { mState = src->mState; - auto* state = getSharedState(); - state->refs++; mPrimaryViewRange = src->mPrimaryViewRange; mSwizzle = composeSwizzle(src->mSwizzle, swizzle); } -VulkanTexture::~VulkanTexture() { - auto* const state = getSharedState(); - state->refs--; - if (state->refs == 0) { - if (state->mTextureImageMemory != VK_NULL_HANDLE) { - vkDestroyImage(state->mDevice, state->mTextureImage, VKALLOC); - vkFreeMemory(state->mDevice, state->mTextureImageMemory, VKALLOC); - } - for (auto entry: state->mCachedImageViews) { - vkDestroyImageView(state->mDevice, entry.second, VKALLOC); - } - mAllocator->destruct(mState); +VulkanTextureState::~VulkanTextureState() { + if (mTextureImageMemory != VK_NULL_HANDLE) { + vkDestroyImage(mDevice, mTextureImage, VKALLOC); + vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC); + } + for (auto entry: mCachedImageViews) { + vkDestroyImageView(mDevice, entry.second, VKALLOC); } } @@ -422,8 +385,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt assert_invariant(width <= this->width && height <= this->height); 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); + assert_invariant(!mState->mIsProtected); const PixelBufferDescriptor* hostData = &data; PixelBufferDescriptor reshapedData; @@ -436,7 +398,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt // If format conversion is both required and supported, use vkCmdBlitImage. const VkFormat hostFormat = backend::getVkFormat(hostData->format, hostData->type); - const VkFormat deviceFormat = getVkFormatLinear(state->mVkFormat); + const VkFormat deviceFormat = getVkFormatLinear(mState->mVkFormat); if (hostFormat != deviceFormat && hostFormat != VK_FORMAT_UNDEFINED) { assert_invariant(xoffset == 0 && yoffset == 0 && zoffset == 0 && "Offsets not yet supported when format conversion is required."); @@ -448,16 +410,16 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt // Otherwise, use vkCmdCopyBufferToImage. void* mapped = nullptr; - VulkanStage const* stage = state->mStagePool.acquireStage(hostData->size); + VulkanStage const* stage = mState->mStagePool.acquireStage(hostData->size); assert_invariant(stage->memory); - vmaMapMemory(state->mAllocator, stage->memory, &mapped); + vmaMapMemory(mState->mAllocator, stage->memory, &mapped); memcpy(mapped, hostData->buffer, hostData->size); - vmaUnmapMemory(state->mAllocator, stage->memory); - vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData->size); + vmaUnmapMemory(mState->mAllocator, stage->memory); + vmaFlushAllocation(mState->mAllocator, stage->memory, 0, hostData->size); - VulkanCommandBuffer& commands = state->mCommands->get(); + VulkanCommandBuffer& commands = mState->mCommands->get(); VkCommandBuffer const cmdbuf = commands.buffer(); - commands.acquire(this); + commands.acquire(fvkmemory::resource_ptr::cast(this)); VkBufferImageCopy copyRegion = { .bufferOffset = {}, @@ -503,25 +465,24 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt transitionLayout(&commands, transitionRange, newLayout); - vkCmdCopyBufferToImage(cmdbuf, stage->buffer, state->mTextureImage, newVkLayout, 1, ©Region); + vkCmdCopyBufferToImage(cmdbuf, stage->buffer, mState->mTextureImage, newVkLayout, 1, ©Region); transitionLayout(&commands, transitionRange, nextLayout); } void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, uint32_t height, uint32_t depth, uint32_t miplevel) { - auto* const state = getSharedState(); void* mapped = nullptr; VulkanStageImage const* stage - = state->mStagePool.acquireImage(hostData.format, hostData.type, width, height); - vmaMapMemory(state->mAllocator, stage->memory, &mapped); + = mState->mStagePool.acquireImage(hostData.format, hostData.type, width, height); + vmaMapMemory(mState->mAllocator, stage->memory, &mapped); memcpy(mapped, hostData.buffer, hostData.size); - vmaUnmapMemory(state->mAllocator, stage->memory); - vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData.size); + vmaUnmapMemory(mState->mAllocator, stage->memory); + vmaFlushAllocation(mState->mAllocator, stage->memory, 0, hostData.size); - VulkanCommandBuffer& commands = state->mCommands->get(); + VulkanCommandBuffer& commands = mState->mCommands->get(); VkCommandBuffer const cmdbuf = commands.buffer(); - commands.acquire(this); + commands.acquire(fvkmemory::resource_ptr::cast(this)); // TODO: support blit-based format conversion for 3D images and cubemaps. const int layer = 0; @@ -544,14 +505,13 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u transitionLayout(&commands, range, newLayout); vkCmdBlitImage(cmdbuf, stage->image, imgutil::getVkLayout(VulkanLayout::TRANSFER_SRC), - state->mTextureImage, imgutil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST); + mState->mTextureImage, imgutil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST); transitionLayout(&commands, range, oldLayout); } VulkanLayout VulkanTexture::getDefaultLayout() const { - auto* const state = getSharedState(); - return state->mDefaultLayout; + return mState->mDefaultLayout; } VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) { @@ -570,42 +530,43 @@ VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) { - auto* const state = getSharedState(); VulkanTextureState::ImageViewKey const key{ range, viewType, swizzle }; - auto iter = state->mCachedImageViews.find(key); - if (iter != state->mCachedImageViews.end()) { + auto iter = mState->mCachedImageViews.find(key); + if (iter != mState->mCachedImageViews.end()) { return iter->second; } VkImageViewCreateInfo viewInfo = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .pNext = nullptr, .flags = 0, - .image = state->mTextureImage, + .image = mState->mTextureImage, .viewType = viewType, - .format = state->mVkFormat, + .format = mState->mVkFormat, .components = swizzle, .subresourceRange = range, }; VkImageView imageView; - vkCreateImageView(state->mDevice, &viewInfo, VKALLOC, &imageView); - state->mCachedImageViews.emplace(key, imageView); + vkCreateImageView(mState->mDevice, &viewInfo, VKALLOC, &imageView); + mState->mCachedImageViews.emplace(key, imageView); return imageView; } VkImageAspectFlags VulkanTexture::getImageAspect() const { // Helper function in VulkanUtility - auto* const state = getSharedState(); - return filament::backend::getImageAspect(state->mVkFormat); + return filament::backend::getImageAspect(mState->mVkFormat); } bool VulkanTexture::transitionLayout(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range, VulkanLayout newLayout) { - return transitionLayout(commands->buffer(), range, newLayout); + if (transitionLayout(commands->buffer(), range, newLayout)) { + commands->acquire(fvkmemory::resource_ptr::cast(this)); + return true; + } + return false; } bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceRange const& range, VulkanLayout newLayout) { - auto* const state = getSharedState(); VulkanLayout const oldLayout = getLayout(range.baseArrayLayer, range.baseMipLevel); uint32_t const firstLayer = range.baseArrayLayer; @@ -636,7 +597,7 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR continue; } hasTransitions = hasTransitions || imgutil::transitionLayout(cmdbuf, { - .image = state->mTextureImage, + .image = mState->mTextureImage, .oldLayout = layout, .newLayout = newLayout, .subresources = { @@ -651,7 +612,7 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR } } else if (newLayout != oldLayout) { hasTransitions = imgutil::transitionLayout(cmdbuf, { - .image = state->mTextureImage, + .image = mState->mTextureImage, .oldLayout = oldLayout, .newLayout = newLayout, .subresources = range, @@ -684,7 +645,6 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range) { VkCommandBuffer const cmdbuf = commands->buffer(); - auto* const state = getSharedState(); VkImageLayout const layout = imgutil::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel)); VkImageMemoryBarrier barrier = { @@ -696,7 +656,7 @@ void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands, .newLayout = layout, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = state->mTextureImage, + .image = mState->mTextureImage, .subresourceRange = range, }; vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, @@ -708,7 +668,6 @@ void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands, void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range) { VkCommandBuffer const cmdbuf = commands->buffer(); - auto* const state = getSharedState(); VkImageLayout const layout = imgutil::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel)); VkImageMemoryBarrier barrier = { @@ -719,7 +678,7 @@ void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands, .newLayout = layout, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = state->mTextureImage, + .image = mState->mTextureImage, .subresourceRange = range, }; vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, @@ -727,7 +686,6 @@ void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands, } void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout newLayout) { - auto* const state = getSharedState(); uint32_t const firstLayer = range.baseArrayLayer; uint32_t const lastLayer = firstLayer + range.layerCount; uint32_t const firstLevel = range.baseMipLevel; @@ -740,34 +698,32 @@ void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) { uint32_t const first = (layer << 16) | firstLevel; uint32_t const last = (layer << 16) | lastLevel; - state->mSubresourceLayouts.clear(first, last); + mState->mSubresourceLayouts.clear(first, last); } } else { for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) { uint32_t const first = (layer << 16) | firstLevel; uint32_t const last = (layer << 16) | lastLevel; - state->mSubresourceLayouts.add(first, last, newLayout); + mState->mSubresourceLayouts.add(first, last, newLayout); } } } VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const { assert_invariant(level <= 0xffff && layer <= 0xffff); - auto* const state = getSharedState(); const uint32_t key = (layer << 16) | level; - if (!state->mSubresourceLayouts.has(key)) { + if (!mState->mSubresourceLayouts.has(key)) { return VulkanLayout::UNDEFINED; } - return state->mSubresourceLayouts.get(key); + return mState->mSubresourceLayouts.get(key); } #if FVK_ENABLED(FVK_DEBUG_TEXTURE) void VulkanTexture::print() const { - auto* const state = getSharedState(); uint32_t const firstLayer = 0; - uint32_t const lastLayer = firstLayer + state->mFullViewRange.layerCount; + uint32_t const lastLayer = firstLayer + mState->mFullViewRange.layerCount; uint32_t const firstLevel = 0; - uint32_t const lastLevel = firstLevel + state->mFullViewRange.levelCount; + uint32_t const lastLevel = firstLevel + mState->mFullViewRange.levelCount; for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) { for (uint32_t level = firstLevel; level < lastLevel; ++level) { @@ -776,16 +732,16 @@ void VulkanTexture::print() const { layer < (mPrimaryViewRange.baseArrayLayer + mPrimaryViewRange.layerCount) && level >= mPrimaryViewRange.baseMipLevel && level < (mPrimaryViewRange.baseMipLevel + mPrimaryViewRange.levelCount); - FVK_LOGD << "[" << state->mTextureImage << "]: (" << layer << "," << level + FVK_LOGD << "[" << mState->mTextureImage << "]: (" << layer << "," << level << ")=" << getLayout(layer, level) << " primary=" << primary << utils::io::endl; } } - for (auto view: state->mCachedImageViews) { + for (auto view: mState->mCachedImageViews) { auto& range = view.first.range; - FVK_LOGD << "[" << state->mTextureImage << ", imageView=" << view.second << "]=>" + FVK_LOGD << "[" << mState->mTextureImage << ", imageView=" << view.second << "]=>" << " (" << range.baseArrayLayer << "," << range.baseMipLevel << ")" << " count=(" << range.layerCount << "," << range.levelCount << ")" << " aspect=" << range.aspectMask << " viewType=" << view.first.type diff --git a/filament/backend/src/vulkan/VulkanTexture.h b/filament/backend/src/vulkan/VulkanTexture.h index 5165b1cec78..e6f9e38ad40 100644 --- a/filament/backend/src/vulkan/VulkanTexture.h +++ b/filament/backend/src/vulkan/VulkanTexture.h @@ -20,8 +20,9 @@ #include "DriverBase.h" #include "VulkanBuffer.h" -#include "VulkanResources.h" #include "VulkanImageUtility.h" +#include "vulkan/memory/Resource.h" +#include "vulkan/memory/ResourcePointer.h" #include #include @@ -30,13 +31,13 @@ namespace filament::backend { -class VulkanResourceAllocator; - -struct VulkanTextureState : public VulkanResource { +struct VulkanTextureState : public fvkmemory::Resource { VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected); + ~VulkanTextureState(); + struct ImageViewKey { VkImageSubresourceRange range; // 4 * 5 bytes VkImageViewType type; // 4 bytes @@ -58,10 +59,8 @@ struct VulkanTextureState : public VulkanResource { using ImageViewHash = utils::hash::MurmurHashFn; - uint32_t refs = 1; - // The texture with the sidecar owns the sidecar. - std::unique_ptr mSidecarMSAA; + fvkmemory::resource_ptr mSidecarMSAA; VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE; VkFormat const mVkFormat; @@ -83,37 +82,32 @@ struct VulkanTextureState : public VulkanResource { bool mIsTransientAttachment; }; - -struct VulkanTexture : public HwTexture, VulkanResource { +struct VulkanTexture : public HwTexture, fvkmemory::Resource { // Standard constructor for user-facing textures. VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, - VmaAllocator allocator, VulkanCommands* commands, - VulkanResourceAllocator* handleAllocator, - SamplerType target, uint8_t levels, - TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, - TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated = false); + VmaAllocator allocator, fvkmemory::ResourceManager* resourceManager, + VulkanCommands* commands, SamplerType target, uint8_t levels, TextureFormat tformat, + uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage tusage, + VulkanStagePool& stagePool); // Specialized constructor for internally created textures (e.g. from a swap chain) // The texture will never destroy the given VkImage, but it does manages its subresources. - VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, - VulkanResourceAllocator* handleAllocator, - VkImage image, + VulkanTexture(VkDevice device, VmaAllocator allocator, + fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image, VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage, - VulkanStagePool& stagePool, bool heapAllocated = false); + VulkanStagePool& stagePool); // Constructor for creating a texture view for wrt specific mip range VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, - VulkanResourceAllocator* handleAllocator, - VulkanTexture const* src, uint8_t baseLevel, uint8_t levelCount); + fvkmemory::resource_ptr src, uint8_t baseLevel, uint8_t levelCount); // Constructor for creating a texture view for swizzle. VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, - VulkanResourceAllocator* handleAllocator, - VulkanTexture const* src, VkComponentMapping swizzle); + fvkmemory::resource_ptr src, VkComponentMapping swizzle); - ~VulkanTexture(); + ~VulkanTexture() = default; // Uploads data into a subregion of a 2D or 3D texture. void updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height, @@ -121,16 +115,14 @@ struct VulkanTexture : public HwTexture, VulkanResource { // Returns the primary image view, which is used for shader sampling. VkImageView getPrimaryImageView() { - VulkanTextureState* state = getSharedState(); - return getImageView(mPrimaryViewRange, state->mViewType, mSwizzle); + return getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle); } VkImageViewType getViewType() const { - VulkanTextureState const* state = getSharedState(); - return state->mViewType; + return mState->mViewType; } - VkImageSubresourceRange getPrimaryViewRange() const { return mPrimaryViewRange; } + VkImageSubresourceRange const& getPrimaryViewRange() const { return mPrimaryViewRange; } VulkanLayout getPrimaryImageLayout() const { return getLayout(mPrimaryViewRange.baseArrayLayer, mPrimaryViewRange.baseMipLevel); @@ -156,34 +148,28 @@ struct VulkanTexture : public HwTexture, VulkanResource { VkImageView getViewForType(VkImageSubresourceRange const& range, VkImageViewType type); VkFormat getVkFormat() const { - VulkanTextureState const* state = getSharedState(); - return state->mVkFormat; + return mState->mVkFormat; } VkImage getVkImage() const { - VulkanTextureState const* state = getSharedState(); - return state->mTextureImage; + return mState->mTextureImage; } VulkanLayout getLayout(uint32_t layer, uint32_t level) const; - void setSidecar(VulkanTexture* sidecar) { - VulkanTextureState* state = getSharedState(); - state->mSidecarMSAA.reset(sidecar); + void setSidecar(fvkmemory::resource_ptr sidecar) { + mState->mSidecarMSAA = sidecar; } - VulkanTexture* getSidecar() const { - VulkanTextureState const* state = getSharedState(); - return state->mSidecarMSAA.get(); + fvkmemory::resource_ptr getSidecar() const { + return mState->mSidecarMSAA; } bool isTransientAttachment() const { - VulkanTextureState const* state = getSharedState(); - return state->mIsTransientAttachment; + return mState->mIsTransientAttachment; } bool getIsProtected() const { - VulkanTextureState const* state = getSharedState(); - return state->mIsProtected; + return mState->mIsProtected; } bool transitionLayout(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range, @@ -211,9 +197,6 @@ struct VulkanTexture : public HwTexture, VulkanResource { #endif private: - VulkanTextureState* getSharedState(); - VulkanTextureState const* getSharedState() const; - // Gets or creates a cached VkImageView for a range of miplevels, array layers, viewType, and // swizzle (or not). VkImageView getImageView(VkImageSubresourceRange range, VkImageViewType viewType, @@ -222,9 +205,7 @@ struct VulkanTexture : public HwTexture, VulkanResource { void updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, uint32_t height, uint32_t depth, uint32_t miplevel); - VulkanResourceAllocator* const mAllocator; - - Handle mState; + fvkmemory::resource_ptr mState; // Track the range of subresources that define the "primary" image view, which is the special // image view that gets bound to an actual texture sampler. diff --git a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp index 0533b58e0f4..19aa989b8c0 100644 --- a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp +++ b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp @@ -16,11 +16,11 @@ #include "VulkanDescriptorSetManager.h" +#include #include #include #include #include -#include #include #include @@ -274,7 +274,7 @@ class VulkanDescriptorSetManager::DescriptorInfinitePool { DescriptorInfinitePool(VkDevice device) : mDevice(device) {} - VkDescriptorSet obtainSet(VulkanDescriptorSetLayout* layout) { + VkDescriptorSet obtainSet(fvkmemory::resource_ptr layout) { auto const vklayout = layout->getVkLayout(); DescriptorPool* sameTypePool = nullptr; for (auto& pool: mPools) { @@ -325,13 +325,12 @@ class VulkanDescriptorSetManager::DescriptorSetLayoutManager { DescriptorSetLayoutManager(VkDevice device) : mDevice(device) {} - VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout* layout) { - auto const& bitmasks = layout->bitmask; + VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks) { if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) { return itr->second; } - auto vklayout = createLayout(mDevice, layout->bitmask); - mVkLayouts[layout->bitmask] = vklayout; + auto vklayout = createLayout(mDevice, bitmasks); + mVkLayouts[bitmasks] = vklayout; return vklayout; } @@ -347,10 +346,11 @@ class VulkanDescriptorSetManager::DescriptorSetLayoutManager { mVkLayouts; }; + VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device, - VulkanResourceAllocator* resourceAllocator) + fvkmemory::ResourceManager* resourceManager) : mDevice(device), - mResourceAllocator(resourceAllocator), + mResourceManager(resourceManager), mLayoutManager(std::make_unique(device)), mDescriptorPool(std::make_unique(device)) {} @@ -359,18 +359,20 @@ VulkanDescriptorSetManager::~VulkanDescriptorSetManager() = default; void VulkanDescriptorSetManager::terminate() noexcept{ mLayoutManager.reset(); mDescriptorPool.reset(); + clearHistory(); } // bind() is not really binding the set but just stashing until we have all the info // (pipelinelayout). -void VulkanDescriptorSetManager::bind(uint8_t setIndex, VulkanDescriptorSet* set, +void VulkanDescriptorSetManager::bind(uint8_t setIndex, + fvkmemory::resource_ptr set, backend::DescriptorSetOffsetArray&& offsets) { set->setOffsets(std::move(offsets)); mStashedSets[setIndex] = set; } void VulkanDescriptorSetManager::unbind(uint8_t setIndex) { - mStashedSets[setIndex] = nullptr; + mStashedSets[setIndex] = {}; } void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands, @@ -412,14 +414,14 @@ void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands, }; } -void VulkanDescriptorSetManager::updateBuffer(VulkanDescriptorSet* set, uint8_t binding, - VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept { +void VulkanDescriptorSetManager::updateBuffer(fvkmemory::resource_ptr set, + uint8_t binding, fvkmemory::resource_ptr bufferObject, + VkDeviceSize offset, VkDeviceSize size) noexcept { VkDescriptorBufferInfo const info = { .buffer = bufferObject->buffer.getGpuBuffer(), .offset = offset, .range = size, }; - VkDescriptorType type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; if (set->dynamicUboMask.test(binding)) { @@ -438,8 +440,9 @@ void VulkanDescriptorSetManager::updateBuffer(VulkanDescriptorSet* set, uint8_t set->acquire(bufferObject); } -void VulkanDescriptorSetManager::updateSampler(VulkanDescriptorSet* set, uint8_t binding, - VulkanTexture* texture, VkSampler sampler) noexcept { +void VulkanDescriptorSetManager::updateSampler(fvkmemory::resource_ptr set, + uint8_t binding, fvkmemory::resource_ptr texture, + VkSampler sampler) noexcept { VkDescriptorImageInfo info{ .sampler = sampler, }; @@ -469,38 +472,35 @@ void VulkanDescriptorSetManager::updateSampler(VulkanDescriptorSet* set, uint8_t set->acquire(texture); } -void VulkanDescriptorSetManager::updateInputAttachment(VulkanDescriptorSet* set, - VulkanAttachment attachment) noexcept { +void VulkanDescriptorSetManager::updateInputAttachment( + fvkmemory::resource_ptr set, + VulkanAttachment const& attachment) noexcept { // TOOD: fill-in this region } -void VulkanDescriptorSetManager::createSet(Handle handle, - VulkanDescriptorSetLayout* layout) { +fvkmemory::resource_ptr VulkanDescriptorSetManager::createSet( + Handle handle, fvkmemory::resource_ptr layout) { auto const vkSet = mDescriptorPool->obtainSet(layout); auto const& count = layout->count; auto const vklayout = layout->getVkLayout(); - mResourceAllocator->construct(handle, mResourceAllocator, vkSet, + return fvkmemory::resource_ptr::make(mResourceManager, handle, vkSet, layout->bitmask.dynamicUbo, layout->count.dynamicUbo, - [vkSet, count, vklayout, this](VulkanDescriptorSet* set) { - eraseSetFromHistory(set); - mDescriptorPool->recycle(count, vklayout, vkSet); + [vkSet, count, vklayout, this](VulkanDescriptorSet*) { + // Note that mDescriptorPool could be gone due to terminate (when the backend shuts + // down). + if (mDescriptorPool) { + mDescriptorPool->recycle(count, vklayout, vkSet); + } }); } -void VulkanDescriptorSetManager::destroySet(Handle handle) { -} - -void VulkanDescriptorSetManager::initVkLayout(VulkanDescriptorSetLayout* layout) { - layout->setVkLayout(mLayoutManager->getVkLayout(layout)); +void VulkanDescriptorSetManager::initVkLayout( + fvkmemory::resource_ptr layout) { + layout->setVkLayout(mLayoutManager->getVkLayout(layout->bitmask)); } -void VulkanDescriptorSetManager::eraseSetFromHistory(VulkanDescriptorSet* set) { - for (uint8_t i = 0; i < mStashedSets.size(); ++i) { - if (mStashedSets[i] == set) { - mStashedSets[i] = nullptr; - } - } +void VulkanDescriptorSetManager::clearHistory() { + mStashedSets = {}; } - } // namespace filament::backend diff --git a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h index 1bbf6e29b04..faeeb144205 100644 --- a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h +++ b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h @@ -17,9 +17,10 @@ #ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H #define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H -#include -#include -#include +#include "vulkan/VulkanHandles.h" +#include "vulkan/VulkanTexture.h" +#include "vulkan/VulkanUtility.h" +#include "vulkan/memory/ResourcePointer.h" #include #include @@ -46,46 +47,45 @@ class VulkanDescriptorSetManager { using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray; - VulkanDescriptorSetManager(VkDevice device, VulkanResourceAllocator* resourceAllocator); + VulkanDescriptorSetManager(VkDevice device, fvkmemory::ResourceManager* resourceManager); ~VulkanDescriptorSetManager(); void terminate() noexcept; - void updateBuffer(VulkanDescriptorSet* set, uint8_t binding, - VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept; + void updateBuffer(fvkmemory::resource_ptr set, uint8_t binding, + fvkmemory::resource_ptr bufferObject, VkDeviceSize offset, + VkDeviceSize size) noexcept; - void updateSampler(VulkanDescriptorSet* set, uint8_t binding, - VulkanTexture* texture, VkSampler sampler) noexcept; + void updateSampler(fvkmemory::resource_ptr set, uint8_t binding, + fvkmemory::resource_ptr texture, VkSampler sampler) noexcept; - void updateInputAttachment(VulkanDescriptorSet* set, VulkanAttachment attachment) noexcept; + void updateInputAttachment(fvkmemory::resource_ptr set, + VulkanAttachment const& attachment) noexcept; - void bind(uint8_t setIndex, VulkanDescriptorSet* set, backend::DescriptorSetOffsetArray&& offsets); + void bind(uint8_t setIndex, fvkmemory::resource_ptr set, + backend::DescriptorSetOffsetArray&& offsets); void unbind(uint8_t setIndex); void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, DescriptorSetMask const& setMask); - void setPlaceHolders(VkSampler sampler, VulkanTexture* texture, - VulkanBufferObject* bufferObject) noexcept; + fvkmemory::resource_ptr createSet(Handle handle, + fvkmemory::resource_ptr layout); - void createSet(Handle handle, VulkanDescriptorSetLayout* layout); + void initVkLayout(fvkmemory::resource_ptr layout); - void destroySet(Handle handle); - - void initVkLayout(VulkanDescriptorSetLayout* layout); + void clearHistory(); private: class DescriptorSetLayoutManager; class DescriptorInfinitePool; - void eraseSetFromHistory(VulkanDescriptorSet* set); - using DescriptorSetArray = - std::array; + std::array, UNIQUE_DESCRIPTOR_SET_COUNT>; VkDevice mDevice; - VulkanResourceAllocator* mResourceAllocator; + fvkmemory::ResourceManager* mResourceManager; std::unique_ptr mLayoutManager; std::unique_ptr mDescriptorPool; std::pair mInputAttachment; diff --git a/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.cpp b/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.cpp index 09a84d33993..256a616aa2e 100644 --- a/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.cpp +++ b/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.cpp @@ -16,12 +16,11 @@ #include "VulkanPipelineLayoutCache.h" -#include - namespace filament::backend { VkPipelineLayout VulkanPipelineLayoutCache::getLayout( - DescriptorSetLayoutArray const& descriptorSetLayouts, VulkanProgram* program) { + DescriptorSetLayoutArray const& descriptorSetLayouts, + fvkmemory::resource_ptr program) { PipelineLayoutKey key = {}; uint8_t descSetLayoutCount = 0; key.descSetLayouts = descriptorSetLayouts; @@ -40,13 +39,13 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout( for (uint8_t i = 0; i < pushConstantRangeCount; ++i) { auto const& range = pushConstantRanges[i]; auto& pushConstant = key.pushConstant[i]; - if (range.stageFlags & VK_SHADER_STAGE_VERTEX_BIT) { + if (range.stageFlags & VK_SHADER_STAGE_VERTEX_BIT) { pushConstant.stage = static_cast(ShaderStage::VERTEX); } - if (range.stageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) { + if (range.stageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) { pushConstant.stage = static_cast(ShaderStage::FRAGMENT); } - if (range.stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) { + if (range.stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) { pushConstant.stage = static_cast(ShaderStage::COMPUTE); } pushConstant.size = range.size; diff --git a/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.h b/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.h index 01d7122e201..212ad60b0cf 100644 --- a/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.h +++ b/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.h @@ -30,7 +30,7 @@ class VulkanPipelineLayoutCache { public: using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray; - VulkanPipelineLayoutCache(VkDevice device, VulkanResourceAllocator* allocator) + VulkanPipelineLayoutCache(VkDevice device) : mDevice(device), mTimestamp(0) {} @@ -56,7 +56,7 @@ class VulkanPipelineLayoutCache { // A pipeline layout depends on the descriptor set layout and the push constant ranges, which // are described in the program. VkPipelineLayout getLayout(DescriptorSetLayoutArray const& descriptorSetLayouts, - VulkanProgram* program); + fvkmemory::resource_ptr program); private: using Timestamp = uint64_t; diff --git a/filament/backend/src/vulkan/memory/ResourceManager.cpp b/filament/backend/src/vulkan/memory/ResourceManager.cpp index db938d61b9f..0a64fd2562a 100644 --- a/filament/backend/src/vulkan/memory/ResourceManager.cpp +++ b/filament/backend/src/vulkan/memory/ResourceManager.cpp @@ -22,7 +22,7 @@ namespace filament::backend::fvkmemory { namespace { -#if FVK_ENABLED(FVK_DEBUG_ALLOCATION) +#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK) uint32_t COUNTER[(size_t) ResourceType::UNDEFINED_TYPE] = {}; #endif } @@ -47,8 +47,7 @@ void ResourceManager::gc() noexcept { } GcList gcs; - gcs.insert(gcs.end(), mGcList.begin(), mGcList.end()); - mGcList.clear(); + std::swap(gcs, mGcList); destroyAll(gcs); } @@ -105,20 +104,20 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) { case ResourceType::UNDEFINED_TYPE: break; } -#if FVK_ENABLED(FVK_DEBUG_ALLOCATION) +#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK) COUNTER[(size_t) type]--; #endif } void ResourceManager::traceConstruction(ResourceType type, HandleId id) { -#if FVK_ENABLED(FVK_DEBUG_ALLOCATION) +#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK) assert_invariant(type != ResourceType::UNDEFINED_TYPE); COUNTER[(size_t) type]++; #endif } void ResourceManager::print() const noexcept { -#if FVK_ENABLED(FVK_DEBUG_ALLOCATION) +#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK) utils::slog.e << "-------------------" << utils::io::endl; for (size_t i = 0; i < (size_t) ResourceType::UNDEFINED_TYPE; ++i) { utils::slog.e <<" " << getTypeStr((ResourceType) i) << "=" << COUNTER[i] << utils::io::endl; diff --git a/filament/backend/src/vulkan/memory/ResourceManager.h b/filament/backend/src/vulkan/memory/ResourceManager.h index fa2b62da260..fd2fef054bb 100644 --- a/filament/backend/src/vulkan/memory/ResourceManager.h +++ b/filament/backend/src/vulkan/memory/ResourceManager.h @@ -95,7 +95,6 @@ class ResourceManager { AllocatorImpl mHandleAllocatorImpl; using GcList = std::vector>; - utils::Mutex mThreadSafeGcListMutex; GcList mThreadSafeGcList; GcList mGcList; From e78691b12b26e5c61239af08b02b67dc977d9728 Mon Sep 17 00:00:00 2001 From: mdagois Date: Wed, 13 Nov 2024 05:05:08 +0900 Subject: [PATCH 10/20] Forcing profile mode (#8256) --- filament/CMakeLists.txt | 5 +++++ filament/backend/src/opengl/OpenGLDriver.cpp | 16 ++++++++++++++-- filament/backend/src/vulkan/VulkanConstants.h | 10 ++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 4fe1c604017..82e336215dd 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -308,6 +308,11 @@ if (FILAMENT_ENABLE_MULTIVIEW) add_definitions(-DFILAMENT_ENABLE_MULTIVIEW) endif() +# Whether to force the profiling mode. +if (FILAMENT_FORCE_PROFILING_MODE) + add_definitions(-DFILAMENT_FORCE_PROFILING_MODE) +endif() + # ================================================================================================== # Definitions # ================================================================================================== diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 676b539be7e..79580fc7c46 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -106,17 +106,29 @@ // set to the desired debug level (for internal debugging [Default: None]) #define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE +// Override the debug markers if we are forcing profiling mode +#if defined(FILAMENT_FORCE_PROFILING_MODE) +# undef DEBUG_GROUP_MARKER_LEVEL +# undef DEBUG_MARKER_LEVEL + +# define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_NONE +# define DEBUG_MARKER_LEVEL DEBUG_MARKER_PROFILE +#endif + #if DEBUG_MARKER_LEVEL == DEBUG_MARKER_PROFILE # define DEBUG_MARKER() # define PROFILE_MARKER(marker) PROFILE_SCOPE(marker); # if DEBUG_GROUP_MARKER_LEVEL != DEBUG_GROUP_MARKER_NONE -# error PROFILING is exclusive; group markers must be disabled. +# error PROFILING is exclusive; group markers must be disabled. +# endif +# ifndef NDEBUG +# error PROFILING is meaningless in DEBUG mode. # endif #elif DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE # define DEBUG_MARKER() DebugMarker _debug_marker(*this, __func__); # define PROFILE_MARKER(marker) DEBUG_MARKER() # if DEBUG_MARKER_LEVEL & DEBUG_MARKER_PROFILE -# error PROFILING is exclusive; all other debug features must be disabled. +# error PROFILING is exclusive; all other debug features must be disabled. # endif #else # define DEBUG_MARKER() diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h index 79b388a7136..ac07debda70 100644 --- a/filament/backend/src/vulkan/VulkanConstants.h +++ b/filament/backend/src/vulkan/VulkanConstants.h @@ -98,6 +98,12 @@ #define FVK_DEBUG_FLAGS 0 #endif +// Override the debug flags if we are forcing profiling mode +#if defined(FILAMENT_FORCE_PROFILING_MODE) +#undef FVK_DEBUG_FLAGS +#define FVK_DEBUG_FLAGS (FVK_DEBUG_PROFILING) +#endif + #define FVK_ENABLED(flags) (((FVK_DEBUG_FLAGS) & (flags)) == (flags)) // Group marker only works only if validation or debug utils is enabled since it uses @@ -133,6 +139,10 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION)); #if FVK_DEBUG_FLAGS == FVK_DEBUG_PROFILING + #ifndef NDEBUG + #error PROFILING is meaningless in DEBUG mode. + #endif + #define FVK_SYSTRACE_CONTEXT() #define FVK_SYSTRACE_START(marker) #define FVK_SYSTRACE_END() From 51392c77aa74bbf32fb563538287cb75ac9ec808 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Tue, 12 Nov 2024 14:08:15 -0800 Subject: [PATCH 11/20] Metal: unbind descriptor sets upon destruction (#8268) --- filament/backend/src/metal/MetalDriver.mm | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 8e49c90dd7d..bee3ba88355 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -872,10 +872,20 @@ void MetalDriver::destroyDescriptorSet(Handle dsh) { DEBUG_LOG("destroyDescriptorSet(dsh = %d)\n", dsh.getId()); - if (dsh) { - executeAfterCurrentCommandBufferCompletes( - [this, dsh]() mutable { destruct_handle(dsh); }); + if (!dsh) { + return; + } + + // Unbind this descriptor set. + auto* descriptorSet = handle_cast(dsh); + for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) { + if (UTILS_UNLIKELY(mContext->currentDescriptorSets[i] == descriptorSet)) { + mContext->currentDescriptorSets[i] = nullptr; + } } + + executeAfterCurrentCommandBufferCompletes( + [this, dsh]() mutable { destruct_handle(dsh); }); } void MetalDriver::terminate() { From 5dfd2851052fdd7a34f736dc18135242daf80118 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 12 Nov 2024 01:36:51 -0800 Subject: [PATCH 12/20] ostream buffer growth was broken It only grew by 3/2 of the needed size instead of the needed capacity, which could be extremely slow when growing by a constant amount many times. --- libs/utils/include/utils/ostream.h | 4 ++-- libs/utils/src/ostream.cpp | 27 ++++++++++++++------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/libs/utils/include/utils/ostream.h b/libs/utils/include/utils/ostream.h index cde8e75bd27..f5549851763 100644 --- a/libs/utils/include/utils/ostream.h +++ b/libs/utils/include/utils/ostream.h @@ -95,11 +95,11 @@ class UTILS_PUBLIC ostream : protected utils::PrivateImplementation { size_t length() const noexcept; private: - void reserve(size_t newSize) noexcept; + void reserve(size_t newCapacity) noexcept; char* buffer = nullptr; // buffer address char* curr = nullptr; // current pointer - size_t size = 0; // size remaining + size_t sizeRemaining = 0; // size remaining size_t capacity = 0; // total capacity of the buffer }; diff --git a/libs/utils/src/ostream.cpp b/libs/utils/src/ostream.cpp index 35ffab84347..c50d0c293b8 100644 --- a/libs/utils/src/ostream.cpp +++ b/libs/utils/src/ostream.cpp @@ -237,23 +237,23 @@ ostream::Buffer::~Buffer() noexcept { void ostream::Buffer::advance(ssize_t n) noexcept { if (n > 0) { - size_t const written = n < size ? size_t(n) : size; + size_t const written = n < sizeRemaining ? size_t(n) : sizeRemaining; curr += written; - size -= written; + sizeRemaining -= written; } } -void ostream::Buffer::reserve(size_t newSize) noexcept { +void ostream::Buffer::reserve(size_t newCapacity) noexcept { size_t const offset = curr - buffer; if (buffer == nullptr) { - buffer = (char*)malloc(newSize); + buffer = (char*)malloc(newCapacity); } else { - buffer = (char*)realloc(buffer, newSize); + buffer = (char*)realloc(buffer, newCapacity); } assert(buffer); - capacity = newSize; + capacity = newCapacity; curr = buffer + offset; - size = capacity - offset; + sizeRemaining = capacity - offset; } void ostream::Buffer::reset() noexcept { @@ -264,7 +264,7 @@ void ostream::Buffer::reset() noexcept { capacity = 1024; } curr = buffer; - size = capacity; + sizeRemaining = capacity; } size_t ostream::Buffer::length() const noexcept { @@ -272,13 +272,14 @@ size_t ostream::Buffer::length() const noexcept { } std::pair ostream::Buffer::grow(size_t s) noexcept { - if (UTILS_UNLIKELY(size < s)) { - size_t const used = curr - buffer; - size_t const newCapacity = std::max(size_t(32), used + (s * 3 + 1) / 2); // 32 bytes minimum + if (UTILS_UNLIKELY(sizeRemaining < s)) { + size_t const usedSize = curr - buffer; + size_t const neededCapacity = usedSize + s; + size_t const newCapacity = std::max(size_t(32), (neededCapacity * 3 + 1) / 2); // 32 bytes minimum reserve(newCapacity); - assert(size >= s); + assert(sizeRemaining >= s); } - return { curr, size }; + return { curr, sizeRemaining }; } } // namespace utils::io From d075a877f2ef78dbffafde15db45bd4522dc7813 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 12 Nov 2024 01:21:31 -0800 Subject: [PATCH 13/20] Fix TSAN warnings with AtomicFreeList I do not think there was an actual error with AtomicFreeList, however TSAN detected a data race when concurrent pop() happened. In that case, there is indeed a race, where we can end-up reading data that is already corrupted by the concurrent pop. However, that situation is corrected by the following CAS. Somehow TSAN didn't see that. The fix is strange and consists in replacing: ``` auto pNext = storage[offset].next; ``` with ``` auto s = storage[offset]; auto pNext = s.next; ``` In this PR we also adjust the memory ordering to be less strong. i.e. we do not need `memory_order_seq_cst`, only the appropriate acquire or release semantic. In addition we also make `Node* next` a non-atomic variable again. It should have been, but was change to placate an older version of TSAN. BUGS=[377369108] --- libs/utils/include/utils/Allocator.h | 36 +++++++++++++++------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/libs/utils/include/utils/Allocator.h b/libs/utils/include/utils/Allocator.h index 7b9978f5b90..25434604885 100644 --- a/libs/utils/include/utils/Allocator.h +++ b/libs/utils/include/utils/Allocator.h @@ -278,18 +278,23 @@ class AtomicFreeList { void* pop() noexcept { Node* const pStorage = mStorage; - HeadPtr currentHead = mHead.load(); + HeadPtr currentHead = mHead.load(std::memory_order_relaxed); while (currentHead.offset >= 0) { // The value of "pNext" we load here might already contain application data if another // thread raced ahead of us. But in that case, the computed "newHead" will be discarded - // since compare_exchange_weak fails. Then this thread will loop with the updated + // since compare_exchange_weak() fails. Then this thread will loop with the updated // value of currentHead, and try again. - Node* const pNext = pStorage[currentHead.offset].next.load(std::memory_order_relaxed); + // TSAN complains if we don't use a local variable here. + Node const node = pStorage[currentHead.offset]; + Node const* const pNext = node.next; const HeadPtr newHead{ pNext ? int32_t(pNext - pStorage) : -1, currentHead.tag + 1 }; - // In the rare case that the other thread that raced ahead of us already returned the - // same mHead we just loaded, but it now has a different "next" value, the tag field will not - // match, and compare_exchange_weak will fail and prevent that particular race condition. - if (mHead.compare_exchange_weak(currentHead, newHead)) { + // In the rare case that the other thread that raced ahead of us already returned the + // same mHead we just loaded, but it now has a different "next" value, the tag field + // will not match, and compare_exchange_weak() will fail and prevent that particular + // race condition. + // acquire: no read/write can be reordered before this + if (mHead.compare_exchange_weak(currentHead, newHead, + std::memory_order_acquire, std::memory_order_relaxed)) { // This assert needs to occur after we have validated that there was no race condition // Otherwise, next might already contain application data, if another thread // raced ahead of us after we loaded mHead, but before we loaded mHead->next. @@ -306,13 +311,15 @@ class AtomicFreeList { Node* const storage = mStorage; assert_invariant(p && p >= storage); Node* const node = static_cast(p); - HeadPtr currentHead = mHead.load(); + HeadPtr currentHead = mHead.load(std::memory_order_relaxed); HeadPtr newHead = { int32_t(node - storage), currentHead.tag + 1 }; do { newHead.tag = currentHead.tag + 1; - Node* const n = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr; - node->next.store(n, std::memory_order_relaxed); - } while(!mHead.compare_exchange_weak(currentHead, newHead)); + Node* const pNext = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr; + node->next = pNext; // could be a race with pop, corrected by CAS + } while(!mHead.compare_exchange_weak(currentHead, newHead, + std::memory_order_release, std::memory_order_relaxed)); + // release: no read/write can be reordered after this } void* getFirst() noexcept { @@ -320,10 +327,7 @@ class AtomicFreeList { } struct Node { - // This should be a regular (non-atomic) pointer, but this causes TSAN to complain - // about a data-race that exists but is benin. We always use this atomic<> in - // relaxed mode. - // The data race TSAN complains about is when a pop() is interrupted by a + // There is a benign data race when a pop() is interrupted by a // pop() + push() just after mHead->next is read -- it appears as though it is written // without synchronization (by the push), however in that case, the pop's CAS will fail // and things will auto-correct. @@ -346,7 +350,7 @@ class AtomicFreeList { // | // CAS, tag++ // - std::atomic next; + Node* next = nullptr; }; private: From 8aa9c4d0044f02b333291bfa6b8cf65ee5aed77d Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 13 Nov 2024 22:53:35 -0800 Subject: [PATCH 14/20] Don't use AtomicFreeList to squash TSAN complains The previous fix attempt didn't work on some test. There are no known bugs with AtomicFreeList other than tripping TSAN, and it's unclear that TSAN isn't at fault. However, switching to using a mutex works fine and doesn't appear to be slower (it's actually faster with synthetic benchmarks on macOS) BUGS=[377369108] --- libs/utils/include/utils/JobSystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/include/utils/JobSystem.h b/libs/utils/include/utils/JobSystem.h index 7ea711661a4..704755aa491 100644 --- a/libs/utils/include/utils/JobSystem.h +++ b/libs/utils/include/utils/JobSystem.h @@ -446,7 +446,7 @@ class JobSystem { Condition mWaiterCondition; std::atomic mActiveJobs = { 0 }; - utils::Arena, LockingPolicy::NoLock> mJobPool; + utils::Arena, LockingPolicy::Mutex> mJobPool; template using aligned_vector = std::vector>; From 8e5ba517184fcb5d74d299d6108e1df6eda0a843 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Thu, 14 Nov 2024 10:12:53 -0800 Subject: [PATCH 15/20] Fix TSAN warning in MetalHandles (#8262) --- filament/backend/src/metal/MetalHandles.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 3758de5842a..484e1dba490 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -438,7 +438,7 @@ struct MetalTimerQuery : public HwTimerQuery { struct Status { std::atomic available {false}; - uint64_t elapsed {0}; // only valid if available is true + std::atomic elapsed {0}; // only valid if available is true }; std::shared_ptr status; From 05ffc7603263c48f05be9c2e3205a096b163b6f3 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 14 Nov 2024 11:11:17 -0800 Subject: [PATCH 16/20] vk: clean up group marker logic (#8272) The old logic was unnecessarily complex. We simplify it and also properly store the marker stack on the CommandPool instead of VulkanCommands. --- .../backend/src/vulkan/VulkanCommands.cpp | 140 +++++++++--------- filament/backend/src/vulkan/VulkanCommands.h | 26 ++-- 2 files changed, 79 insertions(+), 87 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanCommands.cpp b/filament/backend/src/vulkan/VulkanCommands.cpp index 3dd8fd95a60..e531f3433f6 100644 --- a/filament/backend/src/vulkan/VulkanCommands.cpp +++ b/filament/backend/src/vulkan/VulkanCommands.cpp @@ -58,56 +58,32 @@ VkCommandBuffer createCommandBuffer(VkDevice device, VkCommandPool pool) { #if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS) void VulkanGroupMarkers::push(std::string const& marker, Timestamp start) noexcept { - mMarkers.push_back(marker); - -#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) - mTimestamps.push_back(start.time_since_epoch().count() > 0.0 - ? start - : std::chrono::high_resolution_clock::now()); -#endif + mMarkers.push_back({marker, + start.time_since_epoch().count() > 0.0 + ? start + : std::chrono::high_resolution_clock::now()}); } std::pair VulkanGroupMarkers::pop() noexcept { - auto const marker = mMarkers.back(); + auto ret = mMarkers.back(); mMarkers.pop_back(); - -#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) - auto const timestamp = mTimestamps.back(); - mTimestamps.pop_back(); - return std::make_pair(marker, timestamp); -#else - return std::make_pair(marker, Timestamp{}); -#endif + return ret; } std::pair VulkanGroupMarkers::pop_bottom() noexcept { - auto const marker = mMarkers.front(); + auto ret = mMarkers.front(); mMarkers.pop_front(); - -#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) - auto const timestamp = mTimestamps.front(); - mTimestamps.pop_front(); - return std::make_pair(marker, timestamp); -#else - return std::make_pair(marker, Timestamp{}); -#endif + return ret; } -std::pair VulkanGroupMarkers::top() const { +std::pair const& VulkanGroupMarkers::top() const { assert_invariant(!empty()); - auto const marker = mMarkers.back(); -#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) - auto const topTimestamp = mTimestamps.front(); - return std::make_pair(marker, topTimestamp); -#else - return std::make_pair(marker, Timestamp{}); -#endif + return mMarkers.back(); } bool VulkanGroupMarkers::empty() const noexcept { return mMarkers.empty(); } - #endif // FVK_DEBUG_GROUP_MARKERS VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VkDevice device, VkQueue queue, @@ -302,6 +278,19 @@ VulkanCommandBuffer& CommandBufferPool::getRecording() { auto& recording = *mBuffers[mRecording]; recording.begin(); + +#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS) + if (mGroupMarkers) { + std::unique_ptr markers = std::make_unique(); + while (!mGroupMarkers->empty()) { + auto [marker, timestamp] = mGroupMarkers->pop_bottom(); + recording.pushMarker(marker.c_str()); + markers->push(marker, timestamp); + } + std::swap(mGroupMarkers, markers); + } +#endif + return recording; } @@ -356,17 +345,38 @@ void CommandBufferPool::waitFor(VkSemaphore previousAction) { recording->insertWait(previousAction); } -void CommandBufferPool::pushMarker(char const* marker) { +#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS) +std::string CommandBufferPool::topMarker() const { + if (!mGroupMarkers || mGroupMarkers->empty()) { + return ""; + } + return std::get<0>(mGroupMarkers->top()); +} + +void CommandBufferPool::pushMarker(char const* marker, VulkanGroupMarkers::Timestamp timestamp) { + if (!mGroupMarkers) { + mGroupMarkers = std::make_unique(); + } + mGroupMarkers->push(marker, timestamp); getRecording().pushMarker(marker); } -void CommandBufferPool::popMarker() { - getRecording().popMarker(); +std::pair CommandBufferPool::popMarker() { + assert_invariant(mGroupMarkers && !mGroupMarkers->empty()); + auto ret = mGroupMarkers->pop(); + + // Note that if we're popping a marker while not recording, we would just pop the conceptual + // stack of marker (i.e. mGroupMarkers) and not carry out the pop on the command buffer. + if (isRecording()) { + getRecording().popMarker(); + } + return ret; } void CommandBufferPool::insertEvent(char const* marker) { getRecording().insertEvent(marker); } +#endif // FVK_DEBUG_GROUP_MARKERS VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex, VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context) @@ -470,47 +480,31 @@ void VulkanCommands::updateFences() { #if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS) void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp) { -#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) - // If the timestamp is not 0, then we are carrying over a marker across buffer submits. - // If it is 0, then this is a normal marker push and we should just print debug line as usual. - if (timestamp.time_since_epoch().count() == 0.0) { - FVK_LOGD << "----> " << str << utils::io::endl; - } -#endif - if (!mGroupMarkers) { - mGroupMarkers = std::make_unique(); - } - mGroupMarkers->push(str, timestamp); - - mPool->pushMarker(str); + mPool->pushMarker(str, timestamp); if (mProtectedPool) { - mProtectedPool->pushMarker(str); + mProtectedPool->pushMarker(str, timestamp); } +#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) + FVK_LOGD << "----> " << str << utils::io::endl; +#endif } void VulkanCommands::popGroupMarker() { - assert_invariant(mGroupMarkers); - if (!mGroupMarkers->empty()) { - VkCommandBuffer const cmdbuffer = get().buffer(); #if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) - auto const [marker, startTime] = mGroupMarkers->pop(); - auto const endTime = std::chrono::high_resolution_clock::now(); - std::chrono::duration diff = endTime - startTime; - FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms" - << utils::io::endl; + auto ret = mPool->popMarker(); + auto const& marker = ret.first; + auto const& startTime = ret.second; + auto const endTime = std::chrono::high_resolution_clock::now(); + std::chrono::duration diff = endTime - startTime; + FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms" + << utils::io::endl; #else - mGroupMarkers->pop(); -#endif - mPool->popMarker(); - if (mProtectedPool) { - mProtectedPool->popMarker(); - } - } else if (mCarriedOverMarkers && !mCarriedOverMarkers->empty()) { - // It could be that pop is called between flush() and get() (new command buffer), in which - // case the marker is in "carried over" state, we'd just remove that. Since the - // mCarriedOverMarkers is in the opposite order, we pop the bottom instead of the top. - mCarriedOverMarkers->pop_bottom(); + mPool->popMarker(); +#endif // FVK_DEBUG_PRINT_GROUP_MARKERS + + if (mProtectedPool) { + mProtectedPool->popMarker(); } } @@ -522,10 +516,10 @@ void VulkanCommands::insertEventMarker(char const* str, uint32_t len) { } std::string VulkanCommands::getTopGroupMarker() const { - if (!mGroupMarkers || mGroupMarkers->empty()) { - return ""; + if (mProtectedPool) { + return mProtectedPool->topMarker(); } - return std::get<0>(mGroupMarkers->top()); + return mPool->topMarker(); } #endif // FVK_DEBUG_GROUP_MARKERS diff --git a/filament/backend/src/vulkan/VulkanCommands.h b/filament/backend/src/vulkan/VulkanCommands.h index e7354fbe6bd..47c26a00072 100644 --- a/filament/backend/src/vulkan/VulkanCommands.h +++ b/filament/backend/src/vulkan/VulkanCommands.h @@ -51,14 +51,11 @@ class VulkanGroupMarkers { void push(std::string const& marker, Timestamp start = {}) noexcept; std::pair pop() noexcept; std::pair pop_bottom() noexcept; - std::pair top() const; + std::pair const& top() const; bool empty() const noexcept; private: - std::list mMarkers; -#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS) - std::list mTimestamps; -#endif + std::list> mMarkers; }; #endif // FVK_DEBUG_GROUP_MARKERS @@ -123,7 +120,7 @@ struct VulkanCommandBuffer { VkSemaphore mSubmission; VkFence mFence; std::shared_ptr mFenceStatus; - std::vector> mResources; + std::vector> mResources; }; struct CommandBufferPool { @@ -140,12 +137,14 @@ struct CommandBufferPool { void update(); VkSemaphore flush(); void wait(); - void waitFor(VkSemaphore previousAction); - void pushMarker(char const* marker); - void popMarker(); +#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS) + std::string topMarker() const; + void pushMarker(char const* marker, VulkanGroupMarkers::Timestamp timestamp); + std::pair popMarker(); void insertEvent(char const* marker); +#endif inline bool isRecording() const { return mRecording != INVALID; } @@ -159,6 +158,10 @@ struct CommandBufferPool { ActiveBuffers mSubmitted; std::vector> mBuffers; int8_t mRecording; + +#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS) + std::unique_ptr mGroupMarkers; +#endif }; // Manages a set of command buffers and semaphores, exposing an API that is significantly simpler @@ -245,11 +248,6 @@ class VulkanCommands { VkSemaphore mInjectedDependency = VK_NULL_HANDLE; VkSemaphore mLastSubmit = VK_NULL_HANDLE; - -#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS) - std::unique_ptr mGroupMarkers; - std::unique_ptr mCarriedOverMarkers; -#endif }; } // namespace filament::backend From 43b9c06f796dbb8340b50c14f839cfe6c9c56ad2 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 14 Nov 2024 11:24:29 -0800 Subject: [PATCH 17/20] vk: increase number of command buffers (#8271) Small number of pre-allocated command buffers can cause unnecessary blocking on the CPU. We increase this number. --- filament/backend/src/vulkan/VulkanCommands.h | 7 ++++++- filament/backend/src/vulkan/VulkanConstants.h | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanCommands.h b/filament/backend/src/vulkan/VulkanCommands.h index 47c26a00072..2e331dfa4ad 100644 --- a/filament/backend/src/vulkan/VulkanCommands.h +++ b/filament/backend/src/vulkan/VulkanCommands.h @@ -124,7 +124,7 @@ struct VulkanCommandBuffer { }; struct CommandBufferPool { - using ActiveBuffers = utils::bitset32; + using ActiveBuffers = utils::bitset64; static constexpr int8_t INVALID = -1; CommandBufferPool(VulkanContext* context, VkDevice device, VkQueue queue, @@ -152,6 +152,11 @@ struct CommandBufferPool { static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS; // int8 only goes up to 127, therefore capacity must be less than that. static_assert(CAPACITY < 128); + + // The number of bits in ActiveBuffers describe the usage of the buffers in the pool, so must be + // larger than the size of the pool. + static_assert(sizeof(ActiveBuffers) * 8 >= CAPACITY); + using BufferList = utils::FixedCapacityVector>; VkDevice mDevice; VkCommandPool mPool; diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h index ac07debda70..1a454d70696 100644 --- a/filament/backend/src/vulkan/VulkanConstants.h +++ b/filament/backend/src/vulkan/VulkanConstants.h @@ -196,14 +196,16 @@ constexpr static const int FVK_REQUIRED_VERSION_MINOR = 1; // buffers that have been submitted but have not yet finished rendering. Note that Filament can // issue multiple commit calls in a single frame, and that we use a triple buffered swap chain on // some platforms. -constexpr static const int FVK_MAX_COMMAND_BUFFERS = 10; +// +// Heuristic: Triple Buffering (3) multiplied by maximum number of renderpasses (15). +constexpr static const int FVK_MAX_COMMAND_BUFFERS = 3 * 15; // Number of command buffer submissions that should occur before an unused pipeline is removed // from the cache. // // If this number is low, VkPipeline construction will occur frequently, which can // be extremely slow. If this number is high, the memory footprint will be large. -constexpr static const int FVK_MAX_PIPELINE_AGE = 10; +constexpr static const int FVK_MAX_PIPELINE_AGE = FVK_MAX_COMMAND_BUFFERS; // VulkanPipelineCache does not track which command buffers contain references to which pipelines, // instead it simply waits for at least FVK_MAX_COMMAND_BUFFERS submissions to occur before From b587028aa5a1e6e6c32cfbe3d20e0b2cbf2d5f50 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Mon, 18 Nov 2024 16:04:15 -0800 Subject: [PATCH 18/20] Bump material version to 56 (#8281) Fix the material version correctly. --- 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 79822f19830..4092e8a765e 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 = 55; +static constexpr size_t MATERIAL_VERSION = 56; /** * Supported shading models From ffd32e9309f1a680baf84d91ef9e040197cac5e0 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Tue, 19 Nov 2024 17:20:09 +0000 Subject: [PATCH 19/20] Release Filament 1.56.1 --- NEW_RELEASE_NOTES.md | 1 - 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(+), 7 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 71416f7f695..4a1a9c7fa7e 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,4 +7,3 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut -- vk: fix stage pool gc logic diff --git a/README.md b/README.md index 40696f65573..cbfa132634b 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.56.0' + implementation 'com.google.android.filament:filament-android:1.56.1' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.56.0' +pod 'Filament', '~> 1.56.1' ``` ## Documentation diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 42e68e62acc..9f82faac507 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.56.2 + +- vk: fix stage pool gc logic + ## v1.56.1 ## v1.56.0 diff --git a/android/gradle.properties b/android/gradle.properties index 319fd5a0738..c657979b6c3 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.56.0 +VERSION_NAME=1.56.1 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index af98212f0e8..44fe5926955 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.56.0" + spec.version = "1.56.1" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.56.0/filament-v1.56.0-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.56.1/filament-v1.56.1-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 3fde34d6082..bbca1748ed7 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.56.0", + "version": "1.56.1", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From 758818987457eddf8a8e2544be66b5f6aa20436c Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Tue, 19 Nov 2024 17:20:17 +0000 Subject: [PATCH 20/20] Bump version to 1.56.2 --- 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 cbfa132634b..0907d9794c2 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.56.1' + implementation 'com.google.android.filament:filament-android:1.56.2' } ``` @@ -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.56.1' +pod 'Filament', '~> 1.56.2' ``` ## Documentation diff --git a/android/gradle.properties b/android/gradle.properties index c657979b6c3..a478149a0ea 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.56.1 +VERSION_NAME=1.56.2 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 44fe5926955..0d57d4405a5 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.56.1" + spec.version = "1.56.2" 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.56.1/filament-v1.56.1-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.56.2/filament-v1.56.2-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 bbca1748ed7..8022284f92c 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.56.1", + "version": "1.56.2", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js",