Skip to content

Conversation

@Alovchin91
Copy link
Member

@Alovchin91 Alovchin91 commented Jan 30, 2025

This change adds ANGLE renderer support to Skiko.

ANGLE project translates OpenGL ES API calls to the native platform's graphics API. This is mostly useful on Windows where ANGLE provides the Direct3D 11 backend.

Right now, there is a gap in Skiko's graphics support. On Windows ARM, Skia doesn't support OpenGL yet. At the same time, some GPUs, like Parallels or VMware virtual GPUs, don't support DirectX 12. This means Skiko falls back to the slow software rendering on those GPUs. ANGLE solves this problem by providing an easy-to-use DirectX 11 backend.

In addition, ANGLE is used by Chromium-based browsers and apps, so we expect it to be more stable than Skia's DirectX backend.

@igordmn igordmn self-requested a review January 30, 2025 10:50
@@ -43,7 +43,7 @@ case $SKIA_TARGET in
;;
Copy link
Collaborator

Choose a reason for hiding this comment

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

It will be good if we can include Angle as a jar via skiko-awt-runtime-angle-$platform

Copy link
Member Author

@Alovchin91 Alovchin91 Jan 31, 2025

Choose a reason for hiding this comment

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

Let me see if I can figure this out

Copy link
Collaborator

Choose a reason for hiding this comment

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

I have done this in the new branch. It also downloads the binaries from https://github.com/JetBrains/angle-pack/releases

