Add eager Transition support for external textures

This eagerly transitions external textures to be ready for export
on every submit. With this support, we can save the current submit
of export.

Bug: chromium:1258986
Change-Id: I92c2019ff486afc24adc190a1f7b2f85f416cd52
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/97642
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jie A Chen <jie.a.chen@intel.com>
This commit is contained in:
jchen10 2022-08-11 01:36:21 +00:00 committed by Dawn LUCI CQ
parent 490294e6fe
commit 7126ed9111
11 changed files with 250 additions and 112 deletions

View File

@ -14,23 +14,30 @@
#ifndef SRC_DAWN_NATIVE_VULKAN_COMMANDRECORDINGCONTEXT_H_
#define SRC_DAWN_NATIVE_VULKAN_COMMANDRECORDINGCONTEXT_H_
#include <set>
#include <vector>
#include "dawn/common/vulkan_platform.h"
#include "dawn/native/vulkan/BufferVk.h"
namespace dawn::native::vulkan {
class Texture;
// Used to track operations that are handled after recording.
// Currently only tracks semaphores, but may be used to do barrier coalescing in the future.
struct CommandRecordingContext {
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
std::vector<VkSemaphore> waitSemaphores = {};
std::vector<VkSemaphore> signalSemaphores = {};
// The internal buffers used in the workaround of texture-to-texture copies with compressed
// formats.
std::vector<Ref<Buffer>> tempBuffers;
// External textures that will be eagerly transitioned just before VkSubmit. The textures are
// kept alive by the CommandBuffer so they don't need to be Ref-ed.
std::set<Texture*> externalTexturesForEagerTransition;
// For Device state tracking only.
VkCommandPool commandPool = VK_NULL_HANDLE;
bool used = false;

View File

@ -15,6 +15,7 @@
#include "dawn/native/vulkan/DeviceVk.h"
#include "dawn/common/Log.h"
#include "dawn/common/NonCopyable.h"
#include "dawn/common/Platform.h"
#include "dawn/native/BackendConnection.h"
#include "dawn/native/ChainUtils_autogen.h"
@ -47,6 +48,35 @@
namespace dawn::native::vulkan {
namespace {
// Destroy the semaphore when out of scope.
class ScopedSignalSemaphore : public NonMovable {
public:
ScopedSignalSemaphore(Device* device, VkSemaphore semaphore)
: mDevice(device), mSemaphore(semaphore) {}
~ScopedSignalSemaphore() {
if (mSemaphore != VK_NULL_HANDLE) {
ASSERT(mDevice);
mDevice->fn.DestroySemaphore(mDevice->GetVkDevice(), mSemaphore, nullptr);
}
}
VkSemaphore Get() { return mSemaphore; }
VkSemaphore Detach() {
VkSemaphore semaphore = mSemaphore;
mSemaphore = VK_NULL_HANDLE;
return semaphore;
}
VkSemaphore* InitializeInto() { return &mSemaphore; }
private:
Device* mDevice = nullptr;
VkSemaphore mSemaphore = VK_NULL_HANDLE;
};
} // namespace
// static
ResultOrError<Ref<Device>> Device::Create(Adapter* adapter, const DeviceDescriptor* descriptor) {
Ref<Device> device = AcquireRef(new Device(adapter, descriptor));
@ -245,6 +275,10 @@ ResourceMemoryAllocator* Device::GetResourceMemoryAllocator() const {
return mResourceMemoryAllocator.get();
}
external_semaphore::Service* Device::GetExternalSemaphoreService() const {
return mExternalSemaphoreService.get();
}
void Device::EnqueueDeferredDeallocation(DescriptorSetAllocator* allocator) {
mDescriptorAllocatorsPendingDeallocation.Enqueue(allocator, GetPendingCommandSerial());
}
@ -260,6 +294,19 @@ MaybeError Device::SubmitPendingCommands() {
return {};
}
ScopedSignalSemaphore scopedSignalSemaphore(this, VK_NULL_HANDLE);
if (mRecordingContext.externalTexturesForEagerTransition.size() > 0) {
// Create an external semaphore for all external textures that have been used in the pending
// submit.
DAWN_TRY_ASSIGN(*scopedSignalSemaphore.InitializeInto(),
mExternalSemaphoreService->CreateExportableSemaphore());
}
// Transition eagerly all used external textures for export.
for (auto* texture : mRecordingContext.externalTexturesForEagerTransition) {
texture->TransitionEagerlyForExport(&mRecordingContext);
}
DAWN_TRY(
CheckVkSuccess(fn.EndCommandBuffer(mRecordingContext.commandBuffer), "vkEndCommandBuffer"));
@ -274,9 +321,8 @@ MaybeError Device::SubmitPendingCommands() {
submitInfo.pWaitDstStageMask = dstStageMasks.data();
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &mRecordingContext.commandBuffer;
submitInfo.signalSemaphoreCount =
static_cast<uint32_t>(mRecordingContext.signalSemaphores.size());
submitInfo.pSignalSemaphores = AsVkArray(mRecordingContext.signalSemaphores.data());
submitInfo.signalSemaphoreCount = (scopedSignalSemaphore.Get() == VK_NULL_HANDLE ? 0 : 1);
submitInfo.pSignalSemaphores = AsVkArray(scopedSignalSemaphore.InitializeInto());
VkFence fence = VK_NULL_HANDLE;
DAWN_TRY_ASSIGN(fence, GetUnusedFence());
@ -293,10 +339,6 @@ MaybeError Device::SubmitPendingCommands() {
for (VkSemaphore semaphore : mRecordingContext.waitSemaphores) {
mDeleter->DeleteWhenUnused(semaphore);
}
for (VkSemaphore semaphore : mRecordingContext.signalSemaphores) {
mDeleter->DeleteWhenUnused(semaphore);
}
IncrementLastSubmittedCommandSerial();
ExecutionSerial lastSubmittedSerial = GetLastSubmittedCommandSerial();
mFencesInFlight.emplace(fence, lastSubmittedSerial);
@ -304,6 +346,26 @@ MaybeError Device::SubmitPendingCommands() {
CommandPoolAndBuffer submittedCommands = {mRecordingContext.commandPool,
mRecordingContext.commandBuffer};
mCommandsInFlight.Enqueue(submittedCommands, lastSubmittedSerial);
if (mRecordingContext.externalTexturesForEagerTransition.size() > 0) {
// Export the signal semaphore.
ExternalSemaphoreHandle semaphoreHandle;
DAWN_TRY_ASSIGN(semaphoreHandle,
mExternalSemaphoreService->ExportSemaphore(scopedSignalSemaphore.Get()));
// The ownership of signal semaphore has been transferred, we no longer need to track it.
scopedSignalSemaphore.Detach();
// Update all external textures, eagerly transitioned in the submit, with the exported
// handle, and the duplicated handles.
bool first = true;
for (auto* texture : mRecordingContext.externalTexturesForEagerTransition) {
ExternalSemaphoreHandle handle =
(first ? semaphoreHandle
: mExternalSemaphoreService->DuplicateHandle(semaphoreHandle));
first = false;
texture->UpdateExternalSemaphoreHandle(handle);
}
}
mRecordingContext = CommandRecordingContext();
DAWN_TRY(PrepareRecordingContext());
@ -770,7 +832,6 @@ MaybeError Device::ImportExternalImage(const ExternalImageDescriptorVk* descript
ExternalMemoryHandle memoryHandle,
VkImage image,
const std::vector<ExternalSemaphoreHandle>& waitHandles,
VkSemaphore* outSignalSemaphore,
VkDeviceMemory* outAllocation,
std::vector<VkSemaphore>* outWaitSemaphores) {
const TextureDescriptor* textureDescriptor = FromAPI(descriptor->cTextureDescriptor);
@ -794,9 +855,6 @@ MaybeError Device::ImportExternalImage(const ExternalImageDescriptorVk* descript
VK_IMAGE_CREATE_ALIAS_BIT_KHR),
"External memory usage not supported");
// Create an external semaphore to signal when the texture is done being used
DAWN_TRY_ASSIGN(*outSignalSemaphore, mExternalSemaphoreService->CreateExportableSemaphore());
// Import the external image's memory
external_memory::MemoryImportParams importParams;
DAWN_TRY_ASSIGN(importParams, mExternalMemoryService->GetMemoryImportParams(descriptor, image));
@ -821,15 +879,12 @@ bool Device::SignalAndExportExternalTexture(
return !ConsumedError([&]() -> MaybeError {
DAWN_TRY(ValidateObject(texture));
VkSemaphore signalSemaphore;
ExternalSemaphoreHandle semaphoreHandle;
VkImageLayout releasedOldLayout;
VkImageLayout releasedNewLayout;
DAWN_TRY(texture->ExportExternalTexture(desiredLayout, &signalSemaphore, &releasedOldLayout,
DAWN_TRY(texture->ExportExternalTexture(desiredLayout, &semaphoreHandle, &releasedOldLayout,
&releasedNewLayout));
ExternalSemaphoreHandle semaphoreHandle;
DAWN_TRY_ASSIGN(semaphoreHandle,
mExternalSemaphoreService->ExportSemaphore(signalSemaphore));
semaphoreHandles->push_back(semaphoreHandle);
info->releasedOldLayout = releasedOldLayout;
info->releasedNewLayout = releasedNewLayout;
@ -856,7 +911,6 @@ TextureBase* Device::CreateTextureWrappingVulkanImage(
return nullptr;
}
VkSemaphore signalSemaphore = VK_NULL_HANDLE;
VkDeviceMemory allocation = VK_NULL_HANDLE;
std::vector<VkSemaphore> waitSemaphores;
waitSemaphores.reserve(waitHandles.size());
@ -869,18 +923,13 @@ TextureBase* Device::CreateTextureWrappingVulkanImage(
mExternalMemoryService.get()),
&result) ||
ConsumedError(ImportExternalImage(descriptor, memoryHandle, result->GetHandle(),
waitHandles, &signalSemaphore, &allocation,
&waitSemaphores)) ||
ConsumedError(
result->BindExternalMemory(descriptor, signalSemaphore, allocation, waitSemaphores))) {
waitHandles, &allocation, &waitSemaphores)) ||
ConsumedError(result->BindExternalMemory(descriptor, allocation, waitSemaphores))) {
// Delete the Texture if it was created
if (result != nullptr) {
result->Release();
}
// Clear the signal semaphore
fn.DestroySemaphore(GetVkDevice(), signalSemaphore, nullptr);
// Clear image memory
fn.FreeMemory(GetVkDevice(), allocation, nullptr);
@ -1015,11 +1064,6 @@ void Device::DestroyImpl() {
}
mRecordingContext.waitSemaphores.clear();
for (VkSemaphore semaphore : mRecordingContext.signalSemaphores) {
fn.DestroySemaphore(mVkDevice, semaphore, nullptr);
}
mRecordingContext.signalSemaphores.clear();
// Some commands might still be marked as in-flight if we shut down because of a device
// loss. Recycle them as unused so that we free them below.
RecycleCompletedCommands();

View File

@ -61,6 +61,7 @@ class Device final : public DeviceBase {
FencedDeleter* GetFencedDeleter() const;
RenderPassCache* GetRenderPassCache() const;
ResourceMemoryAllocator* GetResourceMemoryAllocator() const;
external_semaphore::Service* GetExternalSemaphoreService() const;
CommandRecordingContext* GetPendingRecordingContext();
MaybeError SubmitPendingCommands();
@ -216,7 +217,6 @@ class Device final : public DeviceBase {
ExternalMemoryHandle memoryHandle,
VkImage image,
const std::vector<ExternalSemaphoreHandle>& waitHandles,
VkSemaphore* outSignalSemaphore,
VkDeviceMemory* outAllocation,
std::vector<VkSemaphore>* outWaitSemaphores);
};

View File

@ -24,15 +24,18 @@ namespace dawn::native::vulkan {
using ExternalMemoryHandle = int;
// File descriptor
using ExternalSemaphoreHandle = int;
const ExternalSemaphoreHandle kNullExternalSemaphoreHandle = -1;
#elif DAWN_PLATFORM_IS(FUCHSIA)
// Really a Zircon vmo handle.
using ExternalMemoryHandle = zx_handle_t;
// Really a Zircon event handle.
using ExternalSemaphoreHandle = zx_handle_t;
const ExternalSemaphoreHandle kNullExternalSemaphoreHandle = ZX_HANDLE_INVALID;
#else
// Generic types so that the Null service can compile, not used for real handles
using ExternalMemoryHandle = void*;
using ExternalSemaphoreHandle = void*;
const ExternalSemaphoreHandle kNullExternalSemaphoreHandle = nullptr;
#endif
} // namespace dawn::native::vulkan

View File

@ -783,7 +783,6 @@ void Texture::InitializeForSwapChain(VkImage nativeImage) {
}
MaybeError Texture::BindExternalMemory(const ExternalImageDescriptorVk* descriptor,
VkSemaphore signalSemaphore,
VkDeviceMemory externalMemoryAllocation,
std::vector<VkSemaphore> waitSemaphores) {
Device* device = ToBackend(GetDevice());
@ -798,17 +797,53 @@ MaybeError Texture::BindExternalMemory(const ExternalImageDescriptorVk* descript
// Success, acquire all the external objects.
mExternalAllocation = externalMemoryAllocation;
mSignalSemaphore = signalSemaphore;
mWaitRequirements = std::move(waitSemaphores);
return {};
}
void Texture::TransitionEagerlyForExport(CommandRecordingContext* recordingContext) {
mExternalState = ExternalState::EagerlyTransitioned;
Aspect aspects = ComputeAspectsForSubresourceStorage();
ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
SubresourceRange range = {aspects, {0, 1}, {0, 1}};
wgpu::TextureUsage usage = mSubresourceLastUsages->Get(aspects, 0, 0);
std::vector<VkImageMemoryBarrier> barriers;
VkPipelineStageFlags srcStages = 0;
VkPipelineStageFlags dstStages = 0;
// Same usage as last.
TransitionUsageAndGetResourceBarrier(usage, range, &barriers, &srcStages, &dstStages);
ASSERT(barriers.size() == 1);
VkImageMemoryBarrier& barrier = barriers[0];
// The barrier must be paired with another barrier that will specify the dst access mask on the
// importing queue.
barrier.dstAccessMask = 0;
if (mDesiredExportLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
barrier.newLayout = mDesiredExportLayout;
}
Device* device = ToBackend(GetDevice());
barrier.srcQueueFamilyIndex = device->GetGraphicsQueueFamily();
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR;
// We don't know when the importing queue will need the texture, so pass
// VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT to ensure the barrier happens-before any usage in the
// importing queue.
dstStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
device->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0,
nullptr, 0, nullptr, 1, &barrier);
}
MaybeError Texture::ExportExternalTexture(VkImageLayout desiredLayout,
VkSemaphore* signalSemaphore,
ExternalSemaphoreHandle* handle,
VkImageLayout* releasedOldLayout,
VkImageLayout* releasedNewLayout) {
Device* device = ToBackend(GetDevice());
DAWN_INVALID_IF(mExternalState == ExternalState::Released,
"Can't export a signal semaphore from signaled texture %s.", this);
@ -816,8 +851,6 @@ MaybeError Texture::ExportExternalTexture(VkImageLayout desiredLayout,
"Can't export a signal semaphore from destroyed or non-external texture %s.",
this);
ASSERT(mSignalSemaphore != VK_NULL_HANDLE);
// Release the texture
mExternalState = ExternalState::Released;
@ -825,54 +858,28 @@ MaybeError Texture::ExportExternalTexture(VkImageLayout desiredLayout,
ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
wgpu::TextureUsage usage = mSubresourceLastUsages->Get(aspects, 0, 0);
VkImageMemoryBarrier barrier;
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.pNext = nullptr;
barrier.image = GetHandle();
barrier.subresourceRange.aspectMask = VulkanAspectMask(aspects);
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.srcAccessMask = VulkanAccessFlags(usage, GetFormat());
barrier.dstAccessMask = 0; // The barrier must be paired with another barrier that will
// specify the dst access mask on the importing queue.
barrier.oldLayout = VulkanImageLayout(this, usage);
if (desiredLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
// VK_IMAGE_LAYOUT_UNDEFINED is invalid here. We use it as a
// special value to indicate no layout transition should be done.
barrier.newLayout = barrier.oldLayout;
} else {
barrier.newLayout = desiredLayout;
}
barrier.srcQueueFamilyIndex = device->GetGraphicsQueueFamily();
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR;
VkPipelineStageFlags srcStages = VulkanPipelineStage(usage, GetFormat());
VkPipelineStageFlags dstStages =
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; // We don't know when the importing queue will need
// the texture, so pass
// VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT to ensure
// the barrier happens-before any usage in the
// importing queue.
CommandRecordingContext* recordingContext = device->GetPendingRecordingContext();
device->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0,
nullptr, 0, nullptr, 1, &barrier);
// Queue submit to signal we are done with the texture
recordingContext->signalSemaphores.push_back(mSignalSemaphore);
DAWN_TRY(device->SubmitPendingCommands());
VkImageLayout layout = VulkanImageLayout(this, usage);
// Write out the layouts and signal semaphore
*releasedOldLayout = barrier.oldLayout;
*releasedNewLayout = barrier.newLayout;
*signalSemaphore = mSignalSemaphore;
*releasedOldLayout = layout;
*releasedNewLayout = (desiredLayout == VK_IMAGE_LAYOUT_UNDEFINED ? layout : desiredLayout);
mSignalSemaphore = VK_NULL_HANDLE;
mDesiredExportLayout = desiredLayout;
// We have to manually trigger a transition if the texture hasn't been actually used, or the
// desired layout is not VK_IMAGE_LAYOUT_UNDEFINED.
// TODO(dawn:1509): Avoid the empty submit.
if (mExternalSemaphoreHandle == kNullExternalSemaphoreHandle ||
desiredLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
Device* device = ToBackend(GetDevice());
CommandRecordingContext* recordingContext = device->GetPendingRecordingContext();
recordingContext->externalTexturesForEagerTransition.insert(this);
DAWN_TRY(device->SubmitPendingCommands());
}
ASSERT(mExternalSemaphoreHandle != kNullExternalSemaphoreHandle);
*handle = mExternalSemaphoreHandle;
mExternalSemaphoreHandle = kNullExternalSemaphoreHandle;
// Destroy the texture so it can't be used again
Destroy();
@ -907,8 +914,11 @@ void Texture::DestroyImpl() {
mHandle = VK_NULL_HANDLE;
mExternalAllocation = VK_NULL_HANDLE;
// If a signal semaphore exists it should be requested before we delete the texture
ASSERT(mSignalSemaphore == VK_NULL_HANDLE);
if (mExternalSemaphoreHandle != kNullExternalSemaphoreHandle) {
device->GetExternalSemaphoreService()->CloseHandle(mExternalSemaphoreHandle);
}
mExternalSemaphoreHandle = kNullExternalSemaphoreHandle;
}
// For Vulkan, we currently run the base destruction code after the internal changes because
// of the dependency on the texture state which the base code overwrites too early.
@ -929,7 +939,9 @@ void Texture::TweakTransitionForExternalUsage(CommandRecordingContext* recording
// have already added into the vector during current transition.
ASSERT(barriers->size() - transitionBarrierStart <= 1);
if (mExternalState == ExternalState::PendingAcquire) {
if (mExternalState == ExternalState::PendingAcquire ||
mExternalState == ExternalState::EagerlyTransitioned) {
recordingContext->externalTexturesForEagerTransition.insert(this);
if (barriers->size() == transitionBarrierStart) {
barriers->push_back(BuildMemoryBarrier(
this, wgpu::TextureUsage::None, wgpu::TextureUsage::None,
@ -946,27 +958,35 @@ void Texture::TweakTransitionForExternalUsage(CommandRecordingContext* recording
// this.
barrier->srcAccessMask = 0;
// This should be the first barrier after import.
ASSERT(barrier->oldLayout == VK_IMAGE_LAYOUT_UNDEFINED);
// Save the desired layout. We may need to transition through an intermediate
// |mPendingAcquireLayout| first.
VkImageLayout desiredLayout = barrier->newLayout;
bool isInitialized = IsSubresourceContentInitialized(GetAllSubresources());
if (mExternalState == ExternalState::PendingAcquire) {
bool isInitialized = IsSubresourceContentInitialized(GetAllSubresources());
// We don't care about the pending old layout if the texture is uninitialized. The
// driver is free to discard it. Also it is invalid to transition to layout UNDEFINED or
// PREINITIALIZED. If the embedder provided no new layout, or we don't care about the
// previous contents, we can skip the layout transition.
// https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-VkImageMemoryBarrier-newLayout-01198
if (!isInitialized || mPendingAcquireNewLayout == VK_IMAGE_LAYOUT_UNDEFINED ||
mPendingAcquireNewLayout == VK_IMAGE_LAYOUT_PREINITIALIZED) {
barrier->oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier->newLayout = desiredLayout;
// We don't care about the pending old layout if the texture is uninitialized. The
// driver is free to discard it. Also it is invalid to transition to layout UNDEFINED or
// PREINITIALIZED. If the embedder provided no new layout, or we don't care about the
// previous contents, we can skip the layout transition.
// https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-VkImageMemoryBarrier-newLayout-01198
if (!isInitialized || mPendingAcquireNewLayout == VK_IMAGE_LAYOUT_UNDEFINED ||
mPendingAcquireNewLayout == VK_IMAGE_LAYOUT_PREINITIALIZED) {
barrier->oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier->newLayout = desiredLayout;
} else {
barrier->oldLayout = mPendingAcquireOldLayout;
barrier->newLayout = mPendingAcquireNewLayout;
}
} else {
barrier->oldLayout = mPendingAcquireOldLayout;
barrier->newLayout = mPendingAcquireNewLayout;
// In case of ExternalState::EagerlyTransitioned, the layouts of the texture's queue
// release were always same. So we exactly match that here for the queue acquire.
// The spec text:
// If the transfer is via an image memory barrier, and an image layout transition is
// desired, then the values of oldLayout and newLayout in the release operation's memory
// barrier must be equal to values of oldLayout and newLayout in the acquire operation's
// memory barrier.
barrier->newLayout = barrier->oldLayout;
}
// If these are unequal, we need an another barrier to transition the layout.
@ -981,9 +1001,8 @@ void Texture::TweakTransitionForExternalUsage(CommandRecordingContext* recording
layoutBarrier.oldLayout = barrier->newLayout;
layoutBarrier.newLayout = desiredLayout;
// We already transitioned these.
layoutBarrier.srcAccessMask = 0;
layoutBarrier.dstAccessMask = 0;
layoutBarrier.dstAccessMask = barrier->dstAccessMask;
layoutBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
layoutBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
@ -1322,6 +1341,14 @@ void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* recor
}
}
void Texture::UpdateExternalSemaphoreHandle(ExternalSemaphoreHandle handle) {
if (mExternalSemaphoreHandle != kNullExternalSemaphoreHandle) {
Device* device = ToBackend(GetDevice());
device->GetExternalSemaphoreService()->CloseHandle(mExternalSemaphoreHandle);
}
mExternalSemaphoreHandle = handle;
}
VkImageLayout Texture::GetCurrentLayoutForSwapChain() const {
ASSERT(GetFormat().aspects == Aspect::Color);
return VulkanImageLayout(this, mSubresourceLastUsages->Get(Aspect::Color, 0, 0));

View File

@ -24,6 +24,7 @@
#include "dawn/native/Texture.h"
#include "dawn/native/vulkan/ExternalHandle.h"
#include "dawn/native/vulkan/external_memory/MemoryService.h"
#include "dawn/native/vulkan/external_semaphore/SemaphoreService.h"
namespace dawn::native::vulkan {
@ -77,6 +78,9 @@ class Texture final : public TextureBase {
VkPipelineStageFlags* srcStages,
VkPipelineStageFlags* dstStages);
// Eagerly transition the texture for export.
void TransitionEagerlyForExport(CommandRecordingContext* recordingContext);
void EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
const SubresourceRange& range);
@ -85,12 +89,12 @@ class Texture final : public TextureBase {
// Binds externally allocated memory to the VkImage and on success, takes ownership of
// semaphores.
MaybeError BindExternalMemory(const ExternalImageDescriptorVk* descriptor,
VkSemaphore signalSemaphore,
VkDeviceMemory externalMemoryAllocation,
std::vector<VkSemaphore> waitSemaphores);
// Update the 'ExternalSemaphoreHandle' to be used for export with the newly submitted one.
void UpdateExternalSemaphoreHandle(ExternalSemaphoreHandle handle);
MaybeError ExportExternalTexture(VkImageLayout desiredLayout,
VkSemaphore* signalSemaphore,
ExternalSemaphoreHandle* handle,
VkImageLayout* releasedOldLayout,
VkImageLayout* releasedNewLayout);
@ -156,14 +160,30 @@ class Texture final : public TextureBase {
ResourceMemoryAllocation mMemoryAllocation;
VkDeviceMemory mExternalAllocation = VK_NULL_HANDLE;
enum class ExternalState { InternalOnly, PendingAcquire, Acquired, Released };
// The states of an external texture:
// InternalOnly: Not initialized as an external texture yet.
// PendingAcquire: Intialized as an external texture already, but unavailable for access yet.
// Acquired: Ready for access.
// EagerlyTransitioned: The texture has ever been used, and eagerly transitioned for export.
// Now it can be acquired for access again, or directly exported. Released: The texture has
// been destoried, and should no longer be used.
enum class ExternalState {
InternalOnly,
PendingAcquire,
Acquired,
EagerlyTransitioned,
Released
};
ExternalState mExternalState = ExternalState::InternalOnly;
ExternalState mLastExternalState = ExternalState::InternalOnly;
VkImageLayout mPendingAcquireOldLayout;
VkImageLayout mPendingAcquireNewLayout;
VkSemaphore mSignalSemaphore = VK_NULL_HANDLE;
VkImageLayout mDesiredExportLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ExternalSemaphoreHandle mExternalSemaphoreHandle = kNullExternalSemaphoreHandle;
std::vector<VkSemaphore> mWaitRequirements;
// Note that in early Vulkan versions it is not possible to transition depth and stencil

View File

@ -48,6 +48,12 @@ class Service {
// Export a VkSemaphore into an external handle
ResultOrError<ExternalSemaphoreHandle> ExportSemaphore(VkSemaphore semaphore);
// Duplicate a new external handle from the given one.
ExternalSemaphoreHandle DuplicateHandle(ExternalSemaphoreHandle handle);
// Close an external handle.
void CloseHandle(ExternalSemaphoreHandle handle);
private:
Device* mDevice = nullptr;

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <unistd.h>
#include <utility>
#include "dawn/native/vulkan/AdapterVk.h"
@ -135,4 +136,15 @@ ResultOrError<ExternalSemaphoreHandle> Service::ExportSemaphore(VkSemaphore sema
return fd;
}
ExternalSemaphoreHandle Service::DuplicateHandle(ExternalSemaphoreHandle handle) {
int fd = dup(handle);
ASSERT(fd >= 0);
return fd;
}
void Service::CloseHandle(ExternalSemaphoreHandle handle) {
int ret = close(handle);
ASSERT(ret == 0);
}
} // namespace dawn::native::vulkan::external_semaphore

View File

@ -47,4 +47,10 @@ ResultOrError<ExternalSemaphoreHandle> Service::ExportSemaphore(VkSemaphore sema
return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan");
}
ExternalSemaphoreHandle Service::DuplicateHandle(ExternalSemaphoreHandle handle) {
return kNullExternalSemaphoreHandle;
}
void Service::CloseHandle(ExternalSemaphoreHandle handle) {}
} // namespace dawn::native::vulkan::external_semaphore

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <zircon/syscalls.h>
#include <utility>
#include "dawn/native/vulkan/AdapterVk.h"
@ -129,4 +130,16 @@ ResultOrError<ExternalSemaphoreHandle> Service::ExportSemaphore(VkSemaphore sema
return handle;
}
ExternalSemaphoreHandle Service::DuplicateHandle(ExternalSemaphoreHandle handle) {
zx_handle_t out_handle = ZX_HANDLE_INVALID;
zx_status_t status = zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, &out_handle);
ASSERT(status == ZX_OK);
return out_handle;
}
void Service::CloseHandle(ExternalSemaphoreHandle handle) {
zx_status_t status = zx_handle_close(handle);
ASSERT(status == ZX_OK);
}
} // namespace dawn::native::vulkan::external_semaphore

View File

@ -110,7 +110,7 @@ class VulkanImageWrappingTestBase : public DawnTest {
// assertion failure
void IgnoreSignalSemaphore(wgpu::Texture wrappedTexture) {
ExternalImageExportInfoVkForTesting exportInfo;
bool result = mBackend->ExportImage(wrappedTexture, VK_IMAGE_LAYOUT_GENERAL, &exportInfo);
bool result = mBackend->ExportImage(wrappedTexture, VK_IMAGE_LAYOUT_UNDEFINED, &exportInfo);
ASSERT(result);
}
@ -202,7 +202,7 @@ TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) {
ExternalImageExportInfoVkForTesting exportInfo;
ASSERT_DEVICE_ERROR(bool success =
mBackend->ExportImage(texture, VK_IMAGE_LAYOUT_GENERAL, &exportInfo));
mBackend->ExportImage(texture, VK_IMAGE_LAYOUT_UNDEFINED, &exportInfo));
ASSERT_FALSE(success);
ASSERT_EQ(exportInfo.semaphores.size(), 0u);
}
@ -214,7 +214,7 @@ TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) {
ExternalImageExportInfoVkForTesting exportInfo;
ASSERT_DEVICE_ERROR(bool success =
mBackend->ExportImage(texture, VK_IMAGE_LAYOUT_GENERAL, &exportInfo));
mBackend->ExportImage(texture, VK_IMAGE_LAYOUT_UNDEFINED, &exportInfo));
ASSERT_FALSE(success);
ASSERT_EQ(exportInfo.semaphores.size(), 0u);
}
@ -227,7 +227,7 @@ TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport
ExternalImageExportInfoVkForTesting exportInfo;
ASSERT_DEVICE_ERROR(bool success =
mBackend->ExportImage(texture, VK_IMAGE_LAYOUT_GENERAL, &exportInfo));
mBackend->ExportImage(texture, VK_IMAGE_LAYOUT_UNDEFINED, &exportInfo));
ASSERT_FALSE(success);
ASSERT_EQ(exportInfo.semaphores.size(), 0u);
}