Skip to content

Commit

Permalink
Improving skia context handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
luigi-rosso committed Dec 12, 2021
1 parent 78bc04d commit d62abfb
Show file tree
Hide file tree
Showing 4 changed files with 243 additions and 18 deletions.
10 changes: 10 additions & 0 deletions RiveRuntime.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,11 @@
"HEADER_SEARCH_PATHS[arch=*]" = (
"submodules/rive-cpp/include",
"submodules/rive-cpp/skia/dependencies/skia",
"submodules/rive-cpp/skia/renderer/include",
"submodules/rive-cpp/skia/dependencies/skia/include/core",
"submodules/rive-cpp/skia/dependencies/skia/include/effects",
"submodules/rive-cpp/skia/dependencies/skia/include/gpu",
"submodules/rive-cpp/skia/dependencies/skia/include/config",
);
INFOPLIST_FILE = Source/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
Expand Down Expand Up @@ -821,6 +826,11 @@
"HEADER_SEARCH_PATHS[arch=*]" = (
"submodules/rive-cpp/include",
"submodules/rive-cpp/skia/dependencies/skia",
"submodules/rive-cpp/skia/renderer/include",
"submodules/rive-cpp/skia/dependencies/skia/include/core",
"submodules/rive-cpp/skia/dependencies/skia/include/effects",
"submodules/rive-cpp/skia/dependencies/skia/include/gpu",
"submodules/rive-cpp/skia/dependencies/skia/include/config",
);
INFOPLIST_FILE = Source/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
Expand Down
36 changes: 22 additions & 14 deletions Source/Views/RiveView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -405,25 +405,32 @@ extension RiveView {
return !playingAnimations.isEmpty || !playingStateMachines.isEmpty
}

override public func drawRive(_ rect:CGRect, at:CGSize) {
guard let artboard = self._artboard else {
return
}
align(with: rect, withContentRect: artboard.bounds(), with: alignment, with: fit)
draw(with:artboard)
}
override public func isPaused() -> Bool {
return !isPlaying
}

/// Creates a Rive renderer and applies the currently animating artboard to it
/// - Parameter rect: the `GCRect` that we will fit the artboard into.
override public func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext(), let artboard = self._artboard else {
override public func drawRive(_ rect: CGRect, at: CGSize) {
guard let artboard = self._artboard else {
return
}
let renderer = RiveRenderer(context: context)
renderer.align(with: rect, withContentRect: artboard.bounds(), with: alignment, with: fit)
artboard.draw(renderer)

align(
with: CGRect(x: rect.origin.x, y: rect.origin.y, width: at.width, height: at.height),
withContentRect: artboard.bounds(), with: alignment, with: fit)
draw(with: artboard)
}

/// Creates a Rive renderer and applies the currently animating artboard to it
/// - Parameter rect: the `GCRect` that we will fit the artboard into.
// override public func draw(_ rect: CGRect) {
// guard let context = UIGraphicsGetCurrentContext(), let artboard = self._artboard else {
// return
// }
// let renderer = RiveRenderer(context: context)
// renderer.align(with: rect, withContentRect: artboard.bounds(), with: alignment, with: fit)
// artboard.draw(renderer)
// }

// Starts the animation timer
private func runTimer() {
if displayLinkProxy == nil {
Expand All @@ -435,6 +442,7 @@ extension RiveView {
if displayLinkProxy?.displayLink?.isPaused == true {
displayLinkProxy?.displayLink?.isPaused = false
}

}

// Stops the animation timer
Expand Down
1 change: 1 addition & 0 deletions Source/Views/rive_renderer_view.hh
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
- (void)alignWithRect:(CGRect)rect withContentRect:(CGRect)contentRect withAlignment:(Alignment)alignment withFit:(Fit)fit;
- (void)drawWithArtboard:(RiveArtboard*)artboard;
- (void)drawRive:(CGRect)rect atSize:(CGSize)size;
- (bool)isPaused;
@end
214 changes: 210 additions & 4 deletions Source/Views/rive_renderer_view.mm
Original file line number Diff line number Diff line change
@@ -1,15 +1,221 @@
#include "skia_view.hh"
#include "rive_renderer_view.hh"

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

@implementation SkiaView {
id<MTLCommandQueue> fQueue;
#include "include/core/SkSurface.h"
#include "include/core/SkSurfaceProps.h"
#include "include/gpu/GrBackendSurface.h"
#include "include/gpu/GrDirectContext.h"
#include "skia_renderer.hpp"

#import "RivePrivateHeaders.h"

/// We build up a share context manager as recycling skia contexts between view
/// changes is inefficient and error prone (leads to crashes).
@interface SkiaContextManager : NSObject
@property(strong) id<MTLDevice> metalDevice;
@property(strong) id<MTLCommandQueue> metalQueue;
@property sk_sp<GrDirectContext> grContext;
+ (SkiaContextManager *)shared;
@end

@implementation SkiaContextManager

+ (SkiaContextManager *)shared {
static SkiaContextManager *single = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
single = [[self alloc] init];
});
return single;
}

- (id)init {
if (self = [super init]) {
[self setMetalDevice:MTLCreateSystemDefaultDevice()];
if (![self metalDevice]) {
NSLog(@"Metal is not supported on this device");
return nil;
}
[self setMetalQueue:[[self metalDevice] newCommandQueue]];
_grContext = GrDirectContext::MakeMetal((__bridge void *)[self metalDevice],
(__bridge void *)[self metalQueue],
GrContextOptions());
}
return self;
}

@end

sk_sp<SkSurface> SkMtkViewToSurface(MTKView *mtkView,
GrDirectContext *grContext) {
if (!grContext ||
MTLPixelFormatDepth32Float_Stencil8 !=
[mtkView depthStencilPixelFormat] ||
MTLPixelFormatBGRA8Unorm != [mtkView colorPixelFormat]) {
return nullptr;
}
const SkColorType colorType = kBGRA_8888_SkColorType;
sk_sp<SkColorSpace> colorSpace = nullptr;
const GrSurfaceOrigin origin = kTopLeft_GrSurfaceOrigin;
const SkSurfaceProps surfaceProps(
SkSurfaceProps::kUseDeviceIndependentFonts_Flag,
SkPixelGeometry::kUnknown_SkPixelGeometry);
int sampleCount = (int)[mtkView sampleCount];
return SkSurface::MakeFromMTKView(grContext, (__bridge GrMTLHandle)mtkView,
origin, sampleCount, colorType, colorSpace,
&surfaceProps);
}

@implementation RiveRendererView {
GrDirectContext *_grContext;
rive::SkiaRenderer *_renderer;
id<MTLCommandQueue> _queue;
}

- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];

self.device = [SkiaContextManager shared].metalDevice;
_grContext = [SkiaContextManager shared].grContext.get();
_queue = [SkiaContextManager shared].metalQueue;

[self setDepthStencilPixelFormat:MTLPixelFormatDepth32Float_Stencil8];
[self setColorPixelFormat:MTLPixelFormatBGRA8Unorm];
[self setFramebufferOnly:false];
[self setSampleCount:1];
return self;
}

- (instancetype)initWithFrame:(CGRect)frameRect {
return [super initWithFrame:frameRect device:MTLCreateSystemDefaultDevice()];
_grContext = [SkiaContextManager shared].grContext.get();
_queue = [SkiaContextManager shared].metalQueue;
auto value = [super initWithFrame:frameRect
device:[SkiaContextManager shared].metalDevice];
[self setDepthStencilPixelFormat:MTLPixelFormatDepth32Float_Stencil8];
[self setColorPixelFormat:MTLPixelFormatBGRA8Unorm];
[self setFramebufferOnly:false];
[self setSampleCount:1];
// [self setPreferredFramesPerSecond:60];
return value;
}

- (void)alignWithRect:(CGRect)rect
withContentRect:(CGRect)contentRect
withAlignment:(Alignment)alignment
withFit:(Fit)fit {

rive::AABB frame(rect.origin.x, rect.origin.y,
rect.size.width + rect.origin.x,
rect.size.height + rect.origin.y);

rive::AABB content(contentRect.origin.x, contentRect.origin.y,
contentRect.size.width + contentRect.origin.x,
contentRect.size.height + contentRect.origin.y);

rive::Fit riveFit;
switch (fit) {
case fitFill:
riveFit = rive::Fit::fill;
break;
case fitContain:
riveFit = rive::Fit::contain;
break;
case fitCover:
riveFit = rive::Fit::cover;
break;
case fitFitHeight:
riveFit = rive::Fit::fitHeight;
break;
case fitFitWidth:
riveFit = rive::Fit::fitWidth;
break;
case fitScaleDown:
riveFit = rive::Fit::scaleDown;
break;
case fitNone:
riveFit = rive::Fit::none;
break;
}

// Work out the alignment
rive::Alignment riveAlignment = rive::Alignment::center;
switch (alignment) {
case alignmentTopLeft:
riveAlignment = rive::Alignment::topLeft;
break;
case alignmentTopCenter:
riveAlignment = rive::Alignment::topCenter;
break;
case alignmentTopRight:
riveAlignment = rive::Alignment::topRight;
break;
case alignmentCenterLeft:
riveAlignment = rive::Alignment::centerLeft;
break;
case alignmentCenter:
riveAlignment = rive::Alignment::center;
break;
case alignmentCenterRight:
riveAlignment = rive::Alignment::centerRight;
break;
case alignmentBottomLeft:
riveAlignment = rive::Alignment::bottomLeft;
break;
case alignmentBottomCenter:
riveAlignment = rive::Alignment::bottomCenter;
break;
case alignmentBottomRight:
riveAlignment = rive::Alignment::bottomRight;
break;
}

_renderer->align(riveFit, riveAlignment, frame, content);
}

- (void)drawWithArtboard:(RiveArtboard *)artboard {
[artboard artboard]->draw(_renderer);
}

- (void)drawRive:(CGRect)rect atSize:(CGSize)size {
// Intended to be overridden.
}

- (bool)isPaused {
return true;
}

- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
if (![[self currentDrawable] texture]) {
return;
}
CGSize size = [self drawableSize];
sk_sp<SkSurface> surface = SkMtkViewToSurface(self, _grContext);
if (!surface) {
NSLog(@"error: no sksurface");
return;
}
auto canvas = surface->getCanvas();
_renderer = new rive::SkiaRenderer(canvas);
canvas->clear(SkColor((0x00000000)));
_renderer->save();
[self drawRive:rect atSize:size];
_renderer->restore();

surface->flushAndSubmit();
surface = nullptr;
delete _renderer;
_renderer = nullptr;

id<MTLCommandBuffer> commandBuffer = [_queue commandBuffer];
[commandBuffer presentDrawable:[self currentDrawable]];
[commandBuffer commit];
bool paused = [self isPaused];
[self setEnableSetNeedsDisplay:paused];
[self setPaused:paused];
}

@end

0 comments on commit d62abfb

Please sign in to comment.