Add wrapping for using external vulkan images as textures
This change adds platform-dependent services that handle creating semaphores and importing image memory. Then, we use them to wrap a texture from an outside source, and release a signal semaphore back when we're done with it. This will be used to allow chrome to render dawn on Vulkan platforms. Bug: chromium:976495 Change-Id: I9f07eaf436e10aa6bd88cffdc74fd23834d62ee0 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8340 Commit-Queue: Idan Raiter <idanr@google.com> Reviewed-by: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
parent
8cb23933b1
commit
74e4834d3d
72
BUILD.gn
72
BUILD.gn
|
@ -362,6 +362,7 @@ source_set("libdawn_native_sources") {
|
|||
"src/dawn_native/vulkan/ComputePipelineVk.h",
|
||||
"src/dawn_native/vulkan/DeviceVk.cpp",
|
||||
"src/dawn_native/vulkan/DeviceVk.h",
|
||||
"src/dawn_native/vulkan/ExternalHandle.h",
|
||||
"src/dawn_native/vulkan/FencedDeleter.cpp",
|
||||
"src/dawn_native/vulkan/FencedDeleter.h",
|
||||
"src/dawn_native/vulkan/Forward.h",
|
||||
|
@ -395,7 +396,21 @@ source_set("libdawn_native_sources") {
|
|||
"src/dawn_native/vulkan/VulkanFunctions.h",
|
||||
"src/dawn_native/vulkan/VulkanInfo.cpp",
|
||||
"src/dawn_native/vulkan/VulkanInfo.h",
|
||||
"src/dawn_native/vulkan/external_memory/MemoryService.h",
|
||||
"src/dawn_native/vulkan/external_semaphore/SemaphoreService.h",
|
||||
]
|
||||
|
||||
if (is_linux) {
|
||||
sources += [
|
||||
"src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp",
|
||||
"src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp",
|
||||
]
|
||||
} else {
|
||||
sources += [
|
||||
"src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp",
|
||||
"src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -667,8 +682,9 @@ test("dawn_unittests") {
|
|||
}
|
||||
}
|
||||
|
||||
test("dawn_end2end_tests") {
|
||||
source_set("dawn_end2end_tests_sources") {
|
||||
configs += [ "${dawn_root}/src/common:dawn_internal" ]
|
||||
testonly = true
|
||||
|
||||
deps = [
|
||||
":dawn_utils",
|
||||
|
@ -681,7 +697,6 @@ test("dawn_end2end_tests") {
|
|||
]
|
||||
|
||||
sources = [
|
||||
"src/tests/DawnTest.cpp",
|
||||
"src/tests/DawnTest.h",
|
||||
"src/tests/end2end/BasicTests.cpp",
|
||||
"src/tests/end2end/BindGroupTests.cpp",
|
||||
|
@ -729,6 +744,59 @@ test("dawn_end2end_tests") {
|
|||
|
||||
libs += [ "IOSurface.framework" ]
|
||||
}
|
||||
}
|
||||
|
||||
source_set("dawn_white_box_tests_sources") {
|
||||
configs += [ "${dawn_root}/src/common:dawn_internal" ]
|
||||
testonly = true
|
||||
|
||||
deps = [
|
||||
":dawn_utils",
|
||||
":libdawn_native",
|
||||
":libdawn_native_sources",
|
||||
":libdawn_wire",
|
||||
"${dawn_root}/src/common",
|
||||
"${dawn_root}/src/dawn:libdawn",
|
||||
"third_party:glfw",
|
||||
"third_party:gmock_and_gtest",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"src/tests/DawnTest.h",
|
||||
]
|
||||
|
||||
if (dawn_enable_vulkan) {
|
||||
deps += [ "third_party:vulkan_headers" ]
|
||||
|
||||
if (is_linux) {
|
||||
sources += [ "src/tests/white_box/VulkanImageWrappingTests.cpp" ]
|
||||
}
|
||||
}
|
||||
|
||||
libs = []
|
||||
}
|
||||
|
||||
test("dawn_end2end_tests") {
|
||||
configs += [ "${dawn_root}/src/common:dawn_internal" ]
|
||||
|
||||
deps = [
|
||||
":dawn_end2end_tests_sources",
|
||||
":dawn_utils",
|
||||
":dawn_white_box_tests_sources",
|
||||
":libdawn_native",
|
||||
":libdawn_wire",
|
||||
"${dawn_root}/src/common",
|
||||
"${dawn_root}/src/dawn:libdawn",
|
||||
"third_party:glfw",
|
||||
"third_party:gmock_and_gtest",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"src/tests/DawnTest.cpp",
|
||||
"src/tests/DawnTest.h",
|
||||
]
|
||||
|
||||
libs = []
|
||||
|
||||
# When building inside Chromium, use their gtest main function because it is
|
||||
# needed to run in swarming correctly.
|
||||
|
|
|
@ -208,6 +208,9 @@ namespace dawn_native {
|
|||
|
||||
MaybeError ValidateTextureDescriptor(const DeviceBase* device,
|
||||
const TextureDescriptor* descriptor) {
|
||||
if (descriptor == nullptr) {
|
||||
return DAWN_VALIDATION_ERROR("Texture descriptor is nullptr");
|
||||
}
|
||||
if (descriptor->nextInChain != nullptr) {
|
||||
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
#include "dawn_native/BackendConnection.h"
|
||||
#include "dawn_native/Commands.h"
|
||||
#include "dawn_native/DynamicUploader.h"
|
||||
#include "dawn_native/Error.h"
|
||||
#include "dawn_native/ErrorData.h"
|
||||
#include "dawn_native/VulkanBackend.h"
|
||||
#include "dawn_native/vulkan/AdapterVk.h"
|
||||
#include "dawn_native/vulkan/BackendVk.h"
|
||||
#include "dawn_native/vulkan/BindGroupLayoutVk.h"
|
||||
|
@ -68,6 +70,9 @@ namespace dawn_native { namespace vulkan {
|
|||
mMemoryAllocator = std::make_unique<MemoryAllocator>(this);
|
||||
mRenderPassCache = std::make_unique<RenderPassCache>(this);
|
||||
|
||||
mExternalMemoryService = std::make_unique<external_memory::Service>(this);
|
||||
mExternalSemaphoreService = std::make_unique<external_semaphore::Service>(this);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -568,4 +573,91 @@ namespace dawn_native { namespace vulkan {
|
|||
|
||||
return {};
|
||||
}
|
||||
|
||||
MaybeError Device::ImportExternalImage(const ExternalImageDescriptor* descriptor,
|
||||
ExternalMemoryHandle memoryHandle,
|
||||
const std::vector<ExternalSemaphoreHandle>& waitHandles,
|
||||
VkSemaphore* outSignalSemaphore,
|
||||
VkDeviceMemory* outAllocation,
|
||||
std::vector<VkSemaphore>* outWaitSemaphores) {
|
||||
// Check services support this combination of handle type / image info
|
||||
if (!mExternalSemaphoreService->Supported()) {
|
||||
return DAWN_VALIDATION_ERROR("External semaphore usage not supported");
|
||||
}
|
||||
if (!mExternalMemoryService->Supported()) {
|
||||
return DAWN_VALIDATION_ERROR("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
|
||||
DAWN_TRY_ASSIGN(*outAllocation,
|
||||
mExternalMemoryService->ImportMemory(
|
||||
memoryHandle, descriptor->allocationSize, descriptor->memoryTypeIndex));
|
||||
|
||||
// Import semaphores we have to wait on before using the texture
|
||||
for (const ExternalSemaphoreHandle& handle : waitHandles) {
|
||||
VkSemaphore semaphore = VK_NULL_HANDLE;
|
||||
DAWN_TRY_ASSIGN(semaphore, mExternalSemaphoreService->ImportSemaphore(handle));
|
||||
outWaitSemaphores->push_back(semaphore);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
MaybeError Device::SignalAndExportExternalTexture(Texture* texture,
|
||||
ExternalSemaphoreHandle* outHandle) {
|
||||
DAWN_TRY(ValidateObject(texture));
|
||||
|
||||
VkSemaphore outSignalSemaphore;
|
||||
DAWN_TRY(texture->SignalAndDestroy(&outSignalSemaphore));
|
||||
|
||||
// This has to happen right after SignalAndDestroy, since the semaphore will be
|
||||
// deleted when the fenced deleter runs after the queue submission
|
||||
DAWN_TRY_ASSIGN(*outHandle, mExternalSemaphoreService->ExportSemaphore(outSignalSemaphore));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
TextureBase* Device::CreateTextureWrappingVulkanImage(
|
||||
const ExternalImageDescriptor* descriptor,
|
||||
ExternalMemoryHandle memoryHandle,
|
||||
const std::vector<ExternalSemaphoreHandle>& waitHandles) {
|
||||
const TextureDescriptor* textureDescriptor =
|
||||
reinterpret_cast<const TextureDescriptor*>(descriptor->cTextureDescriptor);
|
||||
|
||||
// Initial validation
|
||||
if (ConsumedError(ValidateTextureDescriptor(this, textureDescriptor))) {
|
||||
return nullptr;
|
||||
}
|
||||
if (ConsumedError(ValidateVulkanImageCanBeWrapped(this, textureDescriptor))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VkSemaphore signalSemaphore = VK_NULL_HANDLE;
|
||||
VkDeviceMemory allocation = VK_NULL_HANDLE;
|
||||
std::vector<VkSemaphore> waitSemaphores;
|
||||
waitSemaphores.reserve(waitHandles.size());
|
||||
|
||||
// If failed, cleanup
|
||||
if (ConsumedError(ImportExternalImage(descriptor, memoryHandle, waitHandles,
|
||||
&signalSemaphore, &allocation, &waitSemaphores))) {
|
||||
// Clear the signal semaphore
|
||||
fn.DestroySemaphore(GetVkDevice(), signalSemaphore, nullptr);
|
||||
|
||||
// Clear image memory
|
||||
fn.FreeMemory(GetVkDevice(), allocation, nullptr);
|
||||
|
||||
// Clear any wait semaphores we were able to import
|
||||
for (VkSemaphore semaphore : waitSemaphores) {
|
||||
fn.DestroySemaphore(GetVkDevice(), semaphore, nullptr);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new Texture(this, descriptor, textureDescriptor, signalSemaphore, allocation,
|
||||
waitSemaphores);
|
||||
}
|
||||
}} // namespace dawn_native::vulkan
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
#include "dawn_native/vulkan/VulkanFunctions.h"
|
||||
#include "dawn_native/vulkan/VulkanInfo.h"
|
||||
|
||||
#include "dawn_native/vulkan/external_memory/MemoryService.h"
|
||||
#include "dawn_native/vulkan/external_semaphore/SemaphoreService.h"
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
|
@ -32,6 +35,7 @@ namespace dawn_native { namespace vulkan {
|
|||
|
||||
class Adapter;
|
||||
class BufferUploader;
|
||||
struct ExternalImageDescriptor;
|
||||
class FencedDeleter;
|
||||
class MapRequestTracker;
|
||||
class MemoryAllocator;
|
||||
|
@ -64,6 +68,14 @@ namespace dawn_native { namespace vulkan {
|
|||
Serial GetPendingCommandSerial() const override;
|
||||
void SubmitPendingCommands();
|
||||
|
||||
TextureBase* CreateTextureWrappingVulkanImage(
|
||||
const ExternalImageDescriptor* descriptor,
|
||||
ExternalMemoryHandle memoryHandle,
|
||||
const std::vector<ExternalSemaphoreHandle>& waitHandles);
|
||||
|
||||
MaybeError SignalAndExportExternalTexture(Texture* texture,
|
||||
ExternalSemaphoreHandle* outHandle);
|
||||
|
||||
// Dawn API
|
||||
CommandBufferBase* CreateCommandBuffer(CommandEncoderBase* encoder,
|
||||
const CommandBufferDescriptor* descriptor) override;
|
||||
|
@ -119,6 +131,9 @@ namespace dawn_native { namespace vulkan {
|
|||
std::unique_ptr<MemoryAllocator> mMemoryAllocator;
|
||||
std::unique_ptr<RenderPassCache> mRenderPassCache;
|
||||
|
||||
std::unique_ptr<external_memory::Service> mExternalMemoryService;
|
||||
std::unique_ptr<external_semaphore::Service> mExternalSemaphoreService;
|
||||
|
||||
VkFence GetUnusedFence();
|
||||
void CheckPassedFences();
|
||||
|
||||
|
@ -143,8 +158,14 @@ namespace dawn_native { namespace vulkan {
|
|||
SerialQueue<CommandPoolAndBuffer> mCommandsInFlight;
|
||||
std::vector<CommandPoolAndBuffer> mUnusedCommands;
|
||||
CommandPoolAndBuffer mPendingCommands;
|
||||
|
||||
CommandRecordingContext mRecordingContext;
|
||||
|
||||
MaybeError ImportExternalImage(const ExternalImageDescriptor* descriptor,
|
||||
ExternalMemoryHandle memoryHandle,
|
||||
const std::vector<ExternalSemaphoreHandle>& waitHandles,
|
||||
VkSemaphore* outSignalSemaphore,
|
||||
VkDeviceMemory* outAllocation,
|
||||
std::vector<VkSemaphore>* outWaitSemaphores);
|
||||
};
|
||||
|
||||
}} // namespace dawn_native::vulkan
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef DAWNNATIVE_VULKAN_EXTERNALHANDLE_H_
|
||||
#define DAWNNATIVE_VULKAN_EXTERNALHANDLE_H_
|
||||
|
||||
namespace dawn_native { namespace vulkan {
|
||||
|
||||
#ifdef DAWN_PLATFORM_LINUX
|
||||
// File descriptor
|
||||
using ExternalMemoryHandle = int;
|
||||
// File descriptor
|
||||
using ExternalSemaphoreHandle = int;
|
||||
#else
|
||||
// Generic types so that the Null service can compile, not used for real handles
|
||||
using ExternalMemoryHandle = void*;
|
||||
using ExternalSemaphoreHandle = void*;
|
||||
#endif
|
||||
|
||||
}} // namespace dawn_native::vulkan
|
||||
|
||||
#endif // DAWNNATIVE_VULKAN_EXTERNALHANDLE_H_
|
|
@ -41,9 +41,7 @@ namespace dawn_native { namespace vulkan {
|
|||
MemoryAllocator::~MemoryAllocator() {
|
||||
}
|
||||
|
||||
bool MemoryAllocator::Allocate(VkMemoryRequirements requirements,
|
||||
bool mappable,
|
||||
DeviceMemoryAllocation* allocation) {
|
||||
int MemoryAllocator::FindBestTypeIndex(VkMemoryRequirements requirements, bool mappable) {
|
||||
const VulkanDeviceInfo& info = mDevice->GetDeviceInfo();
|
||||
|
||||
// Find a suitable memory type for this allocation
|
||||
|
@ -93,6 +91,14 @@ namespace dawn_native { namespace vulkan {
|
|||
}
|
||||
}
|
||||
|
||||
return bestType;
|
||||
}
|
||||
|
||||
bool MemoryAllocator::Allocate(VkMemoryRequirements requirements,
|
||||
bool mappable,
|
||||
DeviceMemoryAllocation* allocation) {
|
||||
int bestType = FindBestTypeIndex(requirements, mappable);
|
||||
|
||||
// TODO(cwallez@chromium.org): I think the Vulkan spec guarantees this should never happen
|
||||
if (bestType == -1) {
|
||||
ASSERT(false);
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace dawn_native { namespace vulkan {
|
|||
MemoryAllocator(Device* device);
|
||||
~MemoryAllocator();
|
||||
|
||||
int FindBestTypeIndex(VkMemoryRequirements requirements, bool mappable);
|
||||
bool Allocate(VkMemoryRequirements requirements,
|
||||
bool mappable,
|
||||
DeviceMemoryAllocation* allocation);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "dawn_native/vulkan/TextureVk.h"
|
||||
|
||||
#include "dawn_native/VulkanBackend.h"
|
||||
#include "dawn_native/vulkan/AdapterVk.h"
|
||||
#include "dawn_native/vulkan/CommandRecordingContext.h"
|
||||
#include "dawn_native/vulkan/DeviceVk.h"
|
||||
|
@ -379,12 +380,33 @@ namespace dawn_native { namespace vulkan {
|
|||
}
|
||||
}
|
||||
|
||||
MaybeError ValidateVulkanImageCanBeWrapped(const DeviceBase*,
|
||||
const TextureDescriptor* descriptor) {
|
||||
if (descriptor->dimension != dawn::TextureDimension::e2D) {
|
||||
return DAWN_VALIDATION_ERROR("Texture must be 2D");
|
||||
}
|
||||
|
||||
if (descriptor->mipLevelCount != 1) {
|
||||
return DAWN_VALIDATION_ERROR("Mip level count must be 1");
|
||||
}
|
||||
|
||||
if (descriptor->arrayLayerCount != 1) {
|
||||
return DAWN_VALIDATION_ERROR("Array layer count must be 1");
|
||||
}
|
||||
|
||||
if (descriptor->sampleCount != 1) {
|
||||
return DAWN_VALIDATION_ERROR("Sample count must be 1");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Texture::Texture(Device* device, const TextureDescriptor* descriptor)
|
||||
: TextureBase(device, descriptor, TextureState::OwnedInternal) {
|
||||
// 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;
|
||||
VkImageCreateInfo createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
createInfo.pNext = nullptr;
|
||||
createInfo.flags = 0;
|
||||
|
@ -466,6 +488,95 @@ namespace dawn_native { namespace vulkan {
|
|||
: TextureBase(device, descriptor, TextureState::OwnedExternal), mHandle(nativeImage) {
|
||||
}
|
||||
|
||||
// Internally managed, but imported from file descriptor
|
||||
Texture::Texture(Device* device,
|
||||
const ExternalImageDescriptor* descriptor,
|
||||
const TextureDescriptor* textureDescriptor,
|
||||
VkSemaphore signalSemaphore,
|
||||
VkDeviceMemory externalMemoryAllocation,
|
||||
std::vector<VkSemaphore> waitSemaphores)
|
||||
: TextureBase(device, textureDescriptor, TextureState::OwnedInternal),
|
||||
mExternalAllocation(externalMemoryAllocation),
|
||||
mExternalState(ExternalState::PendingAcquire),
|
||||
mSignalSemaphore(signalSemaphore),
|
||||
mWaitRequirements(std::move(waitSemaphores)) {
|
||||
VkImageCreateInfo createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
createInfo.pNext = nullptr;
|
||||
createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR;
|
||||
createInfo.imageType = VulkanImageType(GetDimension());
|
||||
createInfo.format = VulkanImageFormat(GetFormat().format);
|
||||
createInfo.extent = VulkanExtent3D(GetSize());
|
||||
createInfo.mipLevels = GetNumMipLevels();
|
||||
createInfo.arrayLayers = GetArrayLayers();
|
||||
createInfo.samples = VulkanSampleCount(GetSampleCount());
|
||||
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
createInfo.usage = VulkanImageUsage(GetUsage(), GetFormat());
|
||||
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
createInfo.queueFamilyIndexCount = 0;
|
||||
createInfo.pQueueFamilyIndices = nullptr;
|
||||
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
ASSERT(IsSampleCountSupported(device, createInfo));
|
||||
|
||||
// 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;
|
||||
|
||||
if (device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &mHandle) !=
|
||||
VK_SUCCESS) {
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
// Create the image memory and associate it with the container
|
||||
VkMemoryRequirements requirements;
|
||||
device->fn.GetImageMemoryRequirements(device->GetVkDevice(), mHandle, &requirements);
|
||||
|
||||
ASSERT(requirements.size <= descriptor->allocationSize);
|
||||
|
||||
if (device->fn.BindImageMemory(device->GetVkDevice(), mHandle, mExternalAllocation, 0) !=
|
||||
VK_SUCCESS) {
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
// Don't clear imported texture if already cleared
|
||||
if (descriptor->isCleared) {
|
||||
SetIsSubresourceContentInitialized(0, 1, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
MaybeError Texture::SignalAndDestroy(VkSemaphore* outSignalSemaphore) {
|
||||
Device* device = ToBackend(GetDevice());
|
||||
|
||||
if (mExternalState == ExternalState::Released) {
|
||||
return DAWN_VALIDATION_ERROR("Can't export signal semaphore from signaled texture");
|
||||
}
|
||||
|
||||
if (mExternalAllocation == VK_NULL_HANDLE) {
|
||||
return DAWN_VALIDATION_ERROR(
|
||||
"Can't export signal semaphore from destroyed / non-external texture");
|
||||
}
|
||||
|
||||
ASSERT(mSignalSemaphore != VK_NULL_HANDLE);
|
||||
|
||||
// Release the texture
|
||||
mExternalState = ExternalState::PendingRelease;
|
||||
TransitionUsageNow(device->GetPendingRecordingContext(), dawn::TextureUsageBit::None);
|
||||
|
||||
// Queue submit to signal we are done with the texture
|
||||
device->GetPendingRecordingContext()->signalSemaphores.push_back(mSignalSemaphore);
|
||||
device->SubmitPendingCommands();
|
||||
|
||||
// Write out the signal semaphore
|
||||
*outSignalSemaphore = mSignalSemaphore;
|
||||
mSignalSemaphore = VK_NULL_HANDLE;
|
||||
|
||||
// Destroy the texture so it can't be used again
|
||||
DestroyInternal();
|
||||
return {};
|
||||
}
|
||||
|
||||
Texture::~Texture() {
|
||||
DestroyInternal();
|
||||
}
|
||||
|
@ -479,12 +590,20 @@ namespace dawn_native { namespace vulkan {
|
|||
// freed after the VkImage is destroyed and this is taken care of by the
|
||||
// FencedDeleter.
|
||||
device->GetMemoryAllocator()->Free(&mMemoryAllocation);
|
||||
|
||||
if (mHandle != VK_NULL_HANDLE) {
|
||||
device->GetFencedDeleter()->DeleteWhenUnused(mHandle);
|
||||
}
|
||||
}
|
||||
|
||||
if (mHandle != VK_NULL_HANDLE) {
|
||||
device->GetFencedDeleter()->DeleteWhenUnused(mHandle);
|
||||
}
|
||||
|
||||
if (mExternalAllocation != VK_NULL_HANDLE) {
|
||||
device->GetFencedDeleter()->DeleteWhenUnused(mExternalAllocation);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
VkImage Texture::GetHandle() const {
|
||||
|
@ -499,7 +618,7 @@ namespace dawn_native { namespace vulkan {
|
|||
dawn::TextureUsageBit usage) {
|
||||
// Avoid encoding barriers when it isn't needed.
|
||||
bool lastReadOnly = (mLastUsage & kReadOnlyTextureUsages) == mLastUsage;
|
||||
if (lastReadOnly && mLastUsage == usage) {
|
||||
if (lastReadOnly && mLastUsage == usage && mLastExternalState == mExternalState) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -515,8 +634,6 @@ namespace dawn_native { namespace vulkan {
|
|||
barrier.dstAccessMask = VulkanAccessFlags(usage, format);
|
||||
barrier.oldLayout = VulkanImageLayout(mLastUsage, format);
|
||||
barrier.newLayout = VulkanImageLayout(usage, format);
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.image = mHandle;
|
||||
// This transitions the whole resource but assumes it is a 2D texture
|
||||
ASSERT(GetDimension() == dawn::TextureDimension::e2D);
|
||||
|
@ -526,11 +643,36 @@ namespace dawn_native { namespace vulkan {
|
|||
barrier.subresourceRange.baseArrayLayer = 0;
|
||||
barrier.subresourceRange.layerCount = GetArrayLayers();
|
||||
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
|
||||
if (mExternalState == ExternalState::PendingAcquire) {
|
||||
// Transfer texture from external queue to graphics queue
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR;
|
||||
barrier.dstQueueFamilyIndex = ToBackend(GetDevice())->GetGraphicsQueueFamily();
|
||||
// Don't override oldLayout to leave it as VK_IMAGE_LAYOUT_UNDEFINED
|
||||
// TODO(http://crbug.com/dawn/200)
|
||||
mExternalState = ExternalState::Acquired;
|
||||
|
||||
} else if (mExternalState == ExternalState::PendingRelease) {
|
||||
// Transfer texture from graphics queue to external queue
|
||||
barrier.srcQueueFamilyIndex = ToBackend(GetDevice())->GetGraphicsQueueFamily();
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
mExternalState = ExternalState::Released;
|
||||
}
|
||||
|
||||
// Move required semaphores into waitSemaphores
|
||||
recordingContext->waitSemaphores.insert(recordingContext->waitSemaphores.end(),
|
||||
mWaitRequirements.begin(), mWaitRequirements.end());
|
||||
mWaitRequirements.clear();
|
||||
|
||||
ToBackend(GetDevice())
|
||||
->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0,
|
||||
nullptr, 0, nullptr, 1, &barrier);
|
||||
|
||||
mLastUsage = usage;
|
||||
mLastExternalState = mExternalState;
|
||||
}
|
||||
|
||||
void Texture::ClearTexture(CommandRecordingContext* recordingContext,
|
||||
|
|
|
@ -18,20 +18,39 @@
|
|||
#include "dawn_native/Texture.h"
|
||||
|
||||
#include "common/vulkan_platform.h"
|
||||
#include "dawn_native/vulkan/ExternalHandle.h"
|
||||
#include "dawn_native/vulkan/MemoryAllocator.h"
|
||||
|
||||
namespace dawn_native { namespace vulkan {
|
||||
|
||||
struct CommandRecordingContext;
|
||||
struct ExternalImageDescriptor;
|
||||
|
||||
VkFormat VulkanImageFormat(dawn::TextureFormat format);
|
||||
VkImageUsageFlags VulkanImageUsage(dawn::TextureUsageBit usage, const Format& format);
|
||||
VkSampleCountFlagBits VulkanSampleCount(uint32_t sampleCount);
|
||||
|
||||
MaybeError ValidateVulkanImageCanBeWrapped(const DeviceBase* device,
|
||||
const TextureDescriptor* descriptor);
|
||||
|
||||
class Texture : public TextureBase {
|
||||
public:
|
||||
enum class ExternalState {
|
||||
InternalOnly,
|
||||
PendingAcquire,
|
||||
Acquired,
|
||||
PendingRelease,
|
||||
Released
|
||||
};
|
||||
|
||||
Texture(Device* device, const TextureDescriptor* descriptor);
|
||||
Texture(Device* device, const TextureDescriptor* descriptor, VkImage nativeImage);
|
||||
Texture(Device* device,
|
||||
const ExternalImageDescriptor* descriptor,
|
||||
const TextureDescriptor* textureDescriptor,
|
||||
VkSemaphore signalSemaphore,
|
||||
VkDeviceMemory externalMemoryAllocation,
|
||||
std::vector<VkSemaphore> waitSemaphores);
|
||||
~Texture();
|
||||
|
||||
VkImage GetHandle() const;
|
||||
|
@ -48,6 +67,8 @@ namespace dawn_native { namespace vulkan {
|
|||
uint32_t baseArrayLayer,
|
||||
uint32_t layerCount);
|
||||
|
||||
MaybeError SignalAndDestroy(VkSemaphore* outSignalSemaphore);
|
||||
|
||||
private:
|
||||
void DestroyImpl() override;
|
||||
void ClearTexture(CommandRecordingContext* recordingContext,
|
||||
|
@ -58,6 +79,12 @@ namespace dawn_native { namespace vulkan {
|
|||
|
||||
VkImage mHandle = VK_NULL_HANDLE;
|
||||
DeviceMemoryAllocation mMemoryAllocation;
|
||||
VkDeviceMemory mExternalAllocation = VK_NULL_HANDLE;
|
||||
|
||||
ExternalState mExternalState = ExternalState::InternalOnly;
|
||||
ExternalState mLastExternalState = ExternalState::InternalOnly;
|
||||
VkSemaphore mSignalSemaphore = VK_NULL_HANDLE;
|
||||
std::vector<VkSemaphore> mWaitRequirements;
|
||||
|
||||
// A usage of none will make sure the texture is transitioned before its first use as
|
||||
// required by the spec.
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "common/SwapChainUtils.h"
|
||||
#include "dawn_native/vulkan/DeviceVk.h"
|
||||
#include "dawn_native/vulkan/NativeSwapChainImplVk.h"
|
||||
#include "dawn_native/vulkan/TextureVk.h"
|
||||
|
||||
namespace dawn_native { namespace vulkan {
|
||||
|
||||
|
@ -52,4 +53,32 @@ namespace dawn_native { namespace vulkan {
|
|||
return static_cast<DawnTextureFormat>(impl->GetPreferredFormat());
|
||||
}
|
||||
|
||||
#ifdef DAWN_PLATFORM_LINUX
|
||||
DawnTexture WrapVulkanImageOpaqueFD(DawnDevice cDevice,
|
||||
const ExternalImageDescriptorOpaqueFD* descriptor) {
|
||||
Device* device = reinterpret_cast<Device*>(cDevice);
|
||||
|
||||
TextureBase* texture = device->CreateTextureWrappingVulkanImage(
|
||||
descriptor, descriptor->memoryFD, descriptor->waitFDs);
|
||||
|
||||
return reinterpret_cast<DawnTexture>(texture);
|
||||
}
|
||||
|
||||
int ExportSignalSemaphoreOpaqueFD(DawnDevice cDevice, DawnTexture cTexture) {
|
||||
Device* device = reinterpret_cast<Device*>(cDevice);
|
||||
Texture* texture = reinterpret_cast<Texture*>(cTexture);
|
||||
|
||||
if (!texture) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ExternalSemaphoreHandle outHandle;
|
||||
if (device->ConsumedError(device->SignalAndExportExternalTexture(texture, &outHandle))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return outHandle;
|
||||
}
|
||||
#endif
|
||||
|
||||
}} // namespace dawn_native::vulkan
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2019 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef DAWNNATIVE_VULKAN_EXTERNALMEMORY_SERVICE_H_
|
||||
#define DAWNNATIVE_VULKAN_EXTERNALMEMORY_SERVICE_H_
|
||||
|
||||
#include "common/vulkan_platform.h"
|
||||
#include "dawn_native/Error.h"
|
||||
#include "dawn_native/vulkan/ExternalHandle.h"
|
||||
|
||||
namespace dawn_native { namespace vulkan {
|
||||
class Device;
|
||||
}} // namespace dawn_native::vulkan
|
||||
|
||||
namespace dawn_native { namespace vulkan { namespace external_memory {
|
||||
|
||||
class Service {
|
||||
public:
|
||||
explicit Service(Device* device);
|
||||
~Service();
|
||||
|
||||
// True if the device reports it supports this feature
|
||||
bool Supported();
|
||||
|
||||
// Given an external handle pointing to memory, import it into a VkDeviceMemory
|
||||
ResultOrError<VkDeviceMemory> ImportMemory(ExternalMemoryHandle handle,
|
||||
VkDeviceSize allocationSize,
|
||||
uint32_t memoryTypeIndex);
|
||||
|
||||
private:
|
||||
Device* mDevice = nullptr;
|
||||
|
||||
// True if early checks pass that determine if the service is supported
|
||||
bool mSupportedFirstPass = false;
|
||||
};
|
||||
|
||||
}}} // namespace dawn_native::vulkan::external_memory
|
||||
|
||||
#endif // DAWNNATIVE_VULKAN_EXTERNALMEMORY_SERVICE_H_
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2019 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dawn_native/vulkan/DeviceVk.h"
|
||||
#include "dawn_native/vulkan/external_memory/MemoryService.h"
|
||||
|
||||
namespace dawn_native { namespace vulkan { namespace external_memory {
|
||||
|
||||
Service::Service(Device* device) : mDevice(device) {
|
||||
DAWN_UNUSED(mDevice);
|
||||
DAWN_UNUSED(mSupportedFirstPass);
|
||||
}
|
||||
|
||||
Service::~Service() = default;
|
||||
|
||||
bool Service::Supported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
|
||||
VkDeviceSize allocationSize,
|
||||
uint32_t memoryTypeIndex) {
|
||||
return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan");
|
||||
}
|
||||
|
||||
}}} // namespace dawn_native::vulkan::external_memory
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2019 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dawn_native/vulkan/DeviceVk.h"
|
||||
#include "dawn_native/vulkan/VulkanError.h"
|
||||
#include "dawn_native/vulkan/external_memory/MemoryService.h"
|
||||
|
||||
namespace dawn_native { namespace vulkan { namespace external_memory {
|
||||
|
||||
Service::Service(Device* device) : mDevice(device) {
|
||||
const VulkanDeviceInfo& info = mDevice->GetDeviceInfo();
|
||||
mSupportedFirstPass = info.externalMemory && info.externalMemoryFD;
|
||||
}
|
||||
|
||||
Service::~Service() = default;
|
||||
|
||||
bool Service::Supported() {
|
||||
// TODO(idanr): Query device here for additional support information, decide if supported
|
||||
// This will likely be done through vkGetPhysicalDeviceImageFormatProperties2KHR, where
|
||||
// we give it the intended image properties and handle type and see if it's supported
|
||||
return mSupportedFirstPass;
|
||||
}
|
||||
|
||||
ResultOrError<VkDeviceMemory> Service::ImportMemory(ExternalMemoryHandle handle,
|
||||
VkDeviceSize allocationSize,
|
||||
uint32_t memoryTypeIndex) {
|
||||
if (handle < 0) {
|
||||
return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle");
|
||||
}
|
||||
|
||||
VkImportMemoryFdInfoKHR importMemoryFdInfo;
|
||||
importMemoryFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
|
||||
importMemoryFdInfo.pNext = nullptr;
|
||||
importMemoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
|
||||
importMemoryFdInfo.fd = handle;
|
||||
|
||||
VkMemoryAllocateInfo allocateInfo;
|
||||
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocateInfo.pNext = &importMemoryFdInfo;
|
||||
allocateInfo.allocationSize = allocationSize;
|
||||
allocateInfo.memoryTypeIndex = memoryTypeIndex;
|
||||
|
||||
VkDeviceMemory allocatedMemory = VK_NULL_HANDLE;
|
||||
DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo,
|
||||
nullptr, &allocatedMemory),
|
||||
"vkAllocateMemory"));
|
||||
return allocatedMemory;
|
||||
}
|
||||
|
||||
}}} // namespace dawn_native::vulkan::external_memory
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2019 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef DAWNNATIVE_VULKAN_EXTERNALSEMAPHORE_SERVICE_H_
|
||||
#define DAWNNATIVE_VULKAN_EXTERNALSEMAPHORE_SERVICE_H_
|
||||
|
||||
#include "common/vulkan_platform.h"
|
||||
#include "dawn_native/Error.h"
|
||||
#include "dawn_native/vulkan/ExternalHandle.h"
|
||||
|
||||
namespace dawn_native { namespace vulkan {
|
||||
class Device;
|
||||
}} // namespace dawn_native::vulkan
|
||||
|
||||
namespace dawn_native { namespace vulkan { namespace external_semaphore {
|
||||
|
||||
class Service {
|
||||
public:
|
||||
explicit Service(Device* device);
|
||||
~Service();
|
||||
|
||||
// True if the device reports it supports this feature
|
||||
bool Supported();
|
||||
|
||||
// Given an external handle, import it into a VkSemaphore
|
||||
ResultOrError<VkSemaphore> ImportSemaphore(ExternalSemaphoreHandle handle);
|
||||
|
||||
// Create a VkSemaphore that is exportable into an external handle later
|
||||
ResultOrError<VkSemaphore> CreateExportableSemaphore();
|
||||
|
||||
// Export a VkSemaphore into an external handle
|
||||
ResultOrError<ExternalSemaphoreHandle> ExportSemaphore(VkSemaphore semaphore);
|
||||
|
||||
private:
|
||||
Device* mDevice = nullptr;
|
||||
|
||||
// True if early checks pass that determine if the service is supported
|
||||
bool mSupportedFirstPass = false;
|
||||
};
|
||||
|
||||
}}} // namespace dawn_native::vulkan::external_semaphore
|
||||
|
||||
#endif // DAWNNATIVE_VULKAN_EXTERNALSEMAPHORE_SERVICE_H_
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2019 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dawn_native/vulkan/DeviceVk.h"
|
||||
#include "dawn_native/vulkan/external_semaphore/SemaphoreService.h"
|
||||
|
||||
namespace dawn_native { namespace vulkan { namespace external_semaphore {
|
||||
|
||||
Service::Service(Device* device) : mDevice(device) {
|
||||
DAWN_UNUSED(mDevice);
|
||||
DAWN_UNUSED(mSupportedFirstPass);
|
||||
}
|
||||
|
||||
Service::~Service() = default;
|
||||
|
||||
bool Service::Supported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResultOrError<VkSemaphore> Service::ImportSemaphore(ExternalSemaphoreHandle handle) {
|
||||
return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan");
|
||||
}
|
||||
|
||||
ResultOrError<VkSemaphore> Service::CreateExportableSemaphore() {
|
||||
return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan");
|
||||
}
|
||||
|
||||
ResultOrError<ExternalSemaphoreHandle> Service::ExportSemaphore(VkSemaphore semaphore) {
|
||||
return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan");
|
||||
}
|
||||
|
||||
}}} // namespace dawn_native::vulkan::external_semaphore
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright 2019 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dawn_native/vulkan/DeviceVk.h"
|
||||
#include "dawn_native/vulkan/VulkanError.h"
|
||||
#include "dawn_native/vulkan/external_semaphore/SemaphoreService.h"
|
||||
|
||||
namespace dawn_native { namespace vulkan { namespace external_semaphore {
|
||||
|
||||
Service::Service(Device* device) : mDevice(device) {
|
||||
const VulkanDeviceInfo& info = mDevice->GetDeviceInfo();
|
||||
mSupportedFirstPass = info.externalSemaphore && info.externalSemaphoreFD;
|
||||
// TODO(idanr): Query device here for additional support information, decide if supported
|
||||
// This will likely be done through vkGetPhysicalDeviceExternalSemaphorePropertiesKHR, where
|
||||
// we give it the intended handle type and see if it's supported
|
||||
}
|
||||
|
||||
Service::~Service() = default;
|
||||
|
||||
bool Service::Supported() {
|
||||
return mSupportedFirstPass;
|
||||
}
|
||||
|
||||
ResultOrError<VkSemaphore> Service::ImportSemaphore(ExternalSemaphoreHandle handle) {
|
||||
if (handle < 0) {
|
||||
return DAWN_VALIDATION_ERROR("Trying to import semaphore with invalid handle");
|
||||
}
|
||||
|
||||
VkSemaphore semaphore = VK_NULL_HANDLE;
|
||||
VkSemaphoreCreateInfo info;
|
||||
info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
info.pNext = nullptr;
|
||||
info.flags = 0;
|
||||
|
||||
DAWN_TRY(CheckVkSuccess(
|
||||
mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &info, nullptr, &semaphore),
|
||||
"vkCreateSemaphore"));
|
||||
|
||||
VkImportSemaphoreFdInfoKHR importSemaphoreFdInfo;
|
||||
importSemaphoreFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
|
||||
importSemaphoreFdInfo.pNext = nullptr;
|
||||
importSemaphoreFdInfo.semaphore = semaphore;
|
||||
importSemaphoreFdInfo.flags = 0;
|
||||
importSemaphoreFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
|
||||
importSemaphoreFdInfo.fd = handle;
|
||||
|
||||
MaybeError status = CheckVkSuccess(
|
||||
mDevice->fn.ImportSemaphoreFdKHR(mDevice->GetVkDevice(), &importSemaphoreFdInfo),
|
||||
"vkImportSemaphoreFdKHR");
|
||||
|
||||
if (status.IsError()) {
|
||||
mDevice->fn.DestroySemaphore(mDevice->GetVkDevice(), semaphore, nullptr);
|
||||
DAWN_TRY(std::move(status));
|
||||
}
|
||||
|
||||
return semaphore;
|
||||
}
|
||||
|
||||
ResultOrError<VkSemaphore> Service::CreateExportableSemaphore() {
|
||||
VkExportSemaphoreCreateInfoKHR exportSemaphoreInfo;
|
||||
exportSemaphoreInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR;
|
||||
exportSemaphoreInfo.pNext = nullptr;
|
||||
exportSemaphoreInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
|
||||
|
||||
VkSemaphoreCreateInfo semaphoreCreateInfo;
|
||||
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
semaphoreCreateInfo.pNext = &exportSemaphoreInfo;
|
||||
semaphoreCreateInfo.flags = 0;
|
||||
|
||||
VkSemaphore signalSemaphore;
|
||||
DAWN_TRY(
|
||||
CheckVkSuccess(mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &semaphoreCreateInfo,
|
||||
nullptr, &signalSemaphore),
|
||||
"vkCreateSemaphore"));
|
||||
return signalSemaphore;
|
||||
}
|
||||
|
||||
ResultOrError<ExternalSemaphoreHandle> Service::ExportSemaphore(VkSemaphore semaphore) {
|
||||
VkSemaphoreGetFdInfoKHR semaphoreGetFdInfo;
|
||||
semaphoreGetFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
|
||||
semaphoreGetFdInfo.pNext = nullptr;
|
||||
semaphoreGetFdInfo.semaphore = semaphore;
|
||||
semaphoreGetFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
|
||||
|
||||
int fd = -1;
|
||||
DAWN_TRY(CheckVkSuccess(
|
||||
mDevice->fn.GetSemaphoreFdKHR(mDevice->GetVkDevice(), &semaphoreGetFdInfo, &fd),
|
||||
"vkGetSemaphoreFdKHR"));
|
||||
|
||||
ASSERT(fd >= 0);
|
||||
return fd;
|
||||
}
|
||||
|
||||
}}} // namespace dawn_native::vulkan::external_semaphore
|
|
@ -23,12 +23,44 @@
|
|||
#include <vector>
|
||||
|
||||
namespace dawn_native { namespace vulkan {
|
||||
|
||||
// Common properties of external images
|
||||
struct ExternalImageDescriptor {
|
||||
const DawnTextureDescriptor* cTextureDescriptor; // Must match image creation params
|
||||
bool isCleared; // Sets whether the texture will be cleared before use
|
||||
VkDeviceSize allocationSize; // Must match VkMemoryAllocateInfo from image creation
|
||||
uint32_t memoryTypeIndex; // Must match VkMemoryAllocateInfo from image creation
|
||||
};
|
||||
|
||||
DAWN_NATIVE_EXPORT VkInstance GetInstance(DawnDevice device);
|
||||
|
||||
DAWN_NATIVE_EXPORT DawnSwapChainImplementation CreateNativeSwapChainImpl(DawnDevice device,
|
||||
VkSurfaceKHR surface);
|
||||
DAWN_NATIVE_EXPORT DawnTextureFormat
|
||||
GetNativeSwapChainPreferredFormat(const DawnSwapChainImplementation* swapChain);
|
||||
|
||||
// Can't use DAWN_PLATFORM_LINUX since header included in both dawn and chrome
|
||||
#ifdef __linux__
|
||||
// Descriptor for opaque file descriptor image import
|
||||
struct ExternalImageDescriptorOpaqueFD : ExternalImageDescriptor {
|
||||
int memoryFD; // A file descriptor from an export of the memory of the image
|
||||
std::vector<int> waitFDs; // File descriptors of semaphores which will be waited on
|
||||
};
|
||||
|
||||
// Imports an external vulkan image from an opaque file descriptor. Internally, this uses
|
||||
// external memory / semaphore extensions to import the image. Then, waits on the provided
|
||||
// |descriptor->waitFDs| before the texture can be used. Finally, a signal semaphore
|
||||
// can be exported, transferring control back to the caller.
|
||||
// On failure, returns a nullptr
|
||||
DAWN_NATIVE_EXPORT DawnTexture
|
||||
WrapVulkanImageOpaqueFD(DawnDevice cDevice,
|
||||
const ExternalImageDescriptorOpaqueFD* descriptor);
|
||||
|
||||
// Exports a signal semaphore from a wrapped texture. This must be called on wrapped
|
||||
// textures before they are destroyed. On failure, returns -1
|
||||
DAWN_NATIVE_EXPORT int ExportSignalSemaphoreOpaqueFD(DawnDevice cDevice,
|
||||
DawnTexture cTexture);
|
||||
#endif // __linux__
|
||||
}} // namespace dawn_native::vulkan
|
||||
|
||||
#endif // DAWNNATIVE_VULKANBACKEND_H_
|
||||
|
|
|
@ -0,0 +1,912 @@
|
|||
// Copyright 2019 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "tests/DawnTest.h"
|
||||
|
||||
#include "common/vulkan_platform.h"
|
||||
#include "dawn_native/VulkanBackend.h"
|
||||
#include "dawn_native/vulkan/AdapterVk.h"
|
||||
#include "dawn_native/vulkan/DeviceVk.h"
|
||||
#include "dawn_native/vulkan/FencedDeleter.h"
|
||||
#include "dawn_native/vulkan/MemoryAllocator.h"
|
||||
#include "dawn_native/vulkan/TextureVk.h"
|
||||
#include "utils/DawnHelpers.h"
|
||||
#include "utils/SystemUtils.h"
|
||||
|
||||
// TODO(crbug.com/966500): Intel is disabled until upgrade is finished
|
||||
|
||||
namespace {
|
||||
|
||||
class VulkanImageWrappingTestBase : public DawnTest {
|
||||
public:
|
||||
void SetUp() override {
|
||||
DawnTest::SetUp();
|
||||
if (UsesWire() || IsIntel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
deviceVk = reinterpret_cast<dawn_native::vulkan::Device*>(device.Get());
|
||||
}
|
||||
|
||||
// Creates a VkImage with external memory
|
||||
VkResult CreateImage(dawn_native::vulkan::Device* deviceVk,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
VkFormat format,
|
||||
VkImage* image) {
|
||||
VkExternalMemoryImageCreateInfoKHR externalInfo;
|
||||
externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR;
|
||||
externalInfo.pNext = nullptr;
|
||||
externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
|
||||
|
||||
auto usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
|
||||
VkImageCreateInfo createInfo;
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
createInfo.pNext = &externalInfo;
|
||||
createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR;
|
||||
createInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
createInfo.format = format;
|
||||
createInfo.extent = {width, height, 1};
|
||||
createInfo.mipLevels = 1;
|
||||
createInfo.arrayLayers = 1;
|
||||
createInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
createInfo.usage = usage;
|
||||
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
createInfo.queueFamilyIndexCount = 0;
|
||||
createInfo.pQueueFamilyIndices = nullptr;
|
||||
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
return deviceVk->fn.CreateImage(deviceVk->GetVkDevice(), &createInfo, nullptr, image);
|
||||
}
|
||||
|
||||
// Allocates memory for an image
|
||||
VkResult AllocateMemory(dawn_native::vulkan::Device* deviceVk,
|
||||
VkImage handle,
|
||||
VkDeviceMemory* allocation,
|
||||
VkDeviceSize* allocationSize,
|
||||
uint32_t* memoryTypeIndex) {
|
||||
// Create the image memory and associate it with the container
|
||||
VkMemoryRequirements requirements;
|
||||
deviceVk->fn.GetImageMemoryRequirements(deviceVk->GetVkDevice(), handle, &requirements);
|
||||
|
||||
// Import memory from file descriptor
|
||||
VkExportMemoryAllocateInfoKHR externalInfo;
|
||||
externalInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR;
|
||||
externalInfo.pNext = nullptr;
|
||||
externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
|
||||
|
||||
int bestType = deviceVk->GetMemoryAllocator()->FindBestTypeIndex(requirements, false);
|
||||
VkMemoryAllocateInfo allocateInfo;
|
||||
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocateInfo.pNext = &externalInfo;
|
||||
allocateInfo.allocationSize = requirements.size;
|
||||
allocateInfo.memoryTypeIndex = static_cast<uint32_t>(bestType);
|
||||
|
||||
*allocationSize = allocateInfo.allocationSize;
|
||||
*memoryTypeIndex = allocateInfo.memoryTypeIndex;
|
||||
|
||||
return deviceVk->fn.AllocateMemory(deviceVk->GetVkDevice(), &allocateInfo, nullptr,
|
||||
allocation);
|
||||
}
|
||||
|
||||
// Binds memory to an image
|
||||
VkResult BindMemory(dawn_native::vulkan::Device* deviceVk,
|
||||
VkImage handle,
|
||||
VkDeviceMemory* memory) {
|
||||
return deviceVk->fn.BindImageMemory(deviceVk->GetVkDevice(), handle, *memory, 0);
|
||||
}
|
||||
|
||||
// Extracts a file descriptor representing memory on a device
|
||||
int GetMemoryFd(dawn_native::vulkan::Device* deviceVk, VkDeviceMemory memory) {
|
||||
VkMemoryGetFdInfoKHR getFdInfo;
|
||||
getFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;
|
||||
getFdInfo.pNext = nullptr;
|
||||
getFdInfo.memory = memory;
|
||||
getFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
|
||||
|
||||
int memoryFd = -1;
|
||||
deviceVk->fn.GetMemoryFdKHR(deviceVk->GetVkDevice(), &getFdInfo, &memoryFd);
|
||||
|
||||
EXPECT_GE(memoryFd, 0) << "Failed to get file descriptor for external memory";
|
||||
return memoryFd;
|
||||
}
|
||||
|
||||
// Prepares and exports memory for an image on a given device
|
||||
void CreateBindExportImage(dawn_native::vulkan::Device* deviceVk,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
VkFormat format,
|
||||
VkImage* handle,
|
||||
VkDeviceMemory* allocation,
|
||||
VkDeviceSize* allocationSize,
|
||||
uint32_t* memoryTypeIndex,
|
||||
int* memoryFd) {
|
||||
VkResult result = CreateImage(deviceVk, width, height, format, handle);
|
||||
EXPECT_EQ(result, VK_SUCCESS) << "Failed to create external image";
|
||||
|
||||
VkResult resultBool =
|
||||
AllocateMemory(deviceVk, *handle, allocation, allocationSize, memoryTypeIndex);
|
||||
EXPECT_EQ(resultBool, VK_SUCCESS) << "Failed to allocate external memory";
|
||||
|
||||
result = BindMemory(deviceVk, *handle, allocation);
|
||||
EXPECT_EQ(result, VK_SUCCESS) << "Failed to bind image memory";
|
||||
|
||||
*memoryFd = GetMemoryFd(deviceVk, *allocation);
|
||||
}
|
||||
|
||||
// Wraps a vulkan image from external memory
|
||||
dawn::Texture WrapVulkanImage(dawn::Device device,
|
||||
const dawn::TextureDescriptor* textureDescriptor,
|
||||
int memoryFd,
|
||||
VkDeviceSize allocationSize,
|
||||
uint32_t memoryTypeIndex,
|
||||
std::vector<int> waitFDs,
|
||||
bool isCleared = true,
|
||||
bool expectValid = true) {
|
||||
dawn_native::vulkan::ExternalImageDescriptorOpaqueFD descriptor;
|
||||
descriptor.cTextureDescriptor =
|
||||
reinterpret_cast<const DawnTextureDescriptor*>(textureDescriptor);
|
||||
descriptor.isCleared = isCleared;
|
||||
descriptor.allocationSize = allocationSize;
|
||||
descriptor.memoryTypeIndex = memoryTypeIndex;
|
||||
descriptor.memoryFD = memoryFd;
|
||||
descriptor.waitFDs = waitFDs;
|
||||
|
||||
DawnTexture texture =
|
||||
dawn_native::vulkan::WrapVulkanImageOpaqueFD(device.Get(), &descriptor);
|
||||
|
||||
if (expectValid) {
|
||||
EXPECT_NE(texture, nullptr) << "Failed to wrap image, are external memory / "
|
||||
"semaphore extensions supported?";
|
||||
} else {
|
||||
EXPECT_EQ(texture, nullptr);
|
||||
}
|
||||
|
||||
return dawn::Texture::Acquire(texture);
|
||||
}
|
||||
|
||||
// Exports the signal from a wrapped texture and ignores it
|
||||
// We have to export the signal before destroying the wrapped texture else it's an assertion
|
||||
// failure
|
||||
void IgnoreSignalSemaphore(dawn::Device device, dawn::Texture wrappedTexture) {
|
||||
int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(),
|
||||
wrappedTexture.Get());
|
||||
ASSERT_NE(fd, -1);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
protected:
|
||||
dawn_native::vulkan::Device* deviceVk;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class VulkanImageWrappingValidationTests : public VulkanImageWrappingTestBase {
|
||||
public:
|
||||
void SetUp() override {
|
||||
VulkanImageWrappingTestBase::SetUp();
|
||||
if (UsesWire() || IsIntel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage,
|
||||
&defaultAllocation, &defaultAllocationSize, &defaultMemoryTypeIndex,
|
||||
&defaultFd);
|
||||
defaultDescriptor.dimension = dawn::TextureDimension::e2D;
|
||||
defaultDescriptor.format = dawn::TextureFormat::RGBA8Unorm;
|
||||
defaultDescriptor.size = {1, 1, 1};
|
||||
defaultDescriptor.sampleCount = 1;
|
||||
defaultDescriptor.arrayLayerCount = 1;
|
||||
defaultDescriptor.mipLevelCount = 1;
|
||||
defaultDescriptor.usage = dawn::TextureUsageBit::OutputAttachment |
|
||||
dawn::TextureUsageBit::CopySrc | dawn::TextureUsageBit::CopyDst;
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
if (UsesWire() || IsIntel()) {
|
||||
VulkanImageWrappingTestBase::TearDown();
|
||||
return;
|
||||
}
|
||||
|
||||
deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage);
|
||||
deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation);
|
||||
VulkanImageWrappingTestBase::TearDown();
|
||||
}
|
||||
|
||||
protected:
|
||||
dawn::TextureDescriptor defaultDescriptor;
|
||||
VkImage defaultImage;
|
||||
VkDeviceMemory defaultAllocation;
|
||||
VkDeviceSize defaultAllocationSize;
|
||||
uint32_t defaultMemoryTypeIndex;
|
||||
int defaultFd;
|
||||
};
|
||||
|
||||
// Test no error occurs if the import is valid
|
||||
TEST_P(VulkanImageWrappingValidationTests, SuccessfulImport) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
dawn::Texture texture =
|
||||
WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {}, true, true);
|
||||
EXPECT_NE(texture.Get(), nullptr);
|
||||
IgnoreSignalSemaphore(device, texture);
|
||||
}
|
||||
|
||||
// Test an error occurs if the texture descriptor is missing
|
||||
TEST_P(VulkanImageWrappingValidationTests, MissingTextureDescriptor) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
ASSERT_DEVICE_ERROR(dawn::Texture texture =
|
||||
WrapVulkanImage(device, nullptr, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {}, true, false));
|
||||
EXPECT_EQ(texture.Get(), nullptr);
|
||||
}
|
||||
|
||||
// Test an error occurs if the texture descriptor is invalid
|
||||
TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
defaultDescriptor.nextInChain = this;
|
||||
|
||||
ASSERT_DEVICE_ERROR(dawn::Texture texture = WrapVulkanImage(
|
||||
device, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {}, true, false));
|
||||
EXPECT_EQ(texture.Get(), nullptr);
|
||||
}
|
||||
|
||||
// Test an error occurs if the descriptor dimension isn't 2D
|
||||
TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDimension) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
defaultDescriptor.dimension = dawn::TextureDimension::e1D;
|
||||
|
||||
ASSERT_DEVICE_ERROR(dawn::Texture texture = WrapVulkanImage(
|
||||
device, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {}, true, false));
|
||||
EXPECT_EQ(texture.Get(), nullptr);
|
||||
}
|
||||
|
||||
// Test an error occurs if the descriptor mip level count isn't 1
|
||||
TEST_P(VulkanImageWrappingValidationTests, InvalidMipLevelCount) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
defaultDescriptor.mipLevelCount = 2;
|
||||
|
||||
ASSERT_DEVICE_ERROR(dawn::Texture texture = WrapVulkanImage(
|
||||
device, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {}, true, false));
|
||||
EXPECT_EQ(texture.Get(), nullptr);
|
||||
}
|
||||
|
||||
// Test an error occurs if the descriptor array layer count isn't 1
|
||||
TEST_P(VulkanImageWrappingValidationTests, InvalidArrayLayerCount) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
defaultDescriptor.arrayLayerCount = 2;
|
||||
|
||||
ASSERT_DEVICE_ERROR(dawn::Texture texture = WrapVulkanImage(
|
||||
device, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {}, true, false));
|
||||
EXPECT_EQ(texture.Get(), nullptr);
|
||||
}
|
||||
|
||||
// Test an error occurs if the descriptor sample count isn't 1
|
||||
TEST_P(VulkanImageWrappingValidationTests, InvalidSampleCount) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
defaultDescriptor.sampleCount = 4;
|
||||
|
||||
ASSERT_DEVICE_ERROR(dawn::Texture texture = WrapVulkanImage(
|
||||
device, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {}, true, false));
|
||||
EXPECT_EQ(texture.Get(), nullptr);
|
||||
}
|
||||
|
||||
// Test an error occurs if we try to export the signal semaphore twice
|
||||
TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
dawn::Texture texture =
|
||||
WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {}, true, true);
|
||||
ASSERT_NE(texture.Get(), nullptr);
|
||||
IgnoreSignalSemaphore(device, texture);
|
||||
ASSERT_DEVICE_ERROR(
|
||||
int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), texture.Get()));
|
||||
ASSERT_EQ(fd, -1);
|
||||
}
|
||||
|
||||
// Test an error occurs if we try to export the signal semaphore from a normal texture
|
||||
TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
dawn::Texture texture = device.CreateTexture(&defaultDescriptor);
|
||||
ASSERT_NE(texture.Get(), nullptr);
|
||||
ASSERT_DEVICE_ERROR(
|
||||
int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), texture.Get()));
|
||||
ASSERT_EQ(fd, -1);
|
||||
}
|
||||
|
||||
// Test an error occurs if we try to export the signal semaphore from a destroyed texture
|
||||
TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
dawn::Texture texture = device.CreateTexture(&defaultDescriptor);
|
||||
ASSERT_NE(texture.Get(), nullptr);
|
||||
texture.Destroy();
|
||||
ASSERT_DEVICE_ERROR(
|
||||
int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), texture.Get()));
|
||||
ASSERT_EQ(fd, -1);
|
||||
}
|
||||
|
||||
// Fixture to test using external memory textures through different usages.
|
||||
// These tests are skipped if the harness is using the wire.
|
||||
class VulkanImageWrappingUsageTests : public VulkanImageWrappingTestBase {
|
||||
public:
|
||||
void SetUp() override {
|
||||
VulkanImageWrappingTestBase::SetUp();
|
||||
if (UsesWire() || IsIntel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create another device based on the original
|
||||
backendAdapter = reinterpret_cast<dawn_native::vulkan::Adapter*>(deviceVk->GetAdapter());
|
||||
deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds;
|
||||
deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds;
|
||||
|
||||
secondDeviceVk = reinterpret_cast<dawn_native::vulkan::Device*>(
|
||||
backendAdapter->CreateDevice(&deviceDescriptor));
|
||||
secondDevice = dawn::Device::Acquire(reinterpret_cast<DawnDevice>(secondDeviceVk));
|
||||
|
||||
CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage,
|
||||
&defaultAllocation, &defaultAllocationSize, &defaultMemoryTypeIndex,
|
||||
&defaultFd);
|
||||
defaultDescriptor.dimension = dawn::TextureDimension::e2D;
|
||||
defaultDescriptor.format = dawn::TextureFormat::RGBA8Unorm;
|
||||
defaultDescriptor.size = {1, 1, 1};
|
||||
defaultDescriptor.sampleCount = 1;
|
||||
defaultDescriptor.arrayLayerCount = 1;
|
||||
defaultDescriptor.mipLevelCount = 1;
|
||||
defaultDescriptor.usage = dawn::TextureUsageBit::OutputAttachment |
|
||||
dawn::TextureUsageBit::CopySrc | dawn::TextureUsageBit::CopyDst;
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
if (UsesWire() || IsIntel()) {
|
||||
VulkanImageWrappingTestBase::TearDown();
|
||||
return;
|
||||
}
|
||||
|
||||
deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage);
|
||||
deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation);
|
||||
VulkanImageWrappingTestBase::TearDown();
|
||||
}
|
||||
|
||||
protected:
|
||||
dawn::Device secondDevice;
|
||||
dawn_native::vulkan::Device* secondDeviceVk;
|
||||
|
||||
dawn_native::vulkan::Adapter* backendAdapter;
|
||||
dawn_native::DeviceDescriptor deviceDescriptor;
|
||||
|
||||
dawn::TextureDescriptor defaultDescriptor;
|
||||
VkImage defaultImage;
|
||||
VkDeviceMemory defaultAllocation;
|
||||
VkDeviceSize defaultAllocationSize;
|
||||
uint32_t defaultMemoryTypeIndex;
|
||||
int defaultFd;
|
||||
|
||||
// Clear a texture on a given device
|
||||
void ClearImage(dawn::Device device, dawn::Texture wrappedTexture, dawn::Color clearColor) {
|
||||
dawn::TextureView wrappedView = wrappedTexture.CreateDefaultView();
|
||||
|
||||
// Submit a clear operation
|
||||
utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {});
|
||||
renderPassDescriptor.cColorAttachmentsInfoPtr[0]->clearColor = clearColor;
|
||||
|
||||
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||
pass.EndPass();
|
||||
|
||||
dawn::CommandBuffer commands = encoder.Finish();
|
||||
|
||||
dawn::Queue queue = device.CreateQueue();
|
||||
queue.Submit(1, &commands);
|
||||
}
|
||||
|
||||
// Submits a 1x1x1 copy from source to destination
|
||||
void SimpleCopyTextureToTexture(dawn::Device device,
|
||||
dawn::Queue queue,
|
||||
dawn::Texture source,
|
||||
dawn::Texture destination) {
|
||||
dawn::TextureCopyView copySrc;
|
||||
copySrc.texture = source;
|
||||
copySrc.mipLevel = 0;
|
||||
copySrc.arrayLayer = 0;
|
||||
copySrc.origin = {0, 0, 0};
|
||||
|
||||
dawn::TextureCopyView copyDst;
|
||||
copyDst.texture = destination;
|
||||
copyDst.mipLevel = 0;
|
||||
copyDst.arrayLayer = 0;
|
||||
copyDst.origin = {0, 0, 0};
|
||||
|
||||
dawn::Extent3D copySize = {1, 1, 1};
|
||||
|
||||
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
encoder.CopyTextureToTexture(©Src, ©Dst, ©Size);
|
||||
dawn::CommandBuffer commands = encoder.Finish();
|
||||
|
||||
queue.Submit(1, &commands);
|
||||
}
|
||||
};
|
||||
|
||||
// Clear an image in |secondDevice|
|
||||
// Verify clear color is visible in |device|
|
||||
TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
|
||||
// Import the image on |secondDevice|
|
||||
dawn::Texture wrappedTexture =
|
||||
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {});
|
||||
|
||||
// Clear |wrappedTexture| on |secondDevice|
|
||||
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
|
||||
|
||||
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
|
||||
wrappedTexture.Get());
|
||||
|
||||
// Import the image to |device|, making sure we wait on signalFd
|
||||
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
|
||||
dawn::Texture nextWrappedTexture =
|
||||
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {signalFd});
|
||||
|
||||
// Verify |device| sees the changes from |secondDevice|
|
||||
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
|
||||
|
||||
IgnoreSignalSemaphore(device, nextWrappedTexture);
|
||||
}
|
||||
|
||||
// Import texture to |device| and |secondDevice|
|
||||
// Clear image in |secondDevice|
|
||||
// Verify clear color is visible in |device|
|
||||
// Verify the very first import into |device| also sees the change, since it should
|
||||
// alias the same memory
|
||||
TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevicesAliased) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
// Import the image on |device
|
||||
dawn::Texture wrappedTextureAlias = WrapVulkanImage(
|
||||
device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, {});
|
||||
|
||||
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
|
||||
|
||||
// Import the image on |secondDevice|
|
||||
dawn::Texture wrappedTexture =
|
||||
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {});
|
||||
|
||||
// Clear |wrappedTexture| on |secondDevice|
|
||||
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
|
||||
|
||||
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
|
||||
wrappedTexture.Get());
|
||||
|
||||
// Import the image to |device|, making sure we wait on signalFd
|
||||
memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
|
||||
dawn::Texture nextWrappedTexture =
|
||||
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {signalFd});
|
||||
|
||||
// Verify |device| sees the changes from |secondDevice| (waits)
|
||||
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
|
||||
|
||||
// Verify aliased texture sees changes from |secondDevice| (without waiting!)
|
||||
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), wrappedTextureAlias, 0, 0);
|
||||
|
||||
IgnoreSignalSemaphore(device, nextWrappedTexture);
|
||||
IgnoreSignalSemaphore(device, wrappedTextureAlias);
|
||||
}
|
||||
|
||||
// Clear an image in |secondDevice|
|
||||
// Verify clear color is not visible in |device| if we import the texture as not cleared
|
||||
TEST_P(VulkanImageWrappingUsageTests, UnclearedTextureIsCleared) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
|
||||
// Import the image on |secondDevice|
|
||||
dawn::Texture wrappedTexture =
|
||||
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {});
|
||||
|
||||
// Clear |wrappedTexture| on |secondDevice|
|
||||
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
|
||||
|
||||
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
|
||||
wrappedTexture.Get());
|
||||
|
||||
// Import the image to |device|, making sure we wait on signalFd
|
||||
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
|
||||
dawn::Texture nextWrappedTexture =
|
||||
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {signalFd}, false);
|
||||
|
||||
// Verify |device| doesn't see the changes from |secondDevice|
|
||||
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0);
|
||||
|
||||
IgnoreSignalSemaphore(device, nextWrappedTexture);
|
||||
}
|
||||
|
||||
// Import a texture into |secondDevice|
|
||||
// Issue a copy of the imported texture inside |device| to |copyDstTexture|
|
||||
// Verify the clear color from |secondDevice| is visible in |copyDstTexture|
|
||||
TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
|
||||
// Import the image on |secondDevice|
|
||||
dawn::Texture wrappedTexture =
|
||||
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {});
|
||||
|
||||
// Clear |wrappedTexture| on |secondDevice|
|
||||
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
|
||||
|
||||
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
|
||||
wrappedTexture.Get());
|
||||
|
||||
// Import the image to |device|, making sure we wait on |signalFd|
|
||||
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
|
||||
dawn::Texture deviceWrappedTexture =
|
||||
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {signalFd});
|
||||
|
||||
// Create a second texture on |device|
|
||||
dawn::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
|
||||
|
||||
// Copy |deviceWrappedTexture| into |copyDstTexture|
|
||||
SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture);
|
||||
|
||||
// Verify |copyDstTexture| sees changes from |secondDevice|
|
||||
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0);
|
||||
|
||||
IgnoreSignalSemaphore(device, deviceWrappedTexture);
|
||||
}
|
||||
|
||||
// Import a texture into |device|
|
||||
// Copy color A into texture on |device|
|
||||
// Import same texture into |secondDevice|, waiting on the copy signal
|
||||
// Copy color B using Texture to Texture copy on |secondDevice|
|
||||
// Import texture back into |device|, waiting on color B signal
|
||||
// Verify texture contains color B
|
||||
// If texture destination isn't synchronized, |secondDevice| could copy color B
|
||||
// into the texture first, then |device| writes color A
|
||||
TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
|
||||
// Import the image on |device|
|
||||
dawn::Texture wrappedTexture = WrapVulkanImage(
|
||||
device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, {});
|
||||
|
||||
// Clear |wrappedTexture| on |device|
|
||||
ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
|
||||
|
||||
int signalFd =
|
||||
dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get());
|
||||
|
||||
// Import the image to |secondDevice|, making sure we wait on |signalFd|
|
||||
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
|
||||
dawn::Texture secondDeviceWrappedTexture =
|
||||
WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {signalFd});
|
||||
|
||||
// Create a texture with color B on |secondDevice|
|
||||
dawn::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor);
|
||||
ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
|
||||
|
||||
// Copy color B on |secondDevice|
|
||||
dawn::Queue secondDeviceQueue = secondDevice.CreateQueue();
|
||||
SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture,
|
||||
secondDeviceWrappedTexture);
|
||||
|
||||
// Re-import back into |device|, waiting on |secondDevice|'s signal
|
||||
signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
|
||||
secondDeviceWrappedTexture.Get());
|
||||
memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
|
||||
|
||||
dawn::Texture nextWrappedTexture =
|
||||
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {signalFd});
|
||||
|
||||
// Verify |nextWrappedTexture| contains the color from our copy
|
||||
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
|
||||
|
||||
IgnoreSignalSemaphore(device, nextWrappedTexture);
|
||||
}
|
||||
|
||||
// Import a texture from |secondDevice|
|
||||
// Issue a copy of the imported texture inside |device| to |copyDstBuffer|
|
||||
// Verify the clear color from |secondDevice| is visible in |copyDstBuffer|
|
||||
TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
|
||||
// Import the image on |secondDevice|
|
||||
dawn::Texture wrappedTexture =
|
||||
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {});
|
||||
|
||||
// Clear |wrappedTexture| on |secondDevice|
|
||||
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
|
||||
|
||||
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
|
||||
wrappedTexture.Get());
|
||||
|
||||
// Import the image to |device|, making sure we wait on |signalFd|
|
||||
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
|
||||
dawn::Texture deviceWrappedTexture =
|
||||
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {signalFd});
|
||||
|
||||
// Create a destination buffer on |device|
|
||||
dawn::BufferDescriptor bufferDesc;
|
||||
bufferDesc.size = 4;
|
||||
bufferDesc.usage = dawn::BufferUsageBit::CopyDst | dawn::BufferUsageBit::CopySrc;
|
||||
dawn::Buffer copyDstBuffer = device.CreateBuffer(&bufferDesc);
|
||||
|
||||
// Copy |deviceWrappedTexture| into |copyDstBuffer|
|
||||
dawn::TextureCopyView copySrc;
|
||||
copySrc.texture = deviceWrappedTexture;
|
||||
copySrc.mipLevel = 0;
|
||||
copySrc.arrayLayer = 0;
|
||||
copySrc.origin = {0, 0, 0};
|
||||
|
||||
dawn::BufferCopyView copyDst;
|
||||
copyDst.buffer = copyDstBuffer;
|
||||
copyDst.offset = 0;
|
||||
copyDst.rowPitch = 256;
|
||||
copyDst.imageHeight = 0;
|
||||
|
||||
dawn::Extent3D copySize = {1, 1, 1};
|
||||
|
||||
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size);
|
||||
dawn::CommandBuffer commands = encoder.Finish();
|
||||
queue.Submit(1, &commands);
|
||||
|
||||
// Verify |copyDstBuffer| sees changes from |secondDevice|
|
||||
uint32_t expected = 0x04030201;
|
||||
EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0);
|
||||
|
||||
IgnoreSignalSemaphore(device, deviceWrappedTexture);
|
||||
}
|
||||
|
||||
// Import a texture into |device|
|
||||
// Copy color A into texture on |device|
|
||||
// Import same texture into |secondDevice|, waiting on the copy signal
|
||||
// Copy color B using Buffer to Texture copy on |secondDevice|
|
||||
// Import texture back into |device|, waiting on color B signal
|
||||
// Verify texture contains color B
|
||||
// If texture destination isn't synchronized, |secondDevice| could copy color B
|
||||
// into the texture first, then |device| writes color A
|
||||
TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
|
||||
// Import the image on |device|
|
||||
dawn::Texture wrappedTexture = WrapVulkanImage(
|
||||
device, &defaultDescriptor, defaultFd, defaultAllocationSize, defaultMemoryTypeIndex, {});
|
||||
|
||||
// Clear |wrappedTexture| on |device|
|
||||
ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f});
|
||||
|
||||
int signalFd =
|
||||
dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get());
|
||||
|
||||
// Import the image to |secondDevice|, making sure we wait on |signalFd|
|
||||
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
|
||||
dawn::Texture secondDeviceWrappedTexture =
|
||||
WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {signalFd});
|
||||
|
||||
// Copy color B on |secondDevice|
|
||||
dawn::Queue secondDeviceQueue = secondDevice.CreateQueue();
|
||||
|
||||
// Create a buffer on |secondDevice|
|
||||
dawn::Buffer copySrcBuffer =
|
||||
utils::CreateBufferFromData(secondDevice, dawn::BufferUsageBit::CopySrc, {0x04030201});
|
||||
|
||||
// Copy |copySrcBuffer| into |secondDeviceWrappedTexture|
|
||||
dawn::BufferCopyView copySrc;
|
||||
copySrc.buffer = copySrcBuffer;
|
||||
copySrc.offset = 0;
|
||||
copySrc.rowPitch = 256;
|
||||
copySrc.imageHeight = 0;
|
||||
|
||||
dawn::TextureCopyView copyDst;
|
||||
copyDst.texture = secondDeviceWrappedTexture;
|
||||
copyDst.mipLevel = 0;
|
||||
copyDst.arrayLayer = 0;
|
||||
copyDst.origin = {0, 0, 0};
|
||||
|
||||
dawn::Extent3D copySize = {1, 1, 1};
|
||||
|
||||
dawn::CommandEncoder encoder = secondDevice.CreateCommandEncoder();
|
||||
encoder.CopyBufferToTexture(©Src, ©Dst, ©Size);
|
||||
dawn::CommandBuffer commands = encoder.Finish();
|
||||
secondDeviceQueue.Submit(1, &commands);
|
||||
|
||||
// Re-import back into |device|, waiting on |secondDevice|'s signal
|
||||
signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
|
||||
secondDeviceWrappedTexture.Get());
|
||||
memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
|
||||
|
||||
dawn::Texture nextWrappedTexture =
|
||||
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {signalFd});
|
||||
|
||||
// Verify |nextWrappedTexture| contains the color from our copy
|
||||
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0);
|
||||
|
||||
IgnoreSignalSemaphore(device, nextWrappedTexture);
|
||||
}
|
||||
|
||||
// Import a texture from |secondDevice|
|
||||
// Issue a copy of the imported texture inside |device| to |copyDstTexture|
|
||||
// Issue second copy to |secondCopyDstTexture|
|
||||
// Verify the clear color from |secondDevice| is visible in both copies
|
||||
TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
|
||||
// Import the image on |secondDevice|
|
||||
dawn::Texture wrappedTexture =
|
||||
WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {});
|
||||
|
||||
// Clear |wrappedTexture| on |secondDevice|
|
||||
ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
|
||||
|
||||
int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(),
|
||||
wrappedTexture.Get());
|
||||
|
||||
// Import the image to |device|, making sure we wait on |signalFd|
|
||||
int memoryFd = GetMemoryFd(deviceVk, defaultAllocation);
|
||||
dawn::Texture deviceWrappedTexture =
|
||||
WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize,
|
||||
defaultMemoryTypeIndex, {signalFd});
|
||||
|
||||
// Create a second texture on |device|
|
||||
dawn::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor);
|
||||
|
||||
// Create a third texture on |device|
|
||||
dawn::Texture secondCopyDstTexture = device.CreateTexture(&defaultDescriptor);
|
||||
|
||||
// Copy |deviceWrappedTexture| into |copyDstTexture|
|
||||
SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture);
|
||||
|
||||
// Copy |deviceWrappedTexture| into |secondCopyDstTexture|
|
||||
SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, secondCopyDstTexture);
|
||||
|
||||
// Verify |copyDstTexture| sees changes from |secondDevice|
|
||||
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0);
|
||||
|
||||
// Verify |secondCopyDstTexture| sees changes from |secondDevice|
|
||||
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0);
|
||||
|
||||
IgnoreSignalSemaphore(device, deviceWrappedTexture);
|
||||
}
|
||||
|
||||
// Tex A on device 3 (external export)
|
||||
// Tex B on device 2 (external export)
|
||||
// Tex C on device 1 (external export)
|
||||
// Clear color for A on device 3
|
||||
// Copy A->B on device 3
|
||||
// Copy B->C on device 2 (wait on B from previous op)
|
||||
// Copy C->D on device 1 (wait on C from previous op)
|
||||
// Verify D has same color as A
|
||||
TEST_P(VulkanImageWrappingUsageTests, ChainTextureCopy) {
|
||||
DAWN_SKIP_TEST_IF(UsesWire() || IsIntel());
|
||||
|
||||
// Close |defaultFd| since this test doesn't import it anywhere
|
||||
close(defaultFd);
|
||||
|
||||
// device 1 = |device|
|
||||
// device 2 = |secondDevice|
|
||||
// Create device 3
|
||||
dawn_native::vulkan::Device* thirdDeviceVk = reinterpret_cast<dawn_native::vulkan::Device*>(
|
||||
backendAdapter->CreateDevice(&deviceDescriptor));
|
||||
dawn::Device thirdDevice = dawn::Device::Acquire(reinterpret_cast<DawnDevice>(thirdDeviceVk));
|
||||
|
||||
// Make queue for device 2 and 3
|
||||
dawn::Queue secondDeviceQueue = secondDevice.CreateQueue();
|
||||
dawn::Queue thirdDeviceQueue = thirdDevice.CreateQueue();
|
||||
|
||||
// Allocate memory for A, B, C
|
||||
VkImage imageA;
|
||||
VkDeviceMemory allocationA;
|
||||
int memoryFdA;
|
||||
VkDeviceSize allocationSizeA;
|
||||
uint32_t memoryTypeIndexA;
|
||||
CreateBindExportImage(thirdDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageA, &allocationA,
|
||||
&allocationSizeA, &memoryTypeIndexA, &memoryFdA);
|
||||
|
||||
VkImage imageB;
|
||||
VkDeviceMemory allocationB;
|
||||
int memoryFdB;
|
||||
VkDeviceSize allocationSizeB;
|
||||
uint32_t memoryTypeIndexB;
|
||||
CreateBindExportImage(secondDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageB, &allocationB,
|
||||
&allocationSizeB, &memoryTypeIndexB, &memoryFdB);
|
||||
|
||||
VkImage imageC;
|
||||
VkDeviceMemory allocationC;
|
||||
int memoryFdC;
|
||||
VkDeviceSize allocationSizeC;
|
||||
uint32_t memoryTypeIndexC;
|
||||
CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageC, &allocationC,
|
||||
&allocationSizeC, &memoryTypeIndexC, &memoryFdC);
|
||||
|
||||
// Import TexA, TexB on device 3
|
||||
dawn::Texture wrappedTexADevice3 = WrapVulkanImage(thirdDevice, &defaultDescriptor, memoryFdA,
|
||||
allocationSizeA, memoryTypeIndexA, {});
|
||||
|
||||
dawn::Texture wrappedTexBDevice3 = WrapVulkanImage(thirdDevice, &defaultDescriptor, memoryFdB,
|
||||
allocationSizeB, memoryTypeIndexB, {});
|
||||
|
||||
// Clear TexA
|
||||
ClearImage(thirdDevice, wrappedTexADevice3, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f});
|
||||
|
||||
// Copy A->B
|
||||
SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3,
|
||||
wrappedTexBDevice3);
|
||||
|
||||
int signalFdTexBDevice3 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
|
||||
thirdDevice.Get(), wrappedTexBDevice3.Get());
|
||||
IgnoreSignalSemaphore(thirdDevice, wrappedTexADevice3);
|
||||
|
||||
// Import TexB, TexC on device 2
|
||||
memoryFdB = GetMemoryFd(secondDeviceVk, allocationB);
|
||||
dawn::Texture wrappedTexBDevice2 =
|
||||
WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFdB, allocationSizeB,
|
||||
memoryTypeIndexB, {signalFdTexBDevice3});
|
||||
|
||||
dawn::Texture wrappedTexCDevice2 = WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFdC,
|
||||
allocationSizeC, memoryTypeIndexC, {});
|
||||
|
||||
// Copy B->C on device 2
|
||||
SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2,
|
||||
wrappedTexCDevice2);
|
||||
|
||||
int signalFdTexCDevice2 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(
|
||||
secondDevice.Get(), wrappedTexCDevice2.Get());
|
||||
IgnoreSignalSemaphore(secondDevice, wrappedTexBDevice2);
|
||||
|
||||
// Import TexC on device 1
|
||||
memoryFdC = GetMemoryFd(deviceVk, allocationC);
|
||||
dawn::Texture wrappedTexCDevice1 =
|
||||
WrapVulkanImage(device, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC,
|
||||
{signalFdTexCDevice2});
|
||||
|
||||
// Create TexD on device 1
|
||||
dawn::Texture texD = device.CreateTexture(&defaultDescriptor);
|
||||
|
||||
// Copy C->D on device 1
|
||||
SimpleCopyTextureToTexture(device, queue, wrappedTexCDevice1, texD);
|
||||
|
||||
// Verify D matches clear color
|
||||
EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0);
|
||||
|
||||
thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA);
|
||||
thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA);
|
||||
secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageB);
|
||||
secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationB);
|
||||
deviceVk->GetFencedDeleter()->DeleteWhenUnused(imageC);
|
||||
deviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationC);
|
||||
|
||||
IgnoreSignalSemaphore(device, wrappedTexCDevice1);
|
||||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend);
|
||||
DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend);
|
Loading…
Reference in New Issue