Skip to content
Merged
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
11 changes: 11 additions & 0 deletions Ports/iOSPort/nativeSources/CN1ES2compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@
// activated and the OpenGL ES 2 backend stays linked but unused. See
// Ports/iOSPort/METAL_PORT_STATUS.md for the migration plan.
//#define CN1_USE_METAL
// IPhoneBuilder.java replaces the line below with one of:
// #define CN1_METAL_COLORSPACE_SRGB
// #define CN1_METAL_COLORSPACE_DISPLAY_P3
// #define CN1_METAL_COLORSPACE_DEVICE_RGB
// #define CN1_METAL_COLORSPACE_LINEAR_SRGB
// #define CN1_METAL_COLORSPACE_EXTENDED_SRGB
// #define CN1_METAL_COLORSPACE_EXTENDED_LINEAR_SRGB
// #define CN1_METAL_COLORSPACE_NONE
// based on the `ios.metal.colorSpace` build hint. METALView.m falls back
// to sRGB when none of these are defined.
//#define CN1_METAL_COLORSPACE_PLACEHOLDER
#define USE_ES2 1
enum CN1GLenum {
CN1_GL_ALPHA_TEXTURE,
Expand Down
31 changes: 26 additions & 5 deletions Ports/iOSPort/nativeSources/METALView.m
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,37 @@ - (id)initWithCoder:(NSCoder*)coder
metalLayer.opaque = TRUE;
metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
metalLayer.framebufferOnly = YES;
// sRGB colourspace so colours match the GL path's CAEAGLLayer
// output. Without this, CG-rasterised images and gradients
// (DeviceRGB-tagged in their CGBitmapContext) display slightly
// brighter on Metal because the layer treats their bytes as
// linear-RGB instead of sRGB-encoded.
// Colour space for the Metal layer. Default is sRGB so colours
// match the GL path's CAEAGLLayer output: without it, CG-rasterised
// images and gradients (DeviceRGB-tagged in their CGBitmapContext)
// display slightly brighter on Metal because the layer treats
// their bytes as linear-RGB instead of sRGB-encoded.
//
// The build hint `ios.metal.colorSpace` selects the value (see
// IPhoneBuilder, which injects one of the CN1_METAL_COLORSPACE_*
// defines below). Set the hint to "none" to leave the layer's
// colorspace property untouched (system default).
#if defined(CN1_METAL_COLORSPACE_NONE)
// Skip setting metalLayer.colorspace entirely.
#else
#if defined(CN1_METAL_COLORSPACE_DISPLAY_P3)
CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3);
#elif defined(CN1_METAL_COLORSPACE_DEVICE_RGB)
CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
#elif defined(CN1_METAL_COLORSPACE_LINEAR_SRGB)
CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceLinearSRGB);
#elif defined(CN1_METAL_COLORSPACE_EXTENDED_SRGB)
CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB);
#elif defined(CN1_METAL_COLORSPACE_EXTENDED_LINEAR_SRGB)
CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
#else
CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
#endif
if (cs != NULL) {
metalLayer.colorspace = cs;
CGColorSpaceRelease(cs);
}
#endif
// Cap drawable pool to 3 so the GPU has at most one render in
// flight while CPU prepares the next two. Higher counts trade
// smoothness for latency and memory; 3 is the iOS default for
Expand Down
34 changes: 34 additions & 0 deletions docs/developer-guide/Working-With-iOS.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,40 @@ NOTE: This is supported for pro users as part of the crash protection feature.
To take advantage of that capability use the build hint `ios.testFlight=true` and then submit the app to the store for
beta testing. Make sure to use a release build target.

=== Choosing a colour space for the Metal renderer

When the Metal rendering backend is enabled with `ios.metal=true`, the `CAMetalLayer` is configured with the sRGB colour space by default. This matches the behaviour of the legacy OpenGL ES 2 backend on `CAEAGLLayer`: CG-rasterised images and gradients (which are tagged `DeviceRGB` in their `CGBitmapContext`) end up displayed with the same brightness on both backends.

For most apps the default is the right choice. Apps that need a different colour profile -- for example, wide-gamut artwork that should be displayed in Display P3, or a strictly device-RGB pipeline that matches a custom rendering toolchain -- can override the choice with the `ios.metal.colorSpace` build hint:

