Split dawn::native::vulkan::Texture into multiple classes.
The Texture class was becoming very difficult to work with as the
handling of regular barriers was mixed with all the external texture
logic which is very complex, making it increasingly harder to understand
what's happening in all of Texture's state.
This commit splits off Texture into:
- Texture, a base class containing logic for all the regular barrier
transition.
- InternalTexture that creates and owns a regular VkImage.
- SwapChainTexture that owns an externally created VkImage.
- ExternalVkImageTexture for textures created from the the deprecated
ExternalImageDescriptorVk image import path.
- SharedTexture for textures created from a SharedTextureMemory.
- ImportedTextureBase for common logic between SharedTexture and
ExternalVkImageTexture until the latter is removed.
No functional changes are intended and most methods have just been
moved to one of the child classes without changes. Exceptions are:
- DestroyImpl() that has been split off between all child classes.
- TweakTransition() (formerly TweakTransitionForExternalTexture) which
is now a virtual method called unconditionally. This could affect
performance slightly as the hot code path is doing a virtual call
(that is most often a noop) instead of a boolean, but it could be
optimized later.
- CanReuseWithoutBarriers() is now a virtual method overridden by
ExternalTextureBase to handle the external state. Likewise this could
have a slight performance cost and could be optimized later.
- The ExternalState::InternalOnly value was removed, and explicit
initialization of mLastExternalState is done in
ExternalVkImageTexture::Initialize.
Finally a member rename in CommandRecordingContext snuck in, that will be
useful in follow-up commits.
Bug:
Change-Id: I27d0e0165e3d0917067c9cd2469fc5fd6173b47e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/207654
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/native/vulkan/CommandRecordingContext.h b/src/dawn/native/vulkan/CommandRecordingContext.h
index a40d32d..88a4385 100644
--- a/src/dawn/native/vulkan/CommandRecordingContext.h
+++ b/src/dawn/native/vulkan/CommandRecordingContext.h
@@ -33,10 +33,11 @@
#include "dawn/common/vulkan_platform.h"
#include "dawn/native/vulkan/BufferVk.h"
#include "dawn/native/vulkan/VulkanFunctions.h"
+#include "partition_alloc/pointers/raw_ptr.h"
namespace dawn::native::vulkan {
-class Texture;
+class ImportedTextureBase;
// 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.
@@ -58,9 +59,15 @@
// 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.
- absl::flat_hash_set<Texture*> externalTexturesForEagerTransition;
+ // Textures can have special synchronization requirements that need to be handled during submit.
+ // They are tracked here to avoid walking all the textures during submission. It is ok to keep a
+ // raw_ptr as they are kept alive by the CommandBuffer. Special synchronization can be:
+ //
+ // - Eager transition to a new usage after the submit.
+ // - Acquiring extra semaphores or fences.
+ // - Exporting extra semaphore or fences.
+ // - and more!
+ absl::flat_hash_set<raw_ptr<ImportedTextureBase>> specialSyncTextures;
// Mappable buffers which will be eagerly transitioned to usage MapRead or MapWrite after
// VkSubmit.
diff --git a/src/dawn/native/vulkan/DeviceVk.cpp b/src/dawn/native/vulkan/DeviceVk.cpp
index d368647..22a4645 100644
--- a/src/dawn/native/vulkan/DeviceVk.cpp
+++ b/src/dawn/native/vulkan/DeviceVk.cpp
@@ -225,7 +225,7 @@
}
ResultOrError<Ref<TextureBase>> Device::CreateTextureImpl(
const UnpackedPtr<TextureDescriptor>& descriptor) {
- return Texture::Create(this, descriptor);
+ return InternalTexture::Create(this, descriptor);
}
ResultOrError<Ref<TextureViewBase>> Device::CreateTextureViewImpl(
TextureBase* texture,
@@ -737,14 +737,15 @@
VkImageLayout desiredLayout,
ExternalImageExportInfoVk* info,
std::vector<ExternalSemaphoreHandle>* semaphoreHandles) {
+ ExternalVkImageTexture* externalTexture = static_cast<ExternalVkImageTexture*>(texture);
return !ConsumedError([&]() -> MaybeError {
DAWN_TRY(ValidateObject(texture));
ExternalSemaphoreHandle semaphoreHandle;
VkImageLayout releasedOldLayout;
VkImageLayout releasedNewLayout;
- DAWN_TRY(texture->ExportExternalTexture(desiredLayout, &semaphoreHandle, &releasedOldLayout,
- &releasedNewLayout));
+ DAWN_TRY(externalTexture->ExportExternalTexture(desiredLayout, &semaphoreHandle,
+ &releasedOldLayout, &releasedNewLayout));
semaphoreHandles->push_back(semaphoreHandle);
info->releasedOldLayout = releasedOldLayout;
@@ -791,10 +792,10 @@
// Cleanup in case of a failure, the image creation doesn't acquire the external objects
// if a failure happems.
- Ref<Texture> result;
+ Ref<ExternalVkImageTexture> result;
// TODO(crbug.com/1026480): Consolidate this into a single CreateFromExternal call.
- if (ConsumedError(Texture::CreateFromExternal(this, descriptor, textureDescriptor,
- mExternalMemoryService.get()),
+ if (ConsumedError(ExternalVkImageTexture::Create(this, descriptor, textureDescriptor,
+ mExternalMemoryService.get()),
&result) ||
ConsumedError(ImportExternalImage(descriptor, memoryHandle, result->GetHandle(),
waitHandles, &allocation, &waitSemaphores)) ||
diff --git a/src/dawn/native/vulkan/QueueVk.cpp b/src/dawn/native/vulkan/QueueVk.cpp
index 2697ae0..aa87b5a 100644
--- a/src/dawn/native/vulkan/QueueVk.cpp
+++ b/src/dawn/native/vulkan/QueueVk.cpp
@@ -333,8 +333,8 @@
// Create an external semaphore for each external textures used in the pending submit.
std::vector<UniqueVkHandle<VkSemaphore>> externalTextureSemaphores(
- mRecordingContext.externalTexturesForEagerTransition.size());
- for (size_t i = 0; i < mRecordingContext.externalTexturesForEagerTransition.size(); ++i) {
+ mRecordingContext.specialSyncTextures.size());
+ for (size_t i = 0; i < mRecordingContext.specialSyncTextures.size(); ++i) {
VkSemaphore semaphore;
DAWN_TRY_ASSIGN(semaphore,
device->GetExternalSemaphoreService()->CreateExportableSemaphore());
@@ -342,7 +342,7 @@
}
// Transition eagerly all used external textures for export.
- for (auto* texture : mRecordingContext.externalTexturesForEagerTransition) {
+ for (auto texture : mRecordingContext.specialSyncTextures) {
texture->TransitionEagerlyForExport(&mRecordingContext);
// TODO(330385376): Remove once ExternalImageDescriptorVk is removed.
@@ -419,7 +419,7 @@
}
auto externalTextureSemaphoreIter = externalTextureSemaphores.begin();
- for (auto* texture : mRecordingContext.externalTexturesForEagerTransition) {
+ for (auto texture : mRecordingContext.specialSyncTextures) {
// Export the signal semaphore.
ExternalSemaphoreHandle semaphoreHandle;
DAWN_TRY_ASSIGN(semaphoreHandle, device->GetExternalSemaphoreService()->ExportSemaphore(
diff --git a/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp b/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp
index 70e404c..02d5b99 100644
--- a/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp
+++ b/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp
@@ -995,7 +995,7 @@
ResultOrError<Ref<TextureBase>> SharedTextureMemory::CreateTextureImpl(
const UnpackedPtr<TextureDescriptor>& descriptor) {
- return Texture::CreateFromSharedTextureMemory(this, descriptor);
+ return SharedTexture::Create(this, descriptor);
}
MaybeError SharedTextureMemory::BeginAccessImpl(
@@ -1020,7 +1020,7 @@
DAWN_INVALID_IF(descriptor->signaledValues[i] != 1, "%s signaled value (%u) was not 1.",
descriptor->fences[i], descriptor->signaledValues[i]);
}
- ToBackend(texture)->SetPendingAcquire(
+ static_cast<SharedTexture*>(texture)->SetPendingAcquire(
static_cast<VkImageLayout>(vkLayoutBeginState->oldLayout),
static_cast<VkImageLayout>(vkLayoutBeginState->newLayout));
return {};
@@ -1059,8 +1059,8 @@
ExternalSemaphoreHandle semaphoreHandle;
VkImageLayout releasedOldLayout;
VkImageLayout releasedNewLayout;
- DAWN_TRY(ToBackend(texture)->EndAccess(&semaphoreHandle, &releasedOldLayout,
- &releasedNewLayout));
+ DAWN_TRY(static_cast<SharedTexture*>(texture)->EndAccess(
+ &semaphoreHandle, &releasedOldLayout, &releasedNewLayout));
// Handle is acquired from the texture so we need to make sure to close it.
// TODO(dawn:1745): Consider using one event per submit that is tracked by the
// CommandRecordingContext so that we don't need to create one handle per texture,
diff --git a/src/dawn/native/vulkan/SwapChainVk.cpp b/src/dawn/native/vulkan/SwapChainVk.cpp
index 9495c3b..19a7ddc 100644
--- a/src/dawn/native/vulkan/SwapChainVk.cpp
+++ b/src/dawn/native/vulkan/SwapChainVk.cpp
@@ -554,7 +554,7 @@
textureDesc.format = mConfig.wgpuFormat;
textureDesc.usage = mConfig.wgpuUsage;
- mTexture = Texture::CreateForSwapChain(device, Unpack(&textureDesc), lastImage.image);
+ mTexture = SwapChainTexture::Create(device, Unpack(&textureDesc), lastImage.image);
// In the happy path we can use the swapchain image directly.
if (!mConfig.needsBlit) {
@@ -565,8 +565,8 @@
// The blit texture always perfectly matches what the user requested for the swapchain.
// We need to add the Vulkan TRANSFER_SRC flag for the vkCmdBlitImage call.
TextureDescriptor desc = GetSwapChainBaseTextureDescriptor(this);
- DAWN_TRY_ASSIGN(mBlitTexture,
- Texture::Create(device, Unpack(&desc), VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
+ DAWN_TRY_ASSIGN(mBlitTexture, InternalTexture::Create(device, Unpack(&desc),
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
swapChainTextureInfo.texture = mBlitTexture;
return swapChainTextureInfo;
}
diff --git a/src/dawn/native/vulkan/SwapChainVk.h b/src/dawn/native/vulkan/SwapChainVk.h
index b11e695..eda9886 100644
--- a/src/dawn/native/vulkan/SwapChainVk.h
+++ b/src/dawn/native/vulkan/SwapChainVk.h
@@ -38,6 +38,7 @@
namespace dawn::native::vulkan {
class Device;
+class SwapChainTexture;
class Texture;
class PhysicalDevice;
struct VulkanSurfaceInfo;
diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp
index 006cd51..b68fe8f 100644
--- a/src/dawn/native/vulkan/TextureVk.cpp
+++ b/src/dawn/native/vulkan/TextureVk.cpp
@@ -793,49 +793,6 @@
return properties.sampleCounts & imageCreateInfo.samples;
}
-// static
-ResultOrError<Ref<Texture>> Texture::Create(Device* device,
- const UnpackedPtr<TextureDescriptor>& descriptor,
- VkImageUsageFlags extraUsages) {
- Ref<Texture> texture = AcquireRef(new Texture(device, descriptor));
- DAWN_TRY(texture->InitializeAsInternalTexture(extraUsages));
- return std::move(texture);
-}
-
-// static
-ResultOrError<Ref<Texture>> Texture::CreateFromExternal(
- Device* device,
- const ExternalImageDescriptorVk* descriptor,
- const UnpackedPtr<TextureDescriptor>& textureDescriptor,
- external_memory::Service* externalMemoryService) {
- Ref<Texture> texture = AcquireRef(new Texture(device, textureDescriptor));
- DAWN_TRY(texture->InitializeFromExternal(descriptor, externalMemoryService));
- return texture;
-}
-
-// static
-ResultOrError<Ref<Texture>> Texture::CreateFromSharedTextureMemory(
- SharedTextureMemory* memory,
- const UnpackedPtr<TextureDescriptor>& textureDescriptor) {
- Ref<Texture> texture =
- AcquireRef(new Texture(ToBackend(memory->GetDevice()), textureDescriptor));
- texture->mSharedResourceMemoryContents = memory->GetContents();
- texture->mSharedTextureMemoryObjects = {memory->GetVkImage(), memory->GetVkDeviceMemory()};
- texture->mHandle = texture->mSharedTextureMemoryObjects.vkImage->Get();
- texture->mExternalAllocation = texture->mSharedTextureMemoryObjects.vkDeviceMemory->Get();
- texture->mExportQueueFamilyIndex = memory->GetQueueFamilyIndex();
- return texture;
-}
-
-// static
-Ref<Texture> Texture::CreateForSwapChain(Device* device,
- const UnpackedPtr<TextureDescriptor>& descriptor,
- VkImage nativeImage) {
- Ref<Texture> texture = AcquireRef(new Texture(device, descriptor));
- texture->InitializeForSwapChain(nativeImage);
- return texture;
-}
-
Texture::Texture(Device* device, const UnpackedPtr<TextureDescriptor>& descriptor)
: TextureBase(device, descriptor),
mCombinedAspect(ComputeCombinedAspect(device, GetFormat())),
@@ -847,354 +804,6 @@
GetNumMipLevels(),
TextureSyncInfo{wgpu::TextureUsage::None, wgpu::ShaderStage::None}) {}
-MaybeError Texture::InitializeAsInternalTexture(VkImageUsageFlags extraUsages) {
- Device* device = ToBackend(GetDevice());
-
- // If this triggers, it means it's time to add tests and implement support for readonly
- // depth-stencil attachments that are also used as readonly storage bindings in the pass.
- // Have fun! :)
- DAWN_ASSERT(
- !(GetFormat().HasDepthOrStencil() && (GetUsage() & wgpu::TextureUsage::StorageBinding)));
-
- // Create the Vulkan image "container". We don't need to check that the format supports the
- // combination of sample, usage etc. because validation should have been done in the Dawn
- // frontend already based on the minimum supported formats in the Vulkan spec
- VkImageCreateInfo createInfo = {};
- FillVulkanCreateInfoSizesAndType(*this, &createInfo);
- createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
- createInfo.format = VulkanImageFormat(device, GetFormat().format);
- createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
- createInfo.usage = VulkanImageUsage(device, GetInternalUsage(), GetFormat()) | extraUsages;
- createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
- createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-
- std::vector<VkFormat> viewFormats;
- bool requiresViewFormatsList = GetViewFormats().any();
- // As current SPIR-V SPEC doesn't support 'bgra8' as a valid image format, to support the
- // STORAGE usage of BGRA8Unorm we have to create an RGBA8Unorm image view on the BGRA8Unorm
- // storage texture and polyfill it as RGBA8Unorm in Tint. See http://crbug.com/dawn/1641 for
- // more details.
- if (createInfo.format == VK_FORMAT_B8G8R8A8_UNORM &&
- createInfo.usage & VK_IMAGE_USAGE_STORAGE_BIT) {
- viewFormats.push_back(VK_FORMAT_R8G8B8A8_UNORM);
- requiresViewFormatsList = true;
- }
- if (GetFormat().IsMultiPlanar() || requiresViewFormatsList) {
- // Multi-planar image needs to have VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT in order to be able
- // to create per-plane view. See
- // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCreateFlagBits.html
- //
- // Note: we cannot include R8 & RG8 in the viewFormats list of
- // G8_B8R8_2PLANE_420_UNORM. The Vulkan validation layer will disallow that.
- createInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
- }
-
- // Add the view format list only when the usage does not have storage. Otherwise, the VVL will
- // say creation of the texture is invalid.
- // See https://github.com/gpuweb/gpuweb/issues/4426.
- VkImageFormatListCreateInfo imageFormatListInfo = {};
- PNextChainBuilder createInfoChain(&createInfo);
- if (requiresViewFormatsList && device->GetDeviceInfo().HasExt(DeviceExt::ImageFormatList) &&
- !(createInfo.usage & VK_IMAGE_USAGE_STORAGE_BIT)) {
- createInfoChain.Add(&imageFormatListInfo, VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO);
- viewFormats.push_back(VulkanImageFormat(device, GetFormat().format));
- for (FormatIndex i : IterateBitSet(GetViewFormats())) {
- const Format& viewFormat = device->GetValidInternalFormat(i);
- viewFormats.push_back(VulkanImageFormat(device, viewFormat.format));
- }
-
- imageFormatListInfo.viewFormatCount = viewFormats.size();
- imageFormatListInfo.pViewFormats = viewFormats.data();
- }
-
- DAWN_ASSERT(IsSampleCountSupported(device, createInfo));
-
- if (GetArrayLayers() >= 6 && GetBaseSize().width == GetBaseSize().height) {
- createInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
- }
-
- if (createInfo.imageType == VK_IMAGE_TYPE_3D &&
- createInfo.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) {
- createInfo.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
- }
-
- // We always set VK_IMAGE_USAGE_TRANSFER_DST_BIT unconditionally beause the Vulkan images
- // that are used in vkCmdClearColorImage() must have been created with this flag, which is
- // also required for the implementation of robust resource initialization.
- createInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
-
- DAWN_TRY(CheckVkOOMThenSuccess(
- device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
- "CreateImage"));
- mOwnsHandle = true;
-
- // Create the image memory and associate it with the container
- VkMemoryRequirements requirements;
- device->fn.GetImageMemoryRequirements(device->GetVkDevice(), mHandle, &requirements);
-
- bool forceDisableSubAllocation =
- (device->IsToggleEnabled(
- Toggle::DisableSubAllocationFor2DTextureWithCopyDstOrRenderAttachment)) &&
- GetDimension() == wgpu::TextureDimension::e2D &&
- (GetInternalUsage() & (wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment));
- auto memoryKind = (GetInternalUsage() & wgpu::TextureUsage::TransientAttachment)
- ? MemoryKind::LazilyAllocated
- : MemoryKind::Opaque;
- DAWN_TRY_ASSIGN(mMemoryAllocation, device->GetResourceMemoryAllocator()->Allocate(
- requirements, memoryKind, forceDisableSubAllocation));
-
- DAWN_TRY(CheckVkSuccess(
- device->fn.BindImageMemory(device->GetVkDevice(), mHandle,
- ToBackend(mMemoryAllocation.GetResourceHeap())->GetMemory(),
- mMemoryAllocation.GetOffset()),
- "BindImageMemory"));
-
- // crbug.com/1361662
- // This works around an Intel Gen12 mesa bug due to CCS ambiguates stomping on each other.
- // https://gitlab.freedesktop.org/mesa/mesa/-/issues/7301#note_1826367
- if (device->IsToggleEnabled(Toggle::VulkanClearGen12TextureWithCCSAmbiguateOnCreation)) {
- auto format = GetFormat().format;
- bool textureIsBuggy =
- format == wgpu::TextureFormat::R8Unorm || format == wgpu::TextureFormat::R8Snorm ||
- format == wgpu::TextureFormat::R8Uint || format == wgpu::TextureFormat::R8Sint ||
- // These are flaky.
- format == wgpu::TextureFormat::RG16Sint || format == wgpu::TextureFormat::RGBA16Sint ||
- format == wgpu::TextureFormat::RGBA32Float;
- textureIsBuggy &= GetNumMipLevels() > 1;
- textureIsBuggy &= GetDimension() == wgpu::TextureDimension::e2D;
- textureIsBuggy &= IsPowerOfTwo(GetBaseSize().width) && IsPowerOfTwo(GetBaseSize().height);
- if (textureIsBuggy) {
- DAWN_TRY(ClearTexture(ToBackend(GetDevice()->GetQueue())->GetPendingRecordingContext(),
- GetAllSubresources(), TextureBase::ClearValue::Zero));
- }
- }
-
- if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
- DAWN_TRY(ClearTexture(ToBackend(GetDevice()->GetQueue())->GetPendingRecordingContext(),
- GetAllSubresources(), TextureBase::ClearValue::NonZero));
- }
-
- SetLabelImpl();
-
- return {};
-}
-
-// Internally managed, but imported from external handle
-MaybeError Texture::InitializeFromExternal(const ExternalImageDescriptorVk* descriptor,
- external_memory::Service* externalMemoryService) {
- Device* device = ToBackend(GetDevice());
- VkFormat format = VulkanImageFormat(device, GetFormat().format);
- VkImageUsageFlags usage = VulkanImageUsage(device, GetInternalUsage(), GetFormat());
-
- [[maybe_unused]] bool supportsDisjoint;
- DAWN_INVALID_IF(
- !externalMemoryService->SupportsCreateImage(descriptor, format, usage, &supportsDisjoint),
- "Creating an image from external memory is not supported.");
- // The creation of mSubresourceLastUsage assumes that multi-planar are always disjoint and sets
- // the combined aspect without checking for disjoint support.
- // TODO(dawn:1548): Support multi-planar images with the DISJOINT feature and potentially allow
- // acting on planes individually? Always using Color is valid even for disjoint images.
- DAWN_ASSERT(!GetFormat().IsMultiPlanar() || mCombinedAspect == Aspect::Color);
-
- mExternalState = ExternalState::PendingAcquire;
- mExportQueueFamilyIndex = externalMemoryService->GetQueueFamilyIndex(descriptor->GetType());
-
- mPendingAcquireOldLayout = descriptor->releasedOldLayout;
- mPendingAcquireNewLayout = descriptor->releasedNewLayout;
-
- VkImageCreateInfo baseCreateInfo = {};
- FillVulkanCreateInfoSizesAndType(*this, &baseCreateInfo);
- baseCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
- baseCreateInfo.format = format;
- baseCreateInfo.usage = usage;
- baseCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
- baseCreateInfo.queueFamilyIndexCount = 0;
- baseCreateInfo.pQueueFamilyIndices = nullptr;
-
- // We always set VK_IMAGE_USAGE_TRANSFER_DST_BIT unconditionally beause the Vulkan images
- // that are used in vkCmdClearColorImage() must have been created with this flag, which is
- // also required for the implementation of robust resource initialization.
- baseCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
-
- VkImageFormatListCreateInfo imageFormatListInfo = {};
- PNextChainBuilder createInfoChain(&baseCreateInfo);
- std::vector<VkFormat> viewFormats;
- if (GetViewFormats().any()) {
- baseCreateInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
- if (device->GetDeviceInfo().HasExt(DeviceExt::ImageFormatList)) {
- createInfoChain.Add(&imageFormatListInfo,
- VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO);
- for (FormatIndex i : IterateBitSet(GetViewFormats())) {
- const Format& viewFormat = device->GetValidInternalFormat(i);
- viewFormats.push_back(VulkanImageFormat(device, viewFormat.format));
- }
-
- imageFormatListInfo.viewFormatCount = viewFormats.size();
- imageFormatListInfo.pViewFormats = viewFormats.data();
- }
- }
-
- DAWN_TRY_ASSIGN(mHandle, externalMemoryService->CreateImage(descriptor, baseCreateInfo));
- mOwnsHandle = true;
-
- SetLabelHelper("Dawn_ExternalTexture");
-
- return {};
-}
-
-void Texture::InitializeForSwapChain(VkImage nativeImage) {
- mHandle = nativeImage;
- mSubresourceLastSyncInfos.Fill({kPresentAcquireTextureUsage, wgpu::ShaderStage::None});
- SetLabelHelper("Dawn_SwapChainTexture");
-}
-
-MaybeError Texture::BindExternalMemory(const ExternalImageDescriptorVk* descriptor,
- VkDeviceMemory externalMemoryAllocation,
- std::vector<VkSemaphore> waitSemaphores) {
- Device* device = ToBackend(GetDevice());
- DAWN_TRY(CheckVkSuccess(
- device->fn.BindImageMemory(device->GetVkDevice(), mHandle, externalMemoryAllocation, 0),
- "BindImageMemory (external)"));
-
- // Don't clear imported texture if already initialized
- if (descriptor->isInitialized) {
- SetIsSubresourceContentInitialized(true, GetAllSubresources());
- }
-
- // Success, acquire all the external objects.
- mExternalAllocation = externalMemoryAllocation;
- mWaitRequirements = std::move(waitSemaphores);
- return {};
-}
-
-void Texture::TransitionEagerlyForExport(CommandRecordingContext* recordingContext) {
- mExternalState = ExternalState::EagerlyTransitioned;
-
- // Get any usage, ideally the last one to do nothing
- DAWN_ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
- const SubresourceRange range = {GetDisjointVulkanAspects(), {0, 1}, {0, 1}};
- const TextureSyncInfo syncInfo = mSubresourceLastSyncInfos.Get(range.aspects, 0, 0);
-
- std::vector<VkImageMemoryBarrier> barriers;
- VkPipelineStageFlags srcStages = 0;
- VkPipelineStageFlags dstStages = 0;
-
- // Same usage as last.
- TransitionUsageAndGetResourceBarrier(syncInfo.usage, syncInfo.shaderStages, range, &barriers,
- &srcStages, &dstStages);
-
- DAWN_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 = mExportQueueFamilyIndex;
-
- // 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);
-}
-
-std::vector<VkSemaphore> Texture::AcquireWaitRequirements() {
- return std::move(mWaitRequirements);
-}
-
-void Texture::SetPendingAcquire(VkImageLayout pendingAcquireOldLayout,
- VkImageLayout pendingAcquireNewLayout) {
- DAWN_ASSERT(GetSharedResourceMemoryContents() != nullptr);
- mExternalState = ExternalState::PendingAcquire;
- mLastExternalState = ExternalState::PendingAcquire;
-
- mPendingAcquireOldLayout = pendingAcquireOldLayout;
- mPendingAcquireNewLayout = pendingAcquireNewLayout;
-}
-
-MaybeError Texture::EndAccess(ExternalSemaphoreHandle* handle,
- VkImageLayout* releasedOldLayout,
- VkImageLayout* releasedNewLayout) {
- // Release the texture
- mExternalState = ExternalState::Released;
-
- DAWN_ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
- wgpu::TextureUsage usage =
- mSubresourceLastSyncInfos.Get(GetDisjointVulkanAspects(), 0, 0).usage;
-
- // Compute the layouts for the queue transition for export. desiredLayout == UNDEFINED is a tag
- // value used to export with whatever the current layout is. However queue transitioning to the
- // UNDEFINED layout is disallowed so we handle the case where currentLayout is UNDEFINED by
- // promoting to GENERAL.
- VkImageLayout currentLayout = VulkanImageLayout(GetFormat(), usage);
- VkImageLayout targetLayout;
- if (currentLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
- targetLayout = currentLayout;
- } else {
- targetLayout = VK_IMAGE_LAYOUT_GENERAL;
- }
-
- // We have to manually trigger a transition if the texture hasn't been actually used or if we
- // need a layout transition.
- // TODO(dawn:1509): Avoid the empty submit.
- if (mExternalSemaphoreHandle == kNullExternalSemaphoreHandle || targetLayout != currentLayout) {
- mDesiredExportLayout = targetLayout;
-
- Queue* queue = ToBackend(GetDevice()->GetQueue());
- CommandRecordingContext* recordingContext = queue->GetPendingRecordingContext();
- recordingContext->externalTexturesForEagerTransition.insert(this);
- DAWN_TRY(queue->SubmitPendingCommands());
-
- currentLayout = targetLayout;
- }
- DAWN_ASSERT(mExternalSemaphoreHandle != kNullExternalSemaphoreHandle);
-
- // Write out the layouts and signal semaphore
- *releasedOldLayout = currentLayout;
- *releasedNewLayout = targetLayout;
- *handle = mExternalSemaphoreHandle;
- mExternalSemaphoreHandle = kNullExternalSemaphoreHandle;
- return {};
-}
-
-MaybeError Texture::ExportExternalTexture(VkImageLayout desiredLayout,
- ExternalSemaphoreHandle* handle,
- VkImageLayout* releasedOldLayout,
- VkImageLayout* releasedNewLayout) {
- DAWN_INVALID_IF(mExternalState == ExternalState::Released,
- "Can't export a signal semaphore from signaled texture %s.", this);
-
- DAWN_INVALID_IF(mExternalAllocation == VK_NULL_HANDLE,
- "Can't export a signal semaphore from destroyed or non-external texture %s.",
- this);
-
- DAWN_INVALID_IF(desiredLayout != VK_IMAGE_LAYOUT_UNDEFINED,
- "desiredLayout (%d) was not VK_IMAGE_LAYOUT_UNDEFINED", desiredLayout);
-
- DAWN_TRY(EndAccess(handle, releasedOldLayout, releasedNewLayout));
-
- // Destroy the texture so it can't be used again
- Destroy();
- return {};
-}
-
-Texture::~Texture() {
- if (mExternalSemaphoreHandle != kNullExternalSemaphoreHandle) {
- ToBackend(GetDevice())
- ->GetExternalSemaphoreService()
- ->CloseHandle(mExternalSemaphoreHandle);
- }
- mExternalSemaphoreHandle = kNullExternalSemaphoreHandle;
-}
-
void Texture::SetLabelHelper(const char* prefix) {
SetDebugName(ToBackend(GetDevice()), mHandle, prefix, GetLabel());
}
@@ -1211,26 +820,8 @@
// - 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.
- Device* device = ToBackend(GetDevice());
-
- if (mOwnsHandle) {
- device->GetFencedDeleter()->DeleteWhenUnused(mHandle);
- }
-
- // For textures created from a VkImage, the allocation is kInvalid so the Device knows
- // to skip the deallocation of the (absence of) VkDeviceMemory.
- device->GetResourceMemoryAllocator()->Deallocate(&mMemoryAllocation);
-
- if (mExternalAllocation != VK_NULL_HANDLE && GetSharedResourceMemoryContents() == nullptr) {
- device->GetFencedDeleter()->DeleteWhenUnused(mExternalAllocation);
- }
-
mHandle = VK_NULL_HANDLE;
- mExternalAllocation = VK_NULL_HANDLE;
- mSharedTextureMemoryObjects = {};
- // 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.
TextureBase::DestroyImpl();
}
@@ -1238,105 +829,13 @@
return mHandle;
}
-void Texture::TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext,
- std::vector<VkImageMemoryBarrier>* barriers,
- size_t transitionBarrierStart) {
- DAWN_ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
-
- mLastSharedTextureMemoryUsageSerial = GetDevice()->GetQueue()->GetPendingCommandSerial();
-
- // transitionBarrierStart specify the index where barriers for current transition start in
- // the vector. barriers->size() - transitionBarrierStart is the number of barriers that we
- // have already added into the vector during current transition.
- DAWN_ASSERT(barriers->size() - transitionBarrierStart <= 1);
-
- 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,
- SubresourceRange::SingleMipAndLayer(0, 0, GetDisjointVulkanAspects())));
- }
-
- VkImageMemoryBarrier* barrier = &(*barriers)[transitionBarrierStart];
- // Transfer texture from external queue to graphics queue
- barrier->srcQueueFamilyIndex = mExportQueueFamilyIndex;
- barrier->dstQueueFamilyIndex = ToBackend(GetDevice())->GetGraphicsQueueFamily();
-
- // srcAccessMask means nothing when importing. Queue transfers require a barrier on
- // both the importing and exporting queues. The exporting queue should have specified
- // this.
- barrier->srcAccessMask = 0;
-
- // Save the desired layout. We may need to transition through an intermediate
- // |mPendingAcquireLayout| first.
- VkImageLayout desiredLayout = barrier->newLayout;
-
- 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;
- } else {
- barrier->oldLayout = mPendingAcquireOldLayout;
- barrier->newLayout = mPendingAcquireNewLayout;
- }
- } else {
- // 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.
- if (barrier->newLayout != desiredLayout) {
- VkImageMemoryBarrier layoutBarrier;
- layoutBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
- layoutBarrier.pNext = nullptr;
- layoutBarrier.image = GetHandle();
- layoutBarrier.subresourceRange = barrier->subresourceRange;
-
- // Transition from the acquired new layout to the desired layout.
- layoutBarrier.oldLayout = barrier->newLayout;
- layoutBarrier.newLayout = desiredLayout;
-
- layoutBarrier.srcAccessMask = 0;
- layoutBarrier.dstAccessMask = barrier->dstAccessMask;
- layoutBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
- layoutBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-
- barriers->push_back(layoutBarrier);
- }
-
- mExternalState = ExternalState::Acquired;
- }
-
- mLastExternalState = mExternalState;
-}
-
bool Texture::CanReuseWithoutBarrier(wgpu::TextureUsage lastUsage,
wgpu::TextureUsage usage,
wgpu::ShaderStage lastShaderStage,
wgpu::ShaderStage shaderStage) {
// Reuse the texture directly and avoid encoding barriers when it isn't needed.
bool lastReadOnly = IsSubset(lastUsage, kReadOnlyTextureUsages);
- if (lastReadOnly && lastUsage == usage && IsSubset(shaderStage, lastShaderStage) &&
- mLastExternalState == mExternalState) {
- return true;
- }
- return false;
+ return lastReadOnly && lastUsage == usage && IsSubset(shaderStage, lastShaderStage);
}
void Texture::TransitionUsageForPass(CommandRecordingContext* recordingContext,
@@ -1445,9 +944,7 @@
lastSyncInfo->usage = newUsage;
});
- if (mExternalState != ExternalState::InternalOnly) {
- TweakTransitionForExternalUsage(recordingContext, imageBarriers, transitionBarrierStart);
- }
+ TweakTransition(recordingContext, imageBarriers, transitionBarrierStart);
// Skip adding pipeline stages if no barrier was needed to avoid putting TOP_OF_PIPE in the
// destination stages.
@@ -1469,9 +966,7 @@
TransitionUsageAndGetResourceBarrier(usage, shaderStages, range, &barriers, &srcStages,
&dstStages);
- if (mExternalState != ExternalState::InternalOnly) {
- TweakTransitionForExternalUsage(recordingContext, &barriers, 0);
- }
+ TweakTransition(recordingContext, &barriers, 0);
if (!barriers.empty()) {
DAWN_ASSERT(srcStages != 0 && dstStages != 0);
@@ -1747,15 +1242,6 @@
return {};
}
-void Texture::UpdateExternalSemaphoreHandle(ExternalSemaphoreHandle handle) {
- if (mExternalSemaphoreHandle != kNullExternalSemaphoreHandle) {
- ToBackend(GetDevice())
- ->GetExternalSemaphoreService()
- ->CloseHandle(mExternalSemaphoreHandle);
- }
- mExternalSemaphoreHandle = handle;
-}
-
VkImageLayout Texture::GetCurrentLayoutForSwapChain() const {
DAWN_ASSERT(GetFormat().aspects == Aspect::Color);
return VulkanImageLayout(GetFormat(), mSubresourceLastSyncInfos.Get(Aspect::Color, 0, 0).usage);
@@ -1772,6 +1258,588 @@
return GetFormat().aspects;
}
+void Texture::TweakTransition(CommandRecordingContext* recordingContext,
+ std::vector<VkImageMemoryBarrier>* barriers,
+ size_t transitionBarrierStart) {}
+
+//
+// InternalTexture
+//
+
+// static
+ResultOrError<Ref<InternalTexture>> InternalTexture::Create(
+ Device* device,
+ const UnpackedPtr<TextureDescriptor>& descriptor,
+ VkImageUsageFlags extraUsages) {
+ Ref<InternalTexture> texture = AcquireRef(new InternalTexture(device, descriptor));
+ DAWN_TRY(texture->Initialize(extraUsages));
+ return std::move(texture);
+}
+
+MaybeError InternalTexture::Initialize(VkImageUsageFlags extraUsages) {
+ Device* device = ToBackend(GetDevice());
+
+ // If this triggers, it means it's time to add tests and implement support for readonly
+ // depth-stencil attachments that are also used as readonly storage bindings in the pass.
+ // Have fun! :)
+ DAWN_ASSERT(
+ !(GetFormat().HasDepthOrStencil() && (GetUsage() & wgpu::TextureUsage::StorageBinding)));
+
+ // Create the Vulkan image "container". We don't need to check that the format supports the
+ // combination of sample, usage etc. because validation should have been done in the Dawn
+ // frontend already based on the minimum supported formats in the Vulkan spec
+ VkImageCreateInfo createInfo = {};
+ FillVulkanCreateInfoSizesAndType(*this, &createInfo);
+ createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ createInfo.format = VulkanImageFormat(device, GetFormat().format);
+ createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+ createInfo.usage = VulkanImageUsage(device, GetInternalUsage(), GetFormat()) | extraUsages;
+ createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+ std::vector<VkFormat> viewFormats;
+ bool requiresViewFormatsList = GetViewFormats().any();
+ // As current SPIR-V SPEC doesn't support 'bgra8' as a valid image format, to support the
+ // STORAGE usage of BGRA8Unorm we have to create an RGBA8Unorm image view on the BGRA8Unorm
+ // storage texture and polyfill it as RGBA8Unorm in Tint. See http://crbug.com/dawn/1641 for
+ // more details.
+ if (createInfo.format == VK_FORMAT_B8G8R8A8_UNORM &&
+ createInfo.usage & VK_IMAGE_USAGE_STORAGE_BIT) {
+ viewFormats.push_back(VK_FORMAT_R8G8B8A8_UNORM);
+ requiresViewFormatsList = true;
+ }
+ if (GetFormat().IsMultiPlanar() || requiresViewFormatsList) {
+ // Multi-planar image needs to have VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT in order to be able
+ // to create per-plane view. See
+ // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImageCreateFlagBits.html
+ //
+ // Note: we cannot include R8 & RG8 in the viewFormats list of
+ // G8_B8R8_2PLANE_420_UNORM. The Vulkan validation layer will disallow that.
+ createInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+ }
+
+ // Add the view format list only when the usage does not have storage. Otherwise, the VVL will
+ // say creation of the texture is invalid.
+ // See https://github.com/gpuweb/gpuweb/issues/4426.
+ VkImageFormatListCreateInfo imageFormatListInfo = {};
+ PNextChainBuilder createInfoChain(&createInfo);
+ if (requiresViewFormatsList && device->GetDeviceInfo().HasExt(DeviceExt::ImageFormatList) &&
+ !(createInfo.usage & VK_IMAGE_USAGE_STORAGE_BIT)) {
+ createInfoChain.Add(&imageFormatListInfo, VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO);
+ viewFormats.push_back(VulkanImageFormat(device, GetFormat().format));
+ for (FormatIndex i : IterateBitSet(GetViewFormats())) {
+ const Format& viewFormat = device->GetValidInternalFormat(i);
+ viewFormats.push_back(VulkanImageFormat(device, viewFormat.format));
+ }
+
+ imageFormatListInfo.viewFormatCount = viewFormats.size();
+ imageFormatListInfo.pViewFormats = viewFormats.data();
+ }
+
+ DAWN_ASSERT(IsSampleCountSupported(device, createInfo));
+
+ if (GetArrayLayers() >= 6 && GetBaseSize().width == GetBaseSize().height) {
+ createInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
+ }
+
+ if (createInfo.imageType == VK_IMAGE_TYPE_3D &&
+ createInfo.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) {
+ createInfo.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
+ }
+
+ // We always set VK_IMAGE_USAGE_TRANSFER_DST_BIT unconditionally because the Vulkan images
+ // that are used in vkCmdClearColorImage() must have been created with this flag, which is
+ // also required for the implementation of robust resource initialization.
+ createInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+
+ DAWN_TRY(CheckVkOOMThenSuccess(
+ device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
+ "CreateImage"));
+
+ // Create the image memory and associate it with the container
+ VkMemoryRequirements requirements;
+ device->fn.GetImageMemoryRequirements(device->GetVkDevice(), mHandle, &requirements);
+
+ bool forceDisableSubAllocation =
+ (device->IsToggleEnabled(
+ Toggle::DisableSubAllocationFor2DTextureWithCopyDstOrRenderAttachment)) &&
+ GetDimension() == wgpu::TextureDimension::e2D &&
+ (GetInternalUsage() & (wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment));
+ auto memoryKind = (GetInternalUsage() & wgpu::TextureUsage::TransientAttachment)
+ ? MemoryKind::LazilyAllocated
+ : MemoryKind::Opaque;
+ DAWN_TRY_ASSIGN(mMemoryAllocation, device->GetResourceMemoryAllocator()->Allocate(
+ requirements, memoryKind, forceDisableSubAllocation));
+
+ DAWN_TRY(CheckVkSuccess(
+ device->fn.BindImageMemory(device->GetVkDevice(), mHandle,
+ ToBackend(mMemoryAllocation.GetResourceHeap())->GetMemory(),
+ mMemoryAllocation.GetOffset()),
+ "BindImageMemory"));
+
+ // crbug.com/1361662
+ // This works around an Intel Gen12 mesa bug due to CCS ambiguates stomping on each other.
+ // https://gitlab.freedesktop.org/mesa/mesa/-/issues/7301#note_1826367
+ if (device->IsToggleEnabled(Toggle::VulkanClearGen12TextureWithCCSAmbiguateOnCreation)) {
+ auto format = GetFormat().format;
+ bool textureIsBuggy =
+ format == wgpu::TextureFormat::R8Unorm || format == wgpu::TextureFormat::R8Snorm ||
+ format == wgpu::TextureFormat::R8Uint || format == wgpu::TextureFormat::R8Sint ||
+ // These are flaky.
+ format == wgpu::TextureFormat::RG16Sint || format == wgpu::TextureFormat::RGBA16Sint ||
+ format == wgpu::TextureFormat::RGBA32Float;
+ textureIsBuggy &= GetNumMipLevels() > 1;
+ textureIsBuggy &= GetDimension() == wgpu::TextureDimension::e2D;
+ textureIsBuggy &= IsPowerOfTwo(GetBaseSize().width) && IsPowerOfTwo(GetBaseSize().height);
+ if (textureIsBuggy) {
+ DAWN_TRY(ClearTexture(ToBackend(GetDevice()->GetQueue())->GetPendingRecordingContext(),
+ GetAllSubresources(), TextureBase::ClearValue::Zero));
+ }
+ }
+
+ if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
+ DAWN_TRY(ClearTexture(ToBackend(GetDevice()->GetQueue())->GetPendingRecordingContext(),
+ GetAllSubresources(), TextureBase::ClearValue::NonZero));
+ }
+
+ SetLabelImpl();
+
+ return {};
+}
+
+void InternalTexture::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.
+ Device* device = ToBackend(GetDevice());
+
+ device->GetFencedDeleter()->DeleteWhenUnused(mHandle);
+ mHandle = VK_NULL_HANDLE;
+
+ device->GetResourceMemoryAllocator()->Deallocate(&mMemoryAllocation);
+ mMemoryAllocation = ResourceMemoryAllocation();
+
+ Texture::DestroyImpl();
+}
+
+//
+// SwapChainTexture
+//
+
+// static
+Ref<SwapChainTexture> SwapChainTexture::Create(Device* device,
+ const UnpackedPtr<TextureDescriptor>& descriptor,
+ VkImage nativeImage) {
+ Ref<SwapChainTexture> texture = AcquireRef(new SwapChainTexture(device, descriptor));
+ texture->Initialize(nativeImage);
+ return texture;
+}
+
+void SwapChainTexture::Initialize(VkImage nativeImage) {
+ mHandle = nativeImage;
+ mSubresourceLastSyncInfos.Fill({kPresentAcquireTextureUsage, wgpu::ShaderStage::None});
+ SetLabelHelper("Dawn_SwapChainTexture");
+}
+
+//
+// ImportedTextureBase
+//
+
+ImportedTextureBase::~ImportedTextureBase() {
+ // The external semaphore can be queried even after device loss or destroy (so applications can
+ // keep relying on them for synchronization) so only destroy it in the destructor instead of
+ // DestroyImpl.
+ if (mExternalSemaphoreHandle != kNullExternalSemaphoreHandle) {
+ ToBackend(GetDevice())
+ ->GetExternalSemaphoreService()
+ ->CloseHandle(mExternalSemaphoreHandle);
+ }
+ mExternalSemaphoreHandle = kNullExternalSemaphoreHandle;
+}
+
+void ImportedTextureBase::TransitionEagerlyForExport(CommandRecordingContext* recordingContext) {
+ mExternalState = ExternalState::EagerlyTransitioned;
+
+ // Get any usage, ideally the last one to do nothing
+ DAWN_ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
+ const SubresourceRange range = {GetDisjointVulkanAspects(), {0, 1}, {0, 1}};
+ const TextureSyncInfo syncInfo = mSubresourceLastSyncInfos.Get(range.aspects, 0, 0);
+
+ std::vector<VkImageMemoryBarrier> barriers;
+ VkPipelineStageFlags srcStages = 0;
+ VkPipelineStageFlags dstStages = 0;
+
+ // Same usage as last.
+ TransitionUsageAndGetResourceBarrier(syncInfo.usage, syncInfo.shaderStages, range, &barriers,
+ &srcStages, &dstStages);
+
+ DAWN_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 = mExportQueueFamilyIndex;
+
+ // 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);
+}
+
+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,
+ wgpu::ShaderStage shaderStage) {
+ return mLastExternalState == mExternalState &&
+ Texture::CanReuseWithoutBarrier(lastUsage, usage, lastShaderStage, shaderStage);
+}
+
+void ImportedTextureBase::TweakTransition(CommandRecordingContext* recordingContext,
+ std::vector<VkImageMemoryBarrier>* barriers,
+ size_t transitionBarrierStart) {
+ DAWN_ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
+
+ mLastSharedTextureMemoryUsageSerial = GetDevice()->GetQueue()->GetPendingCommandSerial();
+
+ // transitionBarrierStart specify the index where barriers for current transition start in
+ // the vector. barriers->size() - transitionBarrierStart is the number of barriers that we
+ // have already added into the vector during current transition.
+ DAWN_ASSERT(barriers->size() - transitionBarrierStart <= 1);
+
+ if (mExternalState == ExternalState::PendingAcquire ||
+ mExternalState == ExternalState::EagerlyTransitioned) {
+ recordingContext->specialSyncTextures.insert(this);
+ if (barriers->size() == transitionBarrierStart) {
+ barriers->push_back(BuildMemoryBarrier(
+ this, wgpu::TextureUsage::None, wgpu::TextureUsage::None,
+ SubresourceRange::SingleMipAndLayer(0, 0, GetDisjointVulkanAspects())));
+ }
+
+ VkImageMemoryBarrier* barrier = &(*barriers)[transitionBarrierStart];
+ // Transfer texture from external queue to graphics queue
+ barrier->srcQueueFamilyIndex = mExportQueueFamilyIndex;
+ barrier->dstQueueFamilyIndex = ToBackend(GetDevice())->GetGraphicsQueueFamily();
+
+ // srcAccessMask means nothing when importing. Queue transfers require a barrier on
+ // both the importing and exporting queues. The exporting queue should have specified
+ // this.
+ barrier->srcAccessMask = 0;
+
+ // Save the desired layout. We may need to transition through an intermediate
+ // |mPendingAcquireLayout| first.
+ VkImageLayout desiredLayout = barrier->newLayout;
+
+ 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;
+ } else {
+ barrier->oldLayout = mPendingAcquireOldLayout;
+ barrier->newLayout = mPendingAcquireNewLayout;
+ }
+ } else {
+ // 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.
+ if (barrier->newLayout != desiredLayout) {
+ VkImageMemoryBarrier layoutBarrier;
+ layoutBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ layoutBarrier.pNext = nullptr;
+ layoutBarrier.image = GetHandle();
+ layoutBarrier.subresourceRange = barrier->subresourceRange;
+
+ // Transition from the acquired new layout to the desired layout.
+ layoutBarrier.oldLayout = barrier->newLayout;
+ layoutBarrier.newLayout = desiredLayout;
+
+ layoutBarrier.srcAccessMask = 0;
+ layoutBarrier.dstAccessMask = barrier->dstAccessMask;
+ layoutBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ layoutBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+
+ barriers->push_back(layoutBarrier);
+ }
+
+ mExternalState = ExternalState::Acquired;
+ }
+
+ mLastExternalState = mExternalState;
+}
+
+MaybeError ImportedTextureBase::EndAccess(ExternalSemaphoreHandle* handle,
+ VkImageLayout* releasedOldLayout,
+ VkImageLayout* releasedNewLayout) {
+ // Release the texture
+ mExternalState = ExternalState::Released;
+
+ DAWN_ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1);
+ wgpu::TextureUsage usage =
+ mSubresourceLastSyncInfos.Get(GetDisjointVulkanAspects(), 0, 0).usage;
+
+ // Compute the layouts for the queue transition for export. desiredLayout == UNDEFINED is a tag
+ // value used to export with whatever the current layout is. However queue transitioning to the
+ // UNDEFINED layout is disallowed so we handle the case where currentLayout is UNDEFINED by
+ // promoting to GENERAL.
+ VkImageLayout currentLayout = VulkanImageLayout(GetFormat(), usage);
+ VkImageLayout targetLayout;
+ if (currentLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
+ targetLayout = currentLayout;
+ } else {
+ targetLayout = VK_IMAGE_LAYOUT_GENERAL;
+ }
+
+ // We have to manually trigger a transition if the texture hasn't been actually used or if we
+ // need a layout transition.
+ // TODO(dawn:1509): Avoid the empty submit.
+ if (mExternalSemaphoreHandle == kNullExternalSemaphoreHandle || targetLayout != currentLayout) {
+ mDesiredExportLayout = targetLayout;
+
+ Queue* queue = ToBackend(GetDevice()->GetQueue());
+ CommandRecordingContext* recordingContext = queue->GetPendingRecordingContext();
+ recordingContext->specialSyncTextures.insert(this);
+ DAWN_TRY(queue->SubmitPendingCommands());
+
+ currentLayout = targetLayout;
+ }
+ DAWN_ASSERT(mExternalSemaphoreHandle != kNullExternalSemaphoreHandle);
+
+ // Write out the layouts and signal semaphore
+ *releasedOldLayout = currentLayout;
+ *releasedNewLayout = targetLayout;
+ *handle = mExternalSemaphoreHandle;
+ mExternalSemaphoreHandle = kNullExternalSemaphoreHandle;
+ return {};
+}
+
+//
+// ExternalVkImageTexture
+//
+
+// static
+ResultOrError<Ref<ExternalVkImageTexture>> ExternalVkImageTexture::Create(
+ Device* device,
+ const ExternalImageDescriptorVk* descriptor,
+ const UnpackedPtr<TextureDescriptor>& textureDescriptor,
+ external_memory::Service* externalMemoryService) {
+ Ref<ExternalVkImageTexture> texture =
+ AcquireRef(new ExternalVkImageTexture(device, textureDescriptor));
+ DAWN_TRY(texture->Initialize(descriptor, externalMemoryService));
+ return texture;
+}
+
+void ExternalVkImageTexture::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.
+ Device* device = ToBackend(GetDevice());
+
+ device->GetFencedDeleter()->DeleteWhenUnused(mHandle);
+ mHandle = VK_NULL_HANDLE;
+
+ if (mExternalAllocation != VK_NULL_HANDLE) {
+ device->GetFencedDeleter()->DeleteWhenUnused(mExternalAllocation);
+ mExternalAllocation = VK_NULL_HANDLE;
+ }
+
+ Texture::DestroyImpl();
+}
+
+MaybeError ExternalVkImageTexture::Initialize(const ExternalImageDescriptorVk* descriptor,
+ external_memory::Service* externalMemoryService) {
+ Device* device = ToBackend(GetDevice());
+ VkFormat format = VulkanImageFormat(device, GetFormat().format);
+ VkImageUsageFlags usage = VulkanImageUsage(device, GetInternalUsage(), GetFormat());
+
+ [[maybe_unused]] bool supportsDisjoint;
+ DAWN_INVALID_IF(
+ !externalMemoryService->SupportsCreateImage(descriptor, format, usage, &supportsDisjoint),
+ "Creating an image from external memory is not supported.");
+ // The creation of mSubresourceLastUsage assumes that multi-planar are always disjoint and sets
+ // the combined aspect without checking for disjoint support.
+ // TODO(dawn:1548): Support multi-planar images with the DISJOINT feature and potentially allow
+ // acting on planes individually? Always using Color is valid even for disjoint images.
+ DAWN_ASSERT(!GetFormat().IsMultiPlanar() || mCombinedAspect == Aspect::Color);
+
+ mExternalState = ExternalState::PendingAcquire;
+ mLastExternalState = ExternalState::Released;
+ mExportQueueFamilyIndex = externalMemoryService->GetQueueFamilyIndex(descriptor->GetType());
+
+ mPendingAcquireOldLayout = descriptor->releasedOldLayout;
+ mPendingAcquireNewLayout = descriptor->releasedNewLayout;
+
+ VkImageCreateInfo baseCreateInfo = {};
+ FillVulkanCreateInfoSizesAndType(*this, &baseCreateInfo);
+ baseCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ baseCreateInfo.format = format;
+ baseCreateInfo.usage = usage;
+ baseCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ baseCreateInfo.queueFamilyIndexCount = 0;
+ baseCreateInfo.pQueueFamilyIndices = nullptr;
+
+ // We always set VK_IMAGE_USAGE_TRANSFER_DST_BIT unconditionally because the Vulkan images
+ // that are used in vkCmdClearColorImage() must have been created with this flag, which is
+ // also required for the implementation of robust resource initialization.
+ baseCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+
+ VkImageFormatListCreateInfo imageFormatListInfo = {};
+ PNextChainBuilder createInfoChain(&baseCreateInfo);
+ std::vector<VkFormat> viewFormats;
+ if (GetViewFormats().any()) {
+ baseCreateInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+ if (device->GetDeviceInfo().HasExt(DeviceExt::ImageFormatList)) {
+ createInfoChain.Add(&imageFormatListInfo,
+ VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO);
+ for (FormatIndex i : IterateBitSet(GetViewFormats())) {
+ const Format& viewFormat = device->GetValidInternalFormat(i);
+ viewFormats.push_back(VulkanImageFormat(device, viewFormat.format));
+ }
+
+ imageFormatListInfo.viewFormatCount = viewFormats.size();
+ imageFormatListInfo.pViewFormats = viewFormats.data();
+ }
+ }
+
+ DAWN_TRY_ASSIGN(mHandle, externalMemoryService->CreateImage(descriptor, baseCreateInfo));
+ SetLabelHelper("Dawn_ExternalVkImageTexture");
+
+ return {};
+}
+
+MaybeError ExternalVkImageTexture::BindExternalMemory(const ExternalImageDescriptorVk* descriptor,
+ VkDeviceMemory externalMemoryAllocation,
+ std::vector<VkSemaphore> waitSemaphores) {
+ Device* device = ToBackend(GetDevice());
+ DAWN_TRY(CheckVkSuccess(
+ device->fn.BindImageMemory(device->GetVkDevice(), mHandle, externalMemoryAllocation, 0),
+ "BindImageMemory (external)"));
+
+ // Don't clear imported texture if already initialized
+ if (descriptor->isInitialized) {
+ SetIsSubresourceContentInitialized(true, GetAllSubresources());
+ }
+
+ // Success, acquire all the external objects.
+ mExternalAllocation = externalMemoryAllocation;
+ mWaitRequirements = std::move(waitSemaphores);
+ return {};
+}
+
+MaybeError ExternalVkImageTexture::ExportExternalTexture(VkImageLayout desiredLayout,
+ ExternalSemaphoreHandle* handle,
+ VkImageLayout* releasedOldLayout,
+ VkImageLayout* releasedNewLayout) {
+ DAWN_INVALID_IF(mExternalState == ExternalState::Released,
+ "Can't export a signal semaphore from signaled texture %s.", this);
+
+ DAWN_INVALID_IF(mExternalAllocation == VK_NULL_HANDLE,
+ "Can't export a signal semaphore from destroyed or non-external texture %s.",
+ this);
+
+ DAWN_INVALID_IF(desiredLayout != VK_IMAGE_LAYOUT_UNDEFINED,
+ "desiredLayout (%d) was not VK_IMAGE_LAYOUT_UNDEFINED", desiredLayout);
+
+ DAWN_TRY(EndAccess(handle, releasedOldLayout, releasedNewLayout));
+
+ // Destroy the texture so it can't be used again
+ Destroy();
+ return {};
+}
+
+std::vector<VkSemaphore> ExternalVkImageTexture::AcquireWaitRequirements() {
+ return std::move(mWaitRequirements);
+}
+
+//
+// SharedTexture
+//
+
+// static
+ResultOrError<Ref<SharedTexture>> SharedTexture::Create(
+ SharedTextureMemory* memory,
+ const UnpackedPtr<TextureDescriptor>& textureDescriptor) {
+ Ref<SharedTexture> texture =
+ AcquireRef(new SharedTexture(ToBackend(memory->GetDevice()), textureDescriptor));
+ texture->Initialize(memory);
+ return texture;
+}
+
+void SharedTexture::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.
+ mSharedTextureMemoryObjects = {};
+
+ Texture::DestroyImpl();
+}
+
+void SharedTexture::Initialize(SharedTextureMemory* memory) {
+ mSharedResourceMemoryContents = memory->GetContents();
+ mSharedTextureMemoryObjects = {memory->GetVkImage(), memory->GetVkDeviceMemory()};
+ mHandle = mSharedTextureMemoryObjects.vkImage->Get();
+ mExportQueueFamilyIndex = memory->GetQueueFamilyIndex();
+}
+
+void SharedTexture::SetPendingAcquire(VkImageLayout pendingAcquireOldLayout,
+ VkImageLayout pendingAcquireNewLayout) {
+ DAWN_ASSERT(GetSharedResourceMemoryContents() != nullptr);
+ mExternalState = ExternalState::PendingAcquire;
+ mLastExternalState = ExternalState::PendingAcquire;
+
+ mPendingAcquireOldLayout = pendingAcquireOldLayout;
+ mPendingAcquireNewLayout = pendingAcquireNewLayout;
+}
+
+//
+// TextureView
+//
+
// static
ResultOrError<Ref<TextureView>> TextureView::Create(
TextureBase* texture,
diff --git a/src/dawn/native/vulkan/TextureVk.h b/src/dawn/native/vulkan/TextureVk.h
index eb08296..93edce0 100644
--- a/src/dawn/native/vulkan/TextureVk.h
+++ b/src/dawn/native/vulkan/TextureVk.h
@@ -66,36 +66,17 @@
bool IsSampleCountSupported(const dawn::native::vulkan::Device* device,
const VkImageCreateInfo& imageCreateInfo);
-class Texture final : public TextureBase {
+// Base class for all Texture implementation on Vulkan that handles the common logic for barrier
+// tracking, view creation, etc. Cannot be created directly, instead InternalTexture is the
+// Dawn-controlled texture.
+class Texture : public TextureBase {
public:
- // Used to create a regular texture from a descriptor.
- static ResultOrError<Ref<Texture>> Create(Device* device,
- const UnpackedPtr<TextureDescriptor>& descriptor,
- VkImageUsageFlags extraUsages = 0);
-
- // Creates a texture and initializes it with a VkImage that references an external memory
- // object. Before the texture can be used, the VkDeviceMemory associated with the external
- // image must be bound via Texture::BindExternalMemory.
- static ResultOrError<Ref<Texture>> CreateFromExternal(
- Device* device,
- const ExternalImageDescriptorVk* descriptor,
- const UnpackedPtr<TextureDescriptor>& textureDescriptor,
- external_memory::Service* externalMemoryService);
-
- // Create a texture from contents of a SharedTextureMemory.
- static ResultOrError<Ref<Texture>> CreateFromSharedTextureMemory(
- SharedTextureMemory* memory,
- const UnpackedPtr<TextureDescriptor>& textureDescriptor);
-
- // Creates a texture that wraps a swapchain-allocated VkImage.
- static Ref<Texture> CreateForSwapChain(Device* device,
- const UnpackedPtr<TextureDescriptor>& descriptor,
- VkImage nativeImage);
-
VkImage GetHandle() const;
// Returns the aspects used for tracking of Vulkan state. These can be the combined aspects.
Aspect GetDisjointVulkanAspects() const;
+ VkImageLayout GetCurrentLayoutForSwapChain() const;
+
// Transitions the texture to be used as `usage`, recording any necessary barrier in
// `commands`.
// TODO(crbug.com/dawn/851): coalesce barriers and do them early when possible.
@@ -116,47 +97,17 @@
wgpu::ShaderStage shaderStages,
const SubresourceRange& range);
- // Eagerly transition the texture for export.
- void TransitionEagerlyForExport(CommandRecordingContext* recordingContext);
- std::vector<VkSemaphore> AcquireWaitRequirements();
-
MaybeError EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
const SubresourceRange& range);
- VkImageLayout GetCurrentLayoutForSwapChain() const;
-
- // Binds externally allocated memory to the VkImage and on success, takes ownership of
- // semaphores.
- MaybeError BindExternalMemory(const ExternalImageDescriptorVk* descriptor,
- 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,
- ExternalSemaphoreHandle* handle,
- VkImageLayout* releasedOldLayout,
- VkImageLayout* releasedNewLayout);
-
- void SetPendingAcquire(VkImageLayout pendingAcquireOldLayout,
- VkImageLayout pendingAcquireNewLayout);
- MaybeError EndAccess(ExternalSemaphoreHandle* handle,
- VkImageLayout* releasedOldLayout,
- VkImageLayout* releasedNewLayout);
-
void SetLabelHelper(const char* prefix);
// Dawn API
void SetLabelImpl() override;
- private:
- ~Texture() override;
+ protected:
Texture(Device* device, const UnpackedPtr<TextureDescriptor>& descriptor);
- MaybeError InitializeAsInternalTexture(VkImageUsageFlags extraUsages);
- MaybeError InitializeFromExternal(const ExternalImageDescriptorVk* descriptor,
- external_memory::Service* externalMemoryService);
- void InitializeForSwapChain(VkImage nativeImage);
-
void DestroyImpl() override;
MaybeError ClearTexture(CommandRecordingContext* recordingContext,
const SubresourceRange& range,
@@ -180,50 +131,15 @@
std::vector<VkImageMemoryBarrier>* imageBarriers,
VkPipelineStageFlags* srcStages,
VkPipelineStageFlags* dstStages);
- void TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext,
- std::vector<VkImageMemoryBarrier>* barriers,
- size_t transitionBarrierStart);
- bool CanReuseWithoutBarrier(wgpu::TextureUsage lastUsage,
- wgpu::TextureUsage usage,
- wgpu::ShaderStage lastShaderStage,
- wgpu::ShaderStage shaderStage);
- VkImage mHandle = VK_NULL_HANDLE;
- bool mOwnsHandle = false;
- ResourceMemoryAllocation mMemoryAllocation;
- VkDeviceMemory mExternalAllocation = VK_NULL_HANDLE;
- struct SharedTextureMemoryObjects {
- Ref<RefCountedVkHandle<VkImage>> vkImage;
- Ref<RefCountedVkHandle<VkDeviceMemory>> vkDeviceMemory;
- };
- SharedTextureMemoryObjects mSharedTextureMemoryObjects;
-
- // 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;
- uint32_t mExportQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR;
-
- VkImageLayout mPendingAcquireOldLayout;
- VkImageLayout mPendingAcquireNewLayout;
-
- VkImageLayout mDesiredExportLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-
- ExternalSemaphoreHandle mExternalSemaphoreHandle = kNullExternalSemaphoreHandle;
-
- std::vector<VkSemaphore> mWaitRequirements;
+ // TODO(42242084): Make this more robust and maybe predicated on a boolean as we're in hot code.
+ virtual bool CanReuseWithoutBarrier(wgpu::TextureUsage lastUsage,
+ wgpu::TextureUsage usage,
+ wgpu::ShaderStage lastShaderStage,
+ wgpu::ShaderStage shaderStage);
+ virtual void TweakTransition(CommandRecordingContext* recordingContext,
+ std::vector<VkImageMemoryBarrier>* barriers,
+ size_t transitionBarrierStart);
// Sometimes the WebGPU aspects don't directly map to Vulkan aspects:
//
@@ -236,9 +152,153 @@
//
// This variable, if not Aspect::None, is the combined aspect to use for all transitions.
const Aspect mCombinedAspect;
- SubresourceStorage<TextureSyncInfo> mSubresourceLastSyncInfos;
-
bool UseCombinedAspects() const;
+
+ SubresourceStorage<TextureSyncInfo> mSubresourceLastSyncInfos;
+ VkImage mHandle = VK_NULL_HANDLE;
+};
+
+// A texture created and fully owned by Dawn. Typically the result of device.CreateTexture.
+class InternalTexture final : public Texture {
+ public:
+ static ResultOrError<Ref<InternalTexture>> Create(
+ Device* device,
+ const UnpackedPtr<TextureDescriptor>& descriptor,
+ VkImageUsageFlags extraUsages = 0);
+
+ private:
+ using Texture::Texture;
+ MaybeError Initialize(VkImageUsageFlags extraUsages);
+ void DestroyImpl() override;
+
+ ResourceMemoryAllocation mMemoryAllocation;
+};
+
+// A texture owned by a VkSwapChain.
+class SwapChainTexture final : public Texture {
+ public:
+ static Ref<SwapChainTexture> Create(Device* device,
+ const UnpackedPtr<TextureDescriptor>& descriptor,
+ VkImage nativeImage);
+
+ private:
+ using Texture::Texture;
+ void Initialize(VkImage nativeImage);
+};
+
+// 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,
+ size_t transitionBarrierStart) override;
+
+ bool CanReuseWithoutBarrier(wgpu::TextureUsage lastUsage,
+ wgpu::TextureUsage usage,
+ wgpu::ShaderStage lastShaderStage,
+ wgpu::ShaderStage shaderStage) override;
+
+ // Performs the steps to export a texture and returns the export information.
+ MaybeError EndAccess(ExternalSemaphoreHandle* handle,
+ VkImageLayout* releasedOldLayout,
+ VkImageLayout* releasedNewLayout);
+
+ protected:
+ using Texture::Texture;
+ ~ImportedTextureBase() override;
+ // The states of an external texture:
+ // PendingAcquire: Initialized 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 is not associated to any external resource and cannot be used. This
+ // can happen before initialization, or after destruction.
+ enum class ExternalState {
+ PendingAcquire,
+ Acquired,
+ EagerlyTransitioned,
+ Released
+ };
+ ExternalState mExternalState = ExternalState::Released;
+ ExternalState mLastExternalState = ExternalState::Released;
+
+ // The layouts to use for the queue ownership transfer barrier when a texture is used the first
+ // time after being imported. The layouts must match the ones from the queue ownership transfer
+ // barrier done for the export operation.
+ VkImageLayout mPendingAcquireOldLayout;
+ VkImageLayout mPendingAcquireNewLayout;
+
+ // Which of FOREIGN or EXTERNAL queue family to use when exporting.
+ uint32_t mExportQueueFamilyIndex;
+ // The layout requested for the export, or UNDEFINED if the receiver can handle whichever layout
+ // was current.
+ VkImageLayout mDesiredExportLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ // 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;
+};
+
+// A texture created from an VkImage that references an external memory object.
+class ExternalVkImageTexture final : public ImportedTextureBase {
+ public:
+ static ResultOrError<Ref<ExternalVkImageTexture>> Create(
+ Device* device,
+ const ExternalImageDescriptorVk* descriptor,
+ const UnpackedPtr<TextureDescriptor>& textureDescriptor,
+ external_memory::Service* externalMemoryService);
+
+ // Binds externally allocated memory to the VkImage and on success, takes ownership of
+ // semaphores.
+ MaybeError BindExternalMemory(const ExternalImageDescriptorVk* descriptor,
+ VkDeviceMemory externalMemoryAllocation,
+ std::vector<VkSemaphore> waitSemaphores);
+
+ MaybeError ExportExternalTexture(VkImageLayout desiredLayout,
+ ExternalSemaphoreHandle* handle,
+ VkImageLayout* releasedOldLayout,
+ VkImageLayout* releasedNewLayout);
+
+ std::vector<VkSemaphore> AcquireWaitRequirements() override;
+
+ private:
+ using ImportedTextureBase::ImportedTextureBase;
+ MaybeError Initialize(const ExternalImageDescriptorVk* descriptor,
+ external_memory::Service* externalMemoryService);
+ void DestroyImpl() override;
+
+ VkDeviceMemory mExternalAllocation = VK_NULL_HANDLE;
+ std::vector<VkSemaphore> mWaitRequirements;
+};
+
+// A texture created from a SharedTextureMemory
+class SharedTexture final : public ImportedTextureBase {
+ public:
+ static ResultOrError<Ref<SharedTexture>> Create(
+ SharedTextureMemory* memory,
+ const UnpackedPtr<TextureDescriptor>& textureDescriptor);
+
+ void SetPendingAcquire(VkImageLayout pendingAcquireOldLayout,
+ VkImageLayout pendingAcquireNewLayout);
+
+ private:
+ using ImportedTextureBase::ImportedTextureBase;
+ void Initialize(SharedTextureMemory* memory);
+ void DestroyImpl() override;
+
+ struct SharedTextureMemoryObjects {
+ Ref<RefCountedVkHandle<VkImage>> vkImage;
+ Ref<RefCountedVkHandle<VkDeviceMemory>> vkDeviceMemory;
+ };
+ SharedTextureMemoryObjects mSharedTextureMemoryObjects;
};
class TextureView final : public TextureViewBase {
diff --git a/src/dawn/tests/white_box/VulkanImageWrappingTests.cpp b/src/dawn/tests/white_box/VulkanImageWrappingTests.cpp
index a908bdb..25c1409 100644
--- a/src/dawn/tests/white_box/VulkanImageWrappingTests.cpp
+++ b/src/dawn/tests/white_box/VulkanImageWrappingTests.cpp
@@ -276,17 +276,6 @@
ASSERT_EQ(exportInfo.semaphores.size(), 0u);
}
-// Test an error occurs if we try to export the signal semaphore from a normal texture
-TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) {
- wgpu::Texture texture = device.CreateTexture(&defaultDescriptor);
- ASSERT_NE(texture.Get(), nullptr);
-
- ExternalImageExportInfoVkForTesting exportInfo = GetExternalImageExportInfo();
- ASSERT_DEVICE_ERROR(bool success = mBackend->ExportImage(texture, &exportInfo));
- ASSERT_FALSE(success);
- ASSERT_EQ(exportInfo.semaphores.size(), 0u);
-}
-
// Test an error occurs if we try to export the signal semaphore from a destroyed texture
TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) {
wgpu::Texture texture =