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
30 changes: 29 additions & 1 deletion packages/skia/android/cpp/jni/JniWebGPUView.cpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,50 @@
#include <jni.h>
#include <string.h>
#include <android/native_window_jni.h>
#include <android/api-level.h>

#ifdef SK_GRAPHITE
#include "webgpu/webgpu_cpp.h"
#include "rnwgpu/SurfaceRegistry.h"
#include "rnskia/RNDawnContext.h"
#endif

#if __ANDROID_API__ >= 28
#include <android/data_space.h>

static void applyColorSpace(JNIEnv *env, ANativeWindow *window,
jstring jColorSpace) {
int32_t dataSpace = ADATASPACE_SRGB;
if (jColorSpace != nullptr) {
const char *cs = env->GetStringUTFChars(jColorSpace, nullptr);
if (strcmp(cs, "display-p3") == 0) {
dataSpace = ADATASPACE_DISPLAY_P3;
} else if (strcmp(cs, "bt2020-hlg") == 0) {
dataSpace = ADATASPACE_BT2020_HLG;
} else if (strcmp(cs, "bt2020-pq") == 0) {
dataSpace = ADATASPACE_BT2020_PQ;
}
env->ReleaseStringUTFChars(jColorSpace, cs);
}
ANativeWindow_setBuffersDataSpace(window, dataSpace);
}
#endif