@igordmn igordmn self-requested a review January 31, 2025 13:38
@Alovchin91 Alovchin91 force-pushed the alovchin/angle branch 4 times, most recently from b951351 to 87790e5 Compare February 7, 2025 15:34
@Alovchin91 Alovchin91 marked this pull request as ready for review February 7, 2025 15:42
TCHAR libEGL[MAX_PATH] = TEXT("");
TCHAR libGLESv2[MAX_PATH] = TEXT("");
HMODULE hmodule = nullptr;
THROW_IF_NULL(GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Where should I put dll's in case I run a Java library?

Worked only after I packaging via jpackage, and placing the libs near the jars. But not sure how it should work without it, for example, when I run via Gradle that defines a classpath in different cached folders.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure how to answer this. This code looks for the ANGLE libraries in the exact same directory as skiko-windows-<ARCH>.dll for security (because we don't want to load it from a random place in the system). Does Skiko DLL get extracted somewhere in the Gradle case?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Does Skiko DLL get extracted somewhere in the Gradle case

Yes, it has 3-way logic:

  • look in the skiko.library.path
  • look in the jar resources, unpack it to ~/.skiko/hash
  • look in %java.home%

Besides, it checks if a second class loader tries to load the same library and copies it if it does (needed for plugins that use different Skiko versions).

I refactored it in the branch by reusing the existed logic. Please look and verify if it works for your case (it should be loaded the same way as Skiko does).

Copy link
Collaborator

Choose a reason for hiding this comment

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

I implemented it in #1082, it rewrites the current PR logic and reuses the logic above

AngleDevice *angleDevice = new AngleDevice();
angleDevice->window = hwnd;
angleDevice->device = hdc;
angleDevice->display = getAngleEGLDisplay(angleDevice->device);
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should test how if FPS always equals refresh rate when we move a window from one display to another with different refresh rate (after removing dwmFlush)

Copy link
Collaborator

Choose a reason for hiding this comment

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

I tested on a Windows laptop with integrated AMD card:

  • it doesn't change FPS between 120hz and 165hz displays
  • though, the DIRECT3D/OPENGL don't change it either (previously on a different machine it changed it correctly)
  • checking it on one monitor and just changing the refresh rate in the system settings changes the FPS correctly

So, we can assume, that it probably behaves correctly and any deviation is probably system related

Copy link
Member Author

Choose a reason for hiding this comment

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

I would probably open an issue to see how it behaves when the system switches between a discrete and an integrated GPU, and also to make sure the window created by Skiko has the CS_OWNDC flag set.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Collaborator

@igordmn igordmn left a comment

Choose a reason for hiding this comment

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

It is good to merge after you review my last commits.

I also made #1082 for Compose use cases, please review it as well, as it affects your use case.

@@ -43,7 +43,7 @@ case $SKIA_TARGET in
;;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I have done this in the new branch. It also downloads the binaries from https://github.com/JetBrains/angle-pack/releases

TCHAR libEGL[MAX_PATH] = TEXT("");
TCHAR libGLESv2[MAX_PATH] = TEXT("");
HMODULE hmodule = nullptr;
THROW_IF_NULL(GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does Skiko DLL get extracted somewhere in the Gradle case

Yes, it has 3-way logic:

  • look in the skiko.library.path
  • look in the jar resources, unpack it to ~/.skiko/hash
  • look in %java.home%

Besides, it checks if a second class loader tries to load the same library and copies it if it does (needed for plugins that use different Skiko versions).

I refactored it in the branch by reusing the existed logic. Please look and verify if it works for your case (it should be loaded the same way as Skiko does).

AngleDevice *angleDevice = new AngleDevice();
angleDevice->window = hwnd;
angleDevice->device = hdc;
angleDevice->display = getAngleEGLDisplay(angleDevice->device);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I tested on a Windows laptop with integrated AMD card:

  • it doesn't change FPS between 120hz and 165hz displays
  • though, the DIRECT3D/OPENGL don't change it either (previously on a different machine it changed it correctly)
  • checking it on one monitor and just changing the refresh rate in the system settings changes the FPS correctly

So, we can assume, that it probably behaves correctly and any deviation is probably system related

TCHAR libEGL[MAX_PATH] = TEXT("");
TCHAR libGLESv2[MAX_PATH] = TEXT("");
HMODULE hmodule = nullptr;
THROW_IF_NULL(GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I implemented it in #1082, it rewrites the current PR logic and reuses the logic above

@igordmn
Copy link
Collaborator

igordmn commented Aug 14, 2025

@Alovchin91 approved in DM the last my commits. I approved the original commits

@igordmn igordmn merged commit eda88cd into JetBrains:master Aug 14, 2025
7 checks passed
@Alovchin91 Alovchin91 deleted the alovchin/angle branch August 14, 2025 13:04
igordmn added a commit that referenced this pull request Oct 9, 2025
An addition to [the feature
PR](#1017) that allows including
ANGLE the same way as Skiko native libraries included:
- unpack it from an additional jar
`org.jetbrains.skiko:skiko-awt-runtime-angle-$target:$version`
- find them in `skiko.library.path` directoty (a system property)
- find them in `java.home`

It reuses the same logic that is used to load `Skiko.dll`.

Currently only Windows supported, the same way as in the original PR.

No CI changes needed, as building of this jar is added as a dependency
for
`publishSkikoJvmRuntimeWindowsX64PublicationToComposeRepoRepository`/`publishSkikoJvmRuntimeWindowsArm64PublicationToComposeRepoRepository`

## Testing

Manually with `skiko.rendering.angle.enabled` true/false on
SkikoAwtSample.

## Release Notes (Skiko)
### Features - Desktop
A new experimental renderer is introduced for Windows. It uses [the
ANGLE library](https://github.com/google/angle) which has proven to be
stable because it is used inside Chromium, compared to the `DIRECT3D`
renderer that has issues on a few machines. Note that `ANGLE` still uses
`DIRECT3D` API under the hood.

1. Add this to the code to enable it:
```
System.setProperty("skiko.rendering.angle.enabled", "true")
```

2. Add the ANGLE library into the dependencies:
  - If you use Gradle:
  ```
  if (System.getProperty("os.name").startsWith("Win")) {

implementation("org.jetbrains.skiko:skiko-awt-runtime-angle-$target:$version")
  }
  ```
- If you set `skiko.library.path`. Extract `libEGL.dll`, `libGLESv2.dll`
from https://github.com/JetBrains/angle-pack/releases into this
directory.

## Release Notes (Compose)
### Features - Desktop
N/A

Until we specify how to match Skiko and Compose versions, or include it
in Compose, it is a Skiko-only experimental feature. Some projects can
include Skiko directly, it should work, but not supported as a Compose
feature.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants