Compare commits

..

78 Commits

Author SHA1 Message Date
Vitaly Provodin
26923ff2f1 update exclude list on results of 25.0.1_190.10 test runs 2025-11-06 15:09:26 +04:00
Vitaly Provodin
ceb2dcb091 update exclude list on results of 25.0.1_186.7 test runs 2025-10-30 15:56:05 +04:00
Nikita Tsarev
fe29694915 JBR-9547: Fix macOS build failure with Xcode 26.0.1 2025-10-28 12:50:20 +01:00
Rob McKenna
a336a88057 8367031: [backout] Change java.time month/day field types to 'byte'
Backport-of: 00be643fa3eff6fd66d39f5f5ea70ff347296318
2025-10-26 03:05:42 +04:00
Nibedita Jena
6c753b5d49 8368308: ISO 4217 Amendment 180 Update
Backport-of: 3f9c665586705c833674ae998f49cabbc7e15615
2025-10-26 03:05:41 +04:00
Nibedita Jena
099394e43a 8366223: ZGC: ZPageAllocator::cleanup_failed_commit_multi_partition is broken
Backport-of: 009612805f79e37d9ce4e3f5c90627b635b095cf
2025-10-26 03:05:41 +04:00
Matias Saavedra Silva
71241ecc59 8352637: Enhance bytecode verification
Reviewed-by: dlong
Backport-of: d9bf0c2ca2d52d783a8122504cac9566d42b22df
2025-10-26 03:05:41 +04:00
Nibedita Jena
377b0902b1 8360647: [XWayland] [OL10] NumPad keys are not triggered
Backport-of: 4d5fb6eb8bb66556f06dada72df531d537cf32c2
2025-10-26 03:05:41 +04:00
Ravi Reddy
04fdd306ab 8356294: Enhance Path Factories
Backport-of: 5835cefe4946524af3be4933b20cd1b0005b0ad0
2025-10-26 03:05:40 +04:00
Nibedita Jena
06b190878a 8361212: Remove AffirmTrust root CAs
Backport-of: e58859e8acc19bfd8aaa80e98534651e83850a97
2025-10-26 03:05:40 +04:00
Nibedita Jena
b8cfdc28dc 8360937: Enhance certificate handling
Reviewed-by: mullan
Backport-of: f2fba5a55176ca82985ca42996cef36be7b7500a
2025-10-26 03:05:40 +04:00
Taizo Kurashige
1e7ae9c8d5 8358819: The first year is not displayed correctly in Japanese Calendar
Backport-of: 99829950f6
2025-10-26 03:05:39 +04:00
Dingli Zhang
0c7f8b1b00 8361829: [TESTBUG] RISC-V: compiler/vectorization/runner/BasicIntOpTest.java fails with RVV but not Zvbb
Backport-of: 2e7e272d7b
2025-10-26 03:05:39 +04:00
Dingli Zhang
3f53deeaf2 8361532: RISC-V: Several vector tests fail after JDK-8354383
Backport-of: e0245682c8
2025-10-26 03:05:39 +04:00
Matthias Baesken
a2f4b57bb2 8357826: Avoid running some jtreg tests when asan is configured
Backport-of: d7aa349820
2025-10-26 03:05:38 +04:00
Kieran Farrell
a471e942e4 8359454: Enhance String handling
Backport-of: 2f2665738a67aeed224b54870608a346eb627d2a
2025-10-26 03:05:38 +04:00
Ian Myers
d5ceea456c 8358577: Test serviceability/jvmti/thread/GetCurrentContendedMonitor/contmon01/contmon01.java failed: unexpexcted monitor object
Backport-of: 8f487d26c0
2025-10-26 03:05:38 +04:00
Matthias Baesken
e6cb0a1b56 8360533: ContainerRuntimeVersionTestUtils fromVersionString fails with some docker versions
Backport-of: 97ec9d3e0a
2025-10-26 03:05:38 +04:00
Renjith Kannath Pariyangad
ab7a9f8f87 8358452: JNI exception pending in Java_sun_awt_screencast_ScreencastHelper_remoteDesktopKeyImpl of screencast_pipewire.c:1214 (ID: 51119)
Backport-of: 2103dc15cb
2025-10-26 03:05:37 +04:00
Fei Yang
6b43d1a768 8359270: C2: alignment check should consider base offset when emitting arraycopy runtime call
Backport-of: 6b4393917a
2025-10-26 03:05:37 +04:00
Fei Yang
dfe78a31f5 8360179: RISC-V: Only enable BigInteger intrinsics when AvoidUnalignedAccess == false
Backport-of: 34412da52b
2025-10-26 03:05:37 +04:00
Fei Yang
e48181b9aa 8359218: RISC-V: Only enable CRC32 intrinsic when AvoidUnalignedAccess == false
Backport-of: 65e63b6ab4
2025-10-26 03:05:36 +04:00
Rob McKenna
f0d54ab6e5 8359059: Bump version numbers for 25.0.1
Reviewed-by: iris
2025-10-26 03:05:36 +04:00
Vitaly Provodin
20b513f189 JBR-4154 use -V to sort versions 2025-10-26 03:05:28 +04:00
Vitaly Provodin
58f04296e7 fixup! update exclude list on results of 25.176.2 test runs 2025-10-26 03:05:10 +04:00
Maxim Kartashev
7a2d71329d JBR-9503 Wayland: IDE frame disappears after click on 'Cancel' 2025-10-24 13:39:30 +04:00
Maxim Kartashev
54276e869b JBR-9521 sources/TestNoNULL.java: Test found 14 usages of 'NULL' in source files 2025-10-23 12:13:29 +04:00
Ilia K
75dcbe8f20 JBR-9515 Allow size of per-directory buffer used to retrieve events to be configurable to avoid OVERFLOW_EVENT 2025-10-22 14:19:59 +04:00
Nikita Tsarev
37b13e2f17 JBR-9527: Fix NPE with WLDataDevice.performDeletionsOnEDT() when headless [WLToolkit] 2025-10-22 11:57:25 +02:00
Vitaly Provodin
bde37c774a update exclude list on results of 25.176.2 test runs 2025-10-22 07:30:54 +04:00
Nikita Tsarev
8dfd14f671 JBR-8353: Use a deletion queue to destroy data transfer objects [WLToolkit] 2025-10-20 21:09:46 +02:00
Nikita Gubarkov
49bed44184 JBR-9486 Vulkan: Handle VK_ERROR_OUT_OF_DATE_KHR 2025-10-16 20:32:31 +02:00
Vitaly Provodin
a270d9e6a3 clean up fixed issues from exclude lists, start 2026.1 2025-10-16 14:48:45 +04:00
Vitaly Provodin
11e3c06ba0 update exclude list on results of 25.165.38 test runs 2025-10-16 06:50:07 +04:00
Nikita Gubarkov
95c6f7c37f JBR-9481 Vulkan: OOM-safe BLIT 2025-10-15 18:44:53 +02:00
Nikita Provotorov
c4f2b64d19 JBR-5672: Wayland: support input methods.
Providing support of the "text-input-unstable-v3" protocol, except its surrounding text API (zwp_text_input_v3::set_surrounding_text + zwp_text_input_v3::delete_surrounding_text).
A new system property "sun.awt.wl.im.enabled"[=true|false] is introduced to enable/disable all the integrations with Wayland's native input methods. Set to 'true' by default.

(cherry picked from commit 1c37490f00)
2025-10-15 03:43:41 +02:00
Nikita Provotorov
9a6415585d fixup! JBR-6376: implement detecting of OS theme on linux
Adding the D-Bus libs as a dependency: it's required for src/java.desktop/linux/native/libawt/awt/dbus_interface.h

(cherry picked from commit 3fb240f4aa)
2025-10-14 20:53:18 +02:00
Nikita Gubarkov
51d7cd2afb JBR-9477 JBR API: Update local artifact group 2025-10-14 12:40:21 +02:00
bourgesl
3c66bdc1e9 JBR-9375 macOS: Right-click context menu shows blurry animation when opening
Disable NSWindow animationBehavior (=NSWindowAnimationBehaviorNone) by default except if the system property 'apple.awt.window.animation' = true
+ Fixed J2dRlsTraceLn
2025-10-14 08:16:47 +02:00
Nikita Gubarkov
e65cb43ba1 JBR-9438 Vulkan: JBR API for accessing configuration info 2025-10-13 16:52:02 +02:00
Nikita Tsarev
7c84fbe810 JBR-8353: Fix wrong order of java/wayland object destruction in DataOffer/DataSource [WLToolkit] 2025-10-10 14:29:00 +02:00
Vitaly Provodin
6c47534225 update exclude list on results of 25.152.37 test runs 2025-10-10 03:17:36 +04:00
Maxim Kartashev
ffde0964ef JBR-9451 Wayland: Calling other JNI functions in the scope of Get/ReleasePrimitiveArrayCritical or Get/ReleaseStringCritical 2025-10-09 16:38:25 +04:00
Alexey Ushakov
d0299a4c6a JBR-9292 Vulkan: RenderPerfTest missing frames 2025-10-08 22:51:35 +02:00
Nikita Gubarkov
1186447190 JBR-9457 Vulkan: Enable accelerated surfaces by default 2025-10-08 22:10:35 +02:00
Nikita Gubarkov
4eab05ccd9 JBR-7646 Vulkan: Implement painting modes 2025-10-08 22:10:35 +02:00
Nikita Gubarkov
74b4df4496 JBR-9450 Vulkan: Unify pipelines 2025-10-08 22:09:48 +02:00
Nikita Gubarkov
8f660e630c JBR-9439 Vulkan: Fix blit composites 2025-10-08 22:09:40 +02:00
Nikita Gubarkov
4bd1551dc4 JBR-8344 Vulkan: Fix color XOR 2025-10-08 16:46:02 +02:00
Nikita Tsarev
4879508145 JBR-9449: Use wl_proxy_create_wrapper when creating data source objects for thread-safety [WLToolkit] 2025-10-07 10:25:16 +02:00
Maxim Kartashev
84473294fb JBR-9364 Wayland: Popups are shifted with multiple monitor setup after monitor reconnected (Ubuntu) 2025-10-07 10:17:17 +04:00
bourgesl
57e694c1ae JBR-9408 Fix Marlin renderer statistics
Revert JBR-9283 changes to StatLong (completely) to avoid future conflicts

(cherry picked from commit bc60599b45bddcb2d251035f945b5616e43554d2)
(cherry picked from commit 5be4830ecf30e2c74d1e828e3482edca03d166c6)
2025-10-05 23:17:06 +02:00
bourgesl
12e0466566 JDK-8341381 Random lines appear in graphic causing by the fix of JDK-8297230
- Fix cubic offsetting artefacts (sort cubic roots + fixed numerical accuracy problem in ROC^2-w^2 = 0 solver + fixed EliminateInf)
- Restored lower precision using ulp(float) in point, line or flat bezier curve checks

(cherry picked from commit e72b87e6538dda97e6f0f2840040c6864b3f146e)
2025-10-05 23:04:09 +02:00
Nikita Gubarkov
02ab184c01 JBR-9425 Vulkan: Fix surface disposal 2025-10-01 13:19:46 +02:00
Vitaly Provodin
bf7dea965a update exclude list on results of 25.144.34 test runs 2025-10-01 08:48:01 +04:00
Maxim Kartashev
58b3c7ed57 JBR-9378 Wayland: Nullpointer exception in DefaultFrameDecoration, IDE hang on KDE 2025-09-30 16:34:04 +04:00
Maxim Kartashev
631811837d JBR-6990 Wayland: utilize relative-pointer-unstable-v1 protocol 2025-09-30 09:43:53 +04:00
Maxim Kartashev
3ff4b846da JBR-9384 Wayland: ShowPopupAfterHidePopupTest.java fails 2025-09-29 16:24:01 +04:00
Alexey Ushakov
b175e32148 JBR-9301 Vulkan: SwingSet2 crash window server
Clear native peer on windowClosing in Frame object

(cherry picked from commit 441bb9d12ebff4bf2e1629115b9414b0b22ec858)
2025-09-23 15:23:44 +02:00
Nikita Tsarev
8e287c09a0 JBR-9330: Set scale for drag images [WLToolkit] 2025-09-22 17:12:10 +02:00
Vitaly Provodin
28f9408b29 fixup! update exclude list on results of 25.135.30 test runs 2025-09-22 04:51:45 +04:00
Vitaly Provodin
338f1df777 update exclude list on results of 25.135.30 test runs 2025-09-21 16:19:29 +04:00
Alexey Ushakov
690349f07c JBR-9376 Vulkan: Incorrect deallocation in VKDevice_Reset
Moved texture pool into VKRenderer

(cherry picked from commit 392514fc9daf57a501a0c1598fe2a0782045f335)
2025-09-19 12:38:41 +02:00
Dmitry Batrak
fe1d8d85ae JBR-9365 Unnecessary operations on tree node update
(cherry picked from commit cc5d9ca55c484bf8359135498836a64cc49c3406)
2025-09-18 13:41:52 +03:00
Maxim Kartashev
a1d745006b JBR-9332 Wayland: popups are not closed when parent looses focus 2025-09-16 13:36:33 +04:00
bourgesl
2d345b9aab JBR-9351 jb/java/awt/Counters/UpdateWindowsCounter.java fails by time out
Fixed Timers to be daemon
2025-09-16 08:02:29 +02:00
bourgesl
d720f1ee4a JBR-9350 javax/swing/JOptionPane/8081019/bug8081019.java: Cannot invoke "sun.lwawt.LWWindowPeer.getTarget()" because "this.peer" is null
Added peer null checks + use perfCountersEnabled flag
2025-09-16 08:00:43 +02:00
Nikita Provotorov
a2fa35786e JBR-9349 Do_Not_Use_calloc_Use_safe_Calloc_Instead: is not a member of global namespace
Refactoring the code of JBR-4478 so that C++ standard library headers get only included in AccessibleCaret.cpp and not in any headers.

(cherry picked from commit 12bbc14e5e)
2025-09-14 13:44:24 +02:00
bourgesl
e66f996829 JBR-9283 Enhance Window counters to provide statistics
Enhanced Window counters to provide statistics (using the new marlin StatDouble class), enhanced logging code to dump regularly (10s) window stats, added shutdown hook, bumpCounter() renamed to incrementCounter(), added addStat(window, name, value) used by MTLLayer to report blitTexture & nextDrawable timings (ms), use InnocuousThread for shutdown hooks, fixed D3DSurfaceData bumpCounter() usages to incrementCounter()

(cherry picked from commit af437d9d61)
2025-09-12 15:18:23 +02:00
Vitaly Provodin
8569475930 update exclude list on results of 25.107.28 test runs 2025-09-12 13:33:33 +04:00
Nikita Gubarkov
7433067506 JBR-9111 Vulkan: Lock RQ while disposing the surface 2025-09-11 15:25:26 +02:00
Nikita Gubarkov
2e517af41f JBR-9236 Vulkan: Proper builds without Vulkan 2025-09-11 11:03:12 +02:00
Nikita Tsarev
a54096f118 JBR-9336: Fix build error with old wayland protocol headers [WLToolkit] 2025-09-11 08:55:16 +02:00
Maxim Kartashev
d3f0367a19 JBR-9310 Wayland: Gtk-WARNING in swing app 2025-09-10 10:23:53 +04:00
Nikita Tsarev
14f93c154e JBR-9326 Support TransferHandler.setDragImage [WLToolkit] 2025-09-09 11:08:31 +02:00
Maxim Kartashev
07a1aee823 JBR-9302 Wayland: default window decoration to look more like KDE 2025-09-08 15:44:48 +04:00
Maxim Kartashev
3345eb8e16 JBR-9288 Wayland: use builtin window decorations in KDE 2025-09-08 15:44:41 +04:00
Dmitry Drobotov
987f5f9561 JBR-4478 Implement support for native accessible caret events on Windows
The feature adds caret tracking support for assistive tools that don't work with Java Access Bridge, specifically, for the built-in Windows Magnifier.
It works by implementing Win32 IAccessible interface for the text caret, and sending EVENT_OBJECT_LOCATIONCHANGE events whenever it changes.
It's enabled by default and can be disabled by setting `sun.awt.windows.use.native.caret.accessibility.events` property to false.

(cherry picked from commit 88f1599bad)
2025-09-08 06:54:45 +02:00
195 changed files with 9231 additions and 2248 deletions

2
.github/README.md vendored
View File

