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", "Log.h",
"Math.cpp", "Math.cpp",
"Math.h", "Math.h",
"NSRef.h",
"PlacementAllocated.h", "PlacementAllocated.h",
"Platform.h", "Platform.h",
"RefBase.h", "RefBase.h",

View File

@ -29,6 +29,7 @@ target_sources(dawn_common PRIVATE
"Log.h" "Log.h"
"Math.cpp" "Math.cpp"
"Math.h" "Math.h"
"NSRef.h"
"PlacementAllocated.h" "PlacementAllocated.h"
"Platform.h" "Platform.h"
"RefBase.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; 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 { const T operator->() const {
return mValue; return mValue;
} }
@ -166,6 +158,11 @@ class RefBase {
return value; return value;
} }
void Acquire(T value) {
Release();
mValue = value;
}
private: private:
// Friend is needed so that instances of RefBase<U> can call Reference and Release on // Friend is needed so that instances of RefBase<U> can call Reference and Release on
// RefBase<T>. // RefBase<T>.

View File

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

View File

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

View File

@ -15,6 +15,7 @@
#ifndef DAWNNATIVE_METAL_BUFFERMTL_H_ #ifndef DAWNNATIVE_METAL_BUFFERMTL_H_
#define DAWNNATIVE_METAL_BUFFERMTL_H_ #define DAWNNATIVE_METAL_BUFFERMTL_H_
#include "common/NSRef.h"
#include "common/SerialQueue.h" #include "common/SerialQueue.h"
#include "dawn_native/Buffer.h" #include "dawn_native/Buffer.h"
@ -53,7 +54,7 @@ namespace dawn_native { namespace metal {
void InitializeToZero(CommandRecordingContext* commandContext); void InitializeToZero(CommandRecordingContext* commandContext);
void ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue); void ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue);
id<MTLBuffer> mMtlBuffer = nil; NSPRef<id<MTLBuffer>> mMtlBuffer;
}; };
}} // namespace dawn_native::metal }} // 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"); return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation is too large");
} }
mMtlBuffer = [ToBackend(GetDevice())->GetMTLDevice() newBufferWithLength:currentSize mMtlBuffer.Acquire([ToBackend(GetDevice())->GetMTLDevice()
options:storageMode]; newBufferWithLength:currentSize
if (mMtlBuffer == nil) { options:storageMode]);
if (mMtlBuffer == nullptr) {
return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation failed"); return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation failed");
} }
@ -107,7 +108,7 @@ namespace dawn_native { namespace metal {
} }
id<MTLBuffer> Buffer::GetMTLBuffer() const { id<MTLBuffer> Buffer::GetMTLBuffer() const {
return mMtlBuffer; return mMtlBuffer.Get();
} }
bool Buffer::IsCPUWritableAtCreation() const { bool Buffer::IsCPUWritableAtCreation() const {
@ -128,7 +129,7 @@ namespace dawn_native { namespace metal {
} }
void* Buffer::GetMappedPointerImpl() { void* Buffer::GetMappedPointerImpl() {
return [mMtlBuffer contents]; return [*mMtlBuffer contents];
} }
void Buffer::UnmapImpl() { void Buffer::UnmapImpl() {
@ -136,8 +137,7 @@ namespace dawn_native { namespace metal {
} }
void Buffer::DestroyImpl() { void Buffer::DestroyImpl() {
[mMtlBuffer release]; mMtlBuffer = nullptr;
mMtlBuffer = nil;
} }
void Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) { void Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
@ -196,7 +196,7 @@ namespace dawn_native { namespace metal {
return; return;
} }
[commandContext->EnsureBlit() fillBuffer:mMtlBuffer [commandContext->EnsureBlit() fillBuffer:mMtlBuffer.Get()
range:NSMakeRange(0, GetSize()) range:NSMakeRange(0, GetSize())
value:clearValue]; value:clearValue];
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,6 +17,8 @@
#include "dawn_native/QuerySet.h" #include "dawn_native/QuerySet.h"
#include "common/NSRef.h"
#import <Metal/Metal.h> #import <Metal/Metal.h>
namespace dawn_native { namespace metal { namespace dawn_native { namespace metal {
@ -40,9 +42,11 @@ namespace dawn_native { namespace metal {
// Dawn API // Dawn API
void DestroyImpl() override; 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), id<MTLCounterSampleBuffer> mCounterSampleBuffer API_AVAILABLE(macos(10.15),
ios(14.0)) = nil; ios(14.0)) = nullptr;
}; };
}} // namespace dawn_native::metal }} // namespace dawn_native::metal

View File

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

View File

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

View File

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

View File

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

View File

@ -17,10 +17,11 @@
#include "dawn_native/ShaderModule.h" #include "dawn_native/ShaderModule.h"
#import <Metal/Metal.h> #include "common/NSRef.h"
#include "dawn_native/Error.h" #include "dawn_native/Error.h"
#import <Metal/Metal.h>
namespace spirv_cross { namespace spirv_cross {
class CompilerMSL; class CompilerMSL;
} }
@ -37,11 +38,8 @@ namespace dawn_native { namespace metal {
const ShaderModuleDescriptor* descriptor); const ShaderModuleDescriptor* descriptor);
struct MetalFunctionData { struct MetalFunctionData {
id<MTLFunction> function = nil; NSPRef<id<MTLFunction>> function;
bool needsStorageBufferLength; bool needsStorageBufferLength;
~MetalFunctionData() {
[function release];
}
}; };
MaybeError CreateFunction(const char* entryPointName, MaybeError CreateFunction(const char* entryPointName,
SingleShaderStage stage, 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 // SPIRV-Cross also supports re-ordering attributes but it seems to do the correct thing
// by default. // by default.
NSString* mslSource;
std::string msl = compiler.compile(); std::string msl = compiler.compile();
// Some entry point names are forbidden in MSL so SPIRV-Cross modifies them. Query the // 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" #pragma clang diagnostic ignored "-Wall"
#endif #endif
)" + msl; )" + msl;
mslSource = [[NSString alloc] initWithUTF8String:msl.c_str()];
NSRef<NSString> mslSource =
AcquireNSRef([[NSString alloc] initWithUTF8String:msl.c_str()]);
auto mtlDevice = ToBackend(GetDevice())->GetMTLDevice(); auto mtlDevice = ToBackend(GetDevice())->GetMTLDevice();
NSError* error = nil; NSError* error = nullptr;
id<MTLLibrary> library = [mtlDevice newLibraryWithSource:mslSource NSPRef<id<MTLLibrary>> library =
options:nil AcquireNSPRef([mtlDevice newLibraryWithSource:mslSource.Get()
error:&error]; options:nullptr
if (error != nil) { error:&error]);
if (error != nullptr) {
if (error.code != MTLLibraryErrorCompileWarning) { if (error.code != MTLLibraryErrorCompileWarning) {
const char* errorString = [error.localizedDescription UTF8String]; const char* errorString = [error.localizedDescription UTF8String];
return DAWN_VALIDATION_ERROR(std::string("Unable to create library object: ") + 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()]; NSRef<NSString> name =
out->function = [library newFunctionWithName:name]; AcquireNSRef([[NSString alloc] initWithUTF8String:modifiedEntryPointName.c_str()]);
[library release]; out->function = AcquireNSPRef([*library newFunctionWithName:name.Get()]);
} }
out->needsStorageBufferLength = compiler.needs_buffer_size_buffer(); out->needsStorageBufferLength = compiler.needs_buffer_size_buffer();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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