Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions packages/skia/apple/MetalContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class MetalContext {
public:
MetalContext(const MetalContext &) = delete;
MetalContext &operator=(const MetalContext &) = delete;
~MetalContext();

static MetalContext &getInstance() {
static thread_local MetalContext instance;
Expand Down Expand Up @@ -71,13 +72,11 @@ class MetalContext {
switch (format) {
case SkiaCVPixelBufferUtils::CVPixelBufferBaseFormat::rgb: {
// CVPixelBuffer is in any RGB format, single-plane
return SkiaCVPixelBufferUtils::RGB::makeSkImageFromCVPixelBuffer(
_device, _directContext.get(), sampleBuffer);
return SkiaCVPixelBufferUtils::RGB::makeSkImageFromCVPixelBuffer(*this, sampleBuffer);
}
case SkiaCVPixelBufferUtils::CVPixelBufferBaseFormat::yuv: {
// CVPixelBuffer is in any YUV format, multi-plane
return SkiaCVPixelBufferUtils::YUV::makeSkImageFromCVPixelBuffer(
_device, _directContext.get(), sampleBuffer);
return SkiaCVPixelBufferUtils::YUV::makeSkImageFromCVPixelBuffer(*this, sampleBuffer);
}
default:
[[unlikely]] {
Expand All @@ -97,12 +96,17 @@ class MetalContext {
height, useP3ColorSpace);
}

public:
GrDirectContext *getDirectContext() { return _directContext.get(); }
id<MTLDevice> getDevice() const { return _device; }
CVMetalTextureCacheRef getMetalTextureCache();

private:
id<MTLDevice> _device = nullptr;
id<MTLCommandQueue> _commandQueue = nullptr;
sk_sp<GrDirectContext> _directContext = nullptr;
CVMetalTextureCacheRef _metalTextureCache = nullptr;
id _memoryWarningObserver = nil;

MetalContext();
};
35 changes: 35 additions & 0 deletions packages/skia/apple/MetalContext.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "RNSkLog.h"

#import <MetalKit/MetalKit.h>
#import <UIKit/UIKit.h>
#import <chrono>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"
Expand All @@ -17,6 +19,10 @@

#pragma clang diagnostic pop

MetalContext::~MetalContext() {
[[NSNotificationCenter defaultCenter] removeObserver:_memoryWarningObserver];
}

MetalContext::MetalContext() {
_device = MTLCreateSystemDefaultDevice();
if (!_device) {
Expand All @@ -35,4 +41,33 @@
if (_directContext == nullptr) {
RNSkia::RNSkLogger::logToConsole("Couldn't create a Skia Metal Context");
}

// Add a memory warning listener to purge cache
_memoryWarningObserver = [
[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationDidReceiveMemoryWarningNotification
object:nil
queue:nil
usingBlock:^(__unused NSNotification *notification) {
// Clean unused Skia textures (cache/GPU)
_directContext->performDeferredCleanup(std::chrono::milliseconds(0));
if (_metalTextureCache != nil) {
// Flush Metal Texture Cache pool (this can have a huge impact)
CVMetalTextureCacheFlush(_metalTextureCache, 0);
}
}
];
}


CVMetalTextureCacheRef MetalContext::getMetalTextureCache() {
if (_metalTextureCache == nil) {
// Create a new Texture Cache
auto result = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, _device,
nil, &_metalTextureCache);
if (result != kCVReturnSuccess || _metalTextureCache == nil) {
throw std::runtime_error("Failed to create Metal Texture Cache!");
}
}
return _metalTextureCache;
}
12 changes: 5 additions & 7 deletions packages/skia/apple/SkiaCVPixelBufferUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#import "include/gpu/ganesh/GrYUVABackendTextures.h"
#pragma clang diagnostic pop

class MetalContext;

/**
Holds a Metal Texture.
When the `TextureHolder` is destroyed, the underlying Metal Texture
Expand Down Expand Up @@ -84,8 +86,7 @@ class SkiaCVPixelBufferUtils {
CVPixelBuffer.
*/
static sk_sp<SkImage>
makeSkImageFromCVPixelBuffer(id<MTLDevice> device, GrDirectContext *context,
CVPixelBufferRef pixelBuffer);
makeSkImageFromCVPixelBuffer(MetalContext& context, CVPixelBufferRef pixelBuffer);

private:
static SkColorType getCVPixelBufferColorType(CVPixelBufferRef pixelBuffer);
Expand All @@ -98,8 +99,7 @@ class SkiaCVPixelBufferUtils {
CVPixelBuffer.
*/
static sk_sp<SkImage>
makeSkImageFromCVPixelBuffer(id<MTLDevice> device, GrDirectContext *context,
CVPixelBufferRef pixelBuffer);
makeSkImageFromCVPixelBuffer(MetalContext& context, CVPixelBufferRef pixelBuffer);

private:
static SkYUVAInfo::PlaneConfig getPlaneConfig(OSType pixelFormat);
Expand All @@ -109,9 +109,7 @@ class SkiaCVPixelBufferUtils {
};

private:
static CVMetalTextureCacheRef getTextureCache(id<MTLDevice> device);
static TextureHolder *getSkiaTextureForCVPixelBufferPlane(
id<MTLDevice> device, CVPixelBufferRef pixelBuffer, size_t planeIndex);
static TextureHolder *getSkiaTextureForCVPixelBufferPlane(MetalContext& context, CVPixelBufferRef pixelBuffer, size_t planeIndex);
static MTLPixelFormat
getMTLPixelFormatForCVPixelBufferPlane(CVPixelBufferRef pixelBuffer,
size_t planeIndex);
Expand Down
39 changes: 10 additions & 29 deletions packages/skia/apple/SkiaCVPixelBufferUtils.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#import "SkiaCVPixelBufferUtils.h"

#import "MetalContext.h"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"

Expand Down Expand Up @@ -133,28 +135,23 @@
}
}

sk_sp<SkImage> SkiaCVPixelBufferUtils::RGB::makeSkImageFromCVPixelBuffer(
id<MTLDevice> device, GrDirectContext *context,
CVPixelBufferRef pixelBuffer) {
sk_sp<SkImage> SkiaCVPixelBufferUtils::RGB::makeSkImageFromCVPixelBuffer(MetalContext& context, CVPixelBufferRef pixelBuffer) {
// 1. Get Skia color type for RGB buffer
SkColorType colorType = getCVPixelBufferColorType(pixelBuffer);

// 2. Get texture, RGB buffers only have one plane
TextureHolder *texture = getSkiaTextureForCVPixelBufferPlane(
device, pixelBuffer, /* planeIndex */ 0);
TextureHolder *texture = getSkiaTextureForCVPixelBufferPlane(context, pixelBuffer, /* planeIndex */ 0);

// 3. Convert to image with manual memory cleanup
return SkImages::BorrowTextureFrom(
context, texture->toGrBackendTexture(), kTopLeft_GrSurfaceOrigin,
context.getDirectContext(), texture->toGrBackendTexture(), kTopLeft_GrSurfaceOrigin,
colorType, kOpaque_SkAlphaType, nullptr,
[](void *texture) { delete (TextureHolder *)texture; }, (void *)texture);
}

// pragma MARK: YUV

sk_sp<SkImage> SkiaCVPixelBufferUtils::YUV::makeSkImageFromCVPixelBuffer(
id<MTLDevice> device, GrDirectContext *context,
CVPixelBufferRef pixelBuffer) {
sk_sp<SkImage> SkiaCVPixelBufferUtils::YUV::makeSkImageFromCVPixelBuffer(MetalContext& context, CVPixelBufferRef pixelBuffer) {
// 1. Get all planes (YUV, Y_UV, Y_U_V or Y_U_V_A)
const size_t planesCount = CVPixelBufferGetPlaneCount(pixelBuffer);
if (planesCount > SkYUVAInfo::kMaxPlanes) [[unlikely]] {
Expand All @@ -168,7 +165,7 @@

for (size_t planeIndex = 0; planeIndex < planesCount; planeIndex++) {
TextureHolder *textureHolder =
getSkiaTextureForCVPixelBufferPlane(device, pixelBuffer, planeIndex);
getSkiaTextureForCVPixelBufferPlane(context, pixelBuffer, planeIndex);
textures[planeIndex] = textureHolder->toGrBackendTexture();
texturesHolder->addTexture(textureHolder);
}
Expand All @@ -181,7 +178,7 @@

// 4. Wrap into SkImage type with manualy memory cleanup
return SkImages::TextureFromYUVATextures(
context, yuvaTextures, nullptr,
context.getDirectContext(), yuvaTextures, nullptr,
[](void *textureHolders) {
delete (MultiTexturesHolder *)textureHolders;
},
Expand Down Expand Up @@ -290,9 +287,9 @@
// pragma MARK: CVPixelBuffer -> Skia Texture

TextureHolder *SkiaCVPixelBufferUtils::getSkiaTextureForCVPixelBufferPlane(
id<MTLDevice> device, CVPixelBufferRef pixelBuffer, size_t planeIndex) {
MetalContext& context, CVPixelBufferRef pixelBuffer, size_t planeIndex) {
// 1. Get cache
CVMetalTextureCacheRef textureCache = getTextureCache(device);
CVMetalTextureCacheRef textureCache = context.getMetalTextureCache();

// 2. Get MetalTexture from CMSampleBuffer
size_t width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex);
Expand All @@ -314,22 +311,6 @@
return new TextureHolder(textureHolder);
}

// pragma MARK: getTextureCache()

CVMetalTextureCacheRef
SkiaCVPixelBufferUtils::getTextureCache(id<MTLDevice> device) {
static CVMetalTextureCacheRef textureCache = nil;
if (textureCache == nil) {
// Create a new Texture Cache
auto result = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device,
nil, &textureCache);
if (result != kCVReturnSuccess || textureCache == nil) {
throw std::runtime_error("Failed to create Metal Texture Cache!");
}
}
return textureCache;
}

// pragma MARK: Get CVPixelBuffer MTLPixelFormat

MTLPixelFormat SkiaCVPixelBufferUtils::getMTLPixelFormatForCVPixelBufferPlane(
Expand Down