dawn::native::vulkan: Handle Texture semaphores in Texture
Instead of gathering the various possible wait / signal semaphores in
QueueVk::SubmitPendingCommands as well as exporting semaphores etc.
This makes all special synchronization textures expose hooks to modify
the CommandRecordingContext before a submit, and cleanup hooks after the
submit. It keeps the semaphore logic close together in TextureVk and
will help make SwapChainTexture handle it's semaphore internally in
future CLs.
Bug:
Change-Id: I8c67cb5678335c94b820d1c43ec317280665357a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/206615
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/vulkan/CommandRecordingContextVk.h b/src/dawn/native/vulkan/CommandRecordingContextVk.h
index 7c3f096..fc4bea3 100644
--- a/src/dawn/native/vulkan/CommandRecordingContextVk.h
+++ b/src/dawn/native/vulkan/CommandRecordingContextVk.h
@@ -37,7 +37,7 @@
namespace dawn::native::vulkan {
-class ImportedTextureBase;
+class Texture;
// Wrapping class that currently associates a command buffer to it's corresponding pool.
// TODO(dawn:1601) Revisit this structure since it is where the 1:1 mapping is implied.
@@ -67,7 +67,7 @@
// - Acquiring extra semaphores or fences.
// - Exporting extra semaphore or fences.
// - and more!
- absl::flat_hash_set<raw_ptr<ImportedTextureBase>> specialSyncTextures;
+ absl::flat_hash_set<raw_ptr<Texture>> specialSyncTextures;
// Mappable buffers which will be eagerly transitioned to usage MapRead or MapWrite after
// VkSubmit.
diff --git a/src/dawn/native/vulkan/QueueVk.cpp b/src/dawn/native/vulkan/QueueVk.cpp
index d5d12d2..14a3ac6 100644
--- a/src/dawn/native/vulkan/QueueVk.cpp
+++ b/src/dawn/native/vulkan/QueueVk.cpp
@@ -40,7 +40,6 @@
#include "dawn/native/vulkan/CommandRecordingContextVk.h"
#include "dawn/native/vulkan/DeviceVk.h"
#include "dawn/native/vulkan/FencedDeleter.h"
-#include "dawn/native/vulkan/SharedFenceVk.h"
#include "dawn/native/vulkan/TextureVk.h"
#include "dawn/native/vulkan/UniqueVkHandle.h"
#include "dawn/native/vulkan/UtilsVulkan.h"
@@ -332,42 +331,8 @@
device, &mRecordingContext, mRecordingContext.mappableBuffersForEagerTransition);
}
- // Create an external semaphore for each external textures used in the pending submit.
- std::vector<UniqueVkHandle<VkSemaphore>> externalTextureSemaphores(
- mRecordingContext.specialSyncTextures.size());
- for (size_t i = 0; i < mRecordingContext.specialSyncTextures.size(); ++i) {
- VkSemaphore semaphore;
- DAWN_TRY_ASSIGN(semaphore,
- device->GetExternalSemaphoreService()->CreateExportableSemaphore());
- externalTextureSemaphores[i] = {device, semaphore};
- }
-
- // Transition eagerly all used external textures for export.
for (auto texture : mRecordingContext.specialSyncTextures) {
- texture->TransitionEagerlyForExport(&mRecordingContext);
-
- // TODO(330385376): Remove once ExternalImageDescriptorVk is removed.
- std::vector<VkSemaphore> waitRequirements = texture->AcquireWaitRequirements();
- mRecordingContext.waitSemaphores.insert(mRecordingContext.waitSemaphores.end(),
- waitRequirements.begin(), waitRequirements.end());
-
- SharedResourceMemoryContents* contents = texture->GetSharedResourceMemoryContents();
- if (contents != nullptr) {
- SharedTextureMemoryBase::PendingFenceList fences;
- contents->AcquirePendingFences(&fences);
-
- for (const auto& fence : fences) {
- // All semaphores are binary semaphores.
- DAWN_ASSERT(fence.signaledValue == 1u);
- ExternalSemaphoreHandle semaphoreHandle =
- ToBackend(fence.object)->GetHandle().Get();
-
- VkSemaphore semaphore;
- DAWN_TRY_ASSIGN(semaphore, device->GetExternalSemaphoreService()->ImportSemaphore(
- semaphoreHandle));
- mRecordingContext.waitSemaphores.push_back(semaphore);
- }
- }
+ DAWN_TRY(texture->OnBeforeSubmit(&mRecordingContext));
}
DAWN_TRY(CheckVkSuccess(device->fn.EndCommandBuffer(mRecordingContext.commandBuffer),
@@ -376,10 +341,6 @@
std::vector<VkPipelineStageFlags> dstStageMasks(mRecordingContext.waitSemaphores.size(),
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
- for (auto& externalTextureSemaphore : externalTextureSemaphores) {
- mRecordingContext.signalSemaphores.push_back(externalTextureSemaphore.Get());
- }
-
VkSubmitInfo submitInfo;
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pNext = nullptr;
@@ -419,19 +380,9 @@
mCommandsInFlight.Enqueue(submittedCommands, lastSubmittedSerial);
}
- auto externalTextureSemaphoreIter = externalTextureSemaphores.begin();
for (auto texture : mRecordingContext.specialSyncTextures) {
- // Export the signal semaphore.
- ExternalSemaphoreHandle semaphoreHandle;
- DAWN_TRY_ASSIGN(semaphoreHandle, device->GetExternalSemaphoreService()->ExportSemaphore(
- externalTextureSemaphoreIter->Get()));
- ++externalTextureSemaphoreIter;
-
- // Update all external textures, eagerly transitioned in the submit, with the exported
- // handles.
- texture->UpdateExternalSemaphoreHandle(semaphoreHandle);
+ DAWN_TRY(texture->OnAfterSubmit());
}
- DAWN_ASSERT(externalTextureSemaphoreIter == externalTextureSemaphores.end());
mRecordingContext = CommandRecordingContext();
DAWN_TRY(PrepareRecordingContext());
diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp
index 0b8f8c5..2cabce5 100644
--- a/src/dawn/native/vulkan/TextureVk.cpp
+++ b/src/dawn/native/vulkan/TextureVk.cpp
@@ -45,6 +45,7 @@
#include "dawn/native/vulkan/QueueVk.h"
#include "dawn/native/vulkan/ResourceHeapVk.h"
#include "dawn/native/vulkan/ResourceMemoryAllocatorVk.h"
+#include "dawn/native/vulkan/SharedFenceVk.h"
#include "dawn/native/vulkan/UtilsVulkan.h"
#include "dawn/native/vulkan/VulkanError.h"
@@ -1269,6 +1270,13 @@
std::vector<VkImageMemoryBarrier>* barriers,
size_t transitionBarrierStart) {}
+MaybeError Texture::OnBeforeSubmit(CommandRecordingContext*) {
+ DAWN_UNREACHABLE();
+}
+MaybeError Texture::OnAfterSubmit() {
+ DAWN_UNREACHABLE();
+}
+
//
// InternalTexture
//
@@ -1464,8 +1472,23 @@
ToBackend(GetDevice())
->GetExternalSemaphoreService()
->CloseHandle(mExternalSemaphoreHandle);
+ mExternalSemaphoreHandle = kNullExternalSemaphoreHandle;
}
- mExternalSemaphoreHandle = kNullExternalSemaphoreHandle;
+}
+
+void ImportedTextureBase::DestroyImpl() {
+ // TODO(crbug.com/dawn/831): DestroyImpl is called from two places.
+ // - It may be called if the texture is explicitly destroyed with APIDestroy.
+ // This case is NOT thread-safe and needs proper synchronization with other
+ // simultaneous uses of the texture.
+ // - It may be called when the last ref to the texture is dropped and the texture
+ // is implicitly destroyed. This case is thread-safe because there are no
+ // other threads using the texture since there are no other live refs.
+ if (mPendingSemaphore != VK_NULL_HANDLE) {
+ ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mPendingSemaphore);
+ mPendingSemaphore = VK_NULL_HANDLE;
+ }
+ Texture::DestroyImpl();
}
void ImportedTextureBase::TransitionEagerlyForExport(CommandRecordingContext* recordingContext) {
@@ -1507,15 +1530,6 @@
nullptr, 0, nullptr, 1, &barrier);
}
-void ImportedTextureBase::UpdateExternalSemaphoreHandle(ExternalSemaphoreHandle handle) {
- if (mExternalSemaphoreHandle != kNullExternalSemaphoreHandle) {
- ToBackend(GetDevice())
- ->GetExternalSemaphoreService()
- ->CloseHandle(mExternalSemaphoreHandle);
- }
- mExternalSemaphoreHandle = handle;
-}
-
bool ImportedTextureBase::CanReuseWithoutBarrier(wgpu::TextureUsage lastUsage,
wgpu::TextureUsage usage,
wgpu::ShaderStage lastShaderStage,
@@ -1655,6 +1669,40 @@
return {};
}
+MaybeError ImportedTextureBase::OnBeforeSubmit(CommandRecordingContext* context) {
+ // On every submit using an exportable texture, we eagerly prepare for an export. This avoids
+ // the need to schedule an almost empty submit just for exporting later. To export we both need
+ // to transition the resource to export it, and need an exportable semaphore for future
+ // synchronization.
+ TransitionEagerlyForExport(context);
+
+ // Create the external semaphore and add it to be signaled, but only mark it pending: if
+ // anything fails during the submit we still keep the previously signaled exportable semaphore.
+ // Note that VUID-VkSemaphoreGetFdInfoKHR-handleType-01135 requires that only signaled
+ // semaphores be exported, so the export must be done in OnAfterSubmit.
+ auto semaphoreService = ToBackend(GetDevice())->GetExternalSemaphoreService();
+ DAWN_TRY_ASSIGN(mPendingSemaphore, semaphoreService->CreateExportableSemaphore());
+ context->signalSemaphores.push_back(mPendingSemaphore);
+
+ return {};
+}
+
+MaybeError ImportedTextureBase::OnAfterSubmit() {
+ Device* device = ToBackend(GetDevice());
+
+ // The submit succeeded, we can replace the previous external semaphore with the pending one.
+ if (mExternalSemaphoreHandle != kNullExternalSemaphoreHandle) {
+ device->GetExternalSemaphoreService()->CloseHandle(mExternalSemaphoreHandle);
+ }
+
+ DAWN_TRY_ASSIGN(mExternalSemaphoreHandle,
+ device->GetExternalSemaphoreService()->ExportSemaphore(mPendingSemaphore));
+ ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mPendingSemaphore);
+ mPendingSemaphore = VK_NULL_HANDLE;
+
+ return {};
+}
+
//
// ExternalVkImageTexture
//
@@ -1689,7 +1737,7 @@
mExternalAllocation = VK_NULL_HANDLE;
}
- Texture::DestroyImpl();
+ ImportedTextureBase::DestroyImpl();
}
MaybeError ExternalVkImageTexture::Initialize(const ExternalImageDescriptorVk* descriptor,
@@ -1793,8 +1841,11 @@
return {};
}
-std::vector<VkSemaphore> ExternalVkImageTexture::AcquireWaitRequirements() {
- return std::move(mWaitRequirements);
+MaybeError ExternalVkImageTexture::OnBeforeSubmit(CommandRecordingContext* context) {
+ context->waitSemaphores.insert(context->waitSemaphores.end(), mWaitRequirements.begin(),
+ mWaitRequirements.end());
+ mWaitRequirements.clear();
+ return ImportedTextureBase::OnBeforeSubmit(context);
}
//
@@ -1821,7 +1872,7 @@
// other threads using the texture since there are no other live refs.
mSharedTextureMemoryObjects = {};
- Texture::DestroyImpl();
+ ImportedTextureBase::DestroyImpl();
}
void SharedTexture::Initialize(SharedTextureMemory* memory) {
@@ -1841,6 +1892,27 @@
mPendingAcquireNewLayout = pendingAcquireNewLayout;
}
+MaybeError SharedTexture::OnBeforeSubmit(CommandRecordingContext* context) {
+ Device* device = ToBackend(GetDevice());
+ SharedResourceMemoryContents* contents = GetSharedResourceMemoryContents();
+
+ SharedTextureMemoryBase::PendingFenceList fences;
+ contents->AcquirePendingFences(&fences);
+
+ for (const auto& fence : fences) {
+ // All semaphores are binary semaphores.
+ DAWN_ASSERT(fence.signaledValue == 1u);
+ ExternalSemaphoreHandle semaphoreHandle = ToBackend(fence.object)->GetHandle().Get();
+
+ VkSemaphore semaphore;
+ DAWN_TRY_ASSIGN(semaphore,
+ device->GetExternalSemaphoreService()->ImportSemaphore(semaphoreHandle));
+ context->waitSemaphores.push_back(semaphore);
+ }
+
+ return ImportedTextureBase::OnBeforeSubmit(context);
+}
+
//
// TextureView
//
diff --git a/src/dawn/native/vulkan/TextureVk.h b/src/dawn/native/vulkan/TextureVk.h
index 33ae96f..88398b9 100644
--- a/src/dawn/native/vulkan/TextureVk.h
+++ b/src/dawn/native/vulkan/TextureVk.h
@@ -102,6 +102,11 @@
MaybeError EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
const SubresourceRange& range);
+ // Adds any special synchronization once for the current submit.
+ virtual MaybeError OnBeforeSubmit(CommandRecordingContext* context);
+ // Cleans up after the submit.
+ virtual MaybeError OnAfterSubmit();
+
void SetLabelHelper(const char* prefix);
// Dawn API
@@ -191,14 +196,6 @@
// TODO(330385376): Merge in SharedTexture once ExternalImageDescriptorVk is fully removed.
class ImportedTextureBase : public Texture {
public:
- virtual std::vector<VkSemaphore> AcquireWaitRequirements() { return {}; }
-
- // Eagerly transition the texture for export.
- void TransitionEagerlyForExport(CommandRecordingContext* recordingContext);
-
- // Update the 'ExternalSemaphoreHandle' to be used for export with the newly submitted one.
- void UpdateExternalSemaphoreHandle(ExternalSemaphoreHandle handle);
-
// If needed, modifies the VkImageMemoryBarrier to perform a queue ownership transfer etc.
void TweakTransition(CommandRecordingContext* recordingContext,
std::vector<VkImageMemoryBarrier>* barriers,
@@ -214,9 +211,18 @@
VkImageLayout* releasedOldLayout,
VkImageLayout* releasedNewLayout);
+ MaybeError OnBeforeSubmit(CommandRecordingContext* context) override;
+ MaybeError OnAfterSubmit() override;
+
protected:
using Texture::Texture;
~ImportedTextureBase() override;
+
+ void DestroyImpl() override;
+
+ // Eagerly transition the texture for export.
+ void TransitionEagerlyForExport(CommandRecordingContext* recordingContext);
+
// The states of an external texture:
// PendingAcquire: Initialized as an external texture already, but unavailable for access yet.
// Acquired: Ready for access.
@@ -242,6 +248,8 @@
// If the texture was ever used, represents a semaphore signaled once operations on the texture
// are done so that the receiver of the export can synchronize properly.
ExternalSemaphoreHandle mExternalSemaphoreHandle = kNullExternalSemaphoreHandle;
+ // The pending semaphore
+ VkSemaphore mPendingSemaphore = VK_NULL_HANDLE;
};
// A texture created from an VkImage that references an external memory object.
@@ -264,7 +272,7 @@
VkImageLayout* releasedOldLayout,
VkImageLayout* releasedNewLayout);
- std::vector<VkSemaphore> AcquireWaitRequirements() override;
+ MaybeError OnBeforeSubmit(CommandRecordingContext* context) override;
private:
using ImportedTextureBase::ImportedTextureBase;
@@ -283,6 +291,8 @@
SharedTextureMemory* memory,
const UnpackedPtr<TextureDescriptor>& textureDescriptor);
+ MaybeError OnBeforeSubmit(CommandRecordingContext* context) override;
+
void SetPendingAcquire(VkImageLayout pendingAcquireOldLayout,
VkImageLayout pendingAcquireNewLayout);