Skip to content

Commit

Permalink
Rewrite in Swift
Browse files Browse the repository at this point in the history
  • Loading branch information
mrousavy committed Jul 19, 2023
1 parent 7bf4f09 commit 2f4b3ea
Show file tree
Hide file tree
Showing 18 changed files with 390 additions and 562 deletions.
4 changes: 2 additions & 2 deletions VisionCamera.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Pod::Spec.new do |s|
s.license = package["license"]
s.authors = package["author"]

s.platforms = { :ios => "12.4" }
s.platforms = { :ios => "13.0" }
s.source = { :git => "https://github.com/mrousavy/react-native-vision-camera.git", :tag => "#{s.version}" }

s.pod_target_xcconfig = {
Expand Down Expand Up @@ -53,7 +53,7 @@ Pod::Spec.new do |s|

# Skia Frame Processors
hasSkia ? "ios/Skia Render Layer/*.{m,mm,swift}" : "",
hasSkia ? "ios/Skia Render Layer/SkiaPreviewView.h" : "",
hasSkia ? "ios/Skia Render Layer/SkiaRenderer.h" : "",
]
# Any private headers that are not globally unique should be mentioned here.
# Otherwise there will be a nameclash, since CocoaPods flattens out any header directories
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ SPEC CHECKSUMS:
RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
VisionCamera: f3cbd24bba0067a3dece6d88ac4b8b9d44787e87
VisionCamera: c935274ff3da0e443b4fcad23e349715c4416991
Yoga: 65286bb6a07edce5e4fe8c90774da977ae8fc009

PODFILE CHECKSUM: ab9c06b18c63e741c04349c0fd630c6d3145081c
Expand Down
9 changes: 8 additions & 1 deletion ios/CameraQueues.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,18 @@ public class CameraQueues: NSObject {
attributes: [],
autoreleaseFrequency: .inherit,
target: nil)

Check failure on line 26 in ios/CameraQueues.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
/// The serial execution queue for output processing of audio buffers.
@objc public static let audioQueue = DispatchQueue(label: "mrousavy/VisionCamera.audio",
qos: .userInteractive,
attributes: [],
autoreleaseFrequency: .inherit,
target: nil)

Check failure on line 33 in ios/CameraQueues.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
/// The serial execution queue for rendering the Skia preview.
@objc public static let previewQueue = DispatchQueue(label: "mrousavy/VisionCamera.preview",
qos: .userInteractive,
attributes: [],
autoreleaseFrequency: .inherit,
target: nil)
}
18 changes: 0 additions & 18 deletions ios/Skia Render Layer/SkiaCanvas.h

This file was deleted.

11 changes: 0 additions & 11 deletions ios/Skia Render Layer/SkiaFrameProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,7 @@

#import <Foundation/Foundation.h>
#import "FrameProcessor.h"
#import "SkiaMetalRenderContext.h"

typedef void (^render_block_t)(const OffscreenRenderContext&);

@interface SkiaFrameProcessor : FrameProcessor

/**
Render the latest Frame from the Camera.
The callback (`render_block_t`) will be invoked with a read-access
texture that can be used for rendering.
*/
- (void)renderLatestFrame:(render_block_t _Nonnull)callback;
- (bool)hasNewFrame;

@end
120 changes: 8 additions & 112 deletions ios/Skia Render Layer/SkiaFrameProcessor.mm
Original file line number Diff line number Diff line change
Expand Up @@ -8,128 +8,24 @@

#import <Foundation/Foundation.h>
#import "SkiaFrameProcessor.h"
#import <Metal/Metal.h>
#import "SkiaRenderer.h"

#import <include/core/SkSurface.h>
#import <include/core/SkCanvas.h>
#import <include/core/SkColorSpace.h>
#import "SkImageHelpers.h"

@implementation SkiaFrameProcessor {
// Metal/Skia Context
OffscreenRenderContext _offscreenContext;

// For synchronization between the two Threads/Contexts
std::mutex _textureMutex;
std::atomic<bool> _hasNewFrame;
}
@implementation SkiaFrameProcessor

