It is mainly divided into two modules

  • ViewController: Gets the MTKView object and hands over the rendering to the custom rendering loop class HTRender
  • Custom loop rendering class HTRender

ViewController is divided into the following steps

  • Get the MTKView object

    You can change the class of the view to MTKView in the storyboard, and you can create an MTKView object, and you can add it to the view of the controller

  • Set the device of the MTKView

    A MTLDevice object with a GPU, means that we can call the method usually MTLCreateSystemDefaultDevice () to obtain a single object represents the default GPU, create completed, need to decide whether to obtain access to the GPU, and if you don’t succeed, then interrupt rendering process

  • Create a render

    Apple recommends that when developing mental programs, it is best to have the render loop as a separate class in order to more efficiently manage Metal and metal view delegates. After the creation is complete, you also need to determine whether the creation is successful. If the creation is not successful, the rendering process is interrupted

  • Set the View’s delaegate: Set the VIEW’s MTKViewDelegate to the Render object

  • Set frame rate

The view can be triggered by setting the frame rate, different frequencies, and then calling back to the drawInMTKView method in the MTKViewDelegate

Above process code

- (void)viewDidLoad { [super viewDidLoad]; //1. Get _view _view = (MTKView *)self.view; / / 2. Set MTLDevice for _view (must) / / a MTLDevice object with a GPU, means that we can call the method usually MTLCreateSystemDefaultDevice () to obtain a single object represents the default GPU. _view.device = MTLCreateSystemDefaultDevice(); //3. If (! _view.device) { NSLog(@"Metal is not supported on this device"); return; } //4. Create HTRender // separate your render loop: // When we were developing Metal applications, it was very useful to divide the render loop into our own classes. With separate classes, we could better manage the initialization of Metal and the Metal view delegate. _render =[[HTRender alloc]initWithMetalKitView:_view]; //5. Determine whether _render is created successfully if (! _render) { NSLog(@"Renderer failed initialization"); return; } //6. Set MTKView's delegate (CCRender implements MTKView's delegate method) _view.delegate = _render; / / 7. The view can be set according to the view attributes on frame rate (designated time to call drawInMTKView method calls) - view need renders _view. PreferredFramesPerSecond = 60; }Copy the code

Render loop class: HTRender is responsible for implementing the MTKViewDelegate protocol for loop rendering:

  • InitWithMetalKitView function: initialization, need the incoming MTKView object view to obtain access to the GPU, using MTLCommandQueue to create objects, and join MTLCommandBuffer

  • Call -drawinmtkView when the view needs to be rendered:

HTRender.h

#import <Foundation/Foundation.h>
@import MetalKit;

@interface HTRender : NSObject<MTKViewDelegate>

-(id)initWithMetalKitView:(MTKView *)mtkView;


@end

Copy the code

HTRender.m

#import "HTRender.h" @implementation HTRender { id<MTLDevice> _device; id<MTLCommandQueue> _commandQueue; } // struct {float red, green, blue, alpha; } Color; // initialize - (id)initWithMetalKitView:(MTKView *) MTKView {self = [super init]; if(self) { _device = mtkView.device; // The first object that any application needs to interact with the GPU is an object. MTLCommandQueue. // You use MTLCommandQueue to create an object and add it to the MTLCommandBuffer. For each frame, a new MTLCommandBuffer is created and filled with the commands executed by the GPU. _commandQueue = [_device newCommandQueue]; } return self; } // Set Color - (Color)makeFancyColor {//1. Static BOOL growing = YES; //2. Color channel value (0 to 3) static NSUInteger primaryChannel = 0; Static float colorChannels[] = {1.0, 0.0, 0.0, 1.0}; //4. Const float DynamicColorRate = 0.015; If (growing) {NSUInteger dynamicChannelIndex = (primaryChannel+1)%3; ColorChannels [dynamicChannelIndex] += DynamicColorRate; ColorChannels [dynamicChannelIndex] >= 1.0; PrimaryChannel = dynamicChannelIndex; NSUInteger dynamicChannelIndex = (primaryChannel+2)%3; ColorChannels [dynamicChannelIndex] -= DynamicColorRate; ColorChannels [dynamicChannelIndex] <= 0.0; } // Create a Color; Color. Red = colorChannels[0]; color.green = colorChannels[1]; color.blue = colorChannels[2]; color.alpha = colorChannels[3]; // Return color; } #pragma mark-mtkviewDelegate methods (pragma mark-mtkviewDelegate methods) - (void)drawInMTKView (nonnull MTKView *)view {//1. Color Color = [self makeFancyColor]; ClearColor = MTLClearColorMake(color.red, color.green, color.blue, color.alpha); //2. //3. Create a new command buffer for each render pass to the current drawable // create a new commandBuffer for each render pass with id<MTLCommandBuffer> commandBuffer = [_commandQueue] commandBuffer]; commandBuffer.label = @"MyCommand"; / / 4. From the view map, for rendering descriptor MTLRenderPassDescriptor * renderPassDescriptor = the currentRenderPassDescriptor; If (renderPassDescriptor! = nil) {//6. Create MTLRenderCommandEncoder object id<MTLRenderCommandEncoder> renderEncoder = by rendering descriptor renderPassDescriptor [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; renderEncoder.label = @"MyRenderEncoder"; //7. We can use MTLRenderCommandEncoder to draw the object, but for this demo we only created the encoder. We did not ask Metal to execute the drawing. MTLRenderCommandEncoder [renderEncoder endEncoding]; /* When the encoder is finished, the command cache receives 2 commands: 1) present 2) commit Because the GPU does not draw directly to the screen, so you do not give out instructions. There will not be any content rendering to the screen. * / / / 8. Add a command to display the last clear can map screen [commandBuffer presentDrawable: the currentDrawable]; } //9. Render and submit the commandBuffer to the GPU [commandBuffer commit]; - (void) MTKView :(MTKView *)view drawableSizeWillChange:(CGSize)size {} @endCopy the code

The complete code for rendering a triangle using vertex arrays is at Github