我编写了一个由metal渲染的视图,当视图初始化时,我为该视图创建了一个MTLCommandQueue。但是如果我同时在屏幕上放置两个视图,程序就会崩溃。谁能告诉我为什么?
* thread #9, queue = 'com.Metal.CompletionQueueDispatch', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
* frame #0: 0x00000002299221f0 libMTLHud.dylib`Renderer::DrawText(char const*, int, unsigned int) + 340
frame #1: 0x0000000229924d68 libMTLHud.dylib`Overlay::onPresent(id<CAMetalDrawable>) + 2136
frame #2: 0x0000000229923ab0 libMTLHud.dylib`CAMetalDrawable_present(void (*)(), objc_object*, objc_selector*) + 68
frame #3: 0x0000000229923dc0 libMTLHud.dylib`invocation function for block in void replaceMethod<void>(objc_class*, objc_selector*, void (*)(void (*)(), objc_object*, objc_selector*)) + 56
frame #4: 0x00000001a95f5c04 Metal`__45-[_MTLCommandBuffer presentDrawable:options:]_block_invoke + 104
frame #5: 0x00000001a950ff48 Metal`MTLDispatchListApply + 52
frame #6: 0x00000001a9505b84 Metal`-[_MTLCommandBuffer didScheduleWithStartTime:endTime:error:] + 300
frame #7: 0x0000000215d21ed0 IOGPU`IOGPUNotificationQueueDispatchAvailableCompletionNotifications + 128
frame #8: 0x0000000215d15880 IOGPU`__IOGPUNotificationQueueSetDispatchQueue_block_invoke + 64
frame #9: 0x0000000102fd611c libdispatch.dylib`_dispatch_client_callout4 + 20
frame #10: 0x0000000102ff43ac libdispatch.dylib`_dispatch_mach_msg_invoke + 516
frame #11: 0x0000000102fddea8 libdispatch.dylib`_dispatch_lane_serial_drain + 376
frame #12: 0x0000000102ff5370 libdispatch.dylib`_dispatch_mach_invoke + 492
frame #13: 0x0000000102fddea8 libdispatch.dylib`_dispatch_lane_serial_drain + 376
frame #14: 0x0000000102fdee68 libdispatch.dylib`_dispatch_lane_invoke + 472
frame #15: 0x0000000102fddea8 libdispatch.dylib`_dispatch_lane_serial_drain + 376
frame #16: 0x0000000102fdee34 libdispatch.dylib`_dispatch_lane_invoke + 420
frame #17: 0x0000000102febcbc libdispatch.dylib`_dispatch_workloop_worker_thread + 740
frame #18: 0x00000001fb7e3df8 libsystem_pthread.dylib`_pthread_wqthread + 288
我尝试在渲染时添加锁,但不起作用。
您可以为两个视图使用一个共享队列,但每个视图都必须使用其渲染句柄和命令缓冲区的副本。在这里,我为您准备了一个小片段(标头和实现)作为示例,您可以将其用作起始源。
MetalView.h
#import <Metal/Metal.h>
#import <MetalKit/MTKView.h>
#define SHARED_CLASS(classname) \
+ (id) Shared##classname { \
static dispatch_once_t pred = 0; \
static id _sharedObject = nil; \
dispatch_once(&pred, ^{ \
_sharedObject = [[self alloc] init]; \
}); \
return _sharedObject; \
}
NS_ASSUME_NONNULL_BEGIN
@interface MetalQueue : NSObject
@property (atomic, readonly) id<MTLDevice> device;
@property (atomic, readonly) id<MTLCommandQueue> queue;
+ (instancetype)SharedMetalQueue;
@end
NS_ASSUME_NONNULL_END
NS_ASSUME_NONNULL_BEGIN
@interface MetalView : MTKView<MTKViewDelegate>
@end
NS_ASSUME_NONNULL_END
MetalQueue 具有在多个视图之间共享的共享单例属性。
MetalView.m
#import "MetalView.h"
@implementation MetalQueue
- (instancetype)init {
if (self) {
_device = MTLCreateSystemDefaultDevice();
assert(_device);
_queue = [_device newCommandQueue];
}
return self;
}
SHARED_CLASS(MetalQueue);
@end
@implementation MetalView
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self) {
self.delegate = self;
self.framebufferOnly = true;
self.enableSetNeedsDisplay = true;
[self setPaused: true];
}
return [super initWithCoder: coder];
}
- (void)drawInMTKView:(nonnull MTKView *)view {
view.device = [MetalQueue SharedMetalQueue].device;
id<MTLCommandBuffer> commandBuffer = [[MetalQueue SharedMetalQueue].queue commandBuffer];
id<MTLCommandEncoder> commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor: view.currentRenderPassDescriptor];
if (view.currentDrawable) {
[commandEncoder endEncoding];
if ([view tag] == 0) {
view.clearColor = MTLClearColorMake(1.0, 0.0, 0.0, 1.0);
}
else {
view.clearColor = MTLClearColorMake(0.0, 1.0, 0.0, 1.0);
}
[commandBuffer presentDrawable:view.currentDrawable];
[commandBuffer commit];
}
}
- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
}
@end
MetalView 使用自渲染委托实现。