- (instancetype)init {
if (self = [super init]) {
_offscreenContext = OffscreenRenderContext();
_hasNewFrame = false;
// TODO: init?
}
return self;
}

- (bool)hasNewFrame {
return _hasNewFrame;
}

- (void)renderLatestFrame:(render_block_t)callback {
// Lock the Mutex so we can operate on the Texture atomically without
// renderFrameToCanvas() overwriting in between from a different thread
std::unique_lock lock(_textureMutex);

// Call the callback
callback(_offscreenContext);

// Set flag back to false
_hasNewFrame = false;
lock.unlock();
}

- (id<MTLTexture>)getTexture:(NSUInteger)width height:(NSUInteger)height {
if (_offscreenContext.texture == nil
|| _offscreenContext.texture.width != width
|| _offscreenContext.texture.height != height) {
// Create new texture with the given width and height
MTLTextureDescriptor* textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:width
height:height
mipmapped:NO];
textureDescriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
_offscreenContext.texture = [_offscreenContext.device newTextureWithDescriptor:textureDescriptor];
}
return _offscreenContext.texture;
}

- (void)call:(Frame*)frame {
// Wrap in auto release pool since we want the system to clean up after rendering
// and not wait until later - we've seen some example of memory usage growing very
// fast in the simulator without this.
@autoreleasepool {
// Get the Frame's PixelBuffer
CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(frame.buffer);
if (pixelBuffer == nil) {
throw std::runtime_error("Frame Processor: Pixel Buffer is corrupt/empty.");
}

// Lock Mutex to block the runLoop from overwriting the _currentDrawable
std::unique_lock lock(_textureMutex);

// Get the Metal Texture we use for in-memory drawing
auto texture = [self getTexture:CVPixelBufferGetWidth(pixelBuffer)
height:CVPixelBufferGetHeight(pixelBuffer)];

// Get & Lock the writeable Texture from the Metal Drawable
GrMtlTextureInfo textureInfo;
textureInfo.fTexture.retain((__bridge void*)texture);
GrBackendRenderTarget backendRenderTarget((int)texture.width,
(int)texture.height,
1,
textureInfo);

auto context = _offscreenContext.skiaContext.get();

// Create a Skia Surface from the writable Texture
auto surface = SkSurface::MakeFromBackendRenderTarget(context,
backendRenderTarget,
kTopLeft_GrSurfaceOrigin,
kBGRA_8888_SkColorType,
SkColorSpace::MakeSRGB(),
nullptr);

if (surface == nullptr || surface->getCanvas() == nullptr) {
throw std::runtime_error("Skia surface could not be created from parameters.");
}

// Converts the CMSampleBuffer to an SkImage - RGB.
CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
auto image = SkImageHelpers::convertCMSampleBufferToSkImage(context, frame.buffer);

auto canvas = surface->getCanvas();

// Clear everything so we keep it at a clean state
canvas->clear(SkColors::kBlack);

// Draw the Image into the Frame (aspectRatio: cover)
// The Frame Processor might draw the Frame again (through render()) to pass a custom paint/shader,
// but that'll just overwrite the existing one - no need to worry.
canvas->drawImage(image, 0, 0);

// TODO: How to pass SkCanvas here?
// Call the JS Frame Processor.
// TODO: Get SkiaRenderer somehow...
SkiaRenderer* renderer = nil;
[renderer renderCameraFrameToOffscreenCanvas:frame.buffer withDrawCallback:^(SkiaCanvas _Nonnull) {
// TODO: Pass SkiaCanvas along here...
[super call:frame];

// Flush all appended operations on the canvas and commit it to the SkSurface
surface->flushAndSubmit();

_hasNewFrame = true;

lock.unlock();
CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
}
}];
}

@end
53 changes: 0 additions & 53 deletions ios/Skia Render Layer/SkiaMetalCanvasProvider.h

This file was deleted.

Loading

0 comments on commit 2f4b3ea

Please sign in to comment.