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.
215 lines
7.9 KiB
215 lines
7.9 KiB
//
|
|
// SCMetalTextureResource.m
|
|
// Snapchat
|
|
//
|
|
// Created by Brian Ng on 11/7/17.
|
|
//
|
|
|
|
#import "SCMetalTextureResource.h"
|
|
|
|
#import "SCCameraSettingUtils.h"
|
|
#import "SCCameraTweaks.h"
|
|
#import "SCMetalUtils.h"
|
|
|
|
@import CoreImage;
|
|
|
|
#if !TARGET_IPHONE_SIMULATOR
|
|
static NSInteger const kSCFocusRectSize = 4;
|
|
#endif
|
|
|
|
@interface SCMetalTextureResource ()
|
|
#if !TARGET_IPHONE_SIMULATOR
|
|
@property (nonatomic, readonly) CVMetalTextureCacheRef textureCache;
|
|
#endif
|
|
@end
|
|
|
|
@implementation SCMetalTextureResource {
|
|
RenderData _renderData;
|
|
CVImageBufferRef _imageBuffer;
|
|
CIContext *_context;
|
|
}
|
|
|
|
#if !TARGET_IPHONE_SIMULATOR
|
|
@synthesize sourceYTexture = _sourceYTexture;
|
|
@synthesize sourceUVTexture = _sourceUVTexture;
|
|
@synthesize destinationYTexture = _destinationYTexture;
|
|
@synthesize destinationUVTexture = _destinationUVTexture;
|
|
@synthesize sourceBlurredYTexture = _sourceBlurredYTexture;
|
|
@synthesize sourceDepthTexture = _sourceDepthTexture;
|
|
@synthesize depthRange = _depthRange;
|
|
@synthesize depthOffset = _depthOffset;
|
|
@synthesize depthBlurForegroundThreshold = _depthBlurForegroundThreshold;
|
|
@synthesize device = _device;
|
|
@synthesize sampleBufferMetadata = _sampleBufferMetadata;
|
|
|
|
- (instancetype)initWithRenderData:(RenderData)renderData
|
|
textureCache:(CVMetalTextureCacheRef)textureCache
|
|
device:(id<MTLDevice>)device
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
_imageBuffer = CMSampleBufferGetImageBuffer(renderData.sampleBuffer);
|
|
_renderData = renderData;
|
|
_textureCache = textureCache;
|
|
_device = device;
|
|
_context = [CIContext contextWithOptions:@{ kCIContextWorkingFormat : @(kCIFormatRGBAh) }];
|
|
}
|
|
return self;
|
|
}
|
|
#endif
|
|
|
|
#if !TARGET_IPHONE_SIMULATOR
|
|
|
|
- (id<MTLTexture>)sourceYTexture
|
|
{
|
|
if (!_sourceYTexture) {
|
|
CVPixelBufferLockBaseAddress(_imageBuffer, kCVPixelBufferLock_ReadOnly);
|
|
_sourceYTexture = SCMetalTextureFromPixelBuffer(_imageBuffer, 0, MTLPixelFormatR8Unorm, _textureCache);
|
|
CVPixelBufferUnlockBaseAddress(_imageBuffer, kCVPixelBufferLock_ReadOnly);
|
|
}
|
|
return _sourceYTexture;
|
|
}
|
|
|
|
- (id<MTLTexture>)sourceUVTexture
|
|
{
|
|
if (!_sourceUVTexture) {
|
|
CVPixelBufferLockBaseAddress(_imageBuffer, kCVPixelBufferLock_ReadOnly);
|
|
_sourceUVTexture = SCMetalTextureFromPixelBuffer(_imageBuffer, 1, MTLPixelFormatRG8Unorm, _textureCache);
|
|
CVPixelBufferUnlockBaseAddress(_imageBuffer, kCVPixelBufferLock_ReadOnly);
|
|
}
|
|
return _sourceUVTexture;
|
|
}
|
|
|
|
- (id<MTLTexture>)destinationYTexture
|
|
{
|
|
if (!_destinationYTexture) {
|
|
MTLTextureDescriptor *textureDescriptor =
|
|
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm
|
|
width:CVPixelBufferGetWidthOfPlane(_imageBuffer, 0)
|
|
height:CVPixelBufferGetHeightOfPlane(_imageBuffer, 0)
|
|
mipmapped:NO];
|
|
textureDescriptor.usage |= MTLTextureUsageShaderWrite;
|
|
_destinationYTexture = [_device newTextureWithDescriptor:textureDescriptor];
|
|
}
|
|
return _destinationYTexture;
|
|
}
|
|
|
|
- (id<MTLTexture>)destinationUVTexture
|
|
{
|
|
if (!_destinationUVTexture) {
|
|
MTLTextureDescriptor *textureDescriptor =
|
|
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRG8Unorm
|
|
width:CVPixelBufferGetWidthOfPlane(_imageBuffer, 1)
|
|
height:CVPixelBufferGetHeightOfPlane(_imageBuffer, 1)
|
|
mipmapped:NO];
|
|
textureDescriptor.usage |= MTLTextureUsageShaderWrite;
|
|
_destinationUVTexture = [_device newTextureWithDescriptor:textureDescriptor];
|
|
}
|
|
return _destinationUVTexture;
|
|
}
|
|
|
|
- (id<MTLTexture>)sourceBlurredYTexture
|
|
{
|
|
if (!_sourceBlurredYTexture) {
|
|
MTLTextureDescriptor *textureDescriptor =
|
|
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm
|
|
width:CVPixelBufferGetWidthOfPlane(_imageBuffer, 0)
|
|
height:CVPixelBufferGetHeightOfPlane(_imageBuffer, 0)
|
|
mipmapped:NO];
|
|
textureDescriptor.usage |= MTLTextureUsageShaderWrite;
|
|
_sourceBlurredYTexture = [_device newTextureWithDescriptor:textureDescriptor];
|
|
}
|
|
return _sourceBlurredYTexture;
|
|
}
|
|
|
|
- (id<MTLTexture>)sourceDepthTexture
|
|
{
|
|
if (!_sourceDepthTexture) {
|
|
CVPixelBufferLockBaseAddress(_imageBuffer, kCVPixelBufferLock_ReadOnly);
|
|
_sourceDepthTexture =
|
|
SCMetalTextureFromPixelBuffer(_renderData.depthDataMap, 0, MTLPixelFormatR16Float, _textureCache);
|
|
CVPixelBufferUnlockBaseAddress(_imageBuffer, kCVPixelBufferLock_ReadOnly);
|
|
}
|
|
return _sourceDepthTexture;
|
|
}
|
|
|
|
- (float)depthRange
|
|
{
|
|
if (_depthRange == 0) {
|
|
// Get min/max values of depth image to normalize
|
|
size_t bufferWidth = CVPixelBufferGetWidth(_renderData.depthDataMap);
|
|
size_t bufferHeight = CVPixelBufferGetHeight(_renderData.depthDataMap);
|
|
size_t bufferBytesPerRow = CVPixelBufferGetBytesPerRow(_renderData.depthDataMap);
|
|
|
|
CVPixelBufferLockBaseAddress(_renderData.depthDataMap, kCVPixelBufferLock_ReadOnly);
|
|
unsigned char *pixelBufferPointer = CVPixelBufferGetBaseAddress(_renderData.depthDataMap);
|
|
__fp16 *bufferPtr = (__fp16 *)pixelBufferPointer;
|
|
uint32_t ptrInc = (int)bufferBytesPerRow / sizeof(__fp16);
|
|
|
|
float depthMin = MAXFLOAT;
|
|
float depthMax = -MAXFLOAT;
|
|
for (int j = 0; j < bufferHeight; j++) {
|
|
for (int i = 0; i < bufferWidth; i++) {
|
|
float value = bufferPtr[i];
|
|
if (!isnan(value)) {
|
|
depthMax = MAX(depthMax, value);
|
|
depthMin = MIN(depthMin, value);
|
|
}
|
|
}
|
|
bufferPtr += ptrInc;
|
|
}
|
|
CVPixelBufferUnlockBaseAddress(_renderData.depthDataMap, kCVPixelBufferLock_ReadOnly);
|
|
_depthRange = depthMax - depthMin;
|
|
_depthOffset = depthMin;
|
|
}
|
|
return _depthRange;
|
|
}
|
|
|
|
- (float)depthOffset
|
|
{
|
|
if (_depthRange == 0) {
|
|
[self depthRange];
|
|
}
|
|
return _depthOffset;
|
|
}
|
|
|
|
- (CGFloat)depthBlurForegroundThreshold
|
|
{
|
|
if (_renderData.depthBlurPointOfInterest) {
|
|
CGPoint point = *_renderData.depthBlurPointOfInterest;
|
|
CIImage *disparityImage = [CIImage imageWithCVPixelBuffer:_renderData.depthDataMap];
|
|
CIVector *vector =
|
|
[CIVector vectorWithX:point.x * CVPixelBufferGetWidth(_renderData.depthDataMap) - kSCFocusRectSize / 2
|
|
Y:point.y * CVPixelBufferGetHeight(_renderData.depthDataMap) - kSCFocusRectSize / 2
|
|
Z:kSCFocusRectSize
|
|
W:kSCFocusRectSize];
|
|
CIImage *minMaxImage =
|
|
[[disparityImage imageByClampingToExtent] imageByApplyingFilter:@"CIAreaMinMaxRed"
|
|
withInputParameters:@{kCIInputExtentKey : vector}];
|
|
UInt8 pixel[4] = {0, 0, 0, 0};
|
|
[_context render:minMaxImage
|
|
toBitmap:&pixel
|
|
rowBytes:4
|
|
bounds:CGRectMake(0, 0, 1, 1)
|
|
format:kCIFormatRGBA8
|
|
colorSpace:nil];
|
|
CGFloat disparity = pixel[1] / 255.0;
|
|
CGFloat normalizedDisparity = (disparity - self.depthOffset) / self.depthRange;
|
|
return normalizedDisparity;
|
|
} else {
|
|
return SCCameraTweaksDepthBlurForegroundThreshold();
|
|
}
|
|
}
|
|
|
|
- (SampleBufferMetadata)sampleBufferMetadata
|
|
{
|
|
SampleBufferMetadata sampleMetadata = {
|
|
.isoSpeedRating = 0, .exposureTime = 0.033, .brightness = 0,
|
|
};
|
|
retrieveSampleBufferMetadata(_renderData.sampleBuffer, &sampleMetadata);
|
|
return sampleMetadata;
|
|
}
|
|
|
|
#endif
|
|
|
|
@end
|