2014 snapchat source code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
4.7 KiB

  1. //
  2. // SCMetalModule.m
  3. // Snapchat
  4. //
  5. // Created by Michel Loenngren on 7/19/17.
  6. //
  7. //
  8. #import "SCMetalModule.h"
  9. #import "SCCameraTweaks.h"
  10. #import <SCFoundation/SCAssertWrapper.h>
  11. #import <SCFoundation/SCLog.h>
  12. @interface SCMetalModule ()
  13. #if !TARGET_IPHONE_SIMULATOR
  14. @property (nonatomic, readonly) id<MTLLibrary> library;
  15. @property (nonatomic, readonly) id<MTLDevice> device;
  16. @property (nonatomic, readonly) id<MTLFunction> function;
  17. @property (nonatomic, readonly) id<MTLComputePipelineState> computePipelineState;
  18. @property (nonatomic, readonly) id<MTLCommandQueue> commandQueue;
  19. @property (nonatomic, readonly) CVMetalTextureCacheRef textureCache;
  20. #endif
  21. @end
  22. @implementation SCMetalModule {
  23. id<SCMetalRenderCommand> _metalRenderCommand;
  24. }
  25. #if !TARGET_IPHONE_SIMULATOR
  26. @synthesize library = _library;
  27. @synthesize function = _function;
  28. @synthesize computePipelineState = _computePipelineState;
  29. @synthesize commandQueue = _commandQueue;
  30. @synthesize textureCache = _textureCache;
  31. #endif
  32. - (instancetype)initWithMetalRenderCommand:(id<SCMetalRenderCommand>)metalRenderCommand
  33. {
  34. self = [super init];
  35. if (self) {
  36. _metalRenderCommand = metalRenderCommand;
  37. }
  38. return self;
  39. }
  40. #pragma mark - SCProcessingModule
  41. - (CMSampleBufferRef)render:(RenderData)renderData
  42. {
  43. CMSampleBufferRef input = renderData.sampleBuffer;
  44. #if !TARGET_IPHONE_SIMULATOR
  45. id<MTLComputePipelineState> pipelineState = self.computePipelineState;
  46. SC_GUARD_ELSE_RETURN_VALUE(pipelineState, input);
  47. CVMetalTextureCacheRef textureCache = self.textureCache;
  48. SC_GUARD_ELSE_RETURN_VALUE(textureCache, input);
  49. id<MTLCommandQueue> commandQueue = self.commandQueue;
  50. SC_GUARD_ELSE_RETURN_VALUE(commandQueue, input);
  51. SCMetalTextureResource *textureResource =
  52. [[SCMetalTextureResource alloc] initWithRenderData:renderData textureCache:textureCache device:self.device];
  53. id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
  54. if (!_metalRenderCommand) {
  55. SCAssertFail(@"Metal module must be initialized with an SCMetalRenderCommand");
  56. }
  57. id<MTLComputeCommandEncoder> commandEncoder = [_metalRenderCommand encodeMetalCommand:commandBuffer
  58. pipelineState:pipelineState
  59. textureResource:textureResource];
  60. NSUInteger w = pipelineState.threadExecutionWidth;
  61. NSUInteger h = pipelineState.maxTotalThreadsPerThreadgroup / w;
  62. MTLSize threadsPerThreadgroup = MTLSizeMake(w, h, 1);
  63. MTLSize threadgroupsPerGrid = MTLSizeMake((textureResource.sourceYTexture.width + w - 1) / w,
  64. (textureResource.sourceYTexture.height + h - 1) / h, 1);
  65. [commandEncoder dispatchThreadgroups:threadgroupsPerGrid threadsPerThreadgroup:threadsPerThreadgroup];
  66. [commandEncoder endEncoding];
  67. [commandBuffer commit];
  68. [commandBuffer waitUntilCompleted];
  69. CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(renderData.sampleBuffer);
  70. SCMetalCopyTexture(textureResource.destinationYTexture, imageBuffer, 0);
  71. SCMetalCopyTexture(textureResource.destinationUVTexture, imageBuffer, 1);
  72. #endif
  73. return input;
  74. }
  75. - (BOOL)requiresDepthData
  76. {
  77. return [_metalRenderCommand requiresDepthData];
  78. }
  79. #pragma mark - Lazy properties
  80. #if !TARGET_IPHONE_SIMULATOR
  81. - (id<MTLLibrary>)library
  82. {
  83. if (!_library) {
  84. NSString *libPath = [[NSBundle mainBundle] pathForResource:@"sccamera-default" ofType:@"metallib"];
  85. NSError *error = nil;
  86. _library = [self.device newLibraryWithFile:libPath error:&error];
  87. if (error) {
  88. SCLogGeneralError(@"Create metallib error: %@", error.description);
  89. }
  90. }
  91. return _library;
  92. }
  93. - (id<MTLDevice>)device
  94. {
  95. return SCGetManagedCaptureMetalDevice();
  96. }
  97. - (id<MTLFunction>)function
  98. {
  99. return [self.library newFunctionWithName:[_metalRenderCommand functionName]];
  100. }
  101. - (id<MTLComputePipelineState>)computePipelineState
  102. {
  103. if (!_computePipelineState) {
  104. NSError *error = nil;
  105. _computePipelineState = [self.device newComputePipelineStateWithFunction:self.function error:&error];
  106. if (error) {
  107. SCLogGeneralError(@"Error while creating compute pipeline state %@", error.description);
  108. }
  109. }
  110. return _computePipelineState;
  111. }
  112. - (id<MTLCommandQueue>)commandQueue
  113. {
  114. if (!_commandQueue) {
  115. _commandQueue = [self.device newCommandQueue];
  116. }
  117. return _commandQueue;
  118. }
  119. - (CVMetalTextureCacheRef)textureCache
  120. {
  121. if (!_textureCache) {
  122. CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, self.device, nil, &_textureCache);
  123. }
  124. return _textureCache;
  125. }
  126. #endif
  127. @end