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:
parent
490294e6fe
commit
7126ed9111
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue