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
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,14 @@ extern "C"
std::string name(tmp.begin(), tmp.end());
jstring jname = env->NewStringUTF(name.c_str());

static jclass cls = (jclass) env->NewGlobalRef(env->FindClass("org/jetbrains/skiko/swing/Direct3DSwingRedrawer"));
static jclass cls = (jclass) env->NewGlobalRef(env->FindClass("org/jetbrains/skiko/graphicapi/InternalDirectXApi"));
static jmethodID method = env->GetMethodID(cls, "isAdapterSupported", "(Ljava/lang/String;)Z");

return env->CallBooleanMethod(redrawer, method, jname);
}

// TODO: extract common code with directXRedrawer
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_swing_Direct3DSwingRedrawer_chooseAdapter(
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_graphicapi_InternalDirectXApi_chooseAdapter(
JNIEnv *env, jobject redrawer, jint adapterPriority) {
gr_cp<IDXGIFactory4> deviceFactory;
if (!SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&deviceFactory)))) {
Expand Down Expand Up @@ -187,7 +187,7 @@ extern "C"
return 0;
}

JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_swing_Direct3DSwingRedrawer_createDirectXOffscreenDevice(
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_graphicapi_InternalDirectXApi_createDirectXOffscreenDevice(
JNIEnv *env, jobject redrawer, jlong adapterPtr) {

gr_cp<IDXGIFactory4> deviceFactory;
Expand Down Expand Up @@ -260,7 +260,7 @@ extern "C"
return toJavaPointer(d3dDevice);
}

JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_swing_Direct3DSwingRedrawer_makeDirectXRenderTargetOffScreen(
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_graphicapi_InternalDirectXApi_makeDirectXRenderTargetOffScreen(
JNIEnv *env, jobject redrawer, jlong texturePtr) {
DirectXOffScreenTexture *texture = fromJavaPointer<DirectXOffScreenTexture *>(texturePtr);
ID3D12Resource* resource = texture->resource;
Expand All @@ -275,15 +275,15 @@ extern "C"
return reinterpret_cast<jlong>(renderTarget);
}

JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_swing_Direct3DSwingRedrawer_makeDirectXContext(
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_graphicapi_InternalDirectXApi_makeDirectXContext(
JNIEnv *env, jobject redrawer, jlong devicePtr)
{
DirectXOffscreenDevice *d3dDevice = fromJavaPointer<DirectXOffscreenDevice *>(devicePtr);
GrD3DBackendContext backendContext = d3dDevice->backendContext;
return toJavaPointer(GrDirectContext::MakeDirect3D(backendContext).release());
}

JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_swing_Direct3DSwingRedrawer_makeDirectXTexture(
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_graphicapi_InternalDirectXApi_makeDirectXTexture(
JNIEnv *env, jobject redrawer, jlong devicePtr, jlong oldTexturePtr, jint width, jint height) {
DirectXOffscreenDevice *device = fromJavaPointer<DirectXOffscreenDevice *>(devicePtr);
DirectXOffScreenTexture *oldTexture = fromJavaPointer<DirectXOffScreenTexture *>(oldTexturePtr);
Expand All @@ -307,15 +307,14 @@ extern "C"
return toJavaPointer(texture);
}

JNIEXPORT void JNICALL Java_org_jetbrains_skiko_swing_Direct3DSwingRedrawer_disposeDirectXTexture(
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_graphicapi_InternalDirectXApi_disposeDirectXTexture(
JNIEnv *env, jobject redrawer, jlong texturePtr) {
DirectXOffScreenTexture *texture = fromJavaPointer<DirectXOffScreenTexture *>(texturePtr);
delete texture;
}

JNIEXPORT jboolean JNICALL Java_org_jetbrains_skiko_swing_Direct3DSwingRedrawer_readPixels(
JNIEnv *env, jobject redrawer, jlong devicePtr, jlong texturePtr, jbyteArray byteArray) {
jbyte *bytesPtr = env->GetByteArrayElements(byteArray, nullptr);
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_graphicapi_InternalDirectXApi_waitForCompletion(
JNIEnv *env, jobject redrawer, jlong devicePtr, jlong texturePtr) {

DirectXOffscreenDevice *device = fromJavaPointer<DirectXOffscreenDevice *>(devicePtr);

Expand Down Expand Up @@ -373,6 +372,13 @@ extern "C"
fence->SetEventOnCompletion(fenceValue, fenceEvent);
WaitForSingleObject(fenceEvent, INFINITE);
}
}

JNIEXPORT jboolean JNICALL Java_org_jetbrains_skiko_graphicapi_InternalDirectXApi_readPixels(
JNIEnv *env, jobject redrawer, jlong texturePtr, jbyteArray byteArray) {
jbyte *bytesPtr = env->GetByteArrayElements(byteArray, nullptr);

DirectXOffScreenTexture *texture = fromJavaPointer<DirectXOffScreenTexture *>(texturePtr);

auto rangeLength = texture->readbackBufferWidth();
D3D12_RANGE readbackBufferRange{ 0, rangeLength };
Expand Down Expand Up @@ -408,13 +414,13 @@ extern "C"
return true;
}

JNIEXPORT void JNICALL Java_org_jetbrains_skiko_swing_Direct3DSwingRedrawer_disposeDevice(
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_graphicapi_InternalDirectXApi_disposeDevice(
JNIEnv *env, jobject redrawer, jlong devicePtr) {
DirectXOffscreenDevice *device = fromJavaPointer<DirectXOffscreenDevice *>(devicePtr);
delete device;
}

JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_swing_Direct3DSwingRedrawer_getAlignment(
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_graphicapi_InternalDirectXApi_getTextureAlignment(
JNIEnv *env, jobject redrawer) {
return D3D12_TEXTURE_DATA_PITCH_ALIGNMENT;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.jetbrains.skiko.graphicapi

import org.jetbrains.skia.BackendRenderTarget
import org.jetbrains.skia.DirectContext
import org.jetbrains.skiko.ExperimentalSkikoApi
import org.jetbrains.skiko.GpuPriority
import org.jetbrains.skiko.RenderException
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.alignedTextureWidth
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.chooseAdapter
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.createDirectXOffscreenDevice
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.disposeDevice
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.disposeDirectXTexture
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.makeDirectXContext
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.makeDirectXRenderTargetOffScreen
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.makeDirectXTexture
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.readPixels
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.waitForCompletion

/**
* Class that allows drawing into offscreen DirectX texture.
*
* Used in the `benchmarks` project:
* https://github.com/JetBrains/compose-multiplatform/blob/cedd48f99e877f8b936ff574812d573baf231307/benchmarks/multiplatform/benchmarks/src/commonMain/kotlin/MeasureComposable.kt#L16
*/
@ExperimentalSkikoApi
class DirectXOffscreenContext : AutoCloseable {
private val adapter = chooseAdapter(GpuPriority.Integrated)

private val device = createDirectXOffscreenDevice(adapter).also {
if (it == 0L) {
throw RenderException("Failed to create DirectX12 device.")
}
}

val directContext = DirectContext(makeDirectXContext(device))

override fun close() {
directContext.close()
disposeDevice(device)
}

inner class Texture(desiredWidth: Int, desiredHeight: Int) : AutoCloseable {
/**
* Aligned width/height that is needed for performance optimization,
* since DirectX uses aligned bytebuffer.
*/
val actualWidth = alignedTextureWidth(desiredWidth)

val actualHeight = desiredHeight

private val texture = makeDirectXTexture(device, 0, actualWidth, actualHeight).also {
if (it == 0L) {
throw RenderException("Can't allocate DirectX resources")
}
}

val backendRenderTarget = BackendRenderTarget(makeDirectXRenderTargetOffScreen(texture))

override fun close() {
backendRenderTarget.close()
disposeDirectXTexture(texture)
}

fun waitForCompletion() {
waitForCompletion(device, texture)
}

fun readPixels(byteArray: ByteArray) {
if (!readPixels(texture, byteArray)) {
throw RenderException("Couldn't read pixels")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.jetbrains.skiko.graphicapi

import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skiko.GpuPriority
import org.jetbrains.skiko.GraphicsApi
import org.jetbrains.skiko.Library
import org.jetbrains.skiko.hostOs
import org.jetbrains.skiko.isVideoCardSupported

internal object InternalDirectXApi {
init {
Library.load()
}

private external fun getTextureAlignment(): Long
private val rowBytesAlignment = getTextureAlignment().toInt()
private val widthSizeAlignment = rowBytesAlignment / 4

/**
* Calculate aligned width/height that is needed for performance optimization,
* since DirectX uses aligned bytebuffer.
*/
fun alignedTextureWidth(width: Int) = if (width % widthSizeAlignment != 0) {
width + widthSizeAlignment - (width % widthSizeAlignment);
} else {
width
}

// Called from native code
private fun isAdapterSupported(name: String) = isVideoCardSupported(GraphicsApi.DIRECT3D, hostOs, name)

fun chooseAdapter(adapterPriority: GpuPriority): NativePointer = chooseAdapter(adapterPriority.ordinal)
private external fun chooseAdapter(adapterPriority: Int): NativePointer
external fun createDirectXOffscreenDevice(adapter: NativePointer): NativePointer
external fun makeDirectXContext(device: NativePointer): NativePointer

external fun waitForCompletion(device: NativePointer, texturePtr: NativePointer)
external fun readPixels(texturePtr: NativePointer, byteArray: ByteArray): Boolean


/**
* Provides ID3D12Resource texture taking given [oldTexturePtr] into account
* since it can be reused if width and height are not changed,
* or the new one will be created.
*/
external fun makeDirectXTexture(device: NativePointer, oldTexturePtr: NativePointer, width: Int, height: Int): NativePointer
external fun disposeDirectXTexture(texturePtr: NativePointer)

external fun makeDirectXRenderTargetOffScreen(texturePtr: NativePointer): NativePointer

external fun disposeDevice(device: NativePointer)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,19 @@ package org.jetbrains.skiko.swing

import org.jetbrains.skia.*
import org.jetbrains.skiko.*
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.alignedTextureWidth
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.createDirectXOffscreenDevice
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.disposeDirectXTexture
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.chooseAdapter
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.disposeDevice
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.makeDirectXContext
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.makeDirectXRenderTargetOffScreen
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.makeDirectXTexture
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.readPixels
import org.jetbrains.skiko.graphicapi.InternalDirectXApi.waitForCompletion
import java.awt.Graphics2D

// TODO reuse DirectXOffscreenContext
internal class Direct3DSwingRedrawer(
swingLayerProperties: SwingLayerProperties,
private val renderDelegate: SkikoRenderDelegate,
Expand All @@ -15,7 +26,7 @@ internal class Direct3DSwingRedrawer(
}
}

private val adapter = chooseAdapter(swingLayerProperties.adapterPriority.ordinal).also {
private val adapter = chooseAdapter(swingLayerProperties.adapterPriority).also {
onDeviceChosen("DirectX12") // TODO: properly get name
}

Expand All @@ -33,8 +44,6 @@ internal class Direct3DSwingRedrawer(

private var texturePtr: Long = 0
private var bytesToDraw = ByteArray(0)
private val rowBytesAlignment = getAlignment().toInt()
private val widthSizeAlignment = rowBytesAlignment / 4

init {
onContextInit()
Expand All @@ -50,15 +59,9 @@ internal class Direct3DSwingRedrawer(

override fun onRender(g: Graphics2D, width: Int, height: Int, nanoTime: Long) {
autoCloseScope {
// Calculate aligned width that is needed for performance optimization,
// since DirectX uses aligned bytebuffer.
// So we will have [Surface] with width == [alignedWidth],
// We will have [Surface] with width == [alignedWidth],
// but imitate (for SkikoRenderDelegate and Swing) like it has width == [width].
val alignedWidth = if (width % widthSizeAlignment != 0) {
width + widthSizeAlignment - (width % widthSizeAlignment);
} else {
width
}
val alignedWidth = alignedTextureWidth(width)

texturePtr = makeDirectXTexture(device, texturePtr, alignedWidth, height)
if (texturePtr == 0L) {
Expand Down Expand Up @@ -90,7 +93,8 @@ internal class Direct3DSwingRedrawer(
bytesToDraw = ByteArray(bytesArraySize)
}

if(!readPixels(device, texturePtr, bytesToDraw)) {
waitForCompletion(device, texturePtr)
if(!readPixels(texturePtr, bytesToDraw)) {
throw RenderException("Couldn't read pixels")
}

Expand All @@ -100,27 +104,4 @@ internal class Direct3DSwingRedrawer(
private fun makeRenderTarget() = BackendRenderTarget(
makeDirectXRenderTargetOffScreen(texturePtr)
)

// Called from native code
private fun isAdapterSupported(name: String) = isVideoCardSupported(GraphicsApi.DIRECT3D, hostOs, name)

private external fun chooseAdapter(adapterPriority: Int): Long
private external fun createDirectXOffscreenDevice(adapter: Long): Long
private external fun makeDirectXContext(device: Long): Long

private external fun readPixels(device: Long, texturePtr: Long, byteArray: ByteArray): Boolean

private external fun getAlignment(): Long

/**
* Provides ID3D12Resource texture taking given [oldTexturePtr] into account
* since it can be reused if width and height are not changed,
* or the new one will be created.
*/
private external fun makeDirectXTexture(device: Long, oldTexturePtr: Long, width: Int, height: Int): Long
private external fun disposeDirectXTexture(texturePtr: Long)

private external fun makeDirectXRenderTargetOffScreen(texturePtr: Long): Long

private external fun disposeDevice(device: Long)
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,14 @@ open class SkiaSwingLayer(
renderDelegate: SkikoRenderDelegate,
analytics: SkiaLayerAnalytics = SkiaLayerAnalytics.Empty,
externalAccessibleFactory: ((Component) -> Accessible)? = null,
private val properties: SkiaLayerProperties = SkiaLayerProperties()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems unrelated + breaking compatibility for no reason. Can we avoid it in this PR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed in the test - to pass the graphic API:
https://github.com/JetBrains/skiko/pull/1016/files#diff-f09f8e25b6e613b4acc42736872562fec343ba85cd033a84464e2ee426a1cbd7R188

As for compatibility, we don't support it in Skiko

) : JPanel() {
internal companion object {
init {
Library.load()
}
}

private val properties = SkiaLayerProperties()

private var isInitialized = false

@Volatile
Expand Down
Loading
Loading