@@ -161,7 +161,7 @@ Install the necessary tools, libraries, and headers with:
```
$ sudo apt-get install autoconf make build-essential libx11-dev libxext-dev libxrender-dev libxtst-dev \
libxt-dev libxrandr-dev libcups2-dev libfontconfig1-dev libasound2-dev libspeechd-dev libwayland-dev \
wayland-protocols libxkbcommon-x11-0
wayland-protocols libxkbcommon-x11-0 libdbus-1-dev
```
Get Java 23 (for instance, [Azul Zulu Builds of OpenJDK 23](https://www.azul.com/downloads/?version=java-23&os=linux&package=jdk#zulu)).

View File

@@ -1,7 +1,7 @@
[general]
project=jdk
project=jdk-updates
jbs=JDK
version=25
version=25.0.1
[checks]
error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists,copyright

View File

@@ -47,8 +47,8 @@ VERSION_PATCH=$(getVersionProp "DEFAULT_VERSION_PATCH")
[[ $VERSION_UPDATE = 0 ]] && JBSDK_VERSION="$VERSION_FEATURE" || JBSDK_VERSION="${VERSION_FEATURE}.${VERSION_INTERIM}.${VERSION_UPDATE}"
[[ $VERSION_PATCH = 0 ]] || JBSDK_VERSION="${VERSION_FEATURE}.${VERSION_INTERIM}.${VERSION_UPDATE}.${VERSION_PATCH}"
echo "##teamcity[setParameter name='env.JBSDK_VERSION' value='${JBSDK_VERSION}']"
tag_prefix="jdk-"
OPENJDK_TAG=$(git tag -l | grep "$tag_prefix$JBSDK_VERSION" | grep -v ga | sort -t "-" -k 2 -V -f | tail -n 1)
tag_prefix="jbr-"
OPENJDK_TAG=$(git log --simplify-by-decoration --decorate=short --pretty=short | grep "${tag_prefix}${JBSDK_VERSION}" | cut -d "(" -f2 | cut -d ")" -f1 | awk '{print $2}' | sort -t "-" -k 2 -V -f | tail -n 1 | tr -d ",")
JDK_BUILD_NUMBER=$(echo $OPENJDK_TAG | awk -F "-|[+]" '{print $3}')
[ -z $JDK_BUILD_NUMBER ] && JDK_BUILD_NUMBER=1
re='^[0-9]+$'

View File

@@ -40,13 +40,13 @@ else ifeq ($(call isBuildOsEnv, windows.wsl1 windows.wsl2), true)
else
M2_REPO := $(HOME)/.m2/repository
endif
M2_ARTIFACT := $(M2_REPO)/com/jetbrains/jbr-api/SNAPSHOT
M2_ARTIFACT := $(M2_REPO)/org/jetbrains/runtime/jbr-api/SNAPSHOT
M2_POM_CONTENT := \
<?xml version="1.0" encoding="UTF-8"?> \
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" \
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> \
<modelVersion>4.0.0</modelVersion> \
<groupId>com.jetbrains</groupId> \
<groupId>org.jetbrains.runtime</groupId> \
<artifactId>jbr-api</artifactId> \
<version>SNAPSHOT</version> \
</project> \
@@ -65,7 +65,8 @@ jbr-api:
$(MKDIR) -p $(M2_ARTIFACT); \
$(ECHO) '$(M2_POM_CONTENT)' > $(M2_ARTIFACT)/$(ARTIFACT_NAME).pom; \
$(CP) "$(JBR_API_DIR)/out/$(ARTIFACT_NAME).jar" "$(M2_ARTIFACT)"; \
$(ECHO) "Installed into local Maven repository as com.jetbrains:jbr-api:SNAPSHOT"; \
$(ECHO) "Installed into local Maven repository as org.jetbrains.runtime:jbr-api:SNAPSHOT"; \
cd "$(M2_ARTIFACT)" && sha256sum --binary "$(ARTIFACT_NAME).jar"; \
else \
$(ECHO) "No Maven repository found at $(M2_REPO) - skipping local installation"; \
fi

View File

@@ -89,7 +89,7 @@ AC_DEFUN_ONCE([LIB_SETUP_VULKAN],
if (test "x${with_vulkan_shader_compiler}" = x || test "x${with_vulkan_shader_compiler}" = xglslc); then
UTIL_LOOKUP_PROGS(GLSLC, glslc)
SHADER_COMPILER="$GLSLC"
VULKAN_SHADER_COMPILER="glslc --target-env=vulkan1.2 -mfmt=num -o"
VULKAN_SHADER_COMPILER="glslc --target-env=vulkan1.2 -mfmt=num"
fi
# Check glslangValidator
@@ -97,7 +97,8 @@ AC_DEFUN_ONCE([LIB_SETUP_VULKAN],
test "x$SHADER_COMPILER" = x; then
UTIL_LOOKUP_PROGS(GLSLANG, glslangValidator)
SHADER_COMPILER="$GLSLANG"
VULKAN_SHADER_COMPILER="glslangValidator --target-env vulkan1.2 -x -o"
# Newer glslangValidator could use -P\"\#extension GL_GOOGLE_include_directive: require\"
VULKAN_SHADER_COMPILER="glslangValidator --target-env vulkan1.2 -x"
fi
if test "x$SHADER_COMPILER" = x; then

View File

@@ -28,12 +28,12 @@
DEFAULT_VERSION_FEATURE=25
DEFAULT_VERSION_INTERIM=0
DEFAULT_VERSION_UPDATE=0
DEFAULT_VERSION_UPDATE=1
DEFAULT_VERSION_PATCH=0
DEFAULT_VERSION_EXTRA1=0
DEFAULT_VERSION_EXTRA2=0
DEFAULT_VERSION_EXTRA3=0
DEFAULT_VERSION_DATE=2025-09-16
DEFAULT_VERSION_DATE=2025-10-21
DEFAULT_VERSION_CLASSFILE_MAJOR=69 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
DEFAULT_VERSION_CLASSFILE_MINOR=0
DEFAULT_VERSION_DOCS_API_SINCE=11

View File

@@ -542,10 +542,10 @@ class Bundle {
if (pattern != null) {
// Perform date-time format pattern conversion which is
// applicable to both SimpleDateFormat and j.t.f.DateTimeFormatter.
String transPattern = translateDateFormatLetters(calendarType, pattern, this::convertDateTimePatternLetter);
String transPattern = translateDateFormatLetters(calendarType, key, pattern, this::convertDateTimePatternLetter);
dateTimePatterns.add(i, transPattern);
// Additionally, perform SDF specific date-time format pattern conversion
sdfPatterns.add(i, translateDateFormatLetters(calendarType, transPattern, this::convertSDFLetter));
sdfPatterns.add(i, translateDateFormatLetters(calendarType, key, transPattern, this::convertSDFLetter));
} else {
dateTimePatterns.add(i, null);
sdfPatterns.add(i, null);
@@ -568,7 +568,7 @@ class Bundle {
}
}
private String translateDateFormatLetters(CalendarType calendarType, String cldrFormat, ConvertDateTimeLetters converter) {
private String translateDateFormatLetters(CalendarType calendarType, String patternKey, String cldrFormat, ConvertDateTimeLetters converter) {
String pattern = cldrFormat;
int length = pattern.length();
boolean inQuote = false;
@@ -587,7 +587,7 @@ class Bundle {
if (nextc == '\'') {
i++;
if (count != 0) {
converter.convert(calendarType, lastLetter, count, jrePattern);
converter.convert(calendarType, patternKey, lastLetter, count, jrePattern);
lastLetter = 0;
count = 0;
}
@@ -597,7 +597,7 @@ class Bundle {
}
if (!inQuote) {
if (count != 0) {
converter.convert(calendarType, lastLetter, count, jrePattern);
converter.convert(calendarType, patternKey, lastLetter, count, jrePattern);
lastLetter = 0;
count = 0;
}
@@ -614,7 +614,7 @@ class Bundle {
}
if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
if (count != 0) {
converter.convert(calendarType, lastLetter, count, jrePattern);
converter.convert(calendarType, patternKey, lastLetter, count, jrePattern);
lastLetter = 0;
count = 0;
}
@@ -627,7 +627,7 @@ class Bundle {
count++;
continue;
}
converter.convert(calendarType, lastLetter, count, jrePattern);
converter.convert(calendarType, patternKey, lastLetter, count, jrePattern);
lastLetter = c;
count = 1;
}
@@ -637,7 +637,7 @@ class Bundle {
}
if (count != 0) {
converter.convert(calendarType, lastLetter, count, jrePattern);
converter.convert(calendarType, patternKey, lastLetter, count, jrePattern);
}
if (cldrFormat.contentEquals(jrePattern)) {
return cldrFormat;
@@ -661,7 +661,7 @@ class Bundle {
* on the support given by the SimpleDateFormat and the j.t.f.DateTimeFormatter
* for date-time formatting.
*/
private void convertDateTimePatternLetter(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb) {
private void convertDateTimePatternLetter(CalendarType calendarType, String patternKey, char cldrLetter, int count, StringBuilder sb) {
switch (cldrLetter) {
case 'u':
case 'U':
@@ -683,7 +683,7 @@ class Bundle {
* Perform a conversion of CLDR date-time format pattern letter which is
* specific to the SimpleDateFormat.
*/
private void convertSDFLetter(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb) {
private void convertSDFLetter(CalendarType calendarType, String patternKey, char cldrLetter, int count, StringBuilder sb) {
switch (cldrLetter) {
case 'G':
if (calendarType != CalendarType.GREGORIAN) {
@@ -722,6 +722,17 @@ class Bundle {
appendN('z', count, sb);
break;
case 'y':
// If the style is FULL/LONG for a Japanese Calendar, make the
// count == 4 for Gan-nen
if (calendarType == CalendarType.JAPANESE &&
(patternKey.contains("full-") ||
patternKey.contains("long-"))) {
count = 4;
}
appendN(cldrLetter, count, sb);
break;
case 'Z':
if (count == 4 || count == 5) {
sb.append("XXX");
@@ -767,6 +778,7 @@ class Bundle {
.collect(Collectors.toMap(
e -> calendarPrefix + e.getKey(),
e -> translateDateFormatLetters(calendarType,
e.getKey(),
(String)e.getValue(),
this::convertDateTimePatternLetter)
))
@@ -775,7 +787,7 @@ class Bundle {
@FunctionalInterface
private interface ConvertDateTimeLetters {
void convert(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb);
void convert(CalendarType calendarType, String patternKey, char cldrLetter, int count, StringBuilder sb);
}
/**

View File

@@ -65,6 +65,7 @@ ifeq ($(call isTargetOs, aix), false)
CXXFLAGS := $(LIBJSOUND_CFLAGS), \
DISABLED_WARNINGS_gcc := undef unused-variable, \
DISABLED_WARNINGS_clang := undef unused-variable, \
DISABLED_WARNINGS_clang_PLATFORM_API_MacOSX_Ports.cpp := vla-cxx-extension, \
DISABLED_WARNINGS_clang_PLATFORM_API_MacOSX_MidiUtils.c := \
unused-but-set-variable, \
DISABLED_WARNINGS_clang_DirectAudioDevice.c := unused-function, \

View File

@@ -35,6 +35,8 @@ WAYLAND_BASIC_PROTOCOL_FILES := \
$(WAYLAND_PROTOCOLS_ROOT)/staging/xdg-activation/xdg-activation-v1.xml \
$(WAYLAND_PROTOCOLS_ROOT)/unstable/primary-selection/primary-selection-unstable-v1.xml \
$(WAYLAND_PROTOCOLS_ROOT)/unstable/xdg-output/xdg-output-unstable-v1.xml \
$(WAYLAND_PROTOCOLS_ROOT)/unstable/relative-pointer/relative-pointer-unstable-v1.xml \
$(WAYLAND_PROTOCOLS_ROOT)/unstable/text-input/text-input-unstable-v3.xml \
$(GTK_SHELL1_PROTOCOL_PATH) \
#

View File

@@ -199,7 +199,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
-framework OpenGL, \
LIBS_windows := advapi32.lib comctl32.lib comdlg32.lib delayimp.lib \
gdi32.lib gdiplus.lib imm32.lib kernel32.lib ole32.lib shell32.lib shlwapi.lib \
user32.lib uuid.lib winmm.lib winspool.lib dwmapi.lib $(A11Y_NVDA_ANNOUNCING_LIBS), \
user32.lib uuid.lib winmm.lib winspool.lib dwmapi.lib $(A11Y_NVDA_ANNOUNCING_LIBS) oleacc.lib, \
VERSIONINFO_RESOURCE := $(LIBAWT_VERSIONINFO_RESOURCE), \
EXTRA_RCFLAGS := $(LIBAWT_RCFLAGS), \
STATIC_LIB_EXCLUDE_OBJS := $(LIBAWT_STATIC_EXCLUDE_OBJS), \
@@ -220,7 +220,7 @@ endif
# Compile Vulkan shaders
define compile-spirv
$(call MakeTargetDir)
$(VULKAN_SHADER_COMPILER) '$(call DecodeSpace, $@)' '$(call DecodeSpace, $<)'
$(VULKAN_SHADER_COMPILER) -D$(call uppercase,$(patsubst .%,STAGE_%,$(suffix $<))) -o '$(call DecodeSpace, $@)' '$(call DecodeSpace, $<)'
endef
spirv-name = $(strip $1).h

View File

@@ -5344,42 +5344,6 @@ void MacroAssembler::add2_with_carry(Register final_dest_hi, Register dest_hi, R
add(final_dest_hi, dest_hi, carry);
}
/**
* Multiply 32 bit by 32 bit first loop.
*/
void MacroAssembler::multiply_32_x_32_loop(Register x, Register xstart, Register x_xstart,
Register y, Register y_idx, Register z,
Register carry, Register product,
Register idx, Register kdx) {
// jlong carry, x[], y[], z[];
// for (int idx=ystart, kdx=ystart+1+xstart; idx >= 0; idx--, kdx--) {
// long product = y[idx] * x[xstart] + carry;
// z[kdx] = (int)product;
// carry = product >>> 32;
// }
// z[xstart] = (int)carry;
Label L_first_loop, L_first_loop_exit;
blez(idx, L_first_loop_exit);
shadd(t0, xstart, x, t0, LogBytesPerInt);
lwu(x_xstart, Address(t0, 0));
bind(L_first_loop);
subiw(idx, idx, 1);
shadd(t0, idx, y, t0, LogBytesPerInt);
lwu(y_idx, Address(t0, 0));
mul(product, x_xstart, y_idx);
add(product, product, carry);
srli(carry, product, 32);
subiw(kdx, kdx, 1);
shadd(t0, kdx, z, t0, LogBytesPerInt);
sw(product, Address(t0, 0));
bgtz(idx, L_first_loop);
bind(L_first_loop_exit);
}
/**
* Multiply 64 bit by 64 bit first loop.
*/
@@ -5596,77 +5560,16 @@ void MacroAssembler::multiply_to_len(Register x, Register xlen, Register y, Regi
const Register carry = tmp5;
const Register product = xlen;
const Register x_xstart = tmp0;
const Register jdx = tmp1;
mv(idx, ylen); // idx = ylen;
addw(kdx, xlen, ylen); // kdx = xlen+ylen;
mv(carry, zr); // carry = 0;
Label L_multiply_64_x_64_loop, L_done;
Label L_done;
subiw(xstart, xlen, 1);
bltz(xstart, L_done);
const Register jdx = tmp1;
if (AvoidUnalignedAccesses) {
int base_offset = arrayOopDesc::base_offset_in_bytes(T_INT);
assert((base_offset % (UseCompactObjectHeaders ? 4 :
(UseCompressedClassPointers ? 8 : 4))) == 0, "Must be");
if ((base_offset % 8) == 0) {
// multiply_64_x_64_loop emits 8-byte load/store to access two elements
// at a time from int arrays x and y. When base_offset is 8 bytes, these
// accesses are naturally aligned if both xlen and ylen are even numbers.
orr(t0, xlen, ylen);
test_bit(t0, t0, 0);
beqz(t0, L_multiply_64_x_64_loop);
}
Label L_second_loop_unaligned, L_third_loop, L_third_loop_exit;
multiply_32_x_32_loop(x, xstart, x_xstart, y, y_idx, z, carry, product, idx, kdx);
shadd(t0, xstart, z, t0, LogBytesPerInt);
sw(carry, Address(t0, 0));
bind(L_second_loop_unaligned);
mv(carry, zr);
mv(jdx, ylen);
subiw(xstart, xstart, 1);
bltz(xstart, L_done);
subi(sp, sp, 2 * wordSize);
sd(z, Address(sp, 0));
sd(zr, Address(sp, wordSize));
shadd(t0, xstart, z, t0, LogBytesPerInt);
addi(z, t0, 4);
shadd(t0, xstart, x, t0, LogBytesPerInt);
lwu(product, Address(t0, 0));
blez(jdx, L_third_loop_exit);
bind(L_third_loop);
subiw(jdx, jdx, 1);
shadd(t0, jdx, y, t0, LogBytesPerInt);
lwu(t0, Address(t0, 0));
mul(t1, t0, product);
add(t0, t1, carry);
shadd(tmp6, jdx, z, t1, LogBytesPerInt);
lwu(t1, Address(tmp6, 0));
add(t0, t0, t1);
sw(t0, Address(tmp6, 0));
srli(carry, t0, 32);
bgtz(jdx, L_third_loop);
bind(L_third_loop_exit);
ld(z, Address(sp, 0));
addi(sp, sp, 2 * wordSize);
shadd(t0, xstart, z, t0, LogBytesPerInt);
sw(carry, Address(t0, 0));
j(L_second_loop_unaligned);
}
bind(L_multiply_64_x_64_loop);
multiply_64_x_64_loop(x, xstart, x_xstart, y, y_idx, z, carry, product, idx, kdx);
Label L_second_loop_aligned;

View File

@@ -1384,10 +1384,6 @@ public:
void adc(Register dst, Register src1, Register src2, Register carry);
void add2_with_carry(Register final_dest_hi, Register dest_hi, Register dest_lo,
Register src1, Register src2, Register carry);
void multiply_32_x_32_loop(Register x, Register xstart, Register x_xstart,
Register y, Register y_idx, Register z,
Register carry, Register product,
Register idx, Register kdx);
void multiply_64_x_64_loop(Register x, Register xstart, Register x_xstart,
Register y, Register y_idx, Register z,
Register carry, Register product,

View File

@@ -8431,6 +8431,17 @@ instruct castVV(vReg dst)
ins_pipe(pipe_class_empty);
%}
instruct castVVMask(vRegMask dst)
%{
match(Set dst (CastVV dst));
size(0);
format %{ "# castVV of $dst" %}
ins_encode(/* empty encoding */);
ins_cost(0);
ins_pipe(pipe_class_empty);
%}
// ============================================================================
// Convert Instructions

View File

@@ -203,15 +203,15 @@ void VM_Version::common_initialize() {
}
}
// Misc Intrinsics could depend on RVV
// Misc Intrinsics that could depend on RVV.
if (UseZba || UseRVV) {
if (!AvoidUnalignedAccesses && (UseZba || UseRVV)) {
if (FLAG_IS_DEFAULT(UseCRC32Intrinsics)) {
FLAG_SET_DEFAULT(UseCRC32Intrinsics, true);
}
} else {
if (!FLAG_IS_DEFAULT(UseCRC32Intrinsics)) {
warning("CRC32 intrinsic requires Zba or RVV instructions (not available on this CPU)");
warning("CRC32 intrinsic are not available on this CPU.");
}
FLAG_SET_DEFAULT(UseCRC32Intrinsics, false);
}
@@ -325,20 +325,40 @@ void VM_Version::c2_initialize() {
FLAG_SET_DEFAULT(UseMulAddIntrinsic, true);
}
if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) {
FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true);
if (!AvoidUnalignedAccesses) {
if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) {
FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true);
}
} else if (UseMultiplyToLenIntrinsic) {
warning("Intrinsics for BigInteger.multiplyToLen() not available on this CPU.");
FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, false);
}
if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) {
FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, true);
if (!AvoidUnalignedAccesses) {
if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) {
FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, true);
}
} else if (UseSquareToLenIntrinsic) {
warning("Intrinsics for BigInteger.squareToLen() not available on this CPU.");
FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, false);
}
if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) {
FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, true);
if (!AvoidUnalignedAccesses) {
if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) {
FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, true);
}
} else if (UseMontgomeryMultiplyIntrinsic) {
warning("Intrinsics for BigInteger.montgomeryMultiply() not available on this CPU.");
FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, false);
}
if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) {
FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, true);
if (!AvoidUnalignedAccesses) {
if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) {
FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, true);
}
} else if (UseMontgomerySquareIntrinsic) {
warning("Intrinsics for BigInteger.montgomerySquare() not available on this CPU.");
FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, false);
}
// Adler32

View File

@@ -1507,7 +1507,7 @@ BasicType java_lang_Class::as_BasicType(oop java_class, Klass** reference_klass)
oop java_lang_Class::primitive_mirror(BasicType t) {
oop mirror = Universe::java_mirror(t);
assert(mirror != nullptr && (mirror->is_a(vmClasses::Class_klass())
|| (Universe::is_inside_redefinition() && vmClasses::Class_klass()->old_version() != NULL && mirror->is_a(vmClasses::Class_klass()->old_version()))), "must be a Class");
|| (Universe::is_inside_redefinition() && vmClasses::Class_klass()->old_version() != nullptr && mirror->is_a(vmClasses::Class_klass()->old_version()))), "must be a Class");
assert(is_primitive(mirror), "must be primitive");
return mirror;
}

View File

@@ -132,8 +132,16 @@ bool StackMapTable::match_stackmap(
}
void StackMapTable::check_jump_target(
StackMapFrame* frame, int32_t target, TRAPS) const {
StackMapFrame* frame, int bci, int offset, TRAPS) const {
ErrorContext ctx;
// Jump targets must be within the method and the method size is limited. See JVMS 4.11
int min_offset = -1 * max_method_code_size;
if (offset < min_offset || offset > max_method_code_size) {
frame->verifier()->verify_error(ErrorContext::bad_stackmap(bci, frame),
"Illegal target of jump or branch (bci %d + offset %d)", bci, offset);
return;
}
int target = bci + offset;
bool match = match_stackmap(
frame, target, true, false, &ctx, CHECK_VERIFY(frame->verifier()));
if (!match || (target < 0 || target >= _code_length)) {

View File

@@ -67,7 +67,7 @@ class StackMapTable : public StackObj {
// Check jump instructions. Make sure there are no uninitialized
// instances on backward branch.
void check_jump_target(StackMapFrame* frame, int32_t target, TRAPS) const;
void check_jump_target(StackMapFrame* frame, int bci, int offset, TRAPS) const;
// The following methods are only used inside this class.

View File

@@ -1360,7 +1360,7 @@ void SystemDictionary::define_instance_class(InstanceKlass* k, InstanceKlass* ol
ClassLoaderData* loader_data = k->class_loader_data();
assert(loader_data->class_loader() == class_loader(), "they must be the same");
bool is_redefining = (old_klass != NULL);
bool is_redefining = (old_klass != nullptr);
// Bootstrap and other parallel classloaders don't acquire a lock,
// they use placeholder token.
@@ -1487,7 +1487,7 @@ InstanceKlass* SystemDictionary::find_or_define_helper(Symbol* class_name, Handl
}
}
define_instance_class(k, NULL, class_loader, THREAD);
define_instance_class(k, nullptr, class_loader, THREAD);
// definer must notify any waiting threads
{
@@ -1527,7 +1527,7 @@ InstanceKlass* SystemDictionary::find_or_define_instance_class(Symbol* class_nam
// (DCEVM) - remove from klass hierarchy
void SystemDictionary::remove_from_hierarchy(InstanceKlass* k) {
assert(k != NULL, "just checking");
assert(k != nullptr, "just checking");
// remove receiver from sibling list
k->remove_from_sibling_list();

View File

@@ -781,7 +781,6 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) {
// Merge with the next instruction
{
int target;
VerificationType type, type2;
VerificationType atype;
@@ -1606,9 +1605,8 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) {
case Bytecodes::_ifle:
current_frame.pop_stack(
VerificationType::integer_type(), CHECK_VERIFY(this));
target = bcs.dest();
stackmap_table.check_jump_target(
&current_frame, target, CHECK_VERIFY(this));
&current_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this));
no_control_flow = false; break;
case Bytecodes::_if_acmpeq :
case Bytecodes::_if_acmpne :
@@ -1619,19 +1617,16 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) {
case Bytecodes::_ifnonnull :
current_frame.pop_stack(
VerificationType::reference_check(), CHECK_VERIFY(this));
target = bcs.dest();
stackmap_table.check_jump_target
(&current_frame, target, CHECK_VERIFY(this));
(&current_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this));
no_control_flow = false; break;
case Bytecodes::_goto :
target = bcs.dest();
stackmap_table.check_jump_target(
&current_frame, target, CHECK_VERIFY(this));
&current_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this));
no_control_flow = true; break;
case Bytecodes::_goto_w :
target = bcs.dest_w();
stackmap_table.check_jump_target(
&current_frame, target, CHECK_VERIFY(this));
&current_frame, bcs.bci(), bcs.get_offset_s4(), CHECK_VERIFY(this));
no_control_flow = true; break;
case Bytecodes::_tableswitch :
case Bytecodes::_lookupswitch :
@@ -2280,15 +2275,14 @@ void ClassVerifier::verify_switch(
}
}
}
int target = bci + default_offset;
stackmap_table->check_jump_target(current_frame, target, CHECK_VERIFY(this));
stackmap_table->check_jump_target(current_frame, bci, default_offset, CHECK_VERIFY(this));
for (int i = 0; i < keys; i++) {
// Because check_jump_target() may safepoint, the bytecode could have
// moved, which means 'aligned_bcp' is no good and needs to be recalculated.
aligned_bcp = align_up(bcs->bcp() + 1, jintSize);
target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize);
int offset = (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize);
stackmap_table->check_jump_target(
current_frame, target, CHECK_VERIFY(this));
current_frame, bci, offset, CHECK_VERIFY(this));
}
NOT_PRODUCT(aligned_bcp = nullptr); // no longer valid at this point
}
@@ -2549,7 +2543,12 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
case Bytecodes::_goto:
case Bytecodes::_goto_w: {
int target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w());
int offset = (opcode == Bytecodes::_goto ? bcs.get_offset_s2() : bcs.get_offset_s4());
int min_offset = -1 * max_method_code_size;
// Check offset for overflow
if (offset < min_offset || offset > max_method_code_size) return false;
int target = bci + offset;
if (visited_branches->contains(bci)) {
if (bci_stack->is_empty()) {
if (handler_stack->is_empty()) {
@@ -2607,7 +2606,10 @@ bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
// Push the switch alternatives onto the stack.
for (int i = 0; i < keys; i++) {
int target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize);
int min_offset = -1 * max_method_code_size;
int offset = (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize);
if (offset < min_offset || offset > max_method_code_size) return false;
int target = bci + offset;
if (target > code_length) return false;
bci_stack->push(target);
}

View File

@@ -2353,7 +2353,7 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
}
if (AllowEnhancedClassRedefinition) {
// Skip redefined methods
if (task->method()->is_old() || task->method()->method_holder()->new_version() != NULL) {
if (task->method()->is_old() || task->method()->method_holder()->new_version() != nullptr) {
ci_env.record_method_not_compilable("redefined method", true);
} else {
comp->compile_method(&ci_env, target, osr_bci, true, directive);

View File

@@ -231,11 +231,11 @@ uint G1FullGCCompactionPoint::find_contiguous_before(G1HeapRegion* hr, uint num_
}
HeapWord* G1FullGCCompactionPoint::forward_compact_top(size_t size) {
assert(_current_region != NULL, "Must have been initialized");
assert(_current_region != nullptr, "Must have been initialized");
// Ensure the object fit in the current region.
while (!object_will_fit(size)) {
if (!_compaction_region_iterator.has_next()) {
return NULL;
return nullptr;
}
switch_region();
}
@@ -243,7 +243,7 @@ HeapWord* G1FullGCCompactionPoint::forward_compact_top(size_t size) {
}
void G1FullGCCompactionPoint::forward_dcevm(oop object, size_t size, bool force_forward) {
assert(_current_region != NULL, "Must have been initialized");
assert(_current_region != nullptr, "Must have been initialized");
// Ensure the object fit in the current region.
while (!object_will_fit(size)) {

View File

@@ -1936,7 +1936,7 @@ void ZPageAllocator::cleanup_failed_commit_multi_partition(ZMultiPartitionAlloca
}
const size_t committed = allocation->committed_capacity();
const ZVirtualMemory non_harvested_vmem = vmem.last_part(allocation->harvested());
const ZVirtualMemory non_harvested_vmem = partial_vmem.last_part(allocation->harvested());
const ZVirtualMemory committed_vmem = non_harvested_vmem.first_part(committed);
const ZVirtualMemory non_committed_vmem = non_harvested_vmem.last_part(committed);

View File

@@ -214,9 +214,20 @@ void ZPhysicalMemoryManager::free(const ZVirtualMemory& vmem, uint32_t numa_id)
});
}
static size_t inject_commit_limit(const ZVirtualMemory& vmem) {
// To facilitate easier interoperability with multi partition allocations we
// divide by ZNUMA::count(). Users of ZFailLargerCommits need to be aware of
// this when writing tests. In the future we could probe the VirtualMemoryManager
// and condition this division on whether the vmem is in the multi partition
// address space.
return align_up(MIN2(ZFailLargerCommits / ZNUMA::count(), vmem.size()), ZGranuleSize);
}
size_t ZPhysicalMemoryManager::commit(const ZVirtualMemory& vmem, uint32_t numa_id) {
zbacking_index* const pmem = _physical_mappings.addr(vmem.start());
const size_t size = vmem.size();
const size_t size = ZFailLargerCommits > 0
? inject_commit_limit(vmem)
: vmem.size();
size_t total_committed = 0;

View File

@@ -118,6 +118,11 @@
develop(bool, ZVerifyOops, false, \
"Verify accessed oops") \
\
develop(size_t, ZFailLargerCommits, 0, \
"Commits larger than ZFailLargerCommits will be truncated, " \
"used to stress page allocation commit failure paths " \
"(0: Disabled)") \
\
develop(uint, ZFakeNUMA, 1, \
"ZFakeNUMA is used to test the internal NUMA memory support " \
"without the need for UseNUMA") \

View File

@@ -100,8 +100,23 @@ class BaseBytecodeStream: StackObj {
void set_next_bci(int bci) { assert(0 <= bci && bci <= method()->code_size(), "illegal bci"); _next_bci = bci; }
// Bytecode-specific attributes
int dest() const { return bci() + bytecode().get_offset_s2(raw_code()); }
int dest_w() const { return bci() + bytecode().get_offset_s4(raw_code()); }
int get_offset_s2() const { return bytecode().get_offset_s2(raw_code()); }
int get_offset_s4() const { return bytecode().get_offset_s4(raw_code()); }
// These methods are not safe to use before or during verification as they may
// have large offsets and cause overflows
int dest() const {
int min_offset = -1 * max_method_code_size;
int offset = bytecode().get_offset_s2(raw_code());
guarantee(offset >= min_offset && offset <= max_method_code_size, "must be");
return bci() + offset;
}
int dest_w() const {
int min_offset = -1 * max_method_code_size;
int offset = bytecode().get_offset_s4(raw_code());
guarantee(offset >= min_offset && offset <= max_method_code_size, "must be");
return bci() + offset;
}
// One-byte indices.
u1 get_index_u1() const { assert_raw_index_size(1); return *(jubyte*)(bcp()+1); }

View File

@@ -521,7 +521,7 @@ void Klass::initialize_supers(Klass* k, Array<InstanceKlass*>* transitive_interf
_primary_supers[0] = this;
assert(super_depth() == 0, "Object must already be initialized properly");
} else if (k != super() || k == vmClasses::Object_klass() || (k->is_redefining() && k == vmClasses::Object_klass()->newest_version())) {
assert(super() == NULL || super() == vmClasses::Object_klass() || (k->is_redefining() && super() == vmClasses::Object_klass()->newest_version()),
assert(super() == nullptr || super() == vmClasses::Object_klass() || (k->is_redefining() && super() == vmClasses::Object_klass()->newest_version()),
"initialize this only once to a non-trivial value");
set_super(k);
Klass* sup = k;
@@ -1388,18 +1388,18 @@ void Klass::on_secondary_supers_verification_failure(Klass* super, Klass* sub, b
}
void Klass::update_supers_dcevm() {
if (_super != NULL) {
if (_super != nullptr) {
_super = _super->newest_version();
}
int sup_depth = super_depth();
for (int idx = 0; idx < sup_depth; idx++) {
Klass* primary = _primary_supers[idx];
if (primary == NULL) {
if (primary == nullptr) {
break;
}
_primary_supers[idx] = primary->newest_version();
}
if (secondary_super_cache() != NULL) {
if (secondary_super_cache() != nullptr) {
set_secondary_super_cache(secondary_super_cache()->newest_version());
}

View File

@@ -1575,9 +1575,14 @@ bool LibraryCallKit::inline_string_toBytesU() {
Node* src_start = array_element_address(value, offset, T_CHAR);
Node* dst_start = basic_plus_adr(newcopy, arrayOopDesc::base_offset_in_bytes(T_BYTE));
// Check if src array address is aligned to HeapWordSize (dst is always aligned)
const TypeInt* toffset = gvn().type(offset)->is_int();
bool aligned = toffset->is_con() && ((toffset->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
// Check if dst array address is aligned to HeapWordSize
bool aligned = (arrayOopDesc::base_offset_in_bytes(T_BYTE) % HeapWordSize == 0);
// If true, then check if src array address is aligned to HeapWordSize
if (aligned) {
const TypeInt* toffset = gvn().type(offset)->is_int();
aligned = toffset->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) +
toffset->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
}
// Figure out which arraycopy runtime method to call (disjoint, uninitialized).
const char* copyfunc_name = "arraycopy";
@@ -1658,8 +1663,8 @@ bool LibraryCallKit::inline_string_getCharsU() {
// Check if array addresses are aligned to HeapWordSize
const TypeInt* tsrc = gvn().type(src_begin)->is_int();
const TypeInt* tdst = gvn().type(dst_begin)->is_int();
bool aligned = tsrc->is_con() && ((tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) &&
tdst->is_con() && ((tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
bool aligned = tsrc->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) + tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) &&
tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) + tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
// Figure out which arraycopy runtime method to call (disjoint, uninitialized).
const char* copyfunc_name = "arraycopy";

View File

@@ -1473,9 +1473,14 @@ void PhaseStringOpts::arraycopy(GraphKit& kit, IdealKit& ideal, Node* src_array,
Node* src_ptr = __ array_element_address(src_array, __ intcon(0), T_BYTE);
Node* dst_ptr = __ array_element_address(dst_array, start, T_BYTE);
// Check if destination address is aligned to HeapWordSize
const TypeInt* tdst = __ gvn().type(start)->is_int();
bool aligned = tdst->is_con() && ((tdst->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0);
// Check if src array address is aligned to HeapWordSize
bool aligned = (arrayOopDesc::base_offset_in_bytes(T_BYTE) % HeapWordSize == 0);
// If true, then check if dst array address is aligned to HeapWordSize
if (aligned) {
const TypeInt* tdst = __ gvn().type(start)->is_int();
aligned = tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) +
tdst->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0);
}
// Figure out which arraycopy runtime method to call (disjoint, uninitialized).
const char* copyfunc_name = "arraycopy";
address copyfunc_addr = StubRoutines::select_arraycopy_function(elembt, aligned, true, copyfunc_name, true);

View File

@@ -218,7 +218,7 @@ intptr_t jfieldIDWorkaround::encode_klass_hash(Klass* k, int offset) {
DEBUG_ONLY(NoSafepointVerifier nosafepoint;)
if (AllowEnhancedClassRedefinition) {
while (field_klass->old_version() != NULL) {
while (field_klass->old_version() != nullptr) {
field_klass = field_klass->old_version();
}
}
@@ -242,7 +242,7 @@ bool jfieldIDWorkaround::klass_hash_ok(Klass* k, jfieldID id) {
intptr_t klass_hash = (as_uint >> klass_shift) & klass_mask;
if (AllowEnhancedClassRedefinition) {
while (k->old_version() != NULL) {
while (k->old_version() != nullptr) {
k = k->old_version();
}
}

View File

@@ -1097,6 +1097,22 @@ bool WhiteBox::validate_cgroup(bool cgroups_v2_enabled,
}
#endif
bool WhiteBox::is_asan_enabled() {
#ifdef ADDRESS_SANITIZER
return true;
#else
return false;
#endif
}
bool WhiteBox::is_ubsan_enabled() {
#ifdef UNDEFINED_BEHAVIOR_SANITIZER
return true;
#else
return false;
#endif
}
bool WhiteBox::compile_method(Method* method, int comp_level, int bci, JavaThread* THREAD) {
// Screen for unavailable/bad comp level or null method
AbstractCompiler* comp = CompileBroker::compiler(comp_level);
@@ -1908,6 +1924,14 @@ WB_ENTRY(jboolean, WB_IsMonitorInflated(JNIEnv* env, jobject wb, jobject obj))
return (jboolean) obj_oop->mark().has_monitor();
WB_END
WB_ENTRY(jboolean, WB_IsAsanEnabled(JNIEnv* env))
return (jboolean) WhiteBox::is_asan_enabled();
WB_END
WB_ENTRY(jboolean, WB_IsUbsanEnabled(JNIEnv* env))
return (jboolean) WhiteBox::is_ubsan_enabled();
WB_END
WB_ENTRY(jlong, WB_getInUseMonitorCount(JNIEnv* env, jobject wb))
return (jlong) WhiteBox::get_in_use_monitor_count();
WB_END
@@ -2908,6 +2932,8 @@ static JNINativeMethod methods[] = {
(void*)&WB_AddModuleExportsToAll },
{CC"deflateIdleMonitors", CC"()Z", (void*)&WB_DeflateIdleMonitors },
{CC"isMonitorInflated0", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsMonitorInflated },
{CC"isAsanEnabled", CC"()Z", (void*)&WB_IsAsanEnabled },
{CC"isUbsanEnabled", CC"()Z", (void*)&WB_IsUbsanEnabled },
{CC"getInUseMonitorCount", CC"()J", (void*)&WB_getInUseMonitorCount },
{CC"getLockStackCapacity", CC"()I", (void*)&WB_getLockStackCapacity },
{CC"supportsRecursiveLightweightLocking", CC"()Z", (void*)&WB_supportsRecursiveLightweightLocking },

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -72,6 +72,9 @@ class WhiteBox : public AllStatic {
#ifdef LINUX
static bool validate_cgroup(bool cgroups_v2_enabled, const char* controllers_file, const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* cg_flags);
#endif
// provide info about enabling of Address Sanitizer / Undefined Behavior Sanitizer
static bool is_asan_enabled();
static bool is_ubsan_enabled();
};
#endif // SHARE_PRIMS_WHITEBOX_HPP

View File

@@ -1448,8 +1448,8 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence
shift(currValue, coder, count, dstOffset, len);
count += len;
// Coder of CharSequence may be a mismatch, requiring the value array to be inflated
byte[] newValue = (s instanceof String str)
? putStringAt(currValue, coder, count, dstOffset, str, start, end)
byte[] newValue = (s instanceof String str && str.length() == len)
? putStringAt(currValue, coder, count, dstOffset, str)
: putCharsAt(currValue, coder, count, dstOffset, s, start, end);
if (currValue != newValue) {
this.coder = UTF16;
@@ -1928,10 +1928,10 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence
* @param index the index to insert the string
* @param str the string
*/
private static byte[] putStringAt(byte[] value, byte coder, int count, int index, String str, int off, int end) {
private static byte[] putStringAt(byte[] value, byte coder, int count, int index, String str) {
byte[] newValue = inflateIfNeededFor(value, count, coder, str.coder());
coder = (newValue == value) ? coder : UTF16;
str.getBytes(newValue, off, index, coder, end - off);
str.getBytes(newValue, 0, index, coder, str.length());
return newValue;
}

View File

@@ -182,11 +182,11 @@ public final class LocalDate
/**
* @serial The month-of-year.
*/
private final byte month;
private final short month;
/**
* @serial The day-of-month.
*/
private final byte day;
private final short day;
//-----------------------------------------------------------------------
/**
@@ -490,8 +490,8 @@ public final class LocalDate
*/
private LocalDate(int year, int month, int dayOfMonth) {
this.year = year;
this.month = (byte) month;
this.day = (byte) dayOfMonth;
this.month = (short) month;
this.day = (short) dayOfMonth;
}
//-----------------------------------------------------------------------

View File

@@ -146,11 +146,11 @@ public final class MonthDay
/**
* @serial The month-of-year, not null.
*/
private final byte month;
private final int month;
/**
* @serial The day-of-month.
*/
private final byte day;
private final int day;
//-----------------------------------------------------------------------
/**
@@ -319,8 +319,8 @@ public final class MonthDay
* @param dayOfMonth the day-of-month to represent, validated from 1 to 29-31
*/
private MonthDay(int month, int dayOfMonth) {
this.month = (byte) month;
this.day = (byte) dayOfMonth;
this.month = month;
this.day = dayOfMonth;
}
//-----------------------------------------------------------------------

View File

@@ -153,7 +153,7 @@ public final class YearMonth
/**
* @serial The month-of-year, not null.
*/
private final byte month;
private final int month;
//-----------------------------------------------------------------------
/**
@@ -306,7 +306,7 @@ public final class YearMonth
*/
private YearMonth(int year, int month) {
this.year = year;
this.month = (byte) month;
this.month = month;
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -137,11 +137,11 @@ public final class HijrahDate
/**
* The month-of-year.
*/
private final transient byte monthOfYear;
private final transient int monthOfYear;
/**
* The day-of-month.
*/
private final transient byte dayOfMonth;
private final transient int dayOfMonth;
//-------------------------------------------------------------------------
/**
@@ -273,8 +273,8 @@ public final class HijrahDate
this.chrono = chrono;
this.prolepticYear = prolepticYear;
this.monthOfYear = (byte) monthOfYear;
this.dayOfMonth = (byte) dayOfMonth;
this.monthOfYear = monthOfYear;
this.dayOfMonth = dayOfMonth;
}
/**
@@ -287,8 +287,8 @@ public final class HijrahDate
this.chrono = chrono;
this.prolepticYear = dateInfo[0];
this.monthOfYear = (byte) dateInfo[1];
this.dayOfMonth = (byte) dateInfo[2];
this.monthOfYear = dateInfo[1];
this.dayOfMonth = dateInfo[2];
}
//-----------------------------------------------------------------------

View File

@@ -859,6 +859,22 @@ public class DerValue {
return readStringInternal(tag_UniversalString, new UTF_32BE());
}
/**
* Checks that the BMPString does not contain any surrogate characters,
* which are outside the Basic Multilingual Plane.
*
* @throws IOException if illegal characters are detected
*/
public void validateBMPString() throws IOException {
String bmpString = getBMPString();
for (int i = 0; i < bmpString.length(); i++) {
if (Character.isSurrogate(bmpString.charAt(i))) {
throw new IOException(
"Illegal character in BMPString, index: " + i);
}
}
}
/**
* Reads the ASN.1 NULL value
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -71,19 +71,7 @@ final class EntrustTLSPolicy {
// OU=(c) 1999 Entrust.net Limited,
// OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.),
// O=Entrust.net
"6DC47172E01CBCB0BF62580D895FE2B8AC9AD4F873801E0C10B9C837D21EB177",
// cacerts alias: affirmtrustcommercialca
// DN: CN=AffirmTrust Commercial, O=AffirmTrust, C=US
"0376AB1D54C5F9803CE4B2E201A0EE7EEF7B57B636E8A93C9B8D4860C96F5FA7",
// cacerts alias: affirmtrustnetworkingca
// DN: CN=AffirmTrust Networking, O=AffirmTrust, C=US
"0A81EC5A929777F145904AF38D5D509F66B5E2C58FCDB531058B0E17F3F0B41B",
// cacerts alias: affirmtrustpremiumca
// DN: CN=AffirmTrust Premium, O=AffirmTrust, C=US
"70A73F7F376B60074248904534B11482D5BF0E698ECC498DF52577EBF2E93B9A",
// cacerts alias: affirmtrustpremiumeccca
// DN: CN=AffirmTrust Premium ECC, O=AffirmTrust, C=US
"BD71FDF6DA97E4CF62D1647ADD2581B07D79ADF8397EB4ECBA9C5E8488821423"
"6DC47172E01CBCB0BF62580D895FE2B8AC9AD4F873801E0C10B9C837D21EB177"
);
// Any TLS Server certificate that is anchored by one of the Entrust

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,10 +28,13 @@ package sun.security.x509;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import java.text.Normalizer;
import java.util.*;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.charset.StandardCharsets.UTF_16BE;
import sun.security.util.*;
import sun.security.pkcs.PKCS9Attribute;
@@ -589,6 +592,10 @@ public class AVA implements DerEncoder {
throw new IOException("AVA, extra bytes = "
+ derval.data.available());
}
if (value.tag == DerValue.tag_BMPString) {
value.validateBMPString();
}
}
AVA(DerInputStream in) throws IOException {
@@ -713,7 +720,8 @@ public class AVA implements DerEncoder {
* NOTE: this implementation only emits DirectoryStrings of the
* types returned by isDerString().
*/
String valStr = new String(value.getDataBytes(), UTF_8);
String valStr =
new String(value.getDataBytes(), getCharset(value, false));
/*
* 2.4 (cont): If the UTF-8 string does not have any of the
@@ -832,7 +840,8 @@ public class AVA implements DerEncoder {
* NOTE: this implementation only emits DirectoryStrings of the
* types returned by isDerString().
*/
String valStr = new String(value.getDataBytes(), UTF_8);
String valStr =
new String(value.getDataBytes(), getCharset(value, true));
/*
* 2.4 (cont): If the UTF-8 string does not have any of the
@@ -927,6 +936,39 @@ public class AVA implements DerEncoder {
}
}
/*
* Returns the charset that should be used to decode each DN string type.
*
* This method ensures that multi-byte (UTF8String and BMPString) types
* are decoded using the correct charset and the String forms represent
* the correct characters. For 8-bit ASCII-based types (PrintableString
* and IA5String), we return ISO_8859_1 rather than ASCII, so that the
* complete range of characters can be represented, as many certificates
* do not comply with the Internationalized Domain Name ACE format.
*
* NOTE: this method only supports DirectoryStrings of the types returned
* by isDerString().
*/
private static Charset getCharset(DerValue value, boolean canonical) {
if (canonical) {
return switch (value.tag) {
case DerValue.tag_PrintableString -> ISO_8859_1;
case DerValue.tag_UTF8String -> UTF_8;
default -> throw new Error("unexpected tag: " + value.tag);
};
}
return switch (value.tag) {
case DerValue.tag_PrintableString,
DerValue.tag_T61String,
DerValue.tag_IA5String,
DerValue.tag_GeneralString -> ISO_8859_1;
case DerValue.tag_BMPString -> UTF_16BE;
case DerValue.tag_UTF8String -> UTF_8;
default -> throw new Error("unexpected tag: " + value.tag);
};
}
boolean hasRFC2253Keyword() {
return AVAKeyword.hasKeyword(oid, RFC2253);
}

View File

@@ -1,27 +0,0 @@
Owner: CN=AffirmTrust Commercial, O=AffirmTrust, C=US
Issuer: CN=AffirmTrust Commercial, O=AffirmTrust, C=US
Serial number: 7777062726a9b17c
Valid from: Fri Jan 29 14:06:06 GMT 2010 until: Tue Dec 31 14:06:06 GMT 2030
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL
MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP
Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr
ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL
MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1
yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr
VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/
nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG
XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj
vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt
Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g
N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC
nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
-----END CERTIFICATE-----

View File

@@ -1,27 +0,0 @@
Owner: CN=AffirmTrust Networking, O=AffirmTrust, C=US
Issuer: CN=AffirmTrust Networking, O=AffirmTrust, C=US
Serial number: 7c4f04391cd4992d
Valid from: Fri Jan 29 14:08:24 GMT 2010 until: Tue Dec 31 14:08:24 GMT 2030
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL
MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y
YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua
kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL
QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp
6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG
yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i
QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO
tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu
QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ
Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u
olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48
x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
-----END CERTIFICATE-----

View File

@@ -1,38 +0,0 @@
Owner: CN=AffirmTrust Premium, O=AffirmTrust, C=US
Issuer: CN=AffirmTrust Premium, O=AffirmTrust, C=US
Serial number: 6d8c1446b1a60aee
Valid from: Fri Jan 29 14:10:36 GMT 2010 until: Mon Dec 31 14:10:36 GMT 2040
Signature algorithm name: SHA384withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3
-----BEGIN CERTIFICATE-----
MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz
dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG
A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U
cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf
qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ
JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ
+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS
s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5
HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7
70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG
V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S
qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S
5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia
C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX
OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE
FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2
KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B
8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ
MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc
0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ
u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF
u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH
YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8
GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO
RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e
KeC2uAloGRwYQw==
-----END CERTIFICATE-----

View File

@@ -1,20 +0,0 @@
Owner: CN=AffirmTrust Premium ECC, O=AffirmTrust, C=US
Issuer: CN=AffirmTrust Premium ECC, O=AffirmTrust, C=US
Serial number: 7497258ac73f7a54
Valid from: Fri Jan 29 14:20:24 GMT 2010 until: Mon Dec 31 14:20:24 GMT 2040
Signature algorithm name: SHA384withECDSA
Subject Public Key Algorithm: 384-bit EC (secp384r1) key
Version: 3
-----BEGIN CERTIFICATE-----
MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC
VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ
cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ
BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt
VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D
0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9
ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G
A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G
A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs
aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I
flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ==
-----END CERTIFICATE-----

View File

@@ -32,7 +32,7 @@ formatVersion=3
# Version of the currency code information in this class.
# It is a serial number that accompanies with each amendment.
dataVersion=179
dataVersion=180
# List of all valid ISO 4217 currency codes.
# To ensure compatibility, do not remove codes.
@@ -147,7 +147,7 @@ IO=USD
# BRUNEI DARUSSALAM
BN=BND
# BULGARIA
BG=BGN
BG=BGN;2025-12-31-22-00-00;EUR
# BURKINA FASO
BF=XOF
# BURUNDI
@@ -193,7 +193,7 @@ HR=EUR
# CUBA
CU=CUP
# Curaçao
CW=ANG;2025-04-01-04-00-00;XCG
CW=XCG
# CYPRUS
CY=EUR
# CZECHIA
@@ -510,7 +510,7 @@ SR=SRD
# SVALBARD AND JAN MAYEN
SJ=NOK
# Sint Maarten (Dutch part)
SX=ANG;2025-04-01-04-00-00;XCG
SX=XCG
# ESWATINI
SZ=SZL
# SWEDEN

View File

@@ -395,7 +395,8 @@ static jboolean is_superclass(context_type *, fullinfo_type);
static void initialize_exception_table(context_type *);
static int instruction_length(unsigned char *iptr, unsigned char *end);
static jboolean isLegalTarget(context_type *, int offset);
static jboolean isLegalOffset(context_type *, int bci, int offset);
static jboolean isLegalTarget(context_type *, int target);
static void verify_constant_pool_type(context_type *, int, unsigned);
static void initialize_dataflow(context_type *);
@@ -1154,9 +1155,9 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset)
case JVM_OPC_goto: {
/* Set the ->operand to be the instruction number of the target. */
int jump = (((signed char)(code[offset+1])) << 8) + code[offset+2];
int target = offset + jump;
if (!isLegalTarget(context, target))
if (!isLegalOffset(context, offset, jump))
CCerror(context, "Illegal target of jump or branch");
int target = offset + jump;
this_idata->operand.i = code_data[target];
break;
}
@@ -1170,9 +1171,9 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset)
int jump = (((signed char)(code[offset+1])) << 24) +
(code[offset+2] << 16) + (code[offset+3] << 8) +
(code[offset + 4]);
int target = offset + jump;
if (!isLegalTarget(context, target))
if (!isLegalOffset(context, offset, jump))
CCerror(context, "Illegal target of jump or branch");
int target = offset + jump;
this_idata->operand.i = code_data[target];
break;
}
@@ -1211,13 +1212,16 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset)
}
}
saved_operand = NEW(int, keys + 2);
if (!isLegalTarget(context, offset + _ck_ntohl(lpc[0])))
int jump = _ck_ntohl(lpc[0]);
if (!isLegalOffset(context, offset, jump))
CCerror(context, "Illegal default target in switch");
saved_operand[keys + 1] = code_data[offset + _ck_ntohl(lpc[0])];
int target = offset + jump;
saved_operand[keys + 1] = code_data[target];
for (k = keys, lptr = &lpc[3]; --k >= 0; lptr += delta) {
int target = offset + _ck_ntohl(lptr[0]);
if (!isLegalTarget(context, target))
jump = _ck_ntohl(lptr[0]);
if (!isLegalOffset(context, offset, jump))
CCerror(context, "Illegal branch in tableswitch");
target = offset + jump;
saved_operand[k + 1] = code_data[target];
}
saved_operand[0] = keys + 1; /* number of successors */
@@ -1746,11 +1750,24 @@ static int instruction_length(unsigned char *iptr, unsigned char *end)
/* Given the target of a branch, make sure that it's a legal target. */
static jboolean
isLegalTarget(context_type *context, int offset)
isLegalTarget(context_type *context, int target)
{
int code_length = context->code_length;
int *code_data = context->code_data;
return (offset >= 0 && offset < code_length && code_data[offset] >= 0);
return (target >= 0 && target < code_length && code_data[target] >= 0);
}
/* Given a bci and offset, make sure the offset is valid and the target is legal */
static jboolean
isLegalOffset(context_type *context, int bci, int offset)
{
int code_length = context->code_length;
int *code_data = context->code_data;
int max_offset = 65535; // JVMS 4.11
int min_offset = -65535;
if (offset < min_offset || offset > max_offset) return JNI_FALSE;
int target = bci + offset;
return (target >= 0 && target < code_length && code_data[target] >= 0);
}

View File

@@ -36,6 +36,7 @@ import java.util.Map;
import java.util.Set;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.ArraysSupport;
import static sun.nio.fs.WindowsNativeDispatcher.*;
import static sun.nio.fs.WindowsConstants.*;
@@ -284,9 +285,26 @@ class WindowsWatchService
private static final short OFFSETOF_FILENAMELENGTH = 8;
private static final short OFFSETOF_FILENAME = 12;
// size of per-directory buffer for events (FIXME - make this configurable)
// Need to be less than 4*16384 = 65536. DWORD align.
private static final int CHANGES_BUFFER_SIZE = 16 * 1024;
// size of per-directory buffer for events
// Need to be less than 4*16384 = 65536 when monitoring a directory over the network. DWORD align.
private static final int DEFAULT_CHANGES_BUFFER_SIZE = 16 * 1024;
static final int CHANGES_BUFFER_SIZE;
static {
String rawValue = System.getProperty(
"jdk.nio.file.WatchService.bufferSizeToRetrieveEventsPerDirectory",
String.valueOf(DEFAULT_CHANGES_BUFFER_SIZE));
int intValue;
try {
// Clamp to size of per-directory buffer used to retrieve events.
intValue = Math.clamp(
Long.decode(rawValue),
1,
ArraysSupport.SOFT_MAX_ARRAY_LENGTH);
} catch (NumberFormatException e) {
intValue = DEFAULT_CHANGES_BUFFER_SIZE;
}
CHANGES_BUFFER_SIZE = intValue;
}
private final WindowsFileSystem fs;
private final WindowsWatchService watcher;

View File

@@ -33,7 +33,6 @@ import sun.lwawt.macosx.CFLayer;
import sun.util.logging.PlatformLogger;
import java.awt.Component;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.Window;
@@ -51,12 +50,13 @@ public class MTLLayer extends CFLayer {
private static native void nativeSetOpaque(long layerPtr, boolean opaque);
private int scale = 1;
private final boolean perfCountersEnabled;
public MTLLayer(LWWindowPeer peer) {
super(0, true);
Window target = (peer != null) ? peer.getTarget() : null;
boolean perfCountersEnabled = (target != null) && AWTAccessor.getWindowAccessor().countersEnabled(target);
this.perfCountersEnabled = (target != null) && AWTAccessor.getWindowAccessor().countersEnabled(target);
setPtr(nativeCreateLayer(perfCountersEnabled));
this.peer = peer;
@@ -150,11 +150,29 @@ public class MTLLayer extends CFLayer {
}
}
private final static String[] STAT_NAMES = new String[]{
"java2d.native.mtlLayer.drawInMTLContext", // type = 0
"java2d.native.mtlLayer.nextDrawable" // type = 1
};
private void addStat(int type, double value) {
// Called from the native code when this layer has been presented on screen
if (perfCountersEnabled && (peer != null)) {
final Component target = peer.getTarget();
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().addStat(window,
((type >= 0) && (type < STAT_NAMES.length)) ? STAT_NAMES[type] : "undefined", value);
}
}
}
private void countNewFrame() {
// Called from the native code when this layer has been presented on screen
Component target = peer.getTarget();
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.frames");
if (perfCountersEnabled && (peer != null)) {
final Component target = peer.getTarget();
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.frames");
}
}
}
@@ -162,9 +180,11 @@ public class MTLLayer extends CFLayer {
// Called from the native code when an attempt was made to present this layer
// on screen, but that attempt was not successful. This can happen, for example,
// when those attempts are too frequent.
Component target = peer.getTarget();
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.framesDropped");
if (perfCountersEnabled && (peer != null)) {
final Component target = peer.getTarget();
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.framesDropped");
}
}
}
}

View File

@@ -75,6 +75,19 @@ BOOL isColorMatchingEnabled() {
return (BOOL)colorMatchingEnabled;
}
BOOL isWindowAnimationEnabled() {
static int windowAnimationEnabled = -1;
if (windowAnimationEnabled == -1) {
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
if (env == NULL) return NO;
NSString* windowAnimationEnabledProp = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.window.animation"
withEnv:env];
windowAnimationEnabled = [@"true" isCaseInsensitiveLike:windowAnimationEnabledProp] ? YES : NO;
J2dRlsTraceLn(J2D_TRACE_INFO, "AWTWindow_windowAnimationEnabled: %d", windowAnimationEnabled);
}
return (BOOL)windowAnimationEnabled;
}
@interface NSTitlebarAccessoryViewController (Private)
- (void)_setHidden:(BOOL)h animated:(BOOL)a;
@end
@@ -605,6 +618,10 @@ AWT_ASSERT_APPKIT_THREAD;
if (self.nsWindow == nil) return nil; // no hope either
[self.nsWindow release]; // the property retains the object already
if (!isWindowAnimationEnabled()
&& (self.nsWindow.animationBehavior != NSWindowAnimationBehaviorNone)) {
self.nsWindow.animationBehavior = NSWindowAnimationBehaviorNone;
}
if (isColorMatchingEnabled()) {
// Supported by both OpenGL & Metal pipelines
// Tell the system we have an sRGB backing store
@@ -3008,8 +3025,6 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetRoundedCor
NSWindow *w = (NSWindow *)jlong_to_ptr(windowPtr);
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
w.hasShadow = YES;
w.contentView.wantsLayer = YES;
w.contentView.layer.cornerRadius = radius;
w.contentView.layer.masksToBounds = YES;
w.contentView.layer.opaque = NO;
@@ -3024,11 +3039,11 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetRoundedCor
w.contentView.layer.borderWidth = borderWidth;
w.contentView.layer.borderColor = color.CGColor;
}
w.contentView.wantsLayer = YES;
w.backgroundColor = NSColor.clearColor;
w.opaque = NO;
// remove corner radius animation
[w.contentView.layer removeAllAnimations];
w.hasShadow = YES;
[w invalidateShadow];
}];

View File

@@ -311,11 +311,11 @@ static const struct SymbolicHotKey defaultSymbolicHotKeys[] = {
[Shortcut_FullScreenTileRight] = { "FullScreenTileRight", "Windows: Full-Screen Tile Right", YES, 65535, 65535, 0, 15, 4 },
};
static const int numSymbolicHotkeys = sizeof(defaultSymbolicHotKeys) / sizeof(defaultSymbolicHotKeys[0]);
#define NUM_SYMBOLIC_HOTKEYS (sizeof(defaultSymbolicHotKeys) / sizeof(defaultSymbolicHotKeys[0]))
static struct SystemHotkeyState {
bool symbolicHotkeysFilled;
struct SymbolicHotKey currentSymbolicHotkeys[numSymbolicHotkeys];
struct SymbolicHotKey currentSymbolicHotkeys[NUM_SYMBOLIC_HOTKEYS];
bool initialized;
bool enabled;
@@ -398,7 +398,7 @@ static void visitServicesShortcut(Visitor visitorBlock, NSString * key_equivalen
visitorBlock(-1, keyChar.UTF8String, modifiers, desc.UTF8String, -1, NULL);
}
static void readAppleSymbolicHotkeys(struct SymbolicHotKey hotkeys[numSymbolicHotkeys]) {
static void readAppleSymbolicHotkeys(struct SymbolicHotKey hotkeys[NUM_SYMBOLIC_HOTKEYS]) {
// Called from the main thread
@try {
@@ -425,7 +425,7 @@ static void readAppleSymbolicHotkeys(struct SymbolicHotKey hotkeys[numSymbolicHo
return;
}
memcpy(hotkeys, defaultSymbolicHotKeys, numSymbolicHotkeys * sizeof(struct SymbolicHotKey));
memcpy(hotkeys, defaultSymbolicHotKeys, NUM_SYMBOLIC_HOTKEYS * sizeof(struct SymbolicHotKey));
for (id keyObj in hkObj) {
if (![keyObj isKindOfClass:[NSString class]]) {
@@ -438,7 +438,7 @@ static void readAppleSymbolicHotkeys(struct SymbolicHotKey hotkeys[numSymbolicHo
int uid = [hkNumber intValue];
// Ignore hotkeys out of range, as well as 0, which is the "invalid value" for intValue
if (uid <= 0 || uid >= numSymbolicHotkeys) {
if (uid <= 0 || uid >= NUM_SYMBOLIC_HOTKEYS) {
continue;
}
@@ -512,7 +512,7 @@ static void readAppleSymbolicHotkeys(struct SymbolicHotKey hotkeys[numSymbolicHo
}
static void updateAppleSymbolicHotkeysCache() {
struct SymbolicHotKey hotkeys[numSymbolicHotkeys];
struct SymbolicHotKey hotkeys[NUM_SYMBOLIC_HOTKEYS];
readAppleSymbolicHotkeys(hotkeys);
pthread_mutex_lock(&state.mutex);
@@ -521,10 +521,10 @@ static void updateAppleSymbolicHotkeysCache() {
pthread_mutex_unlock(&state.mutex);
}
static void iterateAppleSymbolicHotkeys(struct SymbolicHotKey hotkeys[numSymbolicHotkeys], Visitor visitorBlock) {
static void iterateAppleSymbolicHotkeys(struct SymbolicHotKey hotkeys[NUM_SYMBOLIC_HOTKEYS], Visitor visitorBlock) {
const NSOperatingSystemVersion macOSVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
for (int uid = 0; uid < numSymbolicHotkeys; ++uid) {
for (int uid = 0; uid < NUM_SYMBOLIC_HOTKEYS; ++uid) {
struct SymbolicHotKey* hotkey = &hotkeys[uid];
if (!hotkey->enabled) {
@@ -646,12 +646,12 @@ static bool ensureInitializedAndEnabled() {
return true;
}
static void readAppleSymbolicHotkeysCached(struct SymbolicHotKey hotkeys[numSymbolicHotkeys]) {
memset(hotkeys, 0, sizeof(struct SymbolicHotKey) * numSymbolicHotkeys);
static void readAppleSymbolicHotkeysCached(struct SymbolicHotKey hotkeys[NUM_SYMBOLIC_HOTKEYS]) {
memset(hotkeys, 0, sizeof(struct SymbolicHotKey) * NUM_SYMBOLIC_HOTKEYS);
pthread_mutex_lock(&state.mutex);
if (state.symbolicHotkeysFilled) {
memcpy(hotkeys, state.currentSymbolicHotkeys, sizeof(struct SymbolicHotKey) * numSymbolicHotkeys);
memcpy(hotkeys, state.currentSymbolicHotkeys, sizeof(struct SymbolicHotKey) * NUM_SYMBOLIC_HOTKEYS);
}
pthread_mutex_unlock(&state.mutex);
}
@@ -661,7 +661,7 @@ static void readSystemHotkeysImpl(Visitor visitorBlock) {
return;
}
struct SymbolicHotKey hotkeys[numSymbolicHotkeys];
struct SymbolicHotKey hotkeys[NUM_SYMBOLIC_HOTKEYS];
readAppleSymbolicHotkeysCached(hotkeys);
iterateAppleSymbolicHotkeys(hotkeys, visitorBlock);

View File

@@ -71,6 +71,8 @@
- (void) stopRedraw:(MTLContext*)mtlc displayID:(jint)displayID force:(BOOL)force;
- (void) flushBuffer;
- (void) commitCommandBuffer:(MTLContext*)mtlc wait:(BOOL)waitUntilCompleted display:(BOOL)updateDisplay;
- (void) addStatCallback:(int)type value:(double)value;
- (void) countFramePresentedCallback;
- (void) countFrameDroppedCallback;
@end

View File

@@ -129,6 +129,7 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
[NSNull null], @"anchorPoint",
[NSNull null], @"bounds",
[NSNull null], @"contents",
[NSNull null], @"cornerRadius",
[NSNull null], @"contentsScale",
[NSNull null], @"onOrderIn",
[NSNull null], @"onOrderOut",
@@ -226,29 +227,33 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
}
// Acquire CAMetalDrawable without blocking:
const CFTimeInterval beforeDrawableTime = (TRACE_DISPLAY) ? CACurrentMediaTime() : 0.0;
const CFTimeInterval beforeDrawableTime = CACurrentMediaTime();
const id<CAMetalDrawable> mtlDrawable = [self nextDrawable];
if (mtlDrawable == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: nextDrawable is null");
return;
}
const CFTimeInterval nextDrawableTime = (TRACE_DISPLAY) ? CACurrentMediaTime() : 0.0;
const CFTimeInterval nextDrawableTime = CACurrentMediaTime();
const CFTimeInterval nextDrawableLatency = (nextDrawableTime - beforeDrawableTime);
// rolling mean weight (lerp):
static const NSTimeInterval a = 0.25;
#if TRACE_DISPLAY_ON
const CFTimeInterval nextDrawableLatency = (nextDrawableTime - beforeDrawableTime);
if (nextDrawableLatency > 0.0) {
if (self.perfCountersEnabled) {
[self addStatCallback:1 value:1000.0 * nextDrawableLatency]; // See MTLLayer.STAT_NAMES[1]
}
#if TRACE_DISPLAY_ON
self.avgNextDrawableTime = nextDrawableLatency * a + self.avgNextDrawableTime * (1.0 - a);
}
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"[%.6lf] MTLLayer_blitTexture: drawable(%d) presented"
" - nextDrawableLatency = %.3lf ms - average = %.3lf ms",
CACurrentMediaTime(), mtlDrawable.drawableID,
1000.0 * nextDrawableLatency, 1000.0 * self.avgNextDrawableTime
);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"[%.6lf] MTLLayer_blitTexture: drawable(%d) presented"
" - nextDrawableLatency = %.3lf ms - average = %.3lf ms",
CACurrentMediaTime(), mtlDrawable.drawableID,
1000.0 * nextDrawableLatency, 1000.0 * self.avgNextDrawableTime
);
#endif
}
// Keep Fence from now:
releaseFence = NO;
@@ -389,8 +394,17 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
return;
}
const CFTimeInterval beforeMethod = CACurrentMediaTime();
(*env)->CallVoidMethod(env, javaLayerLocalRef, jm_drawInMTLContext);
CHECK_EXCEPTION();
const CFTimeInterval drawInMTLContextLatency = (CACurrentMediaTime() - beforeMethod);
if (drawInMTLContextLatency > 0.0) {
if (self.perfCountersEnabled) {
[self addStatCallback:0 value:1000.0 * drawInMTLContextLatency]; // See MTLLayer.STAT_NAMES[0]
}
}
(*env)->DeleteLocalRef(env, javaLayerLocalRef);
}
@@ -516,6 +530,20 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
}
}
- (void) addStatCallback:(int)type value:(double)value {
// attach the current thread to the JVM if necessary, and get an env
JNIEnv* env = [ThreadUtilities getJNIEnvUncached];
GET_MTL_LAYER_CLASS();
DECLARE_METHOD(jm_addStatFrame, jc_JavaLayer, "addStat", "(ID)V");
jobject javaLayerLocalRef = (*env)->NewLocalRef(env, self.javaLayer);
if (javaLayerLocalRef != NULL) {
(*env)->CallVoidMethod(env, javaLayerLocalRef, jm_addStatFrame, (jint)type, (jdouble)value);
CHECK_EXCEPTION();
(*env)->DeleteLocalRef(env, javaLayerLocalRef);
}
}
- (void) countFrameDroppedCallback {
// attach the current thread to the JVM if necessary, and get an env
JNIEnv* env = [ThreadUtilities getJNIEnvUncached];

View File

@@ -63,6 +63,7 @@ AWT_ASSERT_APPKIT_THREAD;
[NSNull null], @"anchorPoint",
[NSNull null], @"bounds",
[NSNull null], @"contents",
[NSNull null], @"cornerRadius",
[NSNull null], @"contentsScale",
[NSNull null], @"onOrderIn",
[NSNull null], @"onOrderOut",

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2025 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.jetbrains.desktop;
import com.jetbrains.exported.JBRApi;
import sun.java2d.vulkan.VKEnv;
import sun.java2d.vulkan.VKGPU;
@JBRApi.Service
@JBRApi.Provides("Vulkan")
public class Vulkan {
Vulkan() {
if (!VKEnv.isVulkanEnabled()) throw new JBRApi.ServiceNotAvailableException("Vulkan is not enabled");
}
boolean isPresentationEnabled() {
return VKEnv.isPresentationEnabled();
}
Device[] getDevices() {
return VKEnv.getDevices().map(Device::new).toArray(Device[]::new);
}
@JBRApi.Provides("Vulkan.Device")
static class Device {
private final VKGPU device;
Device(VKGPU device) {
this.device = device;
}
String getName() {
return device.getName();
}
String getTypeString() {
return device.getType().toString();
}
int getCapabilities() {
return device.getCaps();
}
}
}

View File

@@ -34,7 +34,6 @@ import java.awt.event.WindowFocusListener;
import java.awt.event.WindowListener;
import java.awt.event.WindowStateListener;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.im.InputContext;
import java.awt.image.BufferStrategy;
import java.awt.peer.ComponentPeer;
@@ -44,6 +43,7 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.PrintStream;
import java.io.Serial;
import java.io.Serializable;
import java.lang.annotation.Native;
@@ -53,12 +53,15 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EventListener;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -69,11 +72,14 @@ import javax.accessibility.AccessibleState;
import javax.accessibility.AccessibleStateSet;
import com.jetbrains.exported.JBRApi;
import jdk.internal.misc.InnocuousThread;
import sun.awt.AWTAccessor;
import sun.awt.AppContext;
import sun.awt.DebugSettings;
import sun.awt.SunToolkit;
import sun.awt.util.IdentityArrayList;
import sun.awt.util.ThreadGroupUtils;
import sun.java2d.marlin.stats.StatDouble;
import sun.java2d.pipe.Region;
import sun.util.logging.PlatformLogger;
@@ -1153,45 +1159,51 @@ public class Window extends Container implements Accessible {
}
void doDispose() {
class DisposeAction implements Runnable {
public void run() {
disposing = true;
try {
// Check if this window is the fullscreen window for the
// device. Exit the fullscreen mode prior to disposing
// of the window if that's the case.
GraphicsDevice gd = getGraphicsConfiguration().getDevice();
if (gd.getFullScreenWindow() == Window.this) {
gd.setFullScreenWindow(null);
}
final class DisposeAction implements Runnable {
public void run() {
final Window window = Window.this;
Object[] ownedWindowArray;
synchronized(ownedWindowList) {
ownedWindowArray = new Object[ownedWindowList.size()];
ownedWindowList.copyInto(ownedWindowArray);
}
for (int i = 0; i < ownedWindowArray.length; i++) {
Window child = (Window) (((WeakReference)
(ownedWindowArray[i])).get());
if (child != null) {
child.disposeImpl();
// dump stats if needed:
AWTAccessor.getWindowAccessor().dumpStats(window, true, null);
disposing = true;
try {
// Check if this window is the fullscreen window for the
// device. Exit the fullscreen mode prior to disposing
// of the window if that's the case.
GraphicsDevice gd = getGraphicsConfiguration().getDevice();
if (gd.getFullScreenWindow() == window) {
gd.setFullScreenWindow(null);
}
}
hide();
beforeFirstShow = true;
removeNotify();
synchronized (inputContextLock) {
if (inputContext != null) {
inputContext.dispose();
inputContext = null;
Object[] ownedWindowArray;
synchronized(ownedWindowList) {
ownedWindowArray = new Object[ownedWindowList.size()];
ownedWindowList.copyInto(ownedWindowArray);
}
for (int i = 0; i < ownedWindowArray.length; i++) {
Window child = (Window) (((WeakReference)
(ownedWindowArray[i])).get());
if (child != null) {
child.disposeImpl();
}
}
hide();
beforeFirstShow = true;
removeNotify();
synchronized (inputContextLock) {
if (inputContext != null) {
inputContext.dispose();
inputContext = null;
}
}
clearCurrentFocusCycleRootOnHide();
} finally {
disposing = false;
}
clearCurrentFocusCycleRootOnHide();
} finally {
disposing = false;
}
}
}
boolean fireWindowClosedEvent = isDisplayable();
DisposeAction action = new DisposeAction();
if (EventQueue.isDispatchThread()) {
@@ -4164,8 +4176,36 @@ public class Window extends Container implements Accessible {
return value;
}
private final static String STATS_ALL_SUFFIX = ".all";
private final static String SYSTEM_PROPERTY_COUNTERS;
private final static boolean USE_COUNTERS;
private final static boolean TRACE_ALL_COUNTERS;
private final static boolean TRACE_STD_ERR;
private final static int TRACE_CAPACITY;
private final static boolean TRACE_COUNTERS = true;
private final static boolean DUMP_STATS = true;
// thread dump interval (ms)
static final long DUMP_INTERVAL = 10 * 1000L;
private static PrintStream getTraceStdStream() {
// get live std stream:
return TRACE_STD_ERR ? System.err : System.out;
}
static {
String counters = System.getProperty("awt.window.counters");
SYSTEM_PROPERTY_COUNTERS = System.getProperty("awt.window.counters");
USE_COUNTERS = (SYSTEM_PROPERTY_COUNTERS != null);
TRACE_ALL_COUNTERS = USE_COUNTERS && (Objects.equals(SYSTEM_PROPERTY_COUNTERS, "")
|| Objects.equals(SYSTEM_PROPERTY_COUNTERS, "stderr")
|| Objects.equals(SYSTEM_PROPERTY_COUNTERS, "stdout"));
TRACE_STD_ERR = USE_COUNTERS && SYSTEM_PROPERTY_COUNTERS.contains("stderr");
TRACE_CAPACITY = USE_COUNTERS ? 8 : 0;
AWTAccessor.setWindowAccessor(new AWTAccessor.WindowAccessor() {
public void updateWindow(Window window) {
@@ -4202,100 +4242,201 @@ public class Window extends Container implements Accessible {
public boolean countersEnabled(Window w) {
// May want to selectively enable or disable counters per window
return counters != null;
return USE_COUNTERS;
}
public void bumpCounter(Window w, String counterName) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
private final static long NANO_IN_SEC = java.util.concurrent.TimeUnit.SECONDS.toNanos(1);
PerfCounter newCounter;
long curTimeNanos = System.nanoTime();
synchronized (w.perfCounters) {
newCounter = w.perfCounters.compute(counterName, (k, v) ->
v == null
? new PerfCounter(curTimeNanos, 1L)
: new PerfCounter(curTimeNanos, v.value + 1));
}
PerfCounter prevCounter;
synchronized (w.perfCountersPrev) {
prevCounter = w.perfCountersPrev.putIfAbsent(counterName, newCounter);
}
if (prevCounter != null) {
long nanosInSecond = java.util.concurrent.TimeUnit.SECONDS.toNanos(1);
long timeDeltaNanos = curTimeNanos - prevCounter.updateTimeNanos;
if (timeDeltaNanos > nanosInSecond) {
long valPerSecond = (long) ((double) (newCounter.value - prevCounter.value)
* nanosInSecond / timeDeltaNanos);
boolean traceAllCounters = Objects.equals(counters, "")
|| Objects.equals(counters, "stdout")
|| Objects.equals(counters, "stderr");
boolean traceEnabled = traceAllCounters || (counters != null && counters.contains(counterName));
if (traceEnabled) {
if (counters.contains("stderr")) {
System.err.println(counterName + " per second: " + valPerSecond);
} else {
System.out.println(counterName + " per second: " + valPerSecond);
}
}
if (perfLog.isLoggable(PlatformLogger.Level.FINE)) {
perfLog.fine(counterName + " per second: " + valPerSecond);
public void incrementCounter(final Window w, final String counterName) {
if (USE_COUNTERS) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
final long curTimeNanos = System.nanoTime();
// use try-catch to avoid throwing runtime exception to native JNI callers!
try {
PerfCounter newCounter, prevCounter;
synchronized (w.perfCounters) {
newCounter = w.perfCounters.compute(counterName, (_, v) ->
v == null
? new PerfCounter(curTimeNanos, 1L)
: new PerfCounter(curTimeNanos, v.value + 1));
}
synchronized (w.perfCountersPrev) {
w.perfCountersPrev.put(counterName, newCounter);
prevCounter = w.perfCountersPrev.putIfAbsent(counterName, newCounter);
}
if (prevCounter != null) {
final long timeDeltaNanos = curTimeNanos - prevCounter.updateTimeNanos;
if (timeDeltaNanos > NANO_IN_SEC) {
final double valPerSecond = (double) (newCounter.value - prevCounter.value)
* NANO_IN_SEC / timeDeltaNanos;
synchronized (w.perfCountersPrev) {
w.perfCountersPrev.put(counterName, newCounter);
}
addStat(w, counterName, valPerSecond);
if (TRACE_COUNTERS) {
dumpCounter(counterName, valPerSecond);
}
}
}
} catch (RuntimeException re) {
perfLog.severe("incrementCounter: failed", re);
}
}
}
public void addStat(final Window w, final String statName, final double value) {
if (USE_COUNTERS && Double.isFinite(value)) {
Objects.requireNonNull(w);
Objects.requireNonNull(statName);
// use try-catch to avoid throwing runtime exception to native JNI callers!
try {
synchronized (w.perfStats) {
StatDouble stat = w.perfStats.computeIfAbsent(statName, StatDouble::new);
stat.add(value);
stat = w.perfStats.computeIfAbsent(statName + STATS_ALL_SUFFIX, StatDouble::new);
stat.add(value);
}
} catch (RuntimeException re) {
perfLog.severe("addStat: failed", re);
}
}
}
public long getCounter(final Window w, final String counterName) {
if (USE_COUNTERS) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
synchronized (w.perfCounters) {
PerfCounter counter = w.perfCounters.get(counterName);
return counter != null ? counter.value : -1L;
}
}
return -1L;
}
public double getCounterPerSecond(final Window w, final String counterName) {
if (USE_COUNTERS) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
PerfCounter newCounter, prevCounter;
synchronized (w.perfCounters) {
newCounter = w.perfCounters.get(counterName);
}
synchronized (w.perfCountersPrev) {
prevCounter = w.perfCountersPrev.get(counterName);
}
if (newCounter != null && prevCounter != null) {
final long timeDeltaNanos = newCounter.updateTimeNanos - prevCounter.updateTimeNanos;
// Note that this time delta will usually be above one second.
if (timeDeltaNanos > 0L) {
return (double) (newCounter.value - prevCounter.value) * NANO_IN_SEC / timeDeltaNanos;
}
}
}
return Double.NaN;
}
public void dumpStats(final Window w, final boolean reset, StringBuilder sb) {
if (USE_COUNTERS) {
synchronized (w.perfStats) {
boolean header = false;
for (final StatDouble stat : w.perfStats.values()) {
if (stat.shouldLog()) {
final boolean traceEnabled = TRACE_ALL_COUNTERS || SYSTEM_PROPERTY_COUNTERS.contains(stat.name);
if (!header) {
header = true;
doLog(String.format("* Window['%s'@%s]:",
(w instanceof Frame ? ((Frame) w).getTitle() : ""),
Integer.toHexString(System.identityHashCode(w))),
traceEnabled);
}
// format:
if (sb == null) {
sb = new StringBuilder(128);
}
sb.setLength(0);
sb.append(" - ");
stat.toString(sb);
doLog(sb.toString(), traceEnabled);
if (reset && !stat.name.endsWith(STATS_ALL_SUFFIX)) {
stat.reset();
} else {
stat.updateLastLogCount();
}
}
}
}
}
}
public long getCounter(Window w, String counterName) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
synchronized (w.perfCounters) {
PerfCounter counter = w.perfCounters.get(counterName);
return counter != null ? counter.value : -1L;
private static void dumpCounter(final String counterName, final double valPerSecond) {
if (USE_COUNTERS) {
doLog(String.format("%s per second: %.2f", counterName, valPerSecond),
TRACE_ALL_COUNTERS || SYSTEM_PROPERTY_COUNTERS.contains(counterName));
}
}
public long getCounterPerSecond(Window w, String counterName) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
PerfCounter newCounter;
PerfCounter prevCounter;
synchronized (w.perfCounters) {
newCounter = w.perfCounters.get(counterName);
private static void doLog(final String msg, final boolean traceEnabled) {
if (traceEnabled) {
getTraceStdStream().println(msg);
}
synchronized (w.perfCountersPrev) {
prevCounter = w.perfCountersPrev.get(counterName);
if (perfLog.isLoggable(PlatformLogger.Level.FINE)) {
perfLog.fine(msg);
}
if (newCounter != null && prevCounter != null) {
long timeDeltaNanos = newCounter.updateTimeNanos - prevCounter.updateTimeNanos;
// Note that this time delta will usually be above one second.
if (timeDeltaNanos > 0) {
long nanosInSecond = java.util.concurrent.TimeUnit.SECONDS.toNanos(1);
long valPerSecond = (long) ((double) (newCounter.value - prevCounter.value)
* nanosInSecond / timeDeltaNanos);
return valPerSecond;
}
}
return -1;
}
}); // WindowAccessor
if (USE_COUNTERS) {
final Runnable dumper = new Runnable() {
private final static StringBuilder sb = new StringBuilder(128);
@Override
public void run() {
getTraceStdStream().printf("--- WindowStats dump at: %s ---\n", new java.util.Date());
final AWTAccessor.WindowAccessor windowAccessor = AWTAccessor.getWindowAccessor();
for (Window window : Window.getWindows()) {
// dump stats if needed:
windowAccessor.dumpStats(window, true, sb);
}
getTraceStdStream().println("-----");
}
};
final Thread hook = InnocuousThread.newSystemThread("WindowStatsHook", dumper);
hook.setDaemon(true);
hook.setContextClassLoader(null);
Runtime.getRuntime().addShutdownHook(hook);
if (DUMP_STATS) {
final Timer statTimer = new Timer("WindowStats", true);
statTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
dumper.run();
}
}, DUMP_INTERVAL, DUMP_INTERVAL);
}
}
} // static
// a window doesn't need to be updated in the Z-order.
@Override
void updateZOrder() {}
private record PerfCounter(Long updateTimeNanos, Long value) {}
private record PerfCounter(long updateTimeNanos, long value) {}
private transient final Map<String, PerfCounter> perfCounters = new HashMap<>(4);
private transient final Map<String, PerfCounter> perfCountersPrev = new HashMap<>(4);
private transient final HashMap<String, PerfCounter> perfCounters = (USE_COUNTERS) ? new HashMap<>(TRACE_CAPACITY) : null;
private transient final HashMap<String, PerfCounter> perfCountersPrev = (USE_COUNTERS) ? new HashMap<>(TRACE_CAPACITY) : null;
private transient final LinkedHashMap<String, StatDouble> perfStats = (USE_COUNTERS) ? new LinkedHashMap<>(TRACE_CAPACITY) : null;
} // class Window

View File

@@ -27,10 +27,7 @@ package javax.swing;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.VolatileImage;
import java.awt.peer.WindowPeer;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.applet.*;
@@ -43,14 +40,12 @@ import sun.java2d.SunGraphicsEnvironment;
import com.sun.java.swing.SwingUtilities3;
import java.awt.geom.AffineTransform;
import java.util.stream.Collectors;
import sun.java2d.SunGraphics2D;
import sun.java2d.pipe.Region;
import sun.swing.SwingAccessor;
import sun.swing.SwingUtilities2;
import sun.swing.SwingUtilities2.RepaintListener;
import java.util.stream.Collectors;
/**
* This class manages repaint requests, allowing the number
@@ -755,7 +750,7 @@ public class RepaintManager
.filter(Objects::nonNull)
.distinct()
.forEach(w -> AWTAccessor.getWindowAccessor()
.bumpCounter(w, "swing.RepaintManager.updateWindows"));
.incrementCounter(w, "swing.RepaintManager.updateWindows"));
if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit &&
sunToolkit.needUpdateWindow()) {
@@ -774,14 +769,14 @@ public class RepaintManager
for (Window window : windows) {
AWTAccessor.getWindowAccessor().updateWindow(window);
AWTAccessor.getWindowAccessor().bumpCounter(window, "swing.RepaintManager.updateWindows");
AWTAccessor.getWindowAccessor().incrementCounter(window, "swing.RepaintManager.updateWindows");
}
} else {
dirtyComponents.keySet().stream()
.map(c -> c instanceof Window w ? w : SwingUtilities.getWindowAncestor(c))
.filter(Objects::nonNull)
.forEach(w -> AWTAccessor.getWindowAccessor()
.bumpCounter(w, "swing.RepaintManager.updateWindows"));
.incrementCounter(w, "swing.RepaintManager.updateWindows"));
}
}

View File

@@ -748,6 +748,27 @@ public class BasicTreeUI extends TreeUI
return bounds;
}
/**
* A potentially faster version of {@link #getPathBounds(JTree, TreePath)}
* which calculates only {@code y} and {@code height}
* of the bounding {@code Rectangle}
*/
private Rectangle getVerticalPathBounds(TreePath path) {
if (tree == null || treeState == null) {
return null;
}
int rowHeight = treeState.getRowHeight();
if (rowHeight <= 0) {
return getPathBounds(tree, path);
}
int row = treeState.getRowForPath(path);
if (row < 0) {
return null;
}
return new Rectangle(0, tree.getInsets().top + row * rowHeight,
0, rowHeight);
}
/**
* Returns the path for passed in row. If row is not visible
* null is returned.
@@ -4381,17 +4402,21 @@ public class BasicTreeUI extends TreeUI
updateSize();
}
else if (treeState.isExpanded(parentPath)) {
// Changed nodes are visible
// Find the minimum index, we only need paint from there
// down.
int minIndex = indices[0];
for (int i = indices.length - 1; i > 0; i--) {
minIndex = Math.min(indices[i], minIndex);
TreePath minPath = null;
Rectangle minBounds = null;
if (tree.isShowing()) {
// Changed nodes are visible
// Find the minimum index, we only need paint from there
// down.
int minIndex = indices[0];
for (int i = indices.length - 1; i > 0; i--) {
minIndex = Math.min(indices[i], minIndex);
}
Object minChild = treeModel.getChild(
parentPath.getLastPathComponent(), minIndex);
minPath = parentPath.pathByAddingChild(minChild);
minBounds = getVerticalPathBounds(minPath);
}
Object minChild = treeModel.getChild(
parentPath.getLastPathComponent(), minIndex);
TreePath minPath = parentPath.pathByAddingChild(minChild);
Rectangle minBounds = getPathBounds(tree, minPath);
// Forward to the treestate
treeState.treeNodesChanged(e);
@@ -4399,20 +4424,19 @@ public class BasicTreeUI extends TreeUI
// Mark preferred size as bogus.
updateSize0();
// And repaint
Rectangle newMinBounds = getPathBounds(tree, minPath);
if (minBounds == null || newMinBounds == null) {
return;
}
if (indices.length == 1 &&
newMinBounds.height == minBounds.height) {
tree.repaint(0, minBounds.y, tree.getWidth(),
minBounds.height);
}
else {
tree.repaint(0, minBounds.y, tree.getWidth(),
tree.getHeight() - minBounds.y);
if (minBounds != null) {
// And repaint
Rectangle newMinBounds = getVerticalPathBounds(minPath);
if (newMinBounds != null) {
if (indices.length == 1 &&
newMinBounds.height == minBounds.height) {
tree.repaint(0, minBounds.y, tree.getWidth(),
minBounds.height);
} else {
tree.repaint(0, minBounds.y, tree.getWidth(),
tree.getHeight() - minBounds.y);
}
}
}
}
else {

View File

@@ -37,7 +37,6 @@ import java.awt.event.InputEvent;
import java.awt.event.InvocationEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.image.BufferStrategy;
import java.awt.peer.ComponentPeer;
@@ -329,10 +328,15 @@ public final class AWTAccessor {
*/
Window[] getOwnedWindows(Window w);
/* JBR Window counters API */
boolean countersEnabled(Window w);
void bumpCounter(Window w, String counterName);
void incrementCounter(Window w, String counterName);
void addStat(Window w, String statName, double value);
long getCounter(Window w, String counterName);
long getCounterPerSecond(Window w, String counterName);
double getCounterPerSecond(Window w, String counterName);
void dumpStats(Window w, boolean reset, StringBuilder sb);
}
/**

View File

@@ -50,6 +50,7 @@ import java.awt.KeyboardFocusManager;
import java.awt.Label;
import java.awt.MenuComponent;
import java.awt.Panel;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.ScrollPane;
import java.awt.Scrollbar;
@@ -90,6 +91,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import com.jetbrains.exported.JBRApi;
import sun.awt.im.InputContext;
import sun.awt.image.ByteArrayImageSource;
import sun.awt.image.FileImageSource;
@@ -2084,6 +2086,23 @@ public abstract class SunToolkit extends Toolkit
return AWTAccessor.getAWTEventAccessor().isSystemGenerated(e);
}
@JBRApi.Service
@JBRApi.Provides("RelativePointerMovement")
public interface RelativePointerMovementInfoProvider {
private static RelativePointerMovementInfoProvider create() {
var tk = Toolkit.getDefaultToolkit();
if (tk instanceof ComponentFactory cf) {
var mouseInfoPeer = cf.getMouseInfoPeer();
if (mouseInfoPeer instanceof RelativePointerMovementInfoProvider p){
return p;
}
}
throw new JBRApi.ServiceNotAvailableException("Service not supported for toolkit " + tk.getClass().getName());
}
Point getAccumulatedMouseDeltaAndReset();
}
} // class SunToolkit

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -144,7 +144,9 @@ final class Curve {
// finds points where the first and second derivative are
// perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
// * is a dot product). Unfortunately, we have to solve a cubic.
private int perpendiculardfddf(final double[] pts, final int off) {
private int perpendiculardfddf(final double[] pts, final int off,
final double A, final double B)
{
assert pts.length >= off + 4;
// these are the coefficients of some multiple of g(t) (not g(t),
@@ -155,7 +157,7 @@ final class Curve {
final double c = 2.0d * (dax * cx + day * cy) + dbx * dbx + dby * dby;
final double d = dbx * cx + dby * cy;
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0d, 1.0d);
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, A, B);
}
// Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
@@ -171,35 +173,43 @@ final class Curve {
// at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
// points, so roc-w can have at least 6 roots. This shouldn't be a
// problem for what we're trying to do (draw a nice looking curve).
int rootsOfROCMinusW(final double[] roots, final int off, final double w2, final double err) {
int rootsOfROCMinusW(final double[] roots, final int off, final double w2,
final double A, final double B)
{
// no OOB exception, because by now off<=6, and roots.length >= 10
assert off <= 6 && roots.length >= 10;
int ret = off;
final int end = off + perpendiculardfddf(roots, off);
final int end = off + perpendiculardfddf(roots, off, A, B);
Helpers.isort(roots, off, end);
roots[end] = 1.0d; // always check interval end points
double t0 = 0.0d, ft0 = ROCsq(t0) - w2;
double t0 = 0.0d;
double ft0 = eliminateInf(ROCsq(t0) - w2);
double t1, ft1;
for (int i = off; i <= end; i++) {
double t1 = roots[i], ft1 = ROCsq(t1) - w2;
t1 = roots[i];
ft1 = eliminateInf(ROCsq(t1) - w2);
if (ft0 == 0.0d) {
roots[ret++] = t0;
} else if (ft1 * ft0 < 0.0d) { // have opposite signs
// (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
// ROC(t) >= 0 for all t.
roots[ret++] = falsePositionROCsqMinusX(t0, t1, w2, err);
roots[ret++] = falsePositionROCsqMinusX(t0, t1, ft0, ft1, w2, A); // A = err
}
t0 = t1;
ft0 = ft1;
}
return ret - off;
}
private static double eliminateInf(final double x) {
return (x == Double.POSITIVE_INFINITY ? Double.MAX_VALUE :
(x == Double.NEGATIVE_INFINITY ? Double.MIN_VALUE : x));
private final static double MAX_ROC_SQ = 1e20;
private static double eliminateInf(final double x2) {
// limit the value of x to avoid numerical problems (smaller step):
// must handle NaN and +Infinity:
return (x2 <= MAX_ROC_SQ) ? x2 : MAX_ROC_SQ;
}
// A slight modification of the false position algorithm on wikipedia.
@@ -210,17 +220,18 @@ final class Curve {
// and turn out. Same goes for the newton's method
// algorithm in Helpers.java
private double falsePositionROCsqMinusX(final double t0, final double t1,
final double ft0, final double ft1,
final double w2, final double err)
{
final int iterLimit = 100;
int side = 0;
double t = t1, ft = eliminateInf(ROCsq(t) - w2);
double s = t0, fs = eliminateInf(ROCsq(s) - w2);
double s = t0, fs = eliminateInf(ft0);
double t = t1, ft = eliminateInf(ft1);
double r = s, fr;
for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
for (int i = 0; i < iterLimit && Math.abs(t - s) > err; i++) {
r = (fs * t - ft * s) / (fs - ft);
fr = ROCsq(r) - w2;
fr = eliminateInf(ROCsq(r) - w2);
if (sameSign(fr, ft)) {
ft = fr; t = r;
if (side < 0) {
@@ -241,7 +252,7 @@ final class Curve {
break;
}
}
return r;
return (Math.abs(ft) <= Math.abs(fs)) ? t : s;
}
private static boolean sameSign(final double x, final double y) {
@@ -256,9 +267,9 @@ final class Curve {
final double dy = t * (t * day + dby) + cy;
final double ddx = 2.0d * dax * t + dbx;
final double ddy = 2.0d * day * t + dby;
final double dx2dy2 = dx * dx + dy * dy;
final double ddx2ddy2 = ddx * ddx + ddy * ddy;
final double ddxdxddydy = ddx * dx + ddy * dy;
return dx2dy2 * ((dx2dy2 * dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy * ddxdxddydy));
final double dx2dy2 = dx * dx + dy * dy; // positive
final double dxddyddxdy = dx * ddy - dy * ddx;
// may return +Infinity if dxddyddxdy = 0 or NaN if 0/0:
return (dx2dy2 * dx2dy2 * dx2dy2) / (dxddyddxdy * dxddyddxdy); // both positive
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -564,7 +564,7 @@ public final class DMarlinRenderingEngine extends RenderingEngine
}
private static boolean nearZero(final double num) {
return Math.abs(num) < 2.0d * Math.ulp(num);
return Math.abs(num) < 2.0d * Helpers.ulp(num);
}
abstract static class NormalizingPathIterator implements PathIterator {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,12 +31,19 @@ import sun.java2d.marlin.stats.StatLong;
final class Helpers implements MarlinConst {
private final static double T_ERR = 1e-4;
private final static double T_A = T_ERR;
private final static double T_B = 1.0 - T_ERR;
private static final double EPS = 1e-9d;
private Helpers() {
throw new Error("This is a non instantiable class");
}
/** use lower precision like former Pisces and Marlin (float-precision) */
static double ulp(final double value) { return Math.ulp((float)value); }
static boolean within(final double x, final double y) {
return within(x, y, EPS);
}
@@ -322,10 +329,10 @@ final class Helpers implements MarlinConst {
// now we must subdivide at points where one of the offset curves will have
// a cusp. This happens at ts where the radius of curvature is equal to w.
ret += c.rootsOfROCMinusW(ts, ret, w2, 0.0001d);
ret += c.rootsOfROCMinusW(ts, ret, w2, T_A, T_B);
ret = filterOutNotInAB(ts, 0, ret, 0.0001d, 0.9999d);
isort(ts, ret);
ret = filterOutNotInAB(ts, 0, ret, T_A, T_B);
isort(ts, 0, ret);
return ret;
}
@@ -354,7 +361,7 @@ final class Helpers implements MarlinConst {
if ((outCodeOR & OUTCODE_BOTTOM) != 0) {
ret += curve.yPoints(ts, ret, clipRect[1]);
}
isort(ts, ret);
isort(ts, 0, ret);
return ret;
}
@@ -374,11 +381,11 @@ final class Helpers implements MarlinConst {
}
}
static void isort(final double[] a, final int len) {
for (int i = 1, j; i < len; i++) {
static void isort(final double[] a, final int off, final int len) {
for (int i = off + 1, j; i < len; i++) {
final double ai = a[i];
j = i - 1;
for (; j >= 0 && a[j] > ai; j--) {
for (; j >= off && a[j] > ai; j--) {
a[j + 1] = a[j];
}
a[j + 1] = ai;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,8 @@ package sun.java2d.marlin;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue;
import jdk.internal.misc.InnocuousThread;
import jdk.internal.ref.CleanerFactory;
import sun.java2d.marlin.ArrayCacheConst.CacheStats;
import static sun.java2d.marlin.MarlinUtils.logInfo;
@@ -383,21 +385,13 @@ public final class RendererStats implements MarlinConst {
= new ConcurrentLinkedQueue<>();
private RendererStatsHolder() {
final Thread hook = new Thread(
MarlinUtils.getRootThreadGroup(),
new Runnable() {
@Override
public void run() {
dump();
}
},
"MarlinStatsHook"
);
final Thread hook = InnocuousThread.newSystemThread("MarlinStatsHook", () -> dump());
hook.setDaemon(true);
hook.setContextClassLoader(null);
Runtime.getRuntime().addShutdownHook(hook);
if (USE_DUMP_THREAD) {
final Timer statTimer = new Timer("RendererStats");
final Timer statTimer = new Timer("RendererStats", true);
statTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -886,8 +886,8 @@ final class Stroker implements StartFlagPathConsumer2D, MarlinConst {
// if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
// in which case ignore if p1 == p2
final boolean p1eqp2 = Helpers.withinD(dx1, dy1, 6.0d * Math.ulp(y2));
final boolean p3eqp4 = Helpers.withinD(dx4, dy4, 6.0d * Math.ulp(y4));
final boolean p1eqp2 = Helpers.withinD(dx1, dy1, 6.0d * Helpers.ulp(y2));
final boolean p3eqp4 = Helpers.withinD(dx4, dy4, 6.0d * Helpers.ulp(y4));
if (p1eqp2 && p3eqp4) {
return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
@@ -905,7 +905,7 @@ final class Stroker implements StartFlagPathConsumer2D, MarlinConst {
final double l1sq = dx1 * dx1 + dy1 * dy1;
final double l4sq = dx4 * dx4 + dy4 * dy4;
if (Helpers.within(dotsq, l1sq * l4sq, 4.0d * Math.ulp(dotsq))) {
if (Helpers.within(dotsq, l1sq * l4sq, 4.0d * Helpers.ulp(dotsq))) {
return getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
}
@@ -1078,8 +1078,8 @@ final class Stroker implements StartFlagPathConsumer2D, MarlinConst {
// equal if they're very close to each other.
// if p1 == p2 or p2 == p3: draw line from p1->p3
final boolean p1eqp2 = Helpers.withinD(dx12, dy12, 6.0d * Math.ulp(y2));
final boolean p2eqp3 = Helpers.withinD(dx23, dy23, 6.0d * Math.ulp(y3));
final boolean p1eqp2 = Helpers.withinD(dx12, dy12, 6.0d * Helpers.ulp(y2));
final boolean p2eqp3 = Helpers.withinD(dx23, dy23, 6.0d * Helpers.ulp(y3));
if (p1eqp2 || p2eqp3) {
return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
@@ -1091,7 +1091,7 @@ final class Stroker implements StartFlagPathConsumer2D, MarlinConst {
final double l1sq = dx12 * dx12 + dy12 * dy12;
final double l3sq = dx23 * dx23 + dy23 * dy23;
if (Helpers.within(dotsq, l1sq * l3sq, 4.0d * Math.ulp(dotsq))) {
if (Helpers.within(dotsq, l1sq * l3sq, 4.0d * Helpers.ulp(dotsq))) {
return getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,7 @@ package sun.java2d.marlin;
public final class Version {
private static final String VERSION = "marlin-0.9.4.7-Unsafe-OpenJDK";
private static final String VERSION = "marlin-0.9.4.9-Unsafe-OpenJDK";
public static String getVersion() {
return VERSION;

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright 2025 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin.stats;
import static sun.java2d.marlin.stats.StatLong.trimTo3Digits;
/**
* Statistics on double values
*/
public final class StatDouble {
// rolling mean weight (lerp):
private final static double EMA_ALPHA = 0.25;
private final static double EMA_ONE_MINUS_ALPHA = 1.0 - EMA_ALPHA;
public final String name;
private long count, lastLogCount;
private double min, max, mean, ema_mean = 0.0, squaredError;
public StatDouble(final String name) {
this.name = name;
reset();
}
public void reset() {
count = 0L;
lastLogCount = 0L;
min = Double.POSITIVE_INFINITY;
max = Double.NEGATIVE_INFINITY;
mean = 0.0;
// skip ema_mean = 0.0;
squaredError = 0.0;
}
public void add(final double val) {
count++;
if (val < min) {
min = val;
}
if (val > max) {
max = val;
}
// Exponential smoothing (EMA):
ema_mean = EMA_ALPHA * val + EMA_ONE_MINUS_ALPHA * ema_mean;
// Welford's algorithm:
final double oldMean = mean;
mean += (val - mean) / count;
squaredError += (val - mean) * (val - oldMean);
}
public boolean shouldLog() {
return (count > lastLogCount);
}
public void updateLastLogCount() {
this.lastLogCount = this.count;
}
public long count() {
return count;
}
public double min() {
return (count != 0L) ? min : Double.NaN;
}
public double max() {
return (count != 0L) ? max : Double.NaN;
}
public double mean() {
return (count != 0L) ? mean : Double.NaN;
}
public double ema() {
return (count != 0L) ? ema_mean : Double.NaN;
}
public double variance() {
return (count != 0L) ? (squaredError / (count - 1L)) : Double.NaN;
}
public double stddev() {
return (count != 0L) ? Math.sqrt(variance()) : Double.NaN;
}
public double total() {
return (count != 0L) ? (mean() * count) : Double.NaN;
}
@Override
public String toString() {
return toString(new StringBuilder(128)).toString();
}
public StringBuilder toString(final StringBuilder sb) {
sb.append(name).append('[').append(count);
sb.append("] sum: ").append(trimTo3Digits(total()));
sb.append(" avg: ").append(trimTo3Digits(mean()));
sb.append(" stddev: ").append(trimTo3Digits(stddev()));
sb.append(" ema: ").append(trimTo3Digits(ema()));
sb.append(" [").append(trimTo3Digits(min())).append(" - ").append(trimTo3Digits(max())).append("]");
return sb;
}
}

View File

@@ -31,7 +31,6 @@ import sun.util.logging.PlatformLogger;
import java.awt.Toolkit;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public final class VKEnv {
@@ -49,7 +48,7 @@ public final class VKEnv {
@SuppressWarnings("removal")
private static final boolean accelsd = vulkan && "true".equalsIgnoreCase(AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan.accelsd", "")));
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan.accelsd", "true")));
@SuppressWarnings("removal")
private static final int deviceNumber = !vulkan ? 0 : AccessController.doPrivileged(
@@ -109,16 +108,20 @@ public final class VKEnv {
state = newState;
if (Options.verbose || log.isLoggable(PlatformLogger.Level.FINE)) {
String message;
StringBuilder msg = new StringBuilder("Vulkan rendering enabled: ");
if (isVulkanEnabled()) {
message = "Vulkan rendering enabled: YES" +
"\n presentation enabled: " + (isPresentationEnabled() ? "YES" : "NO") +
"\n accelerated surface data enabled: " + (isSurfaceDataAccelerated() ? "YES" : "NO") +
"\n devices:" + Stream.of(devices).map(d -> (d == defaultDevice ?
"\n *" : "\n ") + d.getName()).collect(Collectors.joining());
msg.append("YES")
.append("\n Presentation enabled: ").append(isPresentationEnabled() ? "YES" : "NO")
.append("\n Accelerated surface data enabled: ").append(isSurfaceDataAccelerated() ? "YES" : "NO")
.append("\n Devices:");
for (int i = 0; i < devices.length; i++) {
VKGPU d = devices[i];
msg.append(d == defaultDevice ? "\n *" : "\n ").append(i).append(": ").append(d.getName());
}
} else {
message = "Vulkan rendering enabled: NO";
msg.append("NO");
}
String message = msg.toString();
if (Options.verbose) {
System.err.println(message);
}

View File

@@ -36,6 +36,7 @@ import sun.java2d.loops.CompositeType;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.loops.SurfaceType;
import static sun.java2d.pipe.BufferedOpCodes.CONFIGURE_SURFACE;
import static sun.java2d.pipe.BufferedOpCodes.DISPOSE_SURFACE;
import sun.java2d.pipe.BufferedContext;
import sun.java2d.pipe.ParallelogramPipe;
import sun.java2d.pipe.PixelToParallelogramConverter;
@@ -43,7 +44,6 @@ import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.TextPipe;
import sun.java2d.pipe.hw.AccelSurface;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
@@ -137,6 +137,28 @@ public abstract class VKSurfaceData extends SurfaceData
}
}
/**
* Disposes the native resources associated with the given VKSurfaceData
* (referenced by the pData parameter). This method is invoked from
* the native Dispose() method from the Disposer thread when the
* Java-level VKSurfaceData object is about to go away.
*/
static void dispose(long pData) {
VKRenderQueue rq = VKRenderQueue.getInstance();
rq.lock();
try {
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
buf.putInt(DISPOSE_SURFACE);
buf.putLong(pData);
// this call is expected to complete synchronously, so flush now
rq.flushNow();
} finally {
rq.unlock();
}
}
public BufferedImage getSnapshot(int x, int y, int width, int height) {
BufferedImage image = getFormat().createCompatibleImage(width, height, getTransparency());
SurfaceData sd = SurfaceData.getPrimarySurfaceData(image);

View File

@@ -1,20 +0,0 @@
#define ALPHA_TYPE_PRE_MULTIPLIED 0U
#define ALPHA_TYPE_STRAIGHT 1U
vec4 convertAlpha(vec4 color, uint inType, uint outType) {
if (inType == ALPHA_TYPE_STRAIGHT && outType == ALPHA_TYPE_PRE_MULTIPLIED) {
return vec4(color.rgb * color.a, color.a);
} else if (inType == ALPHA_TYPE_PRE_MULTIPLIED && outType == ALPHA_TYPE_STRAIGHT && color.a > 0.0) {
return vec4(color.rgb / color.a, color.a);
} else return color;
}
#ifdef ALPHA_TYPE_SPEC_INDEX
layout (constant_id = ALPHA_TYPE_SPEC_INDEX ) const uint const_InAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
layout (constant_id = ALPHA_TYPE_SPEC_INDEX+1) const uint const_OutAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
vec4 convertAlpha(vec4 color) {
return convertAlpha(color, const_InAlphaType, const_OutAlphaType);
}
#endif

View File

@@ -1,7 +1,7 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#define ALPHA_TYPE_SPEC_INDEX 0
#include "alpha_type.glsl"
#include "common.glsl"
DEFAULT_PUSH_CONSTANTS();
layout(set = 0, binding = 0) uniform texture2D u_Texture;
layout(set = 1, binding = 0) uniform sampler u_Sampler;
@@ -9,5 +9,5 @@ layout(location = 0) in vec2 in_TexCoord;
layout(location = 0) out vec4 out_Color;
void main() {
out_Color = convertAlpha(texture(sampler2D(u_Texture, u_Sampler), in_TexCoord));
out_Color = APPLY_COMPOSITE(convertAlpha(texture(sampler2D(u_Texture, u_Sampler), in_TexCoord)));
}

View File

@@ -1,14 +1,12 @@
#version 450
layout(push_constant) uniform PushConstants {
mat2x3 transform;
} push;
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in vec2 in_Position;
layout(location = 1) in vec2 in_TexCoord;
layout(location = 0) out vec2 out_TexCoord;
void main() {
gl_Position = vec4(vec3(in_Position, 1.0)*push.transform, 0.0, 1.0);
gl_Position = transformToDeviceSpace(in_Position);
out_TexCoord = in_TexCoord;
}

View File

@@ -1,11 +1,9 @@
#version 450
layout(push_constant) uniform PushConstants {
mat2x3 transform;
} push;
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in ivec2 in_Position;
void main() {
gl_Position = vec4(vec3(in_Position, 1)*push.transform, 0.0, 1.0);
gl_Position = transformToDeviceSpace(in_Position);
}

View File

@@ -1,14 +1,12 @@
#version 450
layout(push_constant) uniform PushConstants {
mat2x3 transform;
} push;
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in vec2 in_Position;
layout(location = 1) in vec4 in_Color;
layout(location = 1) in uint in_Color;
layout(location = 0) out flat vec4 out_Color;
void main() {
gl_Position = vec4(vec3(in_Position, 1)*push.transform, 0.0, 1.0);
out_Color = in_Color;
gl_Position = transformToDeviceSpace(in_Position);
out_Color = convertAlpha(decodeColor(in_Color)); // No need to APPLY_COMPOSITE - it was already done on the host.
}

View File

@@ -0,0 +1,103 @@
// Shader specialization.
#define SHADER_MOD_XOR 1U // Xor composite mode
#define SHADER_MOD_MASK 2U // MASK_FILL / MASK_BLIT
layout (constant_id = 0) const uint const_InAlphaType = 0;
layout (constant_id = 1) const uint const_OutAlphaType = 0;
layout (constant_id = 2) const uint const_ShaderVariant = 0;
layout (constant_id = 3) const uint const_ShaderModifier = 0;
// Host structs.
struct VKTransform {
float m00, m01, m02;
float m10, m11, m12;
};
struct VKCompositeConstants {
uint xorColor;
float extraAlpha;
};
// Vertex shader transformation support.
#ifdef STAGE_VERT
layout(push_constant) uniform PushConstants { VKTransform transform; } push;
vec4 transformToDeviceSpace(vec2 v) {
return vec4(vec3(v, 1.0) * mat2x3(push.transform.m00, push.transform.m01, push.transform.m02, push.transform.m10, push.transform.m11, push.transform.m12), 0.0, 1.0);
}
#endif
// Fragment shader push constant support.
#ifdef STAGE_FRAG
#define PUSH_CONSTANTS_IMPL(STATEMENT) \
layout(push_constant) uniform PushConstants { VKTransform _; VKCompositeConstants push_composite; STATEMENT }
#define DEFAULT_PUSH_CONSTANTS() PUSH_CONSTANTS_IMPL(STAGE_FRAG)
#define PUSH_CONSTANTS(TYPE) PUSH_CONSTANTS_IMPL(TYPE push;)
#endif
// Color conversion support.
#define ALPHA_TYPE_PRE_MULTIPLIED 0U
#define ALPHA_TYPE_STRAIGHT 1U
vec4 convertAlpha(vec4 color, uint inType, uint outType) {
if (inType == ALPHA_TYPE_STRAIGHT && outType == ALPHA_TYPE_PRE_MULTIPLIED) {
return vec4(color.rgb * color.a, color.a);
} else if (inType == ALPHA_TYPE_PRE_MULTIPLIED && outType == ALPHA_TYPE_STRAIGHT && color.a > 0.0) {
return vec4(color.rgb / color.a, color.a);
} else return color;
}
vec4 convertAlpha(vec4 color) {
return convertAlpha(color, const_InAlphaType, const_OutAlphaType);
}
// When applying alpha to a color, straight alpha only multiplies alpha,
// and pre-multiplied multiplies the whole color. Use this for convenience.
vec4 alphaMask(float alpha, uint alphaType) {
return alphaType == ALPHA_TYPE_PRE_MULTIPLIED ? vec4(alpha) : vec4(1.0, 1.0, 1.0, alpha);
}
// Decode color from uint-packed ARGB components.
vec4 decodeColor(uint srgb) {
return vec4((uvec4(srgb) >> uvec4(16, 8, 0, 24)) & 0xFFU) / 255.0;
}
#ifdef STAGE_FRAG
// Before outputting the color, some post-processing is needed:
// - For alpha composite, apply extra alpha.
// - For XOR composite, apply xor.
vec4 applyComposite(vec4 color, VKCompositeConstants composite) {
if ((const_ShaderModifier & SHADER_MOD_XOR) != 0) {
uvec4 xor = uvec4(composite.xorColor) >> uvec4(16, 8, 0, 24);
xor = (uvec4(color * 255.0) ^ xor) & 0xFFU;
return vec4(xor) / 255.0;
} else return color * alphaMask(composite.extraAlpha, const_OutAlphaType);
}
#define APPLY_COMPOSITE(COLOR) applyComposite(COLOR, push_composite)
// MASK_FILL / MASK_BLIT support.
int calculateMaskIndex(vec2 localCoord, ivec4 originOffsetAndScanline) {
ivec2 maskPos = ivec2(localCoord - vec2(originOffsetAndScanline.xy));
int offset = originOffsetAndScanline.z;
int scanline = originOffsetAndScanline.w;
return offset + scanline * maskPos.y + min(scanline, maskPos.x);
}
vec4 applyMaskOp(vec4 color, float mask) {
if ((const_ShaderModifier & SHADER_MOD_XOR) != 0) return color * float(mask > 0.0);
else return color * alphaMask(mask, const_OutAlphaType);
}
#define APPLY_MASK(COLOR) applyMaskOp(COLOR, imageLoad(u_Mask, calculateMaskIndex(gl_FragCoord.xy, in_OriginOffsetAndScanline)).r)
// Generic shader support.
#define GENERIC_INOUT() \
layout(location = 0) out vec4 out_Color; \
layout(location = 0) in vec2 in_Position; \
layout(location = 1) in flat uint in_Data; \
layout(location = 2) in flat ivec4 in_OriginOffsetAndScanline; \
layout(origin_upper_left) in vec4 gl_FragCoord; \
layout(set = 0, binding = 0, r8) uniform readonly restrict imageBuffer u_Mask
// Generic color output - handles composite and mask automatically.
#define OUTPUT(COLOR) out_Color = COLOR; out_Color = APPLY_COMPOSITE(out_Color); \
if ((const_ShaderModifier & SHADER_MOD_MASK) != 0) out_Color = APPLY_MASK(out_Color)
#endif

View File

@@ -0,0 +1,21 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
#define SHADER_VARIANT_GRADIENT_CLAMP 0
#define SHADER_VARIANT_GRADIENT_CYCLE 1
struct VKGradientPaintConstants {
vec4 c0, c1;
vec3 p;
};
PUSH_CONSTANTS(VKGradientPaintConstants);
GENERIC_INOUT();
void main() {
float t = dot(vec3(in_Position, 1.0), push.p);
t = const_ShaderVariant == SHADER_VARIANT_GRADIENT_CYCLE ?
abs(mod(t + 1.0, 2.0) - 1.0) : // Cycle
clamp(t, 0.0, 1.0); // Clamp
OUTPUT(convertAlpha(mix(push.c0, push.c1, t)));
}

View File

@@ -0,0 +1,21 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in ivec4 in_PositionOffsetAndScanline;
layout(location = 1) in uint in_Data;
layout(location = 0) out vec2 out_Position;
layout(location = 1) out uint out_Data;
// This starts with "Origin" and not "Position" intentionally.
// When drawing, vertices are ordered in a such way, that provoking vertex is always the top-left one.
// This gives us an easy way to calculate offset within the rectangle without additional inputs.
layout(location = 2) out flat ivec4 out_OriginOffsetAndScanline;
void main() {
out_Position = in_PositionOffsetAndScanline.xy;
out_Data = in_Data;
out_OriginOffsetAndScanline = in_PositionOffsetAndScanline;
gl_Position = transformToDeviceSpace(in_PositionOffsetAndScanline.xy);
}

View File

@@ -1,4 +1,6 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(set = 0, binding = 0, r8) uniform readonly restrict imageBuffer u_Mask;
@@ -10,9 +12,5 @@ layout(location = 1) in flat vec4 in_Color;
layout(location = 0) out vec4 out_Color;
void main() {
ivec2 maskPos = ivec2(gl_FragCoord.xy - vec2(in_OriginOffsetAndScanline.xy));
int offset = in_OriginOffsetAndScanline.z;
int scanline = in_OriginOffsetAndScanline.w;
int maskIndex = offset + scanline * maskPos.y + min(scanline, maskPos.x);
out_Color = in_Color * imageLoad(u_Mask, maskIndex).r;
out_Color = APPLY_MASK(in_Color);
}

View File

@@ -1,11 +1,9 @@
#version 450
layout(push_constant) uniform PushConstants {
mat2x3 transform;
} push;
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in ivec4 in_PositionOffsetAndScanline;
layout(location = 1) in vec4 in_Color;
layout(location = 1) in uint in_Color;
// This starts with "Origin" and not "Position" intentionally.
// When drawing, vertices are ordered in a such way, that provoking vertex is always the top-left one.
@@ -14,7 +12,7 @@ layout(location = 0) out flat ivec4 out_OriginOffsetAndScanline;
layout(location = 1) out flat vec4 out_Color;
void main() {
gl_Position = vec4(vec3(in_PositionOffsetAndScanline.xy, 1)*push.transform, 0.0, 1.0);
gl_Position = transformToDeviceSpace(in_PositionOffsetAndScanline.xy);
out_OriginOffsetAndScanline = in_PositionOffsetAndScanline;
out_Color = in_Color;
out_Color = convertAlpha(decodeColor(in_Color));
}

View File

@@ -0,0 +1,15 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#include "common.glsl"
layout(location = 0) in vec2 in_Position;
layout(location = 1) in uint in_Data;
layout(location = 0) out vec2 out_Position;
layout(location = 1) out uint out_Data;
layout(location = 2) out flat ivec4 _; // Unused output
void main() {
out_Position = in_Position;
out_Data = in_Data;
gl_Position = transformToDeviceSpace(in_Position);
}

View File

@@ -63,7 +63,7 @@
#define TRACE_USE_API 0
#define TRACE_REUSE 0
#define INIT_TEST 1
#define INIT_TEST 0
#define INIT_TEST_STEP 1
#define INIT_TEST_MAX 1024

View File

@@ -113,7 +113,7 @@ void VKBlitLoops_IsoBlit(VKSDOps* srcOps, jint filter,
VK_COMPONENT_SWIZZLE_ONE);
VKPackedSwizzle swizzle = srcOpaque ? OPAQUE_SWIZZLE : 0;
if (!VKRenderer_Validate(SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) return;
if (!VKRenderer_Validate(SHADER_BLIT, NO_SHADER_VARIANT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) return;
VKRenderer_DrawImage(srcOps->image, srcOps->image->format, swizzle, filter, SAMPLER_WRAP_BORDER,
(float)sx1, (float)sy1, (float)sx2, (float)sy2, (float)dx1, (float)dy1, (float)dx2, (float)dy2);
VKRenderer_AddSurfaceDependency(srcOps, context->surface);
@@ -154,7 +154,7 @@ void VKBlitLoops_Blit(JNIEnv *env, SurfaceDataOps* src, jshort srctype, jint fil
}
if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && srcInfo.bounds.y2 > srcInfo.bounds.y1) {
src->GetRasInfo(env, src, &srcInfo);
if (srcInfo.rasBase) {
while (srcInfo.rasBase) {
if (srcInfo.bounds.x1 != sx1) dx1 += (srcInfo.bounds.x1 - sx1) * (dx2 - dx1) / (sx2 - sx1);
if (srcInfo.bounds.y1 != sy1) dy1 += (srcInfo.bounds.y1 - sy1) * (dy2 - dy1) / (sy2 - sy1);
if (srcInfo.bounds.x2 != sx2) dx2 += (srcInfo.bounds.x2 - sx2) * (dx2 - dx1) / (sx2 - sx1);
@@ -165,12 +165,17 @@ void VKBlitLoops_Blit(JNIEnv *env, SurfaceDataOps* src, jshort srctype, jint fil
// Need to validate render pass early, as image may not yet be configured.
AlphaType alphaType = getSrcAlphaType(srctype);
if (!VKRenderer_Validate(SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) return;
if (!VKRenderer_Validate(SHADER_BLIT, NO_SHADER_VARIANT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) break;
VKDevice* device = context->surface->device;
BlitSrcType type = decodeSrcType(device, srctype);
VKTexturePoolHandle* imageHandle = VKTexturePool_GetTexture(device->texturePool, sw, sh, type.format);
VKTexturePoolHandle* imageHandle =
VKTexturePool_GetTexture(VKRenderer_GetTexturePool(device->renderer), sw, sh, type.format);
VKImage* image = VKTexturePoolHandle_GetTexture(imageHandle);
if (!image) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "VKBlitLoops_Blit: could not get texture from the pool");
break;
}
VkDeviceSize dataSize = sh * sw * srcInfo.pixelStride;
VKBuffer buffer;
@@ -231,7 +236,9 @@ void VKBlitLoops_Blit(JNIEnv *env, SurfaceDataOps* src, jshort srctype, jint fil
VKRenderer_ExecOnCleanup(context->surface, VKBlitLoops_DisposeTexture, imageHandle);
VKRenderer_ExecOnCleanup(context->surface, VKBlitLoops_DisposeBuffer, buffer.handle);
VKRenderer_ExecOnCleanup(context->surface, VKBlitLoops_DisposeMemory, page);
} else {
break;
}
if (!srcInfo.rasBase) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "VKBlitLoops_Blit: could not get raster info");
}
SurfaceData_InvokeRelease(env, src, &srcInfo);

View File

@@ -56,7 +56,7 @@ VKComposites VKComposites_Create() {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT },
{ .logicOpEnable = VK_TRUE,
.logicOp = VK_LOGIC_OP_XOR }, ALPHA_TYPE_PRE_MULTIPLIED });
.logicOp = VK_LOGIC_OP_XOR }, ALPHA_TYPE_STRAIGHT });
// NAME | SRC_COLOR | DST_COLOR | SRC_ALPHA | DST_ALPHA ||
ALPHA_BLEND( CLEAR , ZERO , ZERO , ZERO , ZERO );
@@ -138,7 +138,6 @@ void VKComposites_AddState(VKComposites* composites, VKCompositeMode mode, VKCom
state.blendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
state.blendState.pNext = NULL;
state.blendState.attachmentCount = 1;
state.outAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
MAP_AT(composites->map, (VKCompositeDescriptor) { mode, VK_FALSE }) = state;
// Using pre-multiplied alpha is necessary for correct blending,

View File

@@ -272,7 +272,6 @@ void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice) {
void VKDevice_Reset(VKDevice* device) {
if (device == NULL) return;
VKRenderer_Destroy(device->renderer);
VKTexturePool_Dispose(device->texturePool);
VKAllocator_Destroy(device->allocator);
ARRAY_FREE(device->enabledExtensions);
ARRAY_FREE(device->enabledLayers);
@@ -392,11 +391,4 @@ Java_sun_java2d_vulkan_VKGPU_init(JNIEnv *env, jclass jClass, jlong jDevice) {
JNU_ThrowByName(env, "java/lang/RuntimeException", "Vulkan: Cannot create renderer");
return;
}
device->texturePool = VKTexturePool_InitWithDevice(device);
if (!device->texturePool) {
VKDevice_Reset(device);
JNU_ThrowByName(env, "java/lang/RuntimeException", "Vulkan: Cannot create texture pool");
return;
}
}

View File

@@ -59,7 +59,6 @@ struct VKDevice {
VKAllocator* allocator;
VKRenderer* renderer;
VKTexturePool* texturePool;
DEVICE_FUNCTION_TABLE(DECL_PFN)
SWAPCHAIN_DEVICE_FUNCTION_TABLE(DECL_PFN)

View File

@@ -49,6 +49,7 @@ static size_t pipelineDescriptorHash(const void* ptr) {
hash(&h, d->inAlphaType);
hash(&h, d->composite);
hash(&h, d->shader);
hash(&h, d->shaderVariant);
hash(&h, d->topology);
return (size_t) h;
}
@@ -59,6 +60,7 @@ static bool pipelineDescriptorEquals(const void* ap, const void* bp) {
a->inAlphaType == b->inAlphaType &&
a->composite == b->composite &&
a->shader == b->shader &&
a->shaderVariant == b->shaderVariant &&
a->topology == b->topology;
}
@@ -144,12 +146,20 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
VkPipelineShaderStageCreateInfo createInfos[2]; // vert + frag
} ShaderStages;
ShaderStages stages[count];
typedef struct {
uint32_t inAlphaType, outAlphaType, shaderVariant, shaderModifier;
} SpecializationData;
const VkSpecializationMapEntry SPECIALIZATION_ENTRIES[] = {
{ 0, 0, 4 },
{ 1, 4, 4 },
{ 2, 8, 4 },
{ 3, 12, 4 }
};
typedef struct {
VkSpecializationInfo info;
VkSpecializationMapEntry entries[2];
uint64_t data[1];
SpecializationData data;
} Specialization;
Specialization specializations[count][2];
Specialization specializations[count];
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStates[count];
VkPipelineDepthStencilStateCreateInfo depthStencilStates[count];
VkPipelineDynamicStateCreateInfo dynamicStates[count];
@@ -163,14 +173,19 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
// - pStages (but stageCount is set to 2)
// - pVertexInputState
// - createInfo.layout
for (uint32_t j = 0; j < SARRAY_COUNT_OF(specializations[i]); j++) {
specializations[i][j].info = (VkSpecializationInfo) {
.mapEntryCount = 0,
.pMapEntries = specializations[i][j].entries,
.dataSize = 0,
.pData = specializations[i][j].data
};
}
specializations[i] = (Specialization) {
.info = {
.mapEntryCount = SARRAY_COUNT_OF(SPECIALIZATION_ENTRIES),
.pMapEntries = SPECIALIZATION_ENTRIES,
.dataSize = sizeof(SpecializationData),
.pData = &specializations[i].data
},
.data = {
descriptors[i].inAlphaType, pipelineInfos[i].outAlphaType, (uint32_t) descriptors[i].shaderVariant,
(descriptors[i].composite == LOGIC_COMPOSITE_XOR ? 1 : 0) |
(descriptors[i].shader & SHADER_MASK ? 2 : 0)
}
};
inputAssemblyStates[i] = (VkPipelineInputAssemblyStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = descriptors[i].topology
@@ -234,37 +249,38 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
}
// Setup input states.
MAKE_INPUT_STATE(COLOR, VKColorVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT);
MAKE_INPUT_STATE(MASK_FILL_COLOR, VKMaskFillColorVertex, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32G32B32A32_SFLOAT);
MAKE_INPUT_STATE(PRIMITIVE, VKVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32_UINT);
MAKE_INPUT_STATE(MASK_FILL, VKMaskFillVertex, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32_UINT);
MAKE_INPUT_STATE(BLIT, VKTxVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32_SFLOAT);
MAKE_INPUT_STATE(CLIP, VKIntVertex, VK_FORMAT_R32G32_SINT);
for (uint32_t i = 0; i < count; i++) {
// Setup shader-specific pipeline parameters.
switch (descriptors[i].shader) {
switch ((int) descriptors[i].shader) {
case SHADER_COLOR:
createInfos[i].pVertexInputState = &INPUT_STATE_COLOR;
createInfos[i].layout = pipelineContext->colorPipelineLayout;
createInfos[i].pVertexInputState = &INPUT_STATE_PRIMITIVE;
createInfos[i].layout = pipelineContext->commonPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->color_vert, shaders->color_frag }};
break;
case SHADER_MASK_FILL_COLOR:
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL_COLOR;
case SHADER_COLOR | SHADER_MASK:
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->mask_fill_color_vert, shaders->mask_fill_color_frag }};
break;
case SHADER_GRADIENT:
createInfos[i].pVertexInputState = &INPUT_STATE_PRIMITIVE;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->primitive_vert, shaders->gradient_frag }};
break;
case SHADER_GRADIENT | SHADER_MASK:
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->mask_fill_vert, shaders->gradient_frag }};
break;
case SHADER_BLIT:
createInfos[i].pVertexInputState = &INPUT_STATE_BLIT;
createInfos[i].layout = pipelineContext->texturePipelineLayout;
stages[i] = (ShaderStages) {{ shaders->blit_vert, shaders->blit_frag }};
// Alpha conversion specialization.
uint32_t* spec = (uint32_t*) specializations[i][1].data;
spec[0] = descriptors[i].inAlphaType;
spec[1] = pipelineInfos[i].outAlphaType;
specializations[i][1].info.dataSize = 8;
specializations[i][1].entries[0] = (VkSpecializationMapEntry) { 0, 0, 4 };
specializations[i][1].entries[1] = (VkSpecializationMapEntry) { 1, 4, 4 };
specializations[i][1].info.mapEntryCount = 2;
stages[i].createInfos[1].pSpecializationInfo = &specializations[i][1].info;
break;
case SHADER_CLIP:
createInfos[i].pVertexInputState = &INPUT_STATE_CLIP;
@@ -290,6 +306,9 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
default:
VK_FATAL_ERROR("Cannot create pipeline, unknown shader requested!");
}
for (uint32_t j = 0; j < createInfos[i].stageCount; j++) {
stages[i].createInfos[j].pSpecializationInfo = &specializations[i].info;
}
assert(createInfos[i].pDynamicState->dynamicStateCount <= MAX_DYNAMIC_STATES);
J2dRlsTraceLn(J2D_TRACE_INFO, "VKPipelines_CreatePipelines: stencilMode=%d, dstOpaque=%d, composite=%d, shader=%d, topology=%d",
descriptors[i].stencilMode, descriptors[i].dstOpaque, descriptors[i].composite, descriptors[i].shader, descriptors[i].topology);
@@ -303,6 +322,7 @@ static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPas
J2dRlsTraceLn(J2D_TRACE_INFO, "VKPipelines_CreatePipelines: created %d pipelines", count);
for (uint32_t i = 0; i < count; i++) {
pipelineInfos[i].pipeline = pipelines[i];
pipelineInfos[i].layout = createInfos[i].layout;
MAP_AT(renderPassContext->pipelines, descriptors[i]) = pipelineInfos[i];
}
return pipelineInfos[0];
@@ -403,21 +423,49 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
assert(device != NULL && pipelines != NULL);
VkResult result;
// We want all our pipelines to have same push constant range to ensure common state is compatible between pipelines.
VkPushConstantRange pushConstantRange = {
// We want all our pipelines to have the same push constant ranges to ensure a common state is compatible between pipelines.
VkPushConstantRange pushConstantRanges[] = {{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0,
.size = sizeof(VKTransform)
};
}, {
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.offset = PUSH_CONSTANTS_OFFSET,
.size = PUSH_CONSTANTS_SIZE
}};
// Common pipeline.
VkPipelineLayoutCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pushConstantRange
.pushConstantRangeCount = SARRAY_COUNT_OF(pushConstantRanges),
.pPushConstantRanges = pushConstantRanges
};
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->colorPipelineLayout);
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->commonPipelineLayout);
VK_IF_ERROR(result) return result;
// Mask fill pipeline.
VkDescriptorSetLayoutBinding maskBufferLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &maskBufferLayoutBinding
};
result = device->vkCreateDescriptorSetLayout(device->handle, &descriptorSetLayoutCreateInfo, NULL, &pipelines->maskFillDescriptorSetLayout);
VK_IF_ERROR(result) return result;
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &pipelines->maskFillDescriptorSetLayout;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->maskFillPipelineLayout);
VK_IF_ERROR(result) return result;
// Texture pipeline.
VkDescriptorSetLayoutBinding textureLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
@@ -437,31 +485,11 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
pipelines->textureDescriptorSetLayout,
pipelines->samplers.descriptorSetLayout
};
createInfo.setLayoutCount = 2;
createInfo.setLayoutCount = SARRAY_COUNT_OF(textureDescriptorSetLayouts);
createInfo.pSetLayouts = textureDescriptorSetLayouts;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->texturePipelineLayout);
VK_IF_ERROR(result) return result;
VkDescriptorSetLayoutBinding maskBufferLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &maskBufferLayoutBinding
};
result = device->vkCreateDescriptorSetLayout(device->handle, &descriptorSetLayoutCreateInfo, NULL, &pipelines->maskFillDescriptorSetLayout);
VK_IF_ERROR(result) return result;
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &pipelines->maskFillDescriptorSetLayout;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->maskFillPipelineLayout);
VK_IF_ERROR(result) return result;
return VK_SUCCESS;
}
@@ -504,7 +532,7 @@ void VKPipelines_DestroyContext(VKPipelineContext* pipelineContext) {
VKPipelines_DestroyShaders(device, pipelineContext->shaders);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->colorPipelineLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->commonPipelineLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->texturePipelineLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->textureDescriptorSetLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->maskFillPipelineLayout, NULL);

View File

@@ -36,13 +36,26 @@
* Shader programs.
*/
typedef enum {
// Base shaders.
SHADER_COLOR,
SHADER_MASK_FILL_COLOR,
SHADER_GRADIENT,
SHADER_BLIT,
SHADER_CLIP,
NO_SHADER = 0x7FFFFFFF
NO_SHADER = 0x7FFFFFFF,
// Mask modifier bit (MASK_FILL & MASK_BLIT).
SHADER_MASK = ~NO_SHADER
} VKShader;
/**
* Shader variant.
* It is used to specialize shader behavior, and its meaning varies with each particular shader.
*/
typedef enum {
SHADER_VARIANT_GRADIENT_CLAMP = 0,
SHADER_VARIANT_GRADIENT_CYCLE = 1,
NO_SHADER_VARIANT = 0x7FFFFFFF
} VKShaderVariant;
typedef enum {
STENCIL_MODE_NONE = 0, // No stencil attachment.
STENCIL_MODE_OFF = 1, // Has stencil attachment, stencil test disabled.
@@ -59,12 +72,14 @@ typedef struct {
AlphaType inAlphaType : 1;
VKCompositeMode composite;
VKShader shader;
VKShaderVariant shaderVariant;
VkPrimitiveTopology topology;
} VKPipelineDescriptor;
typedef struct {
VkPipeline pipeline;
AlphaType outAlphaType;
VkPipeline pipeline;
VkPipelineLayout layout;
AlphaType outAlphaType;
} VKPipelineInfo;
/**
@@ -72,7 +87,7 @@ typedef struct {
*/
struct VKPipelineContext {
VKDevice* device;
VkPipelineLayout colorPipelineLayout;
VkPipelineLayout commonPipelineLayout;
VkDescriptorSetLayout textureDescriptorSetLayout;
VkPipelineLayout texturePipelineLayout;
VkDescriptorSetLayout maskFillDescriptorSetLayout;
@@ -93,14 +108,42 @@ struct VKRenderPassContext {
MAP(VKPipelineDescriptor, VKPipelineInfo) pipelines;
};
typedef struct {
unsigned int xorColor;
float extraAlpha;
} VKCompositeConstants;
typedef struct {
RGBA c0, c1;
float p0, p1, p3;
} VKGradientPaintConstants;
typedef union {
// The minimum guaranteed size of push constants is 128 bytes.
alignas(32) // The maximum alignment for built-in glsl types is 32 bytes (dvec4).
char data[(128 - sizeof(VKTransform) - sizeof(VKCompositeConstants)) / 32 * 32];
VKGradientPaintConstants gradientPaint;
} VKShaderConstants;
#define MAX_SHADER_CONSTANTS_SIZE 96 // We expect 96 bytes
typedef char VKShaderConstantsCheckOffset[sizeof(VKShaderConstants) == MAX_SHADER_CONSTANTS_SIZE ? 1 : -1]; // Verify.
typedef struct {
VKTransform transform;
VKCompositeConstants composite;
VKShaderConstants shader;
} VKPushConstants;
typedef char VKPushConstantsCheckSize[sizeof(VKPushConstants) <= 128 ? 1 : -1]; // We should not exceed 128 bytes.
static const uint32_t PUSH_CONSTANTS_OFFSET = (uintptr_t) &((VKPushConstants*) NULL)->composite;
static const uint32_t PUSH_CONSTANTS_SIZE = sizeof(VKPushConstants) - PUSH_CONSTANTS_OFFSET;
typedef struct {
int x, y;
} VKIntVertex;
typedef struct {
float x, y;
RGBA color;
} VKColorVertex;
unsigned int data;
} VKVertex;
typedef struct {
float px, py;
@@ -109,8 +152,8 @@ typedef struct {
typedef struct {
int x, y, maskOffset, maskScanline;
RGBA color;
} VKMaskFillColorVertex;
unsigned int data;
} VKMaskFillVertex;
VKPipelineContext* VKPipelines_CreateContext(VKDevice* device);
void VKPipelines_DestroyContext(VKPipelineContext* pipelines);

View File

@@ -93,6 +93,21 @@
#define OFFSET_XFORM sun_java2d_vulkan_VKBlitLoops_OFFSET_XFORM
#define OFFSET_ISOBLIT sun_java2d_vulkan_VKBlitLoops_OFFSET_ISOBLIT
static void applyXor() {
if (VKRenderer_GetContext()->shader == SHADER_COLOR) {
VKRenderer_GetContext()->vertexData ^= VKRenderer_GetContext()->constants.composite.xorColor;
}
}
static void setComposite(VKCompositeMode comp, unsigned int xorColor, float extraAlpha) {
VKRenderer_GetContext()->composite = comp;
if (VKRenderer_GetContext()->constants.composite.xorColor != xorColor ||
VKRenderer_GetContext()->constants.composite.extraAlpha != extraAlpha) {
VKRenderer_GetContext()->constants.composite = (VKCompositeConstants) { xorColor, extraAlpha };
VKRenderer_GetContext()->constantsModCount++;
}
}
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
(JNIEnv *env, jobject oglrq, jlong buf, jint limit)
{
@@ -476,30 +491,26 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint flags = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_ALPHA_COMPOSITE(%d, %f, %d)", rule, extraAlpha, flags);
VKRenderer_GetContext()->renderColor = VKRenderer_GetContext()->color;
VKRenderer_GetContext()->composite = (VKCompositeMode) rule;
VKRenderer_GetContext()->extraAlpha = extraAlpha;
applyXor();
setComposite((VKCompositeMode) rule, 0, extraAlpha);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_XOR_COMPOSITE:
{
jint xorPixel = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_XOR_COMPOSITE");
VKRenderer_GetContext()->renderColor = VKUtil_DecodeJavaColor(xorPixel, ALPHA_TYPE_STRAIGHT);
// TODO Fix XOR mode!
// VKRenderer_GetContext()->renderColor.a = 0.0f; // Alpha is left unchanged in XOR mode.
VKRenderer_GetContext()->composite = LOGIC_COMPOSITE_XOR;
VKRenderer_GetContext()->extraAlpha = 1.0f;
"VKRenderQueue_flushBuffer: SET_XOR_COMPOSITE(0x%08x)", xorPixel);
applyXor();
setComposite(LOGIC_COMPOSITE_XOR, xorPixel, -1.0f);
applyXor();
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_COMPOSITE:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_COMPOSITE");
VKRenderer_GetContext()->renderColor = VKRenderer_GetContext()->color;
VKRenderer_GetContext()->composite = ALPHA_COMPOSITE_SRC;
VKRenderer_GetContext()->extraAlpha = 1.0f;
applyXor();
setComposite(ALPHA_COMPOSITE_SRC, 0, 1.0f);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_TRANSFORM:
@@ -522,8 +533,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
};
VKRenderingContext* context = VKRenderer_GetContext();
if (VK_IS_NEQ_TRANSFORM(&context->transform, &transform)) {
context->transform = transform;
if (VK_IS_NEQ_TRANSFORM(&context->constants.transform, &transform)) {
context->constants.transform = transform;
context->transformModCount++;
}
}
@@ -533,8 +544,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_TRANSFORM");
VKRenderingContext* context = VKRenderer_GetContext();
if (VK_IS_NEQ_TRANSFORM(&context->transform, &VK_ID_TRANSFORM)) {
context->transform = VK_ID_TRANSFORM;
if (VK_IS_NEQ_TRANSFORM(&context->constants.transform, &VK_ID_TRANSFORM)) {
context->constants.transform = VK_ID_TRANSFORM;
context->transformModCount++;
}
}
@@ -568,9 +579,10 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
break;
case sun_java2d_pipe_BufferedOpCodes_DISPOSE_SURFACE:
{
jlong pData = NEXT_LONG(b);
VKSDOps* surface = NEXT_VK_SURFACE(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: DISPOSE_SURFACE");
VKSD_ResetSurface(surface);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISPOSE_CONFIG:
@@ -643,31 +655,34 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
break;
case sun_java2d_pipe_BufferedOpCodes_SET_COLOR:
{
jint javaColor = NEXT_INT(b);
VKRenderer_GetContext()->color = VKUtil_DecodeJavaColor(javaColor, ALPHA_TYPE_STRAIGHT);
if (COMPOSITE_GROUP(VKRenderer_GetContext()->composite) == ALPHA_COMPOSITE_GROUP) {
VKRenderer_GetContext()->renderColor = VKRenderer_GetContext()->color;
}
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: SET_COLOR(0x%08x)", javaColor);
J2dTraceLn(J2D_TRACE_VERBOSE, // Print color values with straight alpha for convenience.
" srgb={%.3f, %.3f, %.3f, %.3f}",
VKUtil_GetRGBA(VKRenderer_GetContext()->color, ALPHA_TYPE_STRAIGHT).r,
VKUtil_GetRGBA(VKRenderer_GetContext()->color, ALPHA_TYPE_STRAIGHT).g,
VKUtil_GetRGBA(VKRenderer_GetContext()->color, ALPHA_TYPE_STRAIGHT).b,
VKUtil_GetRGBA(VKRenderer_GetContext()->color, ALPHA_TYPE_STRAIGHT).a);
VKRenderer_GetContext()->inAlphaType = ALPHA_TYPE_STRAIGHT;
VKRenderer_GetContext()->shader = SHADER_COLOR;
VKRenderer_GetContext()->shaderVariant = NO_SHADER_VARIANT;
VKRenderer_GetContext()->vertexData = NEXT_INT(b);
applyXor();
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: SET_COLOR(0x%08x)", VKRenderer_GetContext()->vertexData);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_GRADIENT_PAINT:
{
jboolean useMask= NEXT_BOOLEAN(b);
jboolean cyclic = NEXT_BOOLEAN(b);
jdouble p0 = NEXT_DOUBLE(b);
jdouble p1 = NEXT_DOUBLE(b);
jdouble p3 = NEXT_DOUBLE(b);
jint pixel1 = NEXT_INT(b);
jint pixel2 = NEXT_INT(b);
jboolean useMask = NEXT_BOOLEAN(b); // Unused.
jboolean cyclic = NEXT_BOOLEAN(b);
jdouble p0 = NEXT_DOUBLE(b);
jdouble p1 = NEXT_DOUBLE(b);
jdouble p3 = NEXT_DOUBLE(b);
jint pixel1 = NEXT_INT(b);
jint pixel2 = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_GRADIENT_PAINT");
VKRenderer_GetContext()->inAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
VKRenderer_GetContext()->shader = SHADER_GRADIENT;
VKRenderer_GetContext()->shaderVariant = cyclic ? SHADER_VARIANT_GRADIENT_CYCLE : SHADER_VARIANT_GRADIENT_CLAMP;
VKRenderer_GetContext()->constants.shader.gradientPaint = (VKGradientPaintConstants) {
VKUtil_GetRGBA(VKUtil_DecodeJavaColor(pixel1, ALPHA_TYPE_PRE_MULTIPLIED), ALPHA_TYPE_PRE_MULTIPLIED),
VKUtil_GetRGBA(VKUtil_DecodeJavaColor(pixel2, ALPHA_TYPE_PRE_MULTIPLIED), ALPHA_TYPE_PRE_MULTIPLIED),
p0*2.0, p1*2.0, p3*2.0-0.5
};
VKRenderer_GetContext()->constantsModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_LINEAR_GRADIENT_PAINT:

View File

@@ -94,6 +94,7 @@ typedef struct {
struct VKRenderer {
VKDevice* device;
VKPipelineContext* pipelineContext;
VKTexturePool* texturePool;
POOL(VkCommandBuffer, commandBufferPool);
POOL(VkCommandBuffer, secondaryCommandBufferPool);
@@ -163,6 +164,7 @@ struct VKRenderPass {
BufferWritingState maskFillBufferWriting;
VKPipelineDescriptor state;
uint64_t constantsModCount; // Just a tag to detect when constants were changed.
uint64_t transformModCount; // Just a tag to detect when transform was changed.
uint64_t clipModCount; // Just a tag to detect when clip was changed.
VkBool32 pendingFlush : 1;
@@ -175,12 +177,17 @@ struct VKRenderPass {
// which is only called from queue flusher thread, no need for synchronization.
static VKRenderingContext context = {
.surface = NULL,
.transform = VK_ID_TRANSFORM,
.transformModCount = 1,
.color = {},
.renderColor = {},
.composite = ALPHA_COMPOSITE_SRC_OVER,
.extraAlpha = 1.0f,
.inAlphaType = ALPHA_TYPE_UNKNOWN,
.shader = NO_SHADER,
.shaderVariant = NO_SHADER_VARIANT,
.vertexData = 0,
.constantsModCount = 1,
.transformModCount = 1,
.constants = {
.transform = VK_ID_TRANSFORM,
.composite = { 0, 1.0f }
},
.clipModCount = 1,
.clipRect = NO_CLIP,
.clipSpanVertices = NULL
@@ -193,6 +200,9 @@ VKRenderingContext *VKRenderer_GetContext() {
return &context;
}
VKTexturePool *VKRenderer_GetTexturePool(VKRenderer* renderer) {
return renderer->texturePool;
}
/**
* Helper function for POOL_TAKE macro.
*/
@@ -217,7 +227,7 @@ static VkBool32 VKRenderer_CheckPoolDrain(void* pool, void* entry) {
return VK_FALSE;
}
#define VERTEX_BUFFER_SIZE (128 * 1024) // 128KiB - enough to draw 910 quads (6 verts) with VKColorVertex.
#define VERTEX_BUFFER_SIZE (128 * 1024) // 128KiB - enough to draw 1820 quads (6 verts) with VKVertex.
#define VERTEX_BUFFER_PAGE_SIZE (1 * 1024 * 1024) // 1MiB - fits 8 buffers.
static void VKRenderer_FindVertexBufferMemoryType(VKMemoryRequirements* requirements) {
VKAllocator_FindMemoryType(requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
@@ -368,6 +378,12 @@ VKRenderer* VKRenderer_Create(VKDevice* device) {
return NULL;
}
renderer->texturePool = VKTexturePool_InitWithDevice(device);
if (!renderer->texturePool) {
VKRenderer_Destroy(renderer);
return NULL;
}
// Create command pool
// TODO we currently have single command pool with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
// we may need to consider having multiple pools to avoid resetting buffers one-by-one
@@ -438,6 +454,9 @@ void VKRenderer_Destroy(VKRenderer* renderer) {
device->vkDestroyDescriptorPool(device->handle, renderer->descriptorPools[i], NULL);
}
ARRAY_FREE(renderer->descriptorPools);
VKTexturePool_Dispose(renderer->texturePool);
for (uint32_t i = 0; i < ARRAY_SIZE(renderer->imageDescriptorPools); i++) {
device->vkDestroyDescriptorPool(device->handle, renderer->imageDescriptorPools[i], NULL);
}
@@ -584,33 +603,47 @@ inline void VKRenderer_FlushDraw(VKSDOps* surface) {
static void VKRenderer_ResetDrawing(VKSDOps* surface) {
assert(surface != NULL && surface->renderPass != NULL);
VKRenderer* renderer = surface->device->renderer;
surface->renderPass->state.composite = NO_COMPOSITE;
surface->renderPass->state.shader = NO_SHADER;
surface->renderPass->transformModCount = 0;
surface->renderPass->firstVertex = 0;
surface->renderPass->vertexCount = 0;
surface->renderPass->vertexBufferWriting = (BufferWritingState) {NULL, 0, VK_FALSE};
surface->renderPass->maskFillBufferWriting = (BufferWritingState) {NULL, 0, VK_FALSE};
if (ARRAY_SIZE(surface->renderPass->flushRanges) > 0) {
VKRenderPass* renderPass = surface->renderPass;
renderPass->state.composite = NO_COMPOSITE;
renderPass->state.shader = NO_SHADER;
renderPass->constantsModCount = 0;
renderPass->transformModCount = 0;
renderPass->firstVertex = 0;
renderPass->vertexCount = 0;
renderPass->vertexBufferWriting = (BufferWritingState) { NULL, 0, VK_FALSE };
renderPass->maskFillBufferWriting = (BufferWritingState) { NULL, 0, VK_FALSE };
if (ARRAY_SIZE(renderPass->flushRanges) > 0) {
VK_IF_ERROR(surface->device->vkFlushMappedMemoryRanges(surface->device->handle,
ARRAY_SIZE(surface->renderPass->flushRanges), surface->renderPass->flushRanges)) {}
ARRAY_RESIZE(surface->renderPass->flushRanges, 0);
ARRAY_SIZE(renderPass->flushRanges), renderPass->flushRanges)) {}
ARRAY_RESIZE(renderPass->flushRanges, 0);
}
size_t vertexBufferCount = ARRAY_SIZE(surface->renderPass->vertexBuffers);
size_t maskFillBufferCount = ARRAY_SIZE(surface->renderPass->maskFillBuffers);
size_t cleanupQueueCount = ARRAY_SIZE(surface->renderPass->cleanupQueue);
size_t vertexBufferCount = ARRAY_SIZE(renderPass->vertexBuffers);
size_t maskFillBufferCount = ARRAY_SIZE(renderPass->maskFillBuffers);
size_t cleanupQueueCount = ARRAY_SIZE(renderPass->cleanupQueue);
for (uint32_t i = 0; i < vertexBufferCount; i++) {
POOL_RETURN(surface->device->renderer, vertexBufferPool, surface->renderPass->vertexBuffers[i]);
POOL_RETURN(renderer, vertexBufferPool, renderPass->vertexBuffers[i]);
}
for (uint32_t i = 0; i < maskFillBufferCount; i++) {
POOL_RETURN(surface->device->renderer, maskFillBufferPool, surface->renderPass->maskFillBuffers[i]);
POOL_RETURN(renderer, maskFillBufferPool, renderPass->maskFillBuffers[i]);
}
for (uint32_t i = 0; i < cleanupQueueCount; i++) {
POOL_RETURN(surface->device->renderer, cleanupQueue, surface->renderPass->cleanupQueue[i]);
POOL_RETURN(renderer, cleanupQueue, renderPass->cleanupQueue[i]);
}
ARRAY_RESIZE(surface->renderPass->vertexBuffers, 0);
ARRAY_RESIZE(surface->renderPass->maskFillBuffers, 0);
ARRAY_RESIZE(surface->renderPass->cleanupQueue, 0);
ARRAY_RESIZE(renderPass->vertexBuffers, 0);
ARRAY_RESIZE(renderPass->maskFillBuffers, 0);
ARRAY_RESIZE(renderPass->cleanupQueue, 0);
// Update dependencies on used surfaces.
for (uint32_t i = 0, surfaces = (uint32_t) ARRAY_SIZE(renderPass->usedSurfaces); i < surfaces; i++) {
VKSDOps* usedSurface = renderPass->usedSurfaces[i];
uint32_t newSize = 0, oldSize = (uint32_t) ARRAY_SIZE(usedSurface->dependentSurfaces);
for (uint32_t j = 0; j < oldSize; j++) {
VKSDOps* s = usedSurface->dependentSurfaces[j];
if (s != surface) usedSurface->dependentSurfaces[newSize++] = s;
}
if (newSize != oldSize) ARRAY_RESIZE(usedSurface->dependentSurfaces, newSize);
}
ARRAY_RESIZE(renderPass->usedSurfaces, 0);
}
/**
@@ -698,6 +731,7 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
.composite = NO_COMPOSITE,
.shader = NO_SHADER
},
.constantsModCount = 0,
.transformModCount = 0,
.clipModCount = 0,
.pendingFlush = VK_FALSE,
@@ -864,19 +898,11 @@ VkBool32 VKRenderer_FlushRenderPass(VKSDOps* surface) {
VKRenderer* renderer = device->renderer;
VkCommandBuffer cb = VKRenderer_Record(renderer);
// Update dependencies on used surfaces.
// Update timestamps on used surfaces.
surface->lastTimestamp = renderer->writeTimestamp;
for (uint32_t i = 0, surfaces = (uint32_t) ARRAY_SIZE(surface->renderPass->usedSurfaces); i < surfaces; i++) {
VKSDOps* usedSurface = surface->renderPass->usedSurfaces[i];
usedSurface->lastTimestamp = renderer->writeTimestamp;
uint32_t newSize = 0, oldSize = (uint32_t) ARRAY_SIZE(usedSurface->dependentSurfaces);
for (uint32_t j = 0; j < oldSize; j++) {
VKSDOps* s = usedSurface->dependentSurfaces[j];
if (s != surface) usedSurface->dependentSurfaces[newSize++] = s;
}
if (newSize != oldSize) ARRAY_RESIZE(usedSurface->dependentSurfaces, newSize);
surface->renderPass->usedSurfaces[i]->lastTimestamp = renderer->writeTimestamp;
}
ARRAY_RESIZE(surface->renderPass->usedSurfaces, 0);
// Insert barriers to prepare surface for rendering.
VkImageMemoryBarrier barriers[2];
@@ -950,9 +976,12 @@ void VKRenderer_FlushSurface(VKSDOps* surface) {
uint32_t imageIndex;
VkResult acquireImageResult = device->vkAcquireNextImageKHR(device->handle, win->swapchain, UINT64_MAX,
acquireSemaphore, VK_NULL_HANDLE, &imageIndex);
if (acquireImageResult != VK_SUCCESS) {
// TODO possible suboptimal conditions
VK_IF_ERROR(acquireImageResult) {}
if (acquireImageResult != VK_SUCCESS && acquireImageResult != VK_SUBOPTIMAL_KHR) {
VK_IF_ERROR(acquireImageResult) {
// Failed, try again later.
surface->renderPass->pendingFlush = VK_TRUE;
return;
}
}
// Insert barriers to prepare both main (src) and swapchain (dst) images for blit.
@@ -1109,7 +1138,7 @@ static uint32_t VKRenderer_AllocateVertices(uint32_t primitives, uint32_t vertic
* This function can invalidate drawing state, always call it before VK_DRAW.
*/
static BufferWritingState VKRenderer_AllocateMaskFillBytes(uint32_t size) {
assert(size > 0);
// assert(size > 0); // size can be 0 when binding the buffer without allocating any data.
assert(size <= MASK_FILL_BUFFER_SIZE);
VKSDOps* surface = VKRenderer_GetContext()->surface;
BufferWritingState state = VKRenderer_AllocateBufferData(
@@ -1145,16 +1174,35 @@ static void VKRenderer_ValidateTransform() {
0.0f, 2.0f / (float) surface->image->extent.height, -1.0f
};
// Combine it with user transform.
VKUtil_ConcatenateTransform(&transform, &context->transform);
VKUtil_ConcatenateTransform(&transform, &context->constants.transform);
// Push the transform into shader.
surface->device->vkCmdPushConstants(
renderPass->commandBuffer,
surface->device->renderer->pipelineContext->colorPipelineLayout, // TODO what if our pipeline layout differs?
surface->device->renderer->pipelineContext->commonPipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(VKTransform), &transform
);
}
}
static void VKRenderer_ValidateConstants() {
VKRenderingContext* context = VKRenderer_GetContext();
assert(context->surface != NULL);
VKSDOps* surface = context->surface;
VKRenderPass* renderPass = surface->renderPass;
// Update constants, ignoring clip and color shaders.
if (renderPass->constantsModCount != context->constantsModCount &&
renderPass->state.shader != SHADER_CLIP && renderPass->state.shader != SHADER_COLOR) {
J2dTraceLn(J2D_TRACE_VERBOSE, "VKRenderer_ValidateConstants: updating constants");
VKRenderer_FlushDraw(surface);
renderPass->constantsModCount = context->constantsModCount;
surface->device->vkCmdPushConstants(
renderPass->commandBuffer,
surface->device->renderer->pipelineContext->commonPipelineLayout,
VK_SHADER_STAGE_FRAGMENT_BIT, PUSH_CONSTANTS_OFFSET, PUSH_CONSTANTS_SIZE, &context->constants.composite
);
}
}
/**
* Setup stencil attachment according to the context clip state.
* If there is a clip shape, attachment is cleared with "fail" value and then
@@ -1191,6 +1239,7 @@ static void VKRenderer_SetupStencil() {
.inAlphaType = ALPHA_TYPE_UNKNOWN,
.composite = NO_COMPOSITE,
.shader = SHADER_CLIP,
.shaderVariant = NO_SHADER_VARIANT,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
}).pipeline);
// Reset vertex buffer binding.
@@ -1235,7 +1284,7 @@ void VKRenderer_RecordBarriers(VKRenderer* renderer,
/**
* Setup pipeline for drawing. Returns FALSE if surface is not yet ready for drawing.
*/
VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, AlphaType inAlphaType) {
VkBool32 VKRenderer_Validate(VKShader shader, VKShaderVariant shaderVariant, VkPrimitiveTopology topology, AlphaType inAlphaType) {
assert(context.surface != NULL);
VKSDOps* surface = context.surface;
@@ -1277,10 +1326,18 @@ VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, Alph
J2dTraceLn(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating clip");
surface->device->vkCmdSetScissor(renderPass->commandBuffer, 0, 1, &context.clipRect);
if (clipChanged) {
VKStencilMode stencilMode = STENCIL_MODE_NONE;
if (ARRAY_SIZE(context.clipSpanVertices) > 0) {
VKRenderer_SetupStencil();
renderPass->state.stencilMode = STENCIL_MODE_ON;
} else renderPass->state.stencilMode = surface->stencil != NULL ? STENCIL_MODE_OFF : STENCIL_MODE_NONE;
stencilMode = STENCIL_MODE_ON;
} else if (surface->stencil != NULL) {
stencilMode = STENCIL_MODE_OFF;
}
// Reset the pipeline when changing stencil mode.
if (renderPass->state.stencilMode != stencilMode) {
renderPass->state.shader = NO_SHADER;
}
renderPass->state.stencilMode = stencilMode;
}
}
// Validate current composite.
@@ -1296,6 +1353,7 @@ VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, Alph
// Validate current pipeline.
if (renderPass->state.shader != shader ||
renderPass->state.shaderVariant != shaderVariant ||
renderPass->state.topology != topology ||
renderPass->state.inAlphaType != inAlphaType) {
J2dTraceLn(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating pipeline, old=%d, new=%d",
@@ -1303,6 +1361,7 @@ VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, Alph
VKRenderer_FlushDraw(surface);
VkCommandBuffer cb = renderPass->commandBuffer;
renderPass->state.shader = shader;
renderPass->state.shaderVariant = shaderVariant;
renderPass->state.topology = topology;
renderPass->state.inAlphaType = inAlphaType;
VKPipelineInfo pipelineInfo = VKPipelines_GetPipelineInfo(renderPass->context, renderPass->state);
@@ -1310,10 +1369,26 @@ VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, Alph
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineInfo.pipeline);
renderPass->vertexBufferWriting.bound = VK_FALSE;
renderPass->maskFillBufferWriting.bound = VK_FALSE;
// If pipeline uses mask fill layout, but the shader is not actually a MASK one, that must be a generic-layout pipeline.
// In that case, we need to bind a mask buffer, even if we won't ever use it.
// We could use VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT or nullDescriptor, but those require
// optional features or extensions, so don't bother for now...
// TODO this is ugly, do something with it.
if (pipelineInfo.layout == surface->device->renderer->pipelineContext->maskFillPipelineLayout && !(shader & SHADER_MASK)) {
VKRenderer_AllocateMaskFillBytes(0);
}
}
VKRenderer_ValidateConstants();
return VK_TRUE;
}
static VkBool32 VKRenderer_ValidatePaint(VKShader shaderModifier, VkBool32 fill) {
return VKRenderer_Validate(context.shader | shaderModifier, context.shaderVariant,
fill ? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST : VK_PRIMITIVE_TOPOLOGY_LINE_LIST, context.inAlphaType);
}
// Drawing operations.
void VKRenderer_RenderRect(VkBool32 fill, jint x, jint y, jint w, jint h) {
@@ -1324,12 +1399,9 @@ void VKRenderer_RenderRect(VkBool32 fill, jint x, jint y, jint w, jint h) {
void VKRenderer_RenderParallelogram(VkBool32 fill,
jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12)
{
if (!VKRenderer_Validate(SHADER_COLOR,
fill ? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
: VK_PRIMITIVE_TOPOLOGY_LINE_LIST, ALPHA_TYPE_UNKNOWN)) return; // Not ready.
RGBA c = VKRenderer_GetRGBA(context.surface, context.renderColor);
jfloat dx12, jfloat dy12) {
if (!VKRenderer_ValidatePaint(0, fill)) return; // Not ready.
/* dx21
* (p1)---------(p2) | (p1)------
* |\ \ | | \ dy21
@@ -1340,12 +1412,12 @@ void VKRenderer_RenderParallelogram(VkBool32 fill,
* dy21 \ |
* -----(p3)
*/
VKColorVertex p1 = {x11, y11, c};
VKColorVertex p2 = {x11 + dx21, y11 + dy21, c};
VKColorVertex p3 = {x11 + dx21 + dx12, y11 + dy21 + dy12, c};
VKColorVertex p4 = {x11 + dx12, y11 + dy12, c};
VKVertex p1 = {x11, y11, context.vertexData};
VKVertex p2 = {x11 + dx21, y11 + dy21, context.vertexData};
VKVertex p3 = {x11 + dx21 + dx12, y11 + dy21 + dy12, context.vertexData};
VKVertex p4 = {x11 + dx12, y11 + dy12, context.vertexData};
VKColorVertex* vs;
VKVertex* vs;
VK_DRAW(vs, 1, fill ? 6 : 8);
uint32_t i = 0;
vs[i++] = p1;
@@ -1362,19 +1434,18 @@ void VKRenderer_RenderParallelogram(VkBool32 fill,
void VKRenderer_FillSpans(jint spanCount, jint *spans) {
if (spanCount == 0) return;
if (!VKRenderer_Validate(SHADER_COLOR, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, ALPHA_TYPE_UNKNOWN)) return; // Not ready.
RGBA c = VKRenderer_GetRGBA(context.surface, context.renderColor);
if (!VKRenderer_ValidatePaint(0, VK_TRUE)) return; // Not ready.
jfloat x1 = (float)*(spans++);
jfloat y1 = (float)*(spans++);
jfloat x2 = (float)*(spans++);
jfloat y2 = (float)*(spans++);
VKColorVertex p1 = {x1, y1, c};
VKColorVertex p2 = {x2, y1, c};
VKColorVertex p3 = {x2, y2, c};
VKColorVertex p4 = {x1, y2, c};
VKVertex p1 = {x1, y1, context.vertexData};
VKVertex p2 = {x2, y1, context.vertexData};
VKVertex p3 = {x2, y2, context.vertexData};
VKVertex p4 = {x1, y2, context.vertexData};
VKColorVertex* vs;
VKVertex* vs;
VK_DRAW(vs, 1, 6);
vs[0] = p1; vs[1] = p2; vs[2] = p3; vs[3] = p3; vs[4] = p4; vs[5] = p1;
@@ -1391,8 +1462,8 @@ void VKRenderer_FillSpans(jint spanCount, jint *spans) {
void VKRenderer_MaskFill(jint x, jint y, jint w, jint h,
jint maskoff, jint maskscan, jint masklen, uint8_t *mask) {
if (!VKRenderer_Validate(SHADER_MASK_FILL_COLOR,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, ALPHA_TYPE_UNKNOWN)) return; // Not ready.
if (!VKRenderer_ValidatePaint(SHADER_MASK, VK_TRUE)) return; // Not ready.
// maskoff is the offset from the beginning of mask,
// it's the same as x and y offset within a tile (maskoff % maskscan, maskoff / maskscan).
// maskscan is the number of bytes in a row/
@@ -1410,14 +1481,13 @@ void VKRenderer_MaskFill(jint x, jint y, jint w, jint h,
*((char *)maskState.data) = (char)0xFF;
}
VKMaskFillColorVertex* vs;
VKMaskFillVertex* vs;
VK_DRAW(vs, 1, 6);
RGBA c = VKRenderer_GetRGBA(context.surface, context.renderColor);
int offset = (int) maskState.offset;
VKMaskFillColorVertex p1 = {x, y, offset, maskscan, c};
VKMaskFillColorVertex p2 = {x + w, y, offset, maskscan, c};
VKMaskFillColorVertex p3 = {x + w, y + h, offset, maskscan, c};
VKMaskFillColorVertex p4 = {x, y + h, offset, maskscan, c};
VKMaskFillVertex p1 = {x, y, offset, maskscan, context.vertexData};
VKMaskFillVertex p2 = {x + w, y, offset, maskscan, context.vertexData};
VKMaskFillVertex p3 = {x + w, y + h, offset, maskscan, context.vertexData};
VKMaskFillVertex p4 = {x, y + h, offset, maskscan, context.vertexData};
// Always keep p1 as provoking vertex for correct origin calculation in vertex shader.
vs[0] = p1; vs[1] = p3; vs[2] = p2;
vs[3] = p1; vs[4] = p3; vs[5] = p4;

View File

@@ -29,27 +29,22 @@
#include "VKTypes.h"
#include "VKPipelines.h"
#include "VKTexturePool.h"
#define NO_CLIP ((VkRect2D) {{0, 0}, {0x7FFFFFFFU, 0x7FFFFFFFU}})
struct VKRenderingContext {
VKSDOps* surface;
VKTransform transform;
VKCompositeMode composite;
AlphaType inAlphaType;
VKShader shader;
VKShaderVariant shaderVariant;
unsigned int vertexData;
VKPushConstants constants;
uint64_t constantsModCount;
uint64_t transformModCount;
// We keep this color separately from renderColor,
// because we need consistent state when switching between XOR and alpha
// composite modes. This variable holds last value set by SET_COLOR, while
// renderColor holds color, currently used for drawing, which may have
// also been provided by SET_XOR_COMPOSITE.
Color color;
Color renderColor;
VKCompositeMode composite;
// Extra alpha is not used when painting with plain color,
// in this case color.a already includes it.
float extraAlpha;
uint64_t clipModCount; // Used to track changes to the clip.
VkRect2D clipRect;
ARRAY(VKIntVertex) clipSpanVertices;
@@ -62,7 +57,7 @@ VKRenderer* VKRenderer_Create(VKDevice* device);
/**
* Setup pipeline for drawing. Returns FALSE if the surface is not yet ready for drawing.
*/
VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, AlphaType inAlphaType);
VkBool32 VKRenderer_Validate(VKShader shader, VKShaderVariant shaderVariant, VkPrimitiveTopology topology, AlphaType inAlphaType);
/**
* Record commands into the primary command buffer (outside of a render pass).
@@ -147,5 +142,6 @@ void VKRenderer_DrawImage(VKImage* image, VkFormat format,
float dx1, float dy1, float dx2, float dy2);
VKRenderingContext* VKRenderer_GetContext();
VKTexturePool* VKRenderer_GetTexturePool(VKRenderer* );
#endif //VKRenderer_h_Included

View File

@@ -292,7 +292,7 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
}
static void VKSD_OnDispose(JNIEnv* env, SurfaceDataOps* ops) {
VKSD_ResetSurface((VKSDOps*) ops);
JNU_CallStaticMethodByName(env, NULL, "sun/java2d/vulkan/VKSurfaceData", "dispose", "(J)V", ptr_to_jlong(ops));
}
JNIEXPORT VKSDOps* VKSD_CreateSurface(JNIEnv* env, jobject vksd, jint type, jint format, jint backgroundRGB,

View File

@@ -62,7 +62,7 @@ typedef uint16_t VKPackedSwizzle;
*/
typedef struct {
float m00, m01, m02;
float m10 __attribute__((aligned(16))), m11, m12;
float m10, m11, m12;
} VKTransform;
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VKMemory);

View File

@@ -40,35 +40,44 @@ import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
public class DefaultFrameDecoration extends FullFrameDecorationHelper {
private static final int HEIGHT = 30;
private static final int BORDER_SIZE = 1;
private static final int HEIGHT = 28 + BORDER_SIZE;
private static final int BUTTON_ICON_SIZE = 4;
private static final int BUTTON_CIRCLE_RADIUS = 10;
private static final int BUTTON_SIZE = 16;
private static final int BUTTONS_RIGHT_PADDING = 7;
private static final int BUTTONS_PADDING = 8;
private static final Font FONT = new Font(Font.DIALOG, Font.BOLD, 12);
private static final Color ACTIVE_BACKGROUND = new Color(0xebebeb);
private static final Color ACTIVE_BACKGROUND_DARK = new Color(0x222222);
private static final Color INACTIVE_BACKGROUND = new Color(0xfafafa);
private static final Color INACTIVE_BACKGROUND_DARK = new Color(0x2c2c2c);
private static final Color ACTIVE_BACKGROUND = new Color(0xedeeef);
private static final Color ACTIVE_BACKGROUND_DARK = new Color(0x31363b);
private static final Color INACTIVE_BACKGROUND = new Color(0xdcddde);
private static final Color INACTIVE_BACKGROUND_DARK = new Color(0x292d31);
private static final Color ICON_BACKGROUND = ACTIVE_BACKGROUND;
private static final Color ICON_BACKGROUND_DARK = ACTIVE_BACKGROUND_DARK;
private static final Color ICON_HOVERED_BACKGROUND = new Color(0xd1d1d1);
private static final Color ICON_HOVERED_BACKGROUND_DARK = new Color(0x373737);
private static final Color ICON_PRESSED_BACKGROUND = new Color(0xc0c0c0);
private static final Color ICON_PRESSED_BACKGROUND_DARK = new Color(0x565656);
private static final Color ACTIVE_FOREGROUND = Color.darkGray;
private static final Color ACTIVE_FOREGROUND_DARK = new Color(0xf7f7f7);
private static final Color INACTIVE_FOREGROUND = Color.gray;
private static final Color INACTIVE_FOREGROUND_DARK = new Color(0xb5b5b5);
private static final Color ICON_HOVERED_BACKGROUND = new Color(0x232629);
private static final Color ICON_HOVERED_BACKGROUND_DARK = new Color(0xfcfcfc);
private static final Color ICON_HOVERED_FOREGROUND = new Color(0xcacdcf);
private static final Color ICON_HOVERED_FOREGROUND_DARK = new Color(0x43484c);
private static final Color ICON_PRESSED_BACKGROUND = new Color(0xa6a8ab);
private static final Color ICON_PRESSED_BACKGROUND_DARK = new Color(0x6e7175);
private static final Color CLOSE_ICON_PRESSED_BACKGROUND = new Color(0x6d2229);
private static final Color CLOSE_ICON_PRESSED_BACKGROUND_DARK = new Color(0x6d2229);
private static final Color CLOSE_ICON_HOVERED_BACKGROUND = new Color(0xff98a2);
private static final Color CLOSE_ICON_HOVERED_INACTIVE_BACKGROUND = new Color(0xda4453);
private static final Color CLOSE_ICON_HOVERED_INACTIVE_BACKGROUND_DARK = new Color(0xda4453);
private static final Color CLOSE_ICON_HOVERED_BACKGROUND_DARK = new Color(0xff98a2);
private static final Color ACTIVE_FOREGROUND = new Color(0x2d3033);
private static final Color ACTIVE_FOREGROUND_DARK = new Color(0xf1f1f1);
private static final Color INACTIVE_FOREGROUND = ACTIVE_FOREGROUND;
private static final Color INACTIVE_FOREGROUND_DARK = ACTIVE_FOREGROUND_DARK;
private static final Color ACTIVE_BACKGROUND_TOP = new Color(0xfbfbfb);
private static final Color ACTIVE_BACKGROUND_TOP_DARK = new Color(0x313131);
private static final Color INACTIVE_BACKGROUND_TOP = new Color(0xfefefe);
private static final Color INACTIVE_BACKGROUND_TOP_DARK = new Color(0x3a3a3a);
private static final Color ACTIVE_BORDER = new Color(0x9e9e9e);
private static final Color ACTIVE_BORDER_DARK = new Color(0x080808);
private static final Color INACTIVE_BORDER = new Color(0xbcbcbc);
private static final Color INACTIVE_BORDER_DARK = new Color(0x121212);
private static final int BORDER_SIZE = 1;
private static final Color ACTIVE_BACKGROUND_TOP = new Color(0xa9abac);
private static final Color ACTIVE_BACKGROUND_TOP_DARK = new Color(0x4c565f);
private static final Color INACTIVE_BACKGROUND_TOP = new Color(0xb7b8b9);
private static final Color INACTIVE_BACKGROUND_TOP_DARK = new Color(0x424952);
private static final Color ACTIVE_BORDER = ACTIVE_BACKGROUND_TOP;
private static final Color ACTIVE_BORDER_DARK = ACTIVE_BACKGROUND_TOP_DARK;
private static final Color INACTIVE_BORDER = INACTIVE_BACKGROUND_TOP;
private static final Color INACTIVE_BORDER_DARK = INACTIVE_BACKGROUND_TOP_DARK;
private static final int SIGNIFICANT_DRAG_DISTANCE = 4;
public DefaultFrameDecoration(WLDecoratedPeer peer, boolean showMinimize, boolean showMaximize) {
@@ -96,64 +105,31 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
return new Dimension(getButtonSpaceWidth(), HEIGHT);
}
private Point getCloseButtonCenter() {
int width = peer.getWidth();
return width >= HEIGHT ? new Point(width - HEIGHT / 2, HEIGHT / 2) : null;
}
private Point getMaximizeButtonCenter() {
if (!hasMaximizeButton()) return null;
int width = peer.getWidth();
return width >= 2 * HEIGHT ? new Point(width - HEIGHT * 3 / 2, HEIGHT / 2) : null;
}
private Point getMinimizeButtonCenter() {
if (!hasMinimizeButton()) return null;
int width = peer.getWidth();
int buttonSpaceWidth = getButtonSpaceWidth();
return width >= buttonSpaceWidth ? new Point(width - buttonSpaceWidth + HEIGHT / 2, HEIGHT / 2) : null;
}
@Override
protected Rectangle getCloseButtonBounds() {
int width = peer.getWidth();
if (width >= HEIGHT) {
return new Rectangle(width - HEIGHT / 2 - BUTTON_CIRCLE_RADIUS,
HEIGHT / 2 - BUTTON_CIRCLE_RADIUS,
BUTTON_CIRCLE_RADIUS * 2,
BUTTON_CIRCLE_RADIUS * 2);
} else {
return null;
}
int x = peer.getWidth() - BUTTON_SIZE - BUTTONS_RIGHT_PADDING - BORDER_SIZE;
int y = (int) Math.floor((HEIGHT - BUTTON_SIZE + 1f) / 2);
return new Rectangle(x, y, BUTTON_SIZE, BUTTON_SIZE);
}
@Override
protected Rectangle getMaximizeButtonBounds() {
if (!hasMaximizeButton()) return null;
int width = peer.getWidth();
if (width >= 2 * HEIGHT) {
return new Rectangle(width - HEIGHT * 3 / 2 - BUTTON_CIRCLE_RADIUS,
HEIGHT / 2 - BUTTON_CIRCLE_RADIUS,
BUTTON_CIRCLE_RADIUS * 2,
BUTTON_CIRCLE_RADIUS * 2);
} else {
return null;
}
int x = peer.getWidth() - BUTTON_SIZE * 2 - BUTTONS_RIGHT_PADDING
- BUTTONS_PADDING - BORDER_SIZE;
int y = (int) Math.floor((HEIGHT - BUTTON_SIZE + 1f) / 2);
return x > 0 ? new Rectangle(x, y, BUTTON_SIZE, BUTTON_SIZE) : null;
}
@Override
protected Rectangle getMinimizeButtonBounds() {
if (!hasMinimizeButton()) return null;
int width = peer.getWidth();
int buttonSpaceWidth = getButtonSpaceWidth();
if (width >= buttonSpaceWidth) {
return new Rectangle(width - buttonSpaceWidth + HEIGHT / 2 - BUTTON_CIRCLE_RADIUS,
HEIGHT / 2 - BUTTON_CIRCLE_RADIUS,
BUTTON_CIRCLE_RADIUS * 2,
BUTTON_CIRCLE_RADIUS * 2);
} else {
return null;
}
int x = peer.getWidth() - BUTTON_SIZE * 3 - BUTTONS_RIGHT_PADDING
- BUTTONS_PADDING * 2 - BORDER_SIZE;
int y = (int) Math.floor((HEIGHT - BUTTON_SIZE + 1f) / 2);
return x > 0 ? new Rectangle(x, y, BUTTON_SIZE, BUTTON_SIZE) : null;
}
@Override
@@ -170,7 +146,7 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
final int numButtons = 1
+ (hasMaximizeButton() ? 1 : 0)
+ (hasMinimizeButton() ? 1 : 0);
return numButtons * HEIGHT;
return numButtons * BUTTON_SIZE + (numButtons - 1) * BUTTONS_PADDING + BUTTONS_RIGHT_PADDING;
}
private Color getBackgroundColor(boolean isActive) {
@@ -217,13 +193,33 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
}
}
private Color getButtonForeground(boolean isHovered) {
if (isHovered) {
return isDarkTheme() ? ICON_HOVERED_FOREGROUND_DARK : ICON_HOVERED_FOREGROUND;
} else {
return isDarkTheme() ? ACTIVE_FOREGROUND_DARK : ACTIVE_FOREGROUND;
}
}
private Color getClosePressedBackground() {
return isDarkTheme() ? CLOSE_ICON_PRESSED_BACKGROUND_DARK : CLOSE_ICON_PRESSED_BACKGROUND;
}
private Color getCloseHoveredBackground(boolean isActive) {
if (isActive) {
return isDarkTheme() ? CLOSE_ICON_HOVERED_BACKGROUND_DARK : CLOSE_ICON_HOVERED_BACKGROUND;
} else {
return isDarkTheme() ? CLOSE_ICON_HOVERED_INACTIVE_BACKGROUND_DARK : CLOSE_ICON_HOVERED_INACTIVE_BACKGROUND;
}
}
@Override
protected void paintBorder(Graphics2D g2d) {
int width = peer.getWidth();
int height = peer.getHeight();
g2d.setColor(getBorderColor(isActive()));
g2d.setStroke(new BasicStroke(BORDER_SIZE));
g2d.drawRect(0, 0, width - BORDER_SIZE, height - BORDER_SIZE);
g2d.drawRect(0, 0, width, height);
}
@Override
@@ -250,17 +246,11 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
// The title bar
g.fillRoundRect(0, 0, width, HEIGHT + radius + 1, radius, radius);
// The top bevel of the title bar
g.setColor(getBackgroundTopColor(active));
g.drawLine(radius / 2, 1, width - radius / 2, 1);
g.drawArc(1, 1, (radius - 1), (radius - 1), 90, 60);
g.drawArc(width - radius, 1, (radius - 1), (radius - 1), 45, 45);
// The border
var oldStroke = g.getStroke();
g.setColor(getBorderColor(active));
g.setStroke(new BasicStroke(BORDER_SIZE));
g.drawRoundRect(0, 0, width - BORDER_SIZE, HEIGHT + radius + 1, radius, radius);
g.drawRoundRect(0, 0, width, HEIGHT + radius + 1, radius, radius);
g.setStroke(oldStroke);
g.drawLine(0, HEIGHT - 1, width, HEIGHT - 1);
} else {
@@ -270,32 +260,35 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
// The top bevel of the title bar
g.setColor(getBackgroundTopColor(active));
g.drawLine(BORDER_SIZE, BORDER_SIZE, width - BORDER_SIZE, BORDER_SIZE);
g.drawLine(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, HEIGHT - BORDER_SIZE);
g.drawLine(BORDER_SIZE, BORDER_SIZE, width, BORDER_SIZE);
g.drawLine(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, HEIGHT);
// The border
var oldStroke = g.getStroke();
g.setColor(getBorderColor(active));
g.setStroke(new BasicStroke(BORDER_SIZE));
g.drawRect(0, 0, width - BORDER_SIZE, HEIGHT - BORDER_SIZE);
g.drawRect(0, 0, width, HEIGHT);
g.setStroke(oldStroke);
}
paintTitle(g, title, foregroundColor, width);
Point closeButtonCenter = getCloseButtonCenter();
if (closeButtonCenter != null) {
paintButtonBackground(g, closeButtonCenter, closeButton);
paintCloseButton(g, closeButtonCenter, foregroundColor);
Rectangle closeButtonBounds = getCloseButtonBounds();
if (closeButtonBounds != null) {
paintCloseButtonBackground(g, closeButtonBounds, closeButton);
Color buttonColor = getButtonForeground(closeButton.hovered);
paintCloseButton(g, closeButtonBounds, buttonColor);
}
Point maximizedButtonCenter = getMaximizeButtonCenter();
if (maximizedButtonCenter != null) {
paintButtonBackground(g, maximizedButtonCenter, maximizeButton);
paintMaximizeButton(g, maximizedButtonCenter, foregroundColor);
Rectangle maximizedButtonBounds = getMaximizeButtonBounds();
if (maximizedButtonBounds != null) {
paintButtonBackground(g, maximizedButtonBounds, maximizeButton);
Color buttonColor = getButtonForeground(maximizeButton.hovered);
paintMaximizeButton(g, maximizedButtonBounds, buttonColor);
}
Point minimizedButtonCenter = getMinimizeButtonCenter();
if (minimizedButtonCenter != null) {
paintButtonBackground(g, minimizedButtonCenter, minimizeButton);
paintMinimizeButton(g, minimizedButtonCenter, foregroundColor);
Rectangle minimizedButtonBounds = getMinimizeButtonBounds();
if (minimizedButtonBounds != null) {
paintButtonBackground(g, minimizedButtonBounds, minimizeButton);
Color buttonColor = getButtonForeground(minimizeButton.hovered);
paintMinimizeButton(g, minimizedButtonBounds, buttonColor);
}
g.setClip(null);
}
@@ -304,7 +297,7 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
g.setColor(foregroundColor);
g.setFont(FONT);
FontMetrics fm = g.getFontMetrics();
int leftMargin = HEIGHT / 2 - BUTTON_CIRCLE_RADIUS; // same as space between close button and right window edge
int leftMargin = HEIGHT / 2 - BUTTON_SIZE; // same as space between close button and right window edge
int availableWidth = width - getButtonSpaceWidth() - leftMargin;
String text = SwingUtilities2.clipStringIfNecessary(null, fm, title, availableWidth);
int textWidth = fm.stringWidth(text);
@@ -313,48 +306,60 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
(HEIGHT - fm.getHeight()) / 2 + fm.getAscent());
}
private void paintButtonBackground(Graphics2D g, Point center, ButtonState state) {
if (isActive()) {
private void paintCloseButtonBackground(Graphics2D g, Rectangle bounds, ButtonState state) {
if (!isActive() && !state.hovered && !state.pressed) return;
g.setColor(state.pressed ? getClosePressedBackground() :
state.hovered ? getCloseHoveredBackground(isActive()) : getIconBackground());
g.fill(new Ellipse2D.Float(bounds.x, bounds.y, bounds.width, bounds.height));
}
private void paintButtonBackground(Graphics2D g, Rectangle bounds, ButtonState state) {
if (state.hovered || state.pressed) {
g.setColor(state.pressed ? getIconPressedBackground() :
state.hovered ? getIconHoveredBackground() : getIconBackground());
g.fill(new Ellipse2D.Float(center.x - BUTTON_CIRCLE_RADIUS + .5f,
center.y - BUTTON_CIRCLE_RADIUS + .5f,
2 * BUTTON_CIRCLE_RADIUS, 2 * BUTTON_CIRCLE_RADIUS));
g.fill(new Ellipse2D.Float(bounds.x, bounds.y, bounds.width, bounds.height));
}
}
private void paintCloseButton(Graphics2D g, Point center, Color foregroundColor) {
private static Point centerOf(Rectangle rect) {
return new Point((int) Math.floor(rect.x + rect.width / 2f),
(int) Math.floor(rect.y + rect.height / 2f));
}
private void paintCloseButton(Graphics2D g, Rectangle bounds, Color foregroundColor) {
g.setColor(foregroundColor);
Point center = centerOf(bounds);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE);
}
private void paintMaximizeButton(Graphics2D g, Point center, Color foregroundColor) {
private void paintMaximizeButton(Graphics2D g, Rectangle bounds, Color foregroundColor) {
g.setColor(foregroundColor);
Point center = centerOf(bounds);
int size = BUTTON_ICON_SIZE + 1;
if (peer.getState() == Frame.MAXIMIZED_BOTH) {
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y,
center.x, center.y - BUTTON_ICON_SIZE);
g.drawLine(center.x, center.y - BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y,
center.x, center.y + BUTTON_ICON_SIZE);
g.drawLine(center.x, center.y + BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y);
g.drawLine(center.x - size, center.y, center.x, center.y - size);
g.drawLine(center.x, center.y - size, center.x + size, center.y);
g.drawLine(center.x - size, center.y, center.x, center.y + size);
g.drawLine(center.x, center.y + size, center.x + size, center.y);
} else {
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE / 2,
center.x, center.y - BUTTON_ICON_SIZE / 2);
g.drawLine(center.x, center.y - BUTTON_ICON_SIZE / 2,
center.x + BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE / 2);
g.drawLine(center.x - size, (int) (center.y + size / 2f),
center.x, (int) (center.y - size / 2f));
g.drawLine(center.x, (int) (center.y - size / 2f),
center.x + size, (int) (center.y + size / 2f));
}
}
private void paintMinimizeButton(Graphics2D g, Point center, Color foregroundColor) {
private void paintMinimizeButton(Graphics2D g, Rectangle bounds, Color foregroundColor) {
g.setColor(foregroundColor);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE / 2,
center.x, center.y + BUTTON_ICON_SIZE / 2);
g.drawLine(center.x, center.y + BUTTON_ICON_SIZE / 2,
center.x + BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE / 2);
Point center = centerOf(bounds);
int size = BUTTON_ICON_SIZE + 1;
g.drawLine(center.x - size, (int) (center.y - size / 2f),
center.x, (int) (center.y + size / 2f));
g.drawLine(center.x, (int) (center.y + size / 2f),
center.x + size, (int) (center.y - size / 2f));
}
}

View File

@@ -76,8 +76,10 @@ public class GtkFrameDecoration extends FullFrameDecorationHelper {
@Override
public void paint(Graphics g) {
// Determine buttons' bounds, etc.
nativePrePaint(nativePtr, peer.getWidth());
super.paint(g);
nativePrePaint(nativePtr, peer.getWidth(), peer.getHeight());
if (peer.getWidth() >= titleBarMinWidth && peer.getHeight() >= titleBarHeight) {
super.paint(g);
}
}
@Override
@@ -85,6 +87,9 @@ public class GtkFrameDecoration extends FullFrameDecorationHelper {
int width = peer.getWidth();
int height = titleBarHeight;
assert width >= titleBarMinWidth;
assert peer.getHeight() >= titleBarHeight;
double scale = ((WLGraphicsConfig) peer.getGraphicsConfiguration()).getEffectiveScale();
g2d.setBackground(new Color(0, true));
g2d.clearRect(0, 0, width, height);
@@ -217,5 +222,5 @@ public class GtkFrameDecoration extends FullFrameDecorationHelper {
String title, int buttonsState);
private native int nativeGetIntProperty(long nativePtr, String name);
private native void nativeNotifyConfigured(long nativePtr, boolean active, boolean maximized, boolean fullscreen);
private native void nativePrePaint(long nativePtr, int width);
private native void nativePrePaint(long nativePtr, int width, int height);
}

View File

@@ -30,14 +30,12 @@ import sun.awt.AWTAccessor;
import sun.awt.AWTAccessor.ComponentAccessor;
import sun.awt.PaintEventDispatcher;
import sun.awt.SunToolkit;
import sun.awt.SurfacePixelGrabber;
import sun.awt.event.IgnorePaintEvent;
import sun.awt.image.SunVolatileImage;
import sun.java2d.SunGraphics2D;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceData;
import sun.java2d.pipe.Region;
import sun.java2d.vulkan.VKSurfaceData;
import sun.java2d.wl.WLSurfaceDataExt;
import sun.java2d.wl.WLSurfaceSizeListener;
import sun.util.logging.PlatformLogger;
@@ -90,8 +88,8 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.wl.focus.WLComponentPeer");
private static final PlatformLogger popupLog = PlatformLogger.getLogger("sun.awt.wl.popup.WLComponentPeer");
private static final int MINIMUM_WIDTH = 1;
private static final int MINIMUM_HEIGHT = 1;
protected static final int MINIMUM_WIDTH = 1;
protected static final int MINIMUM_HEIGHT = 1;
private final Object stateLock = new Object();
@@ -270,7 +268,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
return false;
}
private static Window getToplevelFor(Component component) {
public static Window getToplevelFor(Component component) {
Container container = component instanceof Container c ? c : component.getParent();
for (Container p = container; p != null; p = p.getParent()) {
if (p instanceof Window window && !isWlPopup(window)) {
@@ -287,7 +285,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
: c.getParent();
}
static Point getRelativeLocation(Component c, Window toplevel) {
public static Point getRelativeLocation(Component c, Window toplevel) {
Objects.requireNonNull(c);
if (toplevel == null) {
@@ -859,8 +857,10 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
}
public Dimension getMinimumSize() {
int shadowSize = (int) Math.ceil(shadow.getSize() * 4);
return new Dimension(shadowSize, shadowSize);
int shadowSize = shadow != null ? (int) Math.ceil(shadow.getSize() * 4) : 0;
return shadowSize == 0
? new Dimension(MINIMUM_WIDTH, MINIMUM_HEIGHT)
: new Dimension(shadowSize, shadowSize);
}
void showWindowMenu(long serial, int x, int y) {
@@ -1590,7 +1590,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
* Converts a value in the Java coordinate system into the Wayland
* surface-local coordinate system.
*/
int javaUnitsToSurfaceUnits(int value) {
public final int javaUnitsToSurfaceUnits(int value) {
if (!WLGraphicsEnvironment.isDebugScaleEnabled()) {
return value;
} else {
@@ -1600,7 +1600,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
}
}
int javaUnitsToSurfaceSize(int value) {
public final int javaUnitsToSurfaceSize(int value) {
if (!WLGraphicsEnvironment.isDebugScaleEnabled()) {
return value;
} else {
@@ -1756,9 +1756,12 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
private Dimension constrainSize(int width, int height) {
Dimension maxBounds = getMaxBufferBounds();
Dimension minSize = getMinimumSize();
minSize.width = Math.max(MINIMUM_WIDTH, minSize.width);
minSize.height = Math.max(MINIMUM_HEIGHT, minSize.height);
return new Dimension(
Math.max(Math.min(width, maxBounds.width), MINIMUM_WIDTH),
Math.max(Math.min(height, maxBounds.height), MINIMUM_HEIGHT));
Math.max(Math.min(width, maxBounds.width), minSize.width),
Math.max(Math.min(height, maxBounds.height), minSize.height));
}
private Dimension constrainSize(Dimension bounds) {

View File

@@ -88,7 +88,8 @@ public class WLDataDevice {
private static native void dispatchDataSourceQueueImpl(long nativePtr);
private static native void setSelectionImpl(int protocol, long nativePtr, long dataOfferNativePtr, long serial);
private static native void startDragImpl(long nativePtr, long dataOfferNativePtr,
long originSurfaceNativePtr, long iconNativePtr, long serial);
long originSurfaceNativePtr, long serial);
private static native void performDeletionsOnEDTImpl(long nativePtr);
public boolean isProtocolSupported(int protocol) {
return isProtocolSupportedImpl(nativePtr, protocol);
@@ -98,8 +99,8 @@ public class WLDataDevice {
setSelectionImpl(protocol, nativePtr, (source == null) ? 0 : source.getNativePtr(), serial);
}
public void startDrag(WLDataSource source, long originSurfaceNativePtr, long iconNativePtr, long serial) {
startDragImpl(nativePtr, source.getNativePtr(), originSurfaceNativePtr, iconNativePtr, serial);
public void startDrag(WLDataSource source, long originSurfaceNativePtr, long serial) {
startDragImpl(nativePtr, source.getNativePtr(), originSurfaceNativePtr, serial);
}
public WLClipboard getSystemClipboard() {
@@ -246,6 +247,10 @@ public class WLDataDevice {
return result;
}
public void performDeletionsOnEDT() {
performDeletionsOnEDTImpl(nativePtr);
}
// Event handlers, called from native on the EDT
private void handleDnDEnter(WLDataOffer offer, long serial, long surfacePtr, double x, double y) {
WLDropTargetContextPeer.getInstance().handleEnter(offer, serial, surfacePtr, x, y);

View File

@@ -61,7 +61,7 @@ public class WLDataOffer {
}
// after calling destroy(), this object enters an invalid state and needs to be deleted
public void destroy() {
public synchronized void destroy() {
if (nativePtr != 0) {
destroyImpl(nativePtr);
nativePtr = 0;

View File

@@ -25,7 +25,10 @@
package sun.awt.wl;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.datatransfer.Transferable;
import java.awt.image.BufferedImage;
import java.util.HashSet;
public class WLDataSource {
@@ -44,6 +47,8 @@ public class WLDataSource {
private static native void setDnDActionsImpl(long nativePtr, int actions);
private static native void setDnDIconImpl(long nativePtr, int scale, int width, int height, int offsetX, int offsetY, int[] pixels);
WLDataSource(WLDataDevice dataDevice, int protocol, Transferable data) {
var wlDataTransferer = (WLDataTransferer) WLDataTransferer.getInstance();
@@ -91,6 +96,31 @@ public class WLDataSource {
setDnDActionsImpl(nativePtr, actions);
}
public void setDnDIcon(Image image, int scale, int offsetX, int offsetY) {
if (nativePtr == 0) {
throw new IllegalStateException("Native pointer is null");
}
int width = image.getWidth(null);
int height = image.getHeight(null);
int[] pixels = new int[width * height];
if (image instanceof BufferedImage) {
// NOTE: no need to ensure that the BufferedImage is TYPE_INT_ARGB,
// getRGB() does pixel format conversion automatically
((BufferedImage) image).getRGB(0, 0, width, height, pixels, 0, width);
} else {
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bufferedImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
bufferedImage.getRGB(0, 0, width, height, pixels, 0, width);
}
setDnDIconImpl(nativePtr, scale, width, height, offsetX, offsetY, pixels);
}
public synchronized void destroy() {
if (nativePtr != 0) {
destroyImpl(nativePtr);

View File

@@ -63,7 +63,7 @@ public abstract class WLDecoratedPeer extends WLWindowPeer {
d = new DefaultFrameDecoration(this, showMinimize, showMaximize);
}
} else {
if (isGTKAvailable()) {
if (!WLToolkit.isKDE() && isGTKAvailable()) {
d = new GtkFrameDecoration(this, showMinimize, showMaximize);
} else {
d = new DefaultFrameDecoration(this, showMinimize, showMaximize);
@@ -134,10 +134,11 @@ public abstract class WLDecoratedPeer extends WLWindowPeer {
@Override
public Dimension getMinimumSize() {
final Dimension parentMinimumSize = super.getMinimumSize();
final Dimension decorMinimumSize = getDecoration().getMinimumSize();
var d = getDecoration();
final Dimension decorMinimumSize = d != null ? d.getMinimumSize() : new Dimension(0, 0);
final Dimension frameMinimumSize
= (decorMinimumSize.getWidth() == 0 && decorMinimumSize.getHeight() == 0)
? new Dimension(1, 1)
? new Dimension(MINIMUM_WIDTH, MINIMUM_HEIGHT)
: decorMinimumSize;
return new Rectangle(parentMinimumSize)
.union(new Rectangle(frameMinimumSize))

View File

@@ -92,21 +92,33 @@ public class WLDragSourceContextPeer extends SunDragSourceContextPeer {
this.dataDevice = dataDevice;
}
private long getComponentWlSurfacePtr() {
private WLComponentPeer getPeer() {
var comp = getComponent();
while (comp != null) {
var peer = AWTAccessor.getComponentAccessor().getPeer(comp);
if (peer instanceof WLComponentPeer wlPeer) {
return wlPeer.getSurface().getWlSurfacePtr();
return wlPeer;
}
comp = comp.getParent();
}
return null;
}
return 0;
private WLMainSurface getSurface() {
WLComponentPeer peer = getPeer();
if (peer != null) {
return peer.getSurface();
}
return null;
}
@Override
protected void startDrag(Transferable trans, long[] formats, Map<Long, DataFlavor> formatMap) {
var mainSurface = getSurface();
if (mainSurface == null) {
return;
}
// formats and formatMap are unused, because WLDataSource already references the same DataTransferer singleton
var source = new WLDragSource(trans);
@@ -115,10 +127,17 @@ public class WLDragSourceContextPeer extends SunDragSourceContextPeer {
source.setDnDActions(waylandActions);
var dragImage = getDragImage();
if (dragImage != null) {
var dragImageOffset = getDragImageOffset();
source.setDnDIcon(dragImage,
mainSurface.getGraphicsDevice().getDisplayScale(),
dragImageOffset.x, dragImageOffset.y);
}
long eventSerial = WLToolkit.getInputState().pointerButtonSerial();
var wlSurface = getComponentWlSurfacePtr();
dataDevice.startDrag(source, wlSurface, 0, eventSerial);
dataDevice.startDrag(source, mainSurface.getWlSurfacePtr(), eventSerial);
}
@Override

View File

@@ -38,35 +38,9 @@ import sun.java2d.wl.WLSurfaceSizeListener;
public abstract class WLGraphicsConfig extends GraphicsConfiguration {
private final WLGraphicsDevice device;
private final int x;
private final int y;
private final int xLogical; // logical (scaled) horizontal location; optional, could be zero
private final int yLogical; // logical (scaled) vertical location; optional, could be zero
private final int width;
private final int height;
private final int widthLogical; // logical (scaled) width; optional, could be zero
private final int heightLogical;// logical (scaled) height; optional, could be zero
private final int displayScale; // as reported by Wayland
private final double effectiveScale; // as enforced by Java
protected WLGraphicsConfig(WLGraphicsDevice device, int x, int y, int xLogical, int yLogical,
int width, int height, int widthLogical, int heightLogical,
int displayScale) {
protected WLGraphicsConfig(WLGraphicsDevice device) {
this.device = device;
this.x = x;
this.y = y;
this.xLogical = xLogical;
this.yLogical = yLogical;
this.width = width;
this.height = height;
this.widthLogical = widthLogical;
this.heightLogical = heightLogical;
this.displayScale = displayScale;
this.effectiveScale = WLGraphicsEnvironment.effectiveScaleFrom(displayScale);
}
boolean differsFrom(int width, int height, int scale) {
return width != this.width || height != this.height || scale != this.displayScale;
}
@Override
@@ -84,7 +58,7 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
@Override
public AffineTransform getDefaultTransform() {
double scale = effectiveScale;
double scale = device.getEffectiveScale();
return AffineTransform.getScaleInstance(scale, scale);
}
@@ -97,20 +71,18 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
@Override
public Rectangle getBounds() {
return (widthLogical > 0 && heightLogical > 0)
? new Rectangle(xLogical, yLogical, widthLogical, heightLogical)
: new Rectangle(x, y, width, height);
return device.getBounds();
}
public Rectangle getRealBounds() {
return new Rectangle(x, y, width, height);
return device.getRealBounds();
}
/**
* Returns the preferred Wayland buffer scale for this display configuration.
*/
public int getDisplayScale() {
return displayScale;
return device.getDisplayScale();
}
/**
@@ -118,7 +90,7 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
* if overridden with the sun.java2d.uiScale system property.
*/
public double getEffectiveScale() {
return effectiveScale;
return device.getEffectiveScale();
}
public abstract SurfaceType getSurfaceType();
@@ -127,6 +99,7 @@ public abstract class WLGraphicsConfig extends GraphicsConfiguration {
@Override
public String toString() {
return String.format("%dx%d@(%d, %d) %dx scale", width, height, x, y, displayScale);
Rectangle bounds = getBounds();
return String.format("%dx%d@(%d, %d) %dx scale", bounds.width, bounds.height, bounds.x, bounds.y, getDisplayScale());
}
}

View File

@@ -39,15 +39,21 @@ import java.util.HashSet;
import java.util.Set;
/**
* Corresponds to Wayland's output and is identified by its wlID and x, y coordinates
* in the multi-monitor setup. Whenever those change, this device is re-created.
* Corresponds to a Wayland output and is identified by its wlID.
* Owns all graphics configurations associated with this device.
* Encapsulates all the other properties of the output, such as its size
* and location in a multi-monitor configuration.
* Whenever any of these properties change, they are updated and
* the GraphicsConfiguration objects get re-created to let referents know of the change.
*/
public class WLGraphicsDevice extends GraphicsDevice {
private static final double MM_IN_INCH = 25.4;
/**
* ID of the corresponding wl_output object received from Wayland.
* Only changes when the device gets invalidated.
*/
private volatile int wlID; // only changes when the device gets invalidated
private volatile int wlID;
/**
* Some human-readable name of the device that came from Wayland.
@@ -58,75 +64,142 @@ public class WLGraphicsDevice extends GraphicsDevice {
/**
* The horizontal location of this device in the multi-monitor configuration.
*/
private volatile int x; // only changes when the device gets invalidated
private volatile int x;
/**
* The vertical location of this device in the multi-monitor configuration.
*/
private volatile int y; // only changes when the device gets invalidated
private volatile int y;
private volatile int xLogical; // logical (scaled) horizontal location; optional, could be zero
private volatile int yLogical; // logical (scaled) vertical location; optional, could be zero
/**
* Pixel width, mostly for accounting and reporting.
*/
private volatile int width;
private final int widthMm;
private final int heightMm;
/**
* Pixel height, mostly for accounting and reporting.
*/
private volatile int height;
// Configs are always the same in size and scale
private volatile GraphicsConfiguration[] configs = null;
/**
* Logical (scaled) width in Java units.
*/
private volatile int widthLogical;
// The default config is an object from the configs array
private volatile WLGraphicsConfig defaultConfig = null;
/**
* Logical (scaled) height in Java units.
*/
private volatile int heightLogical;
// Top-level window peers that consider this device as their primary one
// and get their graphics configuration from it
/**
* Width in millimeters.
*/
private volatile int widthMm;
/**
* Height in millimeters.
*/
private volatile int heightMm;
/**
* The device's scale factor as reported by Wayland.
* Since it is an integer, it's usually higher than the real fraction scale.
*/
private volatile int displayScale;
/**
* The effective scale factor as determined by Java.
*/
private volatile double effectiveScale;
private volatile GraphicsConfiguration[] configs;
private volatile WLGraphicsConfig defaultConfig; // A reference to the configs array
/**
* Top-level window peers that consider this device as their primary one
* and get their graphics configuration from it
*/
private final Set<WLComponentPeer> toplevels = new HashSet<>(); // guarded by 'this'
private WLGraphicsDevice(int id, int x, int y, int xLogical, int yLogical, int widthMm, int heightMm) {
private WLGraphicsDevice(int id,
String name,
int x, int y,
int width, int height,
int widthLogical, int heightLogical,
int widthMm, int heightMm,
int displayScale) {
assert width > 0 && height > 0;
assert widthLogical > 0 && heightLogical > 0;
assert widthMm > 0 && heightMm > 0;
assert displayScale > 0;
this.wlID = id;
this.name = name;
this.x = x;
this.y = y;
this.xLogical = xLogical;
this.yLogical = yLogical;
this.width = width;
this.height = height;
this.widthLogical = widthLogical;
this.heightLogical = heightLogical;
this.widthMm = widthMm;
this.heightMm = heightMm;
this.displayScale = displayScale;
this.effectiveScale = WLGraphicsEnvironment.effectiveScaleFrom(displayScale);
makeGC();
}
int getID() {
return wlID;
}
void updateConfiguration(String name, int width, int height, int widthLogical, int heightLogical, int scale) {
this.name = name;
WLGraphicsConfig config = defaultConfig;
// Note that all configs are of equal size and scale
if (config == null || config.differsFrom(width, height, scale)) {
GraphicsConfiguration[] newConfigs;
WLGraphicsConfig newDefaultConfig;
// It is necessary to create a new object whenever config changes as its
// identity is used to detect changes in scale, among other things.
if (VKEnv.isPresentationEnabled()) {
newConfigs = VKEnv.getDevices().flatMap(d -> d.getPresentableGraphicsConfigs().map(
gc -> WLVKGraphicsConfig.getConfig(
gc, this, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale)))
.toArray(WLGraphicsConfig[]::new);
newDefaultConfig = (WLGraphicsConfig) newConfigs[0];
} else {
// TODO: Actually, Wayland may support a lot more shared memory buffer configurations, need to
// subscribe to the wl_shm:format event and get the list from there.
newDefaultConfig = WLSMGraphicsConfig.getConfig(this, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale, true);
newConfigs = new GraphicsConfiguration[2];
newConfigs[0] = newDefaultConfig;
newConfigs[1] = WLSMGraphicsConfig.getConfig(this, x, y, xLogical, yLogical, width, height, widthLogical, heightLogical, scale, false);
}
configs = newConfigs;
defaultConfig = newDefaultConfig;
private void makeGC() {
GraphicsConfiguration[] newConfigs;
WLGraphicsConfig newDefaultConfig;
if (VKEnv.isPresentationEnabled()) {
newConfigs = VKEnv.getDevices().flatMap(d -> d.getPresentableGraphicsConfigs().map(
gc -> WLVKGraphicsConfig.getConfig(gc, this)))
.toArray(WLGraphicsConfig[]::new);
newDefaultConfig = (WLGraphicsConfig) newConfigs[0];
} else {
// TODO: Actually, Wayland may support a lot more shared memory buffer configurations, need to
// subscribe to the wl_shm:format event and get the list from there.
newDefaultConfig = WLSMGraphicsConfig.getConfig(this, true);
newConfigs = new GraphicsConfiguration[2];
newConfigs[0] = newDefaultConfig;
newConfigs[1] = WLSMGraphicsConfig.getConfig(this, false);
}
configs = newConfigs;
defaultConfig = newDefaultConfig;
}
void updateConfiguration(String name,
int x, int y,
int width, int height,
int widthLogical, int heightLogical,
int widthMm, int heightMm,
int scale) {
assert width > 0 && height > 0;
assert widthLogical > 0 && heightLogical > 0;
assert widthMm > 0 && heightMm > 0;
assert scale > 0;
this.name = name;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.widthLogical = widthLogical;
this.heightLogical = heightLogical;
this.widthMm = widthMm;
this.heightMm = heightMm;
this.displayScale = scale;
this.effectiveScale = WLGraphicsEnvironment.effectiveScaleFrom(scale);
// It is necessary to create new config objects whenever this device changes
// as GraphicsConfiguration identity is used to detect changes in scale, among other things.
makeGC();
// It is important that by the time displayChanged() events are delivered,
// all the peers on this device had their graphics configuration updated
// to refer to the new ones with, perhaps, different scale or resolution.
// to refer to the new ones with, perhaps, a different scale or resolution.
// This affects various BufferStrategy that use volatile images as their buffers.
notifyToplevels();
}
@@ -136,47 +209,36 @@ public class WLGraphicsDevice extends GraphicsDevice {
synchronized (this) {
toplevelsCopy.addAll(toplevels);
}
int wlOutputID = this.wlID;
// NB: each of those peers will likely receive another such notification
// from Wayland when it gets the wl_surface::enter event, but the second one
// will effectively be a no-op.
toplevelsCopy.forEach((peer) -> peer.notifyEnteredOutput(wlOutputID));
toplevelsCopy.forEach(WLComponentPeer::checkIfOnNewScreen);
}
/**
* Changes all aspects of this device including its identity to be that of the given
* Changes all aspects of this device, including its identity to be that of the given
* device. Only used for devices that are no longer physically present, but references
* to which may still exist in the program.
*/
void invalidate(WLGraphicsDevice similarDevice) {
// Note: It is expected that all the surface this device used to host have already received
// the 'leave' event and updated their device/graphics configurations accordingly.
this.wlID = similarDevice.wlID;
this.x = similarDevice.x;
this.y = similarDevice.y;
this.xLogical = similarDevice.xLogical;
this.yLogical = similarDevice.yLogical;
int newScale = similarDevice.getDisplayScale();
Rectangle newBounds = similarDevice.defaultConfig.getBounds();
Rectangle newRealBounds = similarDevice.defaultConfig.getRealBounds();
updateConfiguration(similarDevice.name, newRealBounds.width, newRealBounds.height, newBounds.width, newBounds.height, newScale);
updateConfiguration(similarDevice.name,
similarDevice.x,
similarDevice.y,
similarDevice.width,
similarDevice.height,
similarDevice.widthLogical,
similarDevice.heightLogical,
similarDevice.widthMm,
similarDevice.heightMm,
similarDevice.displayScale);
}
public static WLGraphicsDevice createWithConfiguration(int id, String name,
int x, int y, int xLogical, int yLogical,
int x, int y,
int width, int height, int widthLogical, int heightLogical,
int widthMm, int heightMm,
int scale) {
WLGraphicsDevice device = new WLGraphicsDevice(id, x, y, xLogical, yLogical, widthMm, heightMm);
device.updateConfiguration(name, width, height, widthLogical, heightLogical, scale);
return device;
}
/**
* Compares the identity of this device with the given attributes
* and returns true iff the attributes identify the same device.
*/
boolean isSameDeviceAs(int wlID, int x, int y, int xLogical, int yLogical) {
return this.wlID == wlID && this.x == x && this.y == y && this.xLogical == xLogical && this.yLogical == yLogical;
return new WLGraphicsDevice(id, name, x, y, width, height, widthLogical, heightLogical, widthMm, heightMm, scale);
}
boolean hasSameNameAs(WLGraphicsDevice otherDevice) {
@@ -207,8 +269,8 @@ public class WLGraphicsDevice extends GraphicsDevice {
public GraphicsConfiguration[] getConfigurations() {
// From wayland.xml, wl_output.mode event:
// "Non-current modes are deprecated. A compositor can decide to only
// advertise the current mode and never send other modes. Clients
// should not rely on non-current modes."
// advertise the current mode and never send other modes. Clients
// should not rely on non-current modes."
// So there's always the same set of configs.
return configs.clone();
}
@@ -218,8 +280,24 @@ public class WLGraphicsDevice extends GraphicsDevice {
return defaultConfig;
}
int getID() {
return wlID;
}
Rectangle getBounds() {
return new Rectangle(x, y, widthLogical, heightLogical);
}
Rectangle getRealBounds() {
return new Rectangle(x, y, width, height);
}
int getDisplayScale() {
return defaultConfig.getDisplayScale();
return displayScale;
}
double getEffectiveScale() {
return effectiveScale;
}
int getResolution() {
@@ -227,13 +305,6 @@ public class WLGraphicsDevice extends GraphicsDevice {
return getResolutionX(defaultConfig);
}
double getPhysicalResolution() {
Rectangle bounds = defaultConfig.getRealBounds();
double daigInPixels = Math.sqrt(bounds.width * bounds.width + bounds.height * bounds.height);
double diagInMm = Math.sqrt(widthMm * widthMm + heightMm * heightMm);
return daigInPixels * MM_IN_INCH / diagInMm;
}
double getPhysicalScale() {
Rectangle bounds = defaultConfig.getRealBounds();
double daigInPixels = Math.sqrt(bounds.width * bounds.width + bounds.height * bounds.height);
@@ -267,7 +338,7 @@ public class WLGraphicsDevice extends GraphicsDevice {
boolean fsSupported = isFullScreenSupported();
if (fsSupported && old != null) {
// enter windowed mode and restore original display mode
// enter windowed mode and restore the original display mode
exitFullScreenExclusive(old);
}
@@ -311,9 +382,8 @@ public class WLGraphicsDevice extends GraphicsDevice {
@Override
public String toString() {
var config = defaultConfig;
return String.format("WLGraphicsDevice: '%s' id=%d at (%d, %d) ((%d, %d) logical) with %s",
name, wlID, x, y, xLogical, yLogical,
config != null ? config : "<no configs>");
return String.format("WLGraphicsDevice: '%s' id=%d at (%d, %d) with %s",
name, wlID, x, y, config != null ? config : "<no configs>");
}
public Insets getInsets() {

Some files were not shown because too many files have changed in this diff Show More