[cols="1,3"]
|===
| Value | Effect

| `sRGB` (default)
| Sets `metalLayer.colorspace` to `kCGColorSpaceSRGB`. Use this unless you have a specific reason not to.

| `displayP3`
| Sets `metalLayer.colorspace` to `kCGColorSpaceDisplayP3`. Use for wide-gamut content on devices with a P3 display.

| `deviceRGB`
| Calls `CGColorSpaceCreateDeviceRGB()`. Skips sRGB gamma encoding -- raw bytes are written to the layer.

| `linearSRGB`
| Sets `metalLayer.colorspace` to `kCGColorSpaceLinearSRGB` for linear (non-gamma-encoded) sRGB output.

| `extendedSRGB`
| Sets `metalLayer.colorspace` to `kCGColorSpaceExtendedSRGB` (extended-range sRGB, allows values outside [0,1]).

| `extendedLinearSRGB`
| Sets `metalLayer.colorspace` to `kCGColorSpaceExtendedLinearSRGB`.

| `none`
| Leaves `metalLayer.colorspace` unset, so Metal uses the system default for the device.
|===

NOTE: The hint only takes effect when `ios.metal=true`. With the OpenGL ES 2 backend the layer is `CAEAGLLayer` and the colour space is fixed by the system.

=== Accessing insecure URL's

Due to security exploits Apple blocked some access to insecure URL's which means that http code that worked before could stop working for you on iOS 9+. This is a good move, you should use https and avoid http as much as possible but that's sometimes impractical when working with an internal or debug environment.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,8 @@ public void usesClassMethod(String cls, String method) {
try {
File CN1ES2compat = new File(buildinRes, "CN1ES2compat.h");
replaceInFile(CN1ES2compat, "//#define CN1_USE_METAL", "#define CN1_USE_METAL");
String colorSpaceDefine = resolveMetalColorSpaceDefine(request.getArg("ios.metal.colorSpace", "sRGB"));
replaceInFile(CN1ES2compat, "//#define CN1_METAL_COLORSPACE_PLACEHOLDER", colorSpaceDefine);
copy(new File(buildinRes, "MainWindowMETAL.xib"), new File(buildinRes, "MainWindow.xib"));
copy(new File(buildinRes, "CodenameOne_METALViewController.xib"), new File(buildinRes, "CodenameOne_GLViewController.xib"));
} catch (Exception ex) {
Expand Down Expand Up @@ -3100,6 +3102,37 @@ private String[] getStubCompileSourceTarget(String javacPath) {
return new String[]{source, target};
}

private String resolveMetalColorSpaceDefine(String hint) {
String value = hint == null ? "sRGB" : hint.trim();
if (value.length() == 0) {
value = "sRGB";
}
String key = value.toLowerCase().replace("-", "").replace("_", "");
if (key.equals("srgb")) {
return "#define CN1_METAL_COLORSPACE_SRGB";
}
if (key.equals("displayp3") || key.equals("p3")) {
return "#define CN1_METAL_COLORSPACE_DISPLAY_P3";
}
if (key.equals("devicergb") || key.equals("device")) {
return "#define CN1_METAL_COLORSPACE_DEVICE_RGB";
}
if (key.equals("linearsrgb") || key.equals("linear")) {
return "#define CN1_METAL_COLORSPACE_LINEAR_SRGB";
}
if (key.equals("extendedsrgb")) {
return "#define CN1_METAL_COLORSPACE_EXTENDED_SRGB";
}
if (key.equals("extendedlinearsrgb")) {
return "#define CN1_METAL_COLORSPACE_EXTENDED_LINEAR_SRGB";
}
if (key.equals("none") || key.equals("default") || key.equals("system")) {
return "#define CN1_METAL_COLORSPACE_NONE";
}
log("Unknown ios.metal.colorSpace value: " + hint + " - falling back to sRGB");
return "#define CN1_METAL_COLORSPACE_SRGB";
}

private String resolveXcodebuild() {
String explicitXcodebuild = System.getenv("XCODEBUILD");
if (explicitXcodebuild != null && explicitXcodebuild.length() > 0) {
Expand Down
Loading