Metal: Wrap NS classes and protocols in NSRef.

This makes refcounting of these objects more automatic to try and
prevent leaks or use-after-frees in the future.

Also removes operator* from RefBase (and Ref) because it is never used
and cannot work in a normal way for ObjectiveC protocols that cannot be
dereferenced.

Bug: dawn:89

Change-Id: I2e3fbfd638e2ba76d8c563f30bc489a384152552
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/32161
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Corentin Wallez 2020-11-16 23:07:56 +00:00 committed by Commit Bot service account
parent 1805e46a15
commit 0055d95fa7
30 changed files with 462 additions and 346 deletions

View File

@ -167,6 +167,7 @@ if (is_win || is_linux || is_chromeos || is_mac || is_fuchsia || is_android) {
"Log.h",
"Math.cpp",
"Math.h",
"NSRef.h",
"PlacementAllocated.h",
"Platform.h",
"RefBase.h",

View File

@ -29,6 +29,7 @@ target_sources(dawn_common PRIVATE
"Log.h"
"Math.cpp"
"Math.h"
"NSRef.h"
"PlacementAllocated.h"
"Platform.h"
"RefBase.h"

123
src/common/NSRef.h Normal file
View File

@ -0,0 +1,123 @@
// Copyright 2020 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 COMMON_NSREF_H_
#define COMMON_NSREF_H_
#include "common/RefBase.h"
#import <Foundation/NSObject.h>
#if !defined(__OBJC__)
# error "NSRef can only be used in Objective C/C++ code."
#endif
// This file contains smart pointers that automatically reference and release Objective C objects
// and prototocals in a manner very similar to Ref<>. Note that NSRef<> and NSPRef's constructor add
// a reference to the object by default, so the pattern to get a reference for a newly created
// NSObject is the following:
//
// NSRef<NSFoo> foo = AcquireNSRef([NSFoo alloc]);
//
// NSRef overloads -> and * but these operators don't work extremely well with Objective C's
// features. For example automatic dereferencing when doing the following doesn't work:
//
// NSFoo* foo;
// foo.member = 1;
// someVar = foo.member;
//
// Instead use the message passing syntax:
//
// NSRef<NSFoo> foo;
// [*foo setMember: 1];
// someVar = [*foo member];
//
// Also did you notive the extra '*' in the example above? That's because Objective C's message
// passing doesn't automatically call a C++ operator to dereference smart pointers (like -> does) so
// we have to dereference manually using '*'. In some cases the extra * or message passing syntax
// can get a bit annoying so instead a local "naked" pointer can be borrowed from the NSRef. This
// would change the syntax overload in the following:
//
// NSRef<NSFoo> foo;
// [*foo setA:1];
// [*foo setB:2];
// [*foo setC:3];
//
// Into (note access to members of ObjC classes referenced via pointer is done with . and not ->):
//
// NSRef<NSFoo> fooRef;
// NSFoo* foo = fooRef.Get();
// foo.a = 1;
// foo.b = 2;
// boo.c = 3;
//
// Which can be subjectively easier to read.
template <typename T>
struct NSRefTraits {
static constexpr T kNullValue = nullptr;
static void Reference(T value) {
[value retain];
}
static void Release(T value) {
[value release];
}
};
template <typename T>
class NSRef : public RefBase<T*, NSRefTraits<T*>> {
public:
using RefBase<T*, NSRefTraits<T*>>::RefBase;
const T* operator*() const {
return this->Get();
}
T* operator*() {
return this->Get();
}
};
template <typename T>
NSRef<T> AcquireNSRef(T* pointee) {
NSRef<T> ref;
ref.Acquire(pointee);
return ref;
}
// This is a RefBase<> for an Objective C protocol (hence the P). Objective C protocols must always
// be referenced with id<ProtocolName> and not just ProtocolName* so they cannot use NSRef<>
// itself. That's what the P in NSPRef stands for: Protocol.
template <typename T>
class NSPRef : public RefBase<T, NSRefTraits<T>> {
public:
using RefBase<T, NSRefTraits<T>>::RefBase;
const T operator*() const {
return this->Get();
}
T operator*() {
return this->Get();
}
};
template <typename T>
NSPRef<T> AcquireNSPRef(T pointee) {
NSPRef<T> ref;
ref.Acquire(pointee);
return ref;
}
#endif // COMMON_NSREF_H_

View File

@ -137,14 +137,6 @@ class RefBase {
return mValue != kNullValue;
}
// Operator * and -> to act like a pointer.
const typename Traits::PointedType& operator*() const {
return *mValue;
}
typename Traits::PointedType& operator*() {
return *mValue;
}
const T operator->() const {
return mValue;
}
@ -166,6 +158,11 @@ class RefBase {
return value;
}
void Acquire(T value) {
Release();
mValue = value;
}
private:
// Friend is needed so that instances of RefBase<U> can call Reference and Release on
// RefBase<T>.

View File

@ -42,7 +42,6 @@ class RefCounted {
template <typename T>
struct RefCountedTraits {
using PointedType = T;
static constexpr T* kNullValue = nullptr;
static void Reference(T* value) {
value->Reference();
@ -60,8 +59,8 @@ class Ref : public RefBase<T*, RefCountedTraits<T>> {
template <typename T>
Ref<T> AcquireRef(T* pointee) {
Ref<T> ref(pointee);
ref->Release();
Ref<T> ref;
ref.Acquire(pointee);
return ref;
}

View File

@ -15,6 +15,7 @@
#include "dawn_native/metal/BackendMTL.h"
#include "common/GPUInfo.h"
#include "common/NSRef.h"
#include "common/Platform.h"
#include "dawn_native/Instance.h"
#include "dawn_native/MetalBackend.h"
@ -176,8 +177,8 @@ namespace dawn_native { namespace metal {
class Adapter : public AdapterBase {
public:
Adapter(InstanceBase* instance, id<MTLDevice> device)
: AdapterBase(instance, wgpu::BackendType::Metal), mDevice([device retain]) {
mPCIInfo.name = std::string([mDevice.name UTF8String]);
: AdapterBase(instance, wgpu::BackendType::Metal), mDevice(device) {
mPCIInfo.name = std::string([[*mDevice name] UTF8String]);
PCIIDs ids;
if (!instance->ConsumedError(GetDevicePCIInfo(device, &ids))) {
@ -206,10 +207,6 @@ namespace dawn_native { namespace metal {
InitializeSupportedExtensions();
}
~Adapter() override {
[mDevice release];
}
private:
ResultOrError<DeviceBase*> CreateDeviceImpl(const DeviceDescriptor* descriptor) override {
return Device::Create(this, mDevice, descriptor);
@ -217,14 +214,14 @@ namespace dawn_native { namespace metal {
void InitializeSupportedExtensions() {
#if defined(DAWN_PLATFORM_MACOS)
if ([mDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1]) {
if ([*mDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1]) {
mSupportedExtensions.EnableExtension(Extension::TextureCompressionBC);
}
#endif
if (@available(macOS 10.15, iOS 14.0, *)) {
if ([mDevice supportsFamily:MTLGPUFamilyMac2] ||
[mDevice supportsFamily:MTLGPUFamilyApple5]) {
if ([*mDevice supportsFamily:MTLGPUFamilyMac2] ||
[*mDevice supportsFamily:MTLGPUFamilyApple5]) {
mSupportedExtensions.EnableExtension(Extension::PipelineStatisticsQuery);
mSupportedExtensions.EnableExtension(Extension::TimestampQuery);
}
@ -233,7 +230,7 @@ namespace dawn_native { namespace metal {
mSupportedExtensions.EnableExtension(Extension::ShaderFloat16);
}
id<MTLDevice> mDevice = nil;
NSPRef<id<MTLDevice>> mDevice;
};
// Implementation of the Metal backend's BackendConnection
@ -251,13 +248,12 @@ namespace dawn_native { namespace metal {
#if defined(DAWN_PLATFORM_MACOS)
if (@available(macOS 10.11, *)) {
supportedVersion = YES;
NSArray<id<MTLDevice>>* devices = MTLCopyAllDevices();
for (id<MTLDevice> device in devices) {
NSRef<NSArray<id<MTLDevice>>> devices = AcquireNSRef(MTLCopyAllDevices());
for (id<MTLDevice> device in devices.Get()) {
adapters.push_back(std::make_unique<Adapter>(GetInstance(), device));
}
[devices release];
}
#endif

View File

@ -15,6 +15,7 @@
#ifndef DAWNNATIVE_METAL_BUFFERMTL_H_
#define DAWNNATIVE_METAL_BUFFERMTL_H_
#include "common/NSRef.h"
#include "common/SerialQueue.h"
#include "dawn_native/Buffer.h"
@ -53,7 +54,7 @@ namespace dawn_native { namespace metal {
void InitializeToZero(CommandRecordingContext* commandContext);
void ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue);
id<MTLBuffer> mMtlBuffer = nil;
NSPRef<id<MTLBuffer>> mMtlBuffer;
};
}} // namespace dawn_native::metal

View File

@ -84,9 +84,10 @@ namespace dawn_native { namespace metal {
return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation is too large");
}
mMtlBuffer = [ToBackend(GetDevice())->GetMTLDevice() newBufferWithLength:currentSize
options:storageMode];
if (mMtlBuffer == nil) {
mMtlBuffer.Acquire([ToBackend(GetDevice())->GetMTLDevice()
newBufferWithLength:currentSize
options:storageMode]);
if (mMtlBuffer == nullptr) {
return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation failed");
}
@ -107,7 +108,7 @@ namespace dawn_native { namespace metal {
}
id<MTLBuffer> Buffer::GetMTLBuffer() const {
return mMtlBuffer;
return mMtlBuffer.Get();
}
bool Buffer::IsCPUWritableAtCreation() const {
@ -128,7 +129,7 @@ namespace dawn_native { namespace metal {
}
void* Buffer::GetMappedPointerImpl() {
return [mMtlBuffer contents];
return [*mMtlBuffer contents];
}
void Buffer::UnmapImpl() {
@ -136,8 +137,7 @@ namespace dawn_native { namespace metal {
}
void Buffer::DestroyImpl() {
[mMtlBuffer release];
mMtlBuffer = nil;
mMtlBuffer = nullptr;
}
void Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
@ -196,7 +196,7 @@ namespace dawn_native { namespace metal {
return;
}
[commandContext->EnsureBlit() fillBuffer:mMtlBuffer
[commandContext->EnsureBlit() fillBuffer:mMtlBuffer.Get()
range:NSMakeRange(0, GetSize())
value:clearValue];
}

View File

@ -53,9 +53,12 @@ namespace dawn_native { namespace metal {
}
}
// Creates an autoreleased MTLRenderPassDescriptor matching desc
MTLRenderPassDescriptor* CreateMTLRenderPassDescriptor(BeginRenderPassCmd* renderPass) {
MTLRenderPassDescriptor* descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
NSRef<MTLRenderPassDescriptor> CreateMTLRenderPassDescriptor(
BeginRenderPassCmd* renderPass) {
// Note that this creates a descriptor that's autoreleased so we don't use AcquireNSRef
NSRef<MTLRenderPassDescriptor> descriptorRef =
[MTLRenderPassDescriptor renderPassDescriptor];
MTLRenderPassDescriptor* descriptor = descriptorRef.Get();
for (ColorAttachmentIndex attachment :
IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
@ -167,7 +170,7 @@ namespace dawn_native { namespace metal {
}
}
return descriptor;
return descriptorRef;
}
// Helper function for Toggle EmulateStoreAndMSAAResolve
@ -175,10 +178,13 @@ namespace dawn_native { namespace metal {
CommandRecordingContext* commandContext,
const MTLRenderPassDescriptor* mtlRenderPass,
const std::array<id<MTLTexture>, kMaxColorAttachments>& resolveTextures) {
MTLRenderPassDescriptor* mtlRenderPassForResolve =
// Note that this creates a descriptor that's autoreleased so we don't use AcquireNSRef
NSRef<MTLRenderPassDescriptor> mtlRenderPassForResolveRef =
[MTLRenderPassDescriptor renderPassDescriptor];
MTLRenderPassDescriptor* mtlRenderPassForResolve = mtlRenderPassForResolveRef.Get();
for (uint32_t i = 0; i < kMaxColorAttachments; ++i) {
if (resolveTextures[i] == nil) {
if (resolveTextures[i] == nullptr) {
continue;
}
@ -199,11 +205,13 @@ namespace dawn_native { namespace metal {
}
// Helper functions for Toggle AlwaysResolveIntoZeroLevelAndLayer
id<MTLTexture> CreateResolveTextureForWorkaround(Device* device,
MTLPixelFormat mtlFormat,
uint32_t width,
uint32_t height) {
MTLTextureDescriptor* mtlDesc = [MTLTextureDescriptor new];
NSPRef<id<MTLTexture>> CreateResolveTextureForWorkaround(Device* device,
MTLPixelFormat mtlFormat,
uint32_t width,
uint32_t height) {
NSRef<MTLTextureDescriptor> mtlDescRef = AcquireNSRef([MTLTextureDescriptor new]);
MTLTextureDescriptor* mtlDesc = mtlDescRef.Get();
mtlDesc.textureType = MTLTextureType2D;
mtlDesc.usage = MTLTextureUsageRenderTarget;
mtlDesc.pixelFormat = mtlFormat;
@ -214,10 +222,8 @@ namespace dawn_native { namespace metal {
mtlDesc.arrayLength = 1;
mtlDesc.storageMode = MTLStorageModePrivate;
mtlDesc.sampleCount = 1;
id<MTLTexture> resolveTexture =
[device->GetMTLDevice() newTextureWithDescriptor:mtlDesc];
[mtlDesc release];
return resolveTexture;
return AcquireNSPRef([device->GetMTLDevice() newTextureWithDescriptor:mtlDesc]);
}
void CopyIntoTrueResolveTarget(CommandRecordingContext* commandContext,
@ -350,11 +356,11 @@ namespace dawn_native { namespace metal {
group->GetLayout()->GetBindingInfo(bindingIndex);
bool hasVertStage =
bindingInfo.visibility & wgpu::ShaderStage::Vertex && render != nil;
bindingInfo.visibility & wgpu::ShaderStage::Vertex && render != nullptr;
bool hasFragStage =
bindingInfo.visibility & wgpu::ShaderStage::Fragment && render != nil;
bindingInfo.visibility & wgpu::ShaderStage::Fragment && render != nullptr;
bool hasComputeStage =
bindingInfo.visibility & wgpu::ShaderStage::Compute && compute != nil;
bindingInfo.visibility & wgpu::ShaderStage::Compute && compute != nullptr;
uint32_t vertIndex = 0;
uint32_t fragIndex = 0;
@ -461,12 +467,12 @@ namespace dawn_native { namespace metal {
template <typename... Args>
void ApplyBindGroup(id<MTLRenderCommandEncoder> encoder, Args&&... args) {
ApplyBindGroupImpl(encoder, nil, std::forward<Args&&>(args)...);
ApplyBindGroupImpl(encoder, nullptr, std::forward<Args&&>(args)...);
}
template <typename... Args>
void ApplyBindGroup(id<MTLComputeCommandEncoder> encoder, Args&&... args) {
ApplyBindGroupImpl(nil, encoder, std::forward<Args&&>(args)...);
ApplyBindGroupImpl(nullptr, encoder, std::forward<Args&&>(args)...);
}
StorageBufferLengthTracker* mLengthTracker;
@ -578,8 +584,9 @@ namespace dawn_native { namespace metal {
commandContext->EndBlit();
LazyClearRenderPassAttachments(cmd);
MTLRenderPassDescriptor* descriptor = CreateMTLRenderPassDescriptor(cmd);
DAWN_TRY(EncodeRenderPass(commandContext, descriptor, cmd->width, cmd->height));
NSRef<MTLRenderPassDescriptor> descriptor = CreateMTLRenderPassDescriptor(cmd);
DAWN_TRY(EncodeRenderPass(commandContext, descriptor.Get(), cmd->width,
cmd->height));
nextPassNumber++;
break;
@ -792,9 +799,9 @@ namespace dawn_native { namespace metal {
char* label = mCommands.NextData<char>(cmd->length + 1);
if (@available(macos 10.13, *)) {
NSString* mtlLabel = [[NSString alloc] initWithUTF8String:label];
[commandContext->GetCommands() pushDebugGroup:mtlLabel];
[mtlLabel release];
NSRef<NSString> mtlLabel =
AcquireNSRef([[NSString alloc] initWithUTF8String:label]);
[commandContext->GetCommands() pushDebugGroup:mtlLabel.Get()];
}
break;
@ -876,10 +883,9 @@ namespace dawn_native { namespace metal {
case Command::InsertDebugMarker: {
InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();
char* label = mCommands.NextData<char>(cmd->length + 1);
NSString* mtlLabel = [[NSString alloc] initWithUTF8String:label];
[encoder insertDebugSignpost:mtlLabel];
[mtlLabel release];
NSRef<NSString> mtlLabel =
AcquireNSRef([[NSString alloc] initWithUTF8String:label]);
[encoder insertDebugSignpost:mtlLabel.Get()];
break;
}
@ -893,10 +899,9 @@ namespace dawn_native { namespace metal {
case Command::PushDebugGroup: {
PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>();
char* label = mCommands.NextData<char>(cmd->length + 1);
NSString* mtlLabel = [[NSString alloc] initWithUTF8String:label];
[encoder pushDebugGroup:mtlLabel];
[mtlLabel release];
NSRef<NSString> mtlLabel =
AcquireNSRef([[NSString alloc] initWithUTF8String:label]);
[encoder pushDebugGroup:mtlLabel.Get()];
break;
}
@ -944,9 +949,9 @@ namespace dawn_native { namespace metal {
// Use temporary resolve texture on the resolve targets with non-zero resolveLevel or
// resolveSlice.
bool useTemporaryResolveTexture = false;
std::array<id<MTLTexture>, kMaxColorAttachments> temporaryResolveTextures = {};
std::array<NSPRef<id<MTLTexture>>, kMaxColorAttachments> temporaryResolveTextures = {};
for (uint32_t i = 0; i < kMaxColorAttachments; ++i) {
if (mtlRenderPass.colorAttachments[i].resolveTexture == nil) {
if (mtlRenderPass.colorAttachments[i].resolveTexture == nullptr) {
continue;
}
@ -963,7 +968,8 @@ namespace dawn_native { namespace metal {
temporaryResolveTextures[i] =
CreateResolveTextureForWorkaround(device, mtlFormat, width, height);
mtlRenderPass.colorAttachments[i].resolveTexture = temporaryResolveTextures[i];
mtlRenderPass.colorAttachments[i].resolveTexture =
temporaryResolveTextures[i].Get();
mtlRenderPass.colorAttachments[i].resolveLevel = 0;
mtlRenderPass.colorAttachments[i].resolveSlice = 0;
useTemporaryResolveTexture = true;
@ -974,16 +980,14 @@ namespace dawn_native { namespace metal {
if (useTemporaryResolveTexture) {
DAWN_TRY(EncodeRenderPass(commandContext, mtlRenderPass, width, height));
for (uint32_t i = 0; i < kMaxColorAttachments; ++i) {
if (trueResolveTextures[i] == nil) {
if (trueResolveTextures[i] == nullptr) {
continue;
}
ASSERT(temporaryResolveTextures[i] != nil);
ASSERT(temporaryResolveTextures[i] != nullptr);
CopyIntoTrueResolveTarget(commandContext, trueResolveTextures[i],
trueResolveLevels[i], trueResolveSlices[i],
temporaryResolveTextures[i], width, height);
[temporaryResolveTextures[i] release];
temporaryResolveTextures[i] = nil;
temporaryResolveTextures[i].Get(), width, height);
}
return {};
}
@ -1002,7 +1006,7 @@ namespace dawn_native { namespace metal {
resolveTextures[i] = mtlRenderPass.colorAttachments[i].resolveTexture;
mtlRenderPass.colorAttachments[i].storeAction = MTLStoreActionStore;
mtlRenderPass.colorAttachments[i].resolveTexture = nil;
mtlRenderPass.colorAttachments[i].resolveTexture = nullptr;
}
}
@ -1024,7 +1028,7 @@ namespace dawn_native { namespace metal {
uint32_t height) {
bool enableVertexPulling = GetDevice()->IsToggleEnabled(Toggle::MetalEnableVertexPulling);
RenderPipeline* lastPipeline = nullptr;
id<MTLBuffer> indexBuffer = nil;
id<MTLBuffer> indexBuffer = nullptr;
uint32_t indexBufferBaseOffset = 0;
wgpu::IndexFormat indexBufferFormat = wgpu::IndexFormat::Undefined;
StorageBufferLengthTracker storageBufferLengths = {};
@ -1148,10 +1152,9 @@ namespace dawn_native { namespace metal {
case Command::InsertDebugMarker: {
InsertDebugMarkerCmd* cmd = iter->NextCommand<InsertDebugMarkerCmd>();
char* label = iter->NextData<char>(cmd->length + 1);
NSString* mtlLabel = [[NSString alloc] initWithUTF8String:label];
[encoder insertDebugSignpost:mtlLabel];
[mtlLabel release];
NSRef<NSString> mtlLabel =
AcquireNSRef([[NSString alloc] initWithUTF8String:label]);
[encoder insertDebugSignpost:mtlLabel.Get()];
break;
}
@ -1165,10 +1168,9 @@ namespace dawn_native { namespace metal {
case Command::PushDebugGroup: {
PushDebugGroupCmd* cmd = iter->NextCommand<PushDebugGroupCmd>();
char* label = iter->NextData<char>(cmd->length + 1);
NSString* mtlLabel = [[NSString alloc] initWithUTF8String:label];
[encoder pushDebugGroup:mtlLabel];
[mtlLabel release];
NSRef<NSString> mtlLabel =
AcquireNSRef([[NSString alloc] initWithUTF8String:label]);
[encoder pushDebugGroup:mtlLabel.Get()];
break;
}

View File

@ -14,6 +14,8 @@
#ifndef DAWNNATIVE_METAL_COMMANDRECORDINGCONTEXT_H_
#define DAWNNATIVE_METAL_COMMANDRECORDINGCONTEXT_H_
#include "common/NSRef.h"
#import <Metal/Metal.h>
namespace dawn_native { namespace metal {
@ -23,7 +25,7 @@ namespace dawn_native { namespace metal {
class CommandRecordingContext {
public:
CommandRecordingContext();
CommandRecordingContext(id<MTLCommandBuffer> commands);
CommandRecordingContext(NSPRef<id<MTLCommandBuffer>> commands);
CommandRecordingContext(const CommandRecordingContext& rhs) = delete;
CommandRecordingContext& operator=(const CommandRecordingContext& rhs) = delete;
@ -35,7 +37,7 @@ namespace dawn_native { namespace metal {
id<MTLCommandBuffer> GetCommands();
id<MTLCommandBuffer> AcquireCommands();
NSPRef<id<MTLCommandBuffer>> AcquireCommands();
id<MTLBlitCommandEncoder> EnsureBlit();
void EndBlit();
@ -47,10 +49,10 @@ namespace dawn_native { namespace metal {
void EndRender();
private:
id<MTLCommandBuffer> mCommands = nil;
id<MTLBlitCommandEncoder> mBlit = nil;
id<MTLComputeCommandEncoder> mCompute = nil;
id<MTLRenderCommandEncoder> mRender = nil;
NSPRef<id<MTLCommandBuffer>> mCommands;
NSPRef<id<MTLBlitCommandEncoder>> mBlit;
NSPRef<id<MTLComputeCommandEncoder>> mCompute;
NSPRef<id<MTLRenderCommandEncoder>> mRender;
bool mInEncoder = false;
};

View File

@ -20,8 +20,8 @@ namespace dawn_native { namespace metal {
CommandRecordingContext::CommandRecordingContext() = default;
CommandRecordingContext::CommandRecordingContext(id<MTLCommandBuffer> commands)
: mCommands(commands) {
CommandRecordingContext::CommandRecordingContext(NSPRef<id<MTLCommandBuffer>> commands)
: mCommands(std::move(commands)) {
}
CommandRecordingContext::CommandRecordingContext(CommandRecordingContext&& rhs)
@ -35,90 +35,87 @@ namespace dawn_native { namespace metal {
CommandRecordingContext::~CommandRecordingContext() {
// Commands must be acquired.
ASSERT(mCommands == nil);
ASSERT(mCommands == nullptr);
}
id<MTLCommandBuffer> CommandRecordingContext::GetCommands() {
return mCommands;
return mCommands.Get();
}
id<MTLCommandBuffer> CommandRecordingContext::AcquireCommands() {
if (mCommands == nil) {
return nil;
NSPRef<id<MTLCommandBuffer>> CommandRecordingContext::AcquireCommands() {
// A blit encoder can be left open from WriteBuffer, make sure we close it.
if (mCommands != nullptr) {
EndBlit();
}
// A blit encoder can be left open from WriteBuffer, make sure we close it.
EndBlit();
ASSERT(!mInEncoder);
id<MTLCommandBuffer> commands = mCommands;
mCommands = nil;
return commands;
return std::move(mCommands);
}
id<MTLBlitCommandEncoder> CommandRecordingContext::EnsureBlit() {
ASSERT(mCommands != nil);
ASSERT(mCommands != nullptr);
if (mBlit == nil) {
if (mBlit == nullptr) {
ASSERT(!mInEncoder);
mInEncoder = true;
// The autorelease pool may drain before the encoder is ended. Retain so it stays alive.
mBlit = [[mCommands blitCommandEncoder] retain];
// The encoder is created autoreleased. Retain it to avoid the autoreleasepool from
// draining from under us.
mBlit = [*mCommands blitCommandEncoder];
}
return mBlit;
return mBlit.Get();
}
void CommandRecordingContext::EndBlit() {
ASSERT(mCommands != nil);
ASSERT(mCommands != nullptr);
if (mBlit != nil) {
[mBlit endEncoding];
[mBlit release];
mBlit = nil;
if (mBlit != nullptr) {
[*mBlit endEncoding];
mBlit = nullptr;
mInEncoder = false;
}
}
id<MTLComputeCommandEncoder> CommandRecordingContext::BeginCompute() {
ASSERT(mCommands != nil);
ASSERT(mCompute == nil);
ASSERT(mCommands != nullptr);
ASSERT(mCompute == nullptr);
ASSERT(!mInEncoder);
mInEncoder = true;
// The autorelease pool may drain before the encoder is ended. Retain so it stays alive.
mCompute = [[mCommands computeCommandEncoder] retain];
return mCompute;
// The encoder is created autoreleased. Retain it to avoid the autoreleasepool from draining
// from under us.
mCompute = [*mCommands computeCommandEncoder];
return mCompute.Get();
}
void CommandRecordingContext::EndCompute() {
ASSERT(mCommands != nil);
ASSERT(mCompute != nil);
ASSERT(mCommands != nullptr);
ASSERT(mCompute != nullptr);
[mCompute endEncoding];
[mCompute release];
mCompute = nil;
[*mCompute endEncoding];
mCompute = nullptr;
mInEncoder = false;
}
id<MTLRenderCommandEncoder> CommandRecordingContext::BeginRender(
MTLRenderPassDescriptor* descriptor) {
ASSERT(mCommands != nil);
ASSERT(mRender == nil);
ASSERT(mCommands != nullptr);
ASSERT(mRender == nullptr);
ASSERT(!mInEncoder);
mInEncoder = true;
// The autorelease pool may drain before the encoder is ended. Retain so it stays alive.
mRender = [[mCommands renderCommandEncoderWithDescriptor:descriptor] retain];
return mRender;
// The encoder is created autoreleased. Retain it to avoid the autoreleasepool from draining
// from under us.
mRender = [*mCommands renderCommandEncoderWithDescriptor:descriptor];
return mRender.Get();
}
void CommandRecordingContext::EndRender() {
ASSERT(mCommands != nil);
ASSERT(mRender != nil);
ASSERT(mCommands != nullptr);
ASSERT(mRender != nullptr);
[mRender endEncoding];
[mRender release];
mRender = nil;
[*mRender endEncoding];
mRender = nullptr;
mInEncoder = false;
}

View File

@ -17,6 +17,8 @@
#include "dawn_native/ComputePipeline.h"
#include "common/NSRef.h"
#import <Metal/Metal.h>
namespace dawn_native { namespace metal {
@ -33,11 +35,10 @@ namespace dawn_native { namespace metal {
bool RequiresStorageBufferLength() const;
private:
~ComputePipeline() override;
using ComputePipelineBase::ComputePipelineBase;
MaybeError Initialize(const ComputePipelineDescriptor* descriptor);
id<MTLComputePipelineState> mMtlComputePipelineState = nil;
NSPRef<id<MTLComputePipelineState>> mMtlComputePipelineState;
MTLSize mLocalWorkgroupSize;
bool mRequiresStorageBufferLength;
};

View File

@ -37,10 +37,11 @@ namespace dawn_native { namespace metal {
DAWN_TRY(computeModule->CreateFunction(computeEntryPoint, SingleShaderStage::Compute,
ToBackend(GetLayout()), &computeData));
NSError* error = nil;
mMtlComputePipelineState =
[mtlDevice newComputePipelineStateWithFunction:computeData.function error:&error];
if (error != nil) {
NSError* error = nullptr;
mMtlComputePipelineState.Acquire([mtlDevice
newComputePipelineStateWithFunction:computeData.function.Get()
error:&error]);
if (error != nullptr) {
NSLog(@" error => %@", error);
return DAWN_INTERNAL_ERROR("Error creating pipeline state");
}
@ -53,12 +54,8 @@ namespace dawn_native { namespace metal {
return {};
}
ComputePipeline::~ComputePipeline() {
[mMtlComputePipelineState release];
}
void ComputePipeline::Encode(id<MTLComputeCommandEncoder> encoder) {
[encoder setComputePipelineState:mMtlComputePipelineState];
[encoder setComputePipelineState:mMtlComputePipelineState.Get()];
}
MTLSize ComputePipeline::GetLocalWorkGroupSize() const {

View File

@ -35,7 +35,7 @@ namespace dawn_native { namespace metal {
class Device : public DeviceBase {
public:
static ResultOrError<Device*> Create(AdapterBase* adapter,
id<MTLDevice> mtlDevice,
NSPRef<id<MTLDevice>> mtlDevice,
const DeviceDescriptor* descriptor);
~Device() override;
@ -72,7 +72,9 @@ namespace dawn_native { namespace metal {
uint64_t GetOptimalBufferToTextureCopyOffsetAlignment() const override;
private:
Device(AdapterBase* adapter, id<MTLDevice> mtlDevice, const DeviceDescriptor* descriptor);
Device(AdapterBase* adapter,
NSPRef<id<MTLDevice>> mtlDevice,
const DeviceDescriptor* descriptor);
ResultOrError<BindGroupBase*> CreateBindGroupImpl(
const BindGroupDescriptor* descriptor) override;
@ -108,8 +110,8 @@ namespace dawn_native { namespace metal {
MaybeError WaitForIdleForDestruction() override;
ExecutionSerial CheckAndUpdateCompletedSerials() override;
id<MTLDevice> mMtlDevice = nil;
id<MTLCommandQueue> mCommandQueue = nil;
NSPRef<id<MTLDevice>> mMtlDevice;
NSPRef<id<MTLCommandQueue>> mCommandQueue;
CommandRecordingContext mCommandContext;
@ -120,7 +122,7 @@ namespace dawn_native { namespace metal {
// mLastSubmittedCommands will be accessed in a Metal schedule handler that can be fired on
// a different thread so we guard access to it with a mutex.
std::mutex mLastSubmittedCommandsMutex;
id<MTLCommandBuffer> mLastSubmittedCommands = nil;
NSPRef<id<MTLCommandBuffer>> mLastSubmittedCommands;
};
}} // namespace dawn_native::metal

View File

@ -44,18 +44,17 @@ namespace dawn_native { namespace metal {
// static
ResultOrError<Device*> Device::Create(AdapterBase* adapter,
id<MTLDevice> mtlDevice,
NSPRef<id<MTLDevice>> mtlDevice,
const DeviceDescriptor* descriptor) {
Ref<Device> device = AcquireRef(new Device(adapter, mtlDevice, descriptor));
Ref<Device> device = AcquireRef(new Device(adapter, std::move(mtlDevice), descriptor));
DAWN_TRY(device->Initialize());
return device.Detach();
}
Device::Device(AdapterBase* adapter,
id<MTLDevice> mtlDevice,
NSPRef<id<MTLDevice>> mtlDevice,
const DeviceDescriptor* descriptor)
: DeviceBase(adapter, descriptor), mMtlDevice([mtlDevice retain]), mCompletedSerial(0) {
[mMtlDevice retain];
: DeviceBase(adapter, descriptor), mMtlDevice(std::move(mtlDevice)), mCompletedSerial(0) {
}
Device::~Device() {
@ -69,7 +68,7 @@ namespace dawn_native { namespace metal {
ForceSetToggle(Toggle::MetalEnableVertexPulling, false);
}
mCommandQueue = [mMtlDevice newCommandQueue];
mCommandQueue.Acquire([*mMtlDevice newCommandQueue]);
return DeviceBase::Initialize(new Queue(this));
}
@ -80,18 +79,18 @@ namespace dawn_native { namespace metal {
#if defined(DAWN_PLATFORM_MACOS)
if (@available(macOS 10.12, *)) {
haveStoreAndMSAAResolve =
[mMtlDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v2];
[*mMtlDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v2];
}
#elif defined(DAWN_PLATFORM_IOS)
haveStoreAndMSAAResolve =
[mMtlDevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2];
[*mMtlDevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2];
#endif
// On tvOS, we would need MTLFeatureSet_tvOS_GPUFamily2_v1.
SetToggle(Toggle::EmulateStoreAndMSAAResolve, !haveStoreAndMSAAResolve);
bool haveSamplerCompare = true;
#if defined(DAWN_PLATFORM_IOS)
haveSamplerCompare = [mMtlDevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1];
haveSamplerCompare = [*mMtlDevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1];
#endif
// TODO(crbug.com/dawn/342): Investigate emulation -- possibly expensive.
SetToggle(Toggle::MetalDisableSamplerCompare, !haveSamplerCompare);
@ -99,7 +98,7 @@ namespace dawn_native { namespace metal {
bool haveBaseVertexBaseInstance = true;
#if defined(DAWN_PLATFORM_IOS)
haveBaseVertexBaseInstance =
[mMtlDevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1];
[*mMtlDevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1];
#endif
// TODO(crbug.com/dawn/343): Investigate emulation.
SetToggle(Toggle::DisableBaseVertex, !haveBaseVertexBaseInstance);
@ -187,7 +186,7 @@ namespace dawn_native { namespace metal {
}
MaybeError Device::TickImpl() {
if (mCommandContext.GetCommands() != nil) {
if (mCommandContext.GetCommands() != nullptr) {
SubmitPendingCommandBuffer();
}
@ -195,33 +194,33 @@ namespace dawn_native { namespace metal {
}
id<MTLDevice> Device::GetMTLDevice() {
return mMtlDevice;
return mMtlDevice.Get();
}
id<MTLCommandQueue> Device::GetMTLQueue() {
return mCommandQueue;
return mCommandQueue.Get();
}
CommandRecordingContext* Device::GetPendingCommandContext() {
if (mCommandContext.GetCommands() == nil) {
if (mCommandContext.GetCommands() == nullptr) {
TRACE_EVENT0(GetPlatform(), General, "[MTLCommandQueue commandBuffer]");
// The MTLCommandBuffer will be autoreleased by default.
// The autorelease pool may drain before the command buffer is submitted. Retain so it
// stays alive.
mCommandContext = CommandRecordingContext([[mCommandQueue commandBuffer] retain]);
mCommandContext = CommandRecordingContext([*mCommandQueue commandBuffer]);
}
return &mCommandContext;
}
void Device::SubmitPendingCommandBuffer() {
if (mCommandContext.GetCommands() == nil) {
if (mCommandContext.GetCommands() == nullptr) {
return;
}
IncrementLastSubmittedCommandSerial();
// Acquire the pending command buffer, which is retained. It must be released later.
id<MTLCommandBuffer> pendingCommands = mCommandContext.AcquireCommands();
NSPRef<id<MTLCommandBuffer>> pendingCommands = mCommandContext.AcquireCommands();
// Replace mLastSubmittedCommands with the mutex held so we avoid races between the
// schedule handler and this code.
@ -230,12 +229,15 @@ namespace dawn_native { namespace metal {
mLastSubmittedCommands = pendingCommands;
}
[pendingCommands addScheduledHandler:^(id<MTLCommandBuffer>) {
// Make a local copy of the pointer to the commands because it's not clear how ObjC blocks
// handle types with copy / move constructors being referenced in the block..
id<MTLCommandBuffer> pendingCommandsPointer = pendingCommands.Get();
[*pendingCommands addScheduledHandler:^(id<MTLCommandBuffer>) {
// This is DRF because we hold the mutex for mLastSubmittedCommands and pendingCommands
// is a local value (and not the member itself).
std::lock_guard<std::mutex> lock(mLastSubmittedCommandsMutex);
if (this->mLastSubmittedCommands == pendingCommands) {
this->mLastSubmittedCommands = nil;
if (this->mLastSubmittedCommands.Get() == pendingCommandsPointer) {
this->mLastSubmittedCommands = nullptr;
}
}];
@ -243,7 +245,7 @@ namespace dawn_native { namespace metal {
// mLastSubmittedSerial so it is captured by value.
ExecutionSerial pendingSerial = GetLastSubmittedCommandSerial();
// this ObjC block runs on a different thread
[pendingCommands addCompletedHandler:^(id<MTLCommandBuffer>) {
[*pendingCommands addCompletedHandler:^(id<MTLCommandBuffer>) {
TRACE_EVENT_ASYNC_END0(GetPlatform(), GPUWork, "DeviceMTL::SubmitPendingCommandBuffer",
uint64_t(pendingSerial));
ASSERT(uint64_t(pendingSerial) > mCompletedSerial.load());
@ -252,8 +254,7 @@ namespace dawn_native { namespace metal {
TRACE_EVENT_ASYNC_BEGIN0(GetPlatform(), GPUWork, "DeviceMTL::SubmitPendingCommandBuffer",
uint64_t(pendingSerial));
[pendingCommands commit];
[pendingCommands release];
[*pendingCommands commit];
}
ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(size_t size) {
@ -356,11 +357,12 @@ namespace dawn_native { namespace metal {
void Device::WaitForCommandsToBeScheduled() {
SubmitPendingCommandBuffer();
[mLastSubmittedCommands waitUntilScheduled];
[*mLastSubmittedCommands waitUntilScheduled];
}
MaybeError Device::WaitForIdleForDestruction() {
[mCommandContext.AcquireCommands() release];
// Forget all pending commands.
mCommandContext.AcquireCommands();
CheckPassedSerials();
// Wait for all commands to be finished so we can free resources
@ -375,13 +377,11 @@ namespace dawn_native { namespace metal {
void Device::ShutDownImpl() {
ASSERT(GetState() == State::Disconnected);
[mCommandContext.AcquireCommands() release];
// Forget all pending commands.
mCommandContext.AcquireCommands();
[mCommandQueue release];
mCommandQueue = nil;
[mMtlDevice release];
mMtlDevice = nil;
mCommandQueue = nullptr;
mMtlDevice = nullptr;
}
uint32_t Device::GetOptimalBytesPerRowAlignment() const {

View File

@ -17,6 +17,8 @@
#include "dawn_native/QuerySet.h"
#include "common/NSRef.h"
#import <Metal/Metal.h>
namespace dawn_native { namespace metal {
@ -40,9 +42,11 @@ namespace dawn_native { namespace metal {
// Dawn API
void DestroyImpl() override;
id<MTLBuffer> mVisibilityBuffer = nil;
NSPRef<id<MTLBuffer>> mVisibilityBuffer;
// Note that mCounterSampleBuffer cannot be an NSRef because the API_AVAILABLE macros don't
// propagate nicely through templates.
id<MTLCounterSampleBuffer> mCounterSampleBuffer API_AVAILABLE(macos(10.15),
ios(14.0)) = nil;
ios(14.0)) = nullptr;
};
}} // namespace dawn_native::metal

View File

@ -26,7 +26,9 @@ namespace dawn_native { namespace metal {
Device* device,
MTLCommonCounterSet counterSet,
uint32_t count) API_AVAILABLE(macos(10.15), ios(14.0)) {
MTLCounterSampleBufferDescriptor* descriptor = [MTLCounterSampleBufferDescriptor new];
NSRef<MTLCounterSampleBufferDescriptor> descriptorRef =
AcquireNSRef([MTLCounterSampleBufferDescriptor new]);
MTLCounterSampleBufferDescriptor* descriptor = descriptorRef.Get();
// To determine which counters are available from a device, we need to iterate through
// the counterSets property of a MTLDevice. Then configure which counters will be
@ -38,18 +40,19 @@ namespace dawn_native { namespace metal {
break;
}
}
ASSERT(descriptor.counterSet != nil);
ASSERT(descriptor.counterSet != nullptr);
descriptor.sampleCount = count;
descriptor.storageMode = MTLStorageModePrivate;
if (device->IsToggleEnabled(Toggle::MetalUseSharedModeForCounterSampleBuffer)) {
descriptor.storageMode = MTLStorageModeShared;
}
NSError* error = nil;
NSError* error = nullptr;
id<MTLCounterSampleBuffer> counterSampleBuffer =
[device->GetMTLDevice() newCounterSampleBufferWithDescriptor:descriptor
error:&error];
if (error != nil) {
if (error != nullptr) {
const char* errorString = [error.localizedDescription UTF8String];
return DAWN_INTERNAL_ERROR(std::string("Error creating query set: ") + errorString);
}
@ -73,9 +76,9 @@ namespace dawn_native { namespace metal {
case wgpu::QueryType::Occlusion: {
// Create buffer for writing 64-bit results.
NSUInteger bufferSize = static_cast<NSUInteger>(GetQueryCount() * sizeof(uint64_t));
mVisibilityBuffer =
[device->GetMTLDevice() newBufferWithLength:bufferSize
options:MTLResourceStorageModePrivate];
mVisibilityBuffer = AcquireNSPRef([device->GetMTLDevice()
newBufferWithLength:bufferSize
options:MTLResourceStorageModePrivate]);
break;
}
case wgpu::QueryType::PipelineStatistics:
@ -105,7 +108,7 @@ namespace dawn_native { namespace metal {
}
id<MTLBuffer> QuerySet::GetVisibilityBuffer() const {
return mVisibilityBuffer;
return mVisibilityBuffer.Get();
}
id<MTLCounterSampleBuffer> QuerySet::GetCounterSampleBuffer() const
@ -118,16 +121,13 @@ namespace dawn_native { namespace metal {
}
void QuerySet::DestroyImpl() {
if (mVisibilityBuffer != nil) {
[mVisibilityBuffer release];
mVisibilityBuffer = nil;
}
mVisibilityBuffer = nullptr;
// mCounterSampleBuffer isn't an NSRef because API_AVAILABLE doesn't work will with
// templates.
if (@available(macOS 10.15, iOS 14.0, *)) {
if (mCounterSampleBuffer != nil) {
[mCounterSampleBuffer release];
mCounterSampleBuffer = nil;
}
[mCounterSampleBuffer release];
mCounterSampleBuffer = nullptr;
}
}

View File

@ -17,6 +17,8 @@
#include "dawn_native/RenderPipeline.h"
#include "common/NSRef.h"
#import <Metal/Metal.h>
namespace dawn_native { namespace metal {
@ -43,7 +45,6 @@ namespace dawn_native { namespace metal {
wgpu::ShaderStage GetStagesRequiringStorageBufferLength() const;
private:
~RenderPipeline() override;
using RenderPipelineBase::RenderPipelineBase;
MaybeError Initialize(const RenderPipelineDescriptor* descriptor);
@ -52,8 +53,8 @@ namespace dawn_native { namespace metal {
MTLPrimitiveType mMtlPrimitiveTopology;
MTLWinding mMtlFrontFace;
MTLCullMode mMtlCullMode;
id<MTLRenderPipelineState> mMtlRenderPipelineState = nil;
id<MTLDepthStencilState> mMtlDepthStencilState = nil;
NSPRef<id<MTLRenderPipelineState>> mMtlRenderPipelineState;
NSPRef<id<MTLDepthStencilState>> mMtlDepthStencilState;
ityp::array<VertexBufferSlot, uint32_t, kMaxVertexBuffers> mMtlVertexBufferIndices;
wgpu::ShaderStage mStagesRequiringStorageBufferLength = wgpu::ShaderStage::None;

View File

@ -236,17 +236,23 @@ namespace dawn_native { namespace metal {
}
}
MTLDepthStencilDescriptor* MakeDepthStencilDesc(
NSRef<MTLDepthStencilDescriptor> MakeDepthStencilDesc(
const DepthStencilStateDescriptor* descriptor) {
MTLDepthStencilDescriptor* mtlDepthStencilDescriptor = [MTLDepthStencilDescriptor new];
NSRef<MTLDepthStencilDescriptor> mtlDepthStencilDescRef =
AcquireNSRef([MTLDepthStencilDescriptor new]);
MTLDepthStencilDescriptor* mtlDepthStencilDescriptor = mtlDepthStencilDescRef.Get();
mtlDepthStencilDescriptor.depthCompareFunction =
ToMetalCompareFunction(descriptor->depthCompare);
mtlDepthStencilDescriptor.depthWriteEnabled = descriptor->depthWriteEnabled;
if (StencilTestEnabled(descriptor)) {
MTLStencilDescriptor* backFaceStencil = [MTLStencilDescriptor new];
MTLStencilDescriptor* frontFaceStencil = [MTLStencilDescriptor new];
NSRef<MTLStencilDescriptor> backFaceStencilRef =
AcquireNSRef([MTLStencilDescriptor new]);
MTLStencilDescriptor* backFaceStencil = backFaceStencilRef.Get();
NSRef<MTLStencilDescriptor> frontFaceStencilRef =
AcquireNSRef([MTLStencilDescriptor new]);
MTLStencilDescriptor* frontFaceStencil = frontFaceStencilRef.Get();
backFaceStencil.stencilCompareFunction =
ToMetalCompareFunction(descriptor->stencilBack.compare);
@ -272,12 +278,9 @@ namespace dawn_native { namespace metal {
mtlDepthStencilDescriptor.backFaceStencil = backFaceStencil;
mtlDepthStencilDescriptor.frontFaceStencil = frontFaceStencil;
[backFaceStencil release];
[frontFaceStencil release];
}
return mtlDepthStencilDescriptor;
return mtlDepthStencilDescRef;
}
MTLWinding MTLFrontFace(wgpu::FrontFace face) {
@ -317,20 +320,19 @@ namespace dawn_native { namespace metal {
mMtlCullMode = ToMTLCullMode(GetCullMode());
auto mtlDevice = ToBackend(GetDevice())->GetMTLDevice();
MTLRenderPipelineDescriptor* descriptorMTL = [MTLRenderPipelineDescriptor new];
NSRef<MTLRenderPipelineDescriptor> descriptorMTLRef =
AcquireNSRef([MTLRenderPipelineDescriptor new]);
MTLRenderPipelineDescriptor* descriptorMTL = descriptorMTLRef.Get();
// TODO: MakeVertexDesc should be const in the future, so we don't need to call it here when
// vertex pulling is enabled
MTLVertexDescriptor* vertexDesc = MakeVertexDesc();
descriptorMTL.vertexDescriptor = vertexDesc;
[vertexDesc release];
NSRef<MTLVertexDescriptor> vertexDesc = MakeVertexDesc();
// Calling MakeVertexDesc first is important since it sets indices for packed bindings
if (GetDevice()->IsToggleEnabled(Toggle::MetalEnableVertexPulling)) {
// Calling MakeVertexDesc first is important since it sets indices for packed bindings
MTLVertexDescriptor* emptyVertexDesc = [MTLVertexDescriptor new];
descriptorMTL.vertexDescriptor = emptyVertexDesc;
[emptyVertexDesc release];
vertexDesc = AcquireNSRef([MTLVertexDescriptor new]);
}
descriptorMTL.vertexDescriptor = vertexDesc.Get();
ShaderModule* vertexModule = ToBackend(descriptor->vertexStage.module);
const char* vertexEntryPoint = descriptor->vertexStage.entryPoint;
@ -339,7 +341,7 @@ namespace dawn_native { namespace metal {
ToBackend(GetLayout()), &vertexData, 0xFFFFFFFF,
this));
descriptorMTL.vertexFunction = vertexData.function;
descriptorMTL.vertexFunction = vertexData.function.Get();
if (vertexData.needsStorageBufferLength) {
mStagesRequiringStorageBufferLength |= wgpu::ShaderStage::Vertex;
}
@ -351,7 +353,7 @@ namespace dawn_native { namespace metal {
ToBackend(GetLayout()), &fragmentData,
descriptor->sampleMask));
descriptorMTL.fragmentFunction = fragmentData.function;
descriptorMTL.fragmentFunction = fragmentData.function.Get();
if (fragmentData.needsStorageBufferLength) {
mStagesRequiringStorageBufferLength |= wgpu::ShaderStage::Fragment;
}
@ -384,11 +386,11 @@ namespace dawn_native { namespace metal {
descriptorMTL.alphaToCoverageEnabled = descriptor->alphaToCoverageEnabled;
{
NSError* error = nil;
mMtlRenderPipelineState = [mtlDevice newRenderPipelineStateWithDescriptor:descriptorMTL
error:&error];
[descriptorMTL release];
if (error != nil) {
NSError* error = nullptr;
mMtlRenderPipelineState =
AcquireNSPRef([mtlDevice newRenderPipelineStateWithDescriptor:descriptorMTL
error:&error]);
if (error != nullptr) {
NSLog(@" error => %@", error);
return DAWN_INTERNAL_ERROR("Error creating rendering pipeline state");
}
@ -397,19 +399,14 @@ namespace dawn_native { namespace metal {
// Create depth stencil state and cache it, fetch the cached depth stencil state when we
// call setDepthStencilState() for a given render pipeline in CommandEncoder, in order to
// improve performance.
MTLDepthStencilDescriptor* depthStencilDesc =
NSRef<MTLDepthStencilDescriptor> depthStencilDesc =
MakeDepthStencilDesc(GetDepthStencilStateDescriptor());
mMtlDepthStencilState = [mtlDevice newDepthStencilStateWithDescriptor:depthStencilDesc];
[depthStencilDesc release];
mMtlDepthStencilState =
AcquireNSPRef([mtlDevice newDepthStencilStateWithDescriptor:depthStencilDesc.Get()]);
return {};
}
RenderPipeline::~RenderPipeline() {
[mMtlRenderPipelineState release];
[mMtlDepthStencilState release];
}
MTLPrimitiveType RenderPipeline::GetMTLPrimitiveTopology() const {
return mMtlPrimitiveTopology;
}
@ -423,11 +420,11 @@ namespace dawn_native { namespace metal {
}
void RenderPipeline::Encode(id<MTLRenderCommandEncoder> encoder) {
[encoder setRenderPipelineState:mMtlRenderPipelineState];
[encoder setRenderPipelineState:mMtlRenderPipelineState.Get()];
}
id<MTLDepthStencilState> RenderPipeline::GetMTLDepthStencilState() {
return mMtlDepthStencilState;
return mMtlDepthStencilState.Get();
}
uint32_t RenderPipeline::GetMtlVertexBufferIndex(VertexBufferSlot slot) const {

View File

@ -17,6 +17,8 @@
#include "dawn_native/Sampler.h"
#include "common/NSRef.h"
#import <Metal/Metal.h>
namespace dawn_native { namespace metal {
@ -31,9 +33,8 @@ namespace dawn_native { namespace metal {
private:
Sampler(Device* device, const SamplerDescriptor* descriptor);
~Sampler() override;
id<MTLSamplerState> mMtlSamplerState = nil;
NSPRef<id<MTLSamplerState>> mMtlSamplerState;
};
}} // namespace dawn_native::metal

View File

@ -62,7 +62,8 @@ namespace dawn_native { namespace metal {
Sampler::Sampler(Device* device, const SamplerDescriptor* descriptor)
: SamplerBase(device, descriptor) {
MTLSamplerDescriptor* mtlDesc = [MTLSamplerDescriptor new];
NSRef<MTLSamplerDescriptor> mtlDescRef = AcquireNSRef([MTLSamplerDescriptor new]);
MTLSamplerDescriptor* mtlDesc = mtlDescRef.Get();
mtlDesc.minFilter = FilterModeToMinMagFilter(descriptor->minFilter);
mtlDesc.magFilter = FilterModeToMinMagFilter(descriptor->magFilter);
@ -83,17 +84,12 @@ namespace dawn_native { namespace metal {
// Metal debug device errors.
}
mMtlSamplerState = [device->GetMTLDevice() newSamplerStateWithDescriptor:mtlDesc];
[mtlDesc release];
}
Sampler::~Sampler() {
[mMtlSamplerState release];
mMtlSamplerState =
AcquireNSPRef([device->GetMTLDevice() newSamplerStateWithDescriptor:mtlDesc]);
}
id<MTLSamplerState> Sampler::GetMTLSamplerState() {
return mMtlSamplerState;
return mMtlSamplerState.Get();
}
}} // namespace dawn_native::metal

View File

@ -17,10 +17,11 @@
#include "dawn_native/ShaderModule.h"
#import <Metal/Metal.h>
#include "common/NSRef.h"
#include "dawn_native/Error.h"
#import <Metal/Metal.h>
namespace spirv_cross {
class CompilerMSL;
}
@ -37,11 +38,8 @@ namespace dawn_native { namespace metal {
const ShaderModuleDescriptor* descriptor);
struct MetalFunctionData {
id<MTLFunction> function = nil;
NSPRef<id<MTLFunction>> function;
bool needsStorageBufferLength;
~MetalFunctionData() {
[function release];
}
};
MaybeError CreateFunction(const char* entryPointName,
SingleShaderStage stage,

View File

@ -142,7 +142,6 @@ namespace dawn_native { namespace metal {
{
// SPIRV-Cross also supports re-ordering attributes but it seems to do the correct thing
// by default.
NSString* mslSource;
std::string msl = compiler.compile();
// Some entry point names are forbidden in MSL so SPIRV-Cross modifies them. Query the
@ -159,14 +158,17 @@ namespace dawn_native { namespace metal {
#pragma clang diagnostic ignored "-Wall"
#endif
)" + msl;
mslSource = [[NSString alloc] initWithUTF8String:msl.c_str()];
NSRef<NSString> mslSource =
AcquireNSRef([[NSString alloc] initWithUTF8String:msl.c_str()]);
auto mtlDevice = ToBackend(GetDevice())->GetMTLDevice();
NSError* error = nil;
id<MTLLibrary> library = [mtlDevice newLibraryWithSource:mslSource
options:nil
error:&error];
if (error != nil) {
NSError* error = nullptr;
NSPRef<id<MTLLibrary>> library =
AcquireNSPRef([mtlDevice newLibraryWithSource:mslSource.Get()
options:nullptr
error:&error]);
if (error != nullptr) {
if (error.code != MTLLibraryErrorCompileWarning) {
const char* errorString = [error.localizedDescription UTF8String];
return DAWN_VALIDATION_ERROR(std::string("Unable to create library object: ") +
@ -174,9 +176,9 @@ namespace dawn_native { namespace metal {
}
}
NSString* name = [[NSString alloc] initWithUTF8String:modifiedEntryPointName.c_str()];
out->function = [library newFunctionWithName:name];
[library release];
NSRef<NSString> name =
AcquireNSRef([[NSString alloc] initWithUTF8String:modifiedEntryPointName.c_str()]);
out->function = AcquireNSPRef([*library newFunctionWithName:name.Get()]);
}
out->needsStorageBufferLength = compiler.needs_buffer_size_buffer();

View File

@ -17,6 +17,8 @@
#include "dawn_native/StagingBuffer.h"
#include "common/NSRef.h"
#import <Metal/Metal.h>
namespace dawn_native { namespace metal {
@ -26,7 +28,6 @@ namespace dawn_native { namespace metal {
class StagingBuffer : public StagingBufferBase {
public:
StagingBuffer(size_t size, Device* device);
~StagingBuffer() override;
id<MTLBuffer> GetBufferHandle() const;
@ -34,7 +35,7 @@ namespace dawn_native { namespace metal {
private:
Device* mDevice;
id<MTLBuffer> mBuffer = nil;
NSPRef<id<MTLBuffer>> mBuffer;
};
}} // namespace dawn_native::metal

View File

@ -23,14 +23,15 @@ namespace dawn_native { namespace metal {
MaybeError StagingBuffer::Initialize() {
const size_t bufferSize = GetSize();
mBuffer = [mDevice->GetMTLDevice() newBufferWithLength:bufferSize
options:MTLResourceStorageModeShared];
mBuffer = AcquireNSPRef([mDevice->GetMTLDevice()
newBufferWithLength:bufferSize
options:MTLResourceStorageModeShared]);
if (mBuffer == nil) {
if (mBuffer == nullptr) {
return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer.");
}
mMappedPointer = [mBuffer contents];
mMappedPointer = [*mBuffer contents];
if (mMappedPointer == nullptr) {
return DAWN_INTERNAL_ERROR("Unable to map staging buffer.");
}
@ -38,13 +39,8 @@ namespace dawn_native { namespace metal {
return {};
}
StagingBuffer::~StagingBuffer() {
[mBuffer release];
mBuffer = nil;
}
id<MTLBuffer> StagingBuffer::GetBufferHandle() const {
return mBuffer;
return mBuffer.Get();
}
}} // namespace dawn_native::metal

View File

@ -17,6 +17,8 @@
#include "dawn_native/SwapChain.h"
#include "common/NSRef.h"
@class CAMetalLayer;
@protocol CAMetalDrawable;
@ -47,9 +49,9 @@ namespace dawn_native { namespace metal {
using NewSwapChainBase::NewSwapChainBase;
MaybeError Initialize(NewSwapChainBase* previousSwapChain);
CAMetalLayer* mLayer = nullptr;
NSRef<CAMetalLayer> mLayer;
id<CAMetalDrawable> mCurrentDrawable = nil;
NSPRef<id<CAMetalDrawable>> mCurrentDrawable;
Ref<Texture> mTexture;
MaybeError PresentImpl() override;

View File

@ -92,15 +92,15 @@ namespace dawn_native { namespace metal {
CGSize size = {};
size.width = GetWidth();
size.height = GetHeight();
[mLayer setDrawableSize:size];
[*mLayer setDrawableSize:size];
[mLayer setFramebufferOnly:(GetUsage() == wgpu::TextureUsage::RenderAttachment)];
[mLayer setDevice:ToBackend(GetDevice())->GetMTLDevice()];
[mLayer setPixelFormat:MetalPixelFormat(GetFormat())];
[*mLayer setFramebufferOnly:(GetUsage() == wgpu::TextureUsage::RenderAttachment)];
[*mLayer setDevice:ToBackend(GetDevice())->GetMTLDevice()];
[*mLayer setPixelFormat:MetalPixelFormat(GetFormat())];
#if defined(DAWN_PLATFORM_MACOS)
if (@available(macos 10.13, *)) {
[mLayer setDisplaySyncEnabled:(GetPresentMode() != wgpu::PresentMode::Immediate)];
[*mLayer setDisplaySyncEnabled:(GetPresentMode() != wgpu::PresentMode::Immediate)];
}
#endif // defined(DAWN_PLATFORM_MACOS)
@ -110,40 +110,36 @@ namespace dawn_native { namespace metal {
}
MaybeError SwapChain::PresentImpl() {
ASSERT(mCurrentDrawable != nil);
[mCurrentDrawable present];
ASSERT(mCurrentDrawable != nullptr);
[*mCurrentDrawable present];
mTexture->Destroy();
mTexture = nullptr;
[mCurrentDrawable release];
mCurrentDrawable = nil;
mCurrentDrawable = nullptr;
return {};
}
ResultOrError<TextureViewBase*> SwapChain::GetCurrentTextureViewImpl() {
ASSERT(mCurrentDrawable == nil);
mCurrentDrawable = [mLayer nextDrawable];
[mCurrentDrawable retain];
ASSERT(mCurrentDrawable == nullptr);
mCurrentDrawable = [*mLayer nextDrawable];
TextureDescriptor textureDesc = GetSwapChainBaseTextureDescriptor(this);
// mTexture will add a reference to mCurrentDrawable.texture to keep it alive.
mTexture =
AcquireRef(new Texture(ToBackend(GetDevice()), &textureDesc, mCurrentDrawable.texture));
mTexture = AcquireRef(
new Texture(ToBackend(GetDevice()), &textureDesc, [*mCurrentDrawable texture]));
return mTexture->CreateView(nullptr);
}
void SwapChain::DetachFromSurfaceImpl() {
ASSERT((mTexture.Get() == nullptr) == (mCurrentDrawable == nil));
ASSERT((mTexture.Get() == nullptr) == (mCurrentDrawable == nullptr));
if (mTexture.Get() != nullptr) {
mTexture->Destroy();
mTexture = nullptr;
[mCurrentDrawable release];
mCurrentDrawable = nil;
mCurrentDrawable = nullptr;
}
}

View File

@ -17,9 +17,11 @@
#include "dawn_native/Texture.h"
#include "common/NSRef.h"
#include "dawn_native/DawnNative.h"
#include <IOSurface/IOSurfaceRef.h>
#import <Metal/Metal.h>
#include "dawn_native/DawnNative.h"
namespace dawn_native { namespace metal {
@ -34,7 +36,9 @@ namespace dawn_native { namespace metal {
class Texture final : public TextureBase {
public:
Texture(Device* device, const TextureDescriptor* descriptor);
Texture(Device* device, const TextureDescriptor* descriptor, id<MTLTexture> mtlTexture);
Texture(Device* device,
const TextureDescriptor* descriptor,
NSPRef<id<MTLTexture>> mtlTexture);
Texture(Device* device,
const ExternalImageDescriptor* descriptor,
IOSurfaceRef ioSurface,
@ -51,7 +55,7 @@ namespace dawn_native { namespace metal {
MaybeError ClearTexture(const SubresourceRange& range, TextureBase::ClearValue clearValue);
id<MTLTexture> mMtlTexture = nil;
NSPRef<id<MTLTexture>> mMtlTexture;
};
class TextureView final : public TextureViewBase {
@ -61,9 +65,7 @@ namespace dawn_native { namespace metal {
id<MTLTexture> GetMTLTexture();
private:
~TextureView() override;
id<MTLTexture> mMtlTextureView = nil;
NSPRef<id<MTLTexture>> mMtlTextureView;
};
}} // namespace dawn_native::metal

View File

@ -298,9 +298,10 @@ namespace dawn_native { namespace metal {
return {};
}
MTLTextureDescriptor* CreateMetalTextureDescriptor(DeviceBase* device,
const TextureDescriptor* descriptor) {
MTLTextureDescriptor* mtlDesc = [MTLTextureDescriptor new];
NSRef<MTLTextureDescriptor> CreateMetalTextureDescriptor(DeviceBase* device,
const TextureDescriptor* descriptor) {
NSRef<MTLTextureDescriptor> mtlDescRef = AcquireNSRef([MTLTextureDescriptor new]);
MTLTextureDescriptor* mtlDesc = mtlDescRef.Get();
mtlDesc.width = descriptor->size.width;
mtlDesc.height = descriptor->size.height;
@ -338,14 +339,14 @@ namespace dawn_native { namespace metal {
UNREACHABLE();
}
return mtlDesc;
return mtlDescRef;
}
Texture::Texture(Device* device, const TextureDescriptor* descriptor)
: TextureBase(device, descriptor, TextureState::OwnedInternal) {
MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(device, descriptor);
mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc];
[mtlDesc release];
NSRef<MTLTextureDescriptor> mtlDesc = CreateMetalTextureDescriptor(device, descriptor);
mMtlTexture =
AcquireNSPRef([device->GetMTLDevice() newTextureWithDescriptor:mtlDesc.Get()]);
if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
device->ConsumedError(
@ -353,9 +354,11 @@ namespace dawn_native { namespace metal {
}
}
Texture::Texture(Device* device, const TextureDescriptor* descriptor, id<MTLTexture> mtlTexture)
: TextureBase(device, descriptor, TextureState::OwnedInternal), mMtlTexture(mtlTexture) {
[mMtlTexture retain];
Texture::Texture(Device* device,
const TextureDescriptor* descriptor,
NSPRef<id<MTLTexture>> mtlTexture)
: TextureBase(device, descriptor, TextureState::OwnedInternal),
mMtlTexture(std::move(mtlTexture)) {
}
Texture::Texture(Device* device,
@ -365,13 +368,13 @@ namespace dawn_native { namespace metal {
: TextureBase(device,
reinterpret_cast<const TextureDescriptor*>(descriptor->cTextureDescriptor),
TextureState::OwnedInternal) {
MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(
NSRef<MTLTextureDescriptor> mtlDesc = CreateMetalTextureDescriptor(
device, reinterpret_cast<const TextureDescriptor*>(descriptor->cTextureDescriptor));
mtlDesc.storageMode = kIOSurfaceStorageMode;
mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc
iosurface:ioSurface
plane:plane];
[mtlDesc release];
[*mtlDesc setStorageMode:kIOSurfaceStorageMode];
mMtlTexture = AcquireNSPRef([device->GetMTLDevice() newTextureWithDescriptor:mtlDesc.Get()
iosurface:ioSurface
plane:plane]);
SetIsSubresourceContentInitialized(descriptor->isInitialized, GetAllSubresources());
}
@ -381,14 +384,11 @@ namespace dawn_native { namespace metal {
}
void Texture::DestroyImpl() {
if (GetTextureState() == TextureState::OwnedInternal) {
[mMtlTexture release];
mMtlTexture = nil;
}
mMtlTexture = nullptr;
}
id<MTLTexture> Texture::GetMTLTexture() {
return mMtlTexture;
return mMtlTexture.Get();
}
MaybeError Texture::ClearTexture(const SubresourceRange& range,
@ -419,8 +419,11 @@ namespace dawn_native { namespace metal {
continue;
}
MTLRenderPassDescriptor* descriptor =
// Note that this creates a descriptor that's autoreleased so we don't use
// AcquireNSRef
NSRef<MTLRenderPassDescriptor> descriptorRef =
[MTLRenderPassDescriptor renderPassDescriptor];
MTLRenderPassDescriptor* descriptor = descriptorRef.Get();
// At least one aspect needs clearing. Iterate the aspects individually to
// determine which to clear.
@ -462,7 +465,7 @@ namespace dawn_native { namespace metal {
// Create multiple render passes with each subresource as a color attachment to
// clear them all. Only do this for array layers to ensure all attachments have
// the same size.
MTLRenderPassDescriptor* descriptor = nil;
NSRef<MTLRenderPassDescriptor> descriptor;
uint32_t attachment = 0;
for (uint32_t arrayLayer = range.baseArrayLayer;
@ -474,30 +477,33 @@ namespace dawn_native { namespace metal {
continue;
}
if (descriptor == nil) {
if (descriptor == nullptr) {
// Note that this creates a descriptor that's autoreleased so we don't
// use AcquireNSRef
descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
}
descriptor.colorAttachments[attachment].texture = GetMTLTexture();
descriptor.colorAttachments[attachment].loadAction = MTLLoadActionClear;
descriptor.colorAttachments[attachment].storeAction = MTLStoreActionStore;
descriptor.colorAttachments[attachment].clearColor =
[*descriptor colorAttachments][attachment].texture = GetMTLTexture();
[*descriptor colorAttachments][attachment].loadAction = MTLLoadActionClear;
[*descriptor colorAttachments][attachment].storeAction =
MTLStoreActionStore;
[*descriptor colorAttachments][attachment].clearColor =
MTLClearColorMake(dClearColor, dClearColor, dClearColor, dClearColor);
descriptor.colorAttachments[attachment].level = level;
descriptor.colorAttachments[attachment].slice = arrayLayer;
[*descriptor colorAttachments][attachment].level = level;
[*descriptor colorAttachments][attachment].slice = arrayLayer;
attachment++;
if (attachment == kMaxColorAttachments) {
attachment = 0;
commandContext->BeginRender(descriptor);
commandContext->BeginRender(descriptor.Get());
commandContext->EndRender();
descriptor = nil;
descriptor = nullptr;
}
}
if (descriptor != nil) {
commandContext->BeginRender(descriptor);
if (descriptor != nullptr) {
commandContext->BeginRender(descriptor.Get());
commandContext->EndRender();
}
}
@ -591,9 +597,9 @@ namespace dawn_native { namespace metal {
id<MTLTexture> mtlTexture = ToBackend(texture)->GetMTLTexture();
if (!UsageNeedsTextureView(texture->GetUsage())) {
mMtlTextureView = nil;
mMtlTextureView = nullptr;
} else if (!RequiresCreatingNewTextureView(texture, descriptor)) {
mMtlTextureView = [mtlTexture retain];
mMtlTextureView = mtlTexture;
} else {
MTLPixelFormat format = MetalPixelFormat(descriptor->format);
if (descriptor->aspect == wgpu::TextureAspect::StencilOnly) {
@ -616,19 +622,16 @@ namespace dawn_native { namespace metal {
auto arrayLayerRange =
NSMakeRange(descriptor->baseArrayLayer, descriptor->arrayLayerCount);
mMtlTextureView = [mtlTexture newTextureViewWithPixelFormat:format
mMtlTextureView =
AcquireNSPRef([mtlTexture newTextureViewWithPixelFormat:format
textureType:textureViewType
levels:mipLevelRange
slices:arrayLayerRange];
slices:arrayLayerRange]);
}
}
TextureView::~TextureView() {
[mMtlTextureView release];
}
id<MTLTexture> TextureView::GetMTLTexture() {
ASSERT(mMtlTextureView != nil);
return mMtlTextureView;
ASSERT(mMtlTextureView != nullptr);
return mMtlTextureView.Get();
}
}} // namespace dawn_native::metal

View File

@ -118,7 +118,6 @@ TEST(Ref, Gets) {
test->Release();
EXPECT_EQ(test.Get(), original);
EXPECT_EQ(&*test, original);
EXPECT_EQ(test->GetThis(), original);
}
@ -127,7 +126,6 @@ TEST(Ref, DefaultsToNull) {
Ref<RCTest> test;
EXPECT_EQ(test.Get(), nullptr);
EXPECT_EQ(&*test, nullptr);
EXPECT_EQ(test->GetThis(), nullptr);
}