diff --git a/include/dawn/native/MetalBackend.h b/include/dawn/native/MetalBackend.h index e8699bd02d..6db34a1c5d 100644 --- a/include/dawn/native/MetalBackend.h +++ b/include/dawn/native/MetalBackend.h @@ -43,6 +43,8 @@ namespace dawn::native::metal { 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 920bb1da44..1f75adb6f0 100644 --- a/src/dawn/native/metal/BackendMTL.mm +++ b/src/dawn/native/metal/BackendMTL.mm @@ -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]) { diff --git a/src/dawn/native/metal/DeviceMTL.h b/src/dawn/native/metal/DeviceMTL.h index d72cc3f3d0..a6b6592e73 100644 --- a/src/dawn/native/metal/DeviceMTL.h +++ b/src/dawn/native/metal/DeviceMTL.h @@ -54,8 +54,7 @@ namespace dawn::native::metal { MaybeError SubmitPendingCommandBuffer(); Ref CreateTextureWrappingIOSurface(const ExternalImageDescriptor* descriptor, - IOSurfaceRef ioSurface, - uint32_t plane); + IOSurfaceRef ioSurface); void WaitForCommandsToBeScheduled(); ResultOrError> CreateStagingBuffer(size_t size) override; diff --git a/src/dawn/native/metal/DeviceMTL.mm b/src/dawn/native/metal/DeviceMTL.mm index e654c8be59..e2e784e770 100644 --- a/src/dawn/native/metal/DeviceMTL.mm +++ b/src/dawn/native/metal/DeviceMTL.mm @@ -432,21 +432,18 @@ namespace dawn::native::metal { } Ref 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 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 c01cd40108..c0214e58f2 100644 --- a/src/dawn/native/metal/MetalBackend.mm +++ b/src/dawn/native/metal/MetalBackend.mm @@ -37,8 +37,8 @@ namespace dawn::native::metal { WGPUTexture WrapIOSurface(WGPUDevice device, const ExternalImageDescriptorIOSurface* cDescriptor) { Device* backendDevice = ToBackend(FromAPI(device)); - Ref texture = backendDevice->CreateTextureWrappingIOSurface( - cDescriptor, cDescriptor->ioSurface, cDescriptor->plane); + Ref 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 0ac8103c35..ba7f97bc2b 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 @@ 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> CreateFromIOSurface( Device* device, const ExternalImageDescriptor* descriptor, - IOSurfaceRef ioSurface, - uint32_t plane); + IOSurfaceRef ioSurface); static Ref CreateWrapping(Device* device, const TextureDescriptor* descriptor, NSPRef> wrapped); id GetMTLTexture(); + IOSurfaceRef GetIOSurface(); NSPRef> 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> wrapped); @@ -74,7 +73,9 @@ namespace dawn::native::metal { TextureBase::ClearValue clearValue); NSPRef> mMtlTexture; + MTLTextureUsage mMtlUsage; + CFRef mIOSurface = nullptr; }; class TextureView final : public TextureViewBase { diff --git a/src/dawn/native/metal/TextureMTL.mm b/src/dawn/native/metal/TextureMTL.mm index c47039fb27..da5755a691 100644 --- a/src/dawn/native/metal/TextureMTL.mm +++ b/src/dawn/native/metal/TextureMTL.mm @@ -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> Texture::CreateFromIOSurface( Device* device, const ExternalImageDescriptor* descriptor, - IOSurfaceRef ioSurface, - uint32_t plane) { + IOSurfaceRef ioSurface) { const TextureDescriptor* textureDescriptor = FromAPI(descriptor->cTextureDescriptor); Ref 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) { - Device* device = ToBackend(GetDevice()); + IOSurfaceRef ioSurface) { + mIOSurface = ioSurface; - NSRef 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 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 @@ namespace dawn::native::metal { void Texture::DestroyImpl() { TextureBase::DestroyImpl(); mMtlTexture = nullptr; + mIOSurface = nullptr; } id Texture::GetMTLTexture() { return mMtlTexture.Get(); } + IOSurfaceRef Texture::GetIOSurface() { + return mIOSurface.Get(); + } + NSPRef> 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 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 ae104a4930..990b5b995d 100644 --- a/src/dawn/tests/BUILD.gn +++ b/src/dawn/tests/BUILD.gn @@ -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", diff --git a/src/dawn/tests/end2end/IOSurfaceWrappingTests.cpp b/src/dawn/tests/end2end/IOSurfaceWrappingTests.cpp index 3bfc742f88..cfef5f146c 100644 --- a/src/dawn/tests/end2end/IOSurfaceWrappingTests.cpp +++ b/src/dawn/tests/end2end/IOSurfaceWrappingTests.cpp @@ -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(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); } diff --git a/src/dawn/tests/end2end/VideoViewsTests.cpp b/src/dawn/tests/end2end/VideoViewsTests.cpp index c79bc5b6b5..febfb5006f 100644 --- a/src/dawn/tests/end2end/VideoViewsTests.cpp +++ b/src/dawn/tests/end2end/VideoViewsTests.cpp @@ -117,6 +117,58 @@ std::vector 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 VideoViewsTests::GetTestTextureDataWithPlaneIndex(size_t planeIndex, + size_t bytesPerRow, + size_t height, + bool isCheckerboard) { + std::vector texelData = VideoViewsTests::GetTestTextureData( + wgpu::TextureFormat::R8BG8Biplanar420Unorm, isCheckerboard); + const uint32_t texelDataRowBytes = kYUVImageDataWidthInTexels; + const uint32_t texelDataHeight = + planeIndex == 0 ? kYUVImageDataHeightInTexels : kYUVImageDataHeightInTexels / 2; + + std::vector 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 6dcedb9fc0..60d93a04f5 100644 --- a/src/dawn/tests/end2end/VideoViewsTests.h +++ b/src/dawn/tests/end2end/VideoViewsTests.h @@ -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 GetTestTextureData(wgpu::TextureFormat format, bool isCheckerboard); + static uint32_t NumPlanes(wgpu::TextureFormat format); + static std::vector 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 0000000000..151c0b36c9 --- /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 +#include +#include + +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(iosurface); + } + ~PlatformTextureIOSurface() override { + mIOSurface = nullptr; + } + + bool CanWrapAsWGPUTexture() override { + return true; + } + + private: + CFRef 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 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 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(&textureDesc); + descriptor.isInitialized = true; + descriptor.ioSurface = surface; + + return std::make_unique( + wgpu::Texture::Acquire(dawn::native::metal::WrapIOSurface(mWGPUDevice, &descriptor)), + surface); + } + + void DestroyVideoTextureForTest( + std::unique_ptr&& platformTexture) override { + } + + WGPUDevice mWGPUDevice = nullptr; +}; + +// static +BackendTestConfig VideoViewsTestBackend::Backend() { + return MetalBackend(); +} + +// static +std::unique_ptr VideoViewsTestBackend::Create() { + return std::make_unique(); +} diff --git a/src/dawn/tests/end2end/VideoViewsTests_win.cpp b/src/dawn/tests/end2end/VideoViewsTests_win.cpp index f2f7ed507a..21889d832d 100644 --- a/src/dawn/tests/end2end/VideoViewsTests_win.cpp +++ b/src/dawn/tests/end2end/VideoViewsTests_win.cpp @@ -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) {