extern "C" JNIEXPORT void JNICALL
Java_com_shopify_reactnative_skia_WebGPUView_onSurfaceCreate(
JNIEnv *env, jobject thiz, jobject jSurface, jint contextId, jfloat width,
jfloat height) {
jfloat height, jstring jColorSpace) {
#ifdef SK_GRAPHITE
auto window = ANativeWindow_fromSurface(env, jSurface);
auto &registry = rnwgpu::SurfaceRegistry::getInstance();
auto &dawnContext = RNSkia::DawnContext::getInstance();
auto gpu = dawnContext.getWGPUInstance();

#if __ANDROID_API__ >= 28
// Set color space on the native window
applyColorSpace(env, window, jColorSpace);
#endif

// Create surface from ANativeWindow
wgpu::SurfaceSourceAndroidNativeWindow androidSurfaceDesc;
androidSurfaceDesc.window = window;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class WebGPUView extends ReactViewGroup implements WebGPUViewAPI {

private int mContextId;
private boolean mTransparent = false;
private String mColorSpace = null;
private View mView = null;

WebGPUView(Context context) {
Expand All @@ -21,6 +22,10 @@ public void setContextId(int contextId) {
mContextId = contextId;
}

public void setColorSpace(String value) {
mColorSpace = value;
}

public void setTransparent(boolean value) {
Context ctx = getContext();
if (value != mTransparent || mView == null) {
Expand Down Expand Up @@ -50,7 +55,7 @@ public void surfaceCreated(Surface surface) {
float density = getResources().getDisplayMetrics().density;
float width = getWidth() / density;
float height = getHeight() / density;
onSurfaceCreate(surface, mContextId, width, height);
onSurfaceCreate(surface, mContextId, width, height, mColorSpace);
}

@Override
Expand All @@ -76,7 +81,8 @@ private native void onSurfaceCreate(
Surface surface,
int contextId,
float width,
float height
float height,
String colorSpace
);

@DoNotStrip
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ public void setContextId(WebGPUView view, int value) {
view.setContextId(value);
}

@Override
@ReactProp(name = "colorSpace")
public void setColorSpace(WebGPUView view, @Nullable String value) {
view.setColorSpace(value);
}

@Override
public void onDropViewInstance(@NonNull ReactViewGroup view) {
super.onDropViewInstance(view);
Expand Down
1 change: 1 addition & 0 deletions packages/skia/apple/WebGPUMetalView.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@interface WebGPUMetalView : RNSkPlatformView

@property (nonatomic, strong) NSNumber *contextId;
@property (nonatomic, strong) NSString *colorSpace;

- (void)configure;
- (void)update;
Expand Down
41 changes: 41 additions & 0 deletions packages/skia/apple/WebGPUMetalView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,44 @@ @implementation WebGPUMetalView {
BOOL _isConfigured;
}

- (void)applyColorSpace {
CAMetalLayer *metalLayer = (CAMetalLayer *)self.layer;
CGColorSpaceRef cs = NULL;
BOOL needsEDR = NO;
if ([_colorSpace isEqualToString:@"display-p3"]) {
cs = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3);
} else if ([_colorSpace isEqualToString:@"bt2020-hlg"]) {
cs = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_HLG);
needsEDR = YES;
} else if ([_colorSpace isEqualToString:@"bt2020-pq"]) {
cs = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
needsEDR = YES;
} else {
cs = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
}
metalLayer.colorspace = cs;
CGColorSpaceRelease(cs);
#if !TARGET_OS_OSX
metalLayer.wantsExtendedDynamicRangeContent = needsEDR;
#endif

// Set surface format — HDR needs RGBA16Float
auto &registry = rnwgpu::SurfaceRegistry::getInstance();
auto surfaceInfo = registry.getSurfaceInfo([_contextId intValue]);
if (surfaceInfo) {
surfaceInfo->setFormatOverride(needsEDR
? wgpu::TextureFormat::RGBA16Float
: wgpu::TextureFormat::Undefined);
}
}

- (void)setColorSpace:(NSString *)colorSpace {
_colorSpace = colorSpace;
if (_isConfigured) {
[self applyColorSpace];
}
}

#if !TARGET_OS_OSX
+ (Class)layerClass {
return [CAMetalLayer class];
Expand Down Expand Up @@ -45,6 +83,9 @@ - (void)configure {
.getSurfaceInfoOrCreate([_contextId intValue], gpu, size.width,
size.height)
->switchToOnscreen(nativeSurface, surface);

_isConfigured = YES;
[self applyColorSpace];
}

- (void)update {
Expand Down
10 changes: 10 additions & 0 deletions packages/skia/apple/WebGPUView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,19 @@ - (void)updateProps:(const Props::Shared &)props
WebGPUMetalView *metalView = [WebGPUMetalView new];
self.contentView = metalView;
[metalView setContextId:@(newViewProps.contextId)];
if (!newViewProps.colorSpace.empty()) {
[metalView setColorSpace:[NSString stringWithUTF8String:newViewProps.colorSpace.c_str()]];
}
[metalView configure];
}

if (newViewProps.colorSpace != oldViewProps.colorSpace) {
NSString *cs = newViewProps.colorSpace.empty()
? nil
: [NSString stringWithUTF8String:newViewProps.colorSpace.c_str()];
[(WebGPUMetalView *)self.contentView setColorSpace:cs];
}

[super updateProps:props oldProps:oldProps];
}

Expand Down
21 changes: 21 additions & 0 deletions packages/skia/cpp/rnskia/RNDawnContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,27 @@ class DawnContext {
// Get the wgpu::Device for WebGPU bindings
wgpu::Device getWGPUDevice() { return backendContext.fDevice; }

// Create a secondary Dawn device from the same adapter.
// Has its own command queue and does NOT enable ImplicitDeviceSynchronization,
// so it won't contend with the primary rendering device's mutex.
// Safe for concurrent GPU work (e.g. ML inference) alongside Skia rendering.
wgpu::Device createSecondaryDevice() {
auto adapter = DawnUtils::getMatchedAdapter(instance.get());

std::vector<wgpu::FeatureName> features = {
wgpu::FeatureName::BufferMapExtendedUsages,
#ifdef __APPLE__
wgpu::FeatureName::SharedTextureMemoryIOSurface,
wgpu::FeatureName::DawnMultiPlanarFormats,
// Note: SharedFenceMTLSharedEvent intentionally NOT enabled — it causes
// EndAccess to encode fence signals that crash with "uncommitted encoder".
// IOSurface data is already written by the camera before we read it.
#endif
};

return DawnUtils::requestDevice(adapter, features, false);
}

// Create an SkImage from a WebGPU texture
// The texture must have TextureBinding usage
sk_sp<SkImage> MakeImageFromTexture(wgpu::Texture texture, int width,
Expand Down
Loading
Loading