Skip to content

Commit 915b8a4

Browse files
committed
Address PR comments
Move enablement check for prewarming to the platform API, and add the ability to specify devices that are or aren't supported. Additionally, update naming for enablement APIs, and add missing calls to terminate, etc.
1 parent 0a79c3a commit 915b8a4

File tree

9 files changed

+95
-31
lines changed

9 files changed

+95
-31
lines changed

filament/backend/include/backend/Platform.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -341,22 +341,23 @@ class UTILS_PUBLIC Platform {
341341
GpuContextPriority gpuContextPriority = GpuContextPriority::DEFAULT;
342342

343343
/**
344-
* Bypass the staging buffer because the device is of Unified Memory Architecture.
344+
* Allows asynchronous pipeline cache preloading, if supported on this device.
345345
* This is only supported for:
346346
* - VulkanPlatform
347+
* When the following device extensions are available:
348+
* - VK_KHR_dynamic_rendering
349+
* - VK_EXT_vertex_input_dynamic_state
350+
* Only if the selected implementation of VulkanPlatform returns true for
351+
* VulkanPlatform::isPipelineCachePrewarmingDeviceSupported() for the current device.
347352
*/
348-
bool vulkanEnableStagingBufferBypass = false;
353+
bool vulkanAllowAsyncPipelineCachePrewarming = false;
349354

350355
/**
351-
* Enables asynchronous pipeline cache preloading.
356+
* Bypass the staging buffer because the device is of Unified Memory Architecture.
352357
* This is only supported for:
353358
* - VulkanPlatform
354-
* When the following device extensions are available:
355-
* - VK_KHR_dynamic_rendering
356-
* - VK_EXT_vertex_input_dynamic_state
357-
* And is dependent on Vulkan driver implementation on the current device.
358359
*/
359-
bool vulkanEnableAsyncPipelineCachePrewarming = false;
360+
bool vulkanEnableStagingBufferBypass = false;
360361

361362
/**
362363
* Asynchronous mode for the engine. Defines how asynchronous operations are handled.

filament/backend/include/backend/platforms/VulkanPlatform.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,30 @@ class VulkanPlatform : public Platform, utils::PrivateImplementation<VulkanPlatf
263263
*/
264264
virtual ExtensionSet getRequiredInstanceExtensions() { return {}; }
265265

266+
/**
267+
* Determines if pipeline cache prewarming is supported by the current device. Should be
268+
* implemented by derived classes, as by default, this will simply return false.
269+
*
270+
* @return true if pipeline cache prewarming is safe to be attempted on this device, false
271+
* if not.
272+
*/
273+
virtual bool isPipelineCachePrewarmingDeviceSupported() const noexcept;
274+
275+
/**
276+
* This determines, regardless of whether or not pipeline cache prewarming
277+
* is supported by a specific device, if async pipeline cache prewarming should
278+
* be enabled in the current application. This depends on:
279+
* - if it has been marked as supported for the current device
280+
* - if it is allowed in the driver config
281+
* - if parallel shader compilation is NOT disabled in the driver config
282+
* - if dynamic rendering is supported by the current device
283+
* - if vertex input dynamic state is supported by the current device
284+
*
285+
* @return true if pipeline cache prewarming has been enabled (supported + allowed) on
286+
* this device AND in this application, false if not.
287+
*/
288+
bool isAsyncPipelineCachePrewarmingEnabled() const noexcept;
289+
266290
/**
267291
* Destroy the swapchain.
268292
* @param handle The handle returned by createSwapChain()

filament/backend/src/vulkan/VulkanContext.h

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -158,16 +158,20 @@ struct VulkanContext {
158158
return mVertexInputDynamicStateSupported;
159159
}
160160

161-
inline bool asyncPipelineCachePrewarmingEnabled() const noexcept {
162-
return mAsyncPipelineCachePrewarmingEnabled;
161+
inline bool pipelineCreationFeedbackSupported() const noexcept {
162+
return mPipelineCreationFeedbackSupported;
163163
}
164164

165-
inline bool stagingBufferBypassEnabled() const noexcept {
166-
return mStagingBufferBypassEnabled;
165+
inline bool asyncPipelineCachePrewarmingAllowed() const noexcept {
166+
return mAsyncPipelineCachePrewarmingAllowed;
167167
}
168168

169-
inline bool pipelineCreationFeedbackSupported() const noexcept {
170-
return mPipelineCreationFeedbackSupported;
169+
inline bool parallelShaderCompilationDisabled() const noexcept {
170+
return mParallelShaderCompileDisabled;
171+
}
172+
173+
inline bool stagingBufferBypassEnabled() const noexcept {
174+
return mStagingBufferBypassEnabled;
171175
}
172176

173177
private:
@@ -191,10 +195,6 @@ struct VulkanContext {
191195

192196
VkExternalFenceHandleTypeFlags mFenceExportFlags = {};
193197

194-
// These are options that can be enabled or disabled at an application level.
195-
bool mAsyncPipelineCachePrewarmingEnabled = false;
196-
bool mStagingBufferBypassEnabled = false;
197-
198198
// These are options that are either supported or not supported in the current
199199
// device and instance.
200200
bool mDebugMarkersSupported = false;
@@ -206,6 +206,11 @@ struct VulkanContext {
206206
bool mProtectedMemorySupported = false;
207207
bool mVertexInputDynamicStateSupported = false;
208208

209+
// These are options that can be enabled or disabled at an application level.
210+
bool mAsyncPipelineCachePrewarmingAllowed = false;
211+
bool mParallelShaderCompileDisabled = false;
212+
bool mStagingBufferBypassEnabled = false;
213+
209214
fvkutils::VkFormatList mDepthStencilFormats;
210215
fvkutils::VkFormatList mBlittableDepthStencilFormats;
211216

filament/backend/src/vulkan/VulkanDriver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1500,7 +1500,7 @@ bool VulkanDriver::isStereoSupported() {
15001500
}
15011501

15021502
bool VulkanDriver::isParallelShaderCompileSupported() {
1503-
return VulkanPipelineCache::isAsyncPrewarmingSupported(mContext);
1503+
return mPlatform->isAsyncPipelineCachePrewarmingEnabled();
15041504
}
15051505

15061506
bool VulkanDriver::isDepthStencilResolveSupported() {

filament/backend/src/vulkan/VulkanPipelineCache.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,17 @@ void printPipelineFeedbackInfo(VkPipelineCreationFeedbackCreateInfo const& feedb
6969

7070
} // namespace
7171

72-
VulkanPipelineCache::VulkanPipelineCache(DriverBase& driver, VkDevice device, VulkanContext const& context)
72+
VulkanPipelineCache::VulkanPipelineCache(DriverBase& driver, VkDevice device, VulkanContext const& context, bool isAsyncPrewarmingEnabled)
7373
: mDevice(device),
7474
mCallbackManager(driver),
75-
mContext(context) {
75+
mContext(context),
76+
mIsAsyncPrewarmingEnabled(isAsyncPrewarmingEnabled) {
7677
VkPipelineCacheCreateInfo createInfo = {
7778
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
7879
};
7980
bluevk::vkCreatePipelineCache(mDevice, &createInfo, VKALLOC, &mPipelineCache);
8081

81-
if (isAsyncPrewarmingSupported(context)) {
82+
if (mIsAsyncPrewarmingEnabled) {
8283
mCompilerThreadPool.init(
8384
/*threadCount=*/1,
8485
[]() {
@@ -107,6 +108,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::getOrCreatePipelin
107108
.handle = createPipeline(mPipelineRequirements),
108109
.lastUsed = mCurrentTime,
109110
};
111+
assert_invariant(cacheEntry.handle != nullptr && "Pipeline handle is nullptr");
110112
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
111113
}
112114

@@ -361,6 +363,9 @@ void VulkanPipelineCache::terminate() noexcept {
361363
mPipelines.clear();
362364
resetBoundPipeline();
363365

366+
mCallbackManager.terminate();
367+
mCompilerThreadPool.terminate();
368+
364369
vkDestroyPipelineCache(mDevice, mPipelineCache, VKALLOC);
365370
}
366371

filament/backend/src/vulkan/VulkanPipelineCache.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,6 @@ class VulkanPipelineCache {
4848
VulkanPipelineCache(VulkanPipelineCache const&) = delete;
4949
VulkanPipelineCache& operator=(VulkanPipelineCache const&) = delete;
5050

51-
static inline bool isAsyncPrewarmingSupported(VulkanContext const& context) {
52-
return context.asyncPipelineCachePrewarmingEnabled() &&
53-
context.isDynamicRenderingSupported() &&
54-
context.isVertexInputDynamicStateSupported();
55-
}
56-
5751
static constexpr uint32_t SHADER_MODULE_COUNT = 2;
5852
static constexpr uint32_t VERTEX_ATTRIBUTE_COUNT = MAX_VERTEX_ATTRIBUTE_COUNT;
5953

@@ -94,7 +88,18 @@ class VulkanPipelineCache {
9488

9589
static_assert(sizeof(RasterState) == 16, "RasterState must not have implicit padding.");
9690

97-
VulkanPipelineCache(DriverBase& driver, VkDevice device, VulkanContext const& context);
91+
/**
92+
* Creates a new instance of a pipeline cache for graphics pipelines.
93+
*
94+
* @param driver The driver this is being instantiated for. This is used only for construction of
95+
* the callback manager, which references the driver for scheduling callbacks.
96+
* @param device The device that the pipelines will be created and run on.
97+
* @param context Information about the current instance of Vulkan, such as supported extensions,
98+
* and enabled features.
99+
* @param isAsyncPrewarmingEnabled true if async cache prewarming is enabled (in which case a
100+
* threadpool for such jobs will be spawned), false if not.
101+
*/
102+
VulkanPipelineCache(DriverBase& driver, VkDevice device, VulkanContext const& context, bool isAsyncPrewarmingEnabled = false);
98103

99104
// Loads a fake pipeline into memory on a separate thread, with the intent of
100105
// preloading the Vulkan cache with enough information to have a cache hit when
@@ -235,6 +240,9 @@ class VulkanPipelineCache {
235240
CallbackManager mCallbackManager;
236241

237242
[[maybe_unused]] VulkanContext const& mContext;
243+
244+
// Keep track of whether or not cache prewarming is enabled.
245+
bool mIsAsyncPrewarmingEnabled = false;
238246
};
239247

240248
} // namespace filament::backend

filament/backend/src/vulkan/platform/VulkanPlatform.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,9 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
227227
#if defined(__APPLE__)
228228
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
229229
#endif
230+
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME,
230231
VK_KHR_MULTIVIEW_EXTENSION_NAME,
231-
232+
VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME,
232233
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
233234
VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME,
234235
#endif
@@ -743,6 +744,19 @@ void VulkanPlatform::destroySync(Platform::Sync* sync) noexcept {
743744
delete sync;
744745
}
745746

747+
bool VulkanPlatform::isPipelineCachePrewarmingDeviceSupported() const noexcept {
748+
// By default, this should not be enabled, as it will not work on most devices.
749+
return false;
750+
}
751+
752+
bool VulkanPlatform::isAsyncPipelineCachePrewarmingEnabled() const noexcept {
753+
return isPipelineCachePrewarmingDeviceSupported() &&
754+
mImpl->mContext.asyncPipelineCachePrewarmingAllowed() &&
755+
!mImpl->mContext.parallelShaderCompilationDisabled() &&
756+
mImpl->mContext.isDynamicRenderingSupported() &&
757+
mImpl->mContext.isVertexInputDynamicStateSupported();
758+
}
759+
746760
VkInstance VulkanPlatform::getInstance() const noexcept {
747761
return mImpl->mInstance;
748762
}
@@ -936,7 +950,8 @@ void VulkanPlatform::queryAndSetDeviceFeatures(Platform::DriverConfig const& dri
936950
}
937951

938952
// Pass along relevant driver config (feature flags)
939-
context.mAsyncPipelineCachePrewarmingEnabled = driverConfig.vulkanEnableAsyncPipelineCachePrewarming;
953+
context.mAsyncPipelineCachePrewarmingAllowed = driverConfig.vulkanAllowAsyncPipelineCachePrewarming;
954+
context.mParallelShaderCompileDisabled = driverConfig.disableParallelShaderCompile;
940955
context.mStagingBufferBypassEnabled = driverConfig.vulkanEnableStagingBufferBypass;
941956

942957
// We know we need to allocate the protected version of the VK objects

filament/src/details/Engine.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ backend::Platform::DriverConfig getDriverConfig(FEngine* instance) {
115115
.metalDisablePanicOnDrawableFailure =
116116
instance->getConfig().metalDisablePanicOnDrawableFailure,
117117
.gpuContextPriority = instance->getConfig().gpuContextPriority,
118+
.vulkanAllowAsyncPipelineCachePrewarming =
119+
instance->features.backend.vulkan.allow_pipeline_cache_prewarming,
118120
.vulkanEnableStagingBufferBypass =
119121
instance->features.backend.vulkan.enable_staging_buffer_bypass,
120122
.asynchronousMode = instance->features.backend.enable_asynchronous_operation ?

filament/src/details/Engine.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,10 @@ class FEngine : public Engine {
783783
bool assert_native_window_is_valid = false;
784784
} opengl;
785785
struct {
786+
// In certain GPU drivers, graphics pipelines are cached based on a subset of their
787+
// parameters. In those cases, we can create fake pipelines ahead of time to ensure
788+
// a cache hit when creating graphics pipelines at draw time, eliminating hitching.
789+
bool allow_pipeline_cache_prewarming = false;
786790
// On Unified Memory Architecture device, it is possible to bypass using the staging
787791
// buffer. This is an experimental feature that still needs to be implemented fully
788792
// before it can be fully enabled.

0 commit comments

Comments
 (0)