Unverified Commit 3158b8d7 authored by Roman Sedaikin's avatar Roman Sedaikin Committed by GitHub

Merge pull request #70 from JetBrains/metal

Metal API prototype.
parents 1f13701d 427dbacf
build
.gradle
.project
local.properties
.DS_Store
run.sh
......
......@@ -288,15 +288,21 @@ tasks.withType(CppCompile::class.java).configureEach {
project.tasks.register<Exec>("objcCompile") {
val inputDir = "$projectDir/src/jvmMain/objectiveC/${targetOs.id}"
val outDir = "$buildDir/objc/$target"
val names = File(inputDir).listFiles()!!.map { it.name.removeSuffix(".m") }
val srcs = names.map { "$inputDir/$it.m" }.toTypedArray()
val names = File(inputDir).listFiles()!!.map { it.name.removeSuffix(".mm") }
val srcs = names.map { "$inputDir/$it.mm" }.toTypedArray()
val outs = names.map { "$outDir/$it.o" }.toTypedArray()
workingDir = File(outDir)
val skiaDir = skiaDir.get().absolutePath
commandLine = listOf(
"clang",
"-mmacosx-version-min=10.13",
"-I$jdkHome/include",
"-I$jdkHome/include/darwin",
"-I$skiaDir",
"-I$skiaDir/include",
"-I$skiaDir/include/gpu",
"-DSK_METAL",
"-std=c++17",
"-c",
*srcs
)
......
......@@ -13,62 +13,32 @@ abstract class HardwareLayer : Canvas() {
// getDpiScale is expensive operation on some platforms, so we cache it
private var _contentScale: Float? = null
private var isInit = false
init {
@Suppress("LeakingThis")
addHierarchyListener {
if (it.changeFlags and HierarchyEvent.SHOWING_CHANGED.toLong() != 0L) {
checkIsShowing()
}
}
}
private fun checkIsShowing() {
if (!isInit && isShowing) {
internal fun defineContentScale() {
_contentScale = getDpiScale()
init()
isInit = true
}
}
override fun paint(g: Graphics) {}
protected open fun init() {
internal open fun init() {
useDrawingSurfacePlatformInfo(::nativeInit)
onInit()
}
fun dispose() {
if (isInit) {
onDispose()
internal fun dispose() {
nativeDispose()
}
}
protected open fun onInit() = Unit
protected open fun onDispose() = Unit
private external fun nativeInit(platformInfo: Long)
private external fun nativeDispose()
protected open fun contentScaleChanged() = Unit
override fun setBounds(x: Int, y: Int, width: Int, height: Int) {
super.setBounds(x, y, width, height)
checkContentScale()
}
override fun paint(g: Graphics) {
checkContentScale()
}
// TODO checkContentScale is called before init. it is ok, but when we fix getDpiScale on Linux we should check [isInit]
private fun checkContentScale() {
internal fun checkContentScale(): Boolean {
val contentScale = getDpiScale()
if (contentScale != _contentScale) {
_contentScale = contentScale
contentScaleChanged()
return true
}
return false
}
private fun getDpiScale(): Float {
......@@ -77,12 +47,6 @@ abstract class HardwareLayer : Canvas() {
return scale
}
// Should be called in Swing thread
internal abstract fun update(nanoTime: Long)
// Should be called in the OpenGL thread, and only once after update
internal abstract fun draw()
val windowHandle: Long
get() = useDrawingSurfacePlatformInfo(::getWindowHandle)
......
......@@ -6,6 +6,7 @@ import org.jetbrains.skiko.redrawer.SoftwareRedrawer
import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.redrawer.WindowsOpenGLRedrawer
import org.jetbrains.skiko.redrawer.Direct3DRedrawer
import org.jetbrains.skiko.redrawer.MetalRedrawer
import java.awt.Component
import java.awt.Window
import javax.swing.SwingUtilities
......@@ -14,7 +15,7 @@ internal interface PlatformOperations {
fun isFullscreen(component: Component): Boolean
fun setFullscreen(component: Component, value: Boolean)
fun getDpiScale(component: Component): Float
fun createRedrawer(layer: HardwareLayer, renderApi: GraphicsApi, properties: SkiaLayerProperties): Redrawer
fun createRedrawer(layer: SkiaLayer, renderApi: GraphicsApi, properties: SkiaLayerProperties): Redrawer
}
internal val platformOperations: PlatformOperations by lazy {
......@@ -33,11 +34,12 @@ internal val platformOperations: PlatformOperations by lazy {
}
override fun createRedrawer(
layer: HardwareLayer,
layer: SkiaLayer,
renderApi: GraphicsApi,
properties: SkiaLayerProperties
) = when(renderApi) {
GraphicsApi.SOFTWARE -> SoftwareRedrawer(layer)
GraphicsApi.METAL -> MetalRedrawer(layer, properties)
else -> MacOsOpenGLRedrawer(layer, properties)
}
}
......@@ -60,7 +62,7 @@ internal val platformOperations: PlatformOperations by lazy {
}
override fun createRedrawer(
layer: HardwareLayer,
layer: SkiaLayer,
renderApi: GraphicsApi,
properties: SkiaLayerProperties
) = when(renderApi) {
......@@ -101,7 +103,7 @@ internal val platformOperations: PlatformOperations by lazy {
}
override fun createRedrawer(
layer: HardwareLayer,
layer: SkiaLayer,
renderApi: GraphicsApi,
properties: SkiaLayerProperties
) = when(renderApi) {
......
......@@ -8,7 +8,14 @@ import org.jetbrains.skija.Rect
import org.jetbrains.skiko.context.ContextHandler
import org.jetbrains.skiko.context.createContextHandler
import org.jetbrains.skiko.redrawer.Redrawer
import java.awt.event.InputMethodListener
import java.awt.event.KeyListener
import java.awt.event.MouseListener
import java.awt.event.MouseMotionListener
import java.awt.event.MouseWheelListener
import java.awt.Graphics
import java.awt.event.HierarchyEvent
import javax.swing.JPanel
import javax.swing.SwingUtilities.invokeLater
import javax.swing.SwingUtilities.isEventDispatchThread
......@@ -20,11 +27,48 @@ private class PictureHolder(val instance: Picture, val width: Int, val height: I
open class SkiaLayer(
private val properties: SkiaLayerProperties = SkiaLayerProperties()
) : HardwareLayer() {
) : JPanel() {
val backedLayer : HardwareLayer
init {
setOpaque(false)
layout = null
backedLayer = object : HardwareLayer() { }
add(backedLayer)
@Suppress("LeakingThis")
backedLayer.addHierarchyListener {
if (it.changeFlags and HierarchyEvent.SHOWING_CHANGED.toLong() != 0L) {
checkIsShowing()
}
}
}
private var isInited = false
private fun checkIsShowing() {
if (!isInited && isShowing) {
backedLayer.defineContentScale()
init()
isInited = true
}
}
val contentScale: Float
get() = backedLayer.contentScale
val windowHandle: Long
get() = backedLayer.windowHandle
var fullscreen: Boolean
get() = backedLayer.fullscreen
set(value) { backedLayer.fullscreen = value }
protected open fun contentScaleChanged() = Unit
var renderer: SkiaRenderer? = null
val clipComponents = mutableListOf<ClipRectangle>()
@Volatile
private var isDisposed = false
internal var redrawer: Redrawer? = null
......@@ -36,8 +80,8 @@ open class SkiaLayer(
private val pictureRecorder = PictureRecorder()
private val pictureLock = Any()
override fun onInit() {
super.onInit()
open fun init() {
backedLayer.init()
val initialRenderApi = fallbackRenderApiQueue.removeAt(0)
contextHandler = createContextHandler(this, initialRenderApi)
redrawer = platformOperations.createRedrawer(this, initialRenderApi, properties)
......@@ -45,7 +89,7 @@ open class SkiaLayer(
redraw()
}
override fun onDispose() {
open fun dispose() {
check(!isDisposed)
check(isEventDispatchThread())
redrawer?.dispose() // we should dispose redrawer first (to cancel `draw` in rendering thread)
......@@ -53,21 +97,51 @@ open class SkiaLayer(
picture?.instance?.close()
pictureRecorder.close()
isDisposed = true
super.onDispose()
if (isInited) {
backedLayer.dispose()
}
}
override fun setBounds(x: Int, y: Int, width: Int, height: Int) {
super.setBounds(x, y, width, height)
backedLayer.setSize(width, height)
if (backedLayer.checkContentScale()) {
contentScaleChanged()
}
redrawer?.syncSize()
redraw()
revalidate()
}
override fun paint(g: Graphics) {
super.paint(g)
if (backedLayer.checkContentScale()) {
contentScaleChanged()
}
redrawer?.syncSize()
redrawer?.redrawImmediately()
}
override fun addInputMethodListener(l: InputMethodListener) {
backedLayer.addInputMethodListener(l)
}
override fun addMouseListener(l: MouseListener) {
backedLayer.addMouseListener(l)
}
override fun addMouseMotionListener(l: MouseMotionListener) {
backedLayer.addMouseMotionListener(l)
}
override fun addMouseWheelListener(l: MouseWheelListener) {
backedLayer.addMouseWheelListener(l)
}
override fun addKeyListener(l: KeyListener) {
backedLayer.addKeyListener(l)
}
private var redrawScheduled = false
/**
......@@ -97,7 +171,7 @@ open class SkiaLayer(
@Suppress("LeakingThis")
private val fpsCounter = defaultFPSCounter(this)
override fun update(nanoTime: Long) {
open fun update(nanoTime: Long) {
check(!isDisposed)
check(isEventDispatchThread())
......@@ -126,7 +200,7 @@ open class SkiaLayer(
}
}
override fun draw() {
open fun draw() {
check(!isDisposed)
contextHandler?.apply {
if (!initContext()) {
......
......@@ -43,7 +43,7 @@ internal object SkikoProperties {
private fun bestRenderApiForCurrentOS(): GraphicsApi {
when(hostOs) {
OS.MacOS -> return GraphicsApi.OPENGL
OS.MacOS -> return GraphicsApi.METAL
OS.Linux -> return GraphicsApi.OPENGL
OS.Windows -> return GraphicsApi.DIRECT3D
}
......@@ -54,8 +54,8 @@ internal object SkikoProperties {
var renderApiList = mutableListOf<GraphicsApi>()
when (hostOs) {
OS.MacOS -> renderApiList = mutableListOf(GraphicsApi.OPENGL, GraphicsApi.SOFTWARE)
OS.Linux -> renderApiList = mutableListOf(GraphicsApi.OPENGL, GraphicsApi.SOFTWARE)
OS.MacOS -> renderApiList = mutableListOf(GraphicsApi.METAL, GraphicsApi.OPENGL, GraphicsApi.SOFTWARE)
OS.Windows -> renderApiList = mutableListOf(GraphicsApi.DIRECT3D, GraphicsApi.OPENGL, GraphicsApi.SOFTWARE)
}
renderApiList.remove(head)
......
......@@ -17,6 +17,7 @@ internal fun createContextHandler(layer: SkiaLayer, renderApi: GraphicsApi): Con
GraphicsApi.SOFTWARE -> SoftwareContextHandler(layer)
GraphicsApi.OPENGL -> OpenGLContextHandler(layer)
GraphicsApi.DIRECT3D -> Direct3DContextHandler(layer)
GraphicsApi.METAL -> MetalContextHandler(layer)
else -> TODO("Unsupported yet.")
}
}
......
......@@ -4,20 +4,20 @@ import org.jetbrains.skija.ColorSpace
import org.jetbrains.skija.Surface
import org.jetbrains.skija.SurfaceColorFormat
import org.jetbrains.skija.SurfaceOrigin
import org.jetbrains.skiko.GraphicsApi
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.makeMetalContext
import org.jetbrains.skiko.makeMetalRenderTarget
import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.redrawer.MetalRedrawer
internal class MetalContextHandler(layer: SkiaLayer) : ContextHandler(layer) {
val metalRedrawer: MetalRedrawer
get() = layer.redrawer!! as MetalRedrawer
override fun initContext(): Boolean {
try {
if (context == null) {
context = makeMetalContext()
context = metalRedrawer.makeContext()
}
} catch (e: Exception) {
println("Failed to create Skia Metal context!")
println("${e.message}\nFailed to create Skia Metal context!")
return false
}
return true
......@@ -30,16 +30,22 @@ internal class MetalContextHandler(layer: SkiaLayer) : ContextHandler(layer) {
val w = (layer.width * scale).toInt().coerceAtLeast(0)
val h = (layer.height * scale).toInt().coerceAtLeast(0)
renderTarget = makeMetalRenderTarget(w, h, 0)
renderTarget = metalRedrawer.makeRenderTarget(w, h)
surface = Surface.makeFromBackendRenderTarget(
context!!,
renderTarget!!,
SurfaceOrigin.BOTTOM_LEFT,
SurfaceColorFormat.RGBA_8888,
SurfaceOrigin.TOP_LEFT,
SurfaceColorFormat.BGRA_8888,
ColorSpace.getSRGB()
)
canvas = surface!!.canvas
}
override fun flush() {
super.flush()
surface!!.flushAndSubmit()
metalRedrawer.finishFrame()
}
}
......@@ -73,7 +73,7 @@ internal class SoftwareContextHandler(layer: SkiaLayer) : ContextHandler(layer)
null
)
image = BufferedImage(colorModel, raster!!, false, null)
layer.getGraphics()?.drawImage(image!!, 0, 0, layer.width, layer.height, null)
layer.backedLayer.getGraphics()?.drawImage(image!!, 0, 0, layer.width, layer.height, null)
}
}
......
......@@ -5,11 +5,11 @@ import kotlinx.coroutines.swing.Swing
import org.jetbrains.skija.BackendRenderTarget
import org.jetbrains.skija.DirectContext
import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkiaLayerProperties
internal class Direct3DRedrawer(
private val layer: HardwareLayer,
private val layer: SkiaLayer,
private val properties: SkiaLayerProperties
) : Redrawer {
......
......@@ -5,23 +5,24 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.swing.Swing
import org.jetbrains.skiko.DrawingSurface
import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.OpenGLApi
import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.getDrawingSurface
internal class LinuxOpenGLRedrawer(
private val layer: HardwareLayer,
private val layer: SkiaLayer,
private val properties: SkiaLayerProperties
) : Redrawer {
private val context = layer.lockDrawingSurface {
private val context = layer.backedLayer.lockDrawingSurface {
it.createContext()
}
private var isDisposed = false
override fun dispose() {
check(!isDisposed)
layer.lockDrawingSurface {
layer.backedLayer.lockDrawingSurface {
it.destroyContext(context)
}
isDisposed = true
......@@ -33,7 +34,7 @@ internal class LinuxOpenGLRedrawer(
frameDispatcher.scheduleFrame()
}
override fun redrawImmediately() = layer.lockDrawingSurface {
override fun redrawImmediately() = layer.backedLayer.lockDrawingSurface {
check(!isDisposed)
update(System.nanoTime())
it.makeCurrent(context)
......@@ -73,7 +74,7 @@ internal class LinuxOpenGLRedrawer(
val isVsyncEnabled = toRedrawAlive.all { it.properties.isVsyncEnabled }
val drawingSurfaces = toRedrawAlive.map { lockDrawingSurface(it.layer) }.toList()
val drawingSurfaces = toRedrawAlive.map { lockDrawingSurface(it.layer.backedLayer) }.toList()
try {
toRedrawAlive.forEachIndexed { index, redrawer ->
drawingSurfaces[index].makeCurrent(redrawer.context)
......
......@@ -4,7 +4,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.swing.Swing
import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.OpenGLApi
import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.Task
......@@ -21,10 +21,10 @@ import kotlin.system.measureNanoTime
// P.S. MacOsOpenGLRedrawer will not be used by default in the future, because we will support Metal.
internal class MacOsOpenGLRedrawer(
private val layer: HardwareLayer,
private val layer: SkiaLayer,
private val properties: SkiaLayerProperties
) : Redrawer {
private val containerLayerPtr = layer.useDrawingSurfacePlatformInfo(::initContainer)
private val containerLayerPtr = layer.backedLayer.useDrawingSurfacePlatformInfo(::initContainer)
private val drawLock = Any()
private var isDisposed = false
......
package org.jetbrains.skiko.redrawer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.swing.Swing
import org.jetbrains.skija.BackendRenderTarget
import org.jetbrains.skija.DirectContext
import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.useDrawingSurfacePlatformInfo
import javax.swing.SwingUtilities.convertPoint
import javax.swing.SwingUtilities.getRootPane
internal class MetalRedrawer(
private val layer: SkiaLayer,
private val properties: SkiaLayerProperties
) : Redrawer {
private var isDisposed = false
private val device = layer.backedLayer.useDrawingSurfacePlatformInfo(::createMetalDevice)
private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
update(System.nanoTime())
draw()
}
override fun dispose() {
frameDispatcher.cancel()
disposeDevice(device)
isDisposed = true
}
override fun needRedraw() {
check(!isDisposed)
frameDispatcher.scheduleFrame()
}
override fun redrawImmediately() {
check(!isDisposed)
update(System.nanoTime())
draw()
}
private fun update(nanoTime: Long) {
layer.update(nanoTime)
}
private fun draw() {
layer.draw()
}
override fun syncSize() {
val globalPosition = convertPoint(layer, layer.x, layer.y, getRootPane(layer))
setContentScale(device, layer.contentScale)
resizeLayers(device,
globalPosition.x,
globalPosition.y,
layer.width.coerceAtLeast(0),
layer.height.coerceAtLeast(0)
)
}
fun makeContext() = DirectContext(
makeMetalContext(device)
)
fun makeRenderTarget(width: Int, height: Int) = BackendRenderTarget(
makeMetalRenderTarget(device, width, height)
)
fun finishFrame() = finishFrame(device)
private external fun createMetalDevice(platformInfo: Long): Long
private external fun makeMetalContext(device: Long): Long
private external fun makeMetalRenderTarget(device: Long, width: Int, height: Int): Long
private external fun disposeDevice(device: Long)
private external fun finishFrame(device: Long)
private external fun resizeLayers(device: Long, x: Int, y: Int, width: Int, height: Int)
private external fun setContentScale(device: Long, contentScale: Float)
}
......@@ -3,10 +3,10 @@ package org.jetbrains.skiko.redrawer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.swing.Swing
import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.SkiaLayer
internal class SoftwareRedrawer(
private val layer: HardwareLayer
private val layer: SkiaLayer
) : Redrawer {
private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
......
......@@ -5,16 +5,16 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.swing.Swing
import kotlinx.coroutines.withContext
import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.OpenGLApi
import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.useDrawingSurfacePlatformInfo
internal class WindowsOpenGLRedrawer(
private val layer: HardwareLayer,
private val layer: SkiaLayer,
private val properties: SkiaLayerProperties
) : Redrawer {
private val device = layer.useDrawingSurfacePlatformInfo(::getDevice)
private val device = layer.backedLayer.useDrawingSurfacePlatformInfo(::getDevice)
private val context = createContext(device)
private var isDisposed = false
......
......@@ -36,7 +36,7 @@
- (void) disposeLayer:(JNIEnv *) env
{
(*env)->DeleteGlobalRef(env, self.canvasGlobalRef);
env->DeleteGlobalRef(self.canvasGlobalRef);
self.canvasGlobalRef = NULL;
self.container = NULL;
self.window = NULL;
......@@ -71,7 +71,7 @@ LayerHandler * findByObject(JNIEnv *env, jobject object)
}
for (LayerHandler* layer in layerStorage)
{
if ((*env)->IsSameObject(env, object, layer.canvasGlobalRef) == JNI_TRUE)
if (env->IsSameObject(object, layer.canvasGlobalRef) == JNI_TRUE)
{
return layer;
}
......@@ -79,6 +79,9 @@ LayerHandler * findByObject(JNIEnv *env, jobject object)
return NULL;
}
extern "C"
{
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_nativeInit(JNIEnv *env, jobject canvas, jlong platformInfoPtr)
{
if (layerStorage == nil)
......@@ -91,7 +94,7 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_nativeInit(JNIEnv
LayerHandler *layersSet = [[LayerHandler alloc] init];
layersSet.container = [dsi_mac windowLayer];
jobject canvasGlobalRef = (*env)->NewGlobalRef(env, canvas);
jobject canvasGlobalRef = env->NewGlobalRef(canvas);
[layersSet setCanvasGlobalRef: canvasGlobalRef];
NSMutableArray<NSWindow *> *windows = [NSMutableArray arrayWithArray: [[NSApplication sharedApplication] windows]];
......@@ -153,3 +156,5 @@ void getMetalDeviceAndQueue(void** device, void** queue)
*device = (__bridge void*)fDevice;
*queue = (__bridge void*)fQueue;
}
} // extern C
\ No newline at end of file
#ifdef SK_METAL
#import <jawt.h>
#import <jawt_md.h>
#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#import <GrBackendSurface.h>
#import <GrDirectContext.h>
#import <mtl/GrMtlBackendContext.h>
#import <mtl/GrMtlTypes.h>
@interface AWTMetalLayer : CAMetalLayer
@property jobject javaRef;
@end
@interface MetalDevice : NSObject
@property (retain, strong) CALayer *container;
@property (retain, strong) AWTMetalLayer *layer;
@property (retain, strong) id<MTLDevice> device;
@property (retain, strong) id<MTLCommandQueue> queue;
@property (retain, strong) id<CAMetalDrawable> drawableHandle;
@end
@implementation AWTMetalLayer
- (id)init
{
self = [super init];
assert(self != NULL);
[self removeAllAnimations];
[self setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
[self setNeedsDisplayOnBoundsChange: YES];
return self;
}
@end
@implementation MetalDevice
- (id) init
{
self = [super init];
if (self)
{
self.layer = nil;
self.device = nil;
self.queue = nil;
self.drawableHandle = nil;
}
return self;
}
@end
extern "C"
{
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_makeMetalContext(
JNIEnv* env, jobject redrawer, jlong devicePtr)
{
MetalDevice *device = (MetalDevice *) devicePtr;
GrMtlBackendContext backendContext = {};
backendContext.fDevice.retain((GrMTLHandle) device.device);
backendContext.fQueue.retain((GrMTLHandle) device.queue);
return (jlong) GrDirectContext::MakeMetal(backendContext).release();
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_makeMetalRenderTarget(
JNIEnv * env, jobject redrawer, jlong devicePtr, jint width, jint height)
{
MetalDevice *device = (MetalDevice *) devicePtr;
device.layer.drawableSize = CGRectMake(0, 0, width, height).size;
id<CAMetalDrawable> currentDrawable = [device.layer nextDrawable];
GrMtlTextureInfo info;
info.fTexture.retain(currentDrawable.texture);
GrBackendRenderTarget* renderTarget = new GrBackendRenderTarget(width, height, 0, info);
device.drawableHandle = currentDrawable;
return (jlong) renderTarget;
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_createMetalDevice(
JNIEnv *env, jobject redrawer, jlong platformInfoPtr)
{
MetalDevice *device = [MetalDevice new];
NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) platformInfoPtr;
CALayer *container = [dsi_mac windowLayer];
[container removeAllAnimations];
[container setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)];
[container setNeedsDisplayOnBoundsChange: YES];
AWTMetalLayer *layer = [AWTMetalLayer new];
[container addSublayer: layer];
layer.javaRef = env->NewGlobalRef(redrawer);
id<MTLDevice> fDevice = MTLCreateSystemDefaultDevice();
id<MTLCommandQueue> fQueue = [fDevice newCommandQueue];
device.container = container;
device.layer = layer;
device.device = fDevice;
device.queue = fQueue;
device.layer.device = device.device;
device.layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
device.layer.contentsGravity = kCAGravityTopLeft;
CGFloat transparent[] = { 0.0f, 0.0f, 0.0f, 0.0f };
device.layer.backgroundColor = CGColorCreate(CGColorSpaceCreateDeviceRGB(), transparent);
device.layer.opaque = NO;
return (jlong) device;
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_resizeLayers(
JNIEnv *env, jobject redrawer, jlong devicePtr, jint x, jint y, jint width, jint height)
{
MetalDevice *device = (MetalDevice *) devicePtr;
y = (int)device.container.frame.size.height - y - height;
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
device.layer.frame = CGRectMake(x, y, width, height);
[CATransaction commit];
[CATransaction flush];
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_setContentScale(JNIEnv *env, jobject obj, jlong devicePtr, jfloat contentScale)
{
MetalDevice *device = (MetalDevice *) devicePtr;
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
assert(contentScale != 0);
device.layer.contentsScale = contentScale;
[CATransaction commit];
[CATransaction flush];
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_finishFrame(
JNIEnv *env, jobject redrawer, jlong devicePtr)
{
MetalDevice *device = (MetalDevice *) devicePtr;
id<CAMetalDrawable> currentDrawable = device.drawableHandle;
id<MTLCommandBuffer> commandBuffer = [device.queue commandBuffer];
commandBuffer.label = @"Present";
[commandBuffer presentDrawable:currentDrawable];
[commandBuffer commit];
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MetalRedrawer_disposeDevice(
JNIEnv *env, jobject redrawer, jlong devicePtr)
{
MetalDevice *device = (MetalDevice *) devicePtr;
[device.layer removeFromSuperlayer];
env->DeleteGlobalRef(device.layer.javaRef);
[device.layer release];
[device release];
}
} // extern C
#endif
\ No newline at end of file
......@@ -36,13 +36,13 @@ JavaVM *jvm = NULL;
{
assert(jvm != NULL);
JNIEnv *env;
(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
jvm->AttachCurrentThread((void **)&env, NULL);
static jclass cls = NULL;
static jmethodID method = NULL;
if (!cls) cls = (*env)->GetObjectClass(env, self.javaRef);
if (!method) method = (*env)->GetMethodID(env, cls, "canDraw", "()Z");
return (*env)->CallBooleanMethod(env, self.javaRef, method);
if (!cls) cls = env->GetObjectClass(self.javaRef);
if (!method) method = env->GetMethodID(cls, "canDraw", "()Z");
return env->CallBooleanMethod(self.javaRef, method);
}
-(void)drawInCGLContext:(CGLContextObj)ctx
......@@ -54,22 +54,25 @@ JavaVM *jvm = NULL;
assert(jvm != NULL);
JNIEnv *env;
(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
jvm->AttachCurrentThread((void **)&env, NULL);
static jclass cls = NULL;
static jmethodID method = NULL;
if (!cls) cls = (*env)->GetObjectClass(env, self.javaRef);
if (!method) method = (*env)->GetMethodID(env, cls, "performDraw", "()V");
(*env)->CallVoidMethod(env, self.javaRef, method);
if (!cls) cls = env->GetObjectClass(self.javaRef);
if (!method) method = env->GetMethodID(cls, "performDraw", "()V");
env->CallVoidMethod(self.javaRef, method);
[super drawInCGLContext:ctx pixelFormat:pf forLayerTime:t displayTime:ts];
}
@end
extern "C"
{
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_initContainer(JNIEnv *env, jobject redrawer, jlong platformInfoPtr)
{
(*env)->GetJavaVM(env, &jvm);
env->GetJavaVM(&jvm);
NSObject<JAWT_SurfaceLayers>* dsi_mac = (__bridge NSObject<JAWT_SurfaceLayers> *) platformInfoPtr;
......@@ -93,7 +96,7 @@ JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_
CALayer *container = (CALayer *) containerPtr;
AWTGLLayer *glLayer = [AWTGLLayer new];
glLayer.javaRef = (*env)->NewGlobalRef(env, layer);
glLayer.javaRef = env->NewGlobalRef(layer);
[glLayer setNeedsDisplayOnBoundsChange: setNeedsDisplayOnBoundsChange];
[container addSublayer: glLayer];
......@@ -104,7 +107,7 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_MacOsOpenGLRedrawerKt_d
{
AWTGLLayer *glLayer = (AWTGLLayer *) ptr;
[glLayer removeFromSuperlayer];
(*env)->DeleteGlobalRef(env, glLayer.javaRef);
env->DeleteGlobalRef(glLayer.javaRef);
[glLayer release];
}
......@@ -139,3 +142,5 @@ JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_AWTGLLayer_setFrame(JNI
glLayer.frame = CGRectMake(x, y, width, height);
[CATransaction commit];
}
} // extern C
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment