From 7a6f62bc5cfc17a60eee92c252a4ccdcd0f9bcfd Mon Sep 17 00:00:00 2001 From: Igor Demin Date: Tue, 16 Apr 2024 13:18:34 +0200 Subject: [PATCH] Windows. Fix a crash on CI without `dcomp.dll` (#909) ## Issues fixed Some CI don't have this library and when we run an application we have a crash: ``` ...\skiko-windows-x64.dll: Can't find dependent libraries java.lang.UnsatisfiedLinkError: ...\skiko-windows-x64.dll: Can't find dependent libraries at java.base/jdk.internal.loader.NativeLibraries.load(Native Method) at java.base/jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open(NativeLibraries.java:388) at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:232) at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:174) at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2394) at java.base/java.lang.Runtime.load0(Runtime.java:755) at java.base/java.lang.System.load(System.java:1957) at org.jetbrains.skiko.Library.loadLibraryOrCopy(Library.kt:17) at org.jetbrains.skiko.Library.findAndLoad(Library.kt:111) at org.jetbrains.skiko.Library.load(Library.kt:56) at org.jetbrains.skia.impl.Library$Companion.staticLoad(Library.jvm.kt:12) at org.jetbrains.skia.Surface.(Surface.kt:539) at androidx.compose.ui.test.SkikoComposeUiTest.(ComposeUiTest.skikoMain.kt:172) at androidx.compose.ui.test.SkikoComposeUiTest.(ComposeUiTest.skikoMain.kt:118) at androidx.compose.ui.test.SkikoComposeUiTest.(ComposeUiTest.skikoMain.kt:139) at androidx.compose.ui.test.SkikoComposeUiTest.(ComposeUiTest.skikoMain.kt:134) at androidx.compose.ui.test.junit4.DesktopComposeTestRule.(DesktopComposeTestRule.desktop.kt:54) at androidx.compose.ui.test.junit4.DesktopComposeTestRule_desktopKt.createComposeRule(DesktopComposeTestRule.desktop.kt:41) ``` Reported in https://jetbrains.slack.com/archives/C5VQN94SH/p1712564847131459?thread_ts=1712091431.760099&cid=C5VQN94SH ## Proposed changes Load this library in runtime, and fallback to `CreateSwapChainForComposition` ## Testing 1. Run Compose with transparent window: ``` import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Surface import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import androidx.compose.material.Text import androidx.compose.runtime.* fun main() = application { var isOpen by remember { mutableStateOf(true) } if (isOpen) { Window( onCloseRequest = { isOpen = false }, title = "Transparent Window Example", transparent = true, undecorated = true, //transparent window must be undecorated ) { Surface( modifier = Modifier.fillMaxSize().padding(5.dp).shadow(3.dp, RoundedCornerShape(20.dp)), color = Color(55, 55, 55), shape = RoundedCornerShape(20.dp) //window has round corners now ) { Text("Hello World!", color = Color.White) } } } } ``` Transparency should work, there shouldn't be errors in the log. 2. change `transparent = false` Transparency shouldn't work, there shouldn't be errors in the log. This should be tested by QA. --------- Co-authored-by: Ivan Matkov --- skiko/src/awtMain/cpp/windows/DCompLibrary.cc | 24 +++++++++++++++++++ skiko/src/awtMain/cpp/windows/DCompLibrary.h | 11 +++++++++ .../awtMain/cpp/windows/directXRedrawer.cc | 6 ++--- 3 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 skiko/src/awtMain/cpp/windows/DCompLibrary.cc create mode 100644 skiko/src/awtMain/cpp/windows/DCompLibrary.h diff --git a/skiko/src/awtMain/cpp/windows/DCompLibrary.cc b/skiko/src/awtMain/cpp/windows/DCompLibrary.cc new file mode 100644 index 000000000..7cbbdd1cf --- /dev/null +++ b/skiko/src/awtMain/cpp/windows/DCompLibrary.cc @@ -0,0 +1,24 @@ +#include "DCompLibrary.h" + +namespace DCompLibrary { + static HINSTANCE getLibrary() { + static HINSTANCE library = LoadLibrary("dcomp.dll"); + return library; + } + + HRESULT DCompositionCreateDevice( + IDXGIDevice *dxgiDevice, + REFIID iid, + void **dcompositionDevice + ) { + HINSTANCE library = getLibrary(); + + if (library != nullptr) { + typedef HRESULT(WINAPI * PROC_DCompositionCreateDevice)(IDXGIDevice * dxgiDevice, REFIID iid, void **dcompositionDevice); + static auto compositionCreateDevice = (PROC_DCompositionCreateDevice) GetProcAddress(library, "DCompositionCreateDevice"); + return compositionCreateDevice(dxgiDevice, iid, dcompositionDevice); + } else { + return E_FAIL; + } + } +} diff --git a/skiko/src/awtMain/cpp/windows/DCompLibrary.h b/skiko/src/awtMain/cpp/windows/DCompLibrary.h new file mode 100644 index 000000000..8a0df3ec3 --- /dev/null +++ b/skiko/src/awtMain/cpp/windows/DCompLibrary.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace DCompLibrary { + HRESULT DCompositionCreateDevice( + IDXGIDevice *dxgiDevice, + REFIID iid, + void **dcompositionDevice + ); +} diff --git a/skiko/src/awtMain/cpp/windows/directXRedrawer.cc b/skiko/src/awtMain/cpp/windows/directXRedrawer.cc index db7a1489c..18b02f098 100644 --- a/skiko/src/awtMain/cpp/windows/directXRedrawer.cc +++ b/skiko/src/awtMain/cpp/windows/directXRedrawer.cc @@ -12,6 +12,7 @@ #include "SkSurface.h" #include "include/gpu/ganesh/SkSurfaceGanesh.h" #include "../common/interop.hh" +#include "DCompLibrary.h" #include "d3d/GrD3DTypes.h" #include @@ -20,9 +21,6 @@ #include #include -#include -#pragma comment(lib, "dcomp.lib") - const int BuffersCount = 2; class DirectXDevice @@ -95,7 +93,7 @@ class DirectXDevice HRESULT result = swapChainFactory4->CreateSwapChainForComposition(queue.get(), &swapChainDesc, nullptr, swapChain1); if (FAILED(result)) { return result; } - result = DCompositionCreateDevice(0, IID_PPV_ARGS(&dcDevice)); + result = DCompLibrary::DCompositionCreateDevice(0, IID_PPV_ARGS(&dcDevice)); if (FAILED(result)) { return result; } result = dcDevice->CreateTargetForHwnd(hWnd, true, &dcTarget); if (FAILED(result)) { return result; }