Metal:在一个渲染过程中使用多个MTLRenderCommandEncoder

时间:2016-03-09 18:39:31

标签: ios metal

我开始玩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 个答案:

答案 0 :(得分:2)

您正在尝试将多个渲染过程编码为单个命令缓冲区(一个编码器=一次传递)。这是有效的,但您需要注意您的加载和存储操作才能使其正常工作。由于渲染过程描述符配置为在两次传递之前清除,因此帧缓冲附件基本上在第二次传递之前被擦除。相反,您应该在开始第二次传递之前在传递描述符上设置Load加载操作。