blob: c5e61d6e5d3b4a10b6bad6793ebce97daf70158b [file] [log] [blame]
// Copyright 2023 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 <CoreFoundation/CoreFoundation.h>
#include <CoreVideo/CVPixelBuffer.h>
#include <IOSurface/IOSurface.h>
#import <Metal/Metal.h>
#include "dawn/common/CoreFoundationRef.h"
#include "dawn/common/NSRef.h"
#include "dawn/tests/end2end/SharedTextureMemoryTests.h"
#include "dawn/webgpu_cpp.h"
namespace dawn {
namespace {
void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef key, int32_t value) {
auto number = AcquireCFRef(CFNumberCreate(nullptr, kCFNumberSInt32Type, &value));
CFDictionaryAddValue(dictionary, key, number.Get());
}
class Backend : public SharedTextureMemoryTestBackend {
public:
static Backend* GetInstance() {
static Backend b;
return &b;
}
std::string Name() const override { return "IOSurface"; }
std::vector<wgpu::FeatureName> RequiredFeatures() const override {
return {wgpu::FeatureName::SharedTextureMemoryIOSurface,
wgpu::FeatureName::SharedFenceMTLSharedEvent,
wgpu::FeatureName::DawnMultiPlanarFormats};
}
// Create one basic shared texture memory. It should support most operations.
wgpu::SharedTextureMemory CreateSharedTextureMemory(wgpu::Device& device) override {
auto dict = AcquireCFRef(CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
AddIntegerValue(dict.Get(), kIOSurfaceWidth, 16);
AddIntegerValue(dict.Get(), kIOSurfaceHeight, 16);
AddIntegerValue(dict.Get(), kIOSurfacePixelFormat, kCVPixelFormatType_32RGBA);
AddIntegerValue(dict.Get(), kIOSurfaceBytesPerElement, 4);
wgpu::SharedTextureMemoryIOSurfaceDescriptor ioSurfaceDesc;
ioSurfaceDesc.ioSurface = IOSurfaceCreate(dict.Get());
wgpu::SharedTextureMemoryDescriptor desc;
desc.nextInChain = &ioSurfaceDesc;
return device.ImportSharedTextureMemory(&desc);
}
std::vector<std::vector<wgpu::SharedTextureMemory>> CreatePerDeviceSharedTextureMemories(
const std::vector<wgpu::Device>& devices) override {
std::vector<std::vector<wgpu::SharedTextureMemory>> memories;
for (auto [format, bytesPerElement] : {
std::make_pair(kCVPixelFormatType_64RGBAHalf, 8),
std::make_pair(kCVPixelFormatType_TwoComponent16Half, 4),
std::make_pair(kCVPixelFormatType_OneComponent16Half, 2),
std::make_pair(kCVPixelFormatType_ARGB2101010LEPacked, 4),
std::make_pair(kCVPixelFormatType_32RGBA, 4),
std::make_pair(kCVPixelFormatType_32BGRA, 4),
std::make_pair(kCVPixelFormatType_TwoComponent8, 2),
std::make_pair(kCVPixelFormatType_OneComponent8, 1),
std::make_pair(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, 4),
}) {
for (uint32_t size : {4, 64}) {
auto dict = AcquireCFRef(CFDictionaryCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
AddIntegerValue(dict.Get(), kIOSurfaceWidth, size);
AddIntegerValue(dict.Get(), kIOSurfaceHeight, size);
AddIntegerValue(dict.Get(), kIOSurfacePixelFormat, format);
AddIntegerValue(dict.Get(), kIOSurfaceBytesPerElement, bytesPerElement);
wgpu::SharedTextureMemoryIOSurfaceDescriptor ioSurfaceDesc;
ioSurfaceDesc.ioSurface = IOSurfaceCreate(dict.Get());
// Internally, the CV enums are defined as their fourcc values. Cast to that and use
// it as the label. The fourcc value is a four-character name that can be
// interpreted as a 32-bit integer enum ('ABGR', 'r011', etc.)
std::string label = std::string(reinterpret_cast<char*>(&format), 4) + " " +
std::to_string(size) + "x" + std::to_string(size);
wgpu::SharedTextureMemoryDescriptor desc;
desc.label = label.c_str();
desc.nextInChain = &ioSurfaceDesc;
std::vector<wgpu::SharedTextureMemory> perDeviceMemories;
for (auto& device : devices) {
perDeviceMemories.push_back(device.ImportSharedTextureMemory(&desc));
}
memories.push_back(std::move(perDeviceMemories));
}
}
return memories;
}
wgpu::SharedFence ImportFenceTo(const wgpu::Device& importingDevice,
const wgpu::SharedFence& fence) override {
wgpu::SharedFenceMTLSharedEventExportInfo sharedEventInfo;
wgpu::SharedFenceExportInfo exportInfo;
exportInfo.nextInChain = &sharedEventInfo;
fence.ExportInfo(&exportInfo);
wgpu::SharedFenceMTLSharedEventDescriptor sharedEventDesc;
sharedEventDesc.sharedEvent = sharedEventInfo.sharedEvent;
wgpu::SharedFenceDescriptor fenceDesc;
fenceDesc.nextInChain = &sharedEventDesc;
return importingDevice.ImportSharedFence(&fenceDesc);
}
};
// Test that a shared event can be imported, and then exported.
TEST_P(SharedTextureMemoryTests, SharedFenceSuccessfulImportExport) {
if (@available(macOS 10.14, iOS 12.0, *)) {
auto mtlDevice = AcquireNSPRef(MTLCreateSystemDefaultDevice());
auto sharedEvent = AcquireNSPRef([*mtlDevice newSharedEvent]);
wgpu::SharedFenceMTLSharedEventDescriptor sharedEventDesc;
sharedEventDesc.sharedEvent = static_cast<void*>(*sharedEvent);
wgpu::SharedFenceDescriptor fenceDesc;
fenceDesc.nextInChain = &sharedEventDesc;
wgpu::SharedFence fence = device.ImportSharedFence(&fenceDesc);
// Release the Metal objects. They should be retained by the implementation.
mtlDevice = nil;
sharedEvent = nil;
wgpu::SharedFenceMTLSharedEventExportInfo sharedEventInfo;
wgpu::SharedFenceExportInfo exportInfo;
exportInfo.nextInChain = &sharedEventInfo;
fence.ExportInfo(&exportInfo);
// The exported event should be the same as the imported one.
EXPECT_EQ(sharedEventInfo.sharedEvent, sharedEventDesc.sharedEvent);
EXPECT_EQ(exportInfo.type, wgpu::SharedFenceType::MTLSharedEvent);
}
}
// Test that it is an error to import a shared fence without enabling the feature.
TEST_P(SharedTextureMemoryNoFeatureTests, SharedFenceImportWithoutFeature) {
if (@available(macOS 10.14, iOS 12.0, *)) {
auto mtlDevice = AcquireNSPRef(MTLCreateSystemDefaultDevice());
auto sharedEvent = AcquireNSPRef([*mtlDevice newSharedEvent]);
wgpu::SharedFenceMTLSharedEventDescriptor sharedEventDesc;
sharedEventDesc.sharedEvent = static_cast<void*>(*sharedEvent);
wgpu::SharedFenceDescriptor fenceDesc;
fenceDesc.nextInChain = &sharedEventDesc;
ASSERT_DEVICE_ERROR_MSG(wgpu::SharedFence fence = device.ImportSharedFence(&fenceDesc),
testing::HasSubstr("MTLSharedEvent is not enabled"));
}
}
// Test that it is an error to import a shared fence with a null MTLSharedEvent
TEST_P(SharedTextureMemoryTests, SharedFenceImportMTLSharedEventMissing) {
if (@available(macOS 10.14, iOS 12.0, *)) {
wgpu::SharedFenceMTLSharedEventDescriptor sharedEventDesc;
sharedEventDesc.sharedEvent = nullptr;
wgpu::SharedFenceDescriptor fenceDesc;
fenceDesc.nextInChain = &sharedEventDesc;
ASSERT_DEVICE_ERROR_MSG(wgpu::SharedFence fence = device.ImportSharedFence(&fenceDesc),
testing::HasSubstr("missing"));
}
}
// Test exporting info from a shared fence with no chained struct.
// It should be valid and the fence type is exported.
TEST_P(SharedTextureMemoryTests, SharedFenceExportInfoNoChainedStruct) {
if (@available(macOS 10.14, iOS 12.0, *)) {
auto mtlDevice = AcquireNSPRef(MTLCreateSystemDefaultDevice());
auto sharedEvent = AcquireNSPRef([*mtlDevice newSharedEvent]);
wgpu::SharedFenceMTLSharedEventDescriptor sharedEventDesc;
sharedEventDesc.sharedEvent = static_cast<void*>(*sharedEvent);
wgpu::SharedFenceDescriptor fenceDesc;
fenceDesc.nextInChain = &sharedEventDesc;
wgpu::SharedFence fence = device.ImportSharedFence(&fenceDesc);
// Test no chained struct.
wgpu::SharedFenceExportInfo exportInfo;
exportInfo.nextInChain = nullptr;
fence.ExportInfo(&exportInfo);
EXPECT_EQ(exportInfo.type, wgpu::SharedFenceType::MTLSharedEvent);
}
}
// Test exporting info from a shared fence with an invalid chained struct.
// It should not be valid, but the fence type should still be exported.
TEST_P(SharedTextureMemoryTests, SharedFenceExportInfoInvalidChainedStruct) {
if (@available(macOS 10.14, iOS 12.0, *)) {
auto mtlDevice = AcquireNSPRef(MTLCreateSystemDefaultDevice());
auto sharedEvent = AcquireNSPRef([*mtlDevice newSharedEvent]);
wgpu::SharedFenceMTLSharedEventDescriptor sharedEventDesc;
sharedEventDesc.sharedEvent = static_cast<void*>(*sharedEvent);
wgpu::SharedFenceDescriptor fenceDesc;
fenceDesc.nextInChain = &sharedEventDesc;
wgpu::SharedFence fence = device.ImportSharedFence(&fenceDesc);
wgpu::ChainedStructOut otherStruct;
wgpu::SharedFenceExportInfo exportInfo;
exportInfo.nextInChain = &otherStruct;
ASSERT_DEVICE_ERROR(fence.ExportInfo(&exportInfo));
EXPECT_EQ(exportInfo.type, wgpu::SharedFenceType::MTLSharedEvent);
}
}
DAWN_INSTANTIATE_PREFIXED_TEST_P(Metal,
SharedTextureMemoryNoFeatureTests,
{MetalBackend()},
{Backend::GetInstance()});
DAWN_INSTANTIATE_PREFIXED_TEST_P(Metal,
SharedTextureMemoryTests,
{MetalBackend()},
{Backend::GetInstance()});
} // anonymous namespace
} // namespace dawn