Wrap multiplanar iosurface in wgpuTexture
This CL supports wrapping multiplanar iosurface in wgpuTexture.
It also provides mechanism to create TextureView on each planes.
Bug:1307194
Change-Id: I5e82f47944fdea542abba097240c880628b1181f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/81482
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Shaobo Yan <shaobo.yan@intel.com>
diff --git a/include/dawn/native/MetalBackend.h b/include/dawn/native/MetalBackend.h
index e8699bd..6db34a1 100644
--- a/include/dawn/native/MetalBackend.h
+++ b/include/dawn/native/MetalBackend.h
@@ -43,6 +43,8 @@
ExternalImageDescriptorIOSurface();
IOSurfaceRef ioSurface;
+
+ // This has been deprecated.
uint32_t plane;
};
diff --git a/src/dawn/native/metal/BackendMTL.mm b/src/dawn/native/metal/BackendMTL.mm
index 920bb1d..1f75adb 100644
--- a/src/dawn/native/metal/BackendMTL.mm
+++ b/src/dawn/native/metal/BackendMTL.mm
@@ -358,6 +358,12 @@
mSupportedFeatures.EnableFeature(Feature::Depth32FloatStencil8);
}
+ // Uses newTextureWithDescriptor::iosurface::plane which is available
+ // on ios 11.0+ and macOS 11.0+
+ if (@available(macOS 10.11, iOS 11.0, *)) {
+ mSupportedFeatures.EnableFeature(Feature::MultiPlanarFormats);
+ }
+
#if defined(DAWN_PLATFORM_MACOS)
// MTLPixelFormatDepth24Unorm_Stencil8 is only available on macOS 10.11+
if ([*mDevice isDepth24Stencil8PixelFormatSupported]) {
diff --git a/src/dawn/native/metal/DeviceMTL.h b/src/dawn/native/metal/DeviceMTL.h
index d72cc3f..a6b6592 100644
--- a/src/dawn/native/metal/DeviceMTL.h
+++ b/src/dawn/native/metal/DeviceMTL.h
@@ -54,8 +54,7 @@
MaybeError SubmitPendingCommandBuffer();
Ref<Texture> CreateTextureWrappingIOSurface(const ExternalImageDescriptor* descriptor,
- IOSurfaceRef ioSurface,
- uint32_t plane);
+ IOSurfaceRef ioSurface);
void WaitForCommandsToBeScheduled();
ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override;
diff --git a/src/dawn/native/metal/DeviceMTL.mm b/src/dawn/native/metal/DeviceMTL.mm
index e654c8b..e2e784e 100644
--- a/src/dawn/native/metal/DeviceMTL.mm
+++ b/src/dawn/native/metal/DeviceMTL.mm
@@ -432,21 +432,18 @@
}
Ref<Texture> Device::CreateTextureWrappingIOSurface(const ExternalImageDescriptor* descriptor,
- IOSurfaceRef ioSurface,
- uint32_t plane) {
+ IOSurfaceRef ioSurface) {
const TextureDescriptor* textureDescriptor = FromAPI(descriptor->cTextureDescriptor);
if (ConsumedError(ValidateTextureDescriptor(this, textureDescriptor))) {
return nullptr;
}
- if (ConsumedError(
- ValidateIOSurfaceCanBeWrapped(this, textureDescriptor, ioSurface, plane))) {
+ if (ConsumedError(ValidateIOSurfaceCanBeWrapped(this, textureDescriptor, ioSurface))) {
return nullptr;
}
Ref<Texture> result;
- if (ConsumedError(Texture::CreateFromIOSurface(this, descriptor, ioSurface, plane),
- &result)) {
+ if (ConsumedError(Texture::CreateFromIOSurface(this, descriptor, ioSurface), &result)) {
return nullptr;
}
return result;
diff --git a/src/dawn/native/metal/MetalBackend.mm b/src/dawn/native/metal/MetalBackend.mm
index c01cd40..c0214e5 100644
--- a/src/dawn/native/metal/MetalBackend.mm
+++ b/src/dawn/native/metal/MetalBackend.mm
@@ -37,8 +37,8 @@
WGPUTexture WrapIOSurface(WGPUDevice device,
const ExternalImageDescriptorIOSurface* cDescriptor) {
Device* backendDevice = ToBackend(FromAPI(device));
- Ref<TextureBase> texture = backendDevice->CreateTextureWrappingIOSurface(
- cDescriptor, cDescriptor->ioSurface, cDescriptor->plane);
+ Ref<TextureBase> texture =
+ backendDevice->CreateTextureWrappingIOSurface(cDescriptor, cDescriptor->ioSurface);
return ToAPI(texture.Detach());
}
diff --git a/src/dawn/native/metal/TextureMTL.h b/src/dawn/native/metal/TextureMTL.h
index 0ac8103..ba7f97b 100644
--- a/src/dawn/native/metal/TextureMTL.h
+++ b/src/dawn/native/metal/TextureMTL.h
@@ -17,6 +17,7 @@
#include "dawn/native/Texture.h"
+#include "dawn/common/CoreFoundationRef.h"
#include "dawn/common/NSRef.h"
#include "dawn/native/DawnNative.h"
@@ -31,8 +32,7 @@
MTLPixelFormat MetalPixelFormat(wgpu::TextureFormat format);
MaybeError ValidateIOSurfaceCanBeWrapped(const DeviceBase* device,
const TextureDescriptor* descriptor,
- IOSurfaceRef ioSurface,
- uint32_t plane);
+ IOSurfaceRef ioSurface);
class Texture final : public TextureBase {
public:
@@ -41,13 +41,13 @@
static ResultOrError<Ref<Texture>> CreateFromIOSurface(
Device* device,
const ExternalImageDescriptor* descriptor,
- IOSurfaceRef ioSurface,
- uint32_t plane);
+ IOSurfaceRef ioSurface);
static Ref<Texture> CreateWrapping(Device* device,
const TextureDescriptor* descriptor,
NSPRef<id<MTLTexture>> wrapped);
id<MTLTexture> GetMTLTexture();
+ IOSurfaceRef GetIOSurface();
NSPRef<id<MTLTexture>> CreateFormatView(wgpu::TextureFormat format);
void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
@@ -62,8 +62,7 @@
MaybeError InitializeAsInternalTexture(const TextureDescriptor* descriptor);
MaybeError InitializeFromIOSurface(const ExternalImageDescriptor* descriptor,
const TextureDescriptor* textureDescriptor,
- IOSurfaceRef ioSurface,
- uint32_t plane);
+ IOSurfaceRef ioSurface);
void InitializeAsWrapping(const TextureDescriptor* descriptor,
NSPRef<id<MTLTexture>> wrapped);
@@ -74,7 +73,9 @@
TextureBase::ClearValue clearValue);
NSPRef<id<MTLTexture>> mMtlTexture;
+
MTLTextureUsage mMtlUsage;
+ CFRef<IOSurfaceRef> mIOSurface = nullptr;
};
class TextureView final : public TextureViewBase {
diff --git a/src/dawn/native/metal/TextureMTL.mm b/src/dawn/native/metal/TextureMTL.mm
index c47039f..da5755a 100644
--- a/src/dawn/native/metal/TextureMTL.mm
+++ b/src/dawn/native/metal/TextureMTL.mm
@@ -181,12 +181,25 @@
return wgpu::TextureFormat::RG8Unorm;
case kCVPixelFormatType_OneComponent8:
return wgpu::TextureFormat::R8Unorm;
+ case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
+ return wgpu::TextureFormat::R8BG8Biplanar420Unorm;
default:
return DAWN_FORMAT_VALIDATION_ERROR("Unsupported IOSurface format (%x).",
format);
}
}
+ uint32_t GetIOSurfacePlane(wgpu::TextureAspect aspect) {
+ switch (aspect) {
+ case wgpu::TextureAspect::Plane0Only:
+ return 0;
+ case wgpu::TextureAspect::Plane1Only:
+ return 1;
+ default:
+ UNREACHABLE();
+ }
+ }
+
#if defined(DAWN_PLATFORM_MACOS)
MTLStorageMode kIOSurfaceStorageMode = MTLStorageModeManaged;
#elif defined(DAWN_PLATFORM_IOS)
@@ -392,15 +405,7 @@
MaybeError ValidateIOSurfaceCanBeWrapped(const DeviceBase*,
const TextureDescriptor* descriptor,
- IOSurfaceRef ioSurface,
- uint32_t plane) {
- // IOSurfaceGetPlaneCount can return 0 for non-planar IOSurfaces but we will treat
- // non-planar like it is a single plane.
- size_t surfacePlaneCount = std::max(size_t(1), IOSurfaceGetPlaneCount(ioSurface));
- DAWN_INVALID_IF(plane >= surfacePlaneCount,
- "IOSurface plane (%u) exceeds the surface's plane count (%u).", plane,
- surfacePlaneCount);
-
+ IOSurfaceRef ioSurface) {
DAWN_INVALID_IF(descriptor->dimension != wgpu::TextureDimension::e2D,
"Texture dimension (%s) is not %s.", descriptor->dimension,
wgpu::TextureDimension::e2D);
@@ -414,8 +419,8 @@
DAWN_INVALID_IF(descriptor->sampleCount != 1, "Sample count (%u) is not 1.",
descriptor->sampleCount);
- uint32_t surfaceWidth = IOSurfaceGetWidthOfPlane(ioSurface, plane);
- uint32_t surfaceHeight = IOSurfaceGetHeightOfPlane(ioSurface, plane);
+ uint32_t surfaceWidth = IOSurfaceGetWidth(ioSurface);
+ uint32_t surfaceHeight = IOSurfaceGetHeight(ioSurface);
DAWN_INVALID_IF(
descriptor->size.width != surfaceWidth || descriptor->size.height != surfaceHeight ||
@@ -497,13 +502,12 @@
ResultOrError<Ref<Texture>> Texture::CreateFromIOSurface(
Device* device,
const ExternalImageDescriptor* descriptor,
- IOSurfaceRef ioSurface,
- uint32_t plane) {
+ IOSurfaceRef ioSurface) {
const TextureDescriptor* textureDescriptor = FromAPI(descriptor->cTextureDescriptor);
Ref<Texture> texture =
- AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedInternal));
- DAWN_TRY(texture->InitializeFromIOSurface(descriptor, textureDescriptor, ioSurface, plane));
+ AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedExternal));
+ DAWN_TRY(texture->InitializeFromIOSurface(descriptor, textureDescriptor, ioSurface));
return texture;
}
@@ -546,20 +550,28 @@
MaybeError Texture::InitializeFromIOSurface(const ExternalImageDescriptor* descriptor,
const TextureDescriptor* textureDescriptor,
- IOSurfaceRef ioSurface,
- uint32_t plane) {
- Device* device = ToBackend(GetDevice());
+ IOSurfaceRef ioSurface) {
+ mIOSurface = ioSurface;
- NSRef<MTLTextureDescriptor> mtlDesc = CreateMetalTextureDescriptor();
- [*mtlDesc setStorageMode:kIOSurfaceStorageMode];
+ // Uses WGPUTexture which wraps multiplanar ioSurface needs to create
+ // texture view explicitly. Wrap the ioSurface and delay to extract
+ // MTLTexture from the plane of it when creating texture view.
+ // WGPUTexture which wraps non-multplanar ioSurface needs to support
+ // ops that doesn't require creating texture view(e.g. copy). Extract
+ // MTLTexture from such ioSurface to support this.
+ if (!GetFormat().IsMultiPlanar()) {
+ Device* device = ToBackend(GetDevice());
- mMtlUsage = [*mtlDesc usage];
- mMtlTexture = AcquireNSPRef([device->GetMTLDevice() newTextureWithDescriptor:mtlDesc.Get()
- iosurface:ioSurface
- plane:plane]);
+ NSRef<MTLTextureDescriptor> mtlDesc = CreateMetalTextureDescriptor();
+ [*mtlDesc setStorageMode:kIOSurfaceStorageMode];
+ mMtlUsage = [*mtlDesc usage];
+ mMtlTexture =
+ AcquireNSPRef([device->GetMTLDevice() newTextureWithDescriptor:mtlDesc.Get()
+ iosurface:ioSurface
+ plane:0]);
+ }
SetIsSubresourceContentInitialized(descriptor->isInitialized, GetAllSubresources());
-
return {};
}
@@ -569,12 +581,17 @@
void Texture::DestroyImpl() {
TextureBase::DestroyImpl();
mMtlTexture = nullptr;
+ mIOSurface = nullptr;
}
id<MTLTexture> Texture::GetMTLTexture() {
return mMtlTexture.Get();
}
+ IOSurfaceRef Texture::GetIOSurface() {
+ return mIOSurface.Get();
+ }
+
NSPRef<id<MTLTexture>> Texture::CreateFormatView(wgpu::TextureFormat format) {
if (GetFormat().format == format) {
return mMtlTexture;
@@ -821,6 +838,37 @@
mMtlTextureView = nullptr;
} else if (!RequiresCreatingNewTextureView(texture, descriptor)) {
mMtlTextureView = mtlTexture;
+ } else if (texture->GetFormat().IsMultiPlanar()) {
+ NSRef<MTLTextureDescriptor> mtlDescRef = AcquireNSRef([MTLTextureDescriptor new]);
+ MTLTextureDescriptor* mtlDesc = mtlDescRef.Get();
+
+ mtlDesc.sampleCount = texture->GetSampleCount();
+ mtlDesc.usage = MetalTextureUsage(texture->GetFormat(), texture->GetInternalUsage(),
+ texture->GetSampleCount());
+ mtlDesc.pixelFormat = MetalPixelFormat(descriptor->format);
+ mtlDesc.mipmapLevelCount = texture->GetNumMipLevels();
+ mtlDesc.storageMode = kIOSurfaceStorageMode;
+
+ uint32_t plane = GetIOSurfacePlane(descriptor->aspect);
+ mtlDesc.width = IOSurfaceGetWidthOfPlane(texture->GetIOSurface(), plane);
+ mtlDesc.height = IOSurfaceGetHeightOfPlane(texture->GetIOSurface(), plane);
+
+ // Multiplanar texture is validated to only have single layer, single mipLevel
+ // and 2d textures (depth == 1)
+ ASSERT(texture->GetArrayLayers() == 1 &&
+ texture->GetDimension() == wgpu::TextureDimension::e2D &&
+ texture->GetNumMipLevels() == 1);
+ mtlDesc.arrayLength = 1;
+ mtlDesc.depth = 1;
+
+ mMtlTextureView = AcquireNSPRef([ToBackend(GetDevice())->GetMTLDevice()
+ newTextureWithDescriptor:mtlDesc
+ iosurface:texture->GetIOSurface()
+ plane:plane]);
+ if (mMtlTextureView == nil) {
+ return DAWN_INTERNAL_ERROR(
+ "Failed to create MTLTexture view for external texture.");
+ }
} else {
MTLPixelFormat format = MetalPixelFormat(descriptor->format);
if (descriptor->aspect == wgpu::TextureAspect::StencilOnly) {
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index ae104a4..990b5b9 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -451,7 +451,10 @@
}
if (dawn_enable_metal) {
- sources += [ "end2end/IOSurfaceWrappingTests.cpp" ]
+ sources += [
+ "end2end/IOSurfaceWrappingTests.cpp",
+ "end2end/VideoViewsTests_mac.cpp",
+ ]
frameworks = [ "IOSurface.framework" ]
}
@@ -468,7 +471,8 @@
deps += [ "${dawn_root}/src/dawn/utils:glfw" ]
}
- if (dawn_enable_d3d12 || (dawn_enable_vulkan && is_chromeos)) {
+ if (dawn_enable_d3d12 || (dawn_enable_vulkan && is_chromeos) ||
+ dawn_enable_metal) {
sources += [
"end2end/VideoViewsTests.cpp",
"end2end/VideoViewsTests.h",
diff --git a/src/dawn/tests/end2end/IOSurfaceWrappingTests.cpp b/src/dawn/tests/end2end/IOSurfaceWrappingTests.cpp
index 3bfc742..cfef5f1 100644
--- a/src/dawn/tests/end2end/IOSurfaceWrappingTests.cpp
+++ b/src/dawn/tests/end2end/IOSurfaceWrappingTests.cpp
@@ -96,13 +96,11 @@
public:
wgpu::Texture WrapIOSurface(const wgpu::TextureDescriptor* descriptor,
IOSurfaceRef ioSurface,
- uint32_t plane,
bool isInitialized = true) {
dawn::native::metal::ExternalImageDescriptorIOSurface externDesc;
externDesc.cTextureDescriptor =
reinterpret_cast<const WGPUTextureDescriptor*>(descriptor);
externDesc.ioSurface = ioSurface;
- externDesc.plane = plane;
externDesc.isInitialized = isInitialized;
WGPUTexture texture = dawn::native::metal::WrapIOSurface(device.Get(), &externDesc);
return wgpu::Texture::Acquire(texture);
@@ -134,7 +132,7 @@
// Test a successful wrapping of an IOSurface in a texture
TEST_P(IOSurfaceValidationTests, Success) {
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
- wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 0);
+ wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get());
ASSERT_NE(texture.Get(), nullptr);
}
@@ -145,16 +143,7 @@
wgpu::ChainedStruct chainedDescriptor;
descriptor.nextInChain = &chainedDescriptor;
- ASSERT_DEVICE_ERROR(wgpu::Texture texture =
- WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
- ASSERT_EQ(texture.Get(), nullptr);
-}
-
-// Test an error occurs if the plane is too large
-TEST_P(IOSurfaceValidationTests, PlaneTooLarge) {
- DAWN_TEST_UNSUPPORTED_IF(UsesWire());
- ASSERT_DEVICE_ERROR(wgpu::Texture texture =
- WrapIOSurface(&descriptor, defaultIOSurface.get(), 1));
+ ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get()));
ASSERT_EQ(texture.Get(), nullptr);
}
@@ -164,8 +153,7 @@
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
descriptor.dimension = wgpu::TextureDimension::e3D;
- ASSERT_DEVICE_ERROR(wgpu::Texture texture =
- WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
+ ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get()));
ASSERT_EQ(texture.Get(), nullptr);
}
@@ -174,8 +162,7 @@
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
descriptor.mipLevelCount = 2;
- ASSERT_DEVICE_ERROR(wgpu::Texture texture =
- WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
+ ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get()));
ASSERT_EQ(texture.Get(), nullptr);
}
@@ -184,8 +171,7 @@
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
descriptor.size.depthOrArrayLayers = 2;
- ASSERT_DEVICE_ERROR(wgpu::Texture texture =
- WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
+ ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get()));
ASSERT_EQ(texture.Get(), nullptr);
}
@@ -194,8 +180,7 @@
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
descriptor.sampleCount = 4;
- ASSERT_DEVICE_ERROR(wgpu::Texture texture =
- WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
+ ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get()));
ASSERT_EQ(texture.Get(), nullptr);
}
@@ -204,8 +189,7 @@
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
descriptor.size.width = 11;
- ASSERT_DEVICE_ERROR(wgpu::Texture texture =
- WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
+ ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get()));
ASSERT_EQ(texture.Get(), nullptr);
}
@@ -214,8 +198,7 @@
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
descriptor.size.height = 11;
- ASSERT_DEVICE_ERROR(wgpu::Texture texture =
- WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
+ ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get()));
ASSERT_EQ(texture.Get(), nullptr);
}
@@ -224,8 +207,7 @@
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
descriptor.format = wgpu::TextureFormat::R8Unorm;
- ASSERT_DEVICE_ERROR(wgpu::Texture texture =
- WrapIOSurface(&descriptor, defaultIOSurface.get(), 0));
+ ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get()));
ASSERT_EQ(texture.Get(), nullptr);
}
@@ -305,7 +287,7 @@
textureDescriptor.sampleCount = 1;
textureDescriptor.mipLevelCount = 1;
textureDescriptor.usage = wgpu::TextureUsage::TextureBinding;
- wgpu::Texture wrappingTexture = WrapIOSurface(&textureDescriptor, ioSurface, 0);
+ wgpu::Texture wrappingTexture = WrapIOSurface(&textureDescriptor, ioSurface);
wgpu::TextureView textureView = wrappingTexture.CreateView();
@@ -345,7 +327,7 @@
textureDescriptor.sampleCount = 1;
textureDescriptor.mipLevelCount = 1;
textureDescriptor.usage = wgpu::TextureUsage::RenderAttachment;
- wgpu::Texture ioSurfaceTexture = WrapIOSurface(&textureDescriptor, ioSurface, 0);
+ wgpu::Texture ioSurfaceTexture = WrapIOSurface(&textureDescriptor, ioSurface);
wgpu::TextureView ioSurfaceView = ioSurfaceTexture.CreateView();
@@ -471,7 +453,7 @@
textureDescriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
// wrap ioSurface and ensure color is not visible when isInitialized set to false
- wgpu::Texture ioSurfaceTexture = WrapIOSurface(&textureDescriptor, ioSurface.get(), 0, false);
+ wgpu::Texture ioSurfaceTexture = WrapIOSurface(&textureDescriptor, ioSurface.get(), false);
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), ioSurfaceTexture, 0, 0);
}
diff --git a/src/dawn/tests/end2end/VideoViewsTests.cpp b/src/dawn/tests/end2end/VideoViewsTests.cpp
index c79bc5b..febfb50 100644
--- a/src/dawn/tests/end2end/VideoViewsTests.cpp
+++ b/src/dawn/tests/end2end/VideoViewsTests.cpp
@@ -117,6 +117,58 @@
}
}
+uint32_t VideoViewsTests::NumPlanes(wgpu::TextureFormat format) {
+ switch (format) {
+ case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
+ return 2;
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+}
+std::vector<uint8_t> VideoViewsTests::GetTestTextureDataWithPlaneIndex(size_t planeIndex,
+ size_t bytesPerRow,
+ size_t height,
+ bool isCheckerboard) {
+ std::vector<uint8_t> texelData = VideoViewsTests::GetTestTextureData(
+ wgpu::TextureFormat::R8BG8Biplanar420Unorm, isCheckerboard);
+ const uint32_t texelDataRowBytes = kYUVImageDataWidthInTexels;
+ const uint32_t texelDataHeight =
+ planeIndex == 0 ? kYUVImageDataHeightInTexels : kYUVImageDataHeightInTexels / 2;
+
+ std::vector<uint8_t> texels(bytesPerRow * height, 0);
+ uint32_t plane_first_texel_offset = 0;
+ // The size of the test video frame is 4 x 4
+ switch (planeIndex) {
+ case VideoViewsTests::kYUVLumaPlaneIndex:
+ for (uint32_t i = 0; i < texelDataHeight; ++i) {
+ if (i < texelDataHeight) {
+ for (uint32_t j = 0; j < texelDataRowBytes; ++j) {
+ texels[bytesPerRow * i + j] =
+ texelData[texelDataRowBytes * i + j + plane_first_texel_offset];
+ }
+ }
+ }
+ return texels;
+ case VideoViewsTests::kYUVChromaPlaneIndex:
+ // TexelData is 4 * 6 size, first 4 * 4 is Y plane, UV plane started
+ // at index 16.
+ plane_first_texel_offset = 16;
+ for (uint32_t i = 0; i < texelDataHeight; ++i) {
+ if (i < texelDataHeight) {
+ for (uint32_t j = 0; j < texelDataRowBytes; ++j) {
+ texels[bytesPerRow * i + j] =
+ texelData[texelDataRowBytes * i + j + plane_first_texel_offset];
+ }
+ }
+ }
+ return texels;
+ default:
+ UNREACHABLE();
+ return {};
+ }
+}
+
// Vertex shader used to render a sampled texture into a quad.
wgpu::ShaderModule VideoViewsTests::GetTestVertexShaderModule() const {
return utils::CreateShaderModule(device, R"(
diff --git a/src/dawn/tests/end2end/VideoViewsTests.h b/src/dawn/tests/end2end/VideoViewsTests.h
index 6dcedb9..60d93a0 100644
--- a/src/dawn/tests/end2end/VideoViewsTests.h
+++ b/src/dawn/tests/end2end/VideoViewsTests.h
@@ -28,7 +28,8 @@
virtual ~VideoViewsTestBackend();
virtual void OnSetUp(WGPUDevice device) = 0;
- virtual void OnTearDown() = 0;
+ virtual void OnTearDown() {
+ }
class PlatformTexture {
public:
@@ -74,6 +75,11 @@
RGBA8{90, 240, 0, 0xFF}}; // UV
static std::vector<uint8_t> GetTestTextureData(wgpu::TextureFormat format, bool isCheckerboard);
+ static uint32_t NumPlanes(wgpu::TextureFormat format);
+ static std::vector<uint8_t> GetTestTextureDataWithPlaneIndex(size_t planeIndex,
+ size_t bytesPerRow,
+ size_t height,
+ bool isCheckerboard);
protected:
void SetUp() override;
diff --git a/src/dawn/tests/end2end/VideoViewsTests_mac.cpp b/src/dawn/tests/end2end/VideoViewsTests_mac.cpp
new file mode 100644
index 0000000..151c0b3
--- /dev/null
+++ b/src/dawn/tests/end2end/VideoViewsTests_mac.cpp
@@ -0,0 +1,187 @@
+// Copyright 2022 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 "VideoViewsTests.h"
+
+#include "dawn/common/Assert.h"
+#include "dawn/common/CoreFoundationRef.h"
+#include "dawn/native/MetalBackend.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreVideo/CVPixelBuffer.h>
+#include <IOSurface/IOSurfaceRef.h>
+
+namespace {
+ void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef key, int32_t value) {
+ CFNumberRef number(CFNumberCreate(nullptr, kCFNumberSInt32Type, &value));
+ CFDictionaryAddValue(dictionary, key, number);
+ CFRelease(number);
+ }
+
+} // anonymous namespace
+
+class PlatformTextureIOSurface : public VideoViewsTestBackend::PlatformTexture {
+ public:
+ PlatformTextureIOSurface(wgpu::Texture&& texture, IOSurfaceRef iosurface)
+ : PlatformTexture(std::move(texture)) {
+ mIOSurface = AcquireCFRef<IOSurfaceRef>(iosurface);
+ }
+ ~PlatformTextureIOSurface() override {
+ mIOSurface = nullptr;
+ }
+
+ bool CanWrapAsWGPUTexture() override {
+ return true;
+ }
+
+ private:
+ CFRef<IOSurfaceRef> mIOSurface = nullptr;
+};
+
+class VideoViewsTestBackendIOSurface : public VideoViewsTestBackend {
+ public:
+ void OnSetUp(WGPUDevice device) override {
+ mWGPUDevice = device;
+ }
+
+ private:
+ OSType ToCVFormat(wgpu::TextureFormat format) {
+ switch (format) {
+ case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
+ return kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+ }
+
+ size_t GetSubSamplingFactorPerPlane(wgpu::TextureFormat format, size_t plane) {
+ switch (format) {
+ case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
+ return plane == VideoViewsTests::kYUVLumaPlaneIndex ? 1 : 2;
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+ }
+
+ size_t BytesPerElement(wgpu::TextureFormat format, size_t plane) {
+ switch (format) {
+ case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
+ return plane == VideoViewsTests::kYUVLumaPlaneIndex ? 1 : 2;
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+ }
+
+ std::unique_ptr<VideoViewsTestBackend::PlatformTexture> CreateVideoTextureForTest(
+ wgpu::TextureFormat format,
+ wgpu::TextureUsage usage,
+ bool isCheckerboard) override {
+ CFMutableDictionaryRef dict(CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ AddIntegerValue(dict, kIOSurfaceWidth, VideoViewsTests::kYUVImageDataWidthInTexels);
+ AddIntegerValue(dict, kIOSurfaceHeight, VideoViewsTests::kYUVImageDataHeightInTexels);
+ AddIntegerValue(dict, kIOSurfacePixelFormat, ToCVFormat(format));
+
+ size_t num_planes = VideoViewsTests::NumPlanes(format);
+
+ CFMutableArrayRef planes(
+ CFArrayCreateMutable(kCFAllocatorDefault, num_planes, &kCFTypeArrayCallBacks));
+ size_t total_bytes_alloc = 0;
+ for (size_t plane = 0; plane < num_planes; ++plane) {
+ const size_t factor = GetSubSamplingFactorPerPlane(format, plane);
+ const size_t plane_width = VideoViewsTests::kYUVImageDataWidthInTexels / factor;
+ const size_t plane_height = VideoViewsTests::kYUVImageDataHeightInTexels / factor;
+ const size_t plane_bytes_per_element = BytesPerElement(format, plane);
+ const size_t plane_bytes_per_row = IOSurfaceAlignProperty(
+ kIOSurfacePlaneBytesPerRow, plane_width * plane_bytes_per_element);
+ const size_t plane_bytes_alloc =
+ IOSurfaceAlignProperty(kIOSurfacePlaneSize, plane_height * plane_bytes_per_row);
+ const size_t plane_offset =
+ IOSurfaceAlignProperty(kIOSurfacePlaneOffset, total_bytes_alloc);
+
+ CFMutableDictionaryRef plane_info(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ AddIntegerValue(plane_info, kIOSurfacePlaneWidth, plane_width);
+ AddIntegerValue(plane_info, kIOSurfacePlaneHeight, plane_height);
+ AddIntegerValue(plane_info, kIOSurfacePlaneBytesPerElement, plane_bytes_per_element);
+ AddIntegerValue(plane_info, kIOSurfacePlaneBytesPerRow, plane_bytes_per_row);
+ AddIntegerValue(plane_info, kIOSurfacePlaneSize, plane_bytes_alloc);
+ AddIntegerValue(plane_info, kIOSurfacePlaneOffset, plane_offset);
+ CFArrayAppendValue(planes, plane_info);
+ CFRelease(plane_info);
+ total_bytes_alloc = plane_offset + plane_bytes_alloc;
+ }
+ CFDictionaryAddValue(dict, kIOSurfacePlaneInfo, planes);
+ CFRelease(planes);
+
+ total_bytes_alloc = IOSurfaceAlignProperty(kIOSurfaceAllocSize, total_bytes_alloc);
+ AddIntegerValue(dict, kIOSurfaceAllocSize, total_bytes_alloc);
+
+ IOSurfaceRef surface = IOSurfaceCreate(dict);
+ CFRelease(dict);
+
+ IOSurfaceLock(surface, 0, nullptr);
+ for (size_t plane = 0; plane < num_planes; ++plane) {
+ std::vector<uint8_t> data = VideoViewsTests::GetTestTextureDataWithPlaneIndex(
+ plane, IOSurfaceGetBytesPerRowOfPlane(surface, plane),
+ IOSurfaceGetHeightOfPlane(surface, plane), isCheckerboard);
+ void* pointer = IOSurfaceGetBaseAddressOfPlane(surface, plane);
+ memcpy(pointer, data.data(), data.size());
+ }
+ IOSurfaceUnlock(surface, 0, nullptr);
+
+ wgpu::TextureDescriptor textureDesc;
+ textureDesc.format = format;
+ textureDesc.dimension = wgpu::TextureDimension::e2D;
+ textureDesc.usage = usage;
+ textureDesc.size = {VideoViewsTests::kYUVImageDataWidthInTexels,
+ VideoViewsTests::kYUVImageDataHeightInTexels, 1};
+
+ wgpu::DawnTextureInternalUsageDescriptor internalDesc;
+ internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;
+ textureDesc.nextInChain = &internalDesc;
+
+ dawn::native::metal::ExternalImageDescriptorIOSurface descriptor = {};
+ descriptor.cTextureDescriptor =
+ reinterpret_cast<const WGPUTextureDescriptor*>(&textureDesc);
+ descriptor.isInitialized = true;
+ descriptor.ioSurface = surface;
+
+ return std::make_unique<PlatformTextureIOSurface>(
+ wgpu::Texture::Acquire(dawn::native::metal::WrapIOSurface(mWGPUDevice, &descriptor)),
+ surface);
+ }
+
+ void DestroyVideoTextureForTest(
+ std::unique_ptr<VideoViewsTestBackend::PlatformTexture>&& platformTexture) override {
+ }
+
+ WGPUDevice mWGPUDevice = nullptr;
+};
+
+// static
+BackendTestConfig VideoViewsTestBackend::Backend() {
+ return MetalBackend();
+}
+
+// static
+std::unique_ptr<VideoViewsTestBackend> VideoViewsTestBackend::Create() {
+ return std::make_unique<VideoViewsTestBackendIOSurface>();
+}
diff --git a/src/dawn/tests/end2end/VideoViewsTests_win.cpp b/src/dawn/tests/end2end/VideoViewsTests_win.cpp
index f2f7ed5..21889d8 100644
--- a/src/dawn/tests/end2end/VideoViewsTests_win.cpp
+++ b/src/dawn/tests/end2end/VideoViewsTests_win.cpp
@@ -77,9 +77,6 @@
mD3d11Device = std::move(d3d11Device);
}
- void OnTearDown() override {
- }
-
protected:
static DXGI_FORMAT GetDXGITextureFormat(wgpu::TextureFormat format) {
switch (format) {