[metal] Audit and document device guards for Create* functions. - As a part of ML analysis for CPU bottlenecks, a detailed report identified that lock contention in Metal accounted for ~6.6% of CPU time. The major one is the one for CreateBindGroup which we unfortunately need to conditionally gate for now since the ArgumentBuffer implementation is WIP. Ideally, as we implement that, it would be best if we could put in place the synchronizations such that we don't need to regress to needing the lock. Bug: 363031535 Change-Id: I33045d01ae1013f8ab05a2174d40b1475b871734 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/352915 Reviewed-by: Kai Ninomiya <kainino@chromium.org> Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/native/metal/DeviceMTL.h b/src/dawn/native/metal/DeviceMTL.h index f4213d1..e6765c4 100644 --- a/src/dawn/native/metal/DeviceMTL.h +++ b/src/dawn/native/metal/DeviceMTL.h
@@ -85,6 +85,12 @@ bool BackendWillValidateMultiDraw() const override; + std::optional<DeviceGuard> UseGuardForCreateBindGroup() override; + std::optional<DeviceGuard> UseGuardForCreateBindGroupLayout() override; + std::optional<DeviceGuard> UseGuardForCreateBuffer() override; + std::optional<DeviceGuard> UseGuardForCreateSampler() override; + std::optional<DeviceGuard> UseGuardForCreateTexture() override; + // Get a MTLBuffer that can be used as a mock in a no-op blit encoder based on filling this // single-byte buffer id<MTLBuffer> GetMockBlitMtlBuffer();
diff --git a/src/dawn/native/metal/DeviceMTL.mm b/src/dawn/native/metal/DeviceMTL.mm index 47bc253..0385087 100644 --- a/src/dawn/native/metal/DeviceMTL.mm +++ b/src/dawn/native/metal/DeviceMTL.mm
@@ -412,6 +412,53 @@ return true; } +std::optional<DeviceGuard> Device::UseGuardForCreateBindGroup() { + // TODO(crbug.com/363031535): When argument buffers are enabled, BindGroup::InitializeImpl() + // encodes into a shared MTLArgumentEncoder stored on the BindGroupLayout without + // synchronization. We can remove this guard if we synchronize the shared encoder's state in the + // future. + if (IsToggleEnabled(Toggle::MetalUseArgumentBuffers)) { + return GetGuard(); + } + return std::nullopt; +} + +std::optional<DeviceGuard> Device::UseGuardForCreateBindGroupLayout() { + return std::nullopt; +} + +std::optional<DeviceGuard> Device::UseGuardForCreateBuffer() { + // Buffer::Initialize() clears padding bytes (when LazyClearResourceOnFirstUse is enabled) or + // initializes the buffer to non-zero (when NonzeroClearResourcesOnCreationForTesting is + // enabled) using the queue's shared CommandRecordingContext, which is not thread-safe. If we + // defer padding byte clearing to first use on the GPU in the future, we may be able to remove + // the LazyClearResourceOnFirstUse check here. + if (IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse) || + IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { + return GetGuard(); + } + return std::nullopt; +} + +std::optional<DeviceGuard> Device::UseGuardForCreateSampler() { + return std::nullopt; +} + +std::optional<DeviceGuard> Device::UseGuardForCreateTexture() { + // Texture::Create() clears textures at creation time when ShouldKeepInitialized() is true + // (for multi-subresource depth/stencil textures when LazyClearResourceOnFirstUse and + // MetalKeepMultisubresourceDepthStencilTexturesInitialized are enabled) or when + // NonzeroClearResourcesOnCreationForTesting is enabled. Clearing uses the queue's shared + // CommandRecordingContext and DynamicUploader, which are not thread-safe without the device + // lock. + if (IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting) || + (IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse) && + IsToggleEnabled(Toggle::MetalKeepMultisubresourceDepthStencilTexturesInitialized))) { + return GetGuard(); + } + return std::nullopt; +} + id<MTLBuffer> Device::GetMockBlitMtlBuffer() { if (mMockBlitMtlBuffer == nullptr) { mMockBlitMtlBuffer.Acquire(