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>
This commit is contained in:
Shaobo 2022-03-28 06:59:22 +00:00 committed by Dawn LUCI CQ
parent 6cacdc460a
commit 0a6c2b0f99
13 changed files with 358 additions and 77 deletions

View File

@ -43,6 +43,8 @@ namespace dawn::native::metal {
ExternalImageDescriptorIOSurface();
IOSurfaceRef ioSurface;
// This has been deprecated.
uint32_t plane;
};

View File

@ -358,6 +358,12 @@ namespace dawn::native::metal {
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]) {

View File

@ -54,8 +54,7 @@ namespace dawn::native::metal {
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;

View File

@ -432,21 +432,18 @@ namespace dawn::native::metal {
}
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;

View File

@ -37,8 +37,8 @@ namespace dawn::native::metal {
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());
}

View File

@ -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 @@ namespace dawn::native::metal {
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 @@ namespace dawn::native::metal {
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 @@ namespace dawn::native::metal {
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 @@ namespace dawn::native::metal {
TextureBase::ClearValue clearValue);
NSPRef<id<MTLTexture>> mMtlTexture;
MTLTextureUsage mMtlUsage;
CFRef<IOSurfaceRef> mIOSurface = nullptr;
};
class TextureView final : public TextureViewBase {

View File

@ -181,12 +181,25 @@ namespace dawn::native::metal {
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 @@ namespace dawn::native::metal {
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 @@ namespace dawn::native::metal {
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 @@ namespace dawn::native::metal {
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 @@ namespace dawn::native::metal {
MaybeError Texture::InitializeFromIOSurface(const ExternalImageDescriptor* descriptor,
const TextureDescriptor* textureDescriptor,
IOSurfaceRef ioSurface,
uint32_t plane) {
IOSurfaceRef ioSurface) {
mIOSurface = ioSurface;
// 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());
NSRef<MTLTextureDescriptor> mtlDesc = CreateMetalTextureDescriptor();
[*mtlDesc setStorageMode:kIOSurfaceStorageMode];
mMtlUsage = [*mtlDesc usage];
mMtlTexture = AcquireNSPRef([device->GetMTLDevice() newTextureWithDescriptor:mtlDesc.Get()
mMtlTexture =
AcquireNSPRef([device->GetMTLDevice() newTextureWithDescriptor:mtlDesc.Get()
iosurface:ioSurface
plane:plane]);
plane:0]);
}
SetIsSubresourceContentInitialized(descriptor->isInitialized, GetAllSubresources());
return {};
}
@ -569,12 +581,17 @@ namespace dawn::native::metal {
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 @@ namespace dawn::native::metal {
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) {

View File

@ -451,7 +451,10 @@ source_set("end2end_tests_sources") {
}
if (dawn_enable_metal) {
sources += [ "end2end/IOSurfaceWrappingTests.cpp" ]
sources += [
"end2end/IOSurfaceWrappingTests.cpp",
"end2end/VideoViewsTests_mac.cpp",
]
frameworks = [ "IOSurface.framework" ]
}
@ -468,7 +471,8 @@ source_set("end2end_tests_sources") {
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",

View File

@ -96,13 +96,11 @@ namespace {
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 @@ class IOSurfaceValidationTests : public IOSurfaceTestBase {
// 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 @@ TEST_P(IOSurfaceValidationTests, InvalidTextureDescriptor) {
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 @@ TEST_P(IOSurfaceValidationTests, InvalidTextureDimension) {
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 @@ TEST_P(IOSurfaceValidationTests, InvalidMipLevelCount) {
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 @@ TEST_P(IOSurfaceValidationTests, InvalidDepth) {
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 @@ TEST_P(IOSurfaceValidationTests, InvalidSampleCount) {
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 @@ TEST_P(IOSurfaceValidationTests, InvalidWidth) {
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 @@ TEST_P(IOSurfaceValidationTests, InvalidHeight) {
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 @@ TEST_P(IOSurfaceValidationTests, InvalidFormat) {
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 @@ class IOSurfaceUsageTests : public IOSurfaceTestBase {
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 @@ class IOSurfaceUsageTests : public IOSurfaceTestBase {
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 @@ TEST_P(IOSurfaceUsageTests, UninitializedTextureIsCleared) {
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);
}

View File

@ -117,6 +117,58 @@ std::vector<uint8_t> VideoViewsTests::GetTestTextureData(wgpu::TextureFormat for
}
}
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"(

View File

@ -28,7 +28,8 @@ class VideoViewsTestBackend {
virtual ~VideoViewsTestBackend();
virtual void OnSetUp(WGPUDevice device) = 0;
virtual void OnTearDown() = 0;
virtual void OnTearDown() {
}
class PlatformTexture {
public:
@ -74,6 +75,11 @@ class VideoViewsTests : public DawnTest {
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;

View File

@ -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>();
}

View File

@ -77,9 +77,6 @@ class VideoViewsTestBackendWin : public VideoViewsTestBackend {
mD3d11Device = std::move(d3d11Device);
}
void OnTearDown() override {
}
protected:
static DXGI_FORMAT GetDXGITextureFormat(wgpu::TextureFormat format) {
switch (format) {