Metal:在一个渲染过程中使用多个 MTLRenderCommandEncoder
Posted
技术标签:
【中文标题】Metal:在一个渲染过程中使用多个 MTLRenderCommandEncoder【英文标题】:Metal: using multiple MTLRenderCommandEncoder in one rendering pass 【发布时间】:2016-03-09 18:39:30 【问题描述】:我开始使用 Metal(目前我的应用程序正在使用 OpenGL)。我正在尝试检查如何在一个渲染过程中使用多个管道状态(多个金属功能)进行渲染。问题是总是只实际绘制最后一个 MTLRenderCommandEncoder。以下是我的代码:
@property (nonatomic, strong) UIView* metalView;
@property (nonatomic, strong) id <MTLDevice> mtlDevice;
@property (nonatomic, strong) id <MTLCommandQueue> mtlCommandQueue;
@property (nonatomic, strong) MTLRenderPassDescriptor *mtlRenderPassDescriptor;
@property (nonatomic, strong) CAMetalLayer *metalLayer;
@property (nonatomic, strong) id <CAMetalDrawable> frameDrawable;
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, strong) MTLRenderPipelineDescriptor *renderPipelineDescriptor;
@property (nonatomic, strong) MTLRenderPipelineDescriptor *renderPipelineDescriptorb;
@property (nonatomic, strong) id <MTLRenderPipelineState> renderPipelineState;
@property (nonatomic, strong) id <MTLRenderPipelineState> renderPipelineStateb;
@property (nonatomic, strong) id <MTLBuffer> object;
@property (nonatomic, strong) id <MTLBuffer> objectb;
@end
@implementation MetalGPUAdapter
- (void)setupGraphics
self.mtlDevice = MTLCreateSystemDefaultDevice();
self.mtlCommandQueue = [self.mtlDevice newCommandQueue];
self.metalLayer = [CAMetalLayer layer];
[self.metalLayer setDevice:self.mtlDevice];
[self.metalLayer setPixelFormat:MTLPixelFormatBGRA8Unorm];
self.metalLayer.framebufferOnly = YES;
[self.metalLayer setFrame:self.gpuViewController.view.layer.frame];
self.metalView = [[UIView alloc] initWithFrame:self.gpuViewController.view.frame];
[self.gpuViewController.view addSubview:self.metalView];
[self.gpuViewController.view sendSubviewToBack:self.metalView];
[self.metalView.layer addSublayer:self.metalLayer];
[self.metalView setOpaque:YES];
[self.metalView setBackgroundColor:nil];
[self.metalView setContentScaleFactor:[UIScreen mainScreen].scale];
// Create a reusable pipeline
self.renderPipelineDescriptor = [MTLRenderPipelineDescriptor new];
self.renderPipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
self.renderPipelineDescriptorb = [MTLRenderPipelineDescriptor new];
self.renderPipelineDescriptorb.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
id <MTLLibrary> lib = [self.mtlDevice newDefaultLibrary];
self.renderPipelineDescriptor.vertexFunction = [lib newFunctionWithName:@"VertexColor"];
self.renderPipelineDescriptor.fragmentFunction = [lib newFunctionWithName:@"FragmentColor"];
self.renderPipelineState = [self.mtlDevice newRenderPipelineStateWithDescriptor:self.renderPipelineDescriptor error: nil];
self.renderPipelineDescriptorb.vertexFunction = [lib newFunctionWithName:@"VertexColorb"];
self.renderPipelineDescriptorb.fragmentFunction = [lib newFunctionWithName:@"FragmentColorb"];
self.renderPipelineStateb = [self.mtlDevice newRenderPipelineStateWithDescriptor:self.renderPipelineDescriptorb error: nil];
Triangle triangle[3] = -1.0f, -1.0f , 1.0f, -1.0f , 0.0f, 0.0f ;
Triangle square[4] = -1.0f, 0.0f , -1.0f, 1.0f , 1.0f, 0.0f , 1.0f, 1.0f ;
self.object = [self.mtlDevice newBufferWithBytes:&triangle length:sizeof(Triangle[3]) options:MTLResourceOptionCPUCacheModeDefault];
self.objectb = [self.mtlDevice newBufferWithBytes:&square length:sizeof(Triangle[4]) options:MTLResourceOptionCPUCacheModeDefault];
self.displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderScene)];
[self.displayLink addToRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
- (void)renderScene
id <MTLCommandBuffer>mtlCommandBuffer = [self.mtlCommandQueue commandBuffer];
while (!self.frameDrawable)
self.frameDrawable = [self.metalLayer nextDrawable];
if (!self.mtlRenderPassDescriptor)
self.mtlRenderPassDescriptor = [MTLRenderPassDescriptor new];
self.mtlRenderPassDescriptor.colorAttachments[0].texture = self.frameDrawable.texture;
self.mtlRenderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
self.mtlRenderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.75, 0.5, 1.0, 1.0);
self.mtlRenderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
id <MTLRenderCommandEncoder> renderCommand = [mtlCommandBuffer renderCommandEncoderWithDescriptor: self.mtlRenderPassDescriptor];
[renderCommand setRenderPipelineState:self.renderPipelineStateb];
[renderCommand setVertexBuffer:self.object offset:0 atIndex:0];
[renderCommand drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
[renderCommand endEncoding];
id <MTLRenderCommandEncoder> renderCommandb = [mtlCommandBuffer renderCommandEncoderWithDescriptor: self.mtlRenderPassDescriptor];
[renderCommandb setRenderPipelineState:self.renderPipelineStateb];
[renderCommandb setVertexBuffer:self.objectb offset:0 atIndex:0];
[renderCommandb drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
[renderCommandb endEncoding];
[mtlCommandBuffer presentDrawable: self.frameDrawable];
[mtlCommandBuffer commit];
self.mtlRenderPassDescriptor = nil;
self.frameDrawable = nil;
我的着色器:
#include <metal_stdlib>
using namespace metal;
typedef struct
float2 position;
Triangle;
typedef struct
float4 position [[position]];
TriangleOutput;
vertex TriangleOutput VertexColor(const device Triangle *Vertices [[buffer(0)]], const uint index [[vertex_id]])
TriangleOutput out;
out.position = float4(Vertices[index].position, 0.0, 1.0);
return out;
vertex TriangleOutput VertexColorb(const device Triangle *Vertices [[buffer(0)]], const uint index [[vertex_id]])
TriangleOutput out;
out.position = float4(Vertices[index].position, 0.0, 1.0);
return out;
fragment half4 FragmentColor(void)
return half4(1.0, 0.0, 0.0, 1.0);
fragment half4 FragmentColorb(void)
return half4(1.0, 0.0, 1.0, 1.0);
【问题讨论】:
【参考方案1】:您正在尝试将多个渲染通道编码到一个命令缓冲区中(一个编码器 = 一个通道)。这是有效的,但您需要注意加载和存储操作以使其正常工作。由于您的渲染通道描述符配置为在两次通道之前清除,因此您的帧缓冲区附件基本上在第二通道之前被擦除。相反,您应该在开始第二次传递之前在传递描述符上设置Load
加载操作。
【讨论】:
那么我应该只使用一个编码器吗?在这种情况下,如何更改缓冲区之间的着色器? 要在单遍中更改着色器程序,只需在相应的绘制调用之前在编码器上设置适当的渲染管线状态。当您发出绘图调用时,编码器上设置的状态基本上是“快照”,因此您可以在一次传递中设置管道状态任意多次。以上是关于Metal:在一个渲染过程中使用多个 MTLRenderCommandEncoder的主要内容,如果未能解决你的问题,请参考以下文章
使用 SceneKit 通过 Metal 计算管道渲染几何图形