Skip to content

Instantly share code, notes, and snippets.

@suzumura-ss
Last active July 21, 2022 12:37
Show Gist options
  • Save suzumura-ss/79150309af32bf151435acbdff920cfc to your computer and use it in GitHub Desktop.
Save suzumura-ss/79150309af32bf151435acbdff920cfc to your computer and use it in GitHub Desktop.
Offscreen Metal rendering
#import <Foundation/Foundation.h>
#import <MetalKit/MetalKit.h>
typedef struct
{
vector_float4 position;
vector_float2 texCoord;
} VertexData;
void Exec()
{
// initWithMetalKitView
id <MTLDevice> device = MTLCreateSystemDefaultDevice();
dispatch_semaphore_t inFlightSemaphore = dispatch_semaphore_create(0);
MTLTextureDescriptor* outputDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Uint
width:8
height:8
mipmapped:NO];
outputDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
id <MTLTexture> texture = [device newTextureWithDescriptor:outputDesc];
{
uint8 pixels[outputDesc.width * outputDesc.height * 4];
memset(pixels, 1, sizeof(pixels));
[texture replaceRegion:MTLRegionMake2D(0, 0, outputDesc.width, outputDesc.height)
mipmapLevel:0
withBytes:pixels
bytesPerRow:outputDesc.width * 4];
}
// loadMetalWithView
id <MTLLibrary> defaultLibrary = [device newDefaultLibrary];
MTLRenderPipelineDescriptor* pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipelineStateDescriptor.sampleCount = 1;
pipelineStateDescriptor.vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"];
pipelineStateDescriptor.fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];;
pipelineStateDescriptor.colorAttachments[0].pixelFormat = outputDesc.pixelFormat;
NSError *error = NULL;
id <MTLRenderPipelineState> pipelineState = [device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
if (!pipelineState || error)
{
NSLog(@"Failed to created pipeline state, error %@", error);
abort();
}
id <MTLCommandQueue> commandQueue = [device newCommandQueue];
// loadAssets
VertexData vertexData[3] = {
{{-1, -1, 0, 1}, {0, 0}},
{{ 1, -1, 0, 1}, {1, 0}},
{{ 0, 1, 0, 1}, {0, 1}}
};
id <MTLBuffer> vertexBuffer = [device newBufferWithBytes:vertexData length:sizeof(vertexData) options:MTLResourceCPUCacheModeDefaultCache];
// drawInMTKView
@autoreleasepool {
id <MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
commandBuffer.label = @"MyCommand";
__block dispatch_semaphore_t block_sema = inFlightSemaphore;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
dispatch_semaphore_signal(block_sema);
}];
MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
renderPassDescriptor.colorAttachments[0].texture = texture;
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(1, 2, 3, 4);
renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
id <MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
if (!renderEncoder)
{
NSLog(@"No renderEncoder");
abort();
}
[renderEncoder pushDebugGroup:@"DrawBox"];
[renderEncoder setRenderPipelineState:pipelineState];
[renderEncoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:sizeof(vertexData) / sizeof(VertexData)];
[renderEncoder popDebugGroup];
[renderEncoder endEncoding];
[commandBuffer commit];
dispatch_semaphore_wait(inFlightSemaphore, DISPATCH_TIME_FOREVER);
uint8 pixels[outputDesc.width * outputDesc.height * 4];
memset(pixels, 2, sizeof(pixels));
[texture getBytes:pixels
bytesPerRow:sizeof(uint8) * 4 * outputDesc.width
fromRegion:MTLRegionMake2D(0, 0, outputDesc.width, outputDesc.height)
mipmapLevel:0];
uint8* p = pixels;
for (int y = 0; y < outputDesc.height; ++y)
{
NSMutableString* s = [NSMutableString string];
for (int x = 0; x < outputDesc.width; ++x)
{
[s appendString:@"("];
for (int k = 0; k < 4; ++k)
{
[s appendFormat:@" %3d", *(p++)];
}
[s appendString:@" )"];
}
NSLog(@"%@", s);
}
}
}
int main(int argc, const char * argv[])
{
@autoreleasepool {
Exec();
}
return 0;
}
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
typedef struct
{
float4 position [[position]];
float2 texCoord;
} Vertex;
vertex Vertex vertexShader(constant Vertex* in [[buffer(0)]], uint vid [[vertex_id]])
{
return in[vid];
}
fragment uint4 fragmentShader(Vertex in [[stage_in]])
{
return uint4(0, in.texCoord.x * 255, in.texCoord.y * 255, 1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment