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
1 change: 1 addition & 0 deletions ink/rendering/metal/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ cc_library(
target_compatible_with = ["@platforms//os:ios"],
deps = [
":metal_renderer",
":metal_renderer_objc_helper",
"//ink/strokes:in_progress_stroke",
"//ink/strokes:stroke",
"//ink/strokes/internal/jni:in_progress_stroke_native_helper",
Expand Down
23 changes: 20 additions & 3 deletions ink/rendering/metal/metal_renderer_native.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
#include <simd/vector_make.h>

#include <cstdint>
#include <memory>
#include <optional>
#include <utility>

#include "absl/status/statusor.h"
#include "ink/rendering/metal/metal_renderer.h"
#include "ink/rendering/metal/metal_renderer_objc_helper.h"
#include "ink/strokes/in_progress_stroke.h"
#include "ink/strokes/internal/jni/in_progress_stroke_native_helper.h"
#include "ink/strokes/internal/jni/stroke_native_helper.h"
Expand Down Expand Up @@ -61,18 +62,34 @@ extern "C" {
int64_t MetalRendererNative_create(
void* device, uint64_t color_pixel_format, uint64_t stencil_pixel_format,
int sample_count,
void* (*texture_for_id_callback)(int64_t metal_renderer_native_ptr,
const char* texture_id),
void (*throw_from_status_callback)(void* jni_env, int status_code,
const char* status_str)) {
std::shared_ptr<void> texture_bitmap_store;
if (texture_for_id_callback != nullptr) {
texture_bitmap_store =
ink::rendering::metal_objc::CreateKotlinTextureStoreWrapper(
texture_for_id_callback);
}

absl::StatusOr<MetalRenderer> metal_renderer = MetalRenderer::Create(
device, color_pixel_format, stencil_pixel_format,
sample_count >= 0 ? std::make_optional(sample_count) : std::nullopt);
sample_count >= 0 ? std::make_optional(sample_count) : std::nullopt,
texture_bitmap_store.get());

if (!metal_renderer.ok()) {
throw_from_status_callback(/*jni_env=*/nullptr,
static_cast<int>(metal_renderer.status().code()),
metal_renderer.status().ToString().c_str());
return 0;
}
return NewNativeMetalRenderer(*metal_renderer);
int64_t native_ptr = NewNativeMetalRenderer(*metal_renderer);
if (texture_bitmap_store != nullptr) {
ink::rendering::metal_objc::SetKotlinMetalRendererNativePtr(
texture_bitmap_store.get(), native_ptr);
}
return native_ptr;
}

void MetalRendererNative_drawInProgressStroke(
Expand Down
6 changes: 5 additions & 1 deletion ink/rendering/metal/metal_renderer_native.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ extern "C" {
// `color_pixel_format` and `stencil_pixel_format` are the `MTLPixelFormat` of
// the color and stencil textures to render to. `sample_count` is the number of
// samples per pixel for MSAA. If -1, shader-based antialiasing will be used
// instead.
// instead. `texture_for_id_callback` is a callback used to retrieve textures
// for given texture ID strings, and returns a nullable raw pointer to a
// `CGImage`.
int64_t MetalRendererNative_create(
void* device, uint64_t color_pixel_format, uint64_t stencil_pixel_format,
int sample_count,
void* (*texture_for_id_callback)(int64_t metal_renderer_native_ptr,
const char* texture_id),
void (*throw_from_status_callback)(void* jni_env, int status_code,
const char* status_str));

Expand Down
14 changes: 14 additions & 0 deletions ink/rendering/metal/metal_renderer_objc_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ CreateINKMetalRendererState(
uint64_t stencil_pixel_format_val, std::optional<int> sample_count,
void* absl_nullable texture_bitmap_store_ptr = nullptr);

// Creates a wrapper implementing `INKTextureBitmapStore` that delegates to a
// Kotlin callback.
// Returns an opaque pointer to the wrapper, which should be passed to
// `CreateINKMetalRendererState`.
// The caller is responsible for releasing the wrapper (e.g. via CFRelease)
// after passing it to `CreateINKMetalRendererState` (which will retain it).
std::shared_ptr<void> CreateKotlinTextureStoreWrapper(
void* (*texture_for_id_callback)(int64_t metal_renderer_native_ptr,
const char* texture_id));

// Sets the native pointer of the `MetalRenderer` on the wrapper.
void SetKotlinMetalRendererNativePtr(void* texture_store_wrapper_ptr,
int64_t metal_renderer_native_ptr);

// The functions below take opaque pointers to ObjC objects:
// - `renderer_state_ptr` is an opaque pointer to `INKMetalRendererState`.
// - `texture_ptr` is an opaque pointer to `id<MTLTexture>`.
Expand Down
30 changes: 30 additions & 0 deletions ink/rendering/metal/metal_renderer_objc_helper.mm
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ - (instancetype)initWithVertexBuffer:(id<MTLBuffer>)vertexBuffer

@end

__attribute__((objc_subclassing_restricted))
@interface KotlinTextureStoreWrapper : NSObject<INKTextureBitmapStore>
@property(nonatomic) int64_t kotlinMetalRendererNativePtr;
@property(nonatomic) void* (*callback)(int64_t, const char*);
@end

@implementation KotlinTextureStoreWrapper

- (CGImageRef)textureForID:(NSString*)textureID {
if (!self.callback || !self.kotlinMetalRendererNativePtr) return nullptr;
const char* c_texture_id = [textureID UTF8String];
return (CGImageRef)(*self.callback)(self.kotlinMetalRendererNativePtr, c_texture_id);
}

@end

// Objective-C class holding Metal rendering pipeline state objects by value.
__attribute__((objc_subclassing_restricted))
@interface INKMetalRendererState : NSObject
Expand Down Expand Up @@ -374,6 +390,20 @@ bool SupportsSelfOverlapDiscard(INKMetalRendererState* state) {
(__bridge id<INKTextureBitmapStore>)texture_bitmap_store_ptr);
}

std::shared_ptr<void> CreateKotlinTextureStoreWrapper(
void* (*texture_for_id_callback)(int64_t metal_renderer_native_ptr, const char* texture_id)) {
KotlinTextureStoreWrapper* wrapper = [[KotlinTextureStoreWrapper alloc] init];
wrapper.callback = texture_for_id_callback;
return std::shared_ptr<void>((__bridge_retained void*)wrapper, &CFRelease);
}

void SetKotlinMetalRendererNativePtr(void* texture_store_wrapper_ptr,
int64_t metal_renderer_native_ptr) {
KotlinTextureStoreWrapper* wrapper =
(__bridge KotlinTextureStoreWrapper*)texture_store_wrapper_ptr;
wrapper.kotlinMetalRendererNativePtr = metal_renderer_native_ptr;
}

absl_nullable std::shared_ptr<void> CreateINKMeshBuffers(void* renderer_state_ptr,
const void* vertex_data,
size_t vertex_data_bytes,
Expand Down
Loading