Compare commits

...

94 Commits

Author SHA1 Message Date
Alexey Ushakov
da19b1c201 JBR-8738 Vulkan: Optimize ISO_BLIT
Remove unnecessary wait
2025-07-15 15:09:10 +02:00
Nikita Gubarkov
06cc75d80b JBR-8738 Vulkan: Optimize ISO_BLIT 2025-07-08 21:33:29 +02:00
Vitaly Provodin
203aad0e7d update exclude list on results of 21.0.7_b1050.5 test runs 2025-07-04 15:56:19 +04:00
Vitaly Provodin
fa662e668a JBR-8626 exclude java/awt/event/HierarchyEvent/AncestorResized/AncestorResized.java from Wayland runs 2025-07-04 15:56:18 +04:00
Nikita Gubarkov
f4eed0aabf JBR-9060 Vulkan: Fix MASK_FILL artifacts
Change local maskPos calculation from integer to floating point subtraction.
2025-07-03 19:25:19 +02:00
Alexey Ushakov
c43a8e77a9 JBR-9039 Vulkan: build failure on no vulkan build
Added native stubs for non vulkan builds
2025-07-03 16:34:11 +02:00
Gustavo Fão Valvassori
44c769baf2 JBR-9035 Support RTL on Decorated Window Title Bar (#540) 2025-07-02 10:54:02 +02:00
Alexey Ushakov
d65e4f3a69 JBR-8911 Backport: 8304825: MacOS metal pipeline - window isn't painted if created during display sleep
Adjusting OpenJDK patch for display link

(cherry picked from commit d1f83aace469f3fdc7e07e00dba9348560accb32)
2025-07-01 18:36:44 +02:00
Nikita Tsarev
01a88fa82d JBR-9043: Return null from getPlatformImageBytesForFormat for images with unknown extents on macOS 2025-07-01 11:26:33 +02:00
Nikita Tsarev
224651121f JBR-9044: Use getPlatformImageBytes to transfer TIFF images on macOS 2025-06-30 14:52:10 +02:00
Nikita Tsarev
333dbcb00f JBR-8952: Respect data flavor when encoding images on macOS 2025-06-27 12:21:11 +02:00
Nikita Gubarkov
c874bfb819 JBR-8737 Vulkan: Respect nonCoherentAtomSize in allocator 2025-06-26 20:42:37 +02:00
Alexey Ushakov
f14bfe0de9 JBR-7725 Vulkan: low performance in SwingMark
Removed extra synchronization in blits and extra copy

(cherry picked from commit 5411f2a1df5e6a6ed60c27837b414799b80d6fa5)
2025-06-26 19:21:41 +02:00
Alexey Ushakov
e948e68a1c JBR-9037 Vulkan: NPE in SwingMark
Added VKOffscreenGraphicsConfig check into UnixSurfaceManagerFactory
2025-06-26 17:44:12 +02:00
Alexey Ushakov
a40497a1a1 JBR-8479 Support Vulkan accelerated mode in perf scripts
Added the new option, minor refactoring

(cherry picked from commit b894b5fa96180c43d7a3f48d5c8b3ca35628b532)
2025-06-26 17:44:12 +02:00
Vitaly Provodin
221844284c JBR-7566 apply standard measurement scripts to Render
(cherry picked from commit be24b3e8ed)
(cherry picked from commit 64e4a227097331d53d9ec35172b5dfeaaa16a5da)
2025-06-26 17:44:12 +02:00
Alexey Ushakov
8ce1865667 JBR-6612 Provide standard scripts for performance measurements
Backported J2DBench, RenderPerfTest and SwingMark scripts from main

(cherry picked from commit 6f1ecb9b47)
(cherry picked from commit 00db5a29669d23a78f7c7e23efe99df3f6ab8ba2)
(cherry picked from commit e1cf099c09)
(cherry picked from commit 559015f5493e1177a044ec848e5e414a7424c201)
(cherry picked from commit fe09bf671b)
(cherry picked from commit f6e5fa71f2ad4247adf0038efd16515fcce4fd18)
2025-06-26 17:44:12 +02:00
Maxim Kartashev
dfa2f93ded JBR-9002 Wayland: deadlock with J2DDemo 2025-06-24 17:19:31 +04:00
Maxim Kartashev
a70d004636 JBR-8994 Wayland test runs cause agents to reboot, cannot be completed 2025-06-24 17:19:27 +04:00
Maxim Kartashev
bcaea044f6 JBR-8990 Wayland: make sure activating surface is valid when performing toFront() 2025-06-24 17:19:14 +04:00
Maxim Kartashev
62e4696d1a JBR-8991 Wayland: javax/swing/JMenu/bug4342646.java: PopupMenu is incorrectly placed at left of menu 2025-06-24 17:19:09 +04:00
Maxim Kartashev
3dd9b5b323 JBR-8626 Wayland: window shadow 2025-06-24 17:19:01 +04:00
Maxim Kartashev
9d1557ed3c JBR-8626 Wayland: sub-surface support 2025-06-24 17:18:46 +04:00
Maxim Kartashev
b6f8f9f1b7 JBR-8626 Wayland: proper encapsulation for WLComponentPeer 2025-06-24 17:08:37 +04:00
Maxim Kartashev
548521e162 JBR-8626 Wayland: uniform data access synhronization for WLComponentPeer 2025-06-24 17:08:30 +04:00
Maxim Kartashev
633895e1d8 JBR-8626 Wayland: relocate rounded corner painting to WLWindowPeer 2025-06-24 17:08:25 +04:00
Maxim Kartashev
fdc9dd44b6 JBR-8626 Wayland: fall-back client-side window decorations 2025-06-24 17:08:20 +04:00
Nikita Gubarkov
677d8edb87 JBR-8682 Vulkan: logicOpEnable Validation Error
(cherry picked from commit 196038a982b4bff0f96297ffd6734a14eb48bac4)
2025-06-23 15:17:11 +02:00
Nikita Gubarkov
52b213d96c JBR-8608 Vulkan: Cleanup capability checks
(cherry picked from commit 305cf4b2526dc6189e6715e7780ec9d8be44d1c6)
2025-06-23 15:14:41 +02:00
Nikita Gubarkov
df366c74c1 JBR-8601 Vulkan: Decouple from Wayland
(cherry picked from commit 91e7cb3969e7f06914cb4202cbe183d9e93c7d56)
2025-06-23 15:04:55 +02:00
Vladimir Dvorak
fc40612222 JBR-8890 DCEVM Handle exceptions in do_topological_class_sorting() 2025-06-21 10:51:04 +02:00
Nikita Gubarkov
8b7153234f JBR-8555 Vulkan: Do not flush the surface on transform change 2025-06-20 18:30:54 +02:00
Nikita Gubarkov
26f247ffb3 JBR-8553 Vulkan: Respect filtering hints in blits 2025-06-20 18:30:14 +02:00
Nikita Gubarkov
aa20c8b9f4 JBR-8525 Vulkan: Fix offscreen surface scaling 2025-06-20 18:29:38 +02:00
Nikita Gubarkov
120dd88dd0 JBR-8485 Vulkan: Blit surface into itself 2025-06-20 18:27:28 +02:00
Nikita Gubarkov
bb371ce7b0 JBR-8478 Vulkan: Pull real supported formats from the device 2025-06-20 18:26:35 +02:00
Nikita Gubarkov
abbf53d08e JBR-8473 Vulkan: Support for various source blit formats via swizzling 2025-06-20 18:26:04 +02:00
Nikita Gubarkov
921053cda6 JBR-8472 Vulkan: Respect source alpha type in blit routines 2025-06-20 18:24:11 +02:00
Nikita Gubarkov
1b0348f1b9 JBR-8471 Vulkan: Reuse descriptor sets in blit routines 2025-06-20 17:03:28 +02:00
Nikita Gubarkov
8120022a66 JBR-8448 Vulkan: Cleanup & fix Sw->Surface blit 2025-06-20 17:01:12 +02:00
Nikita Gubarkov
ac68b628c7 JBR-8447 Vulkan: Implement multi-view images 2025-06-20 16:55:07 +02:00
Nikita Gubarkov
99d3227951 JBR-8442 Vulkan: Fix OPAQUE mode rendering 2025-06-20 16:00:20 +02:00
Nikita Gubarkov
9116f5b953 JBR-8441 Vulkan: Update CArrayUtil.h 2025-06-20 15:59:56 +02:00
Nikita Gubarkov
13ddb27c38 JBR-8440 Vulkan: Pass the surface format to native code 2025-06-20 15:58:51 +02:00
Nikita Gubarkov
6e0ea66116 JBR-8439 Vulkan: Cleanup Surface->Surface blit 2025-06-20 15:48:45 +02:00
Nikita Gubarkov
f6b0380fd9 JBR-8424 Vulkan: Format-aware Surface->Sw blit 2025-06-20 15:46:07 +02:00
Nikita Gubarkov
70a658f57a JBR-8423 Vulkan: Expose VKFormat on Java side 2025-06-20 15:43:05 +02:00
Nikita Gubarkov
8fd5f9c426 JBR-8413 Vulkan: Make surfaces VKGPU-aware 2025-06-20 14:58:38 +02:00
Nikita Gubarkov
03cf016f2d JBR-8412 Vulkan: Add generic offscreen GraphicsConfig implementation 2025-06-20 14:55:23 +02:00
Nikita Gubarkov
1e1e4b38c6 JBR-8411 Vulkan: Move generic VKGraphicsConfig implementation into shared code 2025-06-20 14:53:35 +02:00
Nikita Gubarkov
3b4da58e2c JBR-8410 Vulkan: Expose VKDevice on Java side 2025-06-20 12:54:46 +02:00
Nikita Gubarkov
1b439f8a98 JBR-8391 Vulkan: Split instance and device into separate files 2025-06-20 12:29:41 +02:00
Nikita Gubarkov
4516560f65 JBR-8363 Vulkan: Organize usage of FlushRenderPass and FlushSurface 2025-06-20 12:23:31 +02:00
Alexey Ushakov
4a94eadd90 JBR-8418 Vulkan: RenderPerfTest Image test does not work properly
Passed transform to VKRenderer code
Flush content of the destination surface
2025-06-19 18:06:01 +02:00
Alexey Ushakov
42db39fb4e JBR-8430 Vulkan: move RenderingContext into Renderer
Moved context to the VKRenderer
2025-06-19 18:04:23 +02:00
Alexey Ushakov
cb751d0b52 JBR-8398 Vulkan: refactor shader code to use transforms
Replaced normalization logic with transform matrix
2025-06-19 16:29:36 +02:00
Vitaly Provodin
f711061895 update exclude list on results of 21.0.7_b1038.54 test runs 2025-06-17 13:39:48 +04:00
Vladimir Lagunov
6d0eb24888 Revert "JBR-7700 Disable java.io over java.nio.file in JBR21 by default"
This reverts commit 54817b2e8d.
2025-06-11 13:37:46 +02:00
Vitaly Provodin
3c90ecd46c update exclude list on results of 21.0.7_b1034.51 test runs 2025-06-11 03:53:12 +04:00
Vladimir Lagunov
54817b2e8d JBR-7700 Disable java.io over java.nio.file in JBR21 by default
The feature is ready month ago, but every time some new test appears that prevents the feature from being deployed.

By disabling the flag, it becomes possible to roll out the new functionality with the ability to enable it. So, it becomes possible to start verifying the new feature easier.

Meanwhile, we'll keep trying to enable the new feature by default.
2025-06-10 15:54:30 +02:00
Vitaly Provodin
c70e17b88b update exclude list after updating agents 2025-06-07 05:07:43 +04:00
Nikita Tsarev
bcd6f0c9c0 fixup! JBR-5860: Implement drag-and-drop [WLToolkit] 2025-06-06 18:42:33 +02:00
Dmitry Drobotov
646d2e478f JBR-8490 Improve searching for scroll bars in ScrollAreaAccessibility.
Use JScrollPane.getVerticalScrollBar/getHorizontalScrollBar methods to look for scroll bars. In some cases a scroll bar might be not a direct child of the scroll area, but it can still be assigned to the vertical/horizontalScrollBar property.
2025-06-06 18:36:25 +02:00
Dmitry Drobotov
4214897d5e JBR-8408 Post accessibility value changed events for scroll bars
3rd party apps might want to subscribe for scroll bar value changed events to track scroll position. VoiceOver and Zoom don't react on these events.
2025-06-06 18:08:05 +02:00
Nikita Tsarev
189907d2ae JBR-5860: Implement drag-and-drop [WLToolkit] 2025-06-06 16:51:01 +02:00
Maxim Kartashev
4b17fcc46e JBR-8949 Wayland: java/awt/Gtk/GtkVersionTest/GtkVersionTest.java: Wrong GTK library version: null 2025-06-06 12:19:16 +04:00
Nikita Tsarev
03804770b6 JBR-8912: Fix pasting unicode content from clipboard [WLToolkit] 2025-06-05 13:43:35 +02:00
Vladimir Dvorak
1d0cbadf05 JBR-8850 DCEVM: Enable interface replacement 2025-06-04 22:13:03 +02:00
Vladimir Dvorak
8cfd55e764 JBR-8636 DCEVM - eagerly set new_version in MagicAccessorImpl 2025-06-04 22:12:42 +02:00
Vitaly Provodin
b06068de9c JBR-8589 permit the case when lambda's parent is from the CDS archive, add omitted Outer.java 2025-06-03 14:01:42 +04:00
Vladimir Lagunov
eb027e2bc9 JBR-8859 Fix assertion in jtreg:test/jdk/com/sun/jdi/SetLocalWhileThreadInNative.java
The test relied on hard-coded constant that knows internals of FileInputStream.read calls, and these internals changed in JBR-7700.
2025-06-03 11:28:56 +02:00
Maxim Kartashev
5fbd7f38e0 JBR-7087 Wayland: enable more GTK tests 2025-06-03 10:03:19 +04:00
Maxim Kartashev
44754c75f9 JBR-8918 Wayland: javax/swing/LookAndFeel/8145547/DemandGTK2.sh fails due to no libgtk found 2025-06-03 09:59:26 +04:00
Vladimir Lagunov
0bf04b04b0 JBR-8852 Let tests that tune MaxDirectMemorySize work with io over nio
The problem is that java.nio.file always creates a direct buffer inside, and it's unavoidable. However, it's avoidable to tune java.nio.file that way, that direct buffers are never cached for latter use. This trick helps with tests that expect to be exclusive owners of the whole direct memory.
2025-06-02 18:10:12 +02:00
Vitaly Provodin
ca69af5c29 update exclude list on results of 21.0.7_b1021.38 test runs 2025-06-02 13:04:02 +04:00
Vitaly Provodin
1527cb78dd clean up fixed issues from exclude lists 2025-05-30 08:30:58 +04:00
Vitaly Provodin
bd8b5fa16f update exclude list on results of 21.0.7_b1020.35 test runs 2025-05-26 09:17:32 +04:00
Nikita Tsarev
c53afaf1c6 fixup! JBR-8833: Refactor Wayland data device abstraction [WLToolkit] 2025-05-23 18:27:35 +02:00
Sergey Shelomentsev
81b393aa37 fixup! JBR-4154 use -V to sort versions 2025-05-23 18:26:43 +03:00
Nikita Tsarev
8b4249aa00 JBR-8833: Refactor Wayland data device abstraction [WLToolkit] 2025-05-23 12:40:01 +02:00
Vitaly Provodin
a8b4f08808 fixup! JBR-4154 fix extracting version info from sources 2025-05-23 12:35:37 +04:00
Vitaly Provodin
88532ca2e1 fixup! clean up fixed issues from exclude lists 2025-05-23 09:30:08 +04:00
Vitaly Provodin
4b01cab791 clean up fixed issues from exclude lists 2025-05-23 06:18:47 +04:00
Vitaly Provodin
971aaf09d7 fixup! update exclude list on results of 21.0.7_b992.24 test runs 2025-05-23 03:50:30 +04:00
Vitaly Provodin
219ad23c66 JBR-8589 disable CDS specifically for Linux-x86 2025-05-22 02:48:11 +04:00
Vitaly Provodin
20015bc3b7 JBR-8589 replace system CDS with an actual one for IntelliJ 2025-05-21 09:20:01 +04:00
Vitaly Provodin
20bfe049b3 update exclude list on results of 21.0.7_b992.24 test runs 2025-05-21 09:12:19 +04:00
Artem Bochkarev
e32b9d4ebc fixup! JBR-8548 Add possibility to build without out-of-process part in Linux. 2025-05-21 08:17:43 +04:00
Artem Bochkarev
8ec3e80317 JBR-8548 Add possibility to build without out-of-process part in Linux. 2025-05-20 20:30:54 +04:00
Maxim Kartashev
c1325a3735 JBR-6979 Modernize more WaitForSingleObject on Windows
Use -XX:+UnlockExperimentalVMOptions -XX:-UseModernSynchAPI
to switch back to the original implementation
2025-05-19 17:25:34 +03:00
Maxim Kartashev
cc2280fd08 fixup! JBR-8664 Optimize sun.nio.fs.WindowsPath.compareTo 2025-05-19 17:44:06 +04:00
Maxim Kartashev
af4d8df355 JBR-8643 Wayland: popup will not appear if located outside of parent window 2025-05-19 17:27:12 +04:00
Nikita Tsarev
8fe8fb8108 JBR-8685: Add new macOS 15.4 shortcuts to the system shortcuts API 2025-05-19 11:29:07 +02:00
Vladimir Lagunov
997c61fdc5 JBR-8664 Optimize sun.nio.fs.WindowsPath.compareTo 2025-05-19 11:24:05 +04:00
169 changed files with 18662 additions and 5246 deletions

View File

@@ -40,8 +40,6 @@ architecture=${3:-x64} # aarch64 or x64
check_bundle_type_maketest
tag_prefix="jbr-"
OPENJDK_TAG=$(git log --simplify-by-decoration --decorate=short --pretty=short | grep "$tag_prefix" | cut -d "(" -f2 | cut -d ")" -f1 | awk '{print $2}' | sort -t "-" -k 2 -g | tail -n 1 | tr -d ",")
VERSION_FEATURE=$(getVersionProp "DEFAULT_VERSION_FEATURE")
VERSION_INTERIM=$(getVersionProp "DEFAULT_VERSION_INTERIM")
VERSION_UPDATE=$(getVersionProp "DEFAULT_VERSION_UPDATE")
@@ -49,8 +47,15 @@ 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}']"
JDK_BUILD_NUMBER=${JDK_BUILD_NUMBER:=$(echo $OPENJDK_TAG | awk -F "-|[+]" '{print $3}')}
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]+$'
if ! [[ $JDK_BUILD_NUMBER =~ $re ]] ; then
echo "error: JDK_BUILD_NUMBER Not a number: $JDK_BUILD_NUMBER"
JDK_BUILD_NUMBER=1
fi
echo "##teamcity[setParameter name='env.JDK_UPDATE_NUMBER' value='${JDK_BUILD_NUMBER}']"
VENDOR_NAME="JetBrains s.r.o."

View File

@@ -32,6 +32,13 @@ function do_configure {
--build=x86_64-unknown-linux-gnu \
--openjdk-target=x86_64-unknown-linux-gnu"
fi
if [ -n "${JCEF_BUILD_LEGACY:-}" ]; then
WITH_VULKAN=""
else
WITH_VULKAN="--with-vulkan"
fi
sh configure \
$WITH_DEBUG_LEVEL \
--with-vendor-name="$VENDOR_NAME" \
@@ -42,7 +49,7 @@ function do_configure {
--with-version-opt=b"$build_number" \
--with-boot-jdk="$BOOT_JDK" \
--enable-cds=yes \
--with-vulkan \
$WITH_VULKAN \
$LINUX_TARGET \
$DISABLE_WARNINGS_AS_ERRORS \
$STATIC_CONF_ARGS \
@@ -95,7 +102,9 @@ function create_image_bundle {
# jmod does not preserve file permissions (JDK-8173610)
[ -f "$IMAGES_DIR"/"$__root_dir"/lib/jcef_helper ] && chmod a+x "$IMAGES_DIR"/"$__root_dir"/lib/jcef_helper
[ -f "$IMAGES_DIR"/"$__root_dir"/lib/cef_server ] && chmod a+x "$IMAGES_DIR"/"$__root_dir"/lib/cef_server
if [ ! -n "${JCEF_BUILD_LEGACY:-}" ]; then
[ -f "$IMAGES_DIR"/"$__root_dir"/lib/cef_server ] && chmod a+x "$IMAGES_DIR"/"$__root_dir"/lib/cef_server
fi
echo Creating "$JBR".tar.gz ...

View File

@@ -12,6 +12,7 @@ set -x
#
source jb/project/tools/common/scripts/common.sh
ENABLE_CDS="no"
function do_configure {
linux32 bash configure \
@@ -24,7 +25,7 @@ function do_configure {
--with-version-opt=b"$build_number" \
--with-boot-jdk="$BOOT_JDK" \
$STATIC_CONF_ARGS \
--enable-cds=yes \
--enable-cds=$ENABLE_CDS \
$DISABLE_WARNINGS_AS_ERRORS \
$REPRODUCIBLE_BUILD_OPTS \
$WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS \
@@ -51,7 +52,7 @@ function create_image_bundle {
__cds_opt=''
if is_musl; then libc_type_suffix='musl-' ; fi
__cds_opt="--generate-cds-archive"
[ "${ENABLE_CDS}" == "yes" ] && __cds_opt="--generate-cds-archive"
[ "$bundle_type" == "fd" ] && [ "$__arch_name" == "$JBRSDK_BUNDLE" ] && __bundle_name=$__arch_name && fastdebug_infix="fastdebug-"
JBR=${__bundle_name}-${JBSDK_VERSION}-linux-${libc_type_suffix}x86-${fastdebug_infix}b${build_number}

View File

@@ -0,0 +1,267 @@
prog.verbose=disabled
prog.printresults=enabled
global.env.outputwidth=640
global.env.outputheight=480
global.env.runcount=5
global.env.repcount=0
global.env.testtime=2500
global.results.workunits=units
global.results.timeunits=sec
global.results.ratio=unitspersec
global.dest.offscreen=disabled
global.dest.frame.defaultframe=enabled
global.dest.frame.transframe=disabled
global.dest.frame.shapedframe=disabled
global.dest.frame.shapedtransframe=disabled
global.dest.compatimg.compatimg=disabled
global.dest.compatimg.opqcompatimg=disabled
global.dest.compatimg.bmcompatimg=disabled
global.dest.compatimg.transcompatimg=disabled
global.dest.volimg.volimg=disabled
global.dest.volimg.opqvolimg=disabled
global.dest.volimg.bmvolimg=disabled
global.dest.volimg.transvolimg=disabled
global.dest.bufimg.IntXrgb=disabled
global.dest.bufimg.IntArgb=disabled
global.dest.bufimg.IntArgbPre=disabled
global.dest.bufimg.3ByteBgr=disabled
global.dest.bufimg.ByteIndexed=disabled
global.dest.bufimg.ByteGray=disabled
global.dest.bufimg.4ByteAbgr=disabled
global.dest.bufimg.4ByteAbgrPre=disabled
global.dest.bufimg.custom=disabled
graphics.opts.anim=2
graphics.opts.sizes=250
graphics.opts.alpharule=SrcOver
graphics.opts.transform=ident
graphics.opts.extraalpha=Off
graphics.opts.xormode=Off
graphics.opts.clip=Off
graphics.opts.renderhint=Default
graphics.render.opts.paint=random
graphics.render.opts.alphacolor=Off
graphics.render.opts.antialias=On
graphics.render.opts.stroke=width1
graphics.render.tests.drawLine=disabled
graphics.render.tests.drawLineHoriz=disabled
graphics.render.tests.drawLineVert=disabled
graphics.render.tests.fillRect=disabled
graphics.render.tests.drawRect=disabled
graphics.render.tests.fillOval=disabled
graphics.render.tests.drawOval=disabled
graphics.render.tests.fillPoly=disabled
graphics.render.tests.drawPoly=enabled
graphics.render.tests.shape.fillCubic=disabled
graphics.render.tests.shape.drawCubic=disabled
graphics.render.tests.shape.fillEllipse2D=disabled
graphics.render.tests.shape.drawEllipse2D=disabled
graphics.imaging.src.offscr.opaque=disabled
graphics.imaging.src.offscr.bitmask=disabled
graphics.imaging.src.offscr.translucent=disabled
graphics.imaging.src.opqcompatimg.opaque=disabled
graphics.imaging.src.opqcompatimg.bitmask=disabled
graphics.imaging.src.opqcompatimg.translucent=disabled
graphics.imaging.src.bmcompatimg.opaque=disabled
graphics.imaging.src.bmcompatimg.bitmask=disabled
graphics.imaging.src.bmcompatimg.translucent=disabled
graphics.imaging.src.transcompatimg.opaque=disabled
graphics.imaging.src.transcompatimg.bitmask=disabled
graphics.imaging.src.transcompatimg.translucent=disabled
graphics.imaging.src.opqvolimg.opaque=disabled
graphics.imaging.src.opqvolimg.bitmask=disabled
graphics.imaging.src.opqvolimg.translucent=disabled
graphics.imaging.src.bmvolimg.opaque=disabled
graphics.imaging.src.bmvolimg.bitmask=disabled
graphics.imaging.src.bmvolimg.translucent=disabled
graphics.imaging.src.transvolimg.opaque=disabled
graphics.imaging.src.transvolimg.bitmask=disabled
graphics.imaging.src.transvolimg.translucent=disabled
graphics.imaging.src.bufimg.IntXrgb.opaque=disabled
graphics.imaging.src.bufimg.IntXrgb.bitmask=disabled
graphics.imaging.src.bufimg.IntXrgb.translucent=disabled
graphics.imaging.src.bufimg.IntArgb.opaque=disabled
graphics.imaging.src.bufimg.IntArgb.bitmask=disabled
graphics.imaging.src.bufimg.IntArgb.translucent=disabled
graphics.imaging.src.bufimg.IntArgbPre.opaque=disabled
graphics.imaging.src.bufimg.IntArgbPre.bitmask=disabled
graphics.imaging.src.bufimg.IntArgbPre.translucent=disabled
graphics.imaging.src.bufimg.ByteGray.opaque=disabled
graphics.imaging.src.bufimg.ByteGray.bitmask=disabled
graphics.imaging.src.bufimg.ByteGray.translucent=disabled
graphics.imaging.src.bufimg.3ByteBgr.opaque=disabled
graphics.imaging.src.bufimg.3ByteBgr.bitmask=disabled
graphics.imaging.src.bufimg.3ByteBgr.translucent=disabled
graphics.imaging.src.bufimg.4ByteAbgr.opaque=disabled
graphics.imaging.src.bufimg.4ByteAbgr.bitmask=disabled
graphics.imaging.src.bufimg.4ByteAbgr.translucent=disabled
graphics.imaging.src.bufimg.4ByteAbgrPre.opaque=disabled
graphics.imaging.src.bufimg.4ByteAbgrPre.bitmask=disabled
graphics.imaging.src.bufimg.4ByteAbgrPre.translucent=disabled
graphics.imaging.src.bufimg.ByteIndexedBm.opaque=disabled
graphics.imaging.src.bufimg.ByteIndexedBm.bitmask=disabled
graphics.imaging.src.bufimg.ByteIndexedBm.translucent=disabled
graphics.imaging.src.bufimg.unmanagedIntXrgb.opaque=disabled
graphics.imaging.src.bufimg.unmanagedIntXrgb.bitmask=disabled
graphics.imaging.src.bufimg.unmanagedIntXrgb.translucent=disabled
graphics.imaging.src.bufimg.unmanagedIntArgb.opaque=disabled
graphics.imaging.src.bufimg.unmanagedIntArgb.bitmask=disabled
graphics.imaging.src.bufimg.unmanagedIntArgb.translucent=disabled
graphics.imaging.src.bufimg.unmanagedIntArgbPre.opaque=disabled
graphics.imaging.src.bufimg.unmanagedIntArgbPre.bitmask=disabled
graphics.imaging.src.bufimg.unmanagedIntArgbPre.translucent=disabled
graphics.imaging.src.bufimg.unmanaged3ByteBgr.opaque=disabled
graphics.imaging.src.bufimg.unmanaged3ByteBgr.bitmask=disabled
graphics.imaging.src.bufimg.unmanaged3ByteBgr.translucent=disabled
graphics.imaging.benchmarks.opts.interpolation=Nearest neighbor
graphics.imaging.benchmarks.opts.touchsrc=Off
graphics.imaging.benchmarks.tests.drawimage=disabled
graphics.imaging.benchmarks.tests.drawimagebg=disabled
graphics.imaging.benchmarks.tests.drawimagescaleup=disabled
graphics.imaging.benchmarks.tests.drawimagescaledown=disabled
graphics.imaging.benchmarks.tests.drawimagescalesplit=disabled
graphics.imaging.benchmarks.tests.drawimagetxform=disabled
graphics.imaging.imageops.opts.op=convolve3x3zero
graphics.imaging.imageops.tests.graphics2d.drawimageop=disabled
graphics.imaging.imageops.tests.bufimgop.filternull=disabled
graphics.imaging.imageops.tests.bufimgop.filtercached=disabled
graphics.imaging.imageops.tests.rasterop.filternull=disabled
graphics.imaging.imageops.tests.rasterop.filtercached=disabled
graphics.misc.copytests.copyAreaVert=disabled
graphics.misc.copytests.copyAreaHoriz=disabled
graphics.misc.copytests.copyAreaDiag=disabled
pixel.opts.renderto=Off
pixel.opts.renderfrom=Off
pixel.src.1BitBinary=disabled
pixel.src.2BitBinary=disabled
pixel.src.4BitBinary=disabled
pixel.src.ByteIndexed=disabled
pixel.src.ByteGray=disabled
pixel.src.Short555=disabled
pixel.src.Short565=disabled
pixel.src.ShortGray=disabled
pixel.src.3ByteBgr=disabled
pixel.src.4ByteAbgr=disabled
pixel.src.IntXrgb=disabled
pixel.src.IntXbgr=disabled
pixel.src.IntArgb=disabled
pixel.bimgtests.getrgb=disabled
pixel.bimgtests.setrgb=disabled
pixel.rastests.getdataelem=disabled
pixel.rastests.setdataelem=disabled
pixel.rastests.getpixel=disabled
pixel.rastests.setpixel=disabled
pixel.dbtests.getelem=disabled
pixel.dbtests.setelem=disabled
text.opts.data.tlength=16
text.opts.data.tscript=english
text.opts.font.fname=serif,physical
text.opts.font.fstyle=0
text.opts.font.fsize=13.0
text.opts.font.ftx=Identity
text.opts.graphics.textaa=Off
text.opts.graphics.tfm=Off
text.opts.graphics.gaa=Off
text.opts.graphics.gtx=Identity
text.opts.advopts.gvstyle=0
text.opts.advopts.tlruns=1
text.opts.advopts.maptype=FONT
text.Rendering.tests.drawString=disabled
text.Rendering.tests.drawChars=disabled
text.Rendering.tests.drawBytes=disabled
text.Rendering.tests.drawGlyphVectors=disabled
text.Rendering.tests.drawTextLayout=disabled
text.Measuring.tests.stringWidth=disabled
text.Measuring.tests.stringBounds=disabled
text.Measuring.tests.charsWidth=disabled
text.Measuring.tests.charsBounds=disabled
text.Measuring.tests.fontcandisplay=disabled
text.Measuring.tests.gvWidth=disabled
text.Measuring.tests.gvLogicalBounds=disabled
text.Measuring.tests.gvVisualBounds=disabled
text.Measuring.tests.gvPixelBounds=disabled
text.Measuring.tests.gvOutline=disabled
text.Measuring.tests.gvGlyphLogicalBounds=disabled
text.Measuring.tests.gvGlyphVisualBounds=disabled
text.Measuring.tests.gvGlyphPixelBounds=disabled
text.Measuring.tests.gvGlyphOutline=disabled
text.Measuring.tests.gvGlyphTransform=disabled
text.Measuring.tests.gvGlyphMetrics=disabled
text.Measuring.tests.tlAdvance=disabled
text.Measuring.tests.tlAscent=disabled
text.Measuring.tests.tlBounds=disabled
text.Measuring.tests.tlGetCaretInfo=disabled
text.Measuring.tests.tlGetNextHit=disabled
text.Measuring.tests.tlGetCaretShape=disabled
text.Measuring.tests.tlGetLogicalHighlightShape=disabled
text.Measuring.tests.tlHitTest=disabled
text.Measuring.tests.tlOutline=disabled
text.construction.tests.gvfromfontstring=disabled
text.construction.tests.gvfromfontchars=disabled
text.construction.tests.gvfromfontci=disabled
text.construction.tests.gvfromfontglyphs=disabled
text.construction.tests.gvfromfontlayout=disabled
text.construction.tests.tlfromfont=disabled
text.construction.tests.tlfrommap=disabled
imageio.opts.size=250
imageio.opts.content=photo
imageio.input.opts.general.source.file=disabled
imageio.input.opts.general.source.url=disabled
imageio.input.opts.general.source.byteArray=disabled
imageio.input.opts.imageio.useCache=Off
imageio.input.image.toolkit.opts.format=
imageio.input.image.toolkit.tests.createImage=disabled
imageio.input.image.imageio.opts.format=
imageio.input.image.imageio.tests.imageioRead=disabled
imageio.input.image.imageio.reader.opts.seekForwardOnly=On
imageio.input.image.imageio.reader.opts.ignoreMetadata=On
imageio.input.image.imageio.reader.opts.installListener=Off
imageio.input.image.imageio.reader.tests.read=disabled
imageio.input.image.imageio.reader.tests.getImageMetadata=disabled
imageio.input.stream.tests.construct=disabled
imageio.input.stream.tests.read=disabled
imageio.input.stream.tests.readByteArray=disabled
imageio.input.stream.tests.readFullyByteArray=disabled
imageio.input.stream.tests.readBit=disabled
imageio.input.stream.tests.readByte=disabled
imageio.input.stream.tests.readUnsignedByte=disabled
imageio.input.stream.tests.readShort=disabled
imageio.input.stream.tests.readUnsignedShort=disabled
imageio.input.stream.tests.readInt=disabled
imageio.input.stream.tests.readUnsignedInt=disabled
imageio.input.stream.tests.readFloat=disabled
imageio.input.stream.tests.readLong=disabled
imageio.input.stream.tests.readDouble=disabled
imageio.input.stream.tests.skipBytes=disabled
imageio.output.opts.general.dest.file=disabled
imageio.output.opts.general.dest.byteArray=disabled
imageio.output.opts.imageio.useCache=Off
imageio.output.image.imageio.opts.format=
imageio.output.image.imageio.tests.imageioWrite=disabled
imageio.output.image.imageio.writer.opts.installListener=Off
imageio.output.image.imageio.writer.tests.write=disabled
imageio.output.stream.tests.construct=disabled
imageio.output.stream.tests.write=disabled
imageio.output.stream.tests.writeByteArray=disabled
imageio.output.stream.tests.writeBit=disabled
imageio.output.stream.tests.writeByte=disabled
imageio.output.stream.tests.writeShort=disabled
imageio.output.stream.tests.writeInt=disabled
imageio.output.stream.tests.writeFloat=disabled
imageio.output.stream.tests.writeLong=disabled
imageio.output.stream.tests.writeDouble=disabled
cmm.opts.profiles=1001
cmm.colorconv.data.fromRGB=disabled
cmm.colorconv.data.toRGB=disabled
cmm.colorconv.data.fromCIEXYZ=disabled
cmm.colorconv.data.toCIEXYZ=disabled
cmm.colorconv.ccop.ccopOptions.size=250
cmm.colorconv.ccop.ccopOptions.content=photo
cmm.colorconv.ccop.ccopOptions.srcType=INT_RGB
cmm.colorconv.ccop.ccopOptions.dstType=INT_RGB
cmm.colorconv.ccop.op_img=disabled
cmm.colorconv.ccop.op_rst=disabled
cmm.colorconv.ccop.op_draw=disabled
cmm.colorconv.embed.embedOptions.Images=512x512
cmm.colorconv.embed.embd_img_read=disabled
cmm.profiles.getHeader=disabled
cmm.profiles.getNumComponents=disabled

View File

@@ -0,0 +1,267 @@
prog.verbose=disabled
prog.printresults=enabled
global.env.outputwidth=640
global.env.outputheight=480
global.env.runcount=5
global.env.repcount=0
global.env.testtime=2500
global.results.workunits=units
global.results.timeunits=sec
global.results.ratio=unitspersec
global.dest.offscreen=disabled
global.dest.frame.defaultframe=enabled
global.dest.frame.transframe=disabled
global.dest.frame.shapedframe=disabled
global.dest.frame.shapedtransframe=disabled
global.dest.compatimg.compatimg=disabled
global.dest.compatimg.opqcompatimg=disabled
global.dest.compatimg.bmcompatimg=disabled
global.dest.compatimg.transcompatimg=disabled
global.dest.volimg.volimg=disabled
global.dest.volimg.opqvolimg=disabled
global.dest.volimg.bmvolimg=disabled
global.dest.volimg.transvolimg=disabled
global.dest.bufimg.IntXrgb=disabled
global.dest.bufimg.IntArgb=disabled
global.dest.bufimg.IntArgbPre=disabled
global.dest.bufimg.3ByteBgr=disabled
global.dest.bufimg.ByteIndexed=disabled
global.dest.bufimg.ByteGray=disabled
global.dest.bufimg.4ByteAbgr=disabled
global.dest.bufimg.4ByteAbgrPre=disabled
global.dest.bufimg.custom=disabled
graphics.opts.anim=2
graphics.opts.sizes=250
graphics.opts.alpharule=SrcOver
graphics.opts.transform=ident
graphics.opts.extraalpha=Off
graphics.opts.xormode=Off
graphics.opts.clip=Off
graphics.opts.renderhint=Default
graphics.render.opts.paint=random
graphics.render.opts.alphacolor=Off
graphics.render.opts.antialias=Off
graphics.render.opts.stroke=width1
graphics.render.tests.drawLine=disabled
graphics.render.tests.drawLineHoriz=disabled
graphics.render.tests.drawLineVert=disabled
graphics.render.tests.fillRect=disabled
graphics.render.tests.drawRect=disabled
graphics.render.tests.fillOval=disabled
graphics.render.tests.drawOval=disabled
graphics.render.tests.fillPoly=disabled
graphics.render.tests.drawPoly=enabled
graphics.render.tests.shape.fillCubic=disabled
graphics.render.tests.shape.drawCubic=disabled
graphics.render.tests.shape.fillEllipse2D=disabled
graphics.render.tests.shape.drawEllipse2D=disabled
graphics.imaging.src.offscr.opaque=disabled
graphics.imaging.src.offscr.bitmask=disabled
graphics.imaging.src.offscr.translucent=disabled
graphics.imaging.src.opqcompatimg.opaque=disabled
graphics.imaging.src.opqcompatimg.bitmask=disabled
graphics.imaging.src.opqcompatimg.translucent=disabled
graphics.imaging.src.bmcompatimg.opaque=disabled
graphics.imaging.src.bmcompatimg.bitmask=disabled
graphics.imaging.src.bmcompatimg.translucent=disabled
graphics.imaging.src.transcompatimg.opaque=disabled
graphics.imaging.src.transcompatimg.bitmask=disabled
graphics.imaging.src.transcompatimg.translucent=disabled
graphics.imaging.src.opqvolimg.opaque=disabled
graphics.imaging.src.opqvolimg.bitmask=disabled
graphics.imaging.src.opqvolimg.translucent=disabled
graphics.imaging.src.bmvolimg.opaque=disabled
graphics.imaging.src.bmvolimg.bitmask=disabled
graphics.imaging.src.bmvolimg.translucent=disabled
graphics.imaging.src.transvolimg.opaque=disabled
graphics.imaging.src.transvolimg.bitmask=disabled
graphics.imaging.src.transvolimg.translucent=disabled
graphics.imaging.src.bufimg.IntXrgb.opaque=disabled
graphics.imaging.src.bufimg.IntXrgb.bitmask=disabled
graphics.imaging.src.bufimg.IntXrgb.translucent=disabled
graphics.imaging.src.bufimg.IntArgb.opaque=disabled
graphics.imaging.src.bufimg.IntArgb.bitmask=disabled
graphics.imaging.src.bufimg.IntArgb.translucent=disabled
graphics.imaging.src.bufimg.IntArgbPre.opaque=disabled
graphics.imaging.src.bufimg.IntArgbPre.bitmask=disabled
graphics.imaging.src.bufimg.IntArgbPre.translucent=disabled
graphics.imaging.src.bufimg.ByteGray.opaque=disabled
graphics.imaging.src.bufimg.ByteGray.bitmask=disabled
graphics.imaging.src.bufimg.ByteGray.translucent=disabled
graphics.imaging.src.bufimg.3ByteBgr.opaque=disabled
graphics.imaging.src.bufimg.3ByteBgr.bitmask=disabled
graphics.imaging.src.bufimg.3ByteBgr.translucent=disabled
graphics.imaging.src.bufimg.4ByteAbgr.opaque=disabled
graphics.imaging.src.bufimg.4ByteAbgr.bitmask=disabled
graphics.imaging.src.bufimg.4ByteAbgr.translucent=disabled
graphics.imaging.src.bufimg.4ByteAbgrPre.opaque=disabled
graphics.imaging.src.bufimg.4ByteAbgrPre.bitmask=disabled
graphics.imaging.src.bufimg.4ByteAbgrPre.translucent=disabled
graphics.imaging.src.bufimg.ByteIndexedBm.opaque=disabled
graphics.imaging.src.bufimg.ByteIndexedBm.bitmask=disabled
graphics.imaging.src.bufimg.ByteIndexedBm.translucent=disabled
graphics.imaging.src.bufimg.unmanagedIntXrgb.opaque=disabled
graphics.imaging.src.bufimg.unmanagedIntXrgb.bitmask=disabled
graphics.imaging.src.bufimg.unmanagedIntXrgb.translucent=disabled
graphics.imaging.src.bufimg.unmanagedIntArgb.opaque=disabled
graphics.imaging.src.bufimg.unmanagedIntArgb.bitmask=disabled
graphics.imaging.src.bufimg.unmanagedIntArgb.translucent=disabled
graphics.imaging.src.bufimg.unmanagedIntArgbPre.opaque=disabled
graphics.imaging.src.bufimg.unmanagedIntArgbPre.bitmask=disabled
graphics.imaging.src.bufimg.unmanagedIntArgbPre.translucent=disabled
graphics.imaging.src.bufimg.unmanaged3ByteBgr.opaque=disabled
graphics.imaging.src.bufimg.unmanaged3ByteBgr.bitmask=disabled
graphics.imaging.src.bufimg.unmanaged3ByteBgr.translucent=disabled
graphics.imaging.benchmarks.opts.interpolation=Nearest neighbor
graphics.imaging.benchmarks.opts.touchsrc=Off
graphics.imaging.benchmarks.tests.drawimage=disabled
graphics.imaging.benchmarks.tests.drawimagebg=disabled
graphics.imaging.benchmarks.tests.drawimagescaleup=disabled
graphics.imaging.benchmarks.tests.drawimagescaledown=disabled
graphics.imaging.benchmarks.tests.drawimagescalesplit=disabled
graphics.imaging.benchmarks.tests.drawimagetxform=disabled
graphics.imaging.imageops.opts.op=convolve3x3zero
graphics.imaging.imageops.tests.graphics2d.drawimageop=disabled
graphics.imaging.imageops.tests.bufimgop.filternull=disabled
graphics.imaging.imageops.tests.bufimgop.filtercached=disabled
graphics.imaging.imageops.tests.rasterop.filternull=disabled
graphics.imaging.imageops.tests.rasterop.filtercached=disabled
graphics.misc.copytests.copyAreaVert=disabled
graphics.misc.copytests.copyAreaHoriz=disabled
graphics.misc.copytests.copyAreaDiag=disabled
pixel.opts.renderto=Off
pixel.opts.renderfrom=Off
pixel.src.1BitBinary=disabled
pixel.src.2BitBinary=disabled
pixel.src.4BitBinary=disabled
pixel.src.ByteIndexed=disabled
pixel.src.ByteGray=disabled
pixel.src.Short555=disabled
pixel.src.Short565=disabled
pixel.src.ShortGray=disabled
pixel.src.3ByteBgr=disabled
pixel.src.4ByteAbgr=disabled
pixel.src.IntXrgb=disabled
pixel.src.IntXbgr=disabled
pixel.src.IntArgb=disabled
pixel.bimgtests.getrgb=disabled
pixel.bimgtests.setrgb=disabled
pixel.rastests.getdataelem=disabled
pixel.rastests.setdataelem=disabled
pixel.rastests.getpixel=disabled
pixel.rastests.setpixel=disabled
pixel.dbtests.getelem=disabled
pixel.dbtests.setelem=disabled
text.opts.data.tlength=16
text.opts.data.tscript=english
text.opts.font.fname=serif,physical
text.opts.font.fstyle=0
text.opts.font.fsize=13.0
text.opts.font.ftx=Identity
text.opts.graphics.textaa=Off
text.opts.graphics.tfm=Off
text.opts.graphics.gaa=Off
text.opts.graphics.gtx=Identity
text.opts.advopts.gvstyle=0
text.opts.advopts.tlruns=1
text.opts.advopts.maptype=FONT
text.Rendering.tests.drawString=disabled
text.Rendering.tests.drawChars=disabled
text.Rendering.tests.drawBytes=disabled
text.Rendering.tests.drawGlyphVectors=disabled
text.Rendering.tests.drawTextLayout=disabled
text.Measuring.tests.stringWidth=disabled
text.Measuring.tests.stringBounds=disabled
text.Measuring.tests.charsWidth=disabled
text.Measuring.tests.charsBounds=disabled
text.Measuring.tests.fontcandisplay=disabled
text.Measuring.tests.gvWidth=disabled
text.Measuring.tests.gvLogicalBounds=disabled
text.Measuring.tests.gvVisualBounds=disabled
text.Measuring.tests.gvPixelBounds=disabled
text.Measuring.tests.gvOutline=disabled
text.Measuring.tests.gvGlyphLogicalBounds=disabled
text.Measuring.tests.gvGlyphVisualBounds=disabled
text.Measuring.tests.gvGlyphPixelBounds=disabled
text.Measuring.tests.gvGlyphOutline=disabled
text.Measuring.tests.gvGlyphTransform=disabled
text.Measuring.tests.gvGlyphMetrics=disabled
text.Measuring.tests.tlAdvance=disabled
text.Measuring.tests.tlAscent=disabled
text.Measuring.tests.tlBounds=disabled
text.Measuring.tests.tlGetCaretInfo=disabled
text.Measuring.tests.tlGetNextHit=disabled
text.Measuring.tests.tlGetCaretShape=disabled
text.Measuring.tests.tlGetLogicalHighlightShape=disabled
text.Measuring.tests.tlHitTest=disabled
text.Measuring.tests.tlOutline=disabled
text.construction.tests.gvfromfontstring=disabled
text.construction.tests.gvfromfontchars=disabled
text.construction.tests.gvfromfontci=disabled
text.construction.tests.gvfromfontglyphs=disabled
text.construction.tests.gvfromfontlayout=disabled
text.construction.tests.tlfromfont=disabled
text.construction.tests.tlfrommap=disabled
imageio.opts.size=250
imageio.opts.content=photo
imageio.input.opts.general.source.file=disabled
imageio.input.opts.general.source.url=disabled
imageio.input.opts.general.source.byteArray=disabled
imageio.input.opts.imageio.useCache=Off
imageio.input.image.toolkit.opts.format=
imageio.input.image.toolkit.tests.createImage=disabled
imageio.input.image.imageio.opts.format=
imageio.input.image.imageio.tests.imageioRead=disabled
imageio.input.image.imageio.reader.opts.seekForwardOnly=On
imageio.input.image.imageio.reader.opts.ignoreMetadata=On
imageio.input.image.imageio.reader.opts.installListener=Off
imageio.input.image.imageio.reader.tests.read=disabled
imageio.input.image.imageio.reader.tests.getImageMetadata=disabled
imageio.input.stream.tests.construct=disabled
imageio.input.stream.tests.read=disabled
imageio.input.stream.tests.readByteArray=disabled
imageio.input.stream.tests.readFullyByteArray=disabled
imageio.input.stream.tests.readBit=disabled
imageio.input.stream.tests.readByte=disabled
imageio.input.stream.tests.readUnsignedByte=disabled
imageio.input.stream.tests.readShort=disabled
imageio.input.stream.tests.readUnsignedShort=disabled
imageio.input.stream.tests.readInt=disabled
imageio.input.stream.tests.readUnsignedInt=disabled
imageio.input.stream.tests.readFloat=disabled
imageio.input.stream.tests.readLong=disabled
imageio.input.stream.tests.readDouble=disabled
imageio.input.stream.tests.skipBytes=disabled
imageio.output.opts.general.dest.file=disabled
imageio.output.opts.general.dest.byteArray=disabled
imageio.output.opts.imageio.useCache=Off
imageio.output.image.imageio.opts.format=
imageio.output.image.imageio.tests.imageioWrite=disabled
imageio.output.image.imageio.writer.opts.installListener=Off
imageio.output.image.imageio.writer.tests.write=disabled
imageio.output.stream.tests.construct=disabled
imageio.output.stream.tests.write=disabled
imageio.output.stream.tests.writeByteArray=disabled
imageio.output.stream.tests.writeBit=disabled
imageio.output.stream.tests.writeByte=disabled
imageio.output.stream.tests.writeShort=disabled
imageio.output.stream.tests.writeInt=disabled
imageio.output.stream.tests.writeFloat=disabled
imageio.output.stream.tests.writeLong=disabled
imageio.output.stream.tests.writeDouble=disabled
cmm.opts.profiles=1001
cmm.colorconv.data.fromRGB=disabled
cmm.colorconv.data.toRGB=disabled
cmm.colorconv.data.fromCIEXYZ=disabled
cmm.colorconv.data.toCIEXYZ=disabled
cmm.colorconv.ccop.ccopOptions.size=250
cmm.colorconv.ccop.ccopOptions.content=photo
cmm.colorconv.ccop.ccopOptions.srcType=INT_RGB
cmm.colorconv.ccop.ccopOptions.dstType=INT_RGB
cmm.colorconv.ccop.op_img=disabled
cmm.colorconv.ccop.op_rst=disabled
cmm.colorconv.ccop.op_draw=disabled
cmm.colorconv.embed.embedOptions.Images=512x512
cmm.colorconv.embed.embd_img_read=disabled
cmm.profiles.getHeader=disabled
cmm.profiles.getNumComponents=disabled

View File

@@ -0,0 +1,267 @@
prog.verbose=disabled
prog.printresults=enabled
global.env.outputwidth=640
global.env.outputheight=480
global.env.runcount=5
global.env.repcount=0
global.env.testtime=2500
global.results.workunits=units
global.results.timeunits=sec
global.results.ratio=unitspersec
global.dest.offscreen=disabled
global.dest.frame.defaultframe=enabled
global.dest.frame.transframe=disabled
global.dest.frame.shapedframe=disabled
global.dest.frame.shapedtransframe=disabled
global.dest.compatimg.compatimg=disabled
global.dest.compatimg.opqcompatimg=disabled
global.dest.compatimg.bmcompatimg=disabled
global.dest.compatimg.transcompatimg=disabled
global.dest.volimg.volimg=disabled
global.dest.volimg.opqvolimg=disabled
global.dest.volimg.bmvolimg=disabled
global.dest.volimg.transvolimg=disabled
global.dest.bufimg.IntXrgb=disabled
global.dest.bufimg.IntArgb=disabled
global.dest.bufimg.IntArgbPre=disabled
global.dest.bufimg.3ByteBgr=disabled
global.dest.bufimg.ByteIndexed=disabled
global.dest.bufimg.ByteGray=disabled
global.dest.bufimg.4ByteAbgr=disabled
global.dest.bufimg.4ByteAbgrPre=disabled
global.dest.bufimg.custom=disabled
graphics.opts.anim=2
graphics.opts.sizes=250
graphics.opts.alpharule=SrcOver
graphics.opts.transform=ident
graphics.opts.extraalpha=Off
graphics.opts.xormode=Off
graphics.opts.clip=Off
graphics.opts.renderhint=Default
graphics.render.opts.paint=single
graphics.render.opts.alphacolor=Off
graphics.render.opts.antialias=Off
graphics.render.opts.stroke=width1
graphics.render.tests.drawLine=disabled
graphics.render.tests.drawLineHoriz=disabled
graphics.render.tests.drawLineVert=disabled
graphics.render.tests.fillRect=disabled
graphics.render.tests.drawRect=disabled
graphics.render.tests.fillOval=disabled
graphics.render.tests.drawOval=disabled
graphics.render.tests.fillPoly=disabled
graphics.render.tests.drawPoly=enabled
graphics.render.tests.shape.fillCubic=disabled
graphics.render.tests.shape.drawCubic=disabled
graphics.render.tests.shape.fillEllipse2D=disabled
graphics.render.tests.shape.drawEllipse2D=disabled
graphics.imaging.src.offscr.opaque=disabled
graphics.imaging.src.offscr.bitmask=disabled
graphics.imaging.src.offscr.translucent=disabled
graphics.imaging.src.opqcompatimg.opaque=disabled
graphics.imaging.src.opqcompatimg.bitmask=disabled
graphics.imaging.src.opqcompatimg.translucent=disabled
graphics.imaging.src.bmcompatimg.opaque=disabled
graphics.imaging.src.bmcompatimg.bitmask=disabled
graphics.imaging.src.bmcompatimg.translucent=disabled
graphics.imaging.src.transcompatimg.opaque=disabled
graphics.imaging.src.transcompatimg.bitmask=disabled
graphics.imaging.src.transcompatimg.translucent=disabled
graphics.imaging.src.opqvolimg.opaque=disabled
graphics.imaging.src.opqvolimg.bitmask=disabled
graphics.imaging.src.opqvolimg.translucent=disabled
graphics.imaging.src.bmvolimg.opaque=disabled
graphics.imaging.src.bmvolimg.bitmask=disabled
graphics.imaging.src.bmvolimg.translucent=disabled
graphics.imaging.src.transvolimg.opaque=disabled
graphics.imaging.src.transvolimg.bitmask=disabled
graphics.imaging.src.transvolimg.translucent=disabled
graphics.imaging.src.bufimg.IntXrgb.opaque=disabled
graphics.imaging.src.bufimg.IntXrgb.bitmask=disabled
graphics.imaging.src.bufimg.IntXrgb.translucent=disabled
graphics.imaging.src.bufimg.IntArgb.opaque=disabled
graphics.imaging.src.bufimg.IntArgb.bitmask=disabled
graphics.imaging.src.bufimg.IntArgb.translucent=disabled
graphics.imaging.src.bufimg.IntArgbPre.opaque=disabled
graphics.imaging.src.bufimg.IntArgbPre.bitmask=disabled
graphics.imaging.src.bufimg.IntArgbPre.translucent=disabled
graphics.imaging.src.bufimg.ByteGray.opaque=disabled
graphics.imaging.src.bufimg.ByteGray.bitmask=disabled
graphics.imaging.src.bufimg.ByteGray.translucent=disabled
graphics.imaging.src.bufimg.3ByteBgr.opaque=disabled
graphics.imaging.src.bufimg.3ByteBgr.bitmask=disabled
graphics.imaging.src.bufimg.3ByteBgr.translucent=disabled
graphics.imaging.src.bufimg.4ByteAbgr.opaque=disabled
graphics.imaging.src.bufimg.4ByteAbgr.bitmask=disabled
graphics.imaging.src.bufimg.4ByteAbgr.translucent=disabled
graphics.imaging.src.bufimg.4ByteAbgrPre.opaque=disabled
graphics.imaging.src.bufimg.4ByteAbgrPre.bitmask=disabled
graphics.imaging.src.bufimg.4ByteAbgrPre.translucent=disabled
graphics.imaging.src.bufimg.ByteIndexedBm.opaque=disabled
graphics.imaging.src.bufimg.ByteIndexedBm.bitmask=disabled
graphics.imaging.src.bufimg.ByteIndexedBm.translucent=disabled
graphics.imaging.src.bufimg.unmanagedIntXrgb.opaque=disabled
graphics.imaging.src.bufimg.unmanagedIntXrgb.bitmask=disabled
graphics.imaging.src.bufimg.unmanagedIntXrgb.translucent=disabled
graphics.imaging.src.bufimg.unmanagedIntArgb.opaque=disabled
graphics.imaging.src.bufimg.unmanagedIntArgb.bitmask=disabled
graphics.imaging.src.bufimg.unmanagedIntArgb.translucent=disabled
graphics.imaging.src.bufimg.unmanagedIntArgbPre.opaque=disabled
graphics.imaging.src.bufimg.unmanagedIntArgbPre.bitmask=disabled
graphics.imaging.src.bufimg.unmanagedIntArgbPre.translucent=disabled
graphics.imaging.src.bufimg.unmanaged3ByteBgr.opaque=disabled
graphics.imaging.src.bufimg.unmanaged3ByteBgr.bitmask=disabled
graphics.imaging.src.bufimg.unmanaged3ByteBgr.translucent=disabled
graphics.imaging.benchmarks.opts.interpolation=Nearest neighbor
graphics.imaging.benchmarks.opts.touchsrc=Off
graphics.imaging.benchmarks.tests.drawimage=disabled
graphics.imaging.benchmarks.tests.drawimagebg=disabled
graphics.imaging.benchmarks.tests.drawimagescaleup=disabled
graphics.imaging.benchmarks.tests.drawimagescaledown=disabled
graphics.imaging.benchmarks.tests.drawimagescalesplit=disabled
graphics.imaging.benchmarks.tests.drawimagetxform=disabled
graphics.imaging.imageops.opts.op=convolve3x3zero
graphics.imaging.imageops.tests.graphics2d.drawimageop=disabled
graphics.imaging.imageops.tests.bufimgop.filternull=disabled
graphics.imaging.imageops.tests.bufimgop.filtercached=disabled
graphics.imaging.imageops.tests.rasterop.filternull=disabled
graphics.imaging.imageops.tests.rasterop.filtercached=disabled
graphics.misc.copytests.copyAreaVert=disabled
graphics.misc.copytests.copyAreaHoriz=disabled
graphics.misc.copytests.copyAreaDiag=disabled
pixel.opts.renderto=Off
pixel.opts.renderfrom=Off
pixel.src.1BitBinary=disabled
pixel.src.2BitBinary=disabled
pixel.src.4BitBinary=disabled
pixel.src.ByteIndexed=disabled
pixel.src.ByteGray=disabled
pixel.src.Short555=disabled
pixel.src.Short565=disabled
pixel.src.ShortGray=disabled
pixel.src.3ByteBgr=disabled
pixel.src.4ByteAbgr=disabled
pixel.src.IntXrgb=disabled
pixel.src.IntXbgr=disabled
pixel.src.IntArgb=disabled
pixel.bimgtests.getrgb=disabled
pixel.bimgtests.setrgb=disabled
pixel.rastests.getdataelem=disabled
pixel.rastests.setdataelem=disabled
pixel.rastests.getpixel=disabled
pixel.rastests.setpixel=disabled
pixel.dbtests.getelem=disabled
pixel.dbtests.setelem=disabled
text.opts.data.tlength=16
text.opts.data.tscript=english
text.opts.font.fname=serif,physical
text.opts.font.fstyle=0
text.opts.font.fsize=13.0
text.opts.font.ftx=Identity
text.opts.graphics.textaa=Off
text.opts.graphics.tfm=Off
text.opts.graphics.gaa=Off
text.opts.graphics.gtx=Identity
text.opts.advopts.gvstyle=0
text.opts.advopts.tlruns=1
text.opts.advopts.maptype=FONT
text.Rendering.tests.drawString=disabled
text.Rendering.tests.drawChars=disabled
text.Rendering.tests.drawBytes=disabled
text.Rendering.tests.drawGlyphVectors=disabled
text.Rendering.tests.drawTextLayout=disabled
text.Measuring.tests.stringWidth=disabled
text.Measuring.tests.stringBounds=disabled
text.Measuring.tests.charsWidth=disabled
text.Measuring.tests.charsBounds=disabled
text.Measuring.tests.fontcandisplay=disabled
text.Measuring.tests.gvWidth=disabled
text.Measuring.tests.gvLogicalBounds=disabled
text.Measuring.tests.gvVisualBounds=disabled
text.Measuring.tests.gvPixelBounds=disabled
text.Measuring.tests.gvOutline=disabled
text.Measuring.tests.gvGlyphLogicalBounds=disabled
text.Measuring.tests.gvGlyphVisualBounds=disabled
text.Measuring.tests.gvGlyphPixelBounds=disabled
text.Measuring.tests.gvGlyphOutline=disabled
text.Measuring.tests.gvGlyphTransform=disabled
text.Measuring.tests.gvGlyphMetrics=disabled
text.Measuring.tests.tlAdvance=disabled
text.Measuring.tests.tlAscent=disabled
text.Measuring.tests.tlBounds=disabled
text.Measuring.tests.tlGetCaretInfo=disabled
text.Measuring.tests.tlGetNextHit=disabled
text.Measuring.tests.tlGetCaretShape=disabled
text.Measuring.tests.tlGetLogicalHighlightShape=disabled
text.Measuring.tests.tlHitTest=disabled
text.Measuring.tests.tlOutline=disabled
text.construction.tests.gvfromfontstring=disabled
text.construction.tests.gvfromfontchars=disabled
text.construction.tests.gvfromfontci=disabled
text.construction.tests.gvfromfontglyphs=disabled
text.construction.tests.gvfromfontlayout=disabled
text.construction.tests.tlfromfont=disabled
text.construction.tests.tlfrommap=disabled
imageio.opts.size=250
imageio.opts.content=photo
imageio.input.opts.general.source.file=disabled
imageio.input.opts.general.source.url=disabled
imageio.input.opts.general.source.byteArray=disabled
imageio.input.opts.imageio.useCache=Off
imageio.input.image.toolkit.opts.format=
imageio.input.image.toolkit.tests.createImage=disabled
imageio.input.image.imageio.opts.format=
imageio.input.image.imageio.tests.imageioRead=disabled
imageio.input.image.imageio.reader.opts.seekForwardOnly=On
imageio.input.image.imageio.reader.opts.ignoreMetadata=On
imageio.input.image.imageio.reader.opts.installListener=Off
imageio.input.image.imageio.reader.tests.read=disabled
imageio.input.image.imageio.reader.tests.getImageMetadata=disabled
imageio.input.stream.tests.construct=disabled
imageio.input.stream.tests.read=disabled
imageio.input.stream.tests.readByteArray=disabled
imageio.input.stream.tests.readFullyByteArray=disabled
imageio.input.stream.tests.readBit=disabled
imageio.input.stream.tests.readByte=disabled
imageio.input.stream.tests.readUnsignedByte=disabled
imageio.input.stream.tests.readShort=disabled
imageio.input.stream.tests.readUnsignedShort=disabled
imageio.input.stream.tests.readInt=disabled
imageio.input.stream.tests.readUnsignedInt=disabled
imageio.input.stream.tests.readFloat=disabled
imageio.input.stream.tests.readLong=disabled
imageio.input.stream.tests.readDouble=disabled
imageio.input.stream.tests.skipBytes=disabled
imageio.output.opts.general.dest.file=disabled
imageio.output.opts.general.dest.byteArray=disabled
imageio.output.opts.imageio.useCache=Off
imageio.output.image.imageio.opts.format=
imageio.output.image.imageio.tests.imageioWrite=disabled
imageio.output.image.imageio.writer.opts.installListener=Off
imageio.output.image.imageio.writer.tests.write=disabled
imageio.output.stream.tests.construct=disabled
imageio.output.stream.tests.write=disabled
imageio.output.stream.tests.writeByteArray=disabled
imageio.output.stream.tests.writeBit=disabled
imageio.output.stream.tests.writeByte=disabled
imageio.output.stream.tests.writeShort=disabled
imageio.output.stream.tests.writeInt=disabled
imageio.output.stream.tests.writeFloat=disabled
imageio.output.stream.tests.writeLong=disabled
imageio.output.stream.tests.writeDouble=disabled
cmm.opts.profiles=1001
cmm.colorconv.data.fromRGB=disabled
cmm.colorconv.data.toRGB=disabled
cmm.colorconv.data.fromCIEXYZ=disabled
cmm.colorconv.data.toCIEXYZ=disabled
cmm.colorconv.ccop.ccopOptions.size=250
cmm.colorconv.ccop.ccopOptions.content=photo
cmm.colorconv.ccop.ccopOptions.srcType=INT_RGB
cmm.colorconv.ccop.ccopOptions.dstType=INT_RGB
cmm.colorconv.ccop.op_img=disabled
cmm.colorconv.ccop.op_rst=disabled
cmm.colorconv.ccop.op_draw=disabled
cmm.colorconv.embed.embedOptions.Images=512x512
cmm.colorconv.embed.embd_img_read=disabled
cmm.profiles.getHeader=disabled
cmm.profiles.getNumComponents=disabled

View File

@@ -0,0 +1,137 @@
export LC_ALL=C
ST=1 # sleep between iterations
# number of iterations (jvm spawned)
N=5
# number of repeats (within jvm)
R=3
type datamash 2>&1 > /dev/null ; ec=$?
if [ $ec -ne 0 ] ; then
echo "Missing datamash utility"
exit 1
fi
DATAMASH_CMD="datamash --format=%.2f -H count x min x q1 x median x q3 x max x mad x"
J2D_OPTS=""
OS=""
case "$OSTYPE" in
linux*) echo "Linux"
;;
darwin*) echo "OSX"
;;
*) echo "unknown: $OSTYPE"
exit 1
;;
esac
read -r -d '' RENDER_OPS_DOC << EOM
rendering_options:
-opengl # OpenGL pipeline (windows, linux, macOS)
-metal # Metal pipeline (macOS)
-vulkan # Vulkan pipeline (WLToolkit)
-accelsd # Vulkan full acceleration (WLToolkit, Vulkan)
-devnum num # Provide Vulkan device for rendering
-tk tk_name # AWT toolkit (linux: WLToolkit|XToolkit)
-scale # UI scale
-N num # Number of iterations (JVM runs)
-R num # Number of repeats in the test
EOM
while [ $# -ge 1 ] ; do
case "$1" in
-opengl) J2D_OPTS=$J2D_OPTS" -Dsun.java2d.opengl=true"
shift
;;
-metal) J2D_OPTS=$J2D_OPTS" -Dsun.java2d.metal=true"
shift
;;
-vulkan) J2D_OPTS=$J2D_OPTS" -Dsun.java2d.vulkan=true"
shift
;;
-accelsd) J2D_OPTS=$J2D_OPTS" -Dsun.java2d.vulkan.accelsd=true"
shift
;;
-devnum) shift
if [ $# -ge 1 ] ; then
J2D_OPTS=$J2D_OPTS" -Dsun.java2d.vulkan.deviceNumber="$1
shift
else
echo "Invalid parameters for -devnum option. Use: -devnum num"
exit 1
fi
;;
-tk) shift
if [ $# -ge 1 ] ; then
J2D_OPTS=$J2D_OPTS" -Dawt.toolkit.name="$1
shift
else
echo "Invalid parameters for -tk option. Use: -tk tkname"
exit 1
fi
;;
-N) shift
if [ $# -ge 1 ] ; then
N=$1
shift
else
echo "Invalid parameters for -N option. Use: -N <number>"
exit 1
fi
;;
-R) shift
if [ $# -ge 1 ] ; then
R=$1
shift
else
echo "Invalid parameters for -R option. Use: -R <number>"
exit 1
fi
;;
-scale) shift
if [ $# -ge 1 ] ; then
J2D_OPTS=$J2D_OPTS" -Dsun.java2d.uiScale="$1
shift
else
echo "Invalid parameters for -scale option. Use: -scale scale"
exit 1
fi
;;
-dSync) shift
if [ $# -ge 1 ] ; then
J2D_OPTS=$J2D_OPTS" -Dsun.java2d.metal.displaySync="$1
shift
else
echo "Invalid parameters for -dSync option. Use: -dSync [true|false]"
exit 1
fi
;;
-jdk) shift
if [ $# -ge 1 ] ; then
JAVA=$1/bin/java
shift
else
echo "Invalid parameters for -jdk option"
exit 1
fi
;;
*) break
;;
esac
done
if [ -z "$JAVA" ] ; then
BUILD_DIR=`find $BASE_DIR/../../../../build -name '*-release' -type d | head -n 1`
JAVA=`find $BUILD_DIR/images/jdk -name java -type f | head -n 1`
fi
JAVA_HOME=`dirname $JAVA`/../
"$JAVA" -version
LANG=C
WS_ROOT=$BASE_DIR/../../../..
echo "N: $N"
echo "R: $R"
echo "J2D_OPTS: $J2D_OPTS"

View File

@@ -0,0 +1,53 @@
#!/bin/bash
BASE_DIR=$(dirname "$0")
source $BASE_DIR/run_inc.sh
J2DBENCH_DIR=$WS_ROOT/src/demo/share/java2d/J2DBench
if [ -z "$J2DBENCH" ]; then
if [ ! -f "$J2DBENCH_DIR/dist/J2DBench.jar" ]; then
PATH=$JAVA_HOME/bin:$PATH make -C $J2DBENCH_DIR
fi
if [ ! -f "$J2DBENCH_DIR/dist/J2DBench.jar" ]; then
echo "Cannot build J2DBench. You may use J2DBench env variable instead pointing to the J2DBench.jar."
exit 1
fi
J2DBENCH=$J2DBENCH_DIR/dist/J2DBench.jar
fi
if [ $# -ne 1 ] ; then
echo "Usage: run_j2b.sh [rendering_options] bench_name"
echo
echo "bench_name: poly250 poly250-rand_col poly250-AA-rand_col"
echo ""
echo "$RENDER_OPS_DOC"
exit 2
fi
if [ ! -f "$BASE_DIR/j2dbopts_$1.txt" ]; then
echo "Unknown test: $1"
exit 1
fi
OPTS="j2dbopts_$1.txt"
#OPTS=j2dbopts_poly250.txt
#OPTS=j2dbopts_poly250-rand_col.txt
#OPTS=j2dbopts_poly250-AA-rand_col.txt
echo "OPTS: $OPTS"
for i in `seq $N`; do
if [ $i -eq 1 ]; then
echo x
fi
echo `$JAVA $J2D_OPTS -jar $J2DBENCH \
-batch -loadopts $BASE_DIR/$OPTS -saveres pl.res \
-title pl -desc pl | awk '/averaged/{print $3}' | head -n1`
if [ $i -ne $N ]; then
sleep $ST
fi
done | $DATAMASH_CMD | expand -t12

88
jb/project/tools/perf/run_rp.sh Executable file
View File

@@ -0,0 +1,88 @@
#!/bin/bash
#set -euo pipefail
#set -x
BASE_DIR=$(dirname "$0")
source $BASE_DIR/run_inc.sh
RENDERPERFTEST_DIR=$WS_ROOT/test/jdk/performance/client/RenderPerfTest
RENDERPERFTEST=""
if [ -z "$RENDERPERFTEST" ]; then
if [ ! -f "$RENDERPERFTEST_DIR/dist/RenderPerfTest.jar" ]; then
PATH=$JAVA_HOME/bin:$PATH make -C $RENDERPERFTEST_DIR
fi
if [ ! -f "$RENDERPERFTEST_DIR/dist/RenderPerfTest.jar" ]; then
echo "Cannot build RenderPerfTest. You may use RENDERPERFTEST env variable instead pointing to the RenderPerfTest.jar."
exit 1
fi
RENDERPERFTEST=$RENDERPERFTEST_DIR/dist/RenderPerfTest.jar
fi
TRACE=false
# removes leading hyphen
mode_param="${1/-}"
MODE="Robot"
while [ $# -ge 1 ] ; do
case "$1" in
-onscreen) MODE="Robot"
shift
;;
-volatile) MODE="Volatile"
shift
;;
-buffer) MODE="Buffer"
shift
;;
*) break
;;
esac
done
if [[ ($# -eq 1 && "$1" == "-help") || ($# -eq 0) ]] ; then
echo "Usage: run_rp.sh [rp_rendering_mode] [rendering_options] bench_name"
echo
echo "bench_name: ArgbSurfaceBlitImage ArgbSwBlitImage BgrSurfaceBlitImage BgrSwBlitImage"
echo " Image ImageAA Image_XOR VolImage VolImageAA"
echo " ClipFlatBox ClipFlatBoxAA ClipFlatOval ClipFlatOvalAA"
echo " FlatBox FlatBoxAA FlatOval FlatOvalAA FlatOval_XOR FlatQuad FlatQuadAA"
echo " RotatedBox RotatedBoxAA RotatedBox_XOR RotatedOval RotatedOvalAA"
echo " WiredBox WiredBoxAA WiredBubbles WiredBubblesAA WiredQuad WiredQuadAA"
echo " Lines LinesAA Lines_XOR"
echo " TextGray TextLCD TextLCD_XOR TextNoAA TextNoAA_XOR"
echo " LargeTextGray LargeTextLCD LargeTextNoAA WhiteTextGray WhiteTextLCD WhiteTextNoAA"
echo " LinGrad3RotatedOval LinGrad3RotatedOvalAA LinGradRotatedOval LinGradRotatedOvalAA"
echo " RadGrad3RotatedOval RadGrad3RotatedOvalAA"
echo ""
echo "rp_rendering_mode: "
echo " -onscreen : rendering to the window and check it using Robot"
echo " -volatile : rendering to volatile image (default)"
echo " -buffer : rendering to buffered image"
echo "$RENDER_OPS_DOC"
exit 2
fi
OPTS=""
# use time + repeat
OPTS="$OPTS -t -n=$N -e$MODE $1"
echo "OPTS: $OPTS"
echo "Unit: Milliseconds (not FPS), lower is better"
for i in `seq $R` ; do
if [ $i -eq 1 ]; then
echo x
fi
# echo "[debug] " + "test run"
# $JAVA $J2D_OPTS -DTRACE=$TRACE \
# -jar $RENDERPERFTEST $OPTS 2>&1 | awk '/'$1'/{print $3 }' | tee test_run.log
$JAVA $J2D_OPTS -DTRACE=$TRACE \
-jar $RENDERPERFTEST $OPTS -v 2>&1 | tee render_$1_${mode_param}_$i.log | grep -v "^#" | tail -n 1 | \
awk '{print $3 }'
if [ $i -ne $N ]; then
sleep $ST
fi
done | $DATAMASH_CMD | expand -t12 | tee render_$1_${mode_param}.log

39
jb/project/tools/perf/run_sm.sh Executable file
View File

@@ -0,0 +1,39 @@
#!/bin/bash
BASE_DIR=$(dirname "$0")
source $BASE_DIR/run_inc.sh
SWINGMARK_DIR=$WS_ROOT/test/jdk/performance/client/SwingMark
if [ -z "$SWINGMARK" ]; then
if [ ! -f "$SWINGMARK_DIR/dist/SwingMark.jar" ]; then
PATH=$JAVA_HOME/bin:$PATH make -C $SWINGMARK_DIR
fi
if [ ! -f "$SWINGMARK_DIR/dist/SwingMark.jar" ]; then
echo "Cannot build SwingMark. You may use SWINGMARK env variable instead pointing to the SwingMark.jar."
exit 1
fi
SWINGMARK=$SWINGMARK_DIR/dist/SwingMark.jar
fi
if [ $# -eq 1 -a "$1" == "--help" ] ; then
shift
echo "Usage: run_sm [rendering_options]"
echo ""
echo "$RENDER_OPS_DOC"
exit 0
fi
for i in `seq $N` ; do
if [ $i -eq 1 ]; then
echo x
fi
# SwingMark gives 1 global 'Score: <value>'
echo `$JAVA $J2D_OPTS -jar $BASE_DIR/../../../../test/jdk/performance/client/SwingMark/dist/SwingMark.jar \
-r $R -q -lf javax.swing.plaf.metal.MetalLookAndFeel | awk '/Score/{print $2}'`
if [ $i -ne $N ]; then
sleep $ST
fi
done | $DATAMASH_CMD | expand -t12

View File

@@ -68,7 +68,7 @@ $(CLASSLIST_FILE): $(INTERIM_IMAGE_DIR)/bin/java$(EXECUTABLE_SUFFIX) $(CLASSLIST
$(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@.raw \
-Duser.language=en -Duser.country=US \
-cp $(SUPPORT_OUTPUTDIR)/classlist.jar \
build.tools.classlist.HelloClasslist $(LOG_DEBUG)
build.tools.classlist.HelloClasslist jdk/src/classes/build/tools/classlist/clear.classlist $(LOG_DEBUG)
$(GREP) -v HelloClasslist $@.raw > $@.interim
$(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -Xshare:dump \
-XX:SharedClassListFile=$@.interim -XX:SharedArchiveFile=$@.jsa \
@@ -79,7 +79,7 @@ $(CLASSLIST_FILE): $(INTERIM_IMAGE_DIR)/bin/java$(EXECUTABLE_SUFFIX) $(CLASSLIST
-Duser.language=en -Duser.country=US \
--module-path $(SUPPORT_OUTPUTDIR)/classlist.jar \
-cp $(SUPPORT_OUTPUTDIR)/classlist.jar \
build.tools.classlist.HelloClasslist \
build.tools.classlist.HelloClasslist jdk/src/classes/build/tools/classlist/clear.classlist \
2> $(LINK_OPT_DIR)/stderr > $(JLI_TRACE_FILE) \
|| ( \
exitcode=$$? ; \

114
make/autoconf/lib-vulkan.m4 Normal file
View File

@@ -0,0 +1,114 @@
#
# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2025, JetBrains s.r.o.. 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
# 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.
#
################################################################################
# Setup vulkan
################################################################################
AC_DEFUN_ONCE([LIB_SETUP_VULKAN],
[
AC_ARG_WITH(vulkan, [AS_HELP_STRING([--with-vulkan],
[specify whether we use vulkan])])
AC_ARG_WITH(vulkan-include, [AS_HELP_STRING([--with-vulkan-include],
[specify directory for the vulkan include files ({with-vulkan-include}/vulkan/vulkan.h)])])
AC_ARG_WITH(vulkan-shader-compiler, [AS_HELP_STRING([--with-vulkan-shader-compiler],
[specify which shader compiler to use: glslc/glslangValidator])])
VULKAN_ENABLED=false
VULKAN_FLAGS=
# Find Vulkan SDK
if test "x$NEEDS_LIB_VULKAN" = xtrue || test "x${with_vulkan}" = xyes || test "x${with_vulkan_include}" != x ; then
# Check custom directory
if test "x${with_vulkan_include}" != x; then
AC_MSG_CHECKING([for ${with_vulkan_include}/vulkan/vulkan.h])
if test -s "${with_vulkan_include}/vulkan/vulkan.h"; then
VULKAN_ENABLED=true
VULKAN_FLAGS="-I${with_vulkan_include}"
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
AC_MSG_ERROR([Can't find 'vulkan/vulkan.h' under '${with_vulkan_include}'])
fi
fi
# Check $VULKAN_SDK
if test "x$VULKAN_ENABLED" = xfalse && test "x${VULKAN_SDK}" != x; then
AC_MSG_CHECKING([for ${VULKAN_SDK}/include/vulkan/vulkan.h])
if test -s "${VULKAN_SDK}/include/vulkan/vulkan.h"; then
VULKAN_ENABLED=true
VULKAN_FLAGS="-I${VULKAN_SDK}/include"
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
fi
# Check default /usr/include location
if test "x$VULKAN_ENABLED" = xfalse; then
AC_CHECK_HEADERS([vulkan/vulkan.h],
[ VULKAN_ENABLED=true ], [ break ]
)
fi
if test "x$VULKAN_ENABLED" = xfalse; then
# Vulkan SDK not found
HELP_MSG_MISSING_DEPENDENCY([vulkan])
AC_MSG_ERROR([Could not find vulkan! $HELP_MSG ])
fi
fi
# Find shader compiler - glslc or glslangValidator
if test "x$VULKAN_ENABLED" = xtrue; then
SHADER_COMPILER=
# Check glslc
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"
fi
# Check glslangValidator
if (test "x${with_vulkan_shader_compiler}" = x || test "x${with_vulkan_shader_compiler}" = xglslangValidator) && \
test "x$SHADER_COMPILER" = x; then
UTIL_LOOKUP_PROGS(GLSLANG, glslangValidator)
SHADER_COMPILER="$GLSLANG"
VULKAN_SHADER_COMPILER="glslangValidator --target-env vulkan1.2 -x -o"
fi
if test "x$SHADER_COMPILER" = x; then
# Compiler not found
VULKAN_ENABLED=false
VULKAN_FLAGS=
AC_MSG_ERROR([Can't find vulkan shader compiler])
fi
fi
AC_SUBST(VULKAN_ENABLED)
AC_SUBST(VULKAN_FLAGS)
AC_SUBST(VULKAN_SHADER_COMPILER)
])

View File

@@ -45,8 +45,6 @@ AC_DEFUN_ONCE([LIB_SETUP_WAYLAND],
fi
WAYLAND_CFLAGS=
WAYLAND_LIBS=
VULKAN_FLAGS=
VULKAN_ENABLED=false
else
WAYLAND_FOUND=no
@@ -79,7 +77,7 @@ AC_DEFUN_ONCE([LIB_SETUP_WAYLAND],
if test "x${with_wayland_lib}" != x; then
WAYLAND_LIBS="-L${with_wayland_lib} -lwayland-client -lwayland-cursor"
fi
if test "x$WAYLAND_FOUND" = xno; then
# Are the wayland headers installed in the default /usr/include location?
AC_CHECK_HEADERS([wayland-client.h wayland-cursor.h],
@@ -97,98 +95,7 @@ AC_DEFUN_ONCE([LIB_SETUP_WAYLAND],
AC_MSG_ERROR([Could not find wayland! $HELP_MSG ])
fi
# Checking for vulkan sdk
AC_ARG_WITH(vulkan, [AS_HELP_STRING([--with-vulkan],
[specify whether we use vulkan])])
AC_ARG_WITH(vulkan-include, [AS_HELP_STRING([--with-vulkan-include],
[specify directory for the vulkan include files ({with-vulkan-include}/vulkan/vulkan.h)])])
AC_ARG_WITH(vulkan-shader-compiler, [AS_HELP_STRING([--with-vulkan-shader-compiler],
[specify which shader compiler to use: glslc/glslangValidator])])
if test "x$SUPPORTS_LIB_VULKAN" = xfalse; then
if (test "x${with_vulkan}" != x && test "x${with_vulkan}" != xno) || \
(test "x${with_vulkan_include}" != x && test "x${with_vulkan_include}" != xno); then
AC_MSG_WARN([[vulkan not used, so --with-vulkan-include is ignored]])
fi
VULKAN_FLAGS=
VULKAN_ENABLED=false
else
# Do not build vulkan rendering pipeline by default
if (test "x${with_vulkan}" = x && test "x${with_vulkan_include}" = x) || \
test "x${with_vulkan}" = xno || test "x${with_vulkan_include}" = xno ; then
VULKAN_FLAGS=
VULKAN_ENABLED=false
else
VULKAN_FOUND=no
if test "x${with_vulkan_include}" != x; then
AC_MSG_CHECKING([for ${with_vulkan_include}/vulkan/vulkan.h])
if test -s "${with_vulkan_include}/vulkan/vulkan.h"; then
VULKAN_FOUND=yes
VULKAN_FLAGS="-DVK_USE_PLATFORM_WAYLAND_KHR -I${with_vulkan_include} -DVULKAN_ENABLED"
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
AC_MSG_ERROR([Can't find 'vulkan/vulkan.h' under '${with_vulkan_include}'])
fi
fi
if test "x$VULKAN_FOUND" = xno && test "x${VULKAN_SDK}" != x; then
AC_MSG_CHECKING([for ${VULKAN_SDK}/include/vulkan/vulkan.h])
if test -s "${VULKAN_SDK}/include/vulkan/vulkan.h"; then
VULKAN_FOUND=yes
VULKAN_FLAGS="-DVK_USE_PLATFORM_WAYLAND_KHR -I${VULKAN_SDK}/include -DVULKAN_ENABLED"
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
fi
if test "x$VULKAN_FOUND" = xno; then
# Check default /usr/include location
AC_CHECK_HEADERS([vulkan/vulkan.h],
[ VULKAN_FOUND=yes
VULKAN_FLAGS="-DVK_USE_PLATFORM_WAYLAND_KHR -DVULKAN_ENABLED"
],
[ VULKAN_FOUND=no; break ]
)
fi
if test "x$VULKAN_FOUND" = xno; then
HELP_MSG_MISSING_DEPENDENCY([vulkan])
AC_MSG_ERROR([Could not find vulkan! $HELP_MSG ])
else
# Find shader compiler - glslc or glslangValidator
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"
fi
if (test "x${with_vulkan_shader_compiler}" = x || test "x${with_vulkan_shader_compiler}" = xglslangValidator) && \
test "x$SHADER_COMPILER" = x; then
UTIL_LOOKUP_PROGS(GLSLANG, glslangValidator)
SHADER_COMPILER="$GLSLANG"
VULKAN_SHADER_COMPILER="glslangValidator --target-env vulkan1.2 -x -o"
fi
if test "x$SHADER_COMPILER" != x; then
VULKAN_ENABLED=true
else
AC_MSG_ERROR([Can't find shader compiler])
fi
fi
fi
fi
fi
AC_SUBST(VULKAN_FLAGS)
AC_SUBST(VULKAN_SHADER_COMPILER)
AC_SUBST(VULKAN_ENABLED)
AC_SUBST(WAYLAND_CFLAGS)
AC_SUBST(WAYLAND_LIBS)
])

View File

@@ -35,8 +35,9 @@ m4_include([lib-std.m4])
m4_include([lib-x11.m4])
m4_include([lib-speechd.m4])
m4_include([lib-nvdacontrollerclient.m4])
m4_include([lib-wayland.m4])
m4_include([lib-dbus.m4])
m4_include([lib-vulkan.m4])
m4_include([lib-wayland.m4])
m4_include([lib-tests.m4])
################################################################################
@@ -50,23 +51,23 @@ AC_DEFUN_ONCE([LIB_DETERMINE_DEPENDENCIES],
NEEDS_LIB_X11=false
NEEDS_LIB_SPEECHD=false
NEEDS_LIB_WAYLAND=false
SUPPORTS_LIB_VULKAN=false
elif test "x$ENABLE_HEADLESS_ONLY" = xtrue; then
# All other instances need X11, even if building headless only, libawt still
# needs X11 headers.
NEEDS_LIB_X11=true
NEEDS_LIB_SPEECHD=false
NEEDS_LIB_WAYLAND=false
SUPPORTS_LIB_VULKAN=false
else
# All other instances need X11 and wayland, even if building headless only, libawt still
# needs X11 headers.
NEEDS_LIB_X11=true
NEEDS_LIB_SPEECHD=true
NEEDS_LIB_WAYLAND=true
SUPPORTS_LIB_VULKAN=true
fi
# Vulkan is not built by default
NEEDS_LIB_VULKAN=false
# Check if fontconfig is needed
if test "x$OPENJDK_TARGET_OS" = xwindows || test "x$OPENJDK_TARGET_OS" = xmacosx; then
# No fontconfig support on windows or macosx
@@ -153,6 +154,7 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBRARIES],
LIB_SETUP_X11
LIB_SETUP_SPEECHD
LIB_SETUP_NVDACONTROLLERCLIENT
LIB_SETUP_VULKAN
LIB_SETUP_WAYLAND
LIB_SETUP_DBUS
LIB_TESTS_SETUP_GTEST
@@ -193,7 +195,7 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBRARIES],
if test "x$OPENJDK_TARGET_OS" = xwindows; then
BASIC_JVM_LIBS="$BASIC_JVM_LIBS kernel32.lib user32.lib gdi32.lib winspool.lib \
comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib powrprof.lib uuid.lib \
ws2_32.lib winmm.lib version.lib psapi.lib"
ws2_32.lib winmm.lib version.lib psapi.lib Synchronization.lib"
fi
LIB_SETUP_JVM_LIBS(BUILD)
LIB_SETUP_JVM_LIBS(TARGET)

View File

@@ -487,9 +487,9 @@ A11Y_JAWS_ANNOUNCING_ENABLED:=@A11Y_JAWS_ANNOUNCING_ENABLED@
WAYLAND_CFLAGS:=@WAYLAND_CFLAGS@
WAYLAND_LIBS:=@WAYLAND_LIBS@
VULKAN_ENABLED:=@VULKAN_ENABLED@
VULKAN_FLAGS:=@VULKAN_FLAGS@
VULKAN_SHADER_COMPILER:=@VULKAN_SHADER_COMPILER@
VULKAN_ENABLED:=@VULKAN_ENABLED@
# The lowest required version of macosx
MACOSX_VERSION_MIN=@MACOSX_VERSION_MIN@

View File

@@ -36,6 +36,8 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.net.InetAddress;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
@@ -153,6 +155,20 @@ public class HelloClasslist {
// an inconsistency in the classlist between builds (see JDK-8295951).
// To avoid the problem, load the class explicitly.
Class<?> striped64Class = Class.forName("java.util.concurrent.atomic.Striped64$Cell");
Files.lines(Path.of(args[0])).forEach(line -> {
if (line.isEmpty() || line.charAt(0) == '#') {
return;
}
try {
int index = line.indexOf(' ');
if (index <= 0) {
return;
}
String className = line.substring(0, index).replace('/', '.');
Class.forName(className);
} catch (Exception e) {
}
});
}
public HelloClasslist() {}

File diff suppressed because it is too large Load Diff

View File

@@ -73,7 +73,6 @@ ifeq ($(call isTargetOs, macosx), true)
sun/awt/wl \
sun/java2d/wl \
sun/java2d/x11 \
sun/java2d/vulkan \
sun/java2d/jules \
sun/java2d/xr \
com/sun/java/swing/plaf/gtk \
@@ -139,6 +138,13 @@ ifeq ($(call isTargetOs, windows macosx), false)
EXCLUDE_FILES += sun/awt/AWTCharset.java
endif
ifneq ($(VULKAN_ENABLED), true)
# WLToolkit needs Vulkan java classes for initialization
ifneq ($(call isTargetOs, linux), true)
EXCLUDES += sun/java2d/vulkan
endif
endif
# These files do not appear in the build result of the old build. This
# is because they are generated sources, but the AUTO_JAVA_FILES won't
# pick them up since they aren't generated when the source dirs are

View File

@@ -164,10 +164,23 @@ ifeq ($(call isTargetOs, windows), true)
LIBAWT_CFLAGS += $(A11Y_JAWS_ANNOUNCING_CFLAGS)
endif
# Setup Vulkan
ifeq ($(VULKAN_ENABLED), true)
LIBAWT_EXTRA_SRC += common/font common/java2d/vulkan
LIBAWT_EXTRA_HEADER_DIRS += common/font common/java2d/vulkan common/java2d
LIBAWT_CFLAGS += $(VULKAN_FLAGS)
LIBAWT_EXTRA_FILES += $(TOPDIR)/src/$(MODULE)/share/native/common/java2d/AccelTexturePool.c
LIBAWT_EXFILES += $(TOPDIR)/src/$(MODULE)/share/native/common/java2d/vulkan/VKStubs.c
VULKAN_EXCLUDES := vulkan
else
LIBAWT_EXTRA_FILES += $(TOPDIR)/src/$(MODULE)/share/native/common/java2d/vulkan/VKStubs.c
VULKAN_EXCLUDES := vulkan java2d/vulkan
endif
$(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
NAME := awt, \
EXTRA_SRC := $(LIBAWT_EXTRA_SRC), \
EXCLUDES := $(LIBAWT_EXCLUDES), \
EXTRA_FILES := $(LIBAWT_EXTRA_FILES), \
EXCLUDE_FILES := $(LIBAWT_EXFILES), \
OPTIMIZATION := HIGHEST, \
CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_CFLAGS) $(DBUS_CFLAGS), \
@@ -237,7 +250,7 @@ spirv-name = $(strip $1).h
ifeq ($(VULKAN_ENABLED), true)
$(eval $(call SetupCopyFiles, COMPILE_VULKAN_SHADERS, \
SRC := $(TOPDIR)/src/$(MODULE)/share/glsl/vulkan, \
FILES := $(call FindFiles, $(TOPDIR)/src/$(MODULE)/share/glsl/vulkan), \
FILES := $(filter-out %.glsl, $(call FindFiles, $(TOPDIR)/src/$(MODULE)/share/glsl/vulkan)), \
DEST := $(SUPPORT_OUTPUTDIR)/headers/java.desktop/vulkan/spirv, \
MACRO := compile-spirv, \
NAME_MACRO := spirv-name, \
@@ -245,14 +258,14 @@ ifeq ($(VULKAN_ENABLED), true)
VULKAN_SHADER_LIST = $(SUPPORT_OUTPUTDIR)/headers/java.desktop/vulkan/shader_list.h
$(VULKAN_SHADER_LIST): $(COMPILE_VULKAN_SHADERS)
> $(VULKAN_SHADER_LIST) $(NEWLINE) \
$(foreach f, $(patsubst $(TOPDIR)/src/$(MODULE)/share/glsl/vulkan/%,%,$(call FindFiles, $(TOPDIR)/src/$(MODULE)/share/glsl/vulkan)), \
$(foreach f, $(patsubst $(TOPDIR)/src/$(MODULE)/share/glsl/vulkan/%,%,$(filter-out %.glsl, $(call FindFiles, $(TOPDIR)/src/$(MODULE)/share/glsl/vulkan))), \
$(ECHO) SHADER_ENTRY\($(subst .,$(COMMA),$(subst /,_,$f))\) >> $(VULKAN_SHADER_LIST) $(NEWLINE) \
$(ECHO) '#ifdef INCLUDE_BYTECODE' >> $(VULKAN_SHADER_LIST) $(NEWLINE) \
$(ECHO) '#include "spirv/$f.h"' >> $(VULKAN_SHADER_LIST) $(NEWLINE) \
$(ECHO) BYTECODE_END >> $(VULKAN_SHADER_LIST) $(NEWLINE) \
$(ECHO) '#endif' >> $(VULKAN_SHADER_LIST) $(NEWLINE) \
)
$(BUILD_LIBAWT): $(VULKAN_SHADER_LIST)
$(BUILD_LIBAWT_TARGET_DEPS): $(VULKAN_SHADER_LIST)
endif
TARGETS += $(BUILD_LIBAWT)
@@ -382,18 +395,13 @@ ifeq ($(call isTargetOs, windows macosx), false)
common/wayland \
#
LIBAWT_WLAWT_EXCLUDES := medialib debug opengl x11
LIBAWT_WLAWT_EXCLUDES := medialib debug opengl x11 $(VULKAN_EXCLUDES)
LIBAWT_WLAWT_EXCLUDE_FILES := common/awt/X11Color.c common/awt/awt_Font.c
# Substitute Vulkan with stubs if disabled.
ifeq ($(VULKAN_ENABLED), false)
LIBAWT_WLAWT_EXCLUDES += vulkan
LIBAWT_WLAWT_EXTRA_FILES += $(TOPDIR)/src/$(MODULE)/share/native/common/java2d/vulkan/VKStubs.c
endif
LIBAWT_WLAWT_EXTRA_HEADER_DIRS := \
$(LIBAWT_DEFAULT_HEADER_DIRS) \
libawt_wlawt/awt \
$(SUPPORT_OUTPUTDIR)/gensrc/java.desktop/wayland \
include \
common/awt/debug \
common/awt/systemscale \
@@ -1103,8 +1111,7 @@ ifeq ($(call isTargetOs, macosx), true)
LIBAWT_LWAWT_CFLAGS := $(X_CFLAGS) $(X_LIBS)
LIBAWT_LWAWT_EXFILES := fontpath.c awt_Font.c X11Color.c
LIBAWT_LWAWT_EXCLUDES := \
$(TOPDIR)/src/$(MODULE)/share/native/common/java2d/vulkan \
LIBAWT_LWAWT_EXCLUDES := $(VULKAN_EXCLUDES) \
$(TOPDIR)/src/$(MODULE)/unix/native/common/awt/medialib \
#

View File

@@ -43,7 +43,10 @@ product(bool, UseOSErrorReporting, false, \
"Let VM fatal error propagate to the OS (ie. WER on Windows)") \
\
product(bool, UseCriticalSection, true, EXPERIMENTAL, \
"Use the critical section API instead of WaitForSingleObject")
"Use the critical section API instead of WaitForSingleObject") \
\
product(bool, UseModernSynchAPI, true, EXPERIMENTAL, \
"Use more modern WinAPI for synchronization instead of WaitForSingleObject")
// end of RUNTIME_OS_FLAGS

View File

@@ -5805,7 +5805,9 @@ int PlatformEvent::park(jlong Millis) {
// 1 => 0 : pass - return immediately
// 0 => -1 : block; then set _Event to 0 before returning
guarantee(_ParkHandle != nullptr , "Invariant");
if (!UseModernSynchAPI) {
guarantee(_ParkHandle != nullptr , "Invariant");
}
guarantee(Millis > 0 , "Invariant");
// CONSIDER: defer assigning a CreateEvent() handle to the Event until
@@ -5837,22 +5839,34 @@ int PlatformEvent::park(jlong Millis) {
// adjust Millis accordingly if we encounter a spurious wakeup.
const int MAXTIMEOUT = 0x10000000;
DWORD rv = WAIT_TIMEOUT;
while (_Event < 0 && Millis > 0) {
DWORD prd = Millis; // set prd = MAX (Millis, MAXTIMEOUT)
if (Millis > MAXTIMEOUT) {
prd = MAXTIMEOUT;
}
if (UseModernSynchAPI) {
HighResolutionInterval *phri = nullptr;
if (!ForceTimeHighResolution) {
phri = new HighResolutionInterval(prd);
phri = new HighResolutionInterval(Millis);
}
rv = ::WaitForSingleObject(_ParkHandle, prd);
assert(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT, "WaitForSingleObject failed");
if (rv == WAIT_TIMEOUT) {
Millis -= prd;
if ((v = Atomic::load_acquire(&_Event)) < 0) {
::WaitOnAddress(&_Event, &v, sizeof(_Event), Millis);
}
delete phri; // if it is null, harmless
} else {
DWORD rv = WAIT_TIMEOUT;
while (_Event < 0 && Millis > 0) {
DWORD prd = Millis; // set prd = MAX (Millis, MAXTIMEOUT)
if (Millis > MAXTIMEOUT) {
prd = MAXTIMEOUT;
}
HighResolutionInterval *phri = nullptr;
if (!ForceTimeHighResolution) {
phri = new HighResolutionInterval(prd);
}
rv = ::WaitForSingleObject(_ParkHandle, prd);
assert(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT, "WaitForSingleObject failed");
if (rv == WAIT_TIMEOUT) {
Millis -= prd;
}
delete phri; // if it is null, harmless
}
}
v = _Event;
_Event = 0;
@@ -5870,7 +5884,9 @@ void PlatformEvent::park() {
// 1 => 0 : pass - return immediately
// 0 => -1 : block; then set _Event to 0 before returning
guarantee(_ParkHandle != nullptr, "Invariant");
if (!UseModernSynchAPI) {
guarantee(_ParkHandle != nullptr, "Invariant");
}
// Invariant: Only the thread associated with the Event/PlatformEvent
// may call park().
// Consider: use atomic decrement instead of CAS-loop
@@ -5882,12 +5898,18 @@ void PlatformEvent::park() {
guarantee((v == 0) || (v == 1), "invariant");
if (v != 0) return;
// Do this the hard way by blocking ...
// TODO: consider a brief spin here, gated on the success of recent
// spin attempts by this thread.
while (_Event < 0) {
DWORD rv = ::WaitForSingleObject(_ParkHandle, INFINITE);
assert(rv == WAIT_OBJECT_0, "WaitForSingleObject failed");
if (UseModernSynchAPI) {
while ((v = Atomic::load_acquire(&_Event)) < 0) {
::WaitOnAddress(&_Event, &v, sizeof(_Event), INFINITE);
}
} else {
// Do this the hard way by blocking ...
// TODO: consider a brief spin here, gated on the success of recent
// spin attempts by this thread.
while (_Event < 0) {
DWORD rv = ::WaitForSingleObject(_ParkHandle, INFINITE);
assert(rv == WAIT_OBJECT_0, "WaitForSingleObject failed");
}
}
// Usually we'll find _Event == 0 at this point, but as
@@ -5899,7 +5921,9 @@ void PlatformEvent::park() {
}
void PlatformEvent::unpark() {
guarantee(_ParkHandle != nullptr, "Invariant");
if (!UseModernSynchAPI) {
guarantee(_ParkHandle != nullptr, "Invariant");
}
// Transitions for _Event:
// 0 => 1 : just return
@@ -5915,9 +5939,14 @@ void PlatformEvent::unpark() {
// from the first park() call after an unpark() call which will help
// shake out uses of park() and unpark() without condition variables.
if (Atomic::xchg(&_Event, 1) >= 0) return;
::SetEvent(_ParkHandle);
if (UseModernSynchAPI) {
if (Atomic::xchg(&_Event, 1) >= 0) return;
// Changed from -1 to 1; the target thread's WaitOnAddress() must return now
::WakeByAddressAll((PVOID) &_Event);
} else {
if (Atomic::xchg(&_Event, 1) >= 0) return;
::SetEvent(_ParkHandle);
}
}
@@ -5929,7 +5958,6 @@ void PlatformEvent::unpark() {
// use them directly.
void Parker::park(bool isAbsolute, jlong time) {
guarantee(_ParkHandle != nullptr, "invariant");
// First, demultiplex/decode time arguments
if (time < 0) { // don't wait
return;
@@ -5949,23 +5977,50 @@ void Parker::park(bool isAbsolute, jlong time) {
JavaThread* thread = JavaThread::current();
// Don't wait if interrupted or already triggered
if (thread->is_interrupted(false) ||
WaitForSingleObject(_ParkHandle, 0) == WAIT_OBJECT_0) {
ResetEvent(_ParkHandle);
return;
} else {
ThreadBlockInVM tbivm(thread);
OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
if (UseModernSynchAPI) {
// Don't wait if interrupted or already triggered
if (thread->is_interrupted(false)) {
Atomic::release_store(&_TargetValue, 0);
return;
} else {
int curHandle = Atomic::load_acquire(&_TargetValue);
if (curHandle > 0) {
// Already unparked
Atomic::release_store(&_TargetValue, 0);
return;
} else {
ThreadBlockInVM tbivm(thread);
OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
WaitForSingleObject(_ParkHandle, time);
ResetEvent(_ParkHandle);
// Spurios wakeups are fine as per Unsafe.park() promise
::WaitOnAddress(&_TargetValue, &curHandle, sizeof(_TargetValue), time);
Atomic::release_store(&_TargetValue, 0);
}
}
} else {
// Don't wait if interrupted or already triggered
if (thread->is_interrupted(false) ||
WaitForSingleObject(_ParkHandle, 0) == WAIT_OBJECT_0) {
ResetEvent(_ParkHandle);
return;
} else {
ThreadBlockInVM tbivm(thread);
OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
WaitForSingleObject(_ParkHandle, time);
ResetEvent(_ParkHandle);
}
}
}
void Parker::unpark() {
guarantee(_ParkHandle != nullptr, "invariant");
SetEvent(_ParkHandle);
if (UseModernSynchAPI) {
Atomic::release_store(&_TargetValue, 1);
::WakeByAddressAll(&_TargetValue);
} else {
guarantee(_ParkHandle != nullptr, "invariant");
SetEvent(_ParkHandle);
}
}
// Platform Monitor implementation

View File

@@ -41,8 +41,10 @@ class PlatformEvent : public CHeapObj<mtSynchronizer> {
public:
PlatformEvent() {
_Event = 0 ;
_ParkHandle = CreateEvent (nullptr, false, false, nullptr) ;
guarantee (_ParkHandle != nullptr, "invariant") ;
if (!UseModernSynchAPI) {
_ParkHandle = CreateEvent (nullptr, false, false, nullptr) ;
guarantee (_ParkHandle != nullptr, "invariant") ;
}
}
// Exercise caution using reset() and fired() - they may require MEMBARs
@@ -57,16 +59,23 @@ class PlatformEvent : public CHeapObj<mtSynchronizer> {
class PlatformParker {
NONCOPYABLE(PlatformParker);
protected:
protected:
HANDLE _ParkHandle;
int _TargetValue;
public:
public:
PlatformParker() {
_ParkHandle = CreateEvent (nullptr, true, false, nullptr) ;
guarantee(_ParkHandle != nullptr, "invariant") ;
if (UseModernSynchAPI) {
_TargetValue = 0;
} else {
_ParkHandle = CreateEvent (nullptr, true, false, nullptr) ;
guarantee(_ParkHandle != nullptr, "invariant") ;
}
}
~PlatformParker() {
CloseHandle(_ParkHandle);
if (! UseModernSynchAPI) {
CloseHandle(_ParkHandle);
}
}
};

View File

@@ -404,7 +404,6 @@ bool SharedClassPathEntry::validate(bool is_class_path) const {
// filters out any archived module classes that do not have a matching runtime
// module path location.
log_warning(cds)("Required classpath entry does not exist: %s", name);
ok = false;
} else if (is_dir()) {
if (!os::dir_is_empty(name)) {
log_warning(cds)("directory is not empty: %s", name);
@@ -844,6 +843,20 @@ bool FileMapInfo::validate_boot_class_paths() {
num = rp_len;
}
mismatch = check_paths(1, num, rp_array, 0, 0);
if (mismatch) {
// To facilitate app deployment, we allow the JAR files to be moved *together* to
// a different location, as long as they are still stored under the same directory
// structure. E.g., the following is OK.
// Extends JDK-8279366 to boot classpath.
unsigned int dumptime_prefix_len = header()->common_app_classpath_prefix_size();
unsigned int runtime_prefix_len = longest_common_app_classpath_prefix_len(num, rp_array);
if (dumptime_prefix_len != 0 || runtime_prefix_len != 0) {
log_info(cds, path)("LCP length for boot classpath (dumptime: %u, runtime: %u)",
dumptime_prefix_len, runtime_prefix_len);
mismatch = check_paths(1, num, rp_array,
dumptime_prefix_len, runtime_prefix_len);
}
}
} else {
// create_path_array() ignores non-existing paths. Although the dump time and runtime boot classpath lengths
// are the same initially, after the call to create_path_array(), the runtime boot classpath length could become

View File

@@ -34,17 +34,31 @@
#include "oops/oop.inline.hpp"
#include "utilities/macros.hpp"
#include "utilities/copy.hpp"
#include "runtime/fieldDescriptor.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
DcevmSharedGC* DcevmSharedGC::_static_instance = nullptr;
void DcevmSharedGC::create_static_instance() {
_static_instance = new DcevmSharedGC();
}
void DcevmSharedGC::destroy_static_instance() {
if (_static_instance != nullptr) {
delete _static_instance;
_static_instance = nullptr;
}
}
void DcevmSharedGC::copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_oops, bool must_be_new) {
if (rescued_oops != NULL) {
if (rescued_oops != nullptr) {
copy_rescued_objects_back(rescued_oops, 0, rescued_oops->length(), must_be_new);
}
}
// (DCEVM) Copy the rescued objects to their destination address after compaction.
void DcevmSharedGC::copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_oops, int from, int to, bool must_be_new) {
if (rescued_oops != NULL) {
if (rescued_oops != nullptr) {
for (int i=from; i < to; i++) {
HeapWord* rescued_ptr = rescued_oops->at(i);
oop rescued_obj = cast_to_oop(rescued_ptr);
@@ -52,10 +66,10 @@ void DcevmSharedGC::copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_
size_t size = rescued_obj->size();
oop new_obj = rescued_obj->forwardee();
assert(!must_be_new || rescued_obj->klass()->new_version() != NULL, "Just checking");
assert(!must_be_new || rescued_obj->klass()->new_version() != nullptr, "Just checking");
Klass* new_klass = rescued_obj->klass()->new_version();
if (new_klass!= NULL) {
if (new_klass->update_information() != NULL) {
if (new_klass!= nullptr) {
if (new_klass->update_information() != nullptr) {
DcevmSharedGC::update_fields(rescued_obj, new_obj);
} else {
rescued_obj->set_klass(new_klass);
@@ -69,11 +83,10 @@ void DcevmSharedGC::copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_
assert(oopDesc::is_oop(new_obj), "must be a valid oop");
}
}
}
void DcevmSharedGC::clear_rescued_objects_resource(GrowableArray<HeapWord*>* rescued_oops) {
if (rescued_oops != NULL) {
if (rescued_oops != nullptr) {
for (int i=0; i < rescued_oops->length(); i++) {
HeapWord* rescued_ptr = rescued_oops->at(i);
size_t size = cast_to_oop(rescued_ptr)->size();
@@ -84,7 +97,7 @@ void DcevmSharedGC::clear_rescued_objects_resource(GrowableArray<HeapWord*>* res
}
void DcevmSharedGC::clear_rescued_objects_heap(GrowableArray<HeapWord*>* rescued_oops) {
if (rescued_oops != NULL) {
if (rescued_oops != nullptr) {
for (int i=0; i < rescued_oops->length(); i++) {
HeapWord* rescued_ptr = rescued_oops->at(i);
FREE_C_HEAP_ARRAY(HeapWord, rescued_ptr);
@@ -96,7 +109,7 @@ void DcevmSharedGC::clear_rescued_objects_heap(GrowableArray<HeapWord*>* rescued
// (DCEVM) Update instances of a class whose fields changed.
void DcevmSharedGC::update_fields(oop q, oop new_location) {
assert(q->klass()->new_version() != NULL, "class of old object must have new version");
assert(q->klass()->new_version() != nullptr, "class of old object must have new version");
Klass* old_klass_oop = q->klass();
Klass* new_klass_oop = q->klass()->new_version();
@@ -107,7 +120,7 @@ void DcevmSharedGC::update_fields(oop q, oop new_location) {
size_t size = q->size_given_klass(old_klass);
size_t new_size = q->size_given_klass(new_klass);
HeapWord* tmp = NULL;
HeapWord* tmp = nullptr;
oop tmp_obj = q;
// Save object somewhere, there is an overlap in fields
@@ -122,39 +135,141 @@ void DcevmSharedGC::update_fields(oop q, oop new_location) {
q->set_klass(new_klass_oop);
int *cur = new_klass_oop->update_information();
assert(cur != NULL, "just checking");
DcevmSharedGC::update_fields(new_location, q, cur);
assert(cur != nullptr, "just checking");
_static_instance->update_fields(new_location, q, cur, false);
if (tmp != NULL) {
if (tmp != nullptr) {
FREE_RESOURCE_ARRAY(HeapWord, tmp, size);
}
}
void DcevmSharedGC::update_fields(oop new_location, oop tmp_obj, int *cur) {
assert(cur != NULL, "just checking");
char* to = (char*)cast_from_oop<HeapWord*>(new_location);
void DcevmSharedGC::update_fields(oop new_obj, oop old_obj, int* cur, bool do_compat_check) {
assert(cur != nullptr, "just checking");
char* to = (char*)cast_from_oop<HeapWord*>(new_obj);
char* src_base = (char *)cast_from_oop<HeapWord*>(old_obj);
while (*cur != 0) {
int size = *cur;
if (size > 0) {
int raw = *cur;
if (raw > 0) {
cur++;
int offset = *cur;
HeapWord* from = (HeapWord*)(((char *)cast_from_oop<HeapWord*>(tmp_obj)) + offset);
if (size == HeapWordSize) {
*((HeapWord*)to) = *from;
} else if (size == HeapWordSize * 2) {
*((HeapWord*)to) = *from;
*(((HeapWord*)to) + 1) = *(from + 1);
int src_offset = *cur;
HeapWord* from = (HeapWord*)(src_base + src_offset);
bool compat_check = do_compat_check && ((raw & UpdateInfoCompatFlag) != 0);
int size = (raw & UpdateInfoLengthMask);
if (!compat_check) {
if (size == HeapWordSize) {
*((HeapWord *) to) = *from;
} else if (size == HeapWordSize * 2) {
*((HeapWord *) to) = *from;
*(((HeapWord *) to) + 1) = *(from + 1);
} else {
Copy::conjoint_jbytes(from, to, size);
}
} else {
Copy::conjoint_jbytes(from, to, size);
assert(size == heapOopSize, "Must be one oop");
int dst_offset = (int)(to - (char*)cast_from_oop<HeapWord*>(new_obj));
oop obj = old_obj->obj_field(src_offset);
if (obj == nullptr) {
new_obj->obj_field_put(dst_offset, nullptr);
} else {
bool compatible = is_compatible(new_obj, dst_offset, obj);
new_obj->obj_field_put(dst_offset, compatible ? obj : (oop)nullptr);
}
}
to += size;
cur++;
} else {
assert(size < 0, "");
int skip = -*cur;
Copy::fill_to_bytes(to, skip, 0);
to += skip;
Copy::fill_to_bytes(to, -raw, 0);
to += -raw;
cur++;
}
}
}
void DcevmSharedGC::update_fields_in_old(oop old_obj, int* cur) {
assert(cur != nullptr, "just checking");
int dst_offset = 0;
while (*cur != 0) {
int raw = *cur;
if (raw > 0) {
cur++;
int size = (raw & UpdateInfoLengthMask);
if ((raw & UpdateInfoCompatFlag) != 0) {
assert(size == heapOopSize, "Must be one oop");
int src_offset = *cur;
oop obj = old_obj->obj_field(src_offset);
if (obj != nullptr) {
bool compatible = is_compatible(old_obj, dst_offset, obj);
if (!compatible) {
old_obj->obj_field_put(src_offset, nullptr);
}
}
}
dst_offset += size;
cur++;
} else {
dst_offset += -raw;
cur++;
}
}
}
static inline bool signature_matches_name(Symbol* sig, Symbol* name) {
const int sig_len = sig->utf8_length();
const int name_len = name->utf8_length();
if (sig_len != name_len + 2) {
return false;
}
const u1* s = sig ->bytes();
const u1* n = name->bytes();
return (s[0] == 'L' && s[sig_len - 1] == ';' && memcmp(s + 1, n, name_len) == 0);
}
bool DcevmSharedGC::is_compatible(oop fld_holder, int fld_offset, oop fld_val) {
assert(oopDesc::is_oop(fld_val), "val has corrupted header");
bool result = false;
Symbol *sig_wanted;
InstanceKlass* holder_ik = InstanceKlass::cast(fld_holder->klass()->newest_version());
Symbol** sig_cached = _field_sig_table->get({holder_ik, fld_offset});
if (sig_cached != nullptr) {
sig_wanted = *sig_cached;
} else {
fieldDescriptor fd_new;
bool ok = holder_ik->find_field_from_offset(fld_offset, false, &fd_new);
assert(ok, "Must exist");
sig_wanted = fd_new.signature();
_field_sig_table->put({holder_ik, fld_offset}, sig_wanted);
}
InstanceKlass *ik = InstanceKlass::cast(fld_val->klass()->newest_version());
bool* hit = _compat_table->get({ik, sig_wanted });
if (hit != nullptr) {
result = *hit;
} else {
InstanceKlass* scan = ik;
while (scan != nullptr && !result) {
if (signature_matches_name(sig_wanted, scan->name())) {
result = true;
break;
}
Array<InstanceKlass*>* ifaces = scan->local_interfaces();
for (int j = 0; j < ifaces->length(); ++j) {
if (signature_matches_name(sig_wanted, ifaces->at(j)->name())) {
result = true;
break;
}
}
scan = (scan->super() != nullptr) ? InstanceKlass::cast(scan->super()) : nullptr;
}
_compat_table->put({ik, sig_wanted }, result);
}
return result;
}

View File

@@ -33,16 +33,76 @@
#include "runtime/timer.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/stack.hpp"
#include "utilities/resourceHash.hpp"
// Shared GC code used from different GC (Serial, CMS, G1) on enhanced redefinition
class DcevmSharedGC : AllStatic {
public:
class DcevmSharedGC : public CHeapObj<mtInternal> {
private:
struct SymbolKey {
InstanceKlass* ik;
Symbol* dst_sig;
};
static unsigned symbol_hash(const SymbolKey& k) {
return (uintptr_t)k.ik ^ (uintptr_t)k.dst_sig;
}
static bool symbol_eq(const SymbolKey& a, const SymbolKey& b) {
return a.ik == b.ik && a.dst_sig == b.dst_sig;
}
typedef ResourceHashtable<SymbolKey, bool, 512,
AnyObj::C_HEAP, mtInternal,
&symbol_hash, &symbol_eq> CompatTable;
struct OffsetKey {
InstanceKlass* ik;
int offset;
};
static unsigned offset_hash(const OffsetKey& k) {
return uintptr_t(k.ik) ^ k.offset;
}
static bool offset_eq(const OffsetKey& a, const OffsetKey& b) {
return a.ik == b.ik && a.offset == b.offset;
}
typedef ResourceHashtable<OffsetKey, Symbol*, 512,
AnyObj::C_HEAP, mtInternal,
&offset_hash, &offset_eq> FieldSigTable;
CompatTable* _compat_table;
FieldSigTable* _field_sig_table;
static DcevmSharedGC* _static_instance;
public:
// ------------------------------------------------------------------
// update info flags
//
// bit 31 : sign bit (<0 = fill, >0 = copy)
// bit 30 : UpdateInfoCompatFlag copy segment requires per-oop compatibility check
// bits 0-29 : raw byte length of the segment
// ------------------------------------------------------------------
static const int UpdateInfoCompatFlag = 1U << 30;
static const int UpdateInfoLengthMask = ~(1U << 31 | UpdateInfoCompatFlag);
DcevmSharedGC() {
_compat_table = new (mtInternal) CompatTable();
_field_sig_table = new (mtInternal) FieldSigTable();
}
~DcevmSharedGC() {
delete _compat_table;
delete _field_sig_table;
}
static void create_static_instance();
static void destroy_static_instance();
static void copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_oops, bool must_be_new);
static void copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_oops, int from, int to, bool must_be_new);
static void clear_rescued_objects_resource(GrowableArray<HeapWord*>* rescued_oops);
static void clear_rescued_objects_heap(GrowableArray<HeapWord*>* rescued_oops);
static void update_fields(oop q, oop new_location);
static void update_fields(oop new_location, oop tmp_obj, int *cur);
bool is_compatible(oop fld_holder, int fld_offset, oop fld_val);
void update_fields(oop new_obj, oop old_obj, int *cur, bool do_compat_check);
void update_fields_in_old(oop old_obj, int *cur);
};
#endif

View File

@@ -93,7 +93,9 @@ class SATBMarkQueueSet: public PtrQueueSet {
size_t _buffer_enqueue_threshold;
// SATB is only active during marking. Enqueuing is only done when active.
bool _all_active;
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, 4 * sizeof(size_t));
#if ! (defined(_WIN64) && DEFAULT_CACHE_LINE_SIZE == 32)
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, 4 * sizeof(size_t));
#endif
BufferNode* get_completed_buffer();
void abandon_completed_buffers();

View File

@@ -369,18 +369,6 @@ void ConstantPool::remove_unshareable_info() {
// we always set _on_stack to true to avoid having to change _flags during runtime.
_flags |= (_on_stack | _is_shared);
if (!_pool_holder->is_linked() && !_pool_holder->verified_at_dump_time()) {
return;
}
// Resolved references are not in the shared archive.
// Save the length for restoration. It is not necessarily the same length
// as reference_map.length() if invokedynamic is saved. It is needed when
// re-creating the resolved reference array if archived heap data cannot be map
// at runtime.
set_resolved_reference_length(
resolved_references() != nullptr ? resolved_references()->length() : 0);
set_resolved_references(OopHandle());
bool archived = false;
for (int index = 1; index < length(); index++) { // Index 0 is unused
switch (tag_at(index).value()) {
@@ -403,6 +391,19 @@ void ConstantPool::remove_unshareable_info() {
}
}
if (!_pool_holder->is_linked() && !_pool_holder->verified_at_dump_time()) {
return;
}
// Resolved references are not in the shared archive.
// Save the length for restoration. It is not necessarily the same length
// as reference_map.length() if invokedynamic is saved. It is needed when
// re-creating the resolved reference array if archived heap data cannot be map
// at runtime.
set_resolved_reference_length(
resolved_references() != nullptr ? resolved_references()->length() : 0);
set_resolved_references(OopHandle());
if (cache() != nullptr) {
// cache() is null if this class is not yet linked.
cache()->remove_unshareable_info();

View File

@@ -371,6 +371,11 @@ void InstanceKlass::set_nest_host(InstanceKlass* host) {
// Can't assert this as package is not set yet:
// assert(is_same_class_package(host), "proposed host is in wrong package");
// set dynamic nest host
_nest_host = host;
// Record dependency to keep nest host from being unloaded before this class.
ClassLoaderData* this_key = class_loader_data();
if (log_is_enabled(Trace, class, nestmates)) {
ResourceMark rm;
const char* msg = "";
@@ -379,18 +384,19 @@ void InstanceKlass::set_nest_host(InstanceKlass* host) {
msg = "(the NestHost attribute in the current class is ignored)";
} else if (_nest_members != nullptr && _nest_members != Universe::the_empty_short_array()) {
msg = "(the NestMembers attribute in the current class is ignored)";
} else if (this_key == nullptr) {
msg = "(the NestMembers classloader data in the current class is ignored)";
}
log_trace(class, nestmates)("Injected type %s into the nest of %s %s",
this->external_name(),
host->external_name(),
msg);
}
// set dynamic nest host
_nest_host = host;
// Record dependency to keep nest host from being unloaded before this class.
ClassLoaderData* this_key = class_loader_data();
assert(this_key != nullptr, "sanity");
this_key->record_dependency(host);
if (this_key != nullptr) {
this_key->record_dependency(host);
}
}
// check if 'this' and k are nestmates (same nest_host), or k is our nest_host,
@@ -1781,6 +1787,18 @@ bool InstanceKlass::find_field_from_offset(int offset, bool is_static, fieldDesc
}
bool InstanceKlass::find_local_field_by_name(Symbol* name, fieldDescriptor* fd) const {
for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
Symbol* f_name = fs.name();
if (f_name == name) {
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.index());
return true;
}
}
return false;
}
void InstanceKlass::methods_do(void f(Method* method)) {
// Methods aren't stable until they are loaded. This can be read outside
// a lock through the ClassLoaderData for profiling

View File

@@ -566,6 +566,7 @@ public:
bool find_local_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const;
bool find_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const;
bool find_local_field_by_name(Symbol* name, fieldDescriptor* fd) const;
private:
inline static int quick_search(const Array<Method*>* methods, const Symbol* name);

View File

@@ -196,13 +196,14 @@ void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word
// The constructor is also used from CppVtableCloner,
// which doesn't zero out the memory before calling the constructor.
Klass::Klass(KlassKind kind) : _kind(kind),
_old_version(NULL),
_new_version(NULL),
_old_version(nullptr),
_new_version(nullptr),
_redefinition_flags(Klass::NoRedefinition),
_is_redefining(false),
_update_information(NULL),
_update_information(nullptr),
_is_copying_backwards(false),
_is_rolled_back(false),
_compat_check_field_offsets(nullptr),
_shared_class_path_index(-1) {
CDS_ONLY(_shared_class_flags = 0;)
CDS_JAVA_HEAP_ONLY(_archived_mirror_index = -1;)

View File

@@ -167,18 +167,18 @@ class Klass : public Metadata {
JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
// Advanced class redefinition
// Enhanced class redefinition
// Old version (used in advanced class redefinition)
Klass* _old_version;
// New version (used in advanced class redefinition)
Klass* _new_version;
int _redefinition_flags; // Level of class redefinition
bool _is_redefining;
int* _update_information;
bool _is_copying_backwards; // Does the class need to copy fields backwards? => possibly overwrite itself?
bool _is_rolled_back; // true if class was rolled back in redefinition
// offsets of fields used in compatibility check
GrowableArray<int>* volatile _compat_check_field_offsets;
private:
// This is an index into FileMapHeader::_shared_path_table[], to
@@ -419,6 +419,13 @@ protected:
void set_copying_backwards(bool b) { _is_copying_backwards = b; }
bool is_rolled_back() { return _is_rolled_back; }
void set_rolled_back(bool b) { _is_rolled_back = b;}
GrowableArray<int>* compat_check_field_offsets() const { return Atomic::load_acquire(&_compat_check_field_offsets); }
GrowableArray<int>* set_compat_check_field_offsets(GrowableArray<int>* offsets) {
return Atomic::cmpxchg(&_compat_check_field_offsets, (GrowableArray<int>*) nullptr, offsets);
}
void clear_compat_check_field_offsets() {
_compat_check_field_offsets = nullptr;
}
protected: // internal accessors
void set_subklass(Klass* s);
@@ -431,7 +438,8 @@ protected:
ModifyClassSize = ModifyClass << 1, // The size of the class meta data changes.
ModifyInstances = ModifyClassSize << 1, // There are change to the instance format.
ModifyInstanceSize = ModifyInstances << 1, // The size of instances changes.
RemoveSuperType = ModifyInstanceSize << 1, // A super type of this class is removed.
RemoveInterface = ModifyInstanceSize << 1, // A super type of this class is removed.
RemoveSuperType = RemoveInterface << 1, // A super type of this class is removed.
MarkedAsAffected = RemoveSuperType << 1, // This class has been marked as an affected class.
PrimaryRedefine = MarkedAsAffected << 1 // This class is from primary redefinition set
};

View File

@@ -108,6 +108,7 @@ VM_EnhancedRedefineClasses::VM_EnhancedRedefineClasses(jint class_count, const j
VM_GC_Operation(Universe::heap()->total_collections(), GCCause::_heap_inspection, Universe::heap()->total_full_collections(), true) {
_new_classes = nullptr;
_affected_klasses = nullptr;
_removed_interfaces = nullptr;
_class_count = class_count;
_class_defs = class_defs;
_class_load_kind = class_load_kind;
@@ -128,7 +129,6 @@ static inline InstanceKlass* get_ik(jclass def) {
// - Start mark&sweep GC.
// - true if success, otherwise all chnages are rollbacked.
bool VM_EnhancedRedefineClasses::doit_prologue() {
if (_class_count == 0) {
_res = JVMTI_ERROR_NONE;
return false;
@@ -202,7 +202,36 @@ bool VM_EnhancedRedefineClasses::doit_prologue() {
// Closer for static fields - copy value from old class to the new class.
class FieldCopier : public FieldClosure {
public:
private:
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
public:
FieldCopier(VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces)
: _removed_interfaces(removed_interfaces) {}
bool is_compatible(oop fld_holder, int fld_offset, Symbol* sig_new) {
oop oop = fld_holder->obj_field(fld_offset);
if (oop != nullptr) {
Klass* k = oop->klass();
if (k->is_instance_klass() && k->is_redefining()) {
InstanceKlass *scan = InstanceKlass::cast(k);
while (scan != nullptr) {
if (sig_new->equals(scan->signature_name())) {
return true;
}
Array<InstanceKlass*>* local_interfaces = scan->local_interfaces();
for (int j = 0; j < local_interfaces->length(); j++) {
Klass* iface = local_interfaces->at(j);
if (sig_new->equals(iface->signature_name())) {
return true;
}
}
scan = (scan->super() != nullptr) ? InstanceKlass::cast(scan->super()) : nullptr;
}
}
}
return false;
}
void do_field(fieldDescriptor* fd) {
InstanceKlass* cur = InstanceKlass::cast(fd->field_holder());
oop cur_oop = cur->java_mirror();
@@ -211,19 +240,36 @@ class FieldCopier : public FieldClosure {
oop old_oop = old->java_mirror();
fieldDescriptor result;
bool found = old->find_local_field(fd->name(), fd->signature(), &result);
Symbol* sig_new = fd->signature();
bool found = old->find_local_field_by_name(fd->name(), &result);
if (found && result.is_static()) {
log_trace(redefine, class, obsolete, metadata)("Copying static field value for field %s old_offset=%d new_offset=%d",
fd->name()->as_C_string(), result.offset(), fd->offset());
memcpy(cur_oop->field_addr<HeapWord>(fd->offset()),
old_oop->field_addr<HeapWord>(result.offset()),
type2aelembytes(fd->field_type()));
Symbol* sig_old = result.signature();
bool compatible = false;
// Static fields may have references to java.lang.Class
if (fd->field_type() == T_OBJECT) {
oop oop = cur_oop->obj_field(fd->offset());
if (oop != nullptr && oop->is_instance() && InstanceKlass::cast(oop->klass())->is_mirror_instance_klass()) {
Klass* klass = java_lang_Class::as_Klass(oop);
if (sig_new == sig_old) {
if (_removed_interfaces != nullptr && _removed_interfaces->contains(sig_old) && fd->field_type() == T_OBJECT && result.field_type() == T_OBJECT) {
compatible = is_compatible(old_oop, result.offset(), sig_new);
} else {
compatible = true;
}
} else {
if (fd->field_type() == T_OBJECT && result.field_type() == T_OBJECT) {
compatible = is_compatible(old_oop, result.offset(), sig_new);
}
}
if (compatible) {
log_trace(redefine, class, obsolete, metadata)("Copying static field value for field %s old_offset=%d new_offset=%d",
fd->name()->as_C_string(), result.offset(), fd->offset());
memcpy(cur_oop->field_addr<HeapWord>(fd->offset()),
old_oop->field_addr<HeapWord>(result.offset()),
type2aelembytes(fd->field_type()));
// Static fields may have references to java.lang.Class
if (fd->field_type() == T_OBJECT) {
oop oop = cur_oop->obj_field(fd->offset());
if (oop != nullptr && oop->is_instance() && InstanceKlass::cast(oop->klass())->is_mirror_instance_klass()) {
Klass *klass = java_lang_Class::as_Klass(oop);
if (klass != nullptr && klass->is_instance_klass()) {
assert(oop == InstanceKlass::cast(klass)->java_mirror(), "just checking");
if (klass->new_version() != nullptr) {
@@ -233,10 +279,36 @@ class FieldCopier : public FieldClosure {
}
}
}
} else {
log_trace(redefine,class, obsolete, metadata)("Skipping incompatible static field %s, old_signature=%s, new_signature=%s",
fd->name()->as_C_string(), sig_old->as_C_string(), sig_new->as_C_string());
}
}
}
};
class FieldCompatibilityChecker : public FieldClosure {
private:
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
GrowableArray<int>* _compat_check_field_offsets;
public:
FieldCompatibilityChecker(VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces)
: _removed_interfaces(removed_interfaces), _compat_check_field_offsets(nullptr) {}
void do_field(fieldDescriptor* fd) {
Symbol *sig_new = fd->signature();
if (_removed_interfaces->contains(fd->signature())) {
if (_compat_check_field_offsets == nullptr) {
_compat_check_field_offsets = new (mtInternal) GrowableArray<int>(2, mtInternal);
}
_compat_check_field_offsets->append(fd->offset());
}
}
GrowableArray<int>* compat_check_field_offsets() {
return _compat_check_field_offsets;
}
};
// TODO: review...
void VM_EnhancedRedefineClasses::mark_as_scavengable(nmethod* nm) {
@@ -442,15 +514,23 @@ class ChangePointersOopClosure : public BasicOopIterateClosure {
// - otherwise set the _needs_instance_update flag, we need to do full GC
// and reshuffle object positions durring mark&sweep
class ChangePointersObjectClosure : public ObjectClosure {
private:
private:
OopIterateClosure *_closure;
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
DcevmSharedGC* _dcevm_shared_gc;
bool _needs_instance_update;
oop _tmp_obj;
size_t _tmp_obj_size;
public:
ChangePointersObjectClosure(OopIterateClosure *closure) : _closure(closure), _needs_instance_update(false), _tmp_obj(nullptr), _tmp_obj_size(0) {}
ChangePointersObjectClosure(OopIterateClosure *closure, VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces)
: _closure(closure), _removed_interfaces(removed_interfaces), _needs_instance_update(false), _tmp_obj(nullptr), _tmp_obj_size(0) {
_dcevm_shared_gc = new DcevmSharedGC();
}
~ChangePointersObjectClosure() {
delete _dcevm_shared_gc;
}
bool needs_instance_update() {
return _needs_instance_update;
@@ -465,6 +545,36 @@ public:
Copy::aligned_disjoint_words(cast_from_oop<HeapWord*>(o), cast_from_oop<HeapWord*>(_tmp_obj), size);
}
void do_compat_check_field_offsets(oop obj) {
// Non-redefined class may store fields of redefined types
// check field-level compatibility to avoid invalid accesses.
GrowableArray<int>* fields = obj->klass()->compat_check_field_offsets();
if (fields == nullptr) {
FieldCompatibilityChecker fld_compat_check(_removed_interfaces);
InstanceKlass::cast(obj->klass())->do_nonstatic_fields(&fld_compat_check);
fields = fld_compat_check.compat_check_field_offsets();
if (fields != nullptr) {
GrowableArray<int>* old = obj->klass()->set_compat_check_field_offsets(fields);
if (old != nullptr) {
delete fields;
fields = old;
}
} else {
fields = reinterpret_cast<GrowableArray<int>*>(-1);
obj->klass()->set_compat_check_field_offsets(fields);
}
}
if (reinterpret_cast<intptr_t>(fields) != -1) {
for (int i = 0; i < fields->length(); i++) {
int fld_offset = fields->at(i);
oop fld_val = obj->obj_field(fld_offset);
if (fld_val != nullptr && !_dcevm_shared_gc->is_compatible(obj, fld_offset, fld_val)) {
obj->obj_field_put(fld_offset, nullptr);
}
}
}
}
virtual void do_object(oop obj) {
if (obj->is_instance() && InstanceKlass::cast(obj->klass())->is_mirror_instance_klass()) {
// static fields may have references to old java.lang.Class instances, update them
@@ -473,6 +583,9 @@ public:
//instanceMirrorKlass::oop_fields_iterate(obj, _closure);
} else {
obj->oop_iterate(_closure);
if (_removed_interfaces != nullptr && obj->klass()->is_instance_klass() && obj->klass()->new_version() == nullptr) {
do_compat_check_field_offsets(obj);
}
}
if (obj->klass()->new_version() != nullptr) {
@@ -482,6 +595,7 @@ public:
if (obj->size() - obj->size_given_klass(new_klass) != 0) {
// We need an instance update => set back to old klass
_needs_instance_update = true;
_dcevm_shared_gc->update_fields_in_old(obj, new_klass->update_information());
} else {
// Either new size is bigger or gap is too small to be filled
oop src = obj;
@@ -492,7 +606,7 @@ public:
src->set_klass(obj->klass()->new_version());
// FIXME: instance updates...
//guarantee(false, "instance updates!");
DcevmSharedGC::update_fields(obj, src, new_klass->update_information());
_dcevm_shared_gc->update_fields(obj, src, new_klass->update_information(), true);
}
} else {
obj->set_klass(obj->klass()->new_version());
@@ -505,14 +619,15 @@ class ChangePointersObjectTask : public WorkerTask {
private:
ChangePointersOopClosure<StoreBarrier>* _cl;
ParallelObjectIterator* _poi;
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
bool _needs_instance_update;
public:
ChangePointersObjectTask(ChangePointersOopClosure<StoreBarrier>* cl, ParallelObjectIterator* poi) : WorkerTask("IterateObject Closure"),
_cl(cl), _poi(poi), _needs_instance_update(false) { }
ChangePointersObjectTask(ChangePointersOopClosure<StoreBarrier>* cl, ParallelObjectIterator* poi, VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces)
: WorkerTask("IterateObject Closure"), _cl(cl), _poi(poi), _removed_interfaces(removed_interfaces), _needs_instance_update(false) { }
virtual void work(uint worker_id) {
HandleMark hm(Thread::current()); // make sure any handles created are deleted
ChangePointersObjectClosure objectClosure(_cl);
ChangePointersObjectClosure objectClosure(_cl, _removed_interfaces);
_poi->object_iterate(&objectClosure, worker_id);
_needs_instance_update = _needs_instance_update || objectClosure.needs_instance_update();
}
@@ -521,6 +636,19 @@ public:
}
};
class ClearCompatCheckFields : public KlassClosure {
public:
ClearCompatCheckFields() {}
void do_klass(Klass* k) {
if (k->compat_check_field_offsets() != nullptr) {
if (reinterpret_cast<intptr_t>(k->compat_check_field_offsets()) != -1) {
delete k->compat_check_field_offsets();
}
k->clear_compat_check_field_offsets();
}
}
};
// Main transformation method - runs in VM thread.
// - for each scratch class call redefine_single_class
// - clear code cache (flush_dependent_code)
@@ -552,6 +680,7 @@ void VM_EnhancedRedefineClasses::doit() {
}
Universe::set_inside_redefinition(true);
DcevmSharedGC::create_static_instance();
// Mark methods seen on stack and everywhere else so old methods are not
// cleaned up if they're on the stack.
@@ -563,7 +692,10 @@ void VM_EnhancedRedefineClasses::doit() {
for (int i = 0; i < _new_classes->length(); i++) {
Klass *new_class = _new_classes->at(i);
new_class->old_version()->set_new_version(new_class);
// MagicAccessorImpl new_class is set in load_new_class_versions
if (new_class->old_version() != vmClasses::reflect_MagicAccessorImpl_klass() || new_class->old_version()->new_version() == nullptr) {
new_class->old_version()->set_new_version(new_class);
}
}
for (int i = 0; i < _new_classes->length(); i++) {
@@ -636,11 +768,11 @@ void VM_EnhancedRedefineClasses::doit() {
WorkerThreads* workers = Universe::heap()->safepoint_workers();
if (workers != nullptr && workers->active_workers() > 1) {
ParallelObjectIterator poi(workers->active_workers());
ChangePointersObjectTask objectTask(&oopClosure, &poi);
ChangePointersObjectTask objectTask(&oopClosure, &poi, _removed_interfaces);
workers->run_task(&objectTask);
needs_instance_update = objectTask.needs_instance_update();
} else {
ChangePointersObjectClosure objectClosure(&oopClosure);
ChangePointersObjectClosure objectClosure(&oopClosure, _removed_interfaces);
Universe::heap()->object_iterate(&objectClosure);
needs_instance_update = objectClosure.needs_instance_update();
}
@@ -676,7 +808,7 @@ void VM_EnhancedRedefineClasses::doit() {
// Initialize the new class! Special static initialization that does not execute the
// static constructor but copies static field values from the old class if name
// and signature of a static field match.
FieldCopier copier;
FieldCopier copier(_removed_interfaces);
cur->do_local_static_fields(&copier); // TODO (tw): What about internal static fields??
//java_lang_Class::set_klass(old->java_mirror(), cur); // FIXME-isd (from JDK8): is that correct?
//FIXME-isd (from JDK8): do we need this: ??? old->set_java_mirror(cur->java_mirror());
@@ -750,6 +882,12 @@ void VM_EnhancedRedefineClasses::doit() {
cur->clear_update_information();
}
// delete compat_check_fields
if (_removed_interfaces != nullptr) {
ClearCompatCheckFields compat_check_fields;
ClassLoaderDataGraph::classes_do(&compat_check_fields);
}
// TODO: explain...
LoaderConstraintTable::update_after_redefinition();
@@ -787,6 +925,7 @@ void VM_EnhancedRedefineClasses::doit() {
}
#endif
DcevmSharedGC::destroy_static_instance();
Universe::set_inside_redefinition(false);
_timer_vm_op_doit.stop();
@@ -878,9 +1017,10 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
_max_redefinition_flags = Klass::NoRedefinition;
GrowableArray<Klass*>* prev_affected_klasses = new (mtInternal) GrowableArray<Klass*>(_class_count, mtInternal);
GrowableArray<int> klass_redefinition_flags(_class_count, mtInternal);
do {
err = load_new_class_versions_single_step(&old_2_new_klass_map, THREAD);
err = load_new_class_versions_single_step(&old_2_new_klass_map, &klass_redefinition_flags, THREAD);
if (err != JVMTI_ERROR_NONE) {
delete prev_affected_klasses;
return err;
@@ -903,6 +1043,15 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
delete _affected_klasses;
_affected_klasses = prev_affected_klasses;
// Calculate instance update information after all new classes are resolved
for (int i = 0; i < _new_classes->length(); i++) {
int redefinition_flags = klass_redefinition_flags.at(i);
if ((redefinition_flags & Klass::ModifyInstances) != 0) {
Klass *new_class = _new_classes->at(i);
calculate_instance_update_information(new_class);
}
}
// Link and verify new classes _after_ all classes have been updated in the system dictionary!
for (int i = 0; i < _affected_klasses->length(); i++) {
Klass* the_class = _affected_klasses->at(i);
@@ -925,7 +1074,8 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
return JVMTI_ERROR_NONE;
}
jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions_single_step(Old2NewKlassMap* old_2_new_klass_map, TRAPS) {
jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions_single_step(Old2NewKlassMap* old_2_new_klass_map,
GrowableArray<int>* klass_redefinition_flags, TRAPS) {
// thread local state - used to transfer class_being_redefined object to SystemDictonery::resolve_from_stream
JvmtiThreadState *state = JvmtiThreadState::state_for(JavaThread::current());
@@ -1101,6 +1251,14 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions_single_step(Old2N
old_2_new_klass_map->put(the_class, new_class);
_new_classes->append(new_class);
// If MagicAccessorImpl is being redefined, we must set new_version,
// because class loading may trigger Reflection::verify_class_access,
// which performs superclass checks. This can occur when a subclass
// of MagicAccessorImpl is being loaded as part of the same redefinition run.
if (the_class == vmClasses::reflect_MagicAccessorImpl_klass()) {
the_class->set_new_version(new_class);
}
if (the_class == vmClasses::Reference_klass()) {
// must set offset+count to skip field "referent". Look at InstanceRefKlass::update_nonstatic_oop_maps
OopMapBlock* old_map = the_class->start_of_nonstatic_oop_maps();
@@ -1135,9 +1293,7 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions_single_step(Old2N
_max_redefinition_flags = _max_redefinition_flags | redefinition_flags;
if ((redefinition_flags & Klass::ModifyInstances) != 0) {
calculate_instance_update_information(new_class);
}
klass_redefinition_flags->append(redefinition_flags);
if (the_class == vmClasses::Object_klass()) {
_object_klass_redefined = true;
@@ -1184,7 +1340,7 @@ int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_
cur_klass = new_class->super();
while (cur_klass != nullptr) {
if (!the_class->is_subclass_of(cur_klass->is_redefining() ? cur_klass->old_version() : cur_klass)) {
log_info(redefine, class, load)("added super class %s", cur_klass->name()->as_C_string());
log_debug(redefine, class, load)("added super class %s", cur_klass->name()->as_C_string());
result = result | Klass::ModifyClass | Klass::ModifyInstances;
}
cur_klass = cur_klass->super();
@@ -1198,8 +1354,13 @@ int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_
for (i = 0; i < old_interfaces->length(); i++) {
InstanceKlass* old_interface = InstanceKlass::cast(old_interfaces->at(i));
if (!new_class->implements_interface_dcevm(old_interface, old_2_new_klass_map)) {
result = result | Klass::RemoveSuperType | Klass::ModifyClass;
log_info(redefine, class, load)("removed interface %s", old_interface->name()->as_C_string());
result = result | Klass::RemoveInterface | Klass::ModifyClass;
log_debug(redefine, class, load)("removed interface %s", old_interface->name()->as_C_string());
if (_removed_interfaces == nullptr) {
_removed_interfaces = new (mtInternal) SymbolSet();
}
Symbol* interf_sign_sym = SymbolTable::new_symbol(old_interface->signature_name());
_removed_interfaces->put(interf_sign_sym, true);
}
}
@@ -1208,7 +1369,7 @@ int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_
for (i = 0; i<new_interfaces->length(); i++) {
if (!the_class->implements_interface_dcevm(new_interfaces->at(i), old_2_new_klass_map)) {
result = result | Klass::ModifyClass;
log_info(redefine, class, load)("added interface %s", new_interfaces->at(i)->name()->as_C_string());
log_debug(redefine, class, load)("added interface %s", new_interfaces->at(i)->name()->as_C_string());
}
}
@@ -1484,7 +1645,7 @@ jvmtiError VM_EnhancedRedefineClasses::find_class_bytes(InstanceKlass* the_class
// Calculate difference between non static fields of old and new class and store the info into new class:
// instanceKlass->store_update_information
// instanceKlass->copy_backwards
// instanceKlass->copying_backwards
void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* new_version) {
class CalculateFieldUpdates : public FieldClosure {
@@ -1492,17 +1653,24 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
private:
InstanceKlass* _old_ik;
GrowableArray<int> _update_info;
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
int _position;
bool _copy_backwards;
bool _copying_backwards;
bool _compat_check;
public:
bool does_copy_backwards() {
return _copy_backwards;
bool is_copying_backwards() {
return _copying_backwards;
}
CalculateFieldUpdates(InstanceKlass* old_ik) :
_old_ik(old_ik), _position(instanceOopDesc::base_offset_in_bytes()), _copy_backwards(false) {
bool is_compat_check() {
return _compat_check;
}
CalculateFieldUpdates(InstanceKlass* old_ik, VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces) :
_old_ik(old_ik), _removed_interfaces(removed_interfaces), _position(instanceOopDesc::base_offset_in_bytes()),
_copying_backwards(false), _compat_check() {
_update_info.append(_position);
_update_info.append(0);
}
@@ -1524,43 +1692,78 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
assert(_position == fd->offset(), "must be correct offset!");
InstanceKlass* holder = fd->field_holder();
InstanceKlass* maybe_old_holder = holder->is_redefining() ? InstanceKlass::cast(holder->old_version()) : holder;
if (fd->index() < holder->java_fields_count()) {
fieldDescriptor old_fd;
if (_old_ik->find_field(fd->name(), fd->signature(), false, &old_fd) != nullptr) {
// Found field in the old class, copy
copy(old_fd.offset(), type2aelembytes(fd->field_type()));
if (old_fd.offset() < fd->offset()) {
_copy_backwards = true;
bool found = false;
if (_old_ik->find_field(fd->name(), fd->signature(), false, &old_fd) != nullptr) {
found = true;
} else {
if (maybe_old_holder->find_local_field_by_name(fd->name(), &old_fd) && !old_fd.is_static()) {
found = true;
}
}
if (found) {
// Found field in the old class, copy
Symbol *sig_new = fd->signature();
Symbol *sig_old = old_fd.signature();
int compat_flag;
if (sig_new == sig_old) {
if (_removed_interfaces != nullptr && _removed_interfaces->contains(sig_old) && fd->field_type() == T_OBJECT && old_fd.field_type() == T_OBJECT) {
compat_flag = 0;
_compat_check = true;
} else {
compat_flag = 1;
}
} else {
if (fd->field_type() == T_OBJECT && old_fd.field_type() == T_OBJECT) {
compat_flag = 0;
_compat_check = true;
} else {
compat_flag = -1;
}
}
// Transfer special flags
fd->set_is_field_modification_watched(old_fd.is_field_modification_watched());
fd->set_is_field_access_watched(old_fd.is_field_access_watched());
if (compat_flag != -1) {
copy(old_fd.offset(), type2aelembytes(fd->field_type()), (compat_flag == 0));
if (old_fd.offset() < fd->offset()) {
_copying_backwards = true;
}
// Transfer special flags
fd->set_is_field_modification_watched(old_fd.is_field_modification_watched());
fd->set_is_field_access_watched(old_fd.is_field_access_watched());
} else {
fill(type2aelembytes(fd->field_type()));
}
} else {
// New field, fill
fill(type2aelembytes(fd->field_type()));
}
} else {
FieldInfo internal_field = holder->field(fd->index());
InstanceKlass* maybe_old_klass = holder->is_redefining() ? InstanceKlass::cast(holder->old_version()) : holder;
int java_fields_count = maybe_old_klass->java_fields_count();
int java_fields_count = maybe_old_holder->java_fields_count();
int num_injected;
const InjectedField* const injected = JavaClasses::get_injected(maybe_old_klass->name(), &num_injected);
for (int i = java_fields_count; i < java_fields_count+num_injected; i++) {
FieldInfo maybe_old_field = maybe_old_klass->field(i);
const InjectedField *const injected = JavaClasses::get_injected(maybe_old_holder->name(), &num_injected);
for (int i = java_fields_count; i < java_fields_count + num_injected; i++) {
FieldInfo maybe_old_field = maybe_old_holder->field(i);
if (maybe_old_field.field_flags().is_injected() &&
internal_field.field_flags().is_injected() &&
maybe_old_field.lookup_symbol(maybe_old_field.name_index()) == internal_field.lookup_symbol(internal_field.name_index())) {
copy(maybe_old_field.offset(), type2aelembytes(Signature::basic_type(internal_field.signature_injected_dcevm())));
copy(maybe_old_field.offset(), type2aelembytes(Signature::basic_type(internal_field.signature_injected_dcevm())), false);
if (maybe_old_field.offset() < internal_field.offset()) {
_copy_backwards = true;
_copying_backwards = true;
}
break;
}
}
}
}
}
private:
void fill(int size) {
@@ -1572,19 +1775,24 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
_position += size;
}
void copy(int offset, int size) {
int prev_end = -1;
if (_update_info.length() > 0 && _update_info.at(_update_info.length() - 1) > 0) {
prev_end = _update_info.at(_update_info.length() - 2) + _update_info.at(_update_info.length() - 1);
}
void copy(int offset, int size, bool needs_compat_check) {
if (!needs_compat_check && _update_info.length() >= 2) {
int last_size = _update_info.at(_update_info.length() - 2);
int last_offset = _update_info.at(_update_info.length() - 1);
if (prev_end == offset) {
(*_update_info.adr_at(_update_info.length() - 2)) += size;
} else {
_update_info.append(size);
_update_info.append(offset);
if (last_offset > 0 && (last_size & DcevmSharedGC::UpdateInfoCompatFlag) == 0) {
int last_len = last_size & DcevmSharedGC::UpdateInfoLengthMask;
int prev_end = last_offset + last_len;
if (prev_end == offset) {
(*_update_info.adr_at(_update_info.length() - 2)) += size;
_position += size;
return;
}
}
}
int tagged_size = needs_compat_check ? (size | DcevmSharedGC::UpdateInfoCompatFlag) : size;
_update_info.append(tagged_size);
_update_info.append(offset);
_position += size;
}
};
@@ -1593,15 +1801,16 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
InstanceKlass* old_ik = InstanceKlass::cast(new_version->old_version());
//
CalculateFieldUpdates cl(old_ik);
CalculateFieldUpdates cl(old_ik, _removed_interfaces);
ik->do_nonstatic_fields_dcevm(&cl);
GrowableArray<int> result = cl.finish();
ik->store_update_information(result);
ik->set_copying_backwards(cl.does_copy_backwards());
ik->set_copying_backwards(cl.is_copying_backwards());
if (log_is_enabled(Trace, redefine, class, obsolete, metadata)) {
log_trace(redefine, class, obsolete, metadata)("Instance update information for %s:", new_version->name()->as_C_string());
if (cl.does_copy_backwards()) {
if (cl.is_copying_backwards()) {
log_trace(redefine, class, obsolete, metadata)("\tDoes copy backwards!");
}
for (int i=0; i<result.length(); i++) {
@@ -1766,21 +1975,24 @@ u8 VM_EnhancedRedefineClasses::next_id() {
}
// Clean method data for this class
void VM_EnhancedRedefineClasses::MethodDataCleaner::do_klass(Klass* k) {
if (k->is_instance_klass()) {
InstanceKlass *ik = InstanceKlass::cast(k);
// Clean MethodData of this class's methods so they don't refer to
// old methods that are no longer running.
Array<Method*>* methods = ik->methods();
int num_methods = methods->length();
for (int index = 0; index < num_methods; ++index) {
if (methods->at(index)->method_data() != nullptr) {
methods->at(index)->method_data()->clean_weak_method_links();
class MethodDataCleaner : public KlassClosure {
public:
MethodDataCleaner() {}
void do_klass(Klass* k) {
if (k->is_instance_klass()) {
InstanceKlass *ik = InstanceKlass::cast(k);
// Clean MethodData of this class's methods so they don't refer to
// old methods that are no longer running.
Array<Method*>* methods = ik->methods();
int num_methods = methods->length();
for (int index = 0; index < num_methods; ++index) {
if (methods->at(index)->method_data() != nullptr) {
methods->at(index)->method_data()->clean_weak_method_links();
}
}
}
}
}
};
void VM_EnhancedRedefineClasses::update_jmethod_ids(Thread *current) {
for (int j = 0; j < _matching_methods_length; ++j) {
@@ -2343,7 +2555,7 @@ jvmtiError VM_EnhancedRedefineClasses::find_sorted_affected_classes(bool do_init
log_trace(redefine, class, load)("%s", _affected_klasses->at(i)->name()->as_C_string());
}
}
return JVMTI_ERROR_NONE;
return result;
}
// Pairs of class dependencies (for topological sort)
@@ -2389,6 +2601,18 @@ jvmtiError VM_EnhancedRedefineClasses::do_topological_class_sorting(Old2NewKlass
old_2_new_klass_map,
THREAD);
if (HAS_PENDING_EXCEPTION) {
Symbol* ex_name = PENDING_EXCEPTION->klass()->name();
oop message = java_lang_Throwable::message(PENDING_EXCEPTION);
if (message != nullptr) {
char* ex_msg = java_lang_String::as_utf8_string(message);
log_info(redefine, class, load, exceptions)("parse_class exception: '%s %s'", ex_name->as_C_string(), ex_msg);
} else {
log_info(redefine, class, load, exceptions)("parse_class exception: '%s'", ex_name->as_C_string());
}
return JVMTI_ERROR_INTERNAL;
}
const Klass* super_klass = parser.super_klass();
if (super_klass != nullptr && _affected_klasses->contains((Klass*) super_klass)) {
links.append(KlassPair(super_klass, klass));

View File

@@ -47,7 +47,11 @@
// - doit() - main redefition, adjust existing objects on the heap, clear caches
// - doit_epilogue() - cleanup
class VM_EnhancedRedefineClasses: public VM_GC_Operation {
private:
public:
static unsigned int sym_hash (Symbol* const& s) { return (int)(uintptr_t)s; }
static bool sym_equals(Symbol* const& a, Symbol* const& b) { return a == b; }
typedef ResourceHashtable<Symbol*, char, 37, AnyObj::C_HEAP, mtInternal, &sym_hash, &sym_equals> SymbolSet;
private:
// These static fields are needed by ClassLoaderDataGraph::classes_do()
// facility and the AdjustCpoolCacheAndVtable helper:
static Array<Method*>* _old_methods;
@@ -71,7 +75,7 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
// RetransformClasses. Indicate which.
JvmtiClassLoadKind _class_load_kind;
GrowableArray<InstanceKlass*>* _new_classes;
GrowableArray<InstanceKlass*>* _new_classes;
jvmtiError _res;
// Set if any of the InstanceKlasses have entries in the ResolvedMethodTable
@@ -90,6 +94,8 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
int _max_redefinition_flags;
SymbolSet* _removed_interfaces;
// Performance measurement support. These timers do not cover all
// the work done for JVM/TI RedefineClasses() but they do cover
// the heavy lifting.
@@ -113,7 +119,7 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
//
// The result is sotred in _affected_klasses(old definitions) and _new_classes(new definitions) arrays.
jvmtiError load_new_class_versions(TRAPS);
jvmtiError load_new_class_versions_single_step(Old2NewKlassMap* old_2_new_klass_map, TRAPS);
jvmtiError load_new_class_versions_single_step(Old2NewKlassMap* old_2_new_klass_map, GrowableArray<int>* klass_redefinition_flags, TRAPS);
// Searches for all affected classes and performs a sorting such tha
// a supertype is always before a subtype.
@@ -171,13 +177,6 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
ClearCpoolCacheAndUnpatch(Thread* t) : _thread(t) {}
void do_klass(Klass* k);
};
// Clean MethodData out
class MethodDataCleaner : public KlassClosure {
public:
MethodDataCleaner() {}
void do_klass(Klass* k);
};
public:
VM_EnhancedRedefineClasses(jint class_count,
const jvmtiClassDefinition *class_defs,

View File

@@ -64,6 +64,9 @@ class WindowsPath implements Path {
// paths and has a long path prefix for all paths longer than MAX_PATH.
private volatile WeakReference<String> pathForWin32Calls;
// Used for fast string comparison.
private volatile WeakReference<byte[]> uppercasePath;
// offsets into name components (computed lazily)
private volatile Integer[] offsets;
@@ -791,23 +794,7 @@ class WindowsPath implements Path {
public int compareTo(Path obj) {
if (obj == null)
throw new NullPointerException();
String s1 = path;
String s2 = ((WindowsPath)obj).path;
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
return c1 - c2;
}
}
}
return n1 - n2;
return Arrays.compareUnsigned(getUppercasePath(), ((WindowsPath)obj).getUppercasePath());
}
@Override
@@ -917,6 +904,15 @@ class WindowsPath implements Path {
}
}
private byte[] getUppercasePath() {
byte[] result = uppercasePath != null ? uppercasePath.get() : null;
if (result == null) {
result = path.toUpperCase().getBytes();
uppercasePath = new WeakReference<>(result);
}
return result;
}
@Override
public URI toUri() {
return WindowsUriSupport.toUri(this);

View File

@@ -67,6 +67,7 @@ import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JTextArea;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.KeyStroke;
@@ -1096,4 +1097,23 @@ class CAccessibility implements PropertyChangeListener {
}
}, c, false);
}
private static Accessible getScrollBar(Accessible a, Component c, int orientation) {
if (a == null) return null;
return invokeAndWait(() -> {
Accessible sa = CAccessible.getSwingAccessible(a);
if (sa instanceof JScrollPane scrollPane) {
// NSAccessibilityOrientationVertical
if (orientation == 1) {
return scrollPane.getVerticalScrollBar();
}
// NSAccessibilityOrientationHorizontal
else if (orientation == 2) {
return scrollPane.getHorizontalScrollBar();
}
}
return null;
}, c);
}
}

View File

@@ -405,7 +405,8 @@ class CAccessible extends CFRetainedResource implements Accessible {
AccessibleRole thisRole = accessible.getAccessibleContext()
.getAccessibleRole();
if (thisRole == AccessibleRole.SLIDER ||
thisRole == AccessibleRole.PROGRESS_BAR) {
thisRole == AccessibleRole.PROGRESS_BAR ||
thisRole == AccessibleRole.SCROLL_BAR) {
execute(ptr -> valueChanged(ptr));
}
}

View File

@@ -244,6 +244,13 @@ public class CDataTransferer extends DataTransferer {
@Override
protected byte[] imageToPlatformBytes(Image image, long format) {
String formatString = getNativeForFormat(format);
byte[] result = CImage.getCreator().getPlatformImageBytesForFormat(image, formatString);
if (result != null) {
return result;
}
// fallback
return CImage.getCreator().getPlatformImageBytes(image);
}

View File

@@ -26,9 +26,12 @@
package sun.lwawt.macosx;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.geom.Dimension2D;
import java.awt.image.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.awt.image.MultiResolutionImage;
@@ -38,6 +41,8 @@ import sun.awt.image.MultiResolutionCachedImage;
import sun.awt.image.SunWritableRaster;
import javax.imageio.ImageIO;
public class CImage extends CFRetainedResource {
private static native long nativeCreateNSImageFromArray(int[] buffer, int w, int h);
private static native long nativeCreateNSImageFromBytes(byte[] buffer);
@@ -176,6 +181,41 @@ public class CImage extends CFRetainedResource {
return nativeGetPlatformImageBytes(buffer, image.getWidth(null), image.getHeight(null));
}
public byte[] getPlatformImageBytesForFormat(final Image image, final String format) {
if (format.equals("TIFF")) {
return getPlatformImageBytes(image);
}
BufferedImage bufferedImage;
if (image instanceof BufferedImage) {
bufferedImage = (BufferedImage) image;
} else {
int width = image.getWidth(null);
int height = image.getHeight(null);
if (width <= 0 || height <= 0) {
return null;
}
bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bufferedImage.createGraphics();
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
if (!ImageIO.write(bufferedImage, format, out)) {
return null;
}
} catch (IOException e) {
return null;
}
return out.toByteArray();
}
/**
* Translates a byte array which contains platform-specific image data in the given format into an Image.
*/

View File

@@ -179,6 +179,26 @@ enum ShortcutID {
Shortcut_ToggleSpeakSelection = 230,
Shortcut_ToggleSpeakItemUnderPointer = 231,
Shortcut_ToggleTypingFeedback = 232,
Shortcut_MinimizeWindow = 233,
Shortcut_ZoomWindow = 235,
Shortcut_FillWindow = 237,
Shortcut_CenterWindow = 238,
Shortcut_RestoreWindow = 239,
Shortcut_TileLeftHalf = 240,
Shortcut_TileRightHalf = 241,
Shortcut_TileTopHalf = 242,
Shortcut_TileBottomHalf = 243,
Shortcut_TileTopLeftQuarter = 244,
Shortcut_TileTopRightQuarter = 245,
Shortcut_TileBottomLeftQuarter = 246,
Shortcut_TileBottomRightQuarter = 247,
Shortcut_ArrangeLeftRight = 248,
Shortcut_ArrangeRightLeft = 249,
Shortcut_ArrangeTopBottom = 250,
Shortcut_ArrangeBottomTop = 251,
Shortcut_ArrangeQuarters = 256,
Shortcut_FullScreenTileLeft = 257,
Shortcut_FullScreenTileRight = 258,
};
struct SymbolicHotKey {
@@ -204,67 +224,88 @@ struct SymbolicHotKey {
// Modifier mask using the NSEventModifierFlag* values for this shortcut
int modifiers;
// The first major version of macOS that has this shortcut or -1 if unknown.
int macOSVersion;
// The first version of macOS that has this shortcut or -1 if unknown.
int macOSVersionMajor;
int macOSVersionMinor;
};
static const struct SymbolicHotKey defaultSymbolicHotKeys[] = {
[Shortcut_FocusMenuBar] = { "FocusMenuBar", "Move focus to the menu bar", YES, 65535, 120, 0x00840000, -1 },
[Shortcut_FocusDock] = { "FocusDock", "Move focus to the Dock", YES, 65535, 99, 0x00840000, -1 },
[Shortcut_FocusActiveWindow] = { "FocusActiveWindow", "Move focus to active or next window", YES, 65535, 118, 0x00840000, -1 },
[Shortcut_FocusToolbar] = { "FocusToolbar", "Move focus to window toolbar", YES, 65535, 96, 0x00840000, -1 },
[Shortcut_FocusFloatingWindow] = { "FocusFloatingWindow", "Move focus to floating window", YES, 65535, 97, 0x00840000, -1 },
[Shortcut_ToggleKeyboardAccess] = { "ToggleKeyboardAccess", "Turn keyboard access on or off", YES, 65535, 122, 0x00840000, -1 },
[Shortcut_ChangeTabMode] = { "ChangeTabMode", "Change the way Tab moves focus", YES, 65535, 98, 0x00840000, -1 },
[Shortcut_ToggleZoom] = { "ToggleZoom", "Zoom: Turn zoom on or off", NO, 56, 28, 0x00180000, -1 },
[Shortcut_ZoomIn] = { "ZoomIn", "Zoom: Zoom in", NO, 61, 24, 0x00180000, -1 },
[Shortcut_ZoomOut] = { "ZoomOut", "Zoom: Zoom out", NO, 45, 27, 0x00180000, -1 },
[Shortcut_InvertColors] = { "InvertColors", "Invert colors", YES, 56, 28, 0x001c0000, -1 },
[Shortcut_ToggleZoomImageSmoothing] = { "ToggleZoomImageSmoothing", "Zoom: Turn image smoothing on or off", NO, 92, 42, 0x00180000, -1 },
[Shortcut_IncreaseContrast] = { "IncreaseContrast", "Increase contrast", NO, 46, 47, 0x001c0000, -1 },
[Shortcut_DecreaseContrast] = { "DecreaseContrast", "Decrease contrast", NO, 44, 43, 0x001c0000, -1 },
[Shortcut_FocusNextApplicationWindow] = { "FocusNextApplicationWindow", "Move focus to the next window in application", YES, 96, 50, 0x00100000, -1 },
[Shortcut_ScreenshotToFile] = { "ScreenshotToFile", "Save picture of screen as a file", YES, 51, 20, 0x00120000, -1 },
[Shortcut_ScreenshotToClipboard] = { "ScreenshotToClipboard", "Copy picture of screen to the clipboard", YES, 51, 20, 0x00160000, -1 },
[Shortcut_ScreenshotAreaToFile] = { "ScreenshotAreaToFile", "Save picture of selected area as a file", YES, 52, 21, 0x00120000, -1 },
[Shortcut_ScreenshotAreaToClipboard] = { "ScreenshotAreaToClipboard", "Copy picture of selected area to the clipboard", YES, 52, 21, 0x00160000, -1 },
[Shortcut_ShowAllWindows] = { "ShowAllWindows", "Mission Control", YES, 65535, 126, 0x00840000, -1 },
[Shortcut_ShowApplicationWindows] = { "ShowApplicationWindows", "Application windows", YES, 65535, 125, 0x00840000, -1 },
[Shortcut_ShowDesktop] = { "ShowDesktop", "Show desktop", YES, 65535, 103, 0x00800000, -1 },
[Shortcut_ToggleDockHiding] = { "ToggleDockHiding", "Turn Dock hiding on/off", YES, 100, 2, 0x00180000, -1 },
[Shortcut_DecreaseBrightness] = { "DecreaseBrightness", "Decrease display brightness", YES, 65535, 107, 0x00800000, -1 },
[Shortcut_IncreaseBrightness] = { "IncreaseBrightness", "Increase display brightness", YES, 65535, 113, 0x00800000, -1 },
[Shortcut_FocusStatusMenu] = { "FocusStatusMenu", "Move focus to the status menus", YES, 65535, 100, 0x00840000, -1 },
[Shortcut_ToggleVoiceOver] = { "ToggleVoiceOver", "Turn VoiceOver on or off", YES, 65535, 96, 0x00900000, -1 },
[Shortcut_SelectPreviousInputSource] = { "SelectPreviousInputSource", "Select the previous input source", YES, 32, 49, 0x00040000, -1 },
[Shortcut_SelectNextInputSource] = { "SelectNextInputSource", "Select next source in Input menu", YES, 32, 49, 0x000c0000, -1 },
[Shortcut_ShowSpotlight] = { "ShowSpotlight", "Show Spotlight Search", YES, 32, 49, 0x00100000, -1 },
[Shortcut_ShowFinderSearch] = { "ShowFinderSearch", "Show Finder search window", YES, 32, 49, 0x00180000, -1 },
[Shortcut_SwitchToDesktopLeft] = { "SwitchToDesktopLeft", "Move left a space", NO, 65535, 123, 0x00840000, -1 },
[Shortcut_SwitchToDesktopRight] = { "SwitchToDesktopRight", "Move right a space", NO, 65535, 124, 0x00840000, -1 },
[Shortcut_SwitchToDesktop1] = { "SwitchToDesktop1", "Switch to Desktop 1", NO, 65535, 18, 0x00040000, -1 },
[Shortcut_SwitchToDesktop2] = { "SwitchToDesktop2", "Switch to Desktop 2", NO, 65535, 19, 0x00040000, -1 },
[Shortcut_SwitchToDesktop3] = { "SwitchToDesktop3", "Switch to Desktop 3", NO, 65535, 20, 0x00040000, -1 },
[Shortcut_SwitchToDesktop4] = { "SwitchToDesktop4", "Switch to Desktop 4", NO, 65535, 21, 0x00040000, -1 },
[Shortcut_ShowContextualMenu] = { "ShowContextualMenu", "Show contextual menu", YES, 65535, 36, 0x00040000, 15 },
[Shortcut_ShowLaunchpad] = { "ShowLaunchpad", "Show Launchpad", NO, 65535, 65535, 0, -1 },
[Shortcut_ShowAccessibilityControls] = { "ShowAccessibilityControls", "Show Accessibility controls", YES, 65535, 96, 0x00980000, -1 },
[Shortcut_ShowNotificationCenter] = { "ShowNotificationCenter", "Show Notification Center", NO, 65535, 65535, 0, -1 },
[Shortcut_ToggleDoNotDisturb] = { "ToggleDoNotDisturb", "Turn Do Not Disturb on/off", YES, 65535, 65535, 0, -1 },
[Shortcut_ToggleZoomFocusFollowing] = { "ToggleZoomFocusFollowing", "Zoom: Turn focus following on or off", NO, 65535, 65535, 0, -1 },
[Shortcut_ScreenshotOptions] = { "ScreenshotOptions", "Screenshot and recording options", YES, 53, 23, 0x00120000, -1 },
[Shortcut_OpenQuickNote] = { "OpenQuickNote", "Quick note", YES, 113, 12, 0x00800000, -1 },
[Shortcut_ToggleStageManager] = { "ToggleStageManager", "Turn Stage Manager on/off", NO, 65535, 65535, 0, -1 },
[Shortcut_TogglePresenterOverlayLarge] = { "TogglePresenterOverlayLarge", "Turn Presenter Overlay (large) on or off", YES, 65535, 65535, 0, -1 },
[Shortcut_TogglePresenterOverlaySmall] = { "TogglePresenterOverlaySmall", "Turn Presenter Overlay (small) on or off", YES, 65535, 65535, 0, -1 },
[Shortcut_ToggleLiveSpeech] = { "ToggleLiveSpeech", "LiveSpeech: Turn Live Speech on or off", YES, 65535, 65535, 0, 14 },
[Shortcut_ToggleLiveSpeechVisibility] = { "ToggleLiveSpeechVisibility", "LiveSpeech: Toggle visibility", YES, 65535, 65535, 0, 14 },
[Shortcut_PauseOrResumeLiveSpeech] = { "PauseOrResumeLiveSpeech", "LiveSpeech: Pause or resume speech", YES, 65535, 65535, 0, 14 },
[Shortcut_CancelLiveSpeech] = { "CancelLiveSpeech", "LiveSpeech: Cancel speech", YES, 65535, 65535, 0, 14 },
[Shortcut_ToggleLiveSpeechPhrases] = { "ToggleLiveSpeechPhrases", "LiveSpeech: Hide or show phrases", YES, 65535, 65535, 0, 14 },
[Shortcut_ToggleSpeakSelection] = { "ToggleSpeakSelection", "Turn speak selection on or off", YES, 65535, 65535, 0, 14 },
[Shortcut_ToggleSpeakItemUnderPointer] = { "ToggleSpeakItemUnderPointer", "Turn speak item under the pointer on or off", YES, 65535, 65535, 0, 14 },
[Shortcut_ToggleTypingFeedback] = { "ToggleTypingFeedback", "Turn typing feedback on or off", YES, 65535, 65535, 0, 14 },
[Shortcut_FocusMenuBar] = { "FocusMenuBar", "Move focus to the menu bar", YES, 65535, 120, 0x00840000, -1, -1 },
[Shortcut_FocusDock] = { "FocusDock", "Move focus to the Dock", YES, 65535, 99, 0x00840000, -1, -1 },
[Shortcut_FocusActiveWindow] = { "FocusActiveWindow", "Move focus to active or next window", YES, 65535, 118, 0x00840000, -1, -1 },
[Shortcut_FocusToolbar] = { "FocusToolbar", "Move focus to window toolbar", YES, 65535, 96, 0x00840000, -1, -1 },
[Shortcut_FocusFloatingWindow] = { "FocusFloatingWindow", "Move focus to floating window", YES, 65535, 97, 0x00840000, -1, -1 },
[Shortcut_ToggleKeyboardAccess] = { "ToggleKeyboardAccess", "Turn keyboard access on or off", YES, 65535, 122, 0x00840000, -1, -1 },
[Shortcut_ChangeTabMode] = { "ChangeTabMode", "Change the way Tab moves focus", YES, 65535, 98, 0x00840000, -1, -1 },
[Shortcut_ToggleZoom] = { "ToggleZoom", "Zoom: Turn zoom on or off", NO, 56, 28, 0x00180000, -1, -1 },
[Shortcut_ZoomIn] = { "ZoomIn", "Zoom: Zoom in", NO, 61, 24, 0x00180000, -1, -1 },
[Shortcut_ZoomOut] = { "ZoomOut", "Zoom: Zoom out", NO, 45, 27, 0x00180000, -1, -1 },
[Shortcut_InvertColors] = { "InvertColors", "Invert colors", YES, 56, 28, 0x001c0000, -1, -1 },
[Shortcut_ToggleZoomImageSmoothing] = { "ToggleZoomImageSmoothing", "Zoom: Turn image smoothing on or off", NO, 92, 42, 0x00180000, -1, -1 },
[Shortcut_IncreaseContrast] = { "IncreaseContrast", "Increase contrast", NO, 46, 47, 0x001c0000, -1, -1 },
[Shortcut_DecreaseContrast] = { "DecreaseContrast", "Decrease contrast", NO, 44, 43, 0x001c0000, -1, -1 },
[Shortcut_FocusNextApplicationWindow] = { "FocusNextApplicationWindow", "Move focus to the next window in application", YES, 96, 50, 0x00100000, -1, -1 },
[Shortcut_ScreenshotToFile] = { "ScreenshotToFile", "Save picture of screen as a file", YES, 51, 20, 0x00120000, -1, -1 },
[Shortcut_ScreenshotToClipboard] = { "ScreenshotToClipboard", "Copy picture of screen to the clipboard", YES, 51, 20, 0x00160000, -1, -1 },
[Shortcut_ScreenshotAreaToFile] = { "ScreenshotAreaToFile", "Save picture of selected area as a file", YES, 52, 21, 0x00120000, -1, -1 },
[Shortcut_ScreenshotAreaToClipboard] = { "ScreenshotAreaToClipboard", "Copy picture of selected area to the clipboard", YES, 52, 21, 0x00160000, -1, -1 },
[Shortcut_ShowAllWindows] = { "ShowAllWindows", "Mission Control", YES, 65535, 126, 0x00840000, -1, -1 },
[Shortcut_ShowApplicationWindows] = { "ShowApplicationWindows", "Application windows", YES, 65535, 125, 0x00840000, -1, -1 },
[Shortcut_ShowDesktop] = { "ShowDesktop", "Show desktop", YES, 65535, 103, 0x00800000, -1, -1 },
[Shortcut_ToggleDockHiding] = { "ToggleDockHiding", "Turn Dock hiding on/off", YES, 100, 2, 0x00180000, -1, -1 },
[Shortcut_DecreaseBrightness] = { "DecreaseBrightness", "Decrease display brightness", YES, 65535, 107, 0x00800000, -1, -1 },
[Shortcut_IncreaseBrightness] = { "IncreaseBrightness", "Increase display brightness", YES, 65535, 113, 0x00800000, -1, -1 },
[Shortcut_FocusStatusMenu] = { "FocusStatusMenu", "Move focus to the status menus", YES, 65535, 100, 0x00840000, -1, -1 },
[Shortcut_ToggleVoiceOver] = { "ToggleVoiceOver", "Turn VoiceOver on or off", YES, 65535, 96, 0x00900000, -1, -1 },
[Shortcut_SelectPreviousInputSource] = { "SelectPreviousInputSource", "Select the previous input source", YES, 32, 49, 0x00040000, -1, -1 },
[Shortcut_SelectNextInputSource] = { "SelectNextInputSource", "Select next source in Input menu", YES, 32, 49, 0x000c0000, -1, -1 },
[Shortcut_ShowSpotlight] = { "ShowSpotlight", "Show Spotlight Search", YES, 32, 49, 0x00100000, -1, -1 },
[Shortcut_ShowFinderSearch] = { "ShowFinderSearch", "Show Finder search window", YES, 32, 49, 0x00180000, -1, -1 },
[Shortcut_SwitchToDesktopLeft] = { "SwitchToDesktopLeft", "Move left a space", NO, 65535, 123, 0x00840000, -1, -1 },
[Shortcut_SwitchToDesktopRight] = { "SwitchToDesktopRight", "Move right a space", NO, 65535, 124, 0x00840000, -1, -1 },
[Shortcut_SwitchToDesktop1] = { "SwitchToDesktop1", "Switch to Desktop 1", NO, 65535, 18, 0x00040000, -1, -1 },
[Shortcut_SwitchToDesktop2] = { "SwitchToDesktop2", "Switch to Desktop 2", NO, 65535, 19, 0x00040000, -1, -1 },
[Shortcut_SwitchToDesktop3] = { "SwitchToDesktop3", "Switch to Desktop 3", NO, 65535, 20, 0x00040000, -1, -1 },
[Shortcut_SwitchToDesktop4] = { "SwitchToDesktop4", "Switch to Desktop 4", NO, 65535, 21, 0x00040000, -1, -1 },
[Shortcut_ShowContextualMenu] = { "ShowContextualMenu", "Show contextual menu", YES, 65535, 36, 0x00040000, 15, 0 },
[Shortcut_ShowLaunchpad] = { "ShowLaunchpad", "Show Launchpad", NO, 65535, 65535, 0, -1, -1 },
[Shortcut_ShowAccessibilityControls] = { "ShowAccessibilityControls", "Show Accessibility controls", YES, 65535, 96, 0x00980000, -1, -1 },
[Shortcut_ShowNotificationCenter] = { "ShowNotificationCenter", "Show Notification Center", NO, 65535, 65535, 0, -1, -1 },
[Shortcut_ToggleDoNotDisturb] = { "ToggleDoNotDisturb", "Turn Do Not Disturb on/off", YES, 65535, 65535, 0, -1, -1 },
[Shortcut_ToggleZoomFocusFollowing] = { "ToggleZoomFocusFollowing", "Zoom: Turn focus following on or off", NO, 65535, 65535, 0, -1, -1 },
[Shortcut_ScreenshotOptions] = { "ScreenshotOptions", "Screenshot and recording options", YES, 53, 23, 0x00120000, -1, -1 },
[Shortcut_OpenQuickNote] = { "OpenQuickNote", "Quick note", YES, 113, 12, 0x00800000, -1, -1 },
[Shortcut_ToggleStageManager] = { "ToggleStageManager", "Turn Stage Manager on/off", NO, 65535, 65535, 0, -1, -1 },
[Shortcut_TogglePresenterOverlayLarge] = { "TogglePresenterOverlayLarge", "Turn Presenter Overlay (large) on or off", YES, 65535, 65535, 0, -1, -1 },
[Shortcut_TogglePresenterOverlaySmall] = { "TogglePresenterOverlaySmall", "Turn Presenter Overlay (small) on or off", YES, 65535, 65535, 0, -1, -1 },
[Shortcut_ToggleLiveSpeech] = { "ToggleLiveSpeech", "LiveSpeech: Turn Live Speech on or off", YES, 65535, 65535, 0, 14, 0 },
[Shortcut_ToggleLiveSpeechVisibility] = { "ToggleLiveSpeechVisibility", "LiveSpeech: Toggle visibility", YES, 65535, 65535, 0, 14, 0 },
[Shortcut_PauseOrResumeLiveSpeech] = { "PauseOrResumeLiveSpeech", "LiveSpeech: Pause or resume speech", YES, 65535, 65535, 0, 14, 0 },
[Shortcut_CancelLiveSpeech] = { "CancelLiveSpeech", "LiveSpeech: Cancel speech", YES, 65535, 65535, 0, 14, 0 },
[Shortcut_ToggleLiveSpeechPhrases] = { "ToggleLiveSpeechPhrases", "LiveSpeech: Hide or show phrases", YES, 65535, 65535, 0, 14, 0 },
[Shortcut_ToggleSpeakSelection] = { "ToggleSpeakSelection", "Turn speak selection on or off", YES, 65535, 65535, 0, 14, 0 },
[Shortcut_ToggleSpeakItemUnderPointer] = { "ToggleSpeakItemUnderPointer", "Turn speak item under the pointer on or off", YES, 65535, 65535, 0, 14, 0 },
[Shortcut_ToggleTypingFeedback] = { "ToggleTypingFeedback", "Turn typing feedback on or off", YES, 65535, 65535, 0, 14, 0 },
[Shortcut_MinimizeWindow] = { "MinimizeWindow", "Windows: Minimize", YES, 109, 46, 0x00100000, 15, 4 },
[Shortcut_ZoomWindow] = { "ZoomWindow", "Windows: Zoom", YES, 65535, 65535, 0, 15, 4 },
[Shortcut_FillWindow] = { "FillWindow", "Windows: Fill", YES, 102, 3, 0x00840000, 15, 4 },
[Shortcut_CenterWindow] = { "CenterWindow", "Windows: Center", YES, 99, 8, 0x00840000, 15, 4 },
[Shortcut_RestoreWindow] = { "RestoreWindow", "Windows: Return to Previous Size", YES, 114, 15, 0x00840000, 15, 4 },
[Shortcut_TileLeftHalf] = { "TileLeftHalf", "Windows: Tile Left Half", YES, 65535, 123, 0x00840000, 15, 4 },
[Shortcut_TileRightHalf] = { "TileRightHalf", "Windows: Tile Right Half", YES, 65535, 124, 0x00840000, 15, 4 },
[Shortcut_TileTopHalf] = { "TileTopHalf", "Windows: Tile Top Half", YES, 65535, 126, 0x00840000, 15, 4 },
[Shortcut_TileBottomHalf] = { "TileBottomHalf", "Windows: Tile Bottom Half", YES, 65535, 125, 0x00840000, 15, 4 },
[Shortcut_TileTopLeftQuarter] = { "TileTopLeftQuarter", "Windows: Tile Top-Left Quarter", YES, 65535, 65535, 0, 15, 4 },
[Shortcut_TileTopRightQuarter] = { "TileTopRightQuarter", "Windows: Tile Top-Right Quarter", YES, 65535, 65535, 0, 15, 4 },
[Shortcut_TileBottomLeftQuarter] = { "TileBottomLeftQuarter", "Windows: Tile Bottom-Left Quarter", YES, 65535, 65535, 0, 15, 4 },
[Shortcut_TileBottomRightQuarter] = { "TileBottomRightQuarter", "Windows: Tile Bottom-Right Quarter", YES, 65535, 65535, 0, 15, 4 },
[Shortcut_ArrangeLeftRight] = { "ArrangeLeftRight", "Windows: Arrange Left and Right", YES, 65535, 123, 0x00860000, 15, 4 },
[Shortcut_ArrangeRightLeft] = { "ArrangeRightLeft", "Windows: Arrange Right and Left", YES, 65535, 124, 0x00860000, 15, 4 },
[Shortcut_ArrangeTopBottom] = { "ArrangeTopBottom", "Windows: Arrange Top and Bottom", YES, 65535, 126, 0x00860000, 15, 4 },
[Shortcut_ArrangeBottomTop] = { "ArrangeBottomTop", "Windows: Arrange Bottom and Top", YES, 65535, 125, 0x00860000, 15, 4 },
[Shortcut_ArrangeQuarters] = { "ArrangeQuarters", "Windows: Arrange in Quarters", YES, 65535, 65535, 0, 15, 4 },
[Shortcut_FullScreenTileLeft] = { "FullScreenTileLeft", "Windows: Full-Screen Tile Left", YES, 65535, 65535, 0, 15, 4 },
[Shortcut_FullScreenTileRight] = { "FullScreenTileRight", "Windows: Full-Screen Tile Right", YES, 65535, 65535, 0, 15, 4 },
};
static const int numSymbolicHotkeys = sizeof(defaultSymbolicHotKeys) / sizeof(defaultSymbolicHotKeys[0]);
@@ -482,8 +523,19 @@ static void iterateAppleSymbolicHotkeys(struct SymbolicHotKey hotkeys[numSymboli
for (int uid = 0; uid < numSymbolicHotkeys; ++uid) {
struct SymbolicHotKey* hotkey = &hotkeys[uid];
if (!hotkey->enabled) continue;
if (hotkey->macOSVersion > macOSVersion.majorVersion) continue;
if (!hotkey->enabled) {
continue;
}
if (hotkey->macOSVersionMajor > macOSVersion.majorVersion ||
(hotkey->macOSVersionMajor == macOSVersion.majorVersion && hotkey->macOSVersionMinor > macOSVersion.minorVersion)) {
continue;
}
if (hotkey->character == 0xFFFF && hotkey->key == 0xFFFF) {
continue;
}
char keyCharBuf[64];
const char *keyCharStr = keyCharBuf;

View File

@@ -28,6 +28,9 @@
#import "JNIUtilities.h"
#import "sun_lwawt_macosx_CAccessibility.h"
static jclass sjc_CAccessibility = NULL;
static jmethodID sjm_getScrollBar = NULL;
/*
* Implementation of the accessibility peer for the ScrollArea role
*/
@@ -57,10 +60,30 @@
{
JNIEnv *env = [ThreadUtilities getJNIEnv];
// Firstly, try to get the scroll bar using getHorizontalScrollBar/getVerticalScrollBar methods of JScrollPane.
jobject scrollBar = NULL;
GET_CACCESSIBILITY_CLASS_RETURN(nil);
GET_STATIC_METHOD_RETURN(sjm_getScrollBar, sjc_CAccessibility, "getScrollBar",
"(Ljavax/accessibility/Accessible;Ljava/awt/Component;I)Ljavax/accessibility/Accessible;", nil);
scrollBar = (*env)->CallStaticObjectMethod(env, sjc_CAccessibility, sjm_getScrollBar, fAccessible, fComponent, orientation);
CHECK_EXCEPTION();
if (scrollBar != NULL) {
CommonComponentAccessibility *axScrollBar = nil;
DECLARE_CLASS_RETURN(sjc_Accessible, "javax/accessibility/Accessible", nil);
if ((*env)->IsInstanceOf(env, scrollBar, sjc_Accessible)) {
axScrollBar = [CommonComponentAccessibility createWithAccessible:scrollBar withEnv:env withView:fView];
}
(*env)->DeleteLocalRef(env, scrollBar);
if (axScrollBar != nil) {
return axScrollBar;
}
}
// Otherwise, try to search for the scroll bar within the children.
NSArray *children = [CommonComponentAccessibility childrenOfParent:self withEnv:env withChildrenCode:sun_lwawt_macosx_CAccessibility_JAVA_AX_ALL_CHILDREN allowIgnored:YES];
if ([children count] <= 0) return nil;
// The scroll bars are in the children.
CommonComponentAccessibility *aElement;
NSEnumerator *enumerator = [children objectEnumerator];
while ((aElement = (CommonComponentAccessibility *)[enumerator nextObject])) {

View File

@@ -126,7 +126,7 @@ typedef enum {
- (void)dealloc;
- (void)handleDisplayLink:(BOOL)enabled displayID:(jint)displayID source:(const char*)src;
- (void)createDisplayLinkIfAbsent: (jint)displayID;
- (void)createDisplayLinkIfAbsent: (NSNumber*)displayID;
/**
* Resets the current clip state (disables both scissor and depth tests).

View File

@@ -38,6 +38,8 @@
// to avoid multiple start/stop displaylink operations. It speeds up
// scenarios with multiple subsequent updates.
#define KEEP_ALIVE_COUNT 4
#define CV_DISPLAYLINK_FAIL_DELAY 1.0
#define MAX_DISPLAYLINK_FAIL_COUNT 5
#define TO_MS(x) (1000.0 * (x))
#define TO_FPS(x) (1.0 / (x))
@@ -98,6 +100,7 @@ typedef struct {
CVDisplayLinkRef displayLink;
MTLContext* mtlc;
jint redrawCount;
jint failCount;
jint avgDisplayLinkSamples;
CFTimeInterval lastRedrawTime;
@@ -330,13 +333,13 @@ extern void initSamplers(id<MTLDevice> device);
case MTLDCM_DISPLAY_WAKEUP:
if (active) {
// (if needed will start a new display link thread):
[mtlc createDisplayLinkIfAbsent:displayID];
[mtlc createDisplayLinkIfAbsent:@(displayID)];
}
break;
case MTLDCM_DISPLAY_RECONFIGURE:
if (active) {
// (if needed will start a new display link thread):
[mtlc createDisplayLinkIfAbsent:displayID];
[mtlc createDisplayLinkIfAbsent:@(displayID)];
} else {
// kill CVDisplayLinks for inactive displays:
[mtlc destroyDisplayLink:displayID];
@@ -410,7 +413,7 @@ extern void initSamplers(id<MTLDevice> device);
[device release];
}
// (will start a new display link thread if needed):
[mtlc createDisplayLinkIfAbsent:displayID];
[mtlc createDisplayLinkIfAbsent:@(displayID)];
return mtlc;
}
@@ -493,27 +496,47 @@ extern void initSamplers(id<MTLDevice> device);
return [_displayLinkStates allKeys];
}
- (void)createDisplayLinkIfAbsent: (jint)displayID {
- (void)createDisplayLinkIfAbsent: (NSNumber*)displayID {
AWT_ASSERT_APPKIT_THREAD;
if (_displayLinkStates != nil) {
MTLDisplayLinkState *dlState = [self getDisplayLinkState:displayID];
MTLDisplayLinkState *dlState = [self getDisplayLinkState:[displayID intValue]];
if ((dlState != nil) && (dlState->displayLink != nil)) {
return;
}
if (TRACE_DISPLAY) {
dumpDisplayInfo(displayID);
dumpDisplayInfo([displayID intValue]);
}
CVDisplayLinkRef _displayLink;
if (TRACE_CVLINK) {
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_createDisplayLinkIfAbsent: "
"ctx=%p displayID=%d", self, displayID);
"ctx=%p displayID=%d", self, [displayID intValue]);
}
CHECK_CVLINK("CreateWithCGDisplay", nil, &_displayLink,
CVDisplayLinkCreateWithCGDisplay(displayID, &_displayLink));
CVDisplayLinkCreateWithCGDisplay([displayID intValue], &_displayLink));
if (_displayLink == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext_createDisplayLinkIfAbsent: Failed to initialize CVDisplayLink.");
if (dlState == nil) {
dlState = malloc(sizeof(MTLDisplayLinkState));
dlState->displayID = [displayID intValue];
dlState->displayLink = nil;
dlState->failCount = 0;
_displayLinkStates[displayID] = [NSValue valueWithPointer:dlState];
}
if (dlState->failCount >= MAX_DISPLAYLINK_FAIL_COUNT) {
J2dTraceLn(J2D_TRACE_ERROR,
"MTLLayer.createDisplayLink --- unable to create CVDisplayLink.");
dlState->failCount = 0;
return;
}
dlState->failCount++;
[self performSelector:@selector(createDisplayLinkIfAbsent:)
withObject:displayID
afterDelay:CV_DISPLAYLINK_FAIL_DELAY];
} else {
J2dRlsTraceLn3(J2D_TRACE_INFO, "MTLContext_createDisplayLinkIfAbsent["
"ctx=%p displayID=%d] displayLink=%p",
@@ -524,11 +547,12 @@ extern void initSamplers(id<MTLDevice> device);
isNewDisplayLink = true;
}
// update:
dlState->displayID = displayID;
dlState->displayID = [displayID intValue];
dlState->displayLink = _displayLink;
dlState->mtlc = self;
dlState->redrawCount = 0;
dlState->failCount = 0;
dlState->avgDisplayLinkSamples = 0;
dlState->lastRedrawTime = 0.0;
dlState->lastDisplayLinkTime = 0.0;
@@ -537,7 +561,7 @@ extern void initSamplers(id<MTLDevice> device);
if (isNewDisplayLink) {
// publish fully initialized object:
_displayLinkStates[@(displayID)] = [NSValue valueWithPointer:dlState];
_displayLinkStates[displayID] = [NSValue valueWithPointer:dlState];
}
CHECK_CVLINK("SetOutputCallback", nil, &_displayLink,

View File

@@ -173,6 +173,10 @@ public abstract class SunDropTargetContextPeer implements DropTargetContextPeer,
return currentA;
}
protected int getDropAction() {
return currentDA;
}
/**
* get the Transferable associated with the drop
*/

View File

@@ -45,7 +45,7 @@ class VKBufImgOps extends BufferedBufImgOps {
/**
* This method is called from VKDrawImage.transformImage() only. It
* validates the provided BufferedImageOp to determine whether the op
* is one that can be accelerated by the MTL pipeline. If the operation
* is one that can be accelerated by the Vulkan pipeline. If the operation
* cannot be completed for any reason, this method returns false;
* otherwise, the given BufferedImage is rendered to the destination
* using the provided BufferedImageOp and this method returns true.
@@ -53,65 +53,66 @@ class VKBufImgOps extends BufferedBufImgOps {
static boolean renderImageWithOp(SunGraphics2D sg, BufferedImage img,
BufferedImageOp biop, int x, int y)
{
// Validate the provided BufferedImage (make sure it is one that
// is supported, and that its properties are acceleratable)
if (biop instanceof ConvolveOp) {
if (!isConvolveOpValid((ConvolveOp)biop)) {
return false;
}
} else if (biop instanceof RescaleOp) {
if (!isRescaleOpValid((RescaleOp)biop, img)) {
return false;
}
} else if (biop instanceof LookupOp) {
if (!isLookupOpValid((LookupOp)biop, img)) {
return false;
}
} else {
// No acceleration for other BufferedImageOps (yet)
return false;
}
SurfaceData dstData = sg.surfaceData;
if (!(dstData instanceof VKSurfaceData) ||
(sg.interpolationType == AffineTransformOp.TYPE_BICUBIC) ||
(sg.compositeState > SunGraphics2D.COMP_ALPHA))
{
return false;
}
SurfaceData srcData =
dstData.getSourceSurfaceData(img, SunGraphics2D.TRANSFORM_ISIDENT,
CompositeType.SrcOver, null);
if (!(srcData instanceof VKSurfaceData)) {
// REMIND: this hack tries to ensure that we have a cached texture
srcData =
dstData.getSourceSurfaceData(img, SunGraphics2D.TRANSFORM_ISIDENT,
CompositeType.SrcOver, null);
if (!(srcData instanceof VKSurfaceData)) {
return false;
}
}
// Verify that the source surface is actually a texture and
// that the operation is supported
VKSurfaceData vkSrc = (VKSurfaceData)srcData;
VKGraphicsConfig gc = vkSrc.getGraphicsConfig();
if (vkSrc.getType() != VKSurfaceData.TEXTURE || !gc.isCapPresent(CAPS_EXT_BIOP_SHADER))
{
return false;
}
int sw = img.getWidth();
int sh = img.getHeight();
VKBlitLoops.IsoBlit(srcData, dstData,
img, biop,
sg.composite, sg.getCompClip(),
sg.transform, sg.interpolationType,
0, 0, sw, sh,
x, y, x+sw, y+sh,
true);
return true;
// TODO No acceleration for image ops yet.
return false;
// // Validate the provided BufferedImage (make sure it is one that
// // is supported, and that its properties are acceleratable)
// if (biop instanceof ConvolveOp) {
// if (!isConvolveOpValid((ConvolveOp)biop)) {
// return false;
// }
// } else if (biop instanceof RescaleOp) {
// if (!isRescaleOpValid((RescaleOp)biop, img)) {
// return false;
// }
// } else if (biop instanceof LookupOp) {
// if (!isLookupOpValid((LookupOp)biop, img)) {
// return false;
// }
// } else {
// // No acceleration for other BufferedImageOps (yet)
// return false;
// }
//
// SurfaceData dstData = sg.surfaceData;
// if (!(dstData instanceof VKSurfaceData) ||
// (sg.interpolationType == AffineTransformOp.TYPE_BICUBIC) ||
// (sg.compositeState > SunGraphics2D.COMP_ALPHA))
// {
// return false;
// }
//
// SurfaceData srcData =
// dstData.getSourceSurfaceData(img, SunGraphics2D.TRANSFORM_ISIDENT,
// CompositeType.SrcOver, null);
// if (!(srcData instanceof VKSurfaceData)) {
// // REMIND: this hack tries to ensure that we have a cached texture
// srcData =
// dstData.getSourceSurfaceData(img, SunGraphics2D.TRANSFORM_ISIDENT,
// CompositeType.SrcOver, null);
// if (!(srcData instanceof VKSurfaceData)) {
// return false;
// }
// }
//
// // Verify that the source surface is actually a texture and
// // that the operation is supported
// VKSurfaceData vkSrc = (VKSurfaceData)srcData;
// VKGraphicsConfig gc = vkSrc.getGraphicsConfig();
// if (vkSrc.getType() != VKSurfaceData.TEXTURE || !gc.isCapPresent(CAPS_EXT_BIOP_SHADER))
// {
// return false;
// }
//
// int sw = img.getWidth();
// int sh = img.getHeight();
// VKBlitLoops.IsoBlit(srcData, dstData,
// img, biop,
// sg.composite, sg.getCompClip(),
// sg.transform, sg.interpolationType,
// 0, 0, sw, sh,
// x, y, x+sw, y+sh);
//
// return true;
}
}

View File

@@ -31,6 +31,8 @@ import sun.java2d.pipe.RenderQueue;
import sun.java2d.pipe.hw.ContextCapabilities;
import sun.util.logging.PlatformLogger;
import java.awt.BufferCapabilities;
import java.awt.ImageCapabilities;
import java.lang.annotation.Native;
/**
@@ -41,6 +43,8 @@ final class VKContext extends BufferedContext {
private static final PlatformLogger log =
PlatformLogger.getLogger("sun.java2d.vulkan.VKContext");
public static final VKContext INSTANCE = new VKContext(VKRenderQueue.getInstance());
public VKContext(RenderQueue rq) {
super(rq);
}
@@ -73,6 +77,26 @@ final class VKContext extends BufferedContext {
@Native
static final int CAPS_EXT_GRAD_SHADER = (FIRST_PRIVATE_CAP << 3);
public static final VKContextCaps CONTEXT_CAPS = new VKContextCaps(
CAPS_PS30 | CAPS_PS20 | CAPS_RT_TEXTURE_ALPHA |
CAPS_RT_TEXTURE_OPAQUE | CAPS_MULTITEXTURE | CAPS_TEXNONPOW2 |
CAPS_TEXNONSQUARE, null);
public static final ImageCapabilities IMAGE_CAPS = new ImageCapabilities(true) {
@Override
public boolean isTrueVolatile() {
return true;
}
};
public static final BufferCapabilities BUFFER_CAPS = new BufferCapabilities(IMAGE_CAPS, IMAGE_CAPS,
BufferCapabilities.FlipContents.COPIED) {
@Override
public boolean isMultiBufferAvailable() {
return true;
}
};
public VKContextCaps(int caps, String adapterId) {
super(caps, adapterId);
}

View File

@@ -50,13 +50,6 @@ public class VKDrawImage extends DrawImage {
// punt to the MediaLib-based transformImage() in the superclass if:
// - bicubic interpolation is specified
// - a background color is specified and will be used
// - the source surface is neither a texture nor render-to-texture
// surface, and a non-default interpolation hint is specified
// (we can only control the filtering for texture->surface
// copies)
// REMIND: we should tweak the sw->texture->surface
// transform case to handle filtering appropriately
// (see 4841762)...
// - an appropriate TransformBlit primitive could not be found
if (interpType != AffineTransformOp.TYPE_BICUBIC) {
SurfaceData dstData = sg.surfaceData;
@@ -66,12 +59,7 @@ public class VKDrawImage extends DrawImage {
sg.imageComp,
bgColor);
if (srcData != null &&
!isBgOperation(srcData, bgColor) &&
(srcData.getSurfaceType() == VKSurfaceData.VKTexture ||
srcData.getSurfaceType() == VKSurfaceData.VKSurfaceRTT ||
interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR))
{
if (srcData != null && !isBgOperation(srcData, bgColor)) {
SurfaceType srcType = srcData.getSurfaceType();
SurfaceType dstType = dstData.getSurfaceType();
TransformBlit blit = TransformBlit.getFromCache(srcType,

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. 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
* 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.vulkan;
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 {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.java2d.vulkan.VKInstance");
private static final class Options {
@SuppressWarnings("removal")
private static final boolean vulkan = "true".equalsIgnoreCase(AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan", "")));
@SuppressWarnings("removal")
private static final boolean accelsd = vulkan && "true".equalsIgnoreCase(AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan.accelsd", "")));
@SuppressWarnings("removal")
private static final int deviceNumber = !vulkan ? 0 : AccessController.doPrivileged(
(PrivilegedAction<Integer>) () -> Integer.getInteger("sun.java2d.vulkan.deviceNumber", 0));
}
private static final int UNINITIALIZED = 0;
private static final int INITIALIZING = 1;
private static final int DISABLED = 2;
private static final int ENABLED = 3;
private static final int ACCELSD_BIT = 4;
private static final int PRESENT_BIT = 8;
private static int state = UNINITIALIZED;
private static VKGPU[] devices;
private static VKGPU defaultDevice;
private static native long initPlatform(long nativePtr);
private static native VKGPU[] initNative(long platformData);
public static synchronized void init(long nativePtr) {
if (state > INITIALIZING) return;
long platformData = nativePtr == 0 ? 0 : initPlatform(nativePtr);
int newState = DISABLED;
if (Options.vulkan) {
devices = initNative(platformData);
if (devices != null) {
newState = ENABLED;
if (Options.accelsd) newState |= ACCELSD_BIT;
defaultDevice = devices[Options.deviceNumber >= 0 && Options.deviceNumber < devices.length ?
Options.deviceNumber : 0];
// Check whether the presentation is supported.
for (VKGPU device : devices) {
if (device.hasCap(VKGPU.CAP_PRESENTABLE_BIT) &&
device.getPresentableGraphicsConfigs().findAny().isPresent()) {
newState |= PRESENT_BIT;
break;
}
}
VKBlitLoops.register();
VKMaskFill.register();
VKMaskBlit.register();
}
}
state = newState;
if (log.isLoggable(PlatformLogger.Level.FINE)) {
if (isVulkanEnabled()) {
log.fine("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()));
} else log.fine("Vulkan rendering enabled: NO");
}
}
private static void checkInit() {
if (state > INITIALIZING) return;
synchronized (VKEnv.class) {
if (state == UNINITIALIZED) {
// Try initializing the Toolkit first to give it a chance to init Vulkan with proper platform data.
state = INITIALIZING;
Toolkit.getDefaultToolkit();
}
// Still not initialized? Init without platform data.
if (state == INITIALIZING) init(0);
}
}
public static boolean isVulkanEnabled() {
checkInit();
return (state & ENABLED) == ENABLED;
}
public static boolean isPresentationEnabled() {
checkInit();
return (state & PRESENT_BIT) != 0;
}
public static boolean isSurfaceDataAccelerated() {
checkInit();
return (state & ACCELSD_BIT) != 0;
}
public static Stream<VKGPU> getDevices() {
checkInit();
final VKGPU first = defaultDevice;
return Stream.concat(Stream.of(first), Stream.of(devices).filter(d -> d != first));
}
}

View File

@@ -0,0 +1,173 @@
/*
* 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.vulkan;
import java.awt.GraphicsConfiguration;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.nio.ByteOrder;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.awt.image.BufImgSurfaceData;
import sun.awt.image.ByteComponentRaster;
import sun.awt.image.IntegerComponentRaster;
import sun.awt.image.SurfaceManager;
import sun.java2d.SurfaceData;
import sun.java2d.loops.SurfaceType;
/**
* Vulkan format description, which allows creation of compatible BufferedImages.
*/
public enum VKFormat {
// VK_FORMAT_B8G8R8A8_UNORM doesn't have a matching standard format in Java,
// but it can be aliased as TYPE_INT_ARGB on little-endian systems.
B8G8R8A8_UNORM(44,
LEOptimizations.ENABLED ? VKFormatModel.INT_ARGB_PRE : VKFormatModel.CUSTOM_4BYTE_BGRA_PRE,
LEOptimizations.ENABLED ? VKFormatModel.INT_RGB : VKFormatModel.CUSTOM_4BYTE_BGRx),
R8G8B8A8_UNORM(37, VKFormatModel.CUSTOM_4BYTE_RGBA_PRE, VKFormatModel.CUSTOM_4BYTE_RGBx),
A8B8G8R8_UNORM_PACK32(51, VKFormatModel.CUSTOM_INT_ABGR_PRE, VKFormatModel.INT_BGR);
private final int value;
private final SurfaceType surfaceType, translucentSurfaceType, opaqueSurfaceType;
private final VKFormatModel translucentModel, opaqueModel;
private final VKBufImgGraphicsConfig bufferedGraphicsConfig = new VKBufImgGraphicsConfig(this);
VKFormat(int value, VKFormatModel translucentModel, VKFormatModel opaqueModel) {
this.value = value;
this.surfaceType = VKSurfaceData.VKSurface.deriveSubType("Vulkan surface (" + name() + ")");
this.translucentSurfaceType = translucentModel == null ? null :
this.surfaceType.deriveSubType("Vulkan surface (" + name() + ", TRANSLUCENT)");
this.opaqueSurfaceType = this.surfaceType.deriveSubType("Vulkan surface (" + name() + ", OPAQUE)");
this.translucentModel = translucentModel;
this.opaqueModel = opaqueModel;
}
public int getValue(int transparency) {
final int FORMAT_OPAQUE_BIT = 0x80000000;
return transparency != Transparency.OPAQUE ? value : value | FORMAT_OPAQUE_BIT;
}
public SurfaceType getSurfaceType(int transparency) {
return switch (transparency) {
case 0 -> surfaceType; // Any transparency.
case Transparency.TRANSLUCENT, Transparency.BITMASK -> translucentSurfaceType;
case Transparency.OPAQUE -> opaqueSurfaceType;
default -> null;
};
}
public VKFormatModel getFormatModel(int transparency) {
return transparency != Transparency.OPAQUE ? translucentModel : opaqueModel;
}
public BufferedImage createCompatibleImage(int width, int height, int transparency) {
VKFormatModel formatModel = getFormatModel(transparency);
ColorModel colorModel = formatModel.getColorModel();
SampleModel sampleModel = formatModel.createSampleModel(width, height);
SurfaceType surfaceType = formatModel.getSurfaceType();
WritableRaster raster = Raster.createWritableRaster(sampleModel, null);
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
VKBufImgSurfaceData surfaceData = new VKBufImgSurfaceData(bufferedGraphicsConfig, raster, image, surfaceType);
SurfaceManager.setManager(image, new VKBufImgSurfaceManager(image, surfaceData));
return image;
}
public boolean isTranslucencyCapable() {
return translucentModel != VKFormatModel.NONE;
}
/**
* Some Vulkan formats can be more efficiently aliased as built-in Java formats on little-endian systems.
*/
private interface LEOptimizations {
@SuppressWarnings("removal")
boolean ENABLED = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN &&
"true".equalsIgnoreCase(AccessController.doPrivileged((PrivilegedAction<String>) () ->
System.getProperty("sun.java2d.vulkan.leOptimizations", "true")));
}
private static class VKBufImgSurfaceManager extends SurfaceManager {
private final BufferedImage image;
private final VKBufImgSurfaceData sd;
private VKBufImgSurfaceManager(BufferedImage image, VKBufImgSurfaceData sd) {
this.image = image;
this.sd = sd;
}
public SurfaceData getPrimarySurfaceData() { return sd; }
public SurfaceData restoreContents() { return sd; }
}
private static class VKBufImgSurfaceData extends BufImgSurfaceData {
private final GraphicsConfiguration gc;
private VKBufImgSurfaceData(GraphicsConfiguration gc,
WritableRaster raster, BufferedImage image, SurfaceType surfaceType) {
super(raster.getDataBuffer(), image, surfaceType, 1, 1);
this.gc = gc;
Object array;
if (raster instanceof IntegerComponentRaster r) array = r.getDataStorage();
else if (raster instanceof ByteComponentRaster r) array = r.getDataStorage();
else throw new IllegalArgumentException("Unsupported raster type: " + raster.getClass().getCanonicalName());
int pixStr, scanStr;
if (raster.getSampleModel() instanceof PixelInterleavedSampleModel sm) {
pixStr = sm.getPixelStride();
scanStr = sm.getScanlineStride();
} else if (raster.getSampleModel() instanceof SinglePixelPackedSampleModel sm) {
pixStr = DataBuffer.getDataTypeSize(sm.getDataType()) / 8;
scanStr = sm.getScanlineStride() * pixStr;
} else throw new IllegalArgumentException("Unsupported sample model: " +
raster.getSampleModel().getClass().getCanonicalName());
initRaster(array, 0, 0, image.getWidth(), image.getHeight(), pixStr, scanStr, null);
initSolidLoops();
}
@Override
public GraphicsConfiguration getDeviceConfiguration() {
return gc;
}
}
private static class VKBufImgGraphicsConfig extends VKOffscreenGraphicsConfig {
private VKBufImgGraphicsConfig(VKFormat format) {
super(null, format);
}
@Override
public VKGPU getGPU() {
throw new UnsupportedOperationException("No VKGPU associated with VKBufImgGraphicsConfig");
}
}
}

View File

@@ -0,0 +1,150 @@
/*
* 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.vulkan;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DirectColorModel;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.SampleModel;
import sun.awt.image.PixelConverter;
import sun.java2d.loops.SurfaceType;
import static java.awt.image.DataBuffer.TYPE_BYTE;
import static java.awt.image.DataBuffer.TYPE_INT;
import static java.awt.Transparency.OPAQUE;
import static java.awt.Transparency.TRANSLUCENT;
/**
* Format model describes properties of the surface, necessary for the creation of compatible BufferedImages.
*/
public enum VKFormatModel {
NONE(BufferedImage.TYPE_CUSTOM, null, null, null, null),
INT_ARGB_PRE(BufferedImage.TYPE_INT_ARGB_PRE, SurfaceType.IntArgbPre, PixelConverter.ArgbPre.instance,
new DirectColorModel(sRGB(), 32, 0xff0000, 0xff00, 0xff, 0xff000000, true, TYPE_INT), null),
INT_RGB(BufferedImage.TYPE_INT_RGB, SurfaceType.IntRgb, PixelConverter.Xrgb.instance,
new DirectColorModel(sRGB(), 24, 0xff0000, 0xff00, 0xff, 0x00000000, false, TYPE_INT), null),
CUSTOM_4BYTE_BGRA_PRE(BufferedImage.TYPE_CUSTOM, SurfaceType.Any4Byte, PixelConverter.ArgbPre.instance,
new ComponentColorModel(sRGB(), true, true, TRANSLUCENT, TYPE_BYTE), sampleModel(TYPE_BYTE, 4, 2, 1, 0, 3)),
CUSTOM_4BYTE_BGRx(BufferedImage.TYPE_CUSTOM, SurfaceType.Any4Byte, PixelConverter.Xrgb.instance,
new ComponentColorModel(sRGB(), false, false, OPAQUE, TYPE_BYTE), sampleModel(TYPE_BYTE, 4, 2, 1, 0)),
CUSTOM_4BYTE_RGBA_PRE(BufferedImage.TYPE_CUSTOM, SurfaceType.Any4Byte, CustomPixelConverter.AbgrPre,
new ComponentColorModel(sRGB(), true, true, TRANSLUCENT, TYPE_BYTE), sampleModel(TYPE_BYTE, 4, 0, 1, 2, 3)),
CUSTOM_4BYTE_RGBx(BufferedImage.TYPE_CUSTOM, SurfaceType.Any4Byte, PixelConverter.Xbgr.instance,
new ComponentColorModel(sRGB(), false, false, OPAQUE, TYPE_BYTE), sampleModel(TYPE_BYTE, 4, 0, 1, 2)),
CUSTOM_INT_ABGR_PRE(BufferedImage.TYPE_CUSTOM, SurfaceType.AnyDcm, CustomPixelConverter.AbgrPre,
new DirectColorModel(sRGB(), 32, 0xff, 0xff00, 0xff0000, 0xff000000, true, TYPE_INT), null),
INT_BGR(BufferedImage.TYPE_INT_BGR, SurfaceType.IntBgr, PixelConverter.Xbgr.instance,
new DirectColorModel(sRGB(), 24, 0xff, 0xff00, 0xff0000, 0x00000000, false, TYPE_INT), null);
private final int type;
private final SurfaceType surfaceType;
private final ColorModel colorModel;
private final SampleModelFactory sampleModelFactory;
VKFormatModel(int type, SurfaceType surfaceType, PixelConverter pixelConverter,
ColorModel colorModel, SampleModelFactory sampleModelFactory) {
this.type = type;
this.surfaceType = surfaceType == null ? null : surfaceType.deriveSubType(
"Vulkan-compatible buffered surface (" + surfaceType.getDescriptor() + ")", pixelConverter);
this.colorModel = colorModel;
this.sampleModelFactory = sampleModelFactory == null && colorModel != null ?
this.colorModel::createCompatibleSampleModel : sampleModelFactory;
}
public SurfaceType getSurfaceType() {
return surfaceType;
}
public ColorModel getColorModel() {
return colorModel;
}
public SampleModel createSampleModel(int width, int height) {
return sampleModelFactory.createSampleModel(width, height);
}
@FunctionalInterface
interface SampleModelFactory {
SampleModel createSampleModel(int w, int h);
}
private static SampleModelFactory sampleModel(int dataType, int components, int... bandOffsets) {
return (w, h) -> new PixelInterleavedSampleModel(dataType, w, h, components, w*components, bandOffsets);
}
private static ColorSpace sRGB() {
return ColorSpace.getInstance(ColorSpace. CS_sRGB);
}
private static abstract class CustomPixelConverter extends PixelConverter {
private static final PixelConverter AbgrPre = new CustomPixelConverter(0xff000000) {
@Override
public int rgbToPixel(int rgb, ColorModel cm) {
int a = rgb >>> 24;
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = (rgb ) & 0xff;
int a2 = a + (a >> 7);
r = (r * a2) >> 8;
g = (g * a2) >> 8;
b = (b * a2) >> 8;
return ((a << 24) | (b << 16) | (g << 8) | (r));
}
@Override
public int pixelToRgb(int pixel, ColorModel cm) {
int a = pixel >>> 24;
int r = (pixel ) & 0xff;
int g = (pixel >> 8) & 0xff;
int b = (pixel >> 16) & 0xff;
if (a != 0) {
r = ((r << 8) - r) / a;
g = ((g << 8) - g) / a;
b = ((b << 8) - b) / a;
}
return ((a << 24) | (r << 16) | (g << 8) | (b));
}
};
private CustomPixelConverter(int alphaMask) { this.alphaMask = alphaMask; }
@Override
public abstract int rgbToPixel(int rgb, ColorModel cm);
@Override
public abstract int pixelToRgb(int pixel, ColorModel cm);
}
}

View File

@@ -0,0 +1,135 @@
/*
* 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.vulkan;
import sun.awt.image.SurfaceManager;
import java.awt.Transparency;
import java.lang.annotation.Native;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
* VKDevice wrapper.
*/
public class VKGPU {
@Native public static final int CAP_PRESENTABLE_BIT = 0x80000000;
@Native public static final int CAP_LOGIC_OP_BIT = 0x40000000;
@Native public static final int CAP_SAMPLED_4BYTE_BIT = 0; // Considered always supported.
@Native public static final int CAP_SAMPLED_3BYTE_BIT = 1;
@Native public static final int CAP_SAMPLED_565_BIT = 2;
@Native public static final int CAP_SAMPLED_555_BIT = 4;
private boolean initialized;
private final SurfaceManager.ProxyCache surfaceDataProxyCache = new SurfaceManager.ProxyCache();
private final long nativeHandle;
private final String name;
private final Type type;
private final int caps;
private final List<VKGraphicsConfig> offscreenGraphicsConfigs, presentableGraphicsConfigs;
private static native void init(long nativeHandle);
private static native void reset(long nativeHandle);
/**
* Instantiated from native code, see createJavaDevices in VKInstance.c
* Fresh devices are created in uninitialized state. They can be queried for their properties
* but cannot be used for rendering until initialized via getNativeHandle().
*/
private VKGPU(long nativeHandle, String name, int type, int caps, int[] supportedFormats) {
this.nativeHandle = nativeHandle;
this.name = name;
this.type = Type.VALUES[type];
this.caps = caps;
offscreenGraphicsConfigs = new ArrayList<>();
presentableGraphicsConfigs = new ArrayList<>();
VKFormat[] allFormats = VKFormat.values();
for (int supportedFormat : supportedFormats) {
int formatValue = supportedFormat & ~CAP_PRESENTABLE_BIT;
for (VKFormat format : allFormats) {
if (formatValue == format.getValue(Transparency.TRANSLUCENT)) {
VKOffscreenGraphicsConfig gc = new VKOffscreenGraphicsConfig(this, format);
offscreenGraphicsConfigs.add(gc);
if ((supportedFormat & caps & CAP_PRESENTABLE_BIT) != 0) presentableGraphicsConfigs.add(gc);
break;
}
}
}
}
public SurfaceManager.ProxyCache getSurfaceDataProxyCache() { return surfaceDataProxyCache; }
public String getName() { return name; }
public Type getType() { return type; }
public Stream<VKGraphicsConfig> getOffscreenGraphicsConfigs() {
return offscreenGraphicsConfigs.stream();
}
public Stream<VKGraphicsConfig> getPresentableGraphicsConfigs() {
return presentableGraphicsConfigs.stream();
}
public int getCaps() {
return caps;
}
public boolean hasCap(int cap) {
return (caps & cap) == cap;
}
/**
* Initialize the device and return its native handle.
*/
public synchronized long getNativeHandle() {
if (!initialized) {
try {
init(nativeHandle);
} catch (RuntimeException e) {
throw new RuntimeException("Failed to initialize Vulkan device: " + name, e);
}
initialized = true;
}
return nativeHandle;
}
@Override
public String toString() {
return name + " (" + type + ")";
}
public enum Type {
OTHER,
INTEGRATED_GPU,
DISCRETE_GPU,
VIRTUAL_GPU,
CPU;
private static final Type[] VALUES = values();
}
}

View File

@@ -26,9 +26,124 @@
package sun.java2d.vulkan;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.VolatileImage;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.Surface;
import sun.java2d.SurfaceManagerFactory;
import sun.java2d.pipe.BufferedContext;
import sun.java2d.pipe.hw.AccelGraphicsConfig;
import sun.java2d.pipe.hw.AccelTypedVolatileImage;
import sun.java2d.pipe.hw.ContextCapabilities;
public interface VKGraphicsConfig extends AccelGraphicsConfig, SurfaceManager.ProxiedGraphicsConfig {
boolean isCapPresent(int capsExtGradShader);
import java.awt.BufferCapabilities;
import java.awt.GraphicsConfiguration;
import java.awt.ImageCapabilities;
import static java.awt.Transparency.OPAQUE;
import static java.awt.Transparency.TRANSLUCENT;
import static sun.java2d.pipe.hw.AccelSurface.RT_TEXTURE;
import static sun.java2d.pipe.hw.AccelSurface.TEXTURE;
/**
* Base type for Vulkan graphics config.
* Despite it being an interface, it contains a (preferred) default implementation
* for most of the methods, including base methods of GraphicsConfiguration class.
*/
public interface VKGraphicsConfig extends AccelGraphicsConfig,
SurfaceManager.ProxiedGraphicsConfig {
default VolatileSurfaceManager createVolatileManager(SunVolatileImage image,
Object context) {
return new VKVolatileSurfaceManager(image, context);
}
VKGraphicsConfig getOffscreenConfig();
default VKGPU getGPU() {
return getOffscreenConfig().getGPU();
}
default VKFormat getFormat() {
return getOffscreenConfig().getFormat();
}
default double getScale() {
return 1;
}
@Override
default SurfaceManager.ProxyCache getSurfaceDataProxyCache() {
return getGPU().getSurfaceDataProxyCache();
}
@Override
default BufferedContext getContext() {
return VKContext.INSTANCE;
}
/**
* Returns true if the provided capability bit is present for this config.
* See VKContext.java for a list of supported capabilities.
*/
default boolean isCapPresent(int cap) { // TODO refactor capability checks.
return ((getContextCapabilities().getCaps() & cap) != 0);
}
@Override
default ContextCapabilities getContextCapabilities() {
return VKContext.VKContextCaps.CONTEXT_CAPS;
}
@Override
default VolatileImage createCompatibleVolatileImage(int width, int height, int transparency, int type) {
if (type != RT_TEXTURE && type != TEXTURE) return null;
if (transparency != OPAQUE && (transparency != TRANSLUCENT || !isTranslucencyCapable())) return null;
SunVolatileImage vi =
new AccelTypedVolatileImage((GraphicsConfiguration) this, width, height, transparency, type);
Surface sd = vi.getDestSurface();
if (!(sd instanceof VKSurfaceData vsd) || vsd.getType() != type) {
vi.flush();
vi = null;
}
return vi;
}
default String descriptorString() {
return getFormat().name() + ", " + getGPU();
}
// Default implementation of GraphicsConfiguration methods.
// Those need to be explicitly overridden by subclasses using VKGraphicsConfig.super.
default BufferedImage createCompatibleImage(int width, int height) {
return createCompatibleImage(width, height, isTranslucencyCapable() ? TRANSLUCENT : OPAQUE);
}
default BufferedImage createCompatibleImage(int width, int height, int transparency) {
return getFormat().createCompatibleImage(width, height, transparency);
}
default ColorModel getColorModel() {
return getColorModel(isTranslucencyCapable() ? TRANSLUCENT : OPAQUE);
}
default ColorModel getColorModel(int transparency) {
return getFormat().getFormatModel(transparency).getColorModel();
}
default BufferCapabilities getBufferCapabilities() {
return VKContext.VKContextCaps.BUFFER_CAPS;
}
default ImageCapabilities getImageCapabilities() {
return VKContext.VKContextCaps.IMAGE_CAPS;
}
default boolean isTranslucencyCapable() {
return getFormat().isTranslucencyCapable();
}
}

View File

@@ -1,81 +0,0 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. 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
* 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.vulkan;
import sun.util.logging.PlatformLogger;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class VKInstance {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.java2d.vulkan.VKInstance");
private static Boolean initialized;
private static Boolean sdAccelerated;
private static native boolean initNative(long nativePtr, boolean verbose, int deviceNumber);
public static void init(long nativePtr) {
@SuppressWarnings("removal")
String vulkanOption = AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan", ""));
if ("true".equalsIgnoreCase(vulkanOption)) {
@SuppressWarnings("removal")
String deviceNumberOption = AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan.deviceNumber", "0"));
int parsedDeviceNumber = 0;
try {
parsedDeviceNumber = Integer.parseInt(deviceNumberOption);
} catch (NumberFormatException e) {
log.warning("Invalid Vulkan device number:" + deviceNumberOption);
}
final int deviceNumber = parsedDeviceNumber;
final boolean verbose = "True".equals(vulkanOption);
System.loadLibrary("awt");
@SuppressWarnings("removal")
String sdOption = AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan.accelsd", ""));
initialized = initNative(nativePtr, verbose, deviceNumber);
sdAccelerated = initialized && "true".equalsIgnoreCase(sdOption);
} else initialized = false;
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Vulkan rendering enabled: " + (initialized ? "YES" : "NO"));
log.fine("Vulkan accelerated surface data enabled: " + (sdAccelerated ? "YES" : "NO"));
}
}
public static boolean isVulkanEnabled() {
if (initialized == null) throw new RuntimeException("Vulkan not initialized");
return initialized;
}
public static boolean isSurfaceDataAccelerated() {
if (initialized == null) throw new RuntimeException("Vulkan not initialized");
return sdAccelerated;
}
}

View File

@@ -25,26 +25,28 @@
package sun.java2d.vulkan;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.VolatileImage;
import sun.java2d.SurfaceData;
import sun.java2d.pipe.BufferedContext;
/**
* SurfaceData object representing an off-screen buffer
*/
public class VKOffScreenSurfaceData extends VKSurfaceData {
private final Image offscreenImage;
private native void initOps(int width, int height);
public VKOffScreenSurfaceData(VKGraphicsConfig gc, Image image, ColorModel cm,
int type, int width, int height)
{
super(gc, cm, type, width, height);
private final Image offscreenImage;
private final int userWidth, userHeight; // In logical units.
private native void initOps(int format);
public VKOffScreenSurfaceData(Image image, VKFormat format, int transparency, int type, int width, int height) {
super(format, transparency, type);
this.userWidth = width;
this.userHeight = height;
offscreenImage = image;
initOps(width, height);
initOps(format.getValue(transparency));
}
@Override
@@ -52,11 +54,6 @@ public class VKOffScreenSurfaceData extends VKSurfaceData {
return restoreContents(offscreenImage);
}
@Override
public GraphicsConfiguration getDeviceConfiguration() {
return null;
}
@Override
public long getNativeResource(int resType) {
return 0;
@@ -76,13 +73,13 @@ public class VKOffScreenSurfaceData extends VKSurfaceData {
}
@Override
public BufferedContext getContext() {
return getGraphicsConfig().getContext();
}
@Override
public boolean isOnScreen() {
return false;
protected int revalidate(VKGraphicsConfig gc) {
int result = super.revalidate(gc);
if (result != VolatileImage.IMAGE_INCOMPATIBLE) {
scale = gc.getScale();
width = (int) Math.ceil(scale * userWidth);
height = (int) Math.ceil(scale * userHeight);
}
return result;
}
}

View File

@@ -0,0 +1,115 @@
/*
* 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.vulkan;
import java.awt.BufferCapabilities;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.ImageCapabilities;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
public class VKOffscreenGraphicsConfig extends GraphicsConfiguration implements VKGraphicsConfig {
private final VKOffsecreenGraphicsDevice graphicsDevice = new VKOffsecreenGraphicsDevice(this);
private final VKGPU gpu;
private final VKFormat format;
VKOffscreenGraphicsConfig(VKGPU gpu, VKFormat format) {
this.gpu = gpu;
this.format = format;
}
@Override
public GraphicsDevice getDevice() {
return graphicsDevice;
}
@Override
public VKGraphicsConfig getOffscreenConfig() {
return this;
}
@Override
public VKGPU getGPU() {
return gpu;
}
@Override
public VKFormat getFormat() {
return format;
}
@Override
public AffineTransform getDefaultTransform() {
return new AffineTransform();
}
@Override
public AffineTransform getNormalizingTransform() {
return new AffineTransform();
}
@Override
public Rectangle getBounds() {
return new Rectangle(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
}
@Override
public BufferedImage createCompatibleImage(int width, int height) {
return VKGraphicsConfig.super.createCompatibleImage(width, height);
}
@Override
public BufferedImage createCompatibleImage(int width, int height, int transparency) {
return VKGraphicsConfig.super.createCompatibleImage(width, height, transparency);
}
@Override
public ColorModel getColorModel() {
return VKGraphicsConfig.super.getColorModel();
}
@Override
public ColorModel getColorModel(int transparency) {
return VKGraphicsConfig.super.getColorModel(transparency);
}
@Override
public BufferCapabilities getBufferCapabilities() {
return VKGraphicsConfig.super.getBufferCapabilities();
}
@Override
public ImageCapabilities getImageCapabilities() {
return VKGraphicsConfig.super.getImageCapabilities();
}
@Override
public boolean isTranslucencyCapable() {
return VKGraphicsConfig.super.isTranslucencyCapable();
}
@Override
public String toString() {
return "VKOffscreenGraphicsConfig[" + descriptorString() + "]";
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.vulkan;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
public class VKOffsecreenGraphicsDevice extends GraphicsDevice {
private final VKGraphicsConfig config;
VKOffsecreenGraphicsDevice(VKGraphicsConfig config) {
this.config = config;
}
@Override
public int getType() {
return GraphicsDevice.TYPE_IMAGE_BUFFER;
}
@Override
public String getIDstring() {
return "VKOffscreenGraphicsDevice";
}
@Override
public GraphicsConfiguration[] getConfigurations() {
return new GraphicsConfiguration[]{ getDefaultConfiguration() };
}
@Override
public GraphicsConfiguration getDefaultConfiguration() {
return (GraphicsConfiguration) config;
}
}

View File

@@ -31,19 +31,24 @@ import sun.awt.SunHints;
import sun.awt.image.PixelConverter;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.loops.Blit;
import sun.java2d.loops.CompositeType;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.loops.SurfaceType;
import static sun.java2d.pipe.BufferedOpCodes.CONFIGURE_SURFACE;
import sun.java2d.pipe.BufferedContext;
import sun.java2d.pipe.ParallelogramPipe;
import sun.java2d.pipe.PixelToParallelogramConverter;
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.ColorModel;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.VolatileImage;
import static sun.java2d.pipe.BufferedOpCodes.FLUSH_SURFACE;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_PS30;
@@ -51,40 +56,9 @@ import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_PS30;
public abstract class VKSurfaceData extends SurfaceData
implements AccelSurface {
/**
* Pixel formats
*/
public static final int PF_INT_ARGB = 0;
public static final int PF_INT_ARGB_PRE = 1;
public static final int PF_INT_RGB = 2;
public static final int PF_INT_RGBX = 3;
public static final int PF_INT_BGR = 4;
public static final int PF_INT_BGRX = 5;
public static final int PF_USHORT_565_RGB = 6;
public static final int PF_USHORT_555_RGB = 7;
public static final int PF_USHORT_555_RGBX = 8;
public static final int PF_BYTE_GRAY = 9;
public static final int PF_USHORT_GRAY = 10;
public static final int PF_3BYTE_BGR = 11;
/**
* SurfaceTypes
*/
private static final String DESC_VK_SURFACE = "VK Surface";
private static final String DESC_VK_SURFACE_RTT =
"VK Surface (render-to-texture)";
private static final String DESC_VK_TEXTURE = "VK Texture";
// We want non-premultiplied alpha to prevent precision loss, so use PixelConverter.Argb
// See also VKUtil_DecodeJavaColor.
static final SurfaceType VKSurface =
SurfaceType.Any.deriveSubType(DESC_VK_SURFACE,
PixelConverter.Argb.instance);
static final SurfaceType VKSurfaceRTT =
VKSurface.deriveSubType(DESC_VK_SURFACE_RTT);
static final SurfaceType VKTexture =
SurfaceType.Any.deriveSubType(DESC_VK_TEXTURE);
static final SurfaceType VKSurface = SurfaceType.Any.deriveSubType("VK Surface", PixelConverter.Argb.instance);
protected static VKRenderer vkRenderPipe;
protected static PixelToParallelogramConverter vkTxRenderPipe;
@@ -93,74 +67,59 @@ public abstract class VKSurfaceData extends SurfaceData
protected static VKDrawImage vkImagePipe;
static {
if (!GraphicsEnvironment.isHeadless()) {
VKRenderQueue rq = VKRenderQueue.getInstance();
vkImagePipe = new VKDrawImage();
vkTextPipe = new VKTextRenderer(rq);
vkRenderPipe = new VKRenderer(rq);
if (GraphicsPrimitive.tracingEnabled()) {
vkTextPipe = vkTextPipe.traceWrap();
//The wrapped vkRenderPipe will wrap the AA pipe as well...
vkAAPgramPipe = vkRenderPipe.traceWrap();
}
vkAAPgramPipe = vkRenderPipe.getAAParallelogramPipe();
vkTxRenderPipe =
new PixelToParallelogramConverter(vkRenderPipe, vkRenderPipe, 1.0, 0.25, true);
VKBlitLoops.register();
VKMaskFill.register();
VKMaskBlit.register();
VKRenderQueue rq = VKRenderQueue.getInstance();
vkImagePipe = new VKDrawImage();
vkTextPipe = new VKTextRenderer(rq);
vkRenderPipe = new VKRenderer(rq);
if (GraphicsPrimitive.tracingEnabled()) {
vkTextPipe = vkTextPipe.traceWrap();
//The wrapped vkRenderPipe will wrap the AA pipe as well...
vkAAPgramPipe = vkRenderPipe.traceWrap();
}
vkAAPgramPipe = vkRenderPipe.getAAParallelogramPipe();
vkTxRenderPipe = new PixelToParallelogramConverter(vkRenderPipe, vkRenderPipe, 1.0, 0.25, true);
}
// TODO Do we really want to have scale there? It is used by getDefaultScaleX/Y...
protected int scale;
private final VKFormat format;
protected VKGraphicsConfig gc;
protected double scale;
protected int width;
protected int height;
protected int type;
private VKGraphicsConfig graphicsConfig;
// these fields are set from the native code when the surface is
// initialized
private int nativeWidth;
private int nativeHeight;
private int type;
/**
* Returns the appropriate SurfaceType corresponding to the given Metal
* surface type constant (e.g. TEXTURE -> MTLTexture).
*/
private static SurfaceType getCustomSurfaceType(int vkType) {
switch (vkType) {
case TEXTURE:
return VKTexture;
case RT_TEXTURE:
return VKSurfaceRTT;
default:
return VKSurface;
}
}
protected VKSurfaceData(VKGraphicsConfig gc, ColorModel cm, int type, int width, int height)
{
super(getCustomSurfaceType(type), cm);
this.graphicsConfig = gc;
protected VKSurfaceData(VKFormat format, int transparency, int type) {
super(format.getSurfaceType(transparency), format.getFormatModel(transparency).getColorModel());
this.format = format;
this.type = type;
setBlitProxyCache(gc.getSurfaceDataProxyCache());
// TEXTURE shouldn't be scaled, it is used for managed BufferedImages.
scale = 1;
this.width = width * scale;
this.height = height * scale;
}
public VKFormat getFormat() {
return format;
}
@Override
public double getDefaultScaleX() {
return scale;
}
@Override
public double getDefaultScaleY() {
return scale;
}
/**
* Returns one of the surface type constants defined above.
*/
@Override
public final int getType() {
return type;
}
@Override
public GraphicsConfiguration getDeviceConfiguration() {
return (GraphicsConfiguration) gc;
}
public void flush() {
invalidate();
VKRenderQueue rq = VKRenderQueue.getInstance();
@@ -178,28 +137,28 @@ public abstract class VKSurfaceData extends SurfaceData
}
}
public Raster getRaster(int x, int y, int w, int h) {
throw new InternalError("not implemented yet");
protected BufferedImage getSnapshot(int x, int y, int width, int height) {
BufferedImage image = getFormat().createCompatibleImage(width, height, getTransparency());
SurfaceData sd = SurfaceData.getPrimarySurfaceData(image);
Blit blit = Blit.getFromCache(getSurfaceType(), CompositeType.SrcNoEa, sd.getSurfaceType());
blit.Blit(this, sd, AlphaComposite.Src, null, x, y, 0, 0, width, height);
return image;
}
public Raster getRaster(int x, int y, int w, int h) {
return getSnapshot(x, y, w, h).getRaster().createTranslatedChild(x, y);
}
@Override
public Rectangle getNativeBounds() {
VKRenderQueue rq = VKRenderQueue.getInstance();
rq.lock();
try {
return new Rectangle(nativeWidth, nativeHeight);
} finally {
rq.unlock();
}
return new Rectangle(width, height);
}
public void validatePipe(SunGraphics2D sg2d) {
TextPipe textpipe;
boolean validated = false;
// MTLTextRenderer handles both AA and non-AA text, but
// VKTextRenderer handles both AA and non-AA text, but
// only works with the following modes:
// (Note: For LCD text we only enter this code path if
// canRenderLCDText() has already validated that the mode is
@@ -247,7 +206,7 @@ public abstract class VKSurfaceData extends SurfaceData
}
} else {
if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) {
if (graphicsConfig.isCapPresent(CAPS_PS30) &&
if (getGraphicsConfig().isCapPresent(CAPS_PS30) &&
(sg2d.imageComp == CompositeType.SrcOverNoEa ||
sg2d.imageComp == CompositeType.SrcOver))
{
@@ -300,18 +259,29 @@ public abstract class VKSurfaceData extends SurfaceData
sg2d.imagepipe = vkImagePipe;
}
// TODO this is only used for caps checks, refactor and remove this method
public VKGraphicsConfig getGraphicsConfig() {
return graphicsConfig;
return (VKGraphicsConfig) getDeviceConfiguration();
}
protected synchronized void configure() {
protected int revalidate(VKGraphicsConfig gc) {
if (gc.getFormat() != format) return VolatileImage.IMAGE_INCOMPATIBLE;
else if (this.gc == gc) return VolatileImage.IMAGE_OK;
// TODO proxy cache needs to be cleared for this surface data?
setBlitProxyCache(gc.getGPU().getSurfaceDataProxyCache());
this.gc = gc;
return VolatileImage.IMAGE_RESTORED;
}
protected void configure() {
VKRenderQueue rq = VKRenderQueue.getInstance();
rq.lock();
try {
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(20, 4);
rq.ensureCapacityAndAlignment(24, 4);
buf.putInt(CONFIGURE_SURFACE);
buf.putLong(getNativeOps());
buf.putLong(gc.getGPU().getNativeHandle());
buf.putInt(width);
buf.putInt(height);
@@ -321,5 +291,8 @@ public abstract class VKSurfaceData extends SurfaceData
}
}
public abstract boolean isOnScreen();
@Override
public BufferedContext getContext() {
return VKContext.INSTANCE;
}
}

View File

@@ -1,6 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. 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
@@ -29,17 +28,17 @@ package sun.java2d.vulkan;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.SurfaceData;
import sun.java2d.pipe.hw.AccelSurface;
import java.awt.GraphicsConfiguration;
import java.awt.Transparency;
import java.awt.image.ColorModel;
import sun.java2d.pipe.hw.AccelSurface;
import java.awt.image.VolatileImage;
public class WLVKVolatileSurfaceManager extends VolatileSurfaceManager {
public class VKVolatileSurfaceManager extends VolatileSurfaceManager {
private final boolean accelerationEnabled;
public WLVKVolatileSurfaceManager(SunVolatileImage vImg, Object context) {
public VKVolatileSurfaceManager(SunVolatileImage vImg, Object context) {
super(vImg, context);
/*
@@ -48,7 +47,7 @@ public class WLVKVolatileSurfaceManager extends VolatileSurfaceManager {
*/
int transparency = vImg.getTransparency();
accelerationEnabled = VKInstance.isSurfaceDataAccelerated() &&
accelerationEnabled = VKEnv.isSurfaceDataAccelerated() &&
transparency != Transparency.BITMASK;
}
@@ -62,25 +61,43 @@ public class WLVKVolatileSurfaceManager extends VolatileSurfaceManager {
*/
protected SurfaceData initAcceleratedSurface() {
try {
WLVKGraphicsConfig gc =
(WLVKGraphicsConfig)vImg.getGraphicsConfig();
ColorModel cm = gc.getColorModel(vImg.getTransparency());
VKGraphicsConfig gc = (VKGraphicsConfig) vImg.getGraphicsConfig();
int type = vImg.getForcedAccelSurfaceType();
// if acceleration type is forced (type != UNDEFINED) then
// use the forced type, otherwise choose RT_TEXTURE
if (type == AccelSurface.UNDEFINED) {
type = AccelSurface.RT_TEXTURE;
}
return new VKOffScreenSurfaceData(
gc, vImg, cm, type, vImg.getWidth(), vImg.getHeight());
VKOffScreenSurfaceData sd = new VKOffScreenSurfaceData(vImg, gc.getFormat(), vImg.getTransparency(), type,
vImg.getWidth(), vImg.getHeight());
sd.revalidate(gc);
sd.configure();
return sd;
} catch (NullPointerException | OutOfMemoryError ignored) {
return null;
}
}
@Override
public int validate(GraphicsConfiguration gc) {
if (gc != null && sdAccel != null && isAccelerationEnabled() && isConfigValid(gc)) {
VKSurfaceData vksd = (VKSurfaceData) sdAccel;
switch (vksd.revalidate((VKGraphicsConfig) gc)) {
case VolatileImage.IMAGE_INCOMPATIBLE:
return VolatileImage.IMAGE_INCOMPATIBLE;
case VolatileImage.IMAGE_RESTORED:
vksd.setSurfaceLost(true);
vksd.configure();
}
}
return super.validate(gc);
}
@Override
protected boolean isConfigValid(GraphicsConfiguration gc) {
return ((gc == null) || (gc == vImg.getGraphicsConfig()));
// We consider configs with the same format compatible across Vulkan devices.
return gc == null || vImg.getGraphicsConfig() == null ||
((VKGraphicsConfig) gc).getFormat() == ((VKGraphicsConfig) vImg.getGraphicsConfig()).getFormat();
}
@Override

View File

@@ -0,0 +1,20 @@
#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,11 +1,13 @@
#version 450
#extension GL_GOOGLE_include_directive: require
#define ALPHA_TYPE_SPEC_INDEX 0
#include "alpha_type.glsl"
layout(binding = 0) uniform sampler2D u_TexSampler;
layout(set = 0, binding = 0) uniform texture2D u_Texture;
layout(set = 1, binding = 0) uniform sampler u_Sampler;
layout(location = 0) in vec2 in_TexCoord;
layout(location = 0) out vec4 out_Color;
void main() {
out_Color = texture(u_TexSampler, in_TexCoord);
// TODO: Temporary fix of unexpected transparency with blit operations
out_Color.a = 1.0;
out_Color = convertAlpha(texture(sampler2D(u_Texture, u_Sampler), in_TexCoord));
}

View File

@@ -1,7 +1,7 @@
#version 450
layout(push_constant) uniform PushConstants {
vec2 viewportNormalizer; // 2.0 / viewport
mat2x3 transform;
} push;
layout(location = 0) in vec2 in_Position;
@@ -9,6 +9,6 @@ layout(location = 1) in vec2 in_TexCoord;
layout(location = 0) out vec2 out_TexCoord;
void main() {
gl_Position = vec4(in_Position * push.viewportNormalizer - vec2(1.0), 0.0, 1.0);
gl_Position = vec4(vec3(in_Position, 1.0)*push.transform, 0.0, 1.0);
out_TexCoord = in_TexCoord;
}

View File

@@ -1,11 +1,11 @@
#version 450
layout(push_constant) uniform PushConstants {
vec2 viewportNormalizer; // 2.0 / viewport
mat2x3 transform;
} push;
layout(location = 0) in ivec2 in_Position;
void main() {
gl_Position = vec4(vec2(in_Position) * push.viewportNormalizer - vec2(1.0), 0.0, 1.0);
gl_Position = vec4(vec3(in_Position, 1)*push.transform, 0.0, 1.0);
}

View File

@@ -1,7 +1,7 @@
#version 450
layout(push_constant) uniform PushConstants {
vec2 viewportNormalizer; // 2.0 / viewport
mat2x3 transform;
} push;
layout(location = 0) in vec2 in_Position;
@@ -9,6 +9,6 @@ layout(location = 1) in vec4 in_Color;
layout(location = 0) out flat vec4 out_Color;
void main() {
gl_Position = vec4(in_Position * push.viewportNormalizer - vec2(1.0), 0.0, 1.0);
gl_Position = vec4(vec3(in_Position, 1)*push.transform, 0.0, 1.0);
out_Color = in_Color;
}

View File

@@ -10,7 +10,7 @@ layout(location = 1) in flat vec4 in_Color;
layout(location = 0) out vec4 out_Color;
void main() {
ivec2 maskPos = ivec2(gl_FragCoord.xy) - in_OriginOffsetAndScanline.xy;
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);

View File

@@ -1,7 +1,7 @@
#version 450
layout(push_constant) uniform PushConstants {
vec2 viewportNormalizer; // 2.0 / viewport
mat2x3 transform;
} push;
layout(location = 0) in ivec4 in_PositionOffsetAndScanline;
@@ -14,7 +14,7 @@ layout(location = 0) out flat ivec4 out_OriginOffsetAndScanline;
layout(location = 1) out flat vec4 out_Color;
void main() {
gl_Position = vec4(vec2(in_PositionOffsetAndScanline.xy) * push.viewportNormalizer - vec2(1.0), 0.0, 1.0);
gl_Position = vec4(vec3(in_PositionOffsetAndScanline.xy, 1)*push.transform, 0.0, 1.0);
out_OriginOffsetAndScanline = in_PositionOffsetAndScanline;
out_Color = in_Color;
}

View File

@@ -432,22 +432,22 @@ bool CARR_hash_map_linear_probing_rehash(CARR_MAP_LAYOUT_ARGS, void** handle, CA
/**
* Find a value for the provided key.
* @param P map
* @param KEY key to find, can be a compound literal, like (int){0}
* @param ... key to find, can be a compound literal, like (int){0}
* @return pointer to the found value, or NULL
*/
#define MAP_FIND(P, KEY) \
CARR_MAP_VALUE_PTR((P), CARR_MAP_DISPATCH((P), find, (P), CARR_MAP_KEY_GUARD((P), &(KEY)), NULL, false))
#define MAP_FIND(P, ...) \
CARR_MAP_VALUE_PTR((P), CARR_MAP_DISPATCH((P), find, (P), CARR_MAP_KEY_GUARD((P), &(__VA_ARGS__)), NULL, false))
/**
* Find a value for the provided key, or insert a new one.
* Value is zeroed for newly inserted items.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P map
* @param KEY key to find, can be a compound literal, like (int){0}
* @param ... key to find, can be a compound literal, like (int){0}
* @return dereferenced pointer to the found value
*/
#define MAP_AT(P, KEY) (*(MAP_ENSURE_EXTRA_CAPACITY((P), 1), \
CARR_MAP_VALUE_PTR((P), CARR_MAP_DISPATCH((P), find, (P), CARR_MAP_KEY_GUARD((P), &(KEY)), NULL, true))))
#define MAP_AT(P, ...) (*(MAP_ENSURE_EXTRA_CAPACITY((P), 1), \
CARR_MAP_VALUE_PTR((P), CARR_MAP_DISPATCH((P), find, (P), CARR_MAP_KEY_GUARD((P), &(__VA_ARGS__)), NULL, true))))
/**
* Resolve provided key and find corresponding value.
@@ -474,10 +474,10 @@ bool CARR_hash_map_linear_probing_rehash(CARR_MAP_LAYOUT_ARGS, void** handle, CA
/**
* Remove the provided key, if one exists.
* @param P map
* @param KEY key to remove, can be a compound literal, like (int){0}
* @param ... key to remove, can be a compound literal, like (int){0}
* @return true if the key was removed
*/
#define MAP_REMOVE(P, KEY) CARR_MAP_DISPATCH((P), remove, (P), CARR_MAP_KEY_GUARD((P), &(KEY)))
#define MAP_REMOVE(P, ...) CARR_MAP_DISPATCH((P), remove, (P), CARR_MAP_KEY_GUARD((P), &(__VA_ARGS__)))
/**
* Ensure that map has enough capacity to insert COUNT more items without reallocation.

View File

@@ -26,8 +26,8 @@
#include <assert.h>
#include "VKUtil.h"
#include "VKBase.h"
#include "VKAllocator.h"
#include "VKEnv.h"
/**
* Block size is a minimum allocation size.
@@ -165,9 +165,11 @@ VKMemoryRequirements VKAllocator_ImageRequirements(VKAllocator* allocator, VkIma
return r;
}
void VKAllocator_PadToAlignment(VKMemoryRequirements* requirements) {
void VKAllocator_PadToAlignment(VKAllocator* allocator, VKMemoryRequirements* requirements) {
assert(allocator != NULL);
assert(requirements != NULL);
VkMemoryRequirements* t = &requirements->requirements.memoryRequirements;
if (t->alignment < allocator->device->nonCoherentAtomSize) t->alignment = allocator->device->nonCoherentAtomSize;
t->size = ((t->size + t->alignment - 1) / t->alignment) * t->alignment;
requirements->dedicatedRequirements.requiresDedicatedAllocation = VK_FALSE;
requirements->dedicatedRequirements.prefersDedicatedAllocation = VK_FALSE;
@@ -562,8 +564,7 @@ void VKAllocator_Invalidate(VKAllocator* allocator, VKMemory memory, VkDeviceSiz
}
VKAllocator* VKAllocator_Create(VKDevice* device) {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKAllocator* allocator = (VKAllocator*) calloc(1, sizeof(VKAllocator));
VKAllocator* allocator = calloc(1, sizeof(VKAllocator));
allocator->device = device;
allocator->freePageIndex = NO_PAGE_INDEX;
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
@@ -572,7 +573,7 @@ VKAllocator* VKAllocator_Create(VKDevice* device) {
.allocationLevelTracker = MIN_SHARED_PAGE_LEVEL * 2
};
}
ge->vkGetPhysicalDeviceMemoryProperties(device->physicalDevice, &allocator->memoryProperties);
VKEnv_GetInstance()->vkGetPhysicalDeviceMemoryProperties(device->physicalDevice, &allocator->memoryProperties);
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKAllocator_Create: allocator=%p", allocator);
return allocator;

View File

@@ -62,7 +62,7 @@ VKMemoryRequirements VKAllocator_ImageRequirements(VKAllocator* allocator, VkIma
* This also resets dedicated requirement flags, as for dedicated allocations size must
* be strictly equal to the one returned by resource memory requirements.
*/
void VKAllocator_PadToAlignment(VKMemoryRequirements* requirements);
void VKAllocator_PadToAlignment(VKAllocator* allocator, VKMemoryRequirements* requirements);
/**
* Find memory type with properties not less than requiredProperties and not more than allowedProperties,

View File

@@ -1,730 +0,0 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. 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
* 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.
*/
#include <dlfcn.h>
#include "VKUtil.h"
#include "VKBase.h"
#include "VKAllocator.h"
#include "VKRenderer.h"
#include "VKTexturePool.h"
// For old Vulkan headers - define version-related macros.
#ifndef VK_MAKE_API_VERSION
# define VK_MAKE_API_VERSION(variant, major, minor, patch) VK_MAKE_VERSION(major, minor, patch)
# define VK_API_VERSION_MAJOR(version) VK_VERSION_MAJOR(version)
# define VK_API_VERSION_MINOR(version) VK_VERSION_MINOR(version)
# define VK_API_VERSION_PATCH(version) VK_VERSION_PATCH(version)
#endif
#define VULKAN_DLL JNI_LIB_NAME("vulkan")
#define VULKAN_1_DLL VERSIONED_JNI_LIB_NAME("vulkan", "1")
static const uint32_t REQUIRED_VULKAN_VERSION = VK_MAKE_API_VERSION(0, 1, 2, 0);
#define VALIDATION_LAYER_NAME "VK_LAYER_KHRONOS_validation"
static jboolean verbose;
static VKGraphicsEnvironment* geInstance = NULL;
static void* pVulkanLib = NULL;
#define GET_VK_PROC_RET_FALSE_IF_ERR(GETPROCADDR, STRUCT, HANDLE, NAME) do { \
(STRUCT)->NAME = (PFN_ ## NAME) GETPROCADDR(HANDLE, #NAME); \
if ((STRUCT)->NAME == NULL) { \
J2dRlsTraceLn(J2D_TRACE_ERROR, "Required api is not supported. " #NAME " is missing.") \
return JNI_FALSE; \
} \
} while (0)
static void vulkanLibClose() {
if (pVulkanLib != NULL) {
if (geInstance != NULL) {
ARRAY_FREE(geInstance->physicalDevices);
if (geInstance->devices != NULL) {
for (uint32_t i = 0; i < ARRAY_SIZE(geInstance->devices); i++) {
VKDevice* device = &geInstance->devices[i];
VKRenderer_Destroy(device->renderer);
VKTexturePool_Dispose(device->texturePool);
VKAllocator_Destroy(device->allocator);
ARRAY_FREE(device->enabledExtensions);
ARRAY_FREE(device->enabledLayers);
free(device->name);
if (device->vkDestroyDevice != NULL) {
device->vkDestroyDevice(device->handle, NULL);
}
}
ARRAY_FREE(geInstance->devices);
}
#if defined(DEBUG)
if (geInstance->vkDestroyDebugUtilsMessengerEXT != NULL && geInstance->vkInstance != VK_NULL_HANDLE) {
geInstance->vkDestroyDebugUtilsMessengerEXT(geInstance->vkInstance, geInstance->debugMessenger, NULL);
}
#endif
VKComposites_Destroy(geInstance->composites);
if (geInstance->vkDestroyInstance != NULL) {
geInstance->vkDestroyInstance(geInstance->vkInstance, NULL);
}
free(geInstance);
geInstance = NULL;
}
dlclose(pVulkanLib);
pVulkanLib = NULL;
}
}
static PFN_vkGetInstanceProcAddr vulkanLibOpen() {
if (pVulkanLib == NULL) {
pVulkanLib = dlopen(VULKAN_DLL, RTLD_NOW);
if (pVulkanLib == NULL) {
pVulkanLib = dlopen(VULKAN_1_DLL, RTLD_NOW);
}
if (pVulkanLib == NULL) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "Vulkan: Failed to load %s", VULKAN_DLL)
return NULL;
}
}
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) dlsym(pVulkanLib, "vkGetInstanceProcAddr");
if (vkGetInstanceProcAddr == NULL) {
J2dRlsTraceLn1(J2D_TRACE_ERROR,
"Vulkan: Failed to get proc address of vkGetInstanceProcAddr from %s", VULKAN_DLL)
vulkanLibClose();
return NULL;
}
return vkGetInstanceProcAddr;
}
static const char* physicalDeviceTypeString(VkPhysicalDeviceType type)
{
switch (type)
{
#define STR(r) case VK_PHYSICAL_DEVICE_TYPE_ ##r: return #r
STR(OTHER);
STR(INTEGRATED_GPU);
STR(DISCRETE_GPU);
STR(VIRTUAL_GPU);
STR(CPU);
#undef STR
default: return "UNKNOWN_DEVICE_TYPE";
}
}
#if defined(DEBUG)
static VkBool32 debugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData
) {
if (pCallbackData == NULL) return VK_FALSE;
// Here we can filter messages like this:
// if (std::strcmp(pCallbackData->pMessageIdName, "UNASSIGNED-BestPractices-DrawState-ClearCmdBeforeDraw") == 0) return VK_FALSE;
int level = J2D_TRACE_OFF;
if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) level = J2D_TRACE_VERBOSE;
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) level = J2D_TRACE_INFO;
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) level = J2D_TRACE_WARNING;
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) level = J2D_TRACE_ERROR;
J2dRlsTraceLn(level, pCallbackData->pMessage);
if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
VK_FATAL_ERROR("Unhandled Vulkan validation error");
}
return VK_FALSE;
}
#endif
static jboolean VK_InitGraphicsEnvironment(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) {
geInstance->vkInstance = VK_NULL_HANDLE;
#define INSTANCE_PROC(NAME) GET_VK_PROC_RET_FALSE_IF_ERR(vkGetInstanceProcAddr, geInstance, geInstance->vkInstance, NAME)
INSTANCE_PROC(vkEnumerateInstanceVersion);
INSTANCE_PROC(vkEnumerateInstanceExtensionProperties);
INSTANCE_PROC(vkEnumerateInstanceLayerProperties);
INSTANCE_PROC(vkCreateInstance);
uint32_t apiVersion = 0;
VK_IF_ERROR(geInstance->vkEnumerateInstanceVersion(&apiVersion)) return JNI_FALSE;
J2dRlsTraceLn3(J2D_TRACE_INFO, "Vulkan: Available (%d.%d.%d)",
VK_API_VERSION_MAJOR(apiVersion),
VK_API_VERSION_MINOR(apiVersion),
VK_API_VERSION_PATCH(apiVersion))
if (apiVersion < REQUIRED_VULKAN_VERSION) {
J2dRlsTraceLn3(J2D_TRACE_ERROR, "Vulkan: Unsupported version. Required at least (%d.%d.%d)",
VK_API_VERSION_MAJOR(REQUIRED_VULKAN_VERSION),
VK_API_VERSION_MINOR(REQUIRED_VULKAN_VERSION),
VK_API_VERSION_PATCH(REQUIRED_VULKAN_VERSION))
return JNI_FALSE;
}
uint32_t extensionsCount;
// Get the number of extensions and layers
VK_IF_ERROR(geInstance->vkEnumerateInstanceExtensionProperties(NULL, &extensionsCount, NULL)) return JNI_FALSE;
VkExtensionProperties extensions[extensionsCount];
VK_IF_ERROR(geInstance->vkEnumerateInstanceExtensionProperties(NULL, &extensionsCount, extensions)) return JNI_FALSE;
uint32_t layersCount;
VK_IF_ERROR(geInstance->vkEnumerateInstanceLayerProperties(&layersCount, NULL)) return JNI_FALSE;
VkLayerProperties layers[layersCount];
VK_IF_ERROR(geInstance->vkEnumerateInstanceLayerProperties(&layersCount, layers)) return JNI_FALSE;
J2dRlsTraceLn(J2D_TRACE_VERBOSE, " Supported instance layers:")
for (uint32_t i = 0; i < layersCount; i++) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) layers[i].layerName)
}
J2dRlsTraceLn(J2D_TRACE_VERBOSE, " Supported instance extensions:")
for (uint32_t i = 0; i < extensionsCount; i++) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) extensions[i].extensionName)
}
ARRAY(pchar) enabledLayers = NULL;
ARRAY(pchar) enabledExtensions = NULL;
void *pNext = NULL;
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
ARRAY_PUSH_BACK(enabledExtensions) = VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
#endif
ARRAY_PUSH_BACK(enabledExtensions) = VK_KHR_SURFACE_EXTENSION_NAME;
// Check required layers & extensions.
for (uint32_t i = 0; i < ARRAY_SIZE(enabledExtensions); i++) {
int notFound = 1;
for (uint32_t j = 0; j < extensionsCount; j++) {
if (strcmp((char *) extensions[j].extensionName, enabledExtensions[i]) == 0) {
notFound = 0;
break;
}
}
if (notFound) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "Vulkan: Required extension %s not found", enabledExtensions[i])
ARRAY_FREE(enabledLayers);
ARRAY_FREE(enabledExtensions);
return JNI_FALSE;
}
}
// Configure validation
#ifdef DEBUG
VkValidationFeatureEnableEXT enables[] = {
// VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,
// VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT,
VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
// VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT,
VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT
};
VkValidationFeaturesEXT features = {};
features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
features.enabledValidationFeatureCount = SARRAY_COUNT_OF(enables);
features.pEnabledValidationFeatures = enables;
// Includes the validation features into the instance creation process
int foundDebugLayer = 0;
for (uint32_t i = 0; i < layersCount; i++) {
if (strcmp((char *) layers[i].layerName, VALIDATION_LAYER_NAME) == 0) {
foundDebugLayer = 1;
break;
}
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) layers[i].layerName)
}
int foundDebugExt = 0;
for (uint32_t i = 0; i < extensionsCount; i++) {
if (strcmp((char *) extensions[i].extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) {
foundDebugExt = 1;
break;
}
}
if (foundDebugLayer && foundDebugExt) {
ARRAY_PUSH_BACK(enabledLayers) = VALIDATION_LAYER_NAME;
ARRAY_PUSH_BACK(enabledExtensions) = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
pNext = &features;
} else {
J2dRlsTraceLn2(J2D_TRACE_WARNING, "Vulkan: %s and %s are not supported",
VALIDATION_LAYER_NAME, VK_EXT_DEBUG_UTILS_EXTENSION_NAME)
}
#endif
VkApplicationInfo applicationInfo = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = NULL,
.pApplicationName = "OpenJDK",
.applicationVersion = 0,
.pEngineName = "OpenJDK",
.engineVersion = 0,
.apiVersion = REQUIRED_VULKAN_VERSION
};
VkInstanceCreateInfo instanceCreateInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = pNext,
.flags = 0,
.pApplicationInfo = &applicationInfo,
.enabledLayerCount = ARRAY_SIZE(enabledLayers),
.ppEnabledLayerNames = (const char *const *) enabledLayers,
.enabledExtensionCount = ARRAY_SIZE(enabledExtensions),
.ppEnabledExtensionNames = (const char *const *) enabledExtensions
};
VK_IF_ERROR(geInstance->vkCreateInstance(&instanceCreateInfo, NULL, &geInstance->vkInstance)) {
ARRAY_FREE(enabledLayers);
ARRAY_FREE(enabledExtensions);
return JNI_FALSE;
}
J2dRlsTraceLn(J2D_TRACE_INFO, "Vulkan: Instance Created")
ARRAY_FREE(enabledLayers);
ARRAY_FREE(enabledExtensions);
geInstance->composites = VKComposites_Create();
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
INSTANCE_PROC(vkGetPhysicalDeviceWaylandPresentationSupportKHR);
INSTANCE_PROC(vkCreateWaylandSurfaceKHR);
#endif
INSTANCE_PROC(vkDestroyInstance);
INSTANCE_PROC(vkEnumeratePhysicalDevices);
INSTANCE_PROC(vkGetPhysicalDeviceMemoryProperties);
INSTANCE_PROC(vkGetPhysicalDeviceFeatures2);
INSTANCE_PROC(vkGetPhysicalDeviceProperties2);
INSTANCE_PROC(vkGetPhysicalDeviceQueueFamilyProperties);
INSTANCE_PROC(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
INSTANCE_PROC(vkGetPhysicalDeviceSurfaceFormatsKHR);
INSTANCE_PROC(vkGetPhysicalDeviceSurfacePresentModesKHR);
INSTANCE_PROC(vkEnumerateDeviceLayerProperties);
INSTANCE_PROC(vkEnumerateDeviceExtensionProperties);
INSTANCE_PROC(vkCreateDevice);
INSTANCE_PROC(vkDestroySurfaceKHR);
INSTANCE_PROC(vkGetDeviceProcAddr);
// Create debug messenger
#if defined(DEBUG)
if (foundDebugLayer && foundDebugExt) {
INSTANCE_PROC(vkCreateDebugUtilsMessengerEXT);
INSTANCE_PROC(vkDestroyDebugUtilsMessengerEXT);
if (pNext) {
VkDebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.flags = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
.pfnUserCallback = &debugCallback
};
VK_IF_ERROR(geInstance->vkCreateDebugUtilsMessengerEXT(geInstance->vkInstance, &debugUtilsMessengerCreateInfo,
NULL, &geInstance->debugMessenger)) {}
}
}
#endif
return JNI_TRUE;
}
static jboolean VK_FindDevices() {
uint32_t physicalDevicesCount;
VK_IF_ERROR(geInstance->vkEnumeratePhysicalDevices(geInstance->vkInstance,
&physicalDevicesCount, NULL)) return JNI_FALSE;
if (physicalDevicesCount == 0) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: Failed to find GPUs with Vulkan support")
return JNI_FALSE;
} else {
J2dRlsTraceLn1(J2D_TRACE_INFO, "Vulkan: Found %d physical devices:", physicalDevicesCount)
}
ARRAY_RESIZE(geInstance->physicalDevices, physicalDevicesCount);
VK_IF_ERROR(geInstance->vkEnumeratePhysicalDevices(geInstance->vkInstance, &physicalDevicesCount,
geInstance->physicalDevices)) return JNI_FALSE;
ARRAY_ENSURE_CAPACITY(geInstance->devices, physicalDevicesCount);
for (uint32_t i = 0; i < physicalDevicesCount; i++) {
VkPhysicalDeviceVulkan12Features device12Features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.pNext = NULL
};
VkPhysicalDeviceFeatures2 deviceFeatures2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = &device12Features
};
geInstance->vkGetPhysicalDeviceFeatures2(geInstance->physicalDevices[i], &deviceFeatures2);
VkPhysicalDeviceProperties2 deviceProperties2 = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 };
geInstance->vkGetPhysicalDeviceProperties2(geInstance->physicalDevices[i], &deviceProperties2);
J2dRlsTrace5(J2D_TRACE_INFO, "\t- %s (%d.%d.%d, %s) ",
(const char *) deviceProperties2.properties.deviceName,
VK_API_VERSION_MAJOR(deviceProperties2.properties.apiVersion),
VK_API_VERSION_MINOR(deviceProperties2.properties.apiVersion),
VK_API_VERSION_PATCH(deviceProperties2.properties.apiVersion),
physicalDeviceTypeString(deviceProperties2.properties.deviceType))
if (!deviceFeatures2.features.logicOp) {
J2dRlsTraceLn(J2D_TRACE_INFO, " - hasLogicOp not supported, skipped")
continue;
}
if (!device12Features.timelineSemaphore) {
J2dRlsTraceLn(J2D_TRACE_INFO, " - hasTimelineSemaphore not supported, skipped")
continue;
}
J2dRlsTraceLn(J2D_TRACE_INFO, "")
uint32_t queueFamilyCount = 0;
geInstance->vkGetPhysicalDeviceQueueFamilyProperties(
geInstance->physicalDevices[i], &queueFamilyCount, NULL);
VkQueueFamilyProperties queueFamilies[queueFamilyCount];
geInstance->vkGetPhysicalDeviceQueueFamilyProperties(
geInstance->physicalDevices[i], &queueFamilyCount, queueFamilies);
int64_t queueFamily = -1;
for (uint32_t j = 0; j < queueFamilyCount; j++) {
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
VkBool32 presentationSupported =
geInstance->vkGetPhysicalDeviceWaylandPresentationSupportKHR(
geInstance->physicalDevices[i], j, geInstance->waylandDisplay);
#endif
char logFlags[5] = {
queueFamilies[j].queueFlags & VK_QUEUE_GRAPHICS_BIT ? 'G' : '-',
queueFamilies[j].queueFlags & VK_QUEUE_COMPUTE_BIT ? 'C' : '-',
queueFamilies[j].queueFlags & VK_QUEUE_TRANSFER_BIT ? 'T' : '-',
queueFamilies[j].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT ? 'S' : '-',
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
presentationSupported ? 'P' : '-'
#else
'-'
#endif
};
J2dRlsTraceLn3(J2D_TRACE_INFO, " %d queues in family (%.*s)", queueFamilies[j].queueCount, 5,
logFlags)
// TODO use compute workloads? Separate transfer-only DMA queue?
if (queueFamily == -1 && (queueFamilies[j].queueFlags & VK_QUEUE_GRAPHICS_BIT)
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
&& presentationSupported
#endif
) {
queueFamily = j;
}
}
if (queueFamily == -1) {
J2dRlsTraceLn(J2D_TRACE_INFO, " --------------------- Suitable queue not found, skipped")
continue;
}
uint32_t layerCount;
VK_IF_ERROR(geInstance->vkEnumerateDeviceLayerProperties(geInstance->physicalDevices[i],
&layerCount, NULL)) continue;
VkLayerProperties layers[layerCount];
VK_IF_ERROR(geInstance->vkEnumerateDeviceLayerProperties(geInstance->physicalDevices[i],
&layerCount, layers)) continue;
J2dRlsTraceLn(J2D_TRACE_VERBOSE, " Supported device layers:")
for (uint32_t j = 0; j < layerCount; j++) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) layers[j].layerName)
}
uint32_t extensionCount;
VK_IF_ERROR(geInstance->vkEnumerateDeviceExtensionProperties(geInstance->physicalDevices[i],
NULL, &extensionCount, NULL)) continue;
VkExtensionProperties extensions[extensionCount];
VK_IF_ERROR(geInstance->vkEnumerateDeviceExtensionProperties(geInstance->physicalDevices[i],
NULL, &extensionCount, extensions)) continue;
J2dRlsTraceLn(J2D_TRACE_VERBOSE, " Supported device extensions:")
VkBool32 hasSwapChain = VK_FALSE;
for (uint32_t j = 0; j < extensionCount; j++) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) extensions[j].extensionName)
hasSwapChain = hasSwapChain ||
strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, extensions[j].extensionName) == 0;
}
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "Vulkan: Found device extensions:")
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " " VK_KHR_SWAPCHAIN_EXTENSION_NAME " = %s", hasSwapChain ? "true" : "false")
if (!hasSwapChain) {
J2dRlsTraceLn(J2D_TRACE_INFO,
" --------------------- Required " VK_KHR_SWAPCHAIN_EXTENSION_NAME " not found, skipped")
continue;
}
ARRAY(pchar) deviceEnabledLayers = NULL;
ARRAY(pchar) deviceEnabledExtensions = NULL;
ARRAY_PUSH_BACK(deviceEnabledExtensions) = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
// Validation layer
#ifdef DEBUG
int validationLayerNotSupported = 1;
for (uint32_t j = 0; j < layerCount; j++) {
if (strcmp(VALIDATION_LAYER_NAME, layers[j].layerName) == 0) {
validationLayerNotSupported = 0;
ARRAY_PUSH_BACK(deviceEnabledLayers) = VALIDATION_LAYER_NAME;
break;
}
}
if (validationLayerNotSupported) {
J2dRlsTraceLn1(J2D_TRACE_INFO, " %s device layer is not supported", VALIDATION_LAYER_NAME)
}
#endif
char* deviceName = strdup(deviceProperties2.properties.deviceName);
if (deviceName == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: Cannot duplicate deviceName")
return JNI_FALSE;
}
ARRAY_PUSH_BACK(geInstance->devices) = (VKDevice) {
.name = deviceName,
.handle = VK_NULL_HANDLE,
.physicalDevice = geInstance->physicalDevices[i],
.queueFamily = queueFamily,
.enabledLayers = deviceEnabledLayers,
.enabledExtensions = deviceEnabledExtensions
};
}
if (ARRAY_SIZE(geInstance->devices) == 0) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: No compatible device found")
return JNI_FALSE;
}
return JNI_TRUE;
}
static jboolean VK_InitDevice(VKDevice* device) {
if (device->handle != VK_NULL_HANDLE) {
return JNI_TRUE;
}
if (geInstance == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: VKGraphicsEnvironment is not initialized")
return JNI_FALSE;
}
if (verbose) {
for (uint32_t i = 0; i < ARRAY_SIZE(geInstance->devices); i++) {
fprintf(stderr, " %c%d: %s\n", &geInstance->devices[i] == device ? '*' : ' ',
i, geInstance->devices[i].name);
}
fprintf(stderr, "\n");
}
float queuePriority = 1.0f;
VkDeviceQueueCreateInfo queueCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = device->queueFamily, // obtained separately
.queueCount = 1,
.pQueuePriorities = &queuePriority
};
VkPhysicalDeviceFeatures features10 = { .logicOp = VK_TRUE };
VkPhysicalDeviceVulkan12Features features12 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.timelineSemaphore = VK_TRUE
};
void *pNext = &features12;
VkDeviceCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = pNext,
.flags = 0,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queueCreateInfo,
.enabledLayerCount = ARRAY_SIZE(device->enabledLayers),
.ppEnabledLayerNames = (const char *const *) device->enabledLayers,
.enabledExtensionCount = ARRAY_SIZE(device->enabledExtensions),
.ppEnabledExtensionNames = (const char *const *) device->enabledExtensions,
.pEnabledFeatures = &features10
};
VK_IF_ERROR(geInstance->vkCreateDevice(device->physicalDevice, &createInfo, NULL, &device->handle)) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "Vulkan: Cannot create device: %s", device->name)
return JNI_FALSE;
}
J2dRlsTraceLn1(J2D_TRACE_INFO, "VK_InitDevice(%s)", device->name);
#define DEVICE_PROC(NAME) GET_VK_PROC_RET_FALSE_IF_ERR(geInstance->vkGetDeviceProcAddr, device, device->handle, NAME)
DEVICE_PROC(vkDestroyDevice);
DEVICE_PROC(vkCreateShaderModule);
DEVICE_PROC(vkDestroyShaderModule);
DEVICE_PROC(vkCreatePipelineLayout);
DEVICE_PROC(vkDestroyPipelineLayout);
DEVICE_PROC(vkCreateGraphicsPipelines);
DEVICE_PROC(vkDestroyPipeline);
DEVICE_PROC(vkCreateSwapchainKHR);
DEVICE_PROC(vkDestroySwapchainKHR);
DEVICE_PROC(vkGetSwapchainImagesKHR);
DEVICE_PROC(vkCreateImageView);
DEVICE_PROC(vkCreateFramebuffer);
DEVICE_PROC(vkCreateCommandPool);
DEVICE_PROC(vkDestroyCommandPool);
DEVICE_PROC(vkAllocateCommandBuffers);
DEVICE_PROC(vkFreeCommandBuffers);
DEVICE_PROC(vkCreateSemaphore);
DEVICE_PROC(vkDestroySemaphore);
DEVICE_PROC(vkWaitSemaphores);
DEVICE_PROC(vkGetSemaphoreCounterValue);
DEVICE_PROC(vkCreateFence);
DEVICE_PROC(vkGetDeviceQueue);
DEVICE_PROC(vkWaitForFences);
DEVICE_PROC(vkResetFences);
DEVICE_PROC(vkAcquireNextImageKHR);
DEVICE_PROC(vkResetCommandBuffer);
DEVICE_PROC(vkQueueSubmit);
DEVICE_PROC(vkQueuePresentKHR);
DEVICE_PROC(vkBeginCommandBuffer);
DEVICE_PROC(vkCmdBlitImage);
DEVICE_PROC(vkCmdPipelineBarrier);
DEVICE_PROC(vkCmdBeginRenderPass);
DEVICE_PROC(vkCmdExecuteCommands);
DEVICE_PROC(vkCmdClearAttachments);
DEVICE_PROC(vkCmdBindPipeline);
DEVICE_PROC(vkCmdSetViewport);
DEVICE_PROC(vkCmdSetScissor);
DEVICE_PROC(vkCmdDraw);
DEVICE_PROC(vkCmdEndRenderPass);
DEVICE_PROC(vkEndCommandBuffer);
DEVICE_PROC(vkCreateImage);
DEVICE_PROC(vkCreateSampler);
DEVICE_PROC(vkDestroySampler);
DEVICE_PROC(vkAllocateMemory);
DEVICE_PROC(vkBindImageMemory);
DEVICE_PROC(vkCreateDescriptorSetLayout);
DEVICE_PROC(vkDestroyDescriptorSetLayout);
DEVICE_PROC(vkUpdateDescriptorSets);
DEVICE_PROC(vkCreateDescriptorPool);
DEVICE_PROC(vkDestroyDescriptorPool);
DEVICE_PROC(vkAllocateDescriptorSets);
DEVICE_PROC(vkCmdBindDescriptorSets);
DEVICE_PROC(vkGetImageMemoryRequirements2);
DEVICE_PROC(vkCreateBuffer);
DEVICE_PROC(vkDestroyBuffer);
DEVICE_PROC(vkCreateBufferView);
DEVICE_PROC(vkDestroyBufferView);
DEVICE_PROC(vkGetBufferMemoryRequirements2);
DEVICE_PROC(vkBindBufferMemory);
DEVICE_PROC(vkMapMemory);
DEVICE_PROC(vkUnmapMemory);
DEVICE_PROC(vkCmdBindVertexBuffers);
DEVICE_PROC(vkCreateRenderPass);
DEVICE_PROC(vkDestroyRenderPass);
DEVICE_PROC(vkFreeMemory);
DEVICE_PROC(vkDestroyImageView);
DEVICE_PROC(vkDestroyImage);
DEVICE_PROC(vkDestroyFramebuffer);
DEVICE_PROC(vkFlushMappedMemoryRanges);
DEVICE_PROC(vkInvalidateMappedMemoryRanges);
DEVICE_PROC(vkCmdPushConstants);
DEVICE_PROC(vkCmdCopyBufferToImage);
DEVICE_PROC(vkCmdCopyImageToBuffer);
device->vkGetDeviceQueue(device->handle, device->queueFamily, 0, &device->queue);
if (device->queue == NULL) {
J2dRlsTraceLn(J2D_TRACE_INFO, "Vulkan: Failed to get device queue");
VK_UNHANDLED_ERROR();
return JNI_FALSE;
}
device->allocator = VKAllocator_Create(device);
if (!device->allocator) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: Cannot create allocator")
VK_UNHANDLED_ERROR();
return JNI_FALSE;
}
device->renderer = VKRenderer_Create(device);
if (!device->renderer) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: Cannot create renderer")
VK_UNHANDLED_ERROR();
return JNI_FALSE;
}
device->texturePool = VKTexturePool_InitWithDevice(device);
if (!device->texturePool) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: Cannot create texture pool")
VK_UNHANDLED_ERROR();
return JNI_FALSE;
}
geInstance->currentDevice = device;
return JNI_TRUE;
}
VKGraphicsEnvironment* VKGE_graphics_environment() {
return geInstance;
}
/*
* Class: sun_java2d_vulkan_VKInstance
* Method: init
* Signature: (JZI)Z
*/
JNIEXPORT jboolean JNICALL
Java_sun_java2d_vulkan_VKInstance_initNative(JNIEnv *env, jclass wlge, jlong nativePtr, jboolean verb, jint requestedDevice) {
#ifdef DEBUG
// Init random for debug-related validation tricks.
srand(nativePtr);
#endif
verbose = verb;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = vulkanLibOpen();
if (vkGetInstanceProcAddr == NULL) {
return JNI_FALSE;
}
geInstance = (VKGraphicsEnvironment*)malloc(sizeof(VKGraphicsEnvironment));
if (geInstance == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: Cannot allocate VKGraphicsEnvironment")
vulkanLibClose();
return JNI_FALSE;
}
*geInstance = (VKGraphicsEnvironment) {};
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
geInstance->waylandDisplay = (struct wl_display*) jlong_to_ptr(nativePtr);
#endif
if (!VK_InitGraphicsEnvironment(vkGetInstanceProcAddr)) {
vulkanLibClose();
return JNI_FALSE;
}
if (!VK_FindDevices()) {
vulkanLibClose();
return JNI_FALSE;
}
if (requestedDevice < 0 || (uint32_t)requestedDevice >= ARRAY_SIZE(geInstance->devices)) {
requestedDevice = 0;
}
if (!VK_InitDevice(&geInstance->devices[requestedDevice])) {
vulkanLibClose();
return JNI_FALSE;
}
return JNI_TRUE;
}
JNIEXPORT void JNICALL JNI_OnUnload(__attribute__((unused)) JavaVM *vm, __attribute__((unused)) void *reserved) {
vulkanLibClose();
}

View File

@@ -1,167 +0,0 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. 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
* 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.
*/
#ifndef VKBase_h_Included
#define VKBase_h_Included
#include "VKTypes.h"
#include "VKComposites.h"
#include "VKTexturePool.h"
#include "VKRenderState.h"
#include "VKUtil.h"
struct VKDevice {
VkDevice handle;
VkPhysicalDevice physicalDevice;
char* name;
uint32_t queueFamily;
ARRAY(pchar) enabledLayers;
ARRAY(pchar) enabledExtensions;
VkQueue queue;
VKAllocator* allocator;
VKRenderer* renderer;
VKTexturePool* texturePool;
PFN_vkDestroyDevice vkDestroyDevice;
PFN_vkCreateShaderModule vkCreateShaderModule;
PFN_vkDestroyShaderModule vkDestroyShaderModule;
PFN_vkCreatePipelineLayout vkCreatePipelineLayout;
PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout;
PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
PFN_vkDestroyPipeline vkDestroyPipeline;
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR;
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
PFN_vkCreateImageView vkCreateImageView;
PFN_vkCreateFramebuffer vkCreateFramebuffer;
PFN_vkCreateCommandPool vkCreateCommandPool;
PFN_vkDestroyCommandPool vkDestroyCommandPool;
PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
PFN_vkCreateSemaphore vkCreateSemaphore;
PFN_vkDestroySemaphore vkDestroySemaphore;
PFN_vkWaitSemaphores vkWaitSemaphores;
PFN_vkGetSemaphoreCounterValue vkGetSemaphoreCounterValue;
PFN_vkCreateFence vkCreateFence;
PFN_vkGetDeviceQueue vkGetDeviceQueue;
PFN_vkWaitForFences vkWaitForFences;
PFN_vkResetFences vkResetFences;
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
PFN_vkResetCommandBuffer vkResetCommandBuffer;
PFN_vkQueueSubmit vkQueueSubmit;
PFN_vkQueuePresentKHR vkQueuePresentKHR;
PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
PFN_vkCmdBlitImage vkCmdBlitImage;
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
PFN_vkCmdExecuteCommands vkCmdExecuteCommands;
PFN_vkCmdClearAttachments vkCmdClearAttachments;
PFN_vkCmdBindPipeline vkCmdBindPipeline;
PFN_vkCmdSetViewport vkCmdSetViewport;
PFN_vkCmdSetScissor vkCmdSetScissor;
PFN_vkCmdDraw vkCmdDraw;
PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
PFN_vkEndCommandBuffer vkEndCommandBuffer;
PFN_vkCreateImage vkCreateImage;
PFN_vkCreateSampler vkCreateSampler;
PFN_vkDestroySampler vkDestroySampler;
PFN_vkAllocateMemory vkAllocateMemory;
PFN_vkBindImageMemory vkBindImageMemory;
PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets;
PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets;
PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;
PFN_vkCreateBuffer vkCreateBuffer;
PFN_vkDestroyBuffer vkDestroyBuffer;
PFN_vkCreateBufferView vkCreateBufferView;
PFN_vkDestroyBufferView vkDestroyBufferView;
PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;
PFN_vkBindBufferMemory vkBindBufferMemory;
PFN_vkMapMemory vkMapMemory;
PFN_vkUnmapMemory vkUnmapMemory;
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
PFN_vkCreateRenderPass vkCreateRenderPass;
PFN_vkDestroyRenderPass vkDestroyRenderPass;
PFN_vkFreeMemory vkFreeMemory;
PFN_vkDestroyImageView vkDestroyImageView;
PFN_vkDestroyImage vkDestroyImage;
PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
PFN_vkCmdPushConstants vkCmdPushConstants;
PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage;
PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer;
};
struct VKGraphicsEnvironment {
VkInstance vkInstance;
ARRAY(VkPhysicalDevice) physicalDevices;
ARRAY(VKDevice) devices;
VKDevice* currentDevice;
VKComposites composites;
#if defined(DEBUG)
VkDebugUtilsMessengerEXT debugMessenger;
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT;
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT;
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
struct wl_display* waylandDisplay;
PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR;
PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
#endif
PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion;
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
PFN_vkCreateInstance vkCreateInstance;
PFN_vkDestroyInstance vkDestroyInstance;
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2;
PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2;
PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties;
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties;
PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
PFN_vkCreateDevice vkCreateDevice;
PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
};
VKGraphicsEnvironment* VKGE_graphics_environment();
#endif //VKBase_h_Included

View File

@@ -37,22 +37,79 @@
#include "Trace.h"
#include "VKImage.h"
#include "VKBuffer.h"
#include "VKDevice.h"
#include "VKTexturePool.h"
#include "VKUtil.h"
#include "VKRenderer.h"
static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context, VKImage* dest, const SurfaceDataRasInfo *srcInfo,
int dx1, int dy1, int dx2, int dy2) {
#define SRCTYPE_BITS sun_java2d_vulkan_VKSwToSurfaceBlit_SRCTYPE_BITS
typedef struct {
VkFormat format;
VKPackedSwizzle swizzle;
} BlitSrcType;
// See encodeSrcType() in VKBlitLoops.java
static BlitSrcType decodeSrcType(VKDevice* device, jshort srctype) {
jshort type = (jshort) (srctype & sun_java2d_vulkan_VKSwToSurfaceBlit_SRCTYPE_MASK);
const VKSampledSrcType* entry = &device->sampledSrcTypes.table[type];
BlitSrcType result = { entry->format, 0 };
switch (type) {
case sun_java2d_vulkan_VKSwToSurfaceBlit_SRCTYPE_4BYTE: {
uint32_t components[] = {
((uint32_t) srctype >> SRCTYPE_BITS ) & 0b11,
((uint32_t) srctype >> (SRCTYPE_BITS + 2)) & 0b11,
((uint32_t) srctype >> (SRCTYPE_BITS + 4)) & 0b11,
((uint32_t) srctype >> (SRCTYPE_BITS + 6)) & 0b11
};
result.swizzle = VK_PACK_SWIZZLE(
entry->components[components[0]],
entry->components[components[1]],
entry->components[components[2]],
components[3] == components[0] ? VK_COMPONENT_SWIZZLE_ONE : // Special case, a = r means no alpha.
entry->components[components[3]]
);
} break;
case sun_java2d_vulkan_VKSwToSurfaceBlit_SRCTYPE_3BYTE: {
uint32_t components[] = {
((uint32_t) srctype >> SRCTYPE_BITS ) & 0b11,
((uint32_t) srctype >> (SRCTYPE_BITS + 2)) & 0b11,
((uint32_t) srctype >> (SRCTYPE_BITS + 4)) & 0b11
};
result.swizzle = VK_PACK_SWIZZLE(
entry->components[components[0]],
entry->components[components[1]],
entry->components[components[2]],
VK_COMPONENT_SWIZZLE_ONE
);
} break;
default: {
result.swizzle =
VK_PACK_SWIZZLE(entry->components[0], entry->components[1], entry->components[2], entry->components[3]);
} break;
}
return result;
}
static AlphaType getSrcAlphaType(jshort srctype) {
return srctype & sun_java2d_vulkan_VKSwToSurfaceBlit_SRCTYPE_PRE_MULTIPLIED_ALPHA_BIT ?
ALPHA_TYPE_PRE_MULTIPLIED : ALPHA_TYPE_STRAIGHT;
}
static void VKTexturePoolTexture_Dispose(VKDevice* device, void* ctx) {
VKTexturePoolHandle* hnd = (VKTexturePoolHandle*) ctx;
VKTexturePoolHandle_ReleaseTexture(hnd);
}
static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context,
VKSDOps *dstOps,
const SurfaceDataRasInfo *srcInfo, jshort srctype, jint hint,
int dx1, int dy1, int dx2, int dy2) {
VKSDOps* surface = context->surface;
VKDevice* device = surface->device;
const int sw = srcInfo->bounds.x2 - srcInfo->bounds.x1;
const int sh = srcInfo->bounds.y2 - srcInfo->bounds.y1;
const int dw = dx2 - dx1;
const int dh = dy2 - dy1;
if (dw < sw || dh < sh) {
J2dTraceLn4(J2D_TRACE_ERROR, "VKBlitSwToTextureViaPooledTexture: dest size: (%d, %d) less than source size: (%d, %d)", dw, dh, sw, sh);
return;
}
ARRAY(VKTxVertex) vertices = ARRAY_ALLOC(VKTxVertex, 4);
/*
@@ -63,9 +120,10 @@ static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context, VKIma
* (p4)---------(p3)
*/
VKTexturePoolHandle* hnd = VKTexturePool_GetTexture(device->texturePool, sw, sh, surface->image->format);
double u = (double)sw / VKTexturePoolHandle_GetActualWidth(hnd);
double v = (double)sh / VKTexturePoolHandle_GetActualHeight(hnd);
BlitSrcType type = decodeSrcType(device, srctype);
VKTexturePoolHandle* hnd = VKTexturePool_GetTexture(device->texturePool, sw, sh, type.format);
double u = (double)sw;
double v = (double)sh;
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx1, dy1, 0.0f, 0.0f};
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx2, dy1, u, 0.0f};
@@ -75,126 +133,47 @@ static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context, VKIma
VKBuffer* renderVertexBuffer = ARRAY_TO_VERTEX_BUF(device, vertices);
ARRAY_FREE(vertices);
const char *raster = srcInfo->rasBase;
raster += (uint32_t)srcInfo->bounds.y1 * (uint32_t)srcInfo->scanStride + (uint32_t)srcInfo->bounds.x1 * (uint32_t)srcInfo->pixelStride;
J2dTraceLn4(J2D_TRACE_VERBOSE, "replaceTextureRegion src (dw, dh) : [%d, %d] dest (dx1, dy1) =[%d, %d]",
dw, dh, dx1, dy1);
uint32_t dataSize = sw * sh * srcInfo->pixelStride;
char* data = malloc(dataSize);
// copy src pixels inside src bounds to buff
for (int row = 0; row < sh; row++) {
memcpy(data + (row * sw * srcInfo->pixelStride), raster, sw * srcInfo->pixelStride);
raster += (uint32_t)srcInfo->scanStride;
}
VKBuffer *buffer = VKBuffer_CreateFromData(device, data, dataSize);
free(data);
(dx2 - dx1), (dy2 - dy1), dx1, dy1);
VKBuffer *buffer =
VKBuffer_CreateFromRaster(device, (VKBuffer_RasterInfo){
.data = srcInfo->rasBase,
.x1 = srcInfo->bounds.x1,
.y1 = srcInfo->bounds.y1,
.w = sw,
.h = sh,
.pixelStride = srcInfo->pixelStride,
.scanStride = srcInfo->scanStride
}, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT);
VkCommandBuffer cb = VKRenderer_Record(device->renderer);
{
VkImageMemoryBarrier barrier;
VKBarrierBatch barrierBatch = {};
VKRenderer_AddImageBarrier(&barrier, &barrierBatch, ((VKImage *) VKTexturePoolHandle_GetTexture(hnd)),
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
if (barrierBatch.barrierCount > 0) {
device->vkCmdPipelineBarrier(cb, barrierBatch.srcStages, barrierBatch.dstStages,
0, 0, NULL,
0, NULL,
barrierBatch.barrierCount, &barrier);
}
VKImage_AddBarrier(&barrier, &barrierBatch, VKTexturePoolHandle_GetTexture(hnd),
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VKRenderer_RecordBarriers(device->renderer, NULL, NULL, &barrier, &barrierBatch);
}
VKImage_LoadBuffer(context->surface->device,
VKTexturePoolHandle_GetTexture(hnd), buffer, 0, 0, sw, sh);
{
VkImageMemoryBarrier barrier;
VKBarrierBatch barrierBatch = {};
VKRenderer_AddImageBarrier(&barrier, &barrierBatch, ((VKImage *) VKTexturePoolHandle_GetTexture(hnd)),
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_ACCESS_SHADER_READ_BIT,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
if (barrierBatch.barrierCount > 0) {
device->vkCmdPipelineBarrier(cb, barrierBatch.srcStages, barrierBatch.dstStages,
0, 0, NULL,
0, NULL,
barrierBatch.barrierCount, &barrier);
}
VKImage_AddBarrier(&barrier, &barrierBatch, VKTexturePoolHandle_GetTexture(hnd),
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_ACCESS_SHADER_READ_BIT,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
VKRenderer_RecordBarriers(device->renderer, NULL, NULL, &barrier, &barrierBatch);
}
VKRenderer_TextureRender(context, dest, VKTexturePoolHandle_GetTexture(hnd),
renderVertexBuffer->handle, 4);
VKImage* src = VKTexturePoolHandle_GetTexture(hnd);
VkDescriptorSet srcDescriptorSet = VKImage_GetDescriptorSet(device, src, type.format, type.swizzle);
VKRenderer_TextureRender(srcDescriptorSet, renderVertexBuffer->handle, 4, hint, SAMPLER_WRAP_BORDER);
// TODO: Not optimal but required for releasing raster buffer. Such Buffers should also be managed by special pools
// TODO: Also, consider using VKRenderer_FlushRenderPass here to process pending command
VKRenderer_Flush(device->renderer);
VKRenderer_Sync(device->renderer);
// TODO: Track lifecycle of the texture to avoid reuse of occupied texture
VKTexturePoolHandle_ReleaseTexture(hnd);
VKBuffer_Destroy(device, buffer);
// TODO: Add proper sync for renderVertexBuffer
// VKBuffer_Destroy(device, renderVertexBuffer);
}
static void VKBlitTextureToTexture(VKRenderingContext* context, VKImage* src, VKImage* dest,
int sx1, int sy1, int sx2, int sy2,
double dx1, double dy1, double dx2, double dy2)
{
VKSDOps* surface = context->surface;
VKRenderer_FlushRenderPass(surface);
VKDevice* device = surface->device;
VKTxVertex* vertices = ARRAY_ALLOC(VKTxVertex, 4);
/*
* (p1)---------(p2)
* | |
* | |
* | |
* (p4)---------(p3)
*/
double u1 = (double)sx1 / src->extent.width;
double v1 = (double)sy1 / src->extent.height;
double u2 = (double)sx2 / src->extent.width;
double v2 = (double)sy2 / src->extent.height;
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx1, dy1, u1, v1};
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx2, dy1, u2, v1};
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx1, dy2, u1, v2};
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx2, dy2, u2, v2};
VKBuffer* renderVertexBuffer = ARRAY_TO_VERTEX_BUF(device, vertices);
ARRAY_FREE(vertices);
VkCommandBuffer cb = VKRenderer_Record(device->renderer);
{
VkImageMemoryBarrier barrier;
VKBarrierBatch barrierBatch = {};
VKRenderer_AddImageBarrier(&barrier, &barrierBatch, src,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_ACCESS_SHADER_READ_BIT,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
if (barrierBatch.barrierCount > 0) {
device->vkCmdPipelineBarrier(cb, barrierBatch.srcStages, barrierBatch.dstStages,
0, 0, NULL,
0, NULL,
barrierBatch.barrierCount, &barrier);
}
}
VKRenderer_TextureRender(context, dest, src,
renderVertexBuffer->handle, 4);
// TODO: Not optimal but required for releasing raster buffer. Such Buffers should also be managed by special pools
// TODO: Also, consider using VKRenderer_FlushRenderPass here to process pending command
VKRenderer_Flush(device->renderer);
VKRenderer_Sync(device->renderer);
// TODO: Add proper sync for renderVertexBuffer
// VKBuffer_Destroy(device, renderVertexBuffer);
VKRenderer_FlushSurface(dstOps);
VKRenderer_DisposeOnCleanup(device->renderer, VKTexturePoolTexture_Dispose, hnd);
VKRenderer_DisposeOnCleanup(device->renderer, VKBuffer_Dispose, buffer);
}
static jboolean clipDestCoords(
@@ -260,94 +239,43 @@ static jboolean clipDestCoords(
return JNI_TRUE;
}
void VKBlitLoops_IsoBlit(JNIEnv *env,
VKRenderingContext* context, jlong pSrcOps,
jboolean xform, jint hint,
jboolean texture,
jint sx1, jint sy1,
jint sx2, jint sy2,
jdouble dx1, jdouble dy1,
jdouble dx2, jdouble dy2)
void VKBlitLoops_IsoBlit(VKSDOps* srcOps, jint filter,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
J2dRlsTraceLn8(J2D_TRACE_VERBOSE, "VKBlitLoops_IsoBlit: (%d %d %d %d) -> (%f %f %f %f) ",
sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "VKBlitLoops_IsoBlit: texture=%d xform=%d",
texture, xform)
VKSDOps *srcOps = (VKSDOps *)jlong_to_ptr(pSrcOps);
if (context == NULL || srcOps == NULL) {
J2dRlsTraceLn2(J2D_TRACE_ERROR, "VKBlitLoops_IsoBlit: context(%p) or srcOps(%p) is null",
context, srcOps)
if (srcOps == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"VKBlitLoops_IsoBlit: srcOps is null")
return;
}
if (srcOps->image == NULL) {
J2dRlsTraceLn(J2D_TRACE_WARNING, "VKBlitLoops_IsoBlit: srcOps->image is null");
VKRenderingContext* context = VKRenderer_GetContext();
if (srcOps == context->surface) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "VKBlitLoops_IsoBlit: surface blit into itself (%p)", srcOps)
return;
}
if (!VKRenderer_Validate(context, SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)) {
J2dTraceLn(J2D_TRACE_INFO, "VKBlitLoops_IsoBlit: VKRenderer_Validate cannot validate renderer");
return;
}
// Ensure all prior drawing to src surface have finished.
VKRenderer_FlushRenderPass(srcOps);
// TODO: check if srctype is supported
VkBool32 srcOpaque = VKSD_IsOpaque(srcOps);
AlphaType alphaType = srcOpaque ? ALPHA_TYPE_STRAIGHT : ALPHA_TYPE_PRE_MULTIPLIED;
static const VKPackedSwizzle OPAQUE_SWIZZLE = VK_PACK_SWIZZLE(VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_ONE);
VKPackedSwizzle swizzle = srcOpaque ? OPAQUE_SWIZZLE : 0;
SurfaceDataRasInfo srcInfo;
jint sw = sx2 - sx1;
jint sh = sy2 - sy1;
jdouble dw = dx2 - dx1;
jdouble dh = dy2 - dy1;
if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {
J2dTraceLn(J2D_TRACE_WARNING,
"VKBlitLoops_IsoBlit: invalid dimensions");
return;
}
srcInfo.bounds.x1 = sx1;
srcInfo.bounds.y1 = sy1;
srcInfo.bounds.x2 = sx2;
srcInfo.bounds.y2 = sy2;
SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,
0, 0,
srcOps->image->extent.width,
srcOps->image->extent.height);
if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&
srcInfo.bounds.y2 > srcInfo.bounds.y1) {
if (srcInfo.bounds.x1 != sx1) {
dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw);
sx1 = srcInfo.bounds.x1;
}
if (srcInfo.bounds.y1 != sy1) {
dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh);
sy1 = srcInfo.bounds.y1;
}
if (srcInfo.bounds.x2 != sx2) {
dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw);
sx2 = srcInfo.bounds.x2;
}
if (srcInfo.bounds.y2 != sy2) {
dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh);
sy2 = srcInfo.bounds.y2;
}
if (sx2 > sx1 && sy2 > sy1) {
VKBlitTextureToTexture(context, srcOps->image, context->surface->image,
sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
}
}
VKRenderer_DrawImage(srcOps->image, alphaType, 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);
}
void VKBlitLoops_Blit(JNIEnv *env,
VKRenderingContext* context, jlong pSrcOps,
jboolean xform, jint hint,
jint srctype, jboolean texture,
jlong pSrcOps, jboolean xform, jint hint,
jshort srctype,
jint sx1, jint sy1,
jint sx2, jint sy2,
jdouble dx1, jdouble dy1,
@@ -355,29 +283,24 @@ void VKBlitLoops_Blit(JNIEnv *env,
{
J2dRlsTraceLn8(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT_Blit (%d %d %d %d) -> (%f %f %f %f) ",
sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)
J2dRlsTraceLn3(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT_Blit texture=%d xform=%d srctype=%d",
texture, xform, srctype)
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT_Blit xform=%d srctype=%d", xform, srctype)
SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps);
if (context == NULL || srcOps == NULL) {
J2dRlsTraceLn2(J2D_TRACE_ERROR, "VKBlitLoops_Blit: context(%p) or srcOps(%p) is null",
context, srcOps)
if (srcOps == NULL) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "VKBlitLoops_Blit: srcOps(%p) is null",
srcOps)
return;
}
if (!VKRenderer_Validate(context, SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)) {
if (!VKRenderer_Validate(SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, getSrcAlphaType(srctype))) {
J2dTraceLn(J2D_TRACE_INFO, "VKBlitLoops_Blit: VKRenderer_Validate cannot validate renderer");
return;
}
VKRenderingContext *context = VKRenderer_GetContext();
VKSDOps *dstOps = context->surface;
VKImage *dest = context->surface->image;
// if (srctype < 0 || srctype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) {
// J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: source pixel format %d isn't supported", srctype);
// return;
// }
const jint sw = sx2 - sx1;
const jint sh = sy2 - sy1;
const jint dw = dx2 - dx1;
@@ -433,14 +356,9 @@ void VKBlitLoops_Blit(JNIEnv *env,
dstY2 += dy * (dh / sh);
}
// MTLRasterFormatInfo rfi = RasterFormatInfos[srctype];
//
// if (texture) {
// replaceTextureRegion(mtlc, dest, &srcInfo, &rfi, (int) dx1, (int) dy1, (int) dx2, (int) dy2);
// } else {
VKBlitSwToTextureViaPooledTexture(context, dest, &srcInfo,
(int)dstX1, (int)dstY1, (int)dstX2, (int)dstY2);
// }
VKBlitSwToTextureViaPooledTexture(context, dstOps, &srcInfo, srctype, hint,
(int)dstX1, (int)dstY1,
(int)dstX2, (int)dstY2);
}
SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
}
@@ -452,10 +370,10 @@ void VKBlitLoops_Blit(JNIEnv *env,
* memory ("Sw") surface.
*/
void
VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context,
jlong pSrcOps, jlong pDstOps, jint dsttype,
jint srcx, jint srcy, jint dstx, jint dsty,
jint width, jint height)
VKBlitLoops_SurfaceToSwBlit(JNIEnv *env,
jlong pSrcOps, jlong pDstOps, jint dsttype,
jint srcx, jint srcy, jint dstx, jint dsty,
jint width, jint height)
{
VKSDOps *srcOps = (VKSDOps *)jlong_to_ptr(pSrcOps);
SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);
@@ -472,7 +390,6 @@ VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context,
RETURN_IF_NULL(srcOps);
RETURN_IF_NULL(dstOps);
RETURN_IF_NULL(context);
VKDevice* device = srcOps->device;
VKImage* image = srcOps->image;
@@ -519,7 +436,8 @@ VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context,
dsty = dstInfo.bounds.y1;
width = srcInfo.bounds.x2 - srcInfo.bounds.x1;
height = srcInfo.bounds.y2 - srcInfo.bounds.y1;
jsize bufferSizeInPixels = width * height;
jsize bufferScan = width * dstInfo.pixelStride;
jsize bufferSize = bufferScan * height;
pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride);
pDst = PtrPixelsRow(pDst, dsty, dstInfo.scanStride);
@@ -529,20 +447,14 @@ VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context,
{
VkImageMemoryBarrier barrier;
VKBarrierBatch barrierBatch = {};
VKRenderer_AddImageBarrier(&barrier, &barrierBatch, image,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
if (barrierBatch.barrierCount > 0) {
device->vkCmdPipelineBarrier(cb, barrierBatch.srcStages, barrierBatch.dstStages,
0, 0, NULL,
0, NULL,
barrierBatch.barrierCount, &barrier);
}
VKImage_AddBarrier(&barrier, &barrierBatch, image,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
VKRenderer_RecordBarriers(device->renderer, NULL, NULL, &barrier, &barrierBatch);
}
VKBuffer* buffer = VKBuffer_Create(device, bufferSizeInPixels * sizeof(jint),
VKBuffer* buffer = VKBuffer_Create(device, bufferSize,
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
@@ -577,7 +489,17 @@ VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context,
{
J2dRlsTraceLn(J2D_TRACE_ERROR, "VKBlitLoops_SurfaceToSwBlit: could not map buffer memory");
} else {
memcpy(pDst, pixelData, bufferSizeInPixels * sizeof(jint));
if (bufferScan == dstInfo.scanStride) {
// Tightly packed, copy in one go.
memcpy(pDst, pixelData, bufferSize);
} else {
// Sparse, copy by scanlines.
for (jint i = 0; i < height; i++) {
memcpy(pDst, pixelData, bufferScan);
pixelData = PtrAddBytes(pixelData, bufferScan);
pDst = PtrAddBytes(pDst, dstInfo.scanStride);
}
}
device->vkUnmapMemory(device->handle, buffer->range.memory);
}
VKBuffer_Destroy(device, buffer);

View File

@@ -29,28 +29,26 @@
#include "jni.h"
#include "sun_java2d_vulkan_VKBlitLoops.h"
#include "VKBase.h"
#include "VKTypes.h"
void VKBlitLoops_IsoBlit(JNIEnv *env,
VKRenderingContext* context, jlong pSrcOps,
jboolean xform, jint hint,
jboolean texture,
void VKBlitLoops_IsoBlit(VKSDOps* srcOps,
jint filter,
jint sx1, jint sy1,
jint sx2, jint sy2,
jdouble dx1, jdouble dy1,
jdouble dx2, jdouble dy2);
void VKBlitLoops_Blit(JNIEnv *env,
VKRenderingContext* context, jlong pSrcOps,
jlong pSrcOps,
jboolean xform, jint hint,
jint srctype, jboolean texture,
jshort srctype,
jint sx1, jint sy1,
jint sx2, jint sy2,
jdouble dx1, jdouble dy1,
jdouble dx2, jdouble dy2);
void
VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context,
VKBlitLoops_SurfaceToSwBlit(JNIEnv *env,
jlong pSrcOps, jlong pDstOps, jint dsttype,
jint srcx, jint srcy, jint dstx, jint dsty,
jint width, jint height);

View File

@@ -28,8 +28,13 @@
#include <string.h>
#include "VKUtil.h"
#include "VKAllocator.h"
#include "VKBase.h"
#include "VKBuffer.h"
#include "VKDevice.h"
#include "VKRenderer.h"
#define VK_BUFFER_HOST_COHERENT_MEMORY
const size_t VK_BUFFER_CREATE_THRESHOLD = 0xDC000;
static VKMemory VKBuffer_DestroyBuffersOnFailure(VKDevice* device, VKMemory page, uint32_t bufferCount, VKBuffer* buffers) {
assert(device != NULL && device->allocator != NULL);
@@ -62,7 +67,7 @@ VKMemory VKBuffer_CreateBuffers(VKDevice* device, VkBufferUsageFlags usageFlags,
// Check memory requirements. We aim to create maxBufferCount buffers,
// but due to implementation-specific alignment requirements this number can be lower (unlikely though).
VKMemoryRequirements requirements = VKAllocator_BufferRequirements(alloc, buffers[0].handle);
VKAllocator_PadToAlignment(&requirements); // Align for array-like allocation.
VKAllocator_PadToAlignment(alloc, &requirements); // Align for array-like allocation.
VkDeviceSize realBufferSize = requirements.requirements.memoryRequirements.size;
if (pageSize == 0) pageSize = (*bufferCount) * realBufferSize;
uint32_t realBufferCount = pageSize / realBufferSize;
@@ -220,41 +225,82 @@ VKBuffer* VKBuffer_Create(VKDevice* device, VkDeviceSize size,
VKBuffer_Destroy(device, buffer);
return NULL;
}
buffer->lastStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
buffer->lastAccess = 0;
return buffer;
}
VKBuffer* VKBuffer_CreateFromData(VKDevice* device, void* vertices, VkDeviceSize bufferSize)
void VKBuffer_Dispose(VKDevice* device, void* data) {
VKBuffer* buffer = (VKBuffer*) data;
VKBuffer_Destroy(device, buffer);
}
VKBuffer *VKBuffer_CreateFromRaster(VKDevice *device,
VKBuffer_RasterInfo info,
VkPipelineStageFlags stage,
VkAccessFlags access)
{
VKBuffer* buffer = VKBuffer_Create(device, bufferSize,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
uint32_t dataSize = info.w * info.h * info.pixelStride;
VKBuffer *buffer = VKBuffer_Create(device, dataSize,
VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
#ifdef VK_BUFFER_HOST_COHERENT_MEMORY
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
#endif
);
void* data;
VK_IF_ERROR(device->vkMapMemory(device->handle, buffer->range.memory, 0, VK_WHOLE_SIZE, 0, &data)) {
VKBuffer_Destroy(device, buffer);
return NULL;
}
memcpy(data, vertices, bufferSize);
VkMappedMemoryRange memoryRange = {
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.pNext = NULL,
.memory = buffer->range.memory,
.offset = 0,
.size = VK_WHOLE_SIZE
};
char* raster = (char*)info.data + info.y1 * info.scanStride + info.x1 * info.pixelStride;;
VK_IF_ERROR(device->vkFlushMappedMemoryRanges(device->handle, 1, &memoryRange)) {
VKBuffer_Destroy(device, buffer);
return NULL;
// copy src pixels inside src bounds to buff
for (size_t row = 0; row < info.h; row++) {
memcpy((char*)data + (row * info.w * info.pixelStride), raster, info.w * info.pixelStride);
raster += (uint32_t) info.scanStride;
}
device->vkUnmapMemory(device->handle, buffer->range.memory);
#ifndef VK_BUFFER_HOST_COHERENT_MEMORY
device->vkFlushMappedMemoryRanges(device->handle, 1, &buffer->range);
#endif
device->vkUnmapMemory(device->handle, buffer->range.memory);
{
VkCommandBuffer cb = VKRenderer_Record(device->renderer);
VkBufferMemoryBarrier barrier;
VKBarrierBatch barrierBatch = {};
VKRenderer_AddBufferBarrier(&barrier, &barrierBatch, buffer,
stage, access);
if (barrierBatch.barrierCount > 0) {
device->vkCmdPipelineBarrier(cb, barrierBatch.srcStages,
barrierBatch.dstStages,
0, 0, NULL,
barrierBatch.barrierCount, &barrier,
0, NULL);
}
}
return buffer;
}
VKBuffer* VKBuffer_CreateFromData(VKDevice* device, void* data, VkDeviceSize dataSize,
VkPipelineStageFlags stage, VkAccessFlags access) {
return VKBuffer_CreateFromRaster(device, (VKBuffer_RasterInfo) {
.data = data,
.w = dataSize,
.h = 1,
.scanStride = dataSize,
.pixelStride = 1
}, stage, access);
}
void VKBuffer_Destroy(VKDevice* device, VKBuffer* buffer) {
if (buffer != NULL) {
if (buffer->handle != VK_NULL_HANDLE) {

View File

@@ -30,10 +30,13 @@
#include "VKTypes.h"
#define ARRAY_TO_VERTEX_BUF(device, vertices) \
VKBuffer_CreateFromData(device, vertices, ARRAY_SIZE(vertices)*sizeof (vertices[0]))
VKBuffer_CreateFromData(device, vertices, ARRAY_SIZE(vertices)*sizeof (vertices[0]),\
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT)
struct VKBuffer {
VkBuffer handle;
VkPipelineStageFlagBits lastStage;
VkAccessFlagBits lastAccess;
// Buffer has no ownership over its memory.
// Provided memory, offset and size must only be used to flush memory writes.
// Allocation and freeing is done in pages.
@@ -48,6 +51,13 @@ struct VKTexelBuffer {
VkDescriptorSet descriptorSet;
};
typedef struct {
void* data;
size_t x1, y1, w, h;
size_t scanStride;
size_t pixelStride;
} VKBuffer_RasterInfo;
/**
* Create buffers, allocate a memory page and bind them together.
* 'pageSize' can be 0, meaning that page size is calculated based on buffer memory requirements.
@@ -75,9 +85,15 @@ VKBuffer* VKBuffer_Create(VKDevice* device, VkDeviceSize size,
VkBufferUsageFlags usage, VkMemoryPropertyFlags properties);
// TODO usage of this function is suboptimal, we need to avoid creating one-time buffers.
VKBuffer* VKBuffer_CreateFromData(VKDevice* device, void* vertices, VkDeviceSize bufferSize);
VKBuffer* VKBuffer_CreateFromData(VKDevice* device, void* data, VkDeviceSize dataSize,
VkPipelineStageFlags stage, VkAccessFlags access);
VKBuffer* VKBuffer_CreateFromRaster(VKDevice* device, VKBuffer_RasterInfo info,
VkPipelineStageFlags stage, VkAccessFlags access);
// TODO usage of this function is suboptimal, we need to avoid destroying individual buffers.
void VKBuffer_Destroy(VKDevice* device, VKBuffer* buffer);
void VKBuffer_Dispose(VKDevice* device, void* ctx);
#endif // VKBuffer_h_Included

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. 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
* 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.
*/
#ifndef VKCapabilityUtil_h_Included
#define VKCapabilityUtil_h_Included
#include "VKUtil.h"
#define VK_KHR_VALIDATION_LAYER_NAME "VK_LAYER_KHRONOS_validation"
// Named entries are layers or extensions, arragned into on-stack linked list.
typedef struct VKNamedEntry {
pchar name;
const void* found; // Pointer to the found struct.
struct VKNamedEntry* next; // Pointer to the next entry.
} VKNamedEntry;
#define DEF_NAMED_ENTRY(LIST, NAME) VKNamedEntry NAME = { NAME ## _NAME, NULL, (LIST) }; \
if (NAME.name != NULL) (LIST) = &(NAME)
static void VKNamedEntry_LogAll(pchar what, pchar all, uint32_t count, size_t stride) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " Supported %s:", what)
for (uint32_t i = 0; i < count; i++) {
if (i == 0) J2dRlsTrace(J2D_TRACE_VERBOSE, " ")
else J2dRlsTrace(J2D_TRACE_VERBOSE, ", ")
J2dRlsTrace(J2D_TRACE_VERBOSE, all)
all += stride;
}
J2dRlsTrace(J2D_TRACE_VERBOSE, "\n")
}
static void VKNamedEntry_LogFound(const VKNamedEntry* list) {
for (; list != NULL; list = list->next) {
J2dRlsTraceLn2(J2D_TRACE_INFO, " %s = %s", list->name, list->found ? "true" : "false")
}
}
static void VKNamedEntry_Match(VKNamedEntry* list, pchar all, uint32_t count, size_t stride) {
for (; list != NULL; list = list->next) {
pchar check = all;
for (uint32_t i = 0; i < count; i++) {
if (strcmp(list->name, check) == 0) {
list->found = check;
break;
}
check += stride;
}
}
}
static ARRAY(pchar) VKNamedEntry_CollectNames(const VKNamedEntry* list) {
ARRAY(pchar) result = NULL;
for (; list != NULL; list = list->next) {
if (list->found) ARRAY_PUSH_BACK(result) = list->name;
}
return result;
}
static void VKCapabilityUtil_LogErrors(int level, ARRAY(pchar) errors) {
for (uint32_t i = 0; i < ARRAY_SIZE(errors); i++) {
J2dRlsTraceLn1(level, " %s", errors[i])
}
}
#endif //VKCapabilityUtil_h_Included

View File

@@ -24,7 +24,7 @@
#include "VKComposites.h"
#define ALPHA_BLEND(NAME, SRC_COLOR, DST_COLOR, SRC_ALPHA, DST_ALPHA) \
VKComposites_AddState(&map, ALPHA_COMPOSITE_ ## NAME, (VKCompositeState) \
VKComposites_AddState(&composites, ALPHA_COMPOSITE_ ## NAME, (VKCompositeState) \
{{ .blendEnable = VK_TRUE, \
.srcColorBlendFactor = VK_BLEND_FACTOR_ ## SRC_COLOR, \
.dstColorBlendFactor = VK_BLEND_FACTOR_ ## DST_COLOR, \
@@ -34,26 +34,29 @@
.alphaBlendOp = VK_BLEND_OP_ADD, \
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | \
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT }, \
{ .logicOpEnable = VK_FALSE }})
{ .logicOpEnable = VK_FALSE }, ALPHA_TYPE_PRE_MULTIPLIED })
static size_t hash(const void* ptr) {
return (size_t) *(VKCompositeMode*)ptr;
const VKCompositeDescriptor* d = ptr;
return (size_t) (d->mode | (d->dstOpaque << 31));
}
static bool equals(const void* ap, const void* bp) {
return *(VKCompositeMode*)ap == *(VKCompositeMode*)bp;
const VKCompositeDescriptor *a = ap, *b = bp;
return a->mode == b->mode && a->dstOpaque == b->dstOpaque;
}
VKComposites VKComposites_Create() {
VKComposites map = NULL;
HASH_MAP_REHASH(map, linear_probing, &equals, &hash, ALPHA_COMPOSITE_GROUP + 2, 10, 0.75);
const VKCompositeMode NEXT_FREE_MODE = ALPHA_COMPOSITE_GROUP + 1;
VKComposites composites = { NULL };
HASH_MAP_REHASH(composites.map, linear_probing, &equals, &hash, NEXT_FREE_MODE + 1, 10, 0.75);
VKComposites_AddState(&map, LOGIC_COMPOSITE_XOR, (VKCompositeState) {
VKComposites_AddState(&composites, LOGIC_COMPOSITE_XOR, (VKCompositeState) {
{ .blendEnable = VK_FALSE,
.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 }});
.logicOp = VK_LOGIC_OP_XOR }, ALPHA_TYPE_PRE_MULTIPLIED });
// NAME | SRC_COLOR | DST_COLOR | SRC_ALPHA | DST_ALPHA ||
ALPHA_BLEND( CLEAR , ZERO , ZERO , ZERO , ZERO );
@@ -69,27 +72,143 @@ VKComposites VKComposites_Create() {
ALPHA_BLEND( DST_ATOP , ONE_MINUS_DST_ALPHA , SRC_ALPHA , ONE , ZERO );
ALPHA_BLEND( XOR , ONE_MINUS_DST_ALPHA , ONE_MINUS_SRC_ALPHA , ONE_MINUS_DST_ALPHA , ONE_MINUS_SRC_ALPHA );
VKComposites_AddState(&map, NO_COMPOSITE, (VKCompositeState) {
VKComposites_AddState(&composites, NO_COMPOSITE, (VKCompositeState) {
{ .blendEnable = VK_FALSE,
.colorWriteMask = 0 }, // For stencil-only operations.
{ .logicOpEnable = VK_FALSE }});
{ .logicOpEnable = VK_FALSE }, ALPHA_TYPE_PRE_MULTIPLIED });
return map;
return composites;
}
void VKComposites_Destroy(VKComposites composites) {
MAP_FREE(composites);
MAP_FREE(composites.map);
}
typedef union {
struct {
VkBlendFactor sc[2], dc[2], sa[2], da[2];
};
VkBlendFactor all[8];
} VKBlendVariables;
typedef enum {
COLOR,
ALPHA,
ALL
} VKReplace;
static void VKComposites_ReplaceVariables(VKBlendVariables* vars, VKReplace type, VkBlendFactor of, VkBlendFactor nf) {
for (int i = type == ALPHA ? 4 : 0; i < (type == COLOR ? 4 : 8); i++) if (vars->all[i] == of) vars->all[i] = nf;
}
static VkBool32 VKComposites_IsMultiplicativelyDistributive(VkBlendOp op) {
// We can ignore MIN and MAX, as they ignore blending factors and therefore there's nothing to factor out.
return op == VK_BLEND_OP_ADD || op == VK_BLEND_OP_SUBTRACT || op == VK_BLEND_OP_REVERSE_SUBTRACT;
}
static void VKComposites_CollapseCommonMultipliers(VKBlendVariables* vars, VkBlendOp colorOp, VkBlendOp alphaOp) {
if (VKComposites_IsMultiplicativelyDistributive(colorOp) &&
VKComposites_IsMultiplicativelyDistributive(alphaOp)) {
for (int sc = 0; sc < 2; sc++) {
VkBlendFactor common = VK_BLEND_FACTOR_ZERO;
VkBlendFactor* f[4];
if (*(f[0] = &vars->sc[sc]) == VK_BLEND_FACTOR_ONE) continue;
common = *f[0];
for (int dc = 0; dc < 2; dc++) {
if (*(f[1] = &vars->dc[dc]) == VK_BLEND_FACTOR_ONE) continue;
if (common == VK_BLEND_FACTOR_ZERO) common = *f[1];
else if (*f[1] != VK_BLEND_FACTOR_ZERO && *f[1] != common) continue;
for (int sa = 0; sa < 2; sa++) {
if (*(f[2] = &vars->sa[sa]) == VK_BLEND_FACTOR_ONE) continue;
if (common == VK_BLEND_FACTOR_ZERO) common = *f[2];
else if (*f[2] != VK_BLEND_FACTOR_ZERO && *f[2] != common) continue;
for (int da = 0; da < 2; da++) {
if (*(f[3] = &vars->da[da]) == VK_BLEND_FACTOR_ONE) continue;
if (common == VK_BLEND_FACTOR_ZERO) common = *f[3];
else if (*f[3] != VK_BLEND_FACTOR_ZERO && *f[3] != common) continue;
// We have found a common multiplier.
if (common == VK_BLEND_FACTOR_ZERO) return; // All zero.
for (int i = 0; i < 4; i++) { // Collapse.
if (*f[i] != VK_BLEND_FACTOR_ZERO) *f[i] = VK_BLEND_FACTOR_ONE;
}
}
}
}
}
}
}
void VKComposites_AddState(VKComposites* composites, VKCompositeMode mode, VKCompositeState state) {
state.blendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
state.blendState.pNext = NULL;
state.blendState.attachmentCount = 1;
MAP_AT(*composites, mode) = state;
state.outAlphaType = ALPHA_TYPE_PRE_MULTIPLIED;
MAP_AT(composites->map, (VKCompositeDescriptor) { mode, VK_FALSE }) = state;
// Using pre-multiplied alpha is necessary for correct blending,
// but it can lead to information loss, which is crucial in case of opaque destinations.
// For instance, doing SRC blend onto an opaque surface is expected to simply discard the (straight) alpha,
// but doing this with a zero pre-multiplied alpha will always yield transparent black (0,0,0,0).
// Here is how we are solving this:
// General form of blending equation (r-result, s-source, sf-source factor, d-destination, df-destination factor):
// r = OP(s * sf, d * df)
// To restore information lost due to alpha multiplication, let's express it in straight alpha form:
// r.a = OP(s.a * sf.a, d.a * df.a)
// r.rgb = OP(s.rgb * s.a * sf.rgb, d.rgb * d.a * df.rgb) / r.a
// Now, with some specific parameter combinations we might be able to get rid of 0/0-type ambiguities
// by outputting color in a straight-alpha form.
// Generate opaque mode mapping, if needed.
if (!state.blendState.logicOpEnable && state.attachmentState.blendEnable) {
VKBlendVariables vars = {
.sc = {VK_BLEND_FACTOR_SRC_ALPHA, state.attachmentState.srcColorBlendFactor},
.dc = {VK_BLEND_FACTOR_DST_ALPHA, state.attachmentState.dstColorBlendFactor},
.sa = {VK_BLEND_FACTOR_SRC_ALPHA, state.attachmentState.srcAlphaBlendFactor},
.da = {VK_BLEND_FACTOR_DST_ALPHA, state.attachmentState.dstAlphaBlendFactor},
};
// Opaque destination - replace DST_ALPHA.
VKComposites_ReplaceVariables(&vars, ALL, VK_BLEND_FACTOR_DST_ALPHA, VK_BLEND_FACTOR_ONE);
VKComposites_ReplaceVariables(&vars, ALL, VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, VK_BLEND_FACTOR_ZERO);
VKComposites_ReplaceVariables(&vars, COLOR, VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, VK_BLEND_FACTOR_ZERO);
VKComposites_ReplaceVariables(&vars, ALPHA, VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, VK_BLEND_FACTOR_ONE);
// Simplify constants.
const float* constants = state.blendState.blendConstants;
if (constants[0] == 0.0f && constants[1] == 0.0f && constants[2] == 0.0f) {
VKComposites_ReplaceVariables(&vars, COLOR, VK_BLEND_FACTOR_CONSTANT_COLOR, VK_BLEND_FACTOR_ZERO);
VKComposites_ReplaceVariables(&vars, COLOR, VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, VK_BLEND_FACTOR_ONE);
} else if (constants[0] == 1.0f && constants[1] == 1.0f && constants[2] == 1.0f) {
VKComposites_ReplaceVariables(&vars, COLOR, VK_BLEND_FACTOR_CONSTANT_COLOR, VK_BLEND_FACTOR_ONE);
VKComposites_ReplaceVariables(&vars, COLOR, VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, VK_BLEND_FACTOR_ZERO);
}
if (constants[3] == 0.0f) {
VKComposites_ReplaceVariables(&vars, ALL, VK_BLEND_FACTOR_CONSTANT_ALPHA, VK_BLEND_FACTOR_ZERO);
VKComposites_ReplaceVariables(&vars, ALL, VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, VK_BLEND_FACTOR_ONE);
VKComposites_ReplaceVariables(&vars, ALPHA, VK_BLEND_FACTOR_CONSTANT_COLOR, VK_BLEND_FACTOR_ZERO);
VKComposites_ReplaceVariables(&vars, ALPHA, VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, VK_BLEND_FACTOR_ONE);
} else if (constants[3] == 1.0f) {
VKComposites_ReplaceVariables(&vars, ALL, VK_BLEND_FACTOR_CONSTANT_ALPHA, VK_BLEND_FACTOR_ONE);
VKComposites_ReplaceVariables(&vars, ALL, VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, VK_BLEND_FACTOR_ZERO);
VKComposites_ReplaceVariables(&vars, ALPHA, VK_BLEND_FACTOR_CONSTANT_COLOR, VK_BLEND_FACTOR_ONE);
VKComposites_ReplaceVariables(&vars, ALPHA, VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, VK_BLEND_FACTOR_ZERO);
}
VKComposites_CollapseCommonMultipliers(&vars,
state.attachmentState.colorBlendOp, state.attachmentState.alphaBlendOp);
VkBool32 straightSrcAlpha = vars.sc[0] == VK_BLEND_FACTOR_ONE && vars.sc[1] != VK_BLEND_FACTOR_ZERO;
if (vars.sc[1] != state.attachmentState.srcColorBlendFactor ||
vars.dc[1] != state.attachmentState.dstColorBlendFactor || straightSrcAlpha) {
// Need to use opaque-specific blending.
state.attachmentState.srcColorBlendFactor = vars.sc[1];
state.attachmentState.dstColorBlendFactor = vars.dc[1];
state.attachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
state.attachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
state.outAlphaType = straightSrcAlpha ? ALPHA_TYPE_STRAIGHT : ALPHA_TYPE_PRE_MULTIPLIED;
}
}
MAP_AT(composites->map, (VKCompositeDescriptor) { mode, VK_TRUE }) = state;
}
const VKCompositeState* VKComposites_GetState(VKComposites* composites, VKCompositeMode mode) {
VKCompositeState* state = MAP_FIND(*composites, mode);
const VKCompositeState* VKComposites_GetState(VKComposites* composites, VKCompositeMode mode, VkBool32 dstOpaque) {
VKCompositeState* state = MAP_FIND(composites->map, (VKCompositeDescriptor) { mode, dstOpaque });
state->blendState.pAttachments = &state->attachmentState;
return state;
}

View File

@@ -55,16 +55,24 @@ typedef enum {
(COMPOSITE) <= ALPHA_COMPOSITE_GROUP ? ALPHA_COMPOSITE_GROUP : \
NO_COMPOSITE )
typedef struct {
VKCompositeMode mode;
VkBool32 dstOpaque;
} VKCompositeDescriptor;
typedef struct {
VkPipelineColorBlendAttachmentState attachmentState;
VkPipelineColorBlendStateCreateInfo blendState;
AlphaType outAlphaType;
} VKCompositeState;
typedef MAP(VKCompositeMode, VKCompositeState) VKComposites;
typedef struct {
MAP(VKCompositeDescriptor, VKCompositeState) map;
} VKComposites;
VKComposites VKComposites_Create();
void VKComposites_Destroy(VKComposites composites);
void VKComposites_AddState(VKComposites* composites, VKCompositeMode mode, VKCompositeState state);
const VKCompositeState* VKComposites_GetState(VKComposites* composites, VKCompositeMode mode);
const VKCompositeState* VKComposites_GetState(VKComposites* composites, VKCompositeMode mode, VkBool32 dstOpaque);
#endif //VKComposites_h_Included

View File

@@ -0,0 +1,401 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. 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
* 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.
*/
#include <string.h>
#include "sun_java2d_vulkan_VKGPU.h"
#include "VKUtil.h"
#include "VKCapabilityUtil.h"
#include "VKEnv.h"
#include "VKAllocator.h"
#include "VKRenderer.h"
#include "VKTexturePool.h"
#define CAP_PRESENTABLE_BIT sun_java2d_vulkan_VKGPU_CAP_PRESENTABLE_BIT
#if !defined(__BYTE_ORDER__) || __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define VK_LITTLE_ENDIAN
#endif
static const char* physicalDeviceTypeString(VkPhysicalDeviceType type) {
switch (type) {
#define STR(r) case VK_PHYSICAL_DEVICE_TYPE_ ##r: return #r
STR(OTHER);
STR(INTEGRATED_GPU);
STR(DISCRETE_GPU);
STR(VIRTUAL_GPU);
STR(CPU);
#undef STR
default: return "UNKNOWN_DEVICE_TYPE";
}
}
static VkBool32 VKDevice_CheckAndAddFormat(VKEnv* vk, VkPhysicalDevice physicalDevice,
ARRAY(jint)* supportedFormats, VkFormat format, const char* name) {
VkFormatProperties formatProperties;
vk->vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties);
static const VkFormatFeatureFlags SAMPLED_FLAGS = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
if ((formatProperties.optimalTilingFeatures & SAMPLED_FLAGS) == SAMPLED_FLAGS) {
// Our format is supported for sampling.
static const VkFormatFeatureFlags ATTACHMENT_FLAGS = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT |
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT;
if ((formatProperties.optimalTilingFeatures & ATTACHMENT_FLAGS) == ATTACHMENT_FLAGS) {
// Our format is supported as a drawing destination.
J2dRlsTraceLn1(J2D_TRACE_INFO, " %s (attachment)", name)
ARRAY_PUSH_BACK(*supportedFormats) = (jint) format;
} else J2dRlsTraceLn1(J2D_TRACE_INFO, " %s (sampled)", name)
return VK_TRUE;
}
return VK_FALSE;
}
void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice) {
// Query device properties.
VkPhysicalDeviceVulkan12Features device12Features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.pNext = NULL
};
VkPhysicalDeviceFeatures2 deviceFeatures2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = &device12Features
};
vk->vkGetPhysicalDeviceFeatures2(physicalDevice, &deviceFeatures2);
VkPhysicalDeviceProperties2 deviceProperties2 = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 };
vk->vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProperties2);
// Query supported layers.
uint32_t layerCount;
VK_IF_ERROR(vk->vkEnumerateDeviceLayerProperties(physicalDevice, &layerCount, NULL)) return;
VkLayerProperties allLayers[layerCount];
VK_IF_ERROR(vk->vkEnumerateDeviceLayerProperties(physicalDevice, &layerCount, allLayers)) return;
// Query supported extensions.
uint32_t extensionCount;
VK_IF_ERROR(vk->vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &extensionCount, NULL)) return;
VkExtensionProperties allExtensions[extensionCount];
VK_IF_ERROR(vk->vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &extensionCount, allExtensions)) return;
// Check API version.
ARRAY(pchar) errors = NULL;
jint caps = 0;
J2dRlsTraceLn5(J2D_TRACE_INFO, "%s (%d.%d.%d, %s)",
(const char *) deviceProperties2.properties.deviceName,
VK_API_VERSION_MAJOR(deviceProperties2.properties.apiVersion),
VK_API_VERSION_MINOR(deviceProperties2.properties.apiVersion),
VK_API_VERSION_PATCH(deviceProperties2.properties.apiVersion),
physicalDeviceTypeString(deviceProperties2.properties.deviceType))
if (deviceProperties2.properties.apiVersion < REQUIRED_VULKAN_VERSION) {
ARRAY_PUSH_BACK(errors) = "Unsupported API version";
}
// Log layers and extensions.
VKNamedEntry_LogAll("device layers", allLayers[0].layerName, layerCount, sizeof(VkLayerProperties));
VKNamedEntry_LogAll("device extensions", allExtensions[0].extensionName, extensionCount, sizeof(VkExtensionProperties));
// Check layers.
VKNamedEntry* layers = NULL;
#ifdef DEBUG
DEF_NAMED_ENTRY(layers, VK_KHR_VALIDATION_LAYER);
#endif
VKNamedEntry_Match(layers, allLayers[0].layerName, layerCount, sizeof(VkLayerProperties));
// Check extensions.
VKNamedEntry* extensions = NULL;
DEF_NAMED_ENTRY(extensions, VK_KHR_SWAPCHAIN_EXTENSION);
VKNamedEntry_Match(extensions, allExtensions[0].extensionName, extensionCount, sizeof(VkExtensionProperties));
// Query queue family properties.
uint32_t queueFamilyCount = 0;
vk->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, NULL);
VkQueueFamilyProperties queueFamilies[queueFamilyCount];
vk->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies);
// Find a queue family.
int64_t queueFamily = -1;
for (uint32_t j = 0; j < queueFamilyCount; j++) {
VkBool32 presentationSupported = vk->presentationSupported && VK_KHR_SWAPCHAIN_EXTENSION.found &&
vk->platformData->checkPresentationSupport(vk, physicalDevice, j);
char logFlags[5] = {
queueFamilies[j].queueFlags & VK_QUEUE_GRAPHICS_BIT ? 'G' : '-',
queueFamilies[j].queueFlags & VK_QUEUE_COMPUTE_BIT ? 'C' : '-',
queueFamilies[j].queueFlags & VK_QUEUE_TRANSFER_BIT ? 'T' : '-',
queueFamilies[j].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT ? 'S' : '-',
presentationSupported ? 'P' : '-'
};
J2dRlsTraceLn3(J2D_TRACE_INFO, " %d queues in family (%.*s)", queueFamilies[j].queueCount, 5, logFlags)
// TODO use compute workloads? Separate transfer-only DMA queue?
if (queueFamilies[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) { // Queue supports graphics operations.
if (!(caps & CAP_PRESENTABLE_BIT) && presentationSupported) {
// Queue supports presentation, choose it.
caps |= CAP_PRESENTABLE_BIT;
queueFamily = j;
} else if (queueFamily == -1) {
// We have chosen no queue so far, choose this for now.
queueFamily = j;
}
}
}
if (queueFamily == -1) ARRAY_PUSH_BACK(errors) = "Suitable queue not found";
// Check features.
VKNamedEntry_LogFound(layers);
VKNamedEntry_LogFound(extensions);
J2dRlsTraceLn1(J2D_TRACE_INFO, " presentable = %s", (caps & CAP_PRESENTABLE_BIT) ? "true" : "false")
if (!(caps & CAP_PRESENTABLE_BIT)) VK_KHR_SWAPCHAIN_EXTENSION.found = NULL;
J2dRlsTraceLn1(J2D_TRACE_INFO, " logicOp = %s", deviceFeatures2.features.logicOp ? "true" : "false")
if (deviceFeatures2.features.logicOp) caps |= sun_java2d_vulkan_VKGPU_CAP_LOGIC_OP_BIT;
J2dRlsTraceLn1(J2D_TRACE_INFO, " timelineSemaphore = %s", device12Features.timelineSemaphore ? "true" : "false")
if (!device12Features.timelineSemaphore) ARRAY_PUSH_BACK(errors) = "timelineSemaphore not supported";
// Query supported formats.
J2dRlsTraceLn(J2D_TRACE_INFO, " Supported device formats:")
VKSampledSrcTypes sampledSrcTypes = {{}};
VKSampledSrcType* SRCTYPE_4BYTE = &sampledSrcTypes.table[sun_java2d_vulkan_VKSwToSurfaceBlit_SRCTYPE_4BYTE];
VKSampledSrcType* SRCTYPE_3BYTE = &sampledSrcTypes.table[sun_java2d_vulkan_VKSwToSurfaceBlit_SRCTYPE_3BYTE];
VKSampledSrcType* SRCTYPE_565 = &sampledSrcTypes.table[sun_java2d_vulkan_VKSwToSurfaceBlit_SRCTYPE_565];
VKSampledSrcType* SRCTYPE_555 = &sampledSrcTypes.table[sun_java2d_vulkan_VKSwToSurfaceBlit_SRCTYPE_555];
ARRAY(jint) supportedFormats = NULL;
#define CHECK_AND_ADD_FORMAT(FORMAT) VKDevice_CheckAndAddFormat(vk, physicalDevice, &supportedFormats, FORMAT, #FORMAT)
if (CHECK_AND_ADD_FORMAT(VK_FORMAT_B8G8R8A8_UNORM) && SRCTYPE_4BYTE->format == VK_FORMAT_UNDEFINED) {
supportedFormats[0] |= CAP_PRESENTABLE_BIT; // TODO Check presentation support.
*SRCTYPE_4BYTE = (VKSampledSrcType) { VK_FORMAT_B8G8R8A8_UNORM, {
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A }};
}
if (CHECK_AND_ADD_FORMAT(VK_FORMAT_R8G8B8A8_UNORM) && SRCTYPE_4BYTE->format == VK_FORMAT_UNDEFINED) {
*SRCTYPE_4BYTE = (VKSampledSrcType) { VK_FORMAT_R8G8B8A8_UNORM, {
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }};
}
if (CHECK_AND_ADD_FORMAT(VK_FORMAT_A8B8G8R8_UNORM_PACK32) && SRCTYPE_4BYTE->format == VK_FORMAT_UNDEFINED) {
*SRCTYPE_4BYTE = (VKSampledSrcType) { VK_FORMAT_A8B8G8R8_UNORM_PACK32, {
#ifdef VK_LITTLE_ENDIAN
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A
#else
VK_COMPONENT_SWIZZLE_A, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R
#endif
}};
}
if (CHECK_AND_ADD_FORMAT(VK_FORMAT_R8G8B8_UNORM) && SRCTYPE_3BYTE->format == VK_FORMAT_UNDEFINED) {
*SRCTYPE_3BYTE = (VKSampledSrcType) { VK_FORMAT_R8G8B8_UNORM, {
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }};
}
if (CHECK_AND_ADD_FORMAT(VK_FORMAT_B8G8R8_UNORM) && SRCTYPE_3BYTE->format == VK_FORMAT_UNDEFINED) {
*SRCTYPE_3BYTE = (VKSampledSrcType) { VK_FORMAT_B8G8R8_UNORM, {
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE }};
}
if (CHECK_AND_ADD_FORMAT(VK_FORMAT_R5G6B5_UNORM_PACK16) && SRCTYPE_565->format == VK_FORMAT_UNDEFINED) {
*SRCTYPE_565 = (VKSampledSrcType) { VK_FORMAT_R5G6B5_UNORM_PACK16, {
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }};
}
if (CHECK_AND_ADD_FORMAT(VK_FORMAT_A1R5G5B5_UNORM_PACK16) && SRCTYPE_555->format == VK_FORMAT_UNDEFINED) {
*SRCTYPE_555 = (VKSampledSrcType) { VK_FORMAT_A1R5G5B5_UNORM_PACK16, {
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_ONE }};
}
#undef CHECK_AND_ADD_FORMAT
// Check sampled formats capabilities.
if (SRCTYPE_4BYTE->format == VK_FORMAT_UNDEFINED) {
ARRAY_PUSH_BACK(errors) = "4-byte sampled format not found";
} else caps |= sun_java2d_vulkan_VKGPU_CAP_SAMPLED_4BYTE_BIT;
if (SRCTYPE_3BYTE->format != VK_FORMAT_UNDEFINED) caps |= sun_java2d_vulkan_VKGPU_CAP_SAMPLED_3BYTE_BIT;
if (SRCTYPE_565->format != VK_FORMAT_UNDEFINED) caps |= sun_java2d_vulkan_VKGPU_CAP_SAMPLED_565_BIT;
if (SRCTYPE_555->format != VK_FORMAT_UNDEFINED) caps |= sun_java2d_vulkan_VKGPU_CAP_SAMPLED_555_BIT;
{ // Check stencil format.
VkFormatProperties formatProperties;
vk->vkGetPhysicalDeviceFormatProperties(physicalDevice, VK_FORMAT_S8_UINT, &formatProperties);
if ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) {
J2dRlsTraceLn1(J2D_TRACE_INFO, " %s", "VK_FORMAT_S8_UINT (stencil)")
} else ARRAY_PUSH_BACK(errors) = "VK_FORMAT_S8_UINT not supported";
}
// Check found errors.
if (errors != NULL) {
J2dRlsTraceLn(J2D_TRACE_WARNING, " Device is not supported:")
VKCapabilityUtil_LogErrors(J2D_TRACE_WARNING, errors);
ARRAY_FREE(errors);
return;
}
// Copy device name.
char* deviceName = strdup(deviceProperties2.properties.deviceName);
if (deviceName == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, " Cannot duplicate deviceName")
ARRAY_FREE(supportedFormats);
return;
}
// Valid device, add.
ARRAY_PUSH_BACK(vk->devices) = (VKDevice) {
.name = deviceName,
.type = deviceProperties2.properties.deviceType,
.nonCoherentAtomSize = deviceProperties2.properties.limits.nonCoherentAtomSize,
.handle = VK_NULL_HANDLE,
.physicalDevice = physicalDevice,
.queueFamily = queueFamily,
.enabledLayers = VKNamedEntry_CollectNames(layers),
.enabledExtensions = VKNamedEntry_CollectNames(extensions),
.sampledSrcTypes = sampledSrcTypes,
.supportedFormats = supportedFormats,
.caps = caps
};
}
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);
ARRAY_FREE(device->supportedFormats);
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKDevice_Reset(%s)", device->name);
free(device->name);
if (device->vkDestroyDevice != NULL) {
device->vkDestroyDevice(device->handle, NULL);
}
}
/*
* Class: sun_java2d_vulkan_VKGPU
* Method: reset
* Signature: (J)void
*/
JNIEXPORT void JNICALL
Java_sun_java2d_vulkan_VKGPU_reset(JNIEnv *env, jclass jClass, jlong jDevice) {
VKDevice* device = jlong_to_ptr(jDevice);
if (device == NULL) {
JNU_ThrowByName(env, "java/lang/IllegalStateException", "jDevice is NULL");
return;
}
VKDevice_Reset(device);
}
/*
* Class: sun_java2d_vulkan_VKGPU
* Method: init
* Signature: (J)void
*/
JNIEXPORT void JNICALL
Java_sun_java2d_vulkan_VKGPU_init(JNIEnv *env, jclass jClass, jlong jDevice) {
VKDevice* device = jlong_to_ptr(jDevice);
if (device == NULL) {
JNU_ThrowByName(env, "java/lang/IllegalStateException", "jDevice is NULL");
return;
}
if (device->handle != VK_NULL_HANDLE) return;
float queuePriority = 1.0f;
VkDeviceQueueCreateInfo queueCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = device->queueFamily, // obtained separately
.queueCount = 1,
.pQueuePriorities = &queuePriority
};
VkPhysicalDeviceFeatures features10 = { .logicOp = device->caps & sun_java2d_vulkan_VKGPU_CAP_LOGIC_OP_BIT ? VK_TRUE : VK_FALSE };
VkPhysicalDeviceVulkan12Features features12 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.timelineSemaphore = VK_TRUE
};
void *pNext = &features12;
VkDeviceCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = pNext,
.flags = 0,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queueCreateInfo,
.enabledLayerCount = ARRAY_SIZE(device->enabledLayers),
.ppEnabledLayerNames = (const char *const *) device->enabledLayers,
.enabledExtensionCount = ARRAY_SIZE(device->enabledExtensions),
.ppEnabledExtensionNames = (const char *const *) device->enabledExtensions,
.pEnabledFeatures = &features10
};
VKEnv* vk = VKEnv_GetInstance();
VK_IF_ERROR(vk->vkCreateDevice(device->physicalDevice, &createInfo, NULL, &device->handle)) {
JNU_ThrowByName(env, "java/lang/RuntimeException", "Cannot create device");
return;
}
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKDevice_init(%s)", device->name);
VkBool32 missingAPI = JNI_FALSE;
DEVICE_FUNCTION_TABLE(CHECK_PROC_ADDR, missingAPI, vk->vkGetDeviceProcAddr, device->handle, device->)
if (device->caps & CAP_PRESENTABLE_BIT) {
SWAPCHAIN_DEVICE_FUNCTION_TABLE(CHECK_PROC_ADDR, missingAPI, vk->vkGetDeviceProcAddr, device->handle, device->)
}
if (missingAPI) {
VKDevice_Reset(device);
#define REQUIRED_API_MISSING_MESSAGE "Vulkan: Required API is missing: "
size_t size = sizeof(REQUIRED_API_MISSING_MESSAGE);
#define PFN_CALC_MISSING_NAMES_SIZE(_, NAME) if (device->NAME == NULL) size += sizeof(#NAME) + 1;
DEVICE_FUNCTION_TABLE(PFN_CALC_MISSING_NAMES_SIZE)
if (device->caps & CAP_PRESENTABLE_BIT) { SWAPCHAIN_DEVICE_FUNCTION_TABLE(PFN_CALC_MISSING_NAMES_SIZE) }
char message[size];
memcpy(message, REQUIRED_API_MISSING_MESSAGE, size = sizeof(REQUIRED_API_MISSING_MESSAGE) - 1);
#define PFN_APPEND_MISSING_NAME(_, NAME) if (device->NAME == NULL) { \
memcpy(message + size, #NAME ", ", sizeof(#NAME) + 1); size += sizeof(#NAME) + 1; }
DEVICE_FUNCTION_TABLE(PFN_APPEND_MISSING_NAME)
if (device->caps & CAP_PRESENTABLE_BIT) { SWAPCHAIN_DEVICE_FUNCTION_TABLE(PFN_APPEND_MISSING_NAME) }
message[size - 2] = '\0';
JNU_ThrowByName(env, "java/lang/RuntimeException", message);
return;
}
device->vkGetDeviceQueue(device->handle, device->queueFamily, 0, &device->queue);
if (device->queue == NULL) {
VKDevice_Reset(device);
JNU_ThrowByName(env, "java/lang/RuntimeException", "Vulkan: Failed to get device queue");
return;
}
device->allocator = VKAllocator_Create(device);
if (!device->allocator) {
VKDevice_Reset(device);
JNU_ThrowByName(env, "java/lang/RuntimeException", "Vulkan: Cannot create allocator");
return;
}
device->renderer = VKRenderer_Create(device);
if (!device->renderer) {
VKDevice_Reset(device);
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

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. 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
* 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.
*/
#ifndef VKDevice_h_Included
#define VKDevice_h_Included
#include "sun_java2d_vulkan_VKSwToSurfaceBlit.h"
#include "VKTexturePool.h"
#include "VKUtil.h"
#include "VKFunctionTable.h"
/**
* Description of a sampled source type bound to a specific format, supported on the device.
*/
typedef struct {
VkFormat format;
VkComponentSwizzle components[4];
} VKSampledSrcType;
typedef struct {
VKSampledSrcType table[1 << sun_java2d_vulkan_VKSwToSurfaceBlit_SRCTYPE_BITS];
} VKSampledSrcTypes;
struct VKDevice {
VkDevice handle;
VkPhysicalDevice physicalDevice;
char* name;
VkPhysicalDeviceType type;
VkDeviceSize nonCoherentAtomSize;
uint32_t queueFamily;
ARRAY(pchar) enabledLayers;
ARRAY(pchar) enabledExtensions;
VkQueue queue;
VKSampledSrcTypes sampledSrcTypes;
ARRAY(jint) supportedFormats;
jint caps;
VKAllocator* allocator;
VKRenderer* renderer;
VKTexturePool* texturePool;
DEVICE_FUNCTION_TABLE(DECL_PFN)
SWAPCHAIN_DEVICE_FUNCTION_TABLE(DECL_PFN)
};
#endif //VKDevice_h_Included

View File

@@ -0,0 +1,386 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. 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
* 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.
*/
#include <dlfcn.h>
#include <string.h>
#include "VKUtil.h"
#include "VKCapabilityUtil.h"
#include "VKEnv.h"
#include "VKDevice.h"
#define VULKAN_DLL JNI_LIB_NAME("vulkan")
#define VULKAN_1_DLL VERSIONED_JNI_LIB_NAME("vulkan", "1")
static void* pVulkanLib = NULL;
static void vulkanLibClose() {
if (pVulkanLib != NULL) {
dlclose(pVulkanLib);
pVulkanLib = NULL;
}
}
static PFN_vkGetInstanceProcAddr vulkanLibOpen() {
if (pVulkanLib == NULL) {
pVulkanLib = dlopen(VULKAN_DLL, RTLD_NOW);
if (pVulkanLib == NULL) {
pVulkanLib = dlopen(VULKAN_1_DLL, RTLD_NOW);
}
if (pVulkanLib == NULL) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "Vulkan: Failed to load %s", VULKAN_DLL)
return NULL;
}
}
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dlsym(pVulkanLib, "vkGetInstanceProcAddr");
if (vkGetInstanceProcAddr == NULL) {
J2dRlsTraceLn1(J2D_TRACE_ERROR,
"Vulkan: Failed to get proc address of vkGetInstanceProcAddr from %s", VULKAN_DLL)
vulkanLibClose();
return NULL;
}
return vkGetInstanceProcAddr;
}
void VKDevice_Reset(VKDevice* device);
static void VKEnv_Destroy(VKEnv* vk) {
if (vk == NULL) return;
if (vk->devices != NULL) {
for (uint32_t i = 0; i < ARRAY_SIZE(vk->devices); i++) {
VKDevice_Reset(&vk->devices[i]);
}
ARRAY_FREE(vk->devices);
}
#if defined(DEBUG)
if (vk->vkDestroyDebugUtilsMessengerEXT != NULL && vk->instance != VK_NULL_HANDLE) {
vk->vkDestroyDebugUtilsMessengerEXT(vk->instance, vk->debugMessenger, NULL);
}
#endif
VKComposites_Destroy(vk->composites);
if (vk->vkDestroyInstance != NULL) {
vk->vkDestroyInstance(vk->instance, NULL);
}
free(vk);
J2dRlsTraceLn(J2D_TRACE_INFO, "VKEnv_Destroy");
}
#if defined(DEBUG)
static VkBool32 debugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData
) {
if (pCallbackData == NULL) return VK_FALSE;
// Here we can filter messages like this:
// if (std::strcmp(pCallbackData->pMessageIdName, "UNASSIGNED-BestPractices-DrawState-ClearCmdBeforeDraw") == 0) return VK_FALSE;
int level = J2D_TRACE_OFF;
if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) level = J2D_TRACE_VERBOSE;
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) level = J2D_TRACE_INFO;
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) level = J2D_TRACE_WARNING;
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) level = J2D_TRACE_ERROR;
J2dRlsTraceLn(level, pCallbackData->pMessage);
if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
VK_FATAL_ERROR("Unhandled Vulkan validation error");
}
return VK_FALSE;
}
#endif
static VKEnv* VKEnv_Create(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, VKPlatformData* platformData) {
if (vkGetInstanceProcAddr == NULL) return NULL;
// Init global function table.
VkBool32 missingAPI = JNI_FALSE;
GLOBAL_FUNCTION_TABLE(DECL_PFN)
GLOBAL_FUNCTION_TABLE(CHECK_PROC_ADDR, missingAPI, vkGetInstanceProcAddr, NULL,)
if (missingAPI) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: Required API is missing:")
GLOBAL_FUNCTION_TABLE(LOG_MISSING_PFN,)
return NULL;
}
// Query API version.
uint32_t apiVersion = 0;
VK_IF_ERROR(vkEnumerateInstanceVersion(&apiVersion)) return NULL;
J2dRlsTraceLn3(J2D_TRACE_INFO, "Vulkan: Available (%d.%d.%d)",
VK_API_VERSION_MAJOR(apiVersion),
VK_API_VERSION_MINOR(apiVersion),
VK_API_VERSION_PATCH(apiVersion))
// Query supported layers.
uint32_t layerCount;
VK_IF_ERROR(vkEnumerateInstanceLayerProperties(&layerCount, NULL)) return NULL;
VkLayerProperties allLayers[layerCount];
VK_IF_ERROR(vkEnumerateInstanceLayerProperties(&layerCount, allLayers)) return NULL;
// Query supported extensions.
uint32_t extensionCount;
VK_IF_ERROR(vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL)) return NULL;
VkExtensionProperties allExtensions[extensionCount];
VK_IF_ERROR(vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, allExtensions)) return NULL;
// Log layers and extensions.
VKNamedEntry_LogAll("instance layers", allLayers[0].layerName, layerCount, sizeof(VkLayerProperties));
VKNamedEntry_LogAll("instance extensions", allExtensions[0].extensionName, extensionCount, sizeof(VkExtensionProperties));
// Check API version.
ARRAY(pchar) errors = NULL;
if (apiVersion < REQUIRED_VULKAN_VERSION) ARRAY_PUSH_BACK(errors) = "Unsupported API version";
// Check layers.
VKNamedEntry* layers = NULL;
#ifdef DEBUG
DEF_NAMED_ENTRY(layers, VK_KHR_VALIDATION_LAYER);
#endif
VKNamedEntry_Match(layers, allLayers[0].layerName, layerCount, sizeof(VkLayerProperties));
VKNamedEntry_LogFound(layers);
// Check extensions.
pchar PLATFORM_SURFACE_EXTENSION_NAME = platformData != NULL ? platformData->surfaceExtensionName : NULL;
VKNamedEntry* extensions = NULL;
DEF_NAMED_ENTRY(extensions, PLATFORM_SURFACE_EXTENSION);
DEF_NAMED_ENTRY(extensions, VK_KHR_SURFACE_EXTENSION);
#ifdef DEBUG
DEF_NAMED_ENTRY(extensions, VK_EXT_DEBUG_UTILS_EXTENSION);
#endif
VKNamedEntry_Match(extensions, allExtensions[0].extensionName, extensionCount, sizeof(VkExtensionProperties));
VKNamedEntry_LogFound(extensions);
// Check found errors.
if (errors != NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, " Vulkan is not supported:")
VKCapabilityUtil_LogErrors(J2D_TRACE_ERROR, errors);
ARRAY_FREE(errors);
return NULL;
}
// Check presentation support.
VkBool32 presentationSupported = PLATFORM_SURFACE_EXTENSION.found && VK_KHR_SURFACE_EXTENSION.found;
if (!presentationSupported) PLATFORM_SURFACE_EXTENSION.found = VK_KHR_SURFACE_EXTENSION.found = NULL;
// Configure validation
void *pNext = NULL;
#ifdef DEBUG
VkValidationFeatureEnableEXT enables[] = {
// VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,
// VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT,
VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
// VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT,
VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT
};
VkValidationFeaturesEXT features = {
.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
.enabledValidationFeatureCount = SARRAY_COUNT_OF(enables),
.pEnabledValidationFeatures = enables
};
if (VK_KHR_VALIDATION_LAYER.found && VK_EXT_DEBUG_UTILS_EXTENSION.found) {
pNext = &features;
} else {
VK_KHR_VALIDATION_LAYER.found = VK_EXT_DEBUG_UTILS_EXTENSION.found = NULL;
J2dRlsTraceLn(J2D_TRACE_WARNING, " Vulkan validation is not supported")
}
#endif
VKEnv* vk = malloc(sizeof(VKEnv));
if (vk == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, " Cannot allocate VKEnv")
return NULL;
}
*vk = (VKEnv) {
.platformData = platformData,
.presentationSupported = presentationSupported
};
ARRAY(pchar) enabledLayers = VKNamedEntry_CollectNames(layers);
ARRAY(pchar) enabledExtensions = VKNamedEntry_CollectNames(extensions);
VkApplicationInfo applicationInfo = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = NULL,
.pApplicationName = "OpenJDK",
.applicationVersion = 0,
.pEngineName = "OpenJDK",
.engineVersion = 0,
.apiVersion = REQUIRED_VULKAN_VERSION
};
VkInstanceCreateInfo instanceCreateInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = pNext,
.flags = 0,
.pApplicationInfo = &applicationInfo,
.enabledLayerCount = ARRAY_SIZE(enabledLayers),
.ppEnabledLayerNames = enabledLayers,
.enabledExtensionCount = ARRAY_SIZE(enabledExtensions),
.ppEnabledExtensionNames = enabledExtensions
};
VK_IF_ERROR(vkCreateInstance(&instanceCreateInfo, NULL, &vk->instance)) {
ARRAY_FREE(enabledLayers);
ARRAY_FREE(enabledExtensions);
VKEnv_Destroy(vk);
return NULL;
}
J2dRlsTraceLn(J2D_TRACE_INFO, "Vulkan: Instance Created")
ARRAY_FREE(enabledLayers);
ARRAY_FREE(enabledExtensions);
INSTANCE_FUNCTION_TABLE(CHECK_PROC_ADDR, missingAPI, vkGetInstanceProcAddr, vk->instance, vk->)
DEBUG_INSTANCE_FUNCTION_TABLE(GET_PROC_ADDR, vkGetInstanceProcAddr, vk->instance, vk->)
if (missingAPI) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: Required API is missing:")
INSTANCE_FUNCTION_TABLE(LOG_MISSING_PFN, vk->)
VKEnv_Destroy(vk);
return NULL;
}
if (presentationSupported) {
SURFACE_INSTANCE_FUNCTION_TABLE(CHECK_PROC_ADDR, missingAPI, vkGetInstanceProcAddr, vk->instance, vk->)
if (missingAPI) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: Required API is missing:")
SURFACE_INSTANCE_FUNCTION_TABLE(LOG_MISSING_PFN, vk->)
}
if (missingAPI || !vk->platformData->initFunctions(vk, vkGetInstanceProcAddr)) {
vk->presentationSupported = presentationSupported = VK_FALSE;
}
}
J2dRlsTraceLn1(J2D_TRACE_INFO, "Vulkan: Presentation supported = %s", presentationSupported ? "true" : "false")
vk->composites = VKComposites_Create();
// Create debug messenger
#if defined(DEBUG)
if (VK_KHR_VALIDATION_LAYER.found && VK_EXT_DEBUG_UTILS_EXTENSION.found &&
vk->vkCreateDebugUtilsMessengerEXT != NULL && pNext) {
VkDebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.flags = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
.pfnUserCallback = &debugCallback
};
VK_IF_ERROR(vk->vkCreateDebugUtilsMessengerEXT(vk->instance, &debugUtilsMessengerCreateInfo,
NULL, &vk->debugMessenger)) {}
}
#endif
return vk;
}
void VKDevice_CheckAndAdd(VKEnv* vk, VkPhysicalDevice physicalDevice);
static VkBool32 VKEnv_FindDevices(VKEnv* vk) {
uint32_t count;
VK_IF_ERROR(vk->vkEnumeratePhysicalDevices(vk->instance, &count, NULL)) return JNI_FALSE;
VkPhysicalDevice physicalDevices[count];
VK_IF_ERROR(vk->vkEnumeratePhysicalDevices(vk->instance, &count, physicalDevices)) return JNI_FALSE;
ARRAY_ENSURE_CAPACITY(vk->devices, count);
J2dRlsTraceLn1(J2D_TRACE_INFO, "Vulkan: Found %d physical devices:", count)
for (uint32_t i = 0; i < count; i++) {
VKDevice_CheckAndAdd(vk, physicalDevices[i]);
}
if (ARRAY_SIZE(vk->devices) == 0) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Vulkan: No compatible device found")
return JNI_FALSE;
}
return JNI_TRUE;
}
static jobjectArray createJavaGPUs(JNIEnv *env, VKEnv* vk) {
jclass deviceClass = (*env)->FindClass(env, "sun/java2d/vulkan/VKGPU");
if (deviceClass == NULL) return NULL;
jmethodID deviceConstructor = (*env)->GetMethodID(env, deviceClass, "<init>", "(JLjava/lang/String;II[I)V");
if (deviceConstructor == NULL) return NULL;
jobjectArray deviceArray = (*env)->NewObjectArray(env, ARRAY_SIZE(vk->devices), deviceClass, NULL);
if (deviceArray == NULL) return NULL;
for (uint32_t i = 0; i < ARRAY_SIZE(vk->devices); i++) {
jstring name = JNU_NewStringPlatform(env, vk->devices[i].name);
if (name == NULL) return NULL;
jintArray supportedFormats = (*env)->NewIntArray(env, ARRAY_SIZE(vk->devices[i].supportedFormats));
if (supportedFormats == NULL) return NULL;
(*env)->SetIntArrayRegion(env, supportedFormats, 0, ARRAY_SIZE(vk->devices[i].supportedFormats), vk->devices[i].supportedFormats);
jobject device = (*env)->NewObject(env, deviceClass, deviceConstructor,
ptr_to_jlong(&vk->devices[i]), name, vk->devices[i].type,
vk->devices[i].caps, supportedFormats);
if (device == NULL) return NULL;
(*env)->SetObjectArrayElement(env, deviceArray, i, device);
}
return deviceArray;
}
static VKEnv* instance = NULL;
JNIEXPORT VKEnv* VKEnv_GetInstance() {
return instance;
}
/*
* Class: sun_java2d_vulkan_VKEnv
* Method: initNative
* Signature: (J)[Lsun/java2d/vulkan/VKDevice;
*/
JNIEXPORT jobjectArray JNICALL
Java_sun_java2d_vulkan_VKEnv_initNative(JNIEnv* env, jclass vkenv, jlong platformData) {
#ifdef DEBUG
// Init random for debug-related validation tricks.
srand(platformData);
#endif
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = vulkanLibOpen();
if (vkGetInstanceProcAddr == NULL) return NULL;
VKEnv* vk = VKEnv_Create(vkGetInstanceProcAddr, jlong_to_ptr(platformData));
if (vk == NULL) {
vulkanLibClose();
return NULL;
}
if (!VKEnv_FindDevices(vk)) {
VKEnv_Destroy(vk);
vulkanLibClose();
return NULL;
}
jobjectArray deviceArray = createJavaGPUs(env, vk);
if (deviceArray == NULL) {
VKEnv_Destroy(vk);
vulkanLibClose();
return NULL;
}
instance = vk;
return deviceArray;
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. 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
* 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.
*/
#ifndef VKEnv_h_Included
#define VKEnv_h_Included
#include "VKComposites.h"
#include "VKDevice.h"
#include "VKUtil.h"
#include "VKFunctionTable.h"
// For old Vulkan headers - define version-related macros.
#ifndef VK_MAKE_API_VERSION
# define VK_MAKE_API_VERSION(variant, major, minor, patch) VK_MAKE_VERSION(major, minor, patch)
# define VK_API_VERSION_MAJOR(version) VK_VERSION_MAJOR(version)
# define VK_API_VERSION_MINOR(version) VK_VERSION_MINOR(version)
# define VK_API_VERSION_PATCH(version) VK_VERSION_PATCH(version)
#endif
static const uint32_t REQUIRED_VULKAN_VERSION = VK_MAKE_API_VERSION(0, 1, 2, 0);
typedef VkBool32 (*VKPlatform_InitFunctions)(VKEnv* vk, PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr);
typedef VkBool32 (*VKPlatform_CheckPresentationSupport)(VKEnv* vk, VkPhysicalDevice device, uint32_t queueFamily);
typedef struct {
const char* surfaceExtensionName;
VKPlatform_InitFunctions initFunctions;
VKPlatform_CheckPresentationSupport checkPresentationSupport;
} VKPlatformData;
struct VKEnv {
VkInstance instance;
ARRAY(VKDevice) devices;
VKComposites composites;
#if defined(DEBUG)
VkDebugUtilsMessengerEXT debugMessenger;
#endif
VKPlatformData* platformData;
VkBool32 presentationSupported;
INSTANCE_FUNCTION_TABLE(DECL_PFN)
SURFACE_INSTANCE_FUNCTION_TABLE(DECL_PFN)
DEBUG_INSTANCE_FUNCTION_TABLE(DECL_PFN)
};
VKEnv* VKEnv_GetInstance();
#endif //VKEnv_h_Included

View File

@@ -0,0 +1,155 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, JetBrains s.r.o.. 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
* 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.
*/
#ifndef VKFunctionTable_h_Included
#define VKFunctionTable_h_Included
// Enumeration of all used Vulkan functions via includer-provided FUNCTION_TABLE_ENTRY macro.
// Global functions.
#define GLOBAL_FUNCTION_TABLE(ENTRY, ...) \
ENTRY(__VA_ARGS__, vkEnumerateInstanceVersion); \
ENTRY(__VA_ARGS__, vkEnumerateInstanceExtensionProperties); \
ENTRY(__VA_ARGS__, vkEnumerateInstanceLayerProperties); \
ENTRY(__VA_ARGS__, vkCreateInstance); \
// Instance functions.
#define INSTANCE_FUNCTION_TABLE(ENTRY, ...) \
ENTRY(__VA_ARGS__, vkDestroyInstance); \
ENTRY(__VA_ARGS__, vkEnumeratePhysicalDevices); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceMemoryProperties); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceFeatures2); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceProperties2); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceQueueFamilyProperties); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceFormatProperties); \
ENTRY(__VA_ARGS__, vkEnumerateDeviceLayerProperties); \
ENTRY(__VA_ARGS__, vkEnumerateDeviceExtensionProperties); \
ENTRY(__VA_ARGS__, vkCreateDevice); \
ENTRY(__VA_ARGS__, vkGetDeviceProcAddr); \
#define SURFACE_INSTANCE_FUNCTION_TABLE(ENTRY, ...) \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceSurfaceCapabilitiesKHR); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceSurfaceFormatsKHR); \
ENTRY(__VA_ARGS__, vkGetPhysicalDeviceSurfacePresentModesKHR); \
ENTRY(__VA_ARGS__, vkDestroySurfaceKHR); \
#if defined(DEBUG)
#define DEBUG_INSTANCE_FUNCTION_TABLE(ENTRY, ...) \
ENTRY(__VA_ARGS__, vkCreateDebugUtilsMessengerEXT); \
ENTRY(__VA_ARGS__, vkDestroyDebugUtilsMessengerEXT);
#else
#define DEBUG_INSTANCE_FUNCTION_TABLE(ENTRY, ...)
#endif
// Device functions.
#define DEVICE_FUNCTION_TABLE(ENTRY, ...) \
ENTRY(__VA_ARGS__, vkDestroyDevice); \
ENTRY(__VA_ARGS__, vkCreateShaderModule); \
ENTRY(__VA_ARGS__, vkDestroyShaderModule); \
ENTRY(__VA_ARGS__, vkCreatePipelineLayout); \
ENTRY(__VA_ARGS__, vkDestroyPipelineLayout); \
ENTRY(__VA_ARGS__, vkCreateGraphicsPipelines); \
ENTRY(__VA_ARGS__, vkDestroyPipeline); \
ENTRY(__VA_ARGS__, vkCreateImageView); \
ENTRY(__VA_ARGS__, vkCreateFramebuffer); \
ENTRY(__VA_ARGS__, vkCreateCommandPool); \
ENTRY(__VA_ARGS__, vkDestroyCommandPool); \
ENTRY(__VA_ARGS__, vkAllocateCommandBuffers); \
ENTRY(__VA_ARGS__, vkFreeCommandBuffers); \
ENTRY(__VA_ARGS__, vkCreateSemaphore); \
ENTRY(__VA_ARGS__, vkDestroySemaphore); \
ENTRY(__VA_ARGS__, vkWaitSemaphores); \
ENTRY(__VA_ARGS__, vkGetSemaphoreCounterValue); \
ENTRY(__VA_ARGS__, vkCreateFence); \
ENTRY(__VA_ARGS__, vkGetDeviceQueue); \
ENTRY(__VA_ARGS__, vkWaitForFences); \
ENTRY(__VA_ARGS__, vkResetFences); \
ENTRY(__VA_ARGS__, vkResetCommandBuffer); \
ENTRY(__VA_ARGS__, vkQueueSubmit); \
ENTRY(__VA_ARGS__, vkQueueWaitIdle); \
ENTRY(__VA_ARGS__, vkBeginCommandBuffer); \
ENTRY(__VA_ARGS__, vkCmdBlitImage); \
ENTRY(__VA_ARGS__, vkCmdPipelineBarrier); \
ENTRY(__VA_ARGS__, vkCmdBeginRenderPass); \
ENTRY(__VA_ARGS__, vkCmdExecuteCommands); \
ENTRY(__VA_ARGS__, vkCmdClearAttachments); \
ENTRY(__VA_ARGS__, vkCmdBindPipeline); \
ENTRY(__VA_ARGS__, vkCmdSetViewport); \
ENTRY(__VA_ARGS__, vkCmdSetScissor); \
ENTRY(__VA_ARGS__, vkCmdDraw); \
ENTRY(__VA_ARGS__, vkCmdEndRenderPass); \
ENTRY(__VA_ARGS__, vkEndCommandBuffer); \
ENTRY(__VA_ARGS__, vkCreateImage); \
ENTRY(__VA_ARGS__, vkCreateSampler); \
ENTRY(__VA_ARGS__, vkDestroySampler); \
ENTRY(__VA_ARGS__, vkAllocateMemory); \
ENTRY(__VA_ARGS__, vkBindImageMemory); \
ENTRY(__VA_ARGS__, vkCreateDescriptorSetLayout); \
ENTRY(__VA_ARGS__, vkDestroyDescriptorSetLayout); \
ENTRY(__VA_ARGS__, vkUpdateDescriptorSets); \
ENTRY(__VA_ARGS__, vkCreateDescriptorPool); \
ENTRY(__VA_ARGS__, vkDestroyDescriptorPool); \
ENTRY(__VA_ARGS__, vkAllocateDescriptorSets); \
ENTRY(__VA_ARGS__, vkFreeDescriptorSets); \
ENTRY(__VA_ARGS__, vkCmdBindDescriptorSets); \
ENTRY(__VA_ARGS__, vkGetImageMemoryRequirements2); \
ENTRY(__VA_ARGS__, vkCreateBuffer); \
ENTRY(__VA_ARGS__, vkDestroyBuffer); \
ENTRY(__VA_ARGS__, vkCreateBufferView); \
ENTRY(__VA_ARGS__, vkDestroyBufferView); \
ENTRY(__VA_ARGS__, vkGetBufferMemoryRequirements2); \
ENTRY(__VA_ARGS__, vkBindBufferMemory); \
ENTRY(__VA_ARGS__, vkMapMemory); \
ENTRY(__VA_ARGS__, vkUnmapMemory); \
ENTRY(__VA_ARGS__, vkCmdBindVertexBuffers); \
ENTRY(__VA_ARGS__, vkCreateRenderPass); \
ENTRY(__VA_ARGS__, vkDestroyRenderPass); \
ENTRY(__VA_ARGS__, vkFreeMemory); \
ENTRY(__VA_ARGS__, vkDestroyImageView); \
ENTRY(__VA_ARGS__, vkDestroyImage); \
ENTRY(__VA_ARGS__, vkDestroyFramebuffer); \
ENTRY(__VA_ARGS__, vkFlushMappedMemoryRanges); \
ENTRY(__VA_ARGS__, vkInvalidateMappedMemoryRanges); \
ENTRY(__VA_ARGS__, vkCmdPushConstants); \
ENTRY(__VA_ARGS__, vkCmdCopyBufferToImage); \
ENTRY(__VA_ARGS__, vkCmdCopyImageToBuffer); \
ENTRY(__VA_ARGS__, vkCmdCopyBuffer); \
#define SWAPCHAIN_DEVICE_FUNCTION_TABLE(ENTRY, ...) \
ENTRY(__VA_ARGS__, vkCreateSwapchainKHR); \
ENTRY(__VA_ARGS__, vkDestroySwapchainKHR); \
ENTRY(__VA_ARGS__, vkGetSwapchainImagesKHR); \
ENTRY(__VA_ARGS__, vkAcquireNextImageKHR); \
ENTRY(__VA_ARGS__, vkQueuePresentKHR); \
// Utilities for working with function pointers.
#define DECL_PFN(_, NAME) PFN_ ## NAME NAME
#define GET_PROC_ADDR(GETPROCADDR, HANDLE, PREFIX, NAME) ((PREFIX NAME) = (PFN_ ## NAME) GETPROCADDR(HANDLE, #NAME))
#define CHECK_PROC_ADDR(MISSING_FLAG, ...) if (GET_PROC_ADDR(__VA_ARGS__) == NULL) (MISSING_FLAG = VK_TRUE)
#define LOG_MISSING_PFN(PREFIX, NAME) if ((PREFIX NAME) == NULL) J2dRlsTraceLn(J2D_TRACE_ERROR, " " #NAME)
#endif //VKFunctionTable_h_Included

View File

@@ -27,35 +27,38 @@
#include <assert.h>
#include "VKUtil.h"
#include "VKAllocator.h"
#include "VKBase.h"
#include "VKBuffer.h"
#include "VKDevice.h"
#include "VKImage.h"
#include "VKRenderer.h"
#include "VKSurfaceData.h"
static size_t viewKeyHash(const void* ptr) {
const VKImageViewKey* k = ptr;
return (size_t) k->format | ((size_t) k->swizzle << 32);
}
static bool viewKeyEquals(const void* ap, const void* bp) {
const VKImageViewKey *a = ap, *b = bp;
return a->format == b->format && a->swizzle == b->swizzle;
}
static VkBool32 VKImage_CreateView(VKDevice* device, VKImage* image) {
static VkImageView VKImage_CreateView(VKDevice* device, VkImage image, VkFormat format, VkComponentMapping swizzle) {
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image->handle,
.image = image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = image->format,
.subresourceRange.aspectMask = VKImage_GetAspect(image),
.format = format,
.components = swizzle,
.subresourceRange.aspectMask = VKUtil_GetFormatGroup(format).aspect,
.subresourceRange.baseMipLevel = 0,
.subresourceRange.levelCount = 1,
.subresourceRange.baseArrayLayer = 0,
.subresourceRange.layerCount = 1,
};
VK_IF_ERROR(device->vkCreateImageView(device->handle, &viewInfo, NULL, &image->view)) {
return VK_FALSE;
VkImageView view;
VK_IF_ERROR(device->vkCreateImageView(device->handle, &viewInfo, NULL, &view)) {
return VK_NULL_HANDLE;
}
return VK_TRUE;
}
VkImageAspectFlagBits VKImage_GetAspect(VKImage* image) {
return VKUtil_GetFormatGroup(image->format).bytes == 0 ? // Unknown format group means stencil.
VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
return view;
}
VKImage* VKImage_Create(VKDevice* device, uint32_t width, uint32_t height,
@@ -106,10 +109,7 @@ VKImage* VKImage_Create(VKDevice* device, uint32_t width, uint32_t height,
return NULL;
}
if (!VKImage_CreateView(device, image)) {
VKImage_Destroy(device, image);
return NULL;
}
HASH_MAP_REHASH(image->viewMap, linear_probing, &viewKeyEquals, &viewKeyHash, 1, 10, 0.75);
return image;
}
@@ -142,8 +142,78 @@ void VKImage_LoadBuffer(VKDevice* device, VKImage* image, VKBuffer* buffer,
void VKImage_Destroy(VKDevice* device, VKImage* image) {
assert(device != NULL && device->allocator != NULL);
if (image == NULL) return;
device->vkDestroyImageView(device->handle, image->view, NULL);
if (image->viewMap != NULL) {
for (const VKImageViewKey* k = NULL; (k = MAP_NEXT_KEY(image->viewMap, k)) != NULL;) {
const VKImageViewInfo* viewInfo = MAP_FIND(image->viewMap, *k);
if (viewInfo->descriptorSet != VK_NULL_HANDLE) {
device->vkFreeDescriptorSets(device->handle, viewInfo->descriptorPool, 1, &viewInfo->descriptorSet);
}
device->vkDestroyImageView(device->handle, viewInfo->view, NULL);
}
MAP_FREE(image->viewMap);
}
device->vkDestroyImage(device->handle, image->handle, NULL);
VKAllocator_Free(device->allocator, image->memory);
free(image);
}
static VKImageViewInfo* VKImage_GetViewInfo(VKDevice* device, VKImage* image, VkFormat format, VKPackedSwizzle swizzle) {
VKImageViewKey key = { format, swizzle };
VKImageViewInfo* viewInfo = MAP_FIND(image->viewMap, key);
if (viewInfo == NULL || viewInfo->view == VK_NULL_HANDLE) {
if (viewInfo == NULL) viewInfo = &MAP_AT(image->viewMap, key);
viewInfo->view = VKImage_CreateView(device, image->handle, format, VK_UNPACK_SWIZZLE(swizzle));
}
return viewInfo;
}
VkImageView VKImage_GetView(VKDevice* device, VKImage* image, VkFormat format, VKPackedSwizzle swizzle) {
return VKImage_GetViewInfo(device, image, format, swizzle)->view;
}
VkDescriptorSet VKImage_GetDescriptorSet(VKDevice* device, VKImage* image, VkFormat format, VKPackedSwizzle swizzle) {
VKImageViewInfo* info = VKImage_GetViewInfo(device, image, format, swizzle);
if (info->descriptorSet == VK_NULL_HANDLE) {
VKRenderer_CreateImageDescriptorSet(device->renderer, &info->descriptorPool, &info->descriptorSet);
VkDescriptorImageInfo imageInfo = {
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = info->view
};
VkWriteDescriptorSet descriptorWrites = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = info->descriptorSet,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = 1,
.pImageInfo = &imageInfo
};
device->vkUpdateDescriptorSets(device->handle, 1, &descriptorWrites, 0, NULL);
}
return info->descriptorSet;
}
void VKImage_AddBarrier(VkImageMemoryBarrier* barriers, VKBarrierBatch* batch,
VKImage* image, VkPipelineStageFlags stage, VkAccessFlags access, VkImageLayout layout) {
assert(barriers != NULL && batch != NULL && image != NULL);
// TODO Even if stage, access and layout didn't change, we may still need a barrier against WaW hazard.
if (stage != image->lastStage || access != image->lastAccess || layout != image->layout) {
barriers[batch->barrierCount] = (VkImageMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = image->lastAccess,
.dstAccessMask = access,
.oldLayout = image->layout,
.newLayout = layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image->handle,
.subresourceRange = { VKUtil_GetFormatGroup(image->format).aspect, 0, 1, 0, 1 }
};
batch->barrierCount++;
batch->srcStages |= image->lastStage;
batch->dstStages |= stage;
image->lastStage = stage;
image->lastAccess = access;
image->layout = layout;
}
}

View File

@@ -27,23 +27,33 @@
#ifndef VKImage_h_Included
#define VKImage_h_Included
#include "VKTypes.h"
#include "VKAllocator.h"
#include "VKUtil.h"
typedef struct {
VkFormat format;
VKPackedSwizzle swizzle;
} VKImageViewKey;
typedef struct {
VkImageView view;
VkDescriptorSet descriptorSet;
VkDescriptorPool descriptorPool; // Non-owning.
} VKImageViewInfo;
struct VKImage {
VkImage handle;
VKMemory memory;
VkImageView view;
VkFormat format;
VkExtent2D extent;
VkImage handle;
VKMemory memory;
VkFormat format;
VkExtent2D extent;
MAP(VKImageViewKey, VKImageViewInfo) viewMap;
VkImageLayout layout;
VkPipelineStageFlagBits lastStage;
VkAccessFlagBits lastAccess;
};
VkImageAspectFlagBits VKImage_GetAspect(VKImage* image);
VKImage* VKImage_Create(VKDevice* device, uint32_t width, uint32_t height,
VkImageCreateFlags flags, VkFormat format,
VkImageTiling tiling, VkImageUsageFlags usage, VkSampleCountFlagBits samples,
@@ -53,4 +63,12 @@ void VKImage_LoadBuffer(VKDevice* device, VKImage* image, VKBuffer* buffer,
uint32_t x0, uint32_t y0, uint32_t width, uint32_t height);
void VKImage_Destroy(VKDevice* device, VKImage* image);
VkImageView VKImage_GetView(VKDevice* device, VKImage* image, VkFormat format, VKPackedSwizzle swizzle);
VkDescriptorSet VKImage_GetDescriptorSet(VKDevice* device, VKImage* image, VkFormat format, VKPackedSwizzle swizzle);
void VKImage_AddBarrier(VkImageMemoryBarrier* barriers, VKBarrierBatch* batch,
VKImage* image, VkPipelineStageFlags stage, VkAccessFlags access, VkImageLayout layout);
#endif // VKImage_h_Included

View File

@@ -22,7 +22,7 @@
// questions.
#include "VKUtil.h"
#include "VKBase.h"
#include "VKEnv.h"
#include "VKPipelines.h"
#define INCLUDE_BYTECODE
@@ -44,6 +44,8 @@ static size_t pipelineDescriptorHash(const void* ptr) {
const VKPipelineDescriptor* d = ptr;
uint32_t h = 0U;
hash(&h, d->stencilMode);
hash(&h, d->dstOpaque);
hash(&h, d->inAlphaType);
hash(&h, d->composite);
hash(&h, d->shader);
hash(&h, d->topology);
@@ -52,6 +54,8 @@ static size_t pipelineDescriptorHash(const void* ptr) {
static bool pipelineDescriptorEquals(const void* ap, const void* bp) {
const VKPipelineDescriptor *a = ap, *b = bp;
return a->stencilMode == b->stencilMode &&
a->dstOpaque == b->dstOpaque &&
a->inAlphaType == b->inAlphaType &&
a->composite == b->composite &&
a->shader == b->shader &&
a->topology == b->topology;
@@ -123,31 +127,49 @@ for (uint32_t i = 0; i < SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTES_##NAME); i++) {
VKUtil_GetFormatGroup(INPUT_STATE_ATTRIBUTE_FORMATS_##NAME[i]).bytes; \
} if (sizeof(TYPE) != INPUT_STATE_BINDING_SIZE_##NAME) VK_FATAL_ERROR("Vertex size mismatch for input state " #NAME)
static VkPipeline VKPipelines_CreatePipelines(VKRenderPassContext* renderPassContext, uint32_t count,
const VKPipelineDescriptor* descriptors) {
static VKPipelineInfo VKPipelines_CreatePipelines(VKRenderPassContext* renderPassContext, uint32_t count,
const VKPipelineDescriptor* descriptors) {
assert(renderPassContext != NULL && renderPassContext->pipelineContext != NULL);
assert(count > 0 && descriptors != NULL);
VKPipelineContext* pipelineContext = renderPassContext->pipelineContext;
VKDevice* device = pipelineContext->device;
VKShaders* shaders = pipelineContext->shaders;
VKComposites* composites = &VKGE_graphics_environment()->composites;
VKComposites* composites = &VKEnv_GetInstance()->composites;
VKPipelineInfo pipelineInfos[count];
// Setup pipeline creation structs.
static const uint32_t MAX_DYNAMIC_STATES = 2;
typedef struct {
VkPipelineShaderStageCreateInfo createInfos[2]; // vert + frag
} ShaderStages;
ShaderStages stages[count];
typedef struct {
VkSpecializationInfo info;
VkSpecializationMapEntry entries[2];
uint64_t data[1];
} Specialization;
Specialization specializations[count][2];
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStates[count];
VkPipelineDepthStencilStateCreateInfo depthStencilStates[count];
VkPipelineDynamicStateCreateInfo dynamicStates[count];
VkDynamicState dynamicStateValues[count][MAX_DYNAMIC_STATES];
VkGraphicsPipelineCreateInfo createInfos[count];
for (uint32_t i = 0; i < count; i++) {
const VKCompositeState* compositeState =
VKComposites_GetState(composites, descriptors[i].composite, descriptors[i].dstOpaque);
pipelineInfos[i].outAlphaType = compositeState->outAlphaType;
// Init default pipeline state. Some members are left uninitialized:
// - 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
};
}
inputAssemblyStates[i] = (VkPipelineInputAssemblyStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = descriptors[i].topology
@@ -201,7 +223,7 @@ static VkPipeline VKPipelines_CreatePipelines(VKRenderPassContext* renderPassCon
.pRasterizationState = &rasterizationState,
.pMultisampleState = &multisampleState,
.pDepthStencilState = &depthStencilStates[i],
.pColorBlendState = &VKComposites_GetState(composites, descriptors[i].composite)->blendState,
.pColorBlendState = &compositeState->blendState,
.pDynamicState = &dynamicStates[i],
.renderPass = renderPassContext->renderPass[descriptors[i].stencilMode != STENCIL_MODE_NONE],
.subpass = 0,
@@ -233,6 +255,15 @@ static VkPipeline VKPipelines_CreatePipelines(VKRenderPassContext* renderPassCon
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;
@@ -259,8 +290,8 @@ static VkPipeline VKPipelines_CreatePipelines(VKRenderPassContext* renderPassCon
VK_FATAL_ERROR("Cannot create pipeline, unknown shader requested!");
}
assert(createInfos[i].pDynamicState->dynamicStateCount <= MAX_DYNAMIC_STATES);
J2dRlsTraceLn4(J2D_TRACE_INFO, "VKPipelines_CreatePipelines: stencilMode=%d, composite=%d, shader=%d, topology=%d",
descriptors[i].stencilMode, descriptors[i].composite, descriptors[i].shader, descriptors[i].topology);
J2dRlsTraceLn5(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);
}
// Create pipelines.
@@ -269,8 +300,11 @@ static VkPipeline VKPipelines_CreatePipelines(VKRenderPassContext* renderPassCon
VK_IF_ERROR(device->vkCreateGraphicsPipelines(device->handle, VK_NULL_HANDLE, count,
createInfos, NULL, pipelines)) VK_UNHANDLED_ERROR();
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_CreatePipelines: created %d pipelines", count);
for (uint32_t i = 0; i < count; ++i) MAP_AT(renderPassContext->pipelines, descriptors[i]) = pipelines[i];
return pipelines[0];
for (uint32_t i = 0; i < count; i++) {
pipelineInfos[i].pipeline = pipelines[i];
MAP_AT(renderPassContext->pipelines, descriptors[i]) = pipelineInfos[i];
}
return pipelineInfos[0];
}
static VkResult VKPipelines_InitRenderPasses(VKDevice* device, VKRenderPassContext* renderPassContext) {
@@ -332,8 +366,8 @@ static void VKPipelines_DestroyRenderPassContext(VKRenderPassContext* renderPass
VKDevice* device = renderPassContext->pipelineContext->device;
assert(device != NULL);
for (const VKPipelineDescriptor* k = NULL; (k = MAP_NEXT_KEY(renderPassContext->pipelines, k)) != NULL;) {
VkPipeline pipeline = *MAP_FIND(renderPassContext->pipelines, *k);
device->vkDestroyPipeline(device->handle, pipeline, NULL);
const VKPipelineInfo* info = MAP_FIND(renderPassContext->pipelines, *k);
device->vkDestroyPipeline(device->handle, info->pipeline, NULL);
}
MAP_FREE(renderPassContext->pipelines);
for (uint32_t i = 0; i < 2; i++) {
@@ -372,7 +406,7 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
VkPushConstantRange pushConstantRange = {
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0,
.size = sizeof(float) * 2
.size = sizeof(VKTransform)
};
VkPipelineLayoutCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
@@ -385,7 +419,7 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
VkDescriptorSetLayoutBinding textureLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
@@ -398,8 +432,12 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
result = device->vkCreateDescriptorSetLayout(device->handle, &textureDescriptorSetLayoutCreateInfo, NULL, &pipelines->textureDescriptorSetLayout);
VK_IF_ERROR(result) return result;
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &pipelines->textureDescriptorSetLayout;
VkDescriptorSetLayout textureDescriptorSetLayouts[] = {
pipelines->textureDescriptorSetLayout,
pipelines->samplers.descriptorSetLayout
};
createInfo.setLayoutCount = 2;
createInfo.pSetLayouts = textureDescriptorSetLayouts;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->texturePipelineLayout);
VK_IF_ERROR(result) return result;
@@ -432,6 +470,12 @@ VKPipelineContext* VKPipelines_CreateContext(VKDevice* device) {
VK_RUNTIME_ASSERT(pipelineContext);
pipelineContext->device = device;
pipelineContext->samplers = VKSamplers_Create(device);
if (pipelineContext->samplers.descriptorPool == VK_NULL_HANDLE) {
VKPipelines_DestroyContext(pipelineContext);
return NULL;
}
pipelineContext->shaders = VKPipelines_CreateShaders(device);
if (pipelineContext->shaders == NULL) {
VKPipelines_DestroyContext(pipelineContext);
@@ -443,20 +487,6 @@ VKPipelineContext* VKPipelines_CreateContext(VKDevice* device) {
return NULL;
}
// Create sampler.
VkSamplerCreateInfo samplerCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT
};
VK_IF_ERROR(device->vkCreateSampler(device->handle, &samplerCreateInfo, NULL, &pipelineContext->linearRepeatSampler)) {
VKPipelines_DestroyContext(pipelineContext);
return NULL;
}
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_CreateContext(%p)", pipelineContext);
return pipelineContext;
}
@@ -472,7 +502,6 @@ void VKPipelines_DestroyContext(VKPipelineContext* pipelineContext) {
ARRAY_FREE(pipelineContext->renderPassContexts);
VKPipelines_DestroyShaders(device, pipelineContext->shaders);
device->vkDestroySampler(device->handle, pipelineContext->linearRepeatSampler, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->colorPipelineLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->texturePipelineLayout, NULL);
@@ -480,6 +509,8 @@ void VKPipelines_DestroyContext(VKPipelineContext* pipelineContext) {
device->vkDestroyPipelineLayout(device->handle, pipelineContext->maskFillPipelineLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->maskFillDescriptorSetLayout, NULL);
VKSamplers_Destroy(device, pipelineContext->samplers);
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_DestroyContext(%p)", pipelineContext);
free(pipelineContext);
}
@@ -497,11 +528,13 @@ VKRenderPassContext* VKPipelines_GetRenderPassContext(VKPipelineContext* pipelin
return renderPassContext;
}
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKPipelineDescriptor descriptor) {
// static VKPipelineInfo VKPipelines_
VKPipelineInfo VKPipelines_GetPipelineInfo(VKRenderPassContext* renderPassContext, VKPipelineDescriptor descriptor) {
assert(renderPassContext != NULL);
VkPipeline pipeline = MAP_AT(renderPassContext->pipelines, descriptor);
if (pipeline == VK_NULL_HANDLE) {
pipeline = VKPipelines_CreatePipelines(renderPassContext, 1, &descriptor);
VKPipelineInfo info = MAP_AT(renderPassContext->pipelines, descriptor);
if (info.pipeline == VK_NULL_HANDLE) {
info = VKPipelines_CreatePipelines(renderPassContext, 1, &descriptor);
}
return pipeline;
return info;
}

View File

@@ -25,6 +25,7 @@
#define VKPipelines_h_Included
#include "VKComposites.h"
#include "VKSamplers.h"
#include "VKTypes.h"
#include "VKUtil.h"
@@ -53,12 +54,19 @@ typedef enum {
* When adding new fields, update hash and comparison in VKPipelines.c.
*/
typedef struct {
VKStencilMode stencilMode;
VKStencilMode stencilMode : 2;
VkBool32 dstOpaque : 1;
AlphaType inAlphaType : 1;
VKCompositeMode composite;
VKShader shader;
VkPrimitiveTopology topology;
} VKPipelineDescriptor;
typedef struct {
VkPipeline pipeline;
AlphaType outAlphaType;
} VKPipelineInfo;
/**
* Global pipeline context.
*/
@@ -70,8 +78,7 @@ struct VKPipelineContext {
VkDescriptorSetLayout maskFillDescriptorSetLayout;
VkPipelineLayout maskFillPipelineLayout;
VkSampler linearRepeatSampler;
VKSamplers samplers;
struct VKShaders* shaders;
ARRAY(VKRenderPassContext*) renderPassContexts;
};
@@ -80,10 +87,10 @@ struct VKPipelineContext {
* Per-format context.
*/
struct VKRenderPassContext {
VKPipelineContext* pipelineContext;
VkFormat format;
VkRenderPass renderPass[2]; // Color-only and color+stencil.
MAP(VKPipelineDescriptor, VkPipeline) pipelines;
VKPipelineContext* pipelineContext;
VkFormat format;
VkRenderPass renderPass[2]; // Color-only and color+stencil.
MAP(VKPipelineDescriptor, VKPipelineInfo) pipelines;
};
typedef struct {
@@ -92,7 +99,7 @@ typedef struct {
typedef struct {
float x, y;
Color color;
RGBA color;
} VKColorVertex;
typedef struct {
@@ -102,13 +109,13 @@ typedef struct {
typedef struct {
int x, y, maskOffset, maskScanline;
Color color;
RGBA color;
} VKMaskFillColorVertex;
VKPipelineContext* VKPipelines_CreateContext(VKDevice* device);
void VKPipelines_DestroyContext(VKPipelineContext* pipelines);
VKRenderPassContext* VKPipelines_GetRenderPassContext(VKPipelineContext* pipelineContext, VkFormat format);
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKPipelineDescriptor descriptor);
VKPipelineInfo VKPipelines_GetPipelineInfo(VKRenderPassContext* renderPassContext, VKPipelineDescriptor descriptor);
#endif //VKPipelines_h_Included

View File

@@ -24,18 +24,17 @@
* questions.
*/
#ifndef HEADLESS
#include "sun_font_StrikeCache.h"
#include "sun_java2d_pipe_BufferedOpCodes.h"
#include "sun_java2d_pipe_BufferedRenderPipe.h"
#include "sun_java2d_pipe_BufferedTextPipe.h"
#include "sun_java2d_vulkan_VKBlitLoops.h"
#include "fontscalerdefs.h"
#include "Trace.h"
#include "jlong.h"
#include "VKBase.h"
#include "VKBlitLoops.h"
#include "VKSurfaceData.h"
#include "VKEnv.h"
#include "VKRenderer.h"
#include "VKUtil.h"
@@ -50,7 +49,8 @@
#define NEXT_BOOLEAN(buf) (jboolean)NEXT_INT(buf)
#define NEXT_LONG(buf) NEXT_VAL(buf, jlong)
#define NEXT_DOUBLE(buf) NEXT_VAL(buf, jdouble)
#define NEXT_SURFACE(buf) ((VKSDOps*) (SurfaceDataOps*) jlong_to_ptr(NEXT_LONG(buf)))
#define NEXT_SURFACE(buf) ((SurfaceDataOps*) jlong_to_ptr(NEXT_LONG(buf)))
#define NEXT_VK_SURFACE(buf) ((VKSDOps*) NEXT_SURFACE(buf))
/*
* Increments a pointer (buf) by the given number of bytes.
@@ -64,6 +64,8 @@
(((packedval) >> (offset)) & (mask))
#define EXTRACT_BYTE(packedval, offset) \
(unsigned char)EXTRACT_VAL(packedval, offset, 0xff)
#define EXTRACT_SHORT(packedval, offset) \
(jshort)EXTRACT_VAL(packedval, offset, 0xffff)
#define EXTRACT_BOOLEAN(packedval, offset) \
(jboolean)EXTRACT_VAL(packedval, offset, 0x1)
@@ -88,31 +90,9 @@
#define OFFSET_SRCTYPE sun_java2d_vulkan_VKBlitLoops_OFFSET_SRCTYPE
#define OFFSET_HINT sun_java2d_vulkan_VKBlitLoops_OFFSET_HINT
#define OFFSET_TEXTURE sun_java2d_vulkan_VKBlitLoops_OFFSET_TEXTURE
#define OFFSET_RTT sun_java2d_vulkan_VKBlitLoops_OFFSET_RTT
#define OFFSET_XFORM sun_java2d_vulkan_VKBlitLoops_OFFSET_XFORM
#define OFFSET_ISOBLIT sun_java2d_vulkan_VKBlitLoops_OFFSET_ISOBLIT
#define NO_CLIP ((VkRect2D) {{0, 0}, {0x7FFFFFFFU, 0x7FFFFFFFU}})
// Rendering context is only accessed from VKRenderQueue_flushBuffer,
// which is only called from queue flusher thread, no need for synchronization.
static VKRenderingContext context = {
.surface = NULL,
.transform = {1.0, 0.0, 0.0,0.0, 1.0, 0.0},
.color = {},
.composite = ALPHA_COMPOSITE_SRC_OVER,
.extraAlpha = 1.0f,
.clipModCount = 1,
.clipRect = NO_CLIP,
.clipSpanVertices = NULL
};
// We keep this color separately from context.color,
// because we need consistent state when switching between XOR and alpha composite modes.
// This variable holds last value set by SET_COLOR, while context.color holds color,
// currently used for drawing, which may have also been provided by SET_XOR_COMPOSITE.
Color color;
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
(JNIEnv *env, jobject oglrq, jlong buf, jint limit)
{
@@ -162,7 +142,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn4(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: DRAW_RECT(%d, %d, %d, %d)",
x, y, w, h);
VKRenderer_RenderRect(&context, VK_FALSE, x, y, w, h);
VKRenderer_RenderRect(VK_FALSE, x, y, w, h);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_POLY:
@@ -204,7 +184,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn8(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: DRAW_PARALLELOGRAM(%f, %f, %f, %f, %f, %f, %f, %f)",
x11, y11, dx21, dy21, dx12, dy12, lwr21, lwr12);
VKRenderer_RenderParallelogram(&context, VK_FALSE, x11, y11, dx21, dy21, dx12, dy12);
VKRenderer_RenderParallelogram(VK_FALSE, x11, y11, dx21, dy21,
dx12, dy12);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_AAPARALLELOGRAM:
@@ -232,7 +213,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint h = NEXT_INT(b);
J2dRlsTraceLn4(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FILL_RECT(%d, %d, %d, %d)", x, y, w, h);
VKRenderer_RenderRect(&context, VK_TRUE, x, y, w, h);
VKRenderer_RenderRect(VK_TRUE, x, y, w, h);
}
break;
case sun_java2d_pipe_BufferedOpCodes_FILL_SPANS:
@@ -240,7 +221,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint count = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FILL_SPANS");
VKRenderer_FillSpans(&context, count, (jint *)b);
VKRenderer_FillSpans(count, (jint *) b);
SKIP_BYTES(b, count * BYTES_PER_SPAN);
}
break;
@@ -255,7 +236,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn6(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FILL_PARALLELOGRAM(%f, %f, %f, %f, %f, %f)",
x11, y11, dx21, dy21, dx12, dy12);
VKRenderer_RenderParallelogram(&context, VK_TRUE, x11, y11, dx21, dy21, dx12, dy12);
VKRenderer_RenderParallelogram(VK_TRUE, x11, y11, dx21, dy21,
dx12, dy12);
}
break;
case sun_java2d_pipe_BufferedOpCodes_FILL_AAPARALLELOGRAM:
@@ -270,7 +252,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
"VKRenderQueue_flushBuffer: FILL_AAPARALLELOGRAM(%f, %f, %f, %f, %f, %f)",
x11, y11, dx21, dy21, dx12, dy12);
// TODO this is not AA!
VKRenderer_RenderParallelogram(&context, VK_TRUE, x11, y11, dx21, dy21, dx12, dy12);
VKRenderer_RenderParallelogram(VK_TRUE, x11, y11, dx21, dy21,
dx12, dy12);
}
break;
@@ -317,7 +300,11 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
}
if (ginfo->format != sun_font_StrikeCache_PIXEL_FORMAT_GREYSCALE) continue;
if (ginfo->height*ginfo->rowBytes == 0) continue;
VKRenderer_MaskFill(&context, (int) glyphx, (int) glyphy, ginfo->width, ginfo->height, 0, ginfo->rowBytes, ginfo->height*ginfo->rowBytes, ginfo->image);
VKRenderer_MaskFill((int) glyphx, (int) glyphy,
ginfo->width, ginfo->height,
0, ginfo->rowBytes,
ginfo->height * ginfo->rowBytes,
ginfo->image);
}
SKIP_BYTES(b, numGlyphs * bytesPerGlyph);
}
@@ -348,38 +335,33 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jdouble dy1 = NEXT_DOUBLE(b);
jdouble dx2 = NEXT_DOUBLE(b);
jdouble dy2 = NEXT_DOUBLE(b);
jlong pSrc = NEXT_LONG(b);
jlong pDst = NEXT_LONG(b);
SurfaceDataOps* src = NEXT_SURFACE(b);
VKSDOps* dst = NEXT_VK_SURFACE(b);
jint hint = EXTRACT_BYTE(packedParams, OFFSET_HINT);
jboolean texture = EXTRACT_BOOLEAN(packedParams,
OFFSET_TEXTURE);
jboolean rtt = EXTRACT_BOOLEAN(packedParams,
OFFSET_RTT);
jboolean xform = EXTRACT_BOOLEAN(packedParams,
OFFSET_XFORM);
jboolean isoblit = EXTRACT_BOOLEAN(packedParams,
OFFSET_ISOBLIT);
VKSDOps *dstOps = (VKSDOps *)jlong_to_ptr(pDst);
VKSDOps *oldSurface = context.surface;
context.surface = dstOps;
VKRenderingContext* context = VKRenderer_GetContext();
VKSDOps* oldSurface = context->surface;
context->surface = dst;
if (isoblit) {
VKBlitLoops_IsoBlit(env, &context, pSrc,
xform, hint, texture,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
VKBlitLoops_IsoBlit((VKSDOps*) src,
hint,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
} else {
jint srctype = EXTRACT_BYTE(packedParams, OFFSET_SRCTYPE);
VKBlitLoops_Blit(env, &context, pSrc,
xform, hint, srctype, texture,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
jshort srctype = EXTRACT_SHORT(packedParams, OFFSET_SRCTYPE);
VKBlitLoops_Blit(env, ptr_to_jlong(src),
xform, hint, srctype,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
}
context.surface = oldSurface;
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT %p -> %p ", pSrc, pDst)
context->surface = oldSurface;
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT %p -> %p ", src, dst)
J2dRlsTraceLn8(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT (%d %d %d %d) -> (%f %f %f %f) ",
sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)
J2dRlsTraceLn4(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT texture=%d rtt=%d xform=%d isoblit=%d",
texture, rtt, xform, isoblit)
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT xform=%d isoblit=%d", xform, isoblit)
}
break;
@@ -395,7 +377,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jlong pSrc = NEXT_LONG(b);
jlong pDst = NEXT_LONG(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: SURFACE_TO_SW_BLIT");
VKBlitLoops_SurfaceToSwBlit(env, &context, pSrc, pDst, dsttype, sx, sy, dx, dy, w, h);
VKBlitLoops_SurfaceToSwBlit(env, pSrc, pDst, dsttype, sx, sy,
dx, dy, w, h);
}
break;
case sun_java2d_pipe_BufferedOpCodes_MASK_FILL:
@@ -411,7 +394,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn7(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: MASK_FILL(%d, %d, %dx%d, maskoff=%d, maskscan=%d, masklen=%d)",
x, y, w, h, maskoff, maskscan, masklen);
VKRenderer_MaskFill(&context, x, y, w, h, maskoff, maskscan, masklen, pMask);
VKRenderer_MaskFill(x, y, w, h,
maskoff, maskscan, masklen, pMask);
SKIP_BYTES(b, masklen);
}
break;
@@ -437,18 +421,18 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn4(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_RECT_CLIP(%d, %d, %d, %d)",
x1, y1, x2, y2);
ARRAY_RESIZE(context.clipSpanVertices, 0);
ARRAY_RESIZE(VKRenderer_GetContext()->clipSpanVertices, 0);
jint width = x2 - x1, height = y2 - y1;
context.clipRect = (VkRect2D) {{x1, y1}, {CARR_MAX(width, 0), CARR_MAX(height, 0)}};
context.clipModCount++;
VKRenderer_GetContext()->clipRect = (VkRect2D) {{x1, y1}, {CARR_MAX(width, 0), CARR_MAX(height, 0)}};
VKRenderer_GetContext()->clipModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_BEGIN_SHAPE_CLIP:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: BEGIN_SHAPE_CLIP");
ARRAY_RESIZE(context.clipSpanVertices, 0);
context.clipModCount++;
ARRAY_RESIZE(VKRenderer_GetContext()->clipSpanVertices, 0);
VKRenderer_GetContext()->clipModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_SHAPE_CLIP_SPANS:
@@ -456,38 +440,38 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint count = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_SHAPE_CLIP_SPANS");
size_t offset = ARRAY_SIZE(context.clipSpanVertices);
ARRAY_RESIZE(context.clipSpanVertices, offset + count * 6);
size_t offset = ARRAY_SIZE(VKRenderer_GetContext()->clipSpanVertices);
ARRAY_RESIZE(VKRenderer_GetContext()->clipSpanVertices, offset + count * 6);
for (jint i = 0; i < count; i++) {
jint x1 = NEXT_INT(b);
jint y1 = NEXT_INT(b);
jint x2 = NEXT_INT(b);
jint y2 = NEXT_INT(b);
context.clipSpanVertices[offset + i * 6 + 0] = (VKIntVertex) {x1, y1};
context.clipSpanVertices[offset + i * 6 + 1] = (VKIntVertex) {x2, y1};
context.clipSpanVertices[offset + i * 6 + 2] = (VKIntVertex) {x2, y2};
context.clipSpanVertices[offset + i * 6 + 3] = (VKIntVertex) {x2, y2};
context.clipSpanVertices[offset + i * 6 + 4] = (VKIntVertex) {x1, y2};
context.clipSpanVertices[offset + i * 6 + 5] = (VKIntVertex) {x1, y1};
VKRenderer_GetContext()->clipSpanVertices[offset + i * 6 + 0] = (VKIntVertex) {x1, y1};
VKRenderer_GetContext()->clipSpanVertices[offset + i * 6 + 1] = (VKIntVertex) {x2, y1};
VKRenderer_GetContext()->clipSpanVertices[offset + i * 6 + 2] = (VKIntVertex) {x2, y2};
VKRenderer_GetContext()->clipSpanVertices[offset + i * 6 + 3] = (VKIntVertex) {x2, y2};
VKRenderer_GetContext()->clipSpanVertices[offset + i * 6 + 4] = (VKIntVertex) {x1, y2};
VKRenderer_GetContext()->clipSpanVertices[offset + i * 6 + 5] = (VKIntVertex) {x1, y1};
}
context.clipModCount++;
VKRenderer_GetContext()->clipModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_END_SHAPE_CLIP:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: END_SHAPE_CLIP");
context.clipRect = NO_CLIP;
context.clipModCount++;
VKRenderer_GetContext()->clipRect = NO_CLIP;
VKRenderer_GetContext()->clipModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_CLIP:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_CLIP");
ARRAY_RESIZE(context.clipSpanVertices, 0);
context.clipRect = NO_CLIP;
context.clipModCount++;
ARRAY_RESIZE(VKRenderer_GetContext()->clipSpanVertices, 0);
VKRenderer_GetContext()->clipRect = NO_CLIP;
VKRenderer_GetContext()->clipModCount++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_ALPHA_COMPOSITE:
@@ -497,9 +481,9 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint flags = NEXT_INT(b);
J2dRlsTraceLn3(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_ALPHA_COMPOSITE(%d, %f, %d)", rule, extraAlpha, flags);
context.color = color;
context.composite = (VKCompositeMode) rule;
context.extraAlpha = extraAlpha;
VKRenderer_GetContext()->renderColor = VKRenderer_GetContext()->color;
VKRenderer_GetContext()->composite = (VKCompositeMode) rule;
VKRenderer_GetContext()->extraAlpha = extraAlpha;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_XOR_COMPOSITE:
@@ -507,19 +491,20 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint xorPixel = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_XOR_COMPOSITE");
context.color = VKUtil_DecodeJavaColor(xorPixel);
context.color.a = 0.0f; // Alpha is left unchanged in XOR mode.
context.composite = LOGIC_COMPOSITE_XOR;
context.extraAlpha = 1.0f;
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;
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_COMPOSITE:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_COMPOSITE");
context.color = color;
context.composite = ALPHA_COMPOSITE_SRC;
context.extraAlpha = 1.0f;
VKRenderer_GetContext()->renderColor = VKRenderer_GetContext()->color;
VKRenderer_GetContext()->composite = ALPHA_COMPOSITE_SRC;
VKRenderer_GetContext()->extraAlpha = 1.0f;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_TRANSFORM:
@@ -536,37 +521,39 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
" | %.2f %.2f %.2f |", m10, m11, m12);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
" | 0.00 0.00 1.00 |");
context.transform.m00 = m00;
context.transform.m10 = m10;
context.transform.m01 = m01;
context.transform.m11 = m11;
context.transform.m02 = m02;
context.transform.m12 = m12;
VKTransform transform = {
.m00 = m00, .m10 = m10, .m01 = m01,
.m11 = m11, .m02 = m02, .m12 = m12
};
VKRenderingContext* context = VKRenderer_GetContext();
if (VK_IS_NEQ_TRANSFORM(&context->transform, &transform)) {
context->transform = transform;
context->transformModCount++;
}
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_TRANSFORM:
{
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;
context->transformModCount++;
}
}
break;
// context-related ops
case sun_java2d_pipe_BufferedOpCodes_SET_SURFACES:
{
VKSDOps* src = NEXT_SURFACE(b);
VKSDOps* dst = NEXT_SURFACE(b);
VKSDOps* src = NEXT_VK_SURFACE(b);
VKSDOps* dst = NEXT_VK_SURFACE(b);
J2dRlsTraceLn2(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_SURFACES src=%p dst=%p", src, dst);
if (context.surface != NULL && context.surface != dst) {
// TODO Problematic surface flush on a context switch without explicit presentation request.
// Its presence here should not make any difference, but for some reason does.
// Related scenarios need an investigation, e.g. J2Demo.
VKRenderer_FlushSurface(context.surface);
}
context.surface = dst;
VKRenderer_GetContext()->surface = dst;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_SCRATCH_SURFACE:
@@ -574,12 +561,12 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jlong pConfigInfo = NEXT_LONG(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_SCRATCH_SURFACE");
context.surface = NULL;
VKRenderer_GetContext()->surface = NULL;
}
break;
case sun_java2d_pipe_BufferedOpCodes_FLUSH_SURFACE:
{
VKSDOps* surface = NEXT_SURFACE(b);
VKSDOps* surface = NEXT_VK_SURFACE(b);
J2dRlsTraceLn1(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FLUSH_SURFACE (%p)", surface)
}
@@ -596,14 +583,14 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jlong pConfigInfo = NEXT_LONG(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: DISPOSE_CONFIG")
context.surface = NULL;
VKRenderer_GetContext()->surface = NULL;
}
break;
case sun_java2d_pipe_BufferedOpCodes_INVALIDATE_CONTEXT:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: INVALIDATE_CONTEXT");
context.surface = NULL;
VKRenderer_GetContext()->surface = NULL;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SYNC:
@@ -615,12 +602,13 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
case sun_java2d_pipe_BufferedOpCodes_CONFIGURE_SURFACE:
{
VKSDOps* surface = NEXT_SURFACE(b);
VKSDOps* surface = NEXT_VK_SURFACE(b);
VKDevice* device = jlong_to_ptr(NEXT_LONG(b));
jint width = NEXT_INT(b);
jint height = NEXT_INT(b);
J2dRlsTraceLn3(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: CONFIGURE_SURFACE (%p) %dx%d", surface, width, height);
VKRenderer_ConfigureSurface(surface, (VkExtent2D) {width, height});
VKRenderer_ConfigureSurface(surface, (VkExtent2D) {width, height}, device);
}
break;
@@ -635,7 +623,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
case sun_java2d_pipe_BufferedOpCodes_FLUSH_BUFFER:
{
VKSDOps* surface = NEXT_SURFACE(b);
VKSDOps* surface = NEXT_VK_SURFACE(b);
J2dRlsTraceLn1(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FLUSH_BUFFER (%p)", surface)
@@ -661,11 +649,17 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
case sun_java2d_pipe_BufferedOpCodes_SET_COLOR:
{
jint javaColor = NEXT_INT(b);
color = VKUtil_DecodeJavaColor(javaColor);
if (COMPOSITE_GROUP(context.composite) == ALPHA_COMPOSITE_GROUP) context.color = color;
VKRenderer_GetContext()->color = VKUtil_DecodeJavaColor(javaColor, ALPHA_TYPE_STRAIGHT);
if (COMPOSITE_GROUP(VKRenderer_GetContext()->composite) == ALPHA_COMPOSITE_GROUP) {
VKRenderer_GetContext()->renderColor = VKRenderer_GetContext()->color;
}
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: SET_COLOR(0x%08x)", javaColor);
J2dTraceLn4(J2D_TRACE_VERBOSE, // Print color values with straight alpha for convenience.
" srgb={%.3f, %.3f, %.3f, %.3f}", color.r/color.a, color.g/color.a, color.b/color.a, color.a);
" 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);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_GRADIENT_PAINT:
@@ -799,10 +793,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
}
// Flush all pending GPU work
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
for (uint32_t i = 0; i < ARRAY_SIZE(ge->devices); i++) {
VKRenderer_Flush(ge->devices[i].renderer);
VKEnv* vk = VKEnv_GetInstance();
for (uint32_t i = 0; i < ARRAY_SIZE(vk->devices); i++) {
VKRenderer_Flush(vk->devices[i].renderer);
}
}
#endif /* !HEADLESS */

View File

@@ -24,14 +24,13 @@
* questions.
*/
#ifndef HEADLESS
#include <assert.h>
#include <string.h>
#include "sun_java2d_vulkan_VKGPU.h"
#include "VKUtil.h"
#include "VKBase.h"
#include "VKAllocator.h"
#include "VKBuffer.h"
#include "VKDevice.h"
#include "VKImage.h"
#include "VKRenderer.h"
#include "VKSurfaceData.h"
@@ -54,6 +53,12 @@ RING_BUFFER(struct PoolEntry_ ## NAME { \
(VAR) = RING_BUFFER_FRONT((RENDERER)->NAME)->value; RING_BUFFER_POP_FRONT((RENDERER)->NAME); \
}} while(0)
/**
* Check if there are available items in the pool.
*/
#define POOL_NOT_EMPTY(RENDERER, NAME) \
(VKRenderer_CheckPoolEntryAvailable((RENDERER), RING_BUFFER_FRONT((RENDERER)->NAME)))
/**
* Return an item to the pool. It will only become available again
* after the next submitted batch of work completes execution on GPU.
@@ -84,6 +89,11 @@ RING_BUFFER(struct PoolEntry_ ## NAME { \
*/
#define POOL_FREE(RENDERER, NAME) RING_BUFFER_FREE((RENDERER)->NAME)
typedef struct {
VKCleanupHandler handler;
void* data;
} VKCleanupEntry;
/**
* Renderer attached to device.
*/
@@ -97,11 +107,13 @@ struct VKRenderer {
POOL(VKBuffer, vertexBufferPool);
POOL(VKTexelBuffer, maskFillBufferPool);
POOL(VkFramebuffer, framebufferDestructionQueue);
POOL(VKCleanupEntry, cleanupQueue);
ARRAY(VKMemory) bufferMemoryPages;
ARRAY(VkDescriptorPool) descriptorPools;
ARRAY(VkDescriptorPool) imageDescriptorPools;
/**
* Last known timestamp hit by GPU execution. Resources with equal or less timestamp may be safely reused.
* Last known timestamp reached by GPU execution. Resources with equal or less timestamp may be safely reused.
*/
uint64_t readTimestamp;
/**
@@ -133,6 +145,11 @@ typedef struct {
VkBool32 bound;
} BufferWritingState;
typedef struct {
BufferWritingState state;
uint32_t elements;
} BufferWriting;
/**
* Rendering-related info attached to surface.
*/
@@ -140,6 +157,7 @@ struct VKRenderPass {
VKRenderPassContext* context;
ARRAY(VKBuffer) vertexBuffers;
ARRAY(VKTexelBuffer) maskFillBuffers;
ARRAY(VKSDOps*) usedSurfaces;
VkRenderPass renderPass; // Non-owning.
VkFramebuffer framebuffer;
VkCommandBuffer commandBuffer;
@@ -150,13 +168,36 @@ struct VKRenderPass {
BufferWritingState maskFillBufferWriting;
VKPipelineDescriptor state;
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;
VkBool32 pendingCommands;
VkBool32 pendingClear;
uint64_t lastTimestamp; // When was this surface last used?
VkBool32 pendingFlush : 1;
VkBool32 pendingCommands : 1;
VkBool32 pendingClear : 1;
AlphaType outAlphaType : 1;
};
// Rendering context is only accessed from VKRenderQueue_flushBuffer,
// 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,
.clipModCount = 1,
.clipRect = NO_CLIP,
.clipSpanVertices = NULL
};
/**
* Accessor to the global rendering context.
*/
VKRenderingContext *VKRenderer_GetContext() {
return &context;
}
/**
* Helper function for POOL_TAKE macro.
*/
@@ -231,6 +272,48 @@ static VKTexelBuffer VKRenderer_GetMaskFillBuffer(VKRenderer* renderer) {
return texelBuffers[0];
}
#define IMAGE_DESCRIPTOR_POOL_SIZE 64
static VkDescriptorSet VKRenderer_AllocateImageDescriptorSet(VKRenderer* renderer, VkDescriptorPool descriptorPool) {
VKDevice* device = renderer->device;
VkDescriptorSetAllocateInfo allocateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &renderer->pipelineContext->textureDescriptorSetLayout
};
VkDescriptorSet set;
VkResult result = device->vkAllocateDescriptorSets(device->handle, &allocateInfo, &set);
if (result == VK_SUCCESS) return set;
if (result != VK_ERROR_OUT_OF_POOL_MEMORY && result != VK_ERROR_FRAGMENTED_POOL) {
VK_IF_ERROR(result) VK_UNHANDLED_ERROR();
}
return VK_NULL_HANDLE;
}
void VKRenderer_CreateImageDescriptorSet(VKRenderer* renderer, VkDescriptorPool* descriptorPool, VkDescriptorSet* set) {
VKDevice* device = renderer->device;
for (int i = ARRAY_SIZE(renderer->imageDescriptorPools) - 1; i >= 0; i--) {
*set = VKRenderer_AllocateImageDescriptorSet(renderer, renderer->imageDescriptorPools[i]);
if (*set != VK_NULL_HANDLE) {
*descriptorPool = renderer->imageDescriptorPools[i];
return;
}
}
VkDescriptorPoolSize poolSize = {
.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = IMAGE_DESCRIPTOR_POOL_SIZE
};
VkDescriptorPoolCreateInfo poolInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
.poolSizeCount = 1,
.pPoolSizes = &poolSize,
.maxSets = IMAGE_DESCRIPTOR_POOL_SIZE
};
VK_IF_ERROR(device->vkCreateDescriptorPool(device->handle, &poolInfo, NULL, descriptorPool)) VK_UNHANDLED_ERROR();
ARRAY_PUSH_BACK(renderer->imageDescriptorPools) = *descriptorPool;
*set = VKRenderer_AllocateImageDescriptorSet(renderer, *descriptorPool);
}
static VkSemaphore VKRenderer_AddPendingSemaphore(VKRenderer* renderer) {
VKDevice* device = renderer->device;
VkSemaphore semaphore = VK_NULL_HANDLE;
@@ -352,6 +435,10 @@ void VKRenderer_Destroy(VKRenderer* renderer) {
device->vkDestroyDescriptorPool(device->handle, renderer->descriptorPools[i], NULL);
}
ARRAY_FREE(renderer->descriptorPools);
for (uint32_t i = 0; i < ARRAY_SIZE(renderer->imageDescriptorPools); i++) {
device->vkDestroyDescriptorPool(device->handle, renderer->imageDescriptorPools[i], NULL);
}
ARRAY_FREE(renderer->imageDescriptorPools);
device->vkDestroySemaphore(device->handle, renderer->timelineSemaphore, NULL);
device->vkDestroyCommandPool(device->handle, renderer->commandPool, NULL);
@@ -366,6 +453,13 @@ void VKRenderer_Destroy(VKRenderer* renderer) {
static void VKRenderer_CleanupPendingResources(VKRenderer* renderer) {
VKDevice* device = renderer->device;
while (POOL_NOT_EMPTY(renderer, cleanupQueue)) {
VKCleanupEntry entry = {};
POOL_TAKE(renderer, cleanupQueue, entry);
entry.handler(device, entry.data);
}
for (;;) {
VkFramebuffer framebuffer = VK_NULL_HANDLE;
POOL_TAKE(renderer, framebufferDestructionQueue, framebuffer);
@@ -485,7 +579,7 @@ void VKRenderer_Flush(VKRenderer* renderer) {
* Prepare image barrier info to be executed in batch, if needed.
*/
void VKRenderer_AddImageBarrier(VkImageMemoryBarrier* barriers, VKBarrierBatch* batch,
VKImage* image, VkPipelineStageFlags stage, VkAccessFlags access, VkImageLayout layout) {
VKImage* image, VkPipelineStageFlags stage, VkAccessFlags access, VkImageLayout layout) {
assert(barriers != NULL && batch != NULL && image != NULL);
// TODO Even if stage, access and layout didn't change, we may still need a barrier against WaW hazard.
if (stage != image->lastStage || access != image->lastAccess || layout != image->layout) {
@@ -498,7 +592,7 @@ void VKRenderer_AddImageBarrier(VkImageMemoryBarrier* barriers, VKBarrierBatch*
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image->handle,
.subresourceRange = { VKImage_GetAspect(image), 0, 1, 0, 1 }
.subresourceRange = { VKUtil_GetFormatGroup(image->format).aspect, 0, 1, 0, 1 }
};
batch->barrierCount++;
batch->srcStages |= image->lastStage;
@@ -509,6 +603,40 @@ void VKRenderer_AddImageBarrier(VkImageMemoryBarrier* barriers, VKBarrierBatch*
}
}
/**
* Prepare buffer barrier info to be executed in batch, if needed.
*/
void VKRenderer_AddBufferBarrier(VkBufferMemoryBarrier* barriers, VKBarrierBatch* batch,
VKBuffer* buffer, VkPipelineStageFlags stage,
VkAccessFlags access)
{
assert(barriers != NULL && batch != NULL && buffer != NULL);
if (stage != buffer->lastStage || access != buffer->lastAccess) {
barriers[batch->barrierCount] = (VkBufferMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = buffer->lastAccess,
.dstAccessMask = access,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = buffer->handle,
.offset = 0,
.size = VK_WHOLE_SIZE
};
batch->barrierCount++;
batch->srcStages |= buffer->lastStage;
batch->dstStages |= stage;
buffer->lastStage = stage;
buffer->lastAccess = access;
}
}
/**
* Get Color RGBA components in a suitable for the current render pass.
*/
inline RGBA VKRenderer_GetRGBA(VKSDOps* surface, Color color) {
return VKUtil_GetRGBA(color, surface->renderPass->outAlphaType);
}
/**
* Record draw command, if there are any pending vertices in the vertex buffer
*/
@@ -529,6 +657,7 @@ static void VKRenderer_ResetDrawing(VKSDOps* surface) {
assert(surface != NULL && surface->renderPass != NULL);
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};
@@ -564,14 +693,21 @@ static void VKRenderer_DiscardRenderPass(VKSDOps* surface) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_DiscardRenderPass(%p)", surface);
}
}
static void VKRenderer_FlushDependentRenderPasses(VKSDOps* surface);
void VKRenderer_DestroyRenderPass(VKSDOps* surface) {
assert(surface != NULL);
if (surface->renderPass == NULL) return;
VKDevice* device = surface->device;
// Wait while surface & related resources are being used by the device.
if (device != NULL) {
VKRenderer_FlushDependentRenderPasses(surface);
if (surface->lastTimestamp == device->renderer->writeTimestamp) {
VKRenderer_Flush(device->renderer);
}
VKRenderer_Wait(device->renderer, surface->lastTimestamp);
}
if (surface->renderPass == NULL) return;
if (device != NULL && device->renderer != NULL) {
// Wait while surface resources are being used by the device.
VKRenderer_Wait(device->renderer, surface->renderPass->lastTimestamp);
VKRenderer_CleanupPendingResources(device->renderer);
VKRenderer_DiscardRenderPass(surface);
// Release resources.
@@ -607,14 +743,16 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
(*renderPass) = (VKRenderPass) {
.state = {
.stencilMode = STENCIL_MODE_NONE,
.dstOpaque = VKSD_IsOpaque(surface),
.inAlphaType = ALPHA_TYPE_UNKNOWN,
.composite = NO_COMPOSITE,
.shader = NO_SHADER
},
.transformModCount = 0,
.clipModCount = 0,
.pendingFlush = VK_FALSE,
.pendingCommands = VK_FALSE,
.pendingClear = VK_TRUE, // Clear the surface by default
.lastTimestamp = 0
};
// Initialize pipelines. They are cached until surface format changes.
@@ -645,7 +783,7 @@ static void VKRenderer_InitFramebuffer(VKSDOps* surface) {
// Initialize framebuffer.
if (renderPass->framebuffer == VK_NULL_HANDLE) {
renderPass->renderPass = renderPass->context->renderPass[surface->stencil != NULL];
VkImageView views[] = { surface->image->view, VK_NULL_HANDLE };
VkImageView views[] = { VKImage_GetView(device, surface->image, surface->image->format, 0), VK_NULL_HANDLE };
VkFramebufferCreateInfo framebufferCreateInfo = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = renderPass->renderPass,
@@ -657,7 +795,7 @@ static void VKRenderer_InitFramebuffer(VKSDOps* surface) {
};
if (surface->stencil != NULL) {
framebufferCreateInfo.attachmentCount = 2;
views[1] = surface->stencil->view;
views[1] = VKImage_GetView(device, surface->stencil, surface->stencil->format, 0);
}
VK_IF_ERROR(device->vkCreateFramebuffer(device->handle, &framebufferCreateInfo, NULL,
&renderPass->framebuffer)) VK_UNHANDLED_ERROR();
@@ -671,6 +809,7 @@ static void VKRenderer_InitFramebuffer(VKSDOps* surface) {
*/
static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
assert(surface != NULL && surface->renderPass != NULL && !surface->renderPass->pendingCommands);
VKRenderer_FlushDependentRenderPasses(surface);
VKRenderer_InitFramebuffer(surface);
// We may have a pending flush, which is already obsolete.
surface->renderPass->pendingFlush = VK_FALSE;
@@ -716,8 +855,9 @@ static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
VkClearAttachment clearAttachment = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.colorAttachment = 0,
.clearValue = surface->background.vkClearValue
.clearValue = VKRenderer_GetRGBA(surface, surface->background).vkClearValue
};
if (VKSD_IsOpaque(surface)) clearAttachment.clearValue.color.float32[3] = 1.0f;
VkClearRect clearRect = {
.rect = {{0, 0}, surface->image->extent},
.baseArrayLayer = 0,
@@ -737,17 +877,6 @@ static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
.maxDepth = 1.0f
};
device->vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
// Calculate inverse viewport for vertex shader.
viewport.width = 2.0f / viewport.width;
viewport.height = 2.0f / viewport.height;
device->vkCmdPushConstants(
commandBuffer,
renderer->pipelineContext->colorPipelineLayout, // TODO what if our pipeline layout differs?
VK_SHADER_STAGE_VERTEX_BIT,
0,
sizeof(float) * 2,
&viewport.width
);
surface->renderPass->pendingCommands = VK_TRUE;
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_BeginRenderPass(%p)", surface);
@@ -757,16 +886,43 @@ static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
* End render pass for the surface and record it into the primary command buffer,
* which will be executed on the next VKRenderer_Flush.
*/
void VKRenderer_FlushRenderPass(VKSDOps* surface) {
assert(surface != NULL && surface->renderPass != NULL);
VkBool32 VKRenderer_FlushRenderPass(VKSDOps* surface) {
assert(surface != NULL);
// If pendingFlush is TRUE, pendingCommands must be FALSE
assert(surface->renderPass == NULL || !surface->renderPass->pendingFlush || !surface->renderPass->pendingCommands);
// Note that we skip render pass initialization, if we have pending flush,
// which means that we missed the last flush, but didn't start a new render pass yet.
// So now we are going to catch up the last frame, and don't need reconfiguration.
// We also skip initialization if we have pending commands, because that means we are in the middle of frame.
if (surface->renderPass == NULL || (!surface->renderPass->pendingCommands && !surface->renderPass->pendingFlush)) {
if (!VKRenderer_InitRenderPass(surface)) return VK_FALSE;
// Check for pendingClear after VKRenderer_InitRenderPass, it may be set after reconfiguration.
if (!surface->renderPass->pendingClear) return VK_FALSE;
}
assert(surface->renderPass != NULL);
VKRenderer_FlushDraw(surface);
VkBool32 hasCommands = surface->renderPass->pendingCommands, clear = surface->renderPass->pendingClear;
if(!hasCommands && !clear) return;
if(!hasCommands && !clear) return VK_FALSE;
VKDevice* device = surface->device;
VKRenderer* renderer = device->renderer;
surface->renderPass->lastTimestamp = renderer->writeTimestamp;
VkCommandBuffer cb = VKRenderer_Record(renderer);
// Update dependencies 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);
}
ARRAY_RESIZE(surface->renderPass->usedSurfaces, 0);
// Insert barriers to prepare surface for rendering.
VkImageMemoryBarrier barriers[2];
VKBarrierBatch barrierBatch = {};
@@ -810,24 +966,31 @@ void VKRenderer_FlushRenderPass(VKSDOps* surface) {
device->vkCmdEndRenderPass(cb);
VKRenderer_ResetDrawing(surface);
J2dRlsTraceLn3(J2D_TRACE_VERBOSE, "VKRenderer_FlushRenderPass(%p): hasCommands=%d, clear=%d", surface, hasCommands, clear);
return VK_TRUE;
}
/**
* Flush render passes depending on a given surface.
* This function must be called before mutating a surface
* because there may be pending render passes reading from that surface.
*/
static void VKRenderer_FlushDependentRenderPasses(VKSDOps* surface) {
// We're going to clear dependentSurfaces in the end anyway,
// so temporarily reset it to NULL to save on removing flushed render passes one-by-one.
ARRAY(VKSDOps*) deps = surface->dependentSurfaces;
surface->dependentSurfaces = NULL;
uint32_t size = (uint32_t) ARRAY_SIZE(deps);
if (size > 0) J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_FlushDependentRenderPasses(%p): %d", surface, size);
for (uint32_t i = 0; i < size; i++) {
VKRenderer_FlushRenderPass(deps[i]);
}
ARRAY_RESIZE(deps, 0);
surface->dependentSurfaces = deps;
}
void VKRenderer_FlushSurface(VKSDOps* surface) {
assert(surface != NULL);
// If pendingFlush is TRUE, pendingCommands must be FALSE
assert(surface->renderPass == NULL || !surface->renderPass->pendingFlush || !surface->renderPass->pendingCommands);
// Note that we skip render pass initialization, if we have pending flush,
// which means that we missed the last flush, but didn't start a new render pass yet.
// So now we are going to catch up the last frame, and don't need reconfiguration.
// We also skip initialization if we have pending commands, because that means we are in the middle of frame.
if (surface->renderPass == NULL || (!surface->renderPass->pendingCommands && !surface->renderPass->pendingFlush)) {
if (!VKRenderer_InitRenderPass(surface)) return;
// Check for pendingClear after VKRenderer_InitRenderPass, it may be set after reconfiguration.
if (!surface->renderPass->pendingClear) return;
}
if (!VKRenderer_FlushRenderPass(surface)) return;
surface->renderPass->pendingFlush = VK_FALSE;
VKRenderer_FlushRenderPass(surface);
// If this is a swapchain surface, we need to blit the content onto it and queue it for presentation.
if (surface->drawableType == VKSD_WINDOW) {
@@ -842,8 +1005,8 @@ void VKRenderer_FlushSurface(VKSDOps* surface) {
VKDevice* device = surface->device;
VKRenderer* renderer = device->renderer;
surface->renderPass->lastTimestamp = renderer->writeTimestamp;
VkCommandBuffer cb = VKRenderer_Record(renderer);
surface->lastTimestamp = renderer->writeTimestamp;
// Acquire swapchain image.
VkSemaphore acquireSemaphore = VKRenderer_AddPendingSemaphore(renderer);
@@ -915,9 +1078,10 @@ void VKRenderer_FlushSurface(VKSDOps* surface) {
}
}
void VKRenderer_ConfigureSurface(VKSDOps* surface, VkExtent2D extent) {
void VKRenderer_ConfigureSurface(VKSDOps* surface, VkExtent2D extent, VKDevice* device) {
assert(surface != NULL);
surface->requestedExtent = extent;
surface->requestedDevice = device;
// We must only do pending flush between frames.
if (surface->renderPass != NULL && surface->renderPass->pendingFlush) {
if (surface->renderPass->pendingCommands) {
@@ -933,26 +1097,35 @@ void VKRenderer_ConfigureSurface(VKSDOps* surface, VkExtent2D extent) {
/**
* Allocate bytes for writing into buffer. Returned state contains:
* - data - pointer to the beginning of buffer, or NULL, if there is no buffer yet.
* - offset - writing offset into the buffer data, or 0, if there is no buffer yet.
* - bound - whether corresponding buffer is bound to the command buffer,
* caller is responsible for checking this value and setting up & binding the buffer.
* - state.data - pointer to the beginning of buffer, or NULL, if there is no buffer yet.
* - state.offset - writing offset into the buffer data, or 0, if there is no buffer yet.
* - state.bound - whether corresponding buffer is bound to the command buffer,
* caller is responsible for checking this value and setting up & binding the buffer.
* - elements - number of actually allocated elements.
*/
inline BufferWritingState VKRenderer_AllocateBufferData(VKSDOps* surface, BufferWritingState* writingState,
VkDeviceSize size, VkDeviceSize maxBufferSize) {
BufferWriting VKRenderer_AllocateBufferData(VKSDOps* surface, BufferWritingState* writingState,
VkDeviceSize elements, VkDeviceSize elementSize,
VkDeviceSize maxBufferSize) {
assert(surface != NULL && surface->renderPass != NULL && writingState != NULL);
assert(size <= maxBufferSize);
BufferWritingState result = *writingState;
writingState->offset += size;
// Overflow, flush drawing commands and take another buffer.
if (writingState->offset > maxBufferSize) {
VKRenderer_FlushDraw(surface);
writingState->offset = size;
result.offset = 0;
result.bound = VK_FALSE;
result.data = NULL;
assert(elementSize <= maxBufferSize);
VkDeviceSize totalSize = elements * elementSize;
BufferWriting result = { *writingState, elements };
writingState->offset += totalSize;
if (writingState->offset > maxBufferSize) { // Overflow.
if (result.state.offset + elementSize > maxBufferSize) {
// Cannot fit a single element, flush drawing commands and take another buffer.
VKRenderer_FlushDraw(surface);
result.state.offset = 0;
result.state.bound = VK_FALSE;
result.state.data = NULL;
}
// Calculate the number of remaining elements we can fit.
result.elements = (maxBufferSize - result.state.offset) / elementSize;
assert(result.elements > 0);
if (result.elements > elements) result.elements = elements;
writingState->offset = result.state.offset + result.elements * elementSize;
}
writingState->bound = VK_TRUE; // We assume caller will check the result and bind the buffer right away!
writingState->bound = VK_TRUE; // We assume the caller will check the result and bind the buffer right away!
return result;
}
@@ -964,25 +1137,26 @@ inline BufferWritingState VKRenderer_AllocateBufferData(VKSDOps* surface, Buffer
* This function must be called after all dynamic allocation functions,
* which can invalidate drawing state, e.g. VKRenderer_AllocateMaskFillBytes.
*/
static void* VKRenderer_AllocateVertices(const VKRenderingContext* context, uint32_t vertices, size_t vertexSize) {
uint32_t VKRenderer_AllocateVertices(uint32_t primitives, uint32_t vertices, size_t vertexSize, void** result) {
assert(vertices > 0 && vertexSize > 0);
assert(vertexSize * vertices <= VERTEX_BUFFER_SIZE);
VKSDOps* surface = context->surface;
BufferWritingState state = VKRenderer_AllocateBufferData(
surface, &surface->renderPass->vertexBufferWriting, (VkDeviceSize) (vertexSize * vertices), VERTEX_BUFFER_SIZE);
if (!state.bound) {
if (state.data == NULL) {
VKSDOps* surface = VKRenderer_GetContext()->surface;
BufferWriting writing = VKRenderer_AllocateBufferData(
surface, &surface->renderPass->vertexBufferWriting, primitives, vertices * vertexSize, VERTEX_BUFFER_SIZE);
if (!writing.state.bound) {
if (writing.state.data == NULL) {
VKBuffer buffer = VKRenderer_GetVertexBuffer(surface->device->renderer);
ARRAY_PUSH_BACK(surface->renderPass->vertexBuffers) = buffer;
surface->renderPass->vertexBufferWriting.data = state.data = buffer.data;
surface->renderPass->vertexBufferWriting.data = writing.state.data = buffer.data;
}
assert(ARRAY_SIZE(surface->renderPass->vertexBuffers) > 0);
surface->renderPass->firstVertex = surface->renderPass->vertexCount = 0;
surface->device->vkCmdBindVertexBuffers(surface->renderPass->commandBuffer, 0, 1,
&(ARRAY_LAST(surface->renderPass->vertexBuffers).handle), &state.offset);
&(ARRAY_LAST(surface->renderPass->vertexBuffers).handle), &writing.state.offset);
}
surface->renderPass->vertexCount += vertices;
return (void*) ((uint8_t*) state.data + state.offset);
surface->renderPass->vertexCount += writing.elements * vertices;
*((uint8_t**) result) = (uint8_t*) writing.state.data + writing.state.offset;
return writing.elements;
}
/**
@@ -992,22 +1166,23 @@ static void* VKRenderer_AllocateVertices(const VKRenderingContext* context, uint
* This function must be called after all dynamic allocation functions,
* which can invalidate drawing state, e.g. VKRenderer_AllocateMaskFillBytes.
*/
#define VK_DRAW(VERTICES, CONTEXT, VERTEX_COUNT) \
(VERTICES) = VKRenderer_AllocateVertices((CONTEXT), (VERTEX_COUNT), sizeof((VERTICES)[0]))
#define VK_DRAW(VERTICES, PRIMITIVE_COUNT, VERTEX_COUNT) \
VKRenderer_AllocateVertices((PRIMITIVE_COUNT), (VERTEX_COUNT), sizeof((VERTICES)[0]), (void**) &(VERTICES))
/**
* Allocate bytes from mask fill buffer. VKRenderer_Validate must have been called before.
* This function cannot take more bytes than fits into single mask fill buffer at once.
* Caller must write data at the returned pointer DrawingBufferWritingState.data
* and take into account DrawingBufferWritingState.offset from the beginning of the bound buffer.
* Caller must write data at the returned pointer VKBufferWritingState.data
* and take into account VKBufferWritingState.offset from the beginning of the bound buffer.
* This function can invalidate drawing state, always call it before VK_DRAW.
*/
static BufferWritingState VKRenderer_AllocateMaskFillBytes(const VKRenderingContext* context, uint32_t size) {
static BufferWritingState VKRenderer_AllocateMaskFillBytes(uint32_t size) {
assert(size > 0);
assert(size <= MASK_FILL_BUFFER_SIZE);
VKSDOps* surface = context->surface;
VKSDOps* surface = VKRenderer_GetContext()->surface;
BufferWritingState state = VKRenderer_AllocateBufferData(
surface, &surface->renderPass->maskFillBufferWriting, size, MASK_FILL_BUFFER_SIZE);
surface, &surface->renderPass->maskFillBufferWriting, 1, size, MASK_FILL_BUFFER_SIZE).state;
if (!state.bound) {
if (state.data == NULL) {
VKTexelBuffer buffer = VKRenderer_GetMaskFillBuffer(surface->device->renderer);
@@ -1015,7 +1190,7 @@ static BufferWritingState VKRenderer_AllocateMaskFillBytes(const VKRenderingCont
surface->renderPass->maskFillBufferWriting.data = state.data = buffer.buffer.data;
}
assert(ARRAY_SIZE(surface->renderPass->maskFillBuffers) > 0);
surface->device->vkCmdBindDescriptorSets(context->surface->renderPass->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
surface->device->vkCmdBindDescriptorSets(surface->renderPass->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
surface->device->renderer->pipelineContext->maskFillPipelineLayout,
0, 1, &ARRAY_LAST(surface->renderPass->maskFillBuffers).descriptorSet, 0, NULL);
}
@@ -1023,6 +1198,31 @@ static BufferWritingState VKRenderer_AllocateMaskFillBytes(const VKRenderingCont
return state;
}
static void VKRenderer_ValidateTransform() {
VKRenderingContext* context = VKRenderer_GetContext();
assert(context->surface != NULL);
VKSDOps* surface = context->surface;
VKRenderPass* renderPass = surface->renderPass;
if (renderPass->transformModCount != context->transformModCount) {
J2dTraceLn(J2D_TRACE_VERBOSE, "VKRenderer_ValidateTransform: updating transform");
VKRenderer_FlushDraw(surface);
renderPass->transformModCount = context->transformModCount;
// Calculate user to device transform.
VKTransform transform = {
2.0f / (float) surface->image->extent.width, 0.0f, -1.0f,
0.0f, 2.0f / (float) surface->image->extent.height, -1.0f
};
// Combine it with user transform.
VKUtil_ConcatenateTransform(&transform, &context->transform);
// Push the transform into shader.
surface->device->vkCmdPushConstants(
renderPass->commandBuffer,
surface->device->renderer->pipelineContext->colorPipelineLayout, // TODO what if our pipeline layout differs?
VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(VKTransform), &transform
);
}
}
/**
* Setup stencil attachment according to the context clip state.
* If there is a clip shape, attachment is cleared with "fail" value and then
@@ -1035,6 +1235,7 @@ static void VKRenderer_SetupStencil(const VKRenderingContext* context) {
VKRenderPass* renderPass = surface->renderPass;
VkCommandBuffer cb = renderPass->commandBuffer;
VKRenderer_FlushDraw(surface);
VKRenderer_ValidateTransform();
// Clear stencil attachment.
VkClearAttachment clearAttachment = {
@@ -1051,12 +1252,14 @@ static void VKRenderer_SetupStencil(const VKRenderingContext* context) {
// Bind the clip pipeline.
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
VKPipelines_GetPipeline(surface->renderPass->context, (VKPipelineDescriptor) {
VKPipelines_GetPipelineInfo(surface->renderPass->context, (VKPipelineDescriptor) {
.stencilMode = STENCIL_MODE_ON,
.dstOpaque = VK_TRUE,
.inAlphaType = ALPHA_TYPE_UNKNOWN,
.composite = NO_COMPOSITE,
.shader = SHADER_CLIP,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
}));
}).pipeline);
// Reset vertex buffer binding.
renderPass->vertexBufferWriting.bound = VK_FALSE;
@@ -1067,7 +1270,7 @@ static void VKRenderer_SetupStencil(const VKRenderingContext* context) {
uint32_t currentDraw = ARRAY_SIZE(context->clipSpanVertices) - drawn;
if (currentDraw > MAX_VERTICES_PER_DRAW) currentDraw = MAX_VERTICES_PER_DRAW;
else if (currentDraw == 0) break;
VK_DRAW(vs, context, currentDraw);
VK_DRAW(vs, 1, currentDraw);
memcpy(vs, context->clipSpanVertices + drawn, currentDraw * sizeof(VKIntVertex));
drawn += currentDraw;
}
@@ -1077,12 +1280,31 @@ static void VKRenderer_SetupStencil(const VKRenderingContext* context) {
renderPass->state.shader = NO_SHADER;
}
void VKRenderer_DisposeOnCleanup(VKRenderer* renderer, VKCleanupHandler hnd, void* data) {
if (renderer == NULL) return;
VKCleanupEntry entry = {hnd, data};
POOL_RETURN(renderer, cleanupQueue, entry);
}
void VKRenderer_RecordBarriers(VKRenderer* renderer,
VkBufferMemoryBarrier* bufferBarriers, VKBarrierBatch* bufferBatch,
VkImageMemoryBarrier* imageBarriers, VKBarrierBatch* imageBatch) {
assert(renderer != NULL && renderer->device != NULL);
if ((bufferBatch == NULL || bufferBatch->barrierCount == 0) &&
(imageBatch == NULL || imageBatch->barrierCount == 0)) return;
VkPipelineStageFlags srcStages = (bufferBatch != NULL ? bufferBatch->srcStages : 0) | (imageBatch != NULL ? imageBatch->srcStages : 0);
VkPipelineStageFlags dstStages = (bufferBatch != NULL ? bufferBatch->dstStages : 0) | (imageBatch != NULL ? imageBatch->dstStages : 0);
renderer->device->vkCmdPipelineBarrier(VKRenderer_Record(renderer), srcStages, dstStages,
0, 0, NULL,
bufferBatch != NULL ? bufferBatch->barrierCount : 0, bufferBarriers,
imageBatch != NULL ? imageBatch->barrierCount : 0, imageBarriers);
}
/**
* Setup pipeline for drawing. Returns FALSE if surface is not yet ready for drawing.
*/
VkBool32 VKRenderer_Validate(const VKRenderingContext* context, VKShader shader, VkPrimitiveTopology topology) {
assert(context != NULL && context->surface != NULL);
VKSDOps* surface = context->surface;
VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, AlphaType inAlphaType) {
assert(context.surface != NULL);
VKSDOps* surface = context.surface;
// Init render pass.
if (surface->renderPass == NULL || !surface->renderPass->pendingCommands) {
@@ -1093,53 +1315,66 @@ VkBool32 VKRenderer_Validate(const VKRenderingContext* context, VKShader shader,
VKRenderPass* renderPass = surface->renderPass;
// Validate render pass state.
if (renderPass->state.composite != context->composite ||
renderPass->clipModCount != context->clipModCount) {
if (renderPass->state.composite != context.composite ||
renderPass->clipModCount != context.clipModCount) {
// ALPHA_COMPOSITE_DST keeps destination intact, so don't even bother to change the state.
if (context->composite == ALPHA_COMPOSITE_DST) return VK_FALSE;
if (context.composite == ALPHA_COMPOSITE_DST) return VK_FALSE;
// Check whether a logic composite is requested / supported.
if (COMPOSITE_GROUP(context.composite) == LOGIC_COMPOSITE_GROUP &&
(surface->device->caps & sun_java2d_vulkan_VKGPU_CAP_LOGIC_OP_BIT) == 0) {
J2dTraceLn(J2D_TRACE_ERROR, "VKRenderer_Validate: logic composite not supported");
return VK_FALSE;
}
VKCompositeMode oldComposite = renderPass->state.composite;
VkBool32 clipChanged = renderPass->clipModCount != context->clipModCount;
VkBool32 clipChanged = renderPass->clipModCount != context.clipModCount;
// Init stencil attachment, if needed.
if (clipChanged && ARRAY_SIZE(context->clipSpanVertices) > 0 && surface->stencil == NULL) {
if (clipChanged && ARRAY_SIZE(context.clipSpanVertices) > 0 && surface->stencil == NULL) {
if (surface->renderPass->pendingCommands) VKRenderer_FlushRenderPass(surface);
if (!VKSD_ConfigureImageSurfaceStencil(surface)) return VK_FALSE;
}
// Update state.
VKRenderer_FlushDraw(surface);
renderPass->state.composite = context->composite;
renderPass->clipModCount = context->clipModCount;
renderPass->state.composite = context.composite;
renderPass->clipModCount = context.clipModCount;
// Begin render pass.
VkBool32 renderPassJustStarted = !renderPass->pendingCommands;
if (renderPassJustStarted) VKRenderer_BeginRenderPass(surface);
// Validate current clip.
if (clipChanged || renderPassJustStarted) {
J2dTraceLn(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating clip");
surface->device->vkCmdSetScissor(renderPass->commandBuffer, 0, 1, &context->clipRect);
surface->device->vkCmdSetScissor(renderPass->commandBuffer, 0, 1, &context.clipRect);
if (clipChanged) {
if (ARRAY_SIZE(context->clipSpanVertices) > 0) {
VKRenderer_SetupStencil(context);
if (ARRAY_SIZE(context.clipSpanVertices) > 0) {
VKRenderer_SetupStencil(&context);
renderPass->state.stencilMode = STENCIL_MODE_ON;
} else renderPass->state.stencilMode = surface->stencil != NULL ? STENCIL_MODE_OFF : STENCIL_MODE_NONE;
}
}
// Validate current composite.
if (oldComposite != context->composite) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating composite, old=%d, new=%d", oldComposite, context->composite);
if (oldComposite != context.composite) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating composite, old=%d, new=%d", oldComposite, context.composite);
// Reset the pipeline.
renderPass->state.shader = NO_SHADER;
}
}
// Validate current transform.
VKRenderer_ValidateTransform();
// Validate current pipeline.
if (renderPass->state.shader != shader || renderPass->state.topology != topology) {
if (renderPass->state.shader != shader ||
renderPass->state.topology != topology ||
renderPass->state.inAlphaType != inAlphaType) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating pipeline, old=%d, new=%d",
renderPass->state.shader, shader);
VKRenderer_FlushDraw(surface);
VkCommandBuffer cb = renderPass->commandBuffer;
renderPass->state.shader = shader;
renderPass->state.topology = topology;
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
VKPipelines_GetPipeline(renderPass->context, renderPass->state));
renderPass->state.inAlphaType = inAlphaType;
VKPipelineInfo pipelineInfo = VKPipelines_GetPipelineInfo(renderPass->context, renderPass->state);
renderPass->outAlphaType = pipelineInfo.outAlphaType;
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineInfo.pipeline);
renderPass->vertexBufferWriting.bound = VK_FALSE;
renderPass->maskFillBufferWriting.bound = VK_FALSE;
}
@@ -1148,18 +1383,20 @@ VkBool32 VKRenderer_Validate(const VKRenderingContext* context, VKShader shader,
// Drawing operations.
void VKRenderer_RenderRect(const VKRenderingContext* context, VkBool32 fill,
jint x, jint y, jint w, jint h) {
VKRenderer_RenderParallelogram(context, fill, (float) x, (float) y, (float) w, 0, 0, (float) h);
void VKRenderer_RenderRect(VkBool32 fill, jint x, jint y, jint w, jint h) {
VKRenderer_RenderParallelogram(fill, (float) x, (float) y,
(float) w, 0, 0,(float) h);
}
void VKRenderer_RenderParallelogram(const VKRenderingContext* context, VkBool32 fill,
void VKRenderer_RenderParallelogram(VkBool32 fill,
jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12) {
if (!VKRenderer_Validate(context, SHADER_COLOR,
fill ? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST : VK_PRIMITIVE_TOPOLOGY_LINE_LIST)) return; // Not ready.
Color c = context->color;
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);
/* dx21
* (p1)---------(p2) | (p1)------
* |\ \ | | \ dy21
@@ -1176,7 +1413,7 @@ void VKRenderer_RenderParallelogram(const VKRenderingContext* context, VkBool32
VKColorVertex p4 = {x11 + dx12, y11 + dy12, c};
VKColorVertex* vs;
VK_DRAW(vs, context, fill ? 6 : 8);
VK_DRAW(vs, 1, fill ? 6 : 8);
uint32_t i = 0;
vs[i++] = p1;
vs[i++] = p2;
@@ -1190,10 +1427,10 @@ void VKRenderer_RenderParallelogram(const VKRenderingContext* context, VkBool32
vs[i++] = p3;
}
void VKRenderer_FillSpans(const VKRenderingContext* context, jint spanCount, jint *spans) {
void VKRenderer_FillSpans(jint spanCount, jint *spans) {
if (spanCount == 0) return;
if (!VKRenderer_Validate(context, SHADER_COLOR, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)) return; // Not ready.
Color c = context->color;
if (!VKRenderer_Validate(SHADER_COLOR, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, ALPHA_TYPE_UNKNOWN)) return; // Not ready.
RGBA c = VKRenderer_GetRGBA(context.surface, context.renderColor);
jfloat x1 = (float)*(spans++);
jfloat y1 = (float)*(spans++);
@@ -1205,7 +1442,7 @@ void VKRenderer_FillSpans(const VKRenderingContext* context, jint spanCount, jin
VKColorVertex p4 = {x1, y2, c};
VKColorVertex* vs;
VK_DRAW(vs, context, 6);
VK_DRAW(vs, 1, 6);
vs[0] = p1; vs[1] = p2; vs[2] = p3; vs[3] = p3; vs[4] = p4; vs[5] = p1;
for (int i = 1; i < spanCount; i++) {
@@ -1214,61 +1451,19 @@ void VKRenderer_FillSpans(const VKRenderingContext* context, jint spanCount, jin
p2.x = p3.x = (float)*(spans++);
p3.y = p4.y = (float)*(spans++);
VK_DRAW(vs, context, 6);
VK_DRAW(vs, 1, 6);
vs[0] = p1; vs[1] = p2; vs[2] = p3; vs[3] = p3; vs[4] = p4; vs[5] = p1;
}
}
void VKRenderer_TextureRender(const VKRenderingContext* context, VKImage *destImage, VKImage *srcImage,
VkBuffer vertexBuffer, uint32_t vertexNum) {
if (!VKRenderer_Validate(context, SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)) return; // Not ready.
VKSDOps* surface = (VKSDOps*)context->surface;
void VKRenderer_TextureRender(VkDescriptorSet srcDescriptorSet, VkBuffer vertexBuffer, uint32_t vertexNum,
jint filter, VKSamplerWrap wrap) {
// VKRenderer_Validate was called by VKBlitLoops. TODO refactor this.
VKSDOps* surface = (VKSDOps*)context.surface;
VKRenderPass* renderPass = surface->renderPass;
VkCommandBuffer cb = renderPass->commandBuffer;
VKDevice* device = surface->device;
// TODO We create a new descriptor set on each command, we'll implement reusing them later.
VkDescriptorPoolSize poolSize = {
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1
};
VkDescriptorPoolCreateInfo descrPoolInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.poolSizeCount = 1,
.pPoolSizes = &poolSize,
.maxSets = 1
};
VkDescriptorPool descriptorPool;
VK_IF_ERROR(device->vkCreateDescriptorPool(device->handle, &descrPoolInfo, NULL, &descriptorPool)) VK_UNHANDLED_ERROR();
VkDescriptorSetAllocateInfo descrAllocInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &device->renderer->pipelineContext->textureDescriptorSetLayout
};
VkDescriptorSet descriptorSet;
VK_IF_ERROR(device->vkAllocateDescriptorSets(device->handle, &descrAllocInfo, &descriptorSet)) VK_UNHANDLED_ERROR();
VkDescriptorImageInfo imageInfo = {
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = srcImage->view,
.sampler = device->renderer->pipelineContext->linearRepeatSampler
};
VkWriteDescriptorSet descriptorWrites = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptorSet,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.pImageInfo = &imageInfo
};
device->vkUpdateDescriptorSets(device->handle, 1, &descriptorWrites, 0, NULL);
// TODO We flush all pending draws and rebind the vertex buffer with the provided one.
// We will make it work with our unified vertex buffer later.
VKRenderer_FlushDraw(surface);
@@ -1276,14 +1471,17 @@ void VKRenderer_TextureRender(const VKRenderingContext* context, VKImage *destIm
VkBuffer vertexBuffers[] = {vertexBuffer};
VkDeviceSize offsets[] = {0};
device->vkCmdBindVertexBuffers(cb, 0, 1, vertexBuffers, offsets);
VkDescriptorSet descriptorSets[] = { srcDescriptorSet,
VKSamplers_GetDescriptorSet(device, &device->renderer->pipelineContext->samplers, filter, wrap) };
device->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
device->renderer->pipelineContext->texturePipelineLayout, 0, 1, &descriptorSet, 0, NULL);
device->renderer->pipelineContext->texturePipelineLayout, 0, 2, descriptorSets, 0, NULL);
device->vkCmdDraw(cb, vertexNum, 1, 0, 0);
}
void VKRenderer_MaskFill(const VKRenderingContext* context, jint x, jint y, jint w, jint h,
jint maskoff, jint maskscan, jint masklen, uint8_t* mask) {
if (!VKRenderer_Validate(context, SHADER_MASK_FILL_COLOR, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)) return; // Not ready.
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.
// 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/
@@ -1293,7 +1491,7 @@ void VKRenderer_MaskFill(const VKRenderingContext* context, jint x, jint y, jint
maskscan = 0;
byteCount = 1;
}
BufferWritingState maskState = VKRenderer_AllocateMaskFillBytes(context, byteCount);
BufferWritingState maskState = VKRenderer_AllocateMaskFillBytes(byteCount);
if (mask != NULL) {
memcpy(maskState.data, mask + maskoff, byteCount);
} else {
@@ -1302,8 +1500,8 @@ void VKRenderer_MaskFill(const VKRenderingContext* context, jint x, jint y, jint
}
VKMaskFillColorVertex* vs;
VK_DRAW(vs, context, 6);
Color c = context->color;
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};
@@ -1314,4 +1512,56 @@ void VKRenderer_MaskFill(const VKRenderingContext* context, jint x, jint y, jint
vs[3] = p1; vs[4] = p3; vs[5] = p4;
}
#endif /* !HEADLESS */
void VKRenderer_DrawImage(VKImage* image, AlphaType alphaType, VkFormat format,
VKPackedSwizzle swizzle, jint filter, VKSamplerWrap wrap,
float sx1, float sy1, float sx2, float sy2,
float dx1, float dy1, float dx2, float dy2) {
if (!VKRenderer_Validate(SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, alphaType)) return;
VKSDOps* surface = VKRenderer_GetContext()->surface;
VKDevice* device = surface->device;
// Insert image barrier.
{
VkImageMemoryBarrier barrier;
VKBarrierBatch barrierBatch = {};
VKImage_AddBarrier(&barrier, &barrierBatch, image,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_ACCESS_SHADER_READ_BIT,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
VKRenderer_RecordBarriers(device->renderer, NULL, NULL, &barrier, &barrierBatch);
}
// We are going to change descriptor bindings, so flush drawing.
VKRenderer_FlushDraw(surface);
// Bind image & sampler descriptor sets.
VkDescriptorSet descriptorSets[] = {
VKImage_GetDescriptorSet(device, image, format, swizzle),
VKSamplers_GetDescriptorSet(device, &device->renderer->pipelineContext->samplers, filter, wrap)
};
device->vkCmdBindDescriptorSets(surface->renderPass->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
device->renderer->pipelineContext->texturePipelineLayout, 0, 2, descriptorSets, 0, NULL);
// Add vertices.
VKTxVertex* vs;
VK_DRAW(vs, 1, 4);
vs[0] = (VKTxVertex) {dx1, dy1, sx1, sy1};
vs[1] = (VKTxVertex) {dx2, dy1, sx2, sy1};
vs[2] = (VKTxVertex) {dx1, dy2, sx1, sy2};
vs[3] = (VKTxVertex) {dx2, dy2, sx2, sy2};
}
void VKRenderer_AddSurfaceDependency(VKSDOps* src, VKSDOps* dst) {
assert(src != NULL);
assert(dst != NULL);
assert(dst->renderPass != NULL);
// We don't care much about duplicates in our dependency arrays,
// so just make a lazy deduplication attempt by checking the last element.
if (ARRAY_SIZE(src->dependentSurfaces) == 0 || ARRAY_LAST(src->dependentSurfaces) != dst) {
ARRAY_PUSH_BACK(src->dependentSurfaces) = dst;
}
if (ARRAY_SIZE(dst->renderPass->usedSurfaces) == 0 || ARRAY_LAST(dst->renderPass->usedSurfaces) != src) {
ARRAY_PUSH_BACK(dst->renderPass->usedSurfaces) = src;
}
}

View File

@@ -30,11 +30,23 @@
#include "VKTypes.h"
#include "VKPipelines.h"
#define NO_CLIP ((VkRect2D) {{0, 0}, {0x7FFFFFFFU, 0x7FFFFFFFU}})
struct VKRenderingContext {
VKSDOps* surface;
VKTransform transform;
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;
@@ -43,18 +55,19 @@ struct VKRenderingContext {
ARRAY(VKIntVertex) clipSpanVertices;
};
typedef struct {
uint32_t barrierCount;
VkPipelineStageFlags srcStages;
VkPipelineStageFlags dstStages;
} VKBarrierBatch;
typedef void (*VKCleanupHandler)(VKDevice *renderer, void* data);
VKRenderer* VKRenderer_Create(VKDevice* device);
/**
* Setup pipeline for drawing. Returns FALSE if surface is not yet ready for drawing.
*/
VkBool32 VKRenderer_Validate(const VKRenderingContext* context, VKShader shader, VkPrimitiveTopology topology);
VkBool32 VKRenderer_Validate(VKShader shader, VkPrimitiveTopology topology, AlphaType inAlphaType);
/**
* Record draw command, if there are any pending vertices in the vertex buffer
*/
void VKRenderer_FlushDraw(VKSDOps* surface);
/**
* Record commands into primary command buffer (outside of a render pass).
@@ -68,6 +81,19 @@ VkCommandBuffer VKRenderer_Record(VKRenderer* renderer);
void VKRenderer_AddImageBarrier(VkImageMemoryBarrier* barriers, VKBarrierBatch* batch,
VKImage* image, VkPipelineStageFlags stage, VkAccessFlags access, VkImageLayout layout);
void VKRenderer_AddBufferBarrier(VkBufferMemoryBarrier* barriers, VKBarrierBatch* batch,
VKBuffer* buffer, VkPipelineStageFlags stage,
VkAccessFlags access);
/**
* Record barrier batches into the primary command buffer.
*/
void VKRenderer_RecordBarriers(VKRenderer* renderer,
VkBufferMemoryBarrier* bufferBarriers, VKBarrierBatch* bufferBatch,
VkImageMemoryBarrier* imageBarriers, VKBarrierBatch* imageBatch);
void VKRenderer_CreateImageDescriptorSet(VKRenderer* renderer, VkDescriptorPool* descriptorPool, VkDescriptorSet* set);
void VKRenderer_Destroy(VKRenderer* renderer);
/**
@@ -85,6 +111,17 @@ void VKRenderer_Flush(VKRenderer* renderer);
*/
void VKRenderer_DestroyRenderPass(VKSDOps* surface);
/**
* End render pass for the surface and record it into the primary command buffer,
* which will be executed on the next VKRenderer_Flush.
*/
VkBool32 VKRenderer_FlushRenderPass(VKSDOps* surface);
/**
* Register a handler to be called at the cleanup phase of the renderer.
*/
void VKRenderer_DisposeOnCleanup(VKRenderer* renderer, VKCleanupHandler hnd, void* data);
/**
* Flush pending render pass and queue surface for presentation (if applicable).
*/
@@ -94,33 +131,34 @@ void VKRenderer_FlushSurface(VKSDOps* surface);
* Request size for the surface. It has no effect, if it is already of the same size.
* Actual resize will be performed later, before starting a new frame.
*/
void VKRenderer_ConfigureSurface(VKSDOps* surface, VkExtent2D extent);
void VKRenderer_ConfigureSurface(VKSDOps* surface, VkExtent2D extent, VKDevice* device);
/**
* End render pass for the surface and record it into the primary command buffer,
* which will be executed on the next VKRenderer_Flush.
*/
void VKRenderer_FlushRenderPass(VKSDOps* surface);
void VKRenderer_AddSurfaceDependency(VKSDOps* src, VKSDOps* dst);
// Blit operations.
void VKRenderer_TextureRender(const VKRenderingContext* context,
VKImage *destImage, VKImage *srcImage,
VkBuffer vertexBuffer, uint32_t vertexNum);
void VKRenderer_TextureRender(VkDescriptorSet srcDescriptorSet, VkBuffer vertexBuffer, uint32_t vertexNum,
jint filter, VKSamplerWrap wrap);
// Drawing operations.
void VKRenderer_RenderRect(const VKRenderingContext* context, VkBool32 fill,
jint x, jint y, jint w, jint h);
void VKRenderer_RenderRect(VkBool32 fill, jint x, jint y, jint w, jint h);
void VKRenderer_RenderParallelogram(const VKRenderingContext* context, VkBool32 fill,
void VKRenderer_RenderParallelogram(VkBool32 fill,
jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12);
void VKRenderer_FillSpans(const VKRenderingContext* context, jint spanCount, jint *spans);
void VKRenderer_FillSpans(jint spanCount, jint *spans);
void VKRenderer_MaskFill(const VKRenderingContext* context,
jint x, jint y, jint w, jint h, jint maskoff, jint maskscan, jint masklen, uint8_t* mask);
void VKRenderer_MaskFill(jint x, jint y, jint w, jint h,
jint maskoff, jint maskscan, jint masklen, uint8_t *mask);
void VKRenderer_DrawImage(VKImage* image, AlphaType alphaType, VkFormat format,
VKPackedSwizzle swizzle, jint filter, VKSamplerWrap wrap,
float sx1, float sy1, float sx2, float sy2,
float dx1, float dy1, float dx2, float dy2);
VKRenderingContext* VKRenderer_GetContext();
#endif //VKRenderer_h_Included

View File

@@ -0,0 +1,145 @@
// 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.
#include <assert.h>
#include "VKDevice.h"
#include "VKSamplers.h"
VKSamplers VKSamplers_Create(VKDevice* device) {
VKSamplers result = {};
// Create descriptor pool.
uint32_t size = SAMPLER_FILTER_COUNT * SAMPLER_WRAP_COUNT;
VkDescriptorPoolSize poolSize = {
.type = VK_DESCRIPTOR_TYPE_SAMPLER,
.descriptorCount = size
};
VkDescriptorPoolCreateInfo poolInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.poolSizeCount = 1,
.pPoolSizes = &poolSize,
.maxSets = size
};
VK_IF_ERROR(device->vkCreateDescriptorPool(device->handle, &poolInfo, NULL, &result.descriptorPool)) {
return (VKSamplers) {};
}
// Create descriptor set layout.
VkDescriptorSetLayoutBinding descriptorSetLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &descriptorSetLayoutBinding
};
VK_IF_ERROR(device->vkCreateDescriptorSetLayout(device->handle, &descriptorSetLayoutCreateInfo, NULL, &result.descriptorSetLayout)) {
VKSamplers_Destroy(device, result);
return (VKSamplers) {};
}
return result;
}
void VKSamplers_Destroy(VKDevice* device, VKSamplers samplers) {
device->vkDestroyDescriptorPool(device->handle, samplers.descriptorPool, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, samplers.descriptorSetLayout, NULL);
for (jint filter = 0; filter < SAMPLER_FILTER_COUNT; filter++) {
for (VKSamplerWrap wrap = 0; wrap < SAMPLER_WRAP_COUNT; wrap++) {
device->vkDestroySampler(device->handle, samplers.table[filter][wrap].sampler, NULL);
}
}
}
VkDescriptorSet VKSamplers_GetDescriptorSet(VKDevice* device, VKSamplers* samplers, jint filter, VKSamplerWrap wrap) {
assert(device != NULL && samplers != NULL);
assert(filter > 0 && filter <= SAMPLER_FILTER_COUNT);
assert(wrap >= 0 && wrap < SAMPLER_WRAP_COUNT);
if (samplers->table[filter-1][wrap].descriptorSet == VK_NULL_HANDLE) {
// Resolve filtering mode.
VkFilter filterMode;
switch (filter) {
case java_awt_image_AffineTransformOp_TYPE_NEAREST_NEIGHBOR:
filterMode = VK_FILTER_NEAREST;
break;
case java_awt_image_AffineTransformOp_TYPE_BILINEAR:
filterMode = VK_FILTER_LINEAR;
break;
case java_awt_image_AffineTransformOp_TYPE_BICUBIC:
filterMode = VK_FILTER_CUBIC_EXT;
assert(false); // Cubic filter is currently not supported, see VK_EXT_filter_cubic.
break;
default: return VK_NULL_HANDLE;
}
// Resolve address mode.
VkSamplerAddressMode addressMode;
switch (wrap) {
case SAMPLER_WRAP_BORDER:
addressMode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
break;
case SAMPLER_WRAP_REPEAT:
addressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT;
break;
default: return VK_NULL_HANDLE;
}
// Create sampler.
VkSamplerCreateInfo samplerCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = filterMode,
.minFilter = filterMode,
.addressModeU = addressMode,
.addressModeV = addressMode,
.addressModeW = addressMode,
.unnormalizedCoordinates = VK_TRUE
};
VK_IF_ERROR(device->vkCreateSampler(device->handle, &samplerCreateInfo, NULL, &samplers->table[filter-1][wrap].sampler)) {
VK_UNHANDLED_ERROR();
}
// Create descriptor set.
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = samplers->descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &samplers->descriptorSetLayout
};
VK_IF_ERROR(device->vkAllocateDescriptorSets(device->handle, &descriptorSetAllocateInfo,
&samplers->table[filter-1][wrap].descriptorSet)) {
VK_UNHANDLED_ERROR();
}
VkDescriptorImageInfo samplerImageInfo = {
.sampler = samplers->table[filter-1][wrap].sampler
};
VkWriteDescriptorSet descriptorWrites = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = samplers->table[filter-1][wrap].descriptorSet,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
.descriptorCount = 1,
.pImageInfo = &samplerImageInfo
};
device->vkUpdateDescriptorSets(device->handle, 1, &descriptorWrites, 0, NULL);
}
return samplers->table[filter-1][wrap].descriptorSet;
}

View File

@@ -0,0 +1,53 @@
// 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.
#ifndef VKSamplers_h_Included
#define VKSamplers_h_Included
#include "java_awt_image_AffineTransformOp.h"
#include "VKUtil.h"
// Filter types in Java are numbered from 1
// Cubic filter is currently not supported, see VK_EXT_filter_cubic.
#define SAMPLER_FILTER_COUNT java_awt_image_AffineTransformOp_TYPE_BILINEAR
typedef enum {
SAMPLER_WRAP_BORDER = 0,
SAMPLER_WRAP_REPEAT = 1,
SAMPLER_WRAP_COUNT = 2
} VKSamplerWrap;
typedef struct {
VkDescriptorPool descriptorPool;
VkDescriptorSetLayout descriptorSetLayout;
struct {
VkSampler sampler;
VkDescriptorSet descriptorSet;
} table[SAMPLER_FILTER_COUNT][SAMPLER_WRAP_COUNT];
} VKSamplers;
VKSamplers VKSamplers_Create(VKDevice* device);
void VKSamplers_Destroy(VKDevice* device, VKSamplers samplers);
VkDescriptorSet VKSamplers_GetDescriptorSet(VKDevice* device, VKSamplers* samplers, jint filter, VKSamplerWrap wrap);
#endif //VKSamplers_h_Included

View File

@@ -30,13 +30,25 @@
#include "jni.h"
/*
* Class: sun_java2d_vulkan_VKInstance
* Method: init
* Signature: (JZI)Z
* Class: sun_java2d_vulkan_VKEnv
* Method: initPlatform
* Signature: (J)Z
*/
JNIEXPORT jboolean JNICALL
Java_sun_java2d_vulkan_VKInstance_initNative(JNIEnv *env, jclass wlge, jlong nativePtr, jboolean verb, jint requestedDevice) {
Java_sun_java2d_vulkan_VKEnv_initPlatform(JNIEnv *env, jclass wlge, jlong nativePtr) {
return JNI_FALSE;
}
/*
* Class: sun_java2d_vulkan_VKEnv
* Method: initNative
* Signature: (J)[Lsun/java2d/vulkan/VKGPU;
*/
JNIEXPORT jobjectArray JNICALL Java_sun_java2d_vulkan_VKEnv_initNative
(JNIEnv *env, jclass cls, jlong platformData)
{
return NULL;
}
#endif

View File

@@ -24,12 +24,11 @@
* questions.
*/
#ifndef HEADLESS
#include "VKUtil.h"
#include "VKBase.h"
#include "VKRenderer.h"
#include "VKSurfaceData.h"
#include "VKImage.h"
#include "VKEnv.h"
/**
* Release VKSDOps resources & reset to initial state.
@@ -37,8 +36,11 @@
static void VKSD_ResetImageSurface(VKSDOps* vksdo) {
if (vksdo == NULL) return;
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKSD_ResetImageSurface(%p)", vksdo);
// DestroyRenderPass also waits while the surface resources are being used by device.
VKRenderer_DestroyRenderPass(vksdo);
vksdo->lastTimestamp = 0;
if (vksdo->device != NULL) {
VKImage_Destroy(vksdo->device, vksdo->stencil);
@@ -59,8 +61,8 @@ void VKSD_ResetSurface(VKSDOps* vksdo) {
vkwinsdo->vksdOps.device->vkDestroySwapchainKHR(vkwinsdo->vksdOps.device->handle, vkwinsdo->swapchain, NULL);
}
if (vkwinsdo->surface != VK_NULL_HANDLE) {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
if (ge != NULL) ge->vkDestroySurfaceKHR(ge->vkInstance, vkwinsdo->surface, NULL);
VKEnv* vk = VKEnv_GetInstance();
vk->vkDestroySurfaceKHR(vk->instance, vkwinsdo->surface, NULL);
}
vkwinsdo->swapchain = VK_NULL_HANDLE;
vkwinsdo->surface = VK_NULL_HANDLE;
@@ -73,8 +75,9 @@ static void VKSD_FindImageSurfaceMemoryType(VKMemoryRequirements* requirements)
}
VkBool32 VKSD_ConfigureImageSurface(VKSDOps* vksdo) {
// Initialize the device. currentDevice can be changed on the fly, and we must reconfigure surfaces accordingly.
VKDevice* device = VKGE_graphics_environment()->currentDevice;
// Initialize the device. current device can be changed on the fly, and we must reconfigure surfaces accordingly.
VKDevice* device = vksdo->requestedDevice;
if (device == NULL) return VK_FALSE;
if (device != vksdo->device) {
VKSD_ResetImageSurface(vksdo);
vksdo->device = device;
@@ -84,10 +87,9 @@ VkBool32 VKSD_ConfigureImageSurface(VKSDOps* vksdo) {
if (vksdo->requestedExtent.width > 0 && vksdo->requestedExtent.height > 0 && (vksdo->image == NULL ||
vksdo->requestedExtent.width != vksdo->image->extent.width ||
vksdo->requestedExtent.height != vksdo->image->extent.height)) {
// VK_FORMAT_B8G8R8A8_UNORM is the most widely-supported format for our use.
// Currently, we only support *_SRGB and *_UNORM formats,
// as other types may not be trivial to alias for logicOp rendering.
VkFormat format = VK_FORMAT_B8G8R8A8_UNORM;
VkFormat format = (VkFormat) (vksdo->drawableFormat & ~VKSD_FORMAT_OPAQUE_BIT);
VKImage* image = VKImage_Create(device, vksdo->requestedExtent.width, vksdo->requestedExtent.height,
0, format, VK_IMAGE_TILING_OPTIMAL,
@@ -137,12 +139,12 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
return VK_FALSE;
}
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKEnv* vk = VKEnv_GetInstance();
VKDevice* device = vkwinsdo->vksdOps.device;
VkPhysicalDevice physicalDevice = device->physicalDevice;
VkSurfaceCapabilitiesKHR capabilities;
VK_IF_ERROR(ge->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, vkwinsdo->surface, &capabilities)) {
VK_IF_ERROR(vk->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, vkwinsdo->surface, &capabilities)) {
return VK_FALSE;
}
@@ -188,11 +190,11 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
} else compositeAlpha = (VkCompositeAlphaFlagBitsKHR) capabilities.supportedCompositeAlpha;
uint32_t formatCount;
VK_IF_ERROR(ge->vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, vkwinsdo->surface, &formatCount, NULL)) {
VK_IF_ERROR(vk->vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, vkwinsdo->surface, &formatCount, NULL)) {
return VK_FALSE;
}
VkSurfaceFormatKHR formats[formatCount];
VK_IF_ERROR(ge->vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, vkwinsdo->surface, &formatCount, formats)) {
VK_IF_ERROR(vk->vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, vkwinsdo->surface, &formatCount, formats)) {
return VK_FALSE;
}
@@ -203,13 +205,8 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
// We draw with sRGB colors (see VKUtil_DecodeJavaColor()), so we don't want Vulkan to do color space
// conversions when drawing to surface. We use *_UNORM formats, so that colors are written "as is".
// With VK_COLOR_SPACE_SRGB_NONLINEAR_KHR these colors will be interpreted by presentation engine as sRGB.
if (formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR && (
formats[i].format == VK_FORMAT_A8B8G8R8_UNORM_PACK32 ||
formats[i].format == VK_FORMAT_B8G8R8A8_UNORM ||
formats[i].format == VK_FORMAT_R8G8B8A8_UNORM ||
formats[i].format == VK_FORMAT_B8G8R8_UNORM ||
formats[i].format == VK_FORMAT_R8G8B8_UNORM
)) {
if (formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR &&
formats[i].format == vkwinsdo->vksdOps.image->format) {
format = &formats[i];
}
}
@@ -219,11 +216,11 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
}
uint32_t presentModeCount;
VK_IF_ERROR(ge->vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, vkwinsdo->surface, &presentModeCount, NULL)) {
VK_IF_ERROR(vk->vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, vkwinsdo->surface, &presentModeCount, NULL)) {
return VK_FALSE;
}
VkPresentModeKHR presentModes[presentModeCount];
VK_IF_ERROR(ge->vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, vkwinsdo->surface, &presentModeCount, presentModes)) {
VK_IF_ERROR(vk->vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, vkwinsdo->surface, &presentModeCount, presentModes)) {
return VK_FALSE;
}
// FIFO mode is guaranteed to be supported.
@@ -262,7 +259,7 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
.compositeAlpha = compositeAlpha,
.presentMode = presentMode,
.clipped = VK_TRUE,
.oldSwapchain = vkwinsdo->swapchain
.oldSwapchain = vkwinsdo->swapchainDevice == device ? vkwinsdo->swapchain : NULL
};
VK_IF_ERROR(device->vkCreateSwapchainKHR(device->handle, &createInfoKhr, NULL, &swapchain)) {
@@ -275,7 +272,7 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
if (vkwinsdo->swapchain != VK_NULL_HANDLE) {
// Destroy old swapchain.
// TODO is it possible that old swapchain is still being presented, can we destroy it right now?
device->vkDestroySwapchainKHR(device->handle, vkwinsdo->swapchain, NULL);
device->vkDestroySwapchainKHR(vkwinsdo->swapchainDevice->handle, vkwinsdo->swapchain, NULL);
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKSD_ConfigureWindowSurface(%p): old swapchain destroyed", vkwinsdo);
}
vkwinsdo->swapchain = swapchain;
@@ -294,23 +291,61 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
return VK_TRUE;
}
static void VKSD_OnDispose(JNIEnv* env, SurfaceDataOps* ops) {
VKSD_ResetSurface((VKSDOps*) ops);
}
JNIEXPORT VKSDOps* VKSD_CreateSurface(JNIEnv* env, jobject vksd, jint type, jint format, jint backgroundRGB,
VKWinSD_SurfaceResizeCallback resizeCallback) {
VKSDOps* sd = (VKSDOps*)SurfaceData_InitOps(env, vksd,
type == VKSD_WINDOW ? sizeof(VKWinSDOps) : sizeof(VKSDOps));
J2dTraceLn3(J2D_TRACE_INFO,
"VKSD_CreateSurface(%p): type=%d, format=%d", sd, type, format & ~VKSD_FORMAT_OPAQUE_BIT);
if (sd == NULL) {
JNU_ThrowOutOfMemoryError(env, "Initialization of VKSDOps failed");
return NULL;
}
sd->sdOps.Dispose = VKSD_OnDispose;
sd->drawableType = type;
sd->drawableFormat = format;
sd->background = VKUtil_DecodeJavaColor(backgroundRGB, ALPHA_TYPE_STRAIGHT);
sd->lastTimestamp = 0;
if (type == VKSD_WINDOW) {
VKWinSDOps* winSD = (VKWinSDOps*) sd;
winSD->resizeCallback = resizeCallback;
}
VKSD_ResetSurface(sd);
return sd;
}
JNIEXPORT void VKSD_InitWindowSurface(JNIEnv* env, jobject vksd, VKWinSD_SurfaceInitCallback initCallback, void* data) {
VKWinSDOps* sd = (VKWinSDOps*)SurfaceData_GetOps(env, vksd);
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKSD_InitWindowSurface(%p)", sd);
if (sd == NULL) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "VKSD_InitWindowSurface(%p): VKWinSDOps is NULL", vksd);
VK_UNHANDLED_ERROR();
}
if (sd->surface != VK_NULL_HANDLE) {
VKSD_ResetSurface(&sd->vksdOps);
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKSD_InitWindowSurface(%p): surface reset", vksd);
}
initCallback(sd, data);
if (sd->surface != VK_NULL_HANDLE) {
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKSD_InitWindowSurface(%p): surface created", vksd);
}
// Swapchain will be created later after CONFIGURE_SURFACE.
}
/*
* Class: sun_java2d_vulkan_VKOffScreenSurfaceData
* Method: initOps
* Signature: (II)V
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKOffScreenSurfaceData_initOps
(JNIEnv *env, jobject vksd, jint width, jint height) {
VKSDOps * sd = (VKSDOps*)SurfaceData_InitOps(env, vksd, sizeof(VKSDOps));
J2dTraceLn1(J2D_TRACE_VERBOSE, "VKOffScreenSurfaceData_initOps(%p)", sd);
if (sd == NULL) {
JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
return;
}
sd->drawableType = VKSD_RT_TEXTURE;
sd->background = VKUtil_DecodeJavaColor(0);
VKSD_ResetSurface(sd);
VKRenderer_ConfigureSurface(sd, (VkExtent2D){width, height});
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKOffScreenSurfaceData_initOps(JNIEnv* env, jobject vksd, jint format) {
VKSD_CreateSurface(env, vksd, VKSD_RT_TEXTURE, format, 0, NULL);
}
#endif /* !HEADLESS */

View File

@@ -30,17 +30,11 @@
#include "SurfaceData.h"
#include "sun_java2d_pipe_hw_AccelSurface.h"
#include "VKUtil.h"
#include "VKTypes.h"
#include "VKRenderer.h"
/**
* These are shorthand names for the surface type constants defined in
* VKSurfaceData.java.
* TODO which constants?
*/
#define VKSD_UNDEFINED sun_java2d_pipe_hw_AccelSurface_UNDEFINED
#define VKSD_WINDOW sun_java2d_pipe_hw_AccelSurface_WINDOW
#define VKSD_RT_TEXTURE sun_java2d_pipe_hw_AccelSurface_RT_TEXTURE
#define VKSD_FORMAT_OPAQUE_BIT ((jint) 0x80000000)
/**
* The VKSDOps structure describes a native Vulkan surface and contains all
@@ -49,17 +43,23 @@
struct VKSDOps {
SurfaceDataOps sdOps;
jint drawableType;
jint drawableFormat;
VKDevice* device;
VKImage* image;
VKImage* stencil;
Color background;
VkExtent2D requestedExtent;
VKDevice* requestedDevice;
VKRenderPass* renderPass;
ARRAY(VKSDOps*) dependentSurfaces; // Whose pending render passes read from the surface.
uint64_t lastTimestamp; // When was this surface last used?
};
typedef void (*VKWinSD_SurfaceResizeCallback)(VKWinSDOps* surface, VkExtent2D extent);
typedef void (*VKWinSD_SurfaceInitCallback)(VKWinSDOps* surface, void* data);
/**
* The VKWinSDOps structure describes a native Vulkan surface connected with a window.
@@ -74,10 +74,26 @@ struct VKWinSDOps {
VKWinSD_SurfaceResizeCallback resizeCallback;
};
/**
* Create a new surface with given parameters.
* This is intended to be called via JNI and provided with a valid VKSurfaceData object handle.
* Use SurfaceData_DisposeOps to destroy the surface.
*/
JNIEXPORT VKSDOps* VKSD_CreateSurface(JNIEnv* env, jobject vksd, jint drawableType, jint format, jint backgroundRGB,
VKWinSD_SurfaceResizeCallback resizeCallback);
/**
* [Re]initialize window surface with a given platform-specific initialization callback.
* This is intended to be called via JNI and provided with a valid VKSurfaceData object handle.
*/
JNIEXPORT void VKSD_InitWindowSurface(JNIEnv *env, jobject vksd, VKWinSD_SurfaceInitCallback initCallback, void* data);
inline VkBool32 VKSD_IsOpaque(VKSDOps* vksdo) {
return vksdo->drawableFormat & VKSD_FORMAT_OPAQUE_BIT ? VK_TRUE : VK_FALSE;
}
/**
* Release all resources of the surface, resetting it to initial state.
* This function must also be used to initialize newly allocated surfaces.
* VKSDOps.drawableType must be properly set before calling this function.
*/
void VKSD_ResetSurface(VKSDOps* vksdo);

View File

@@ -124,7 +124,7 @@ void VKTexturePoolHandle_ReleaseTexture(VKTexturePoolHandle *handle) {
ATexturePoolHandle_ReleaseTexture(handle);
}
ATexturePrivPtr* VKTexturePoolHandle_GetTexture(VKTexturePoolHandle *handle) {
VKImage* VKTexturePoolHandle_GetTexture(VKTexturePoolHandle *handle) {
return ATexturePoolHandle_GetTexture(handle);
}

View File

@@ -29,6 +29,7 @@
#include "AccelTexturePool.h"
#include "jni.h"
#include "VKTypes.h"
/* VKTexturePoolHandle API */
@@ -36,7 +37,7 @@ typedef ATexturePoolHandle VKTexturePoolHandle;
void VKTexturePoolHandle_ReleaseTexture(VKTexturePoolHandle *handle);
ATexturePrivPtr* VKTexturePoolHandle_GetTexture(VKTexturePoolHandle *handle);
VKImage* VKTexturePoolHandle_GetTexture(VKTexturePoolHandle *handle);
jint VKTexturePoolHandle_GetRequestedWidth(VKTexturePoolHandle *handle);
jint VKTexturePoolHandle_GetRequestedHeight(VKTexturePoolHandle *handle);

View File

@@ -23,18 +23,37 @@
#ifndef VKTypes_h_Included
#define VKTypes_h_Included
#define VK_NO_PROTOTYPES
#include <vulkan/vulkan.h>
typedef enum {
ALPHA_TYPE_PRE_MULTIPLIED = 0,
ALPHA_TYPE_STRAIGHT = 1,
ALPHA_TYPE_UNKNOWN = ALPHA_TYPE_PRE_MULTIPLIED // Fallback to pre-multiplied.
} AlphaType;
/**
* Floating-point RGBA color with sRGB encoding and pre-multiplied alpha.
* Floating-point RGBA color in unspecified color space and alpha type.
*/
typedef union {
struct {
float r, g, b, a;
};
VkClearValue vkClearValue;
} RGBA;
/**
* Floating-point encoding-agnostic color.
*/
typedef struct {
RGBA values[ALPHA_TYPE_STRAIGHT+1];
} Color;
/**
* VkComponentMapping packed into 12 bits.
*/
typedef uint16_t VKPackedSwizzle;
/**
* Transform matrix
* [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ]
@@ -42,13 +61,13 @@ typedef union {
* [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
*/
typedef struct {
double m00, m01, m02;
double m10, m11, m12;
float m00, m01, m02;
float m10 __attribute__((aligned(16))), m11, m12;
} VKTransform;
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VKMemory);
typedef struct VKGraphicsEnvironment VKGraphicsEnvironment;
typedef struct VKEnv VKEnv;
typedef struct VKDevice VKDevice;
typedef struct VKAllocator VKAllocator;
typedef struct VKRenderer VKRenderer;
@@ -62,6 +81,6 @@ typedef struct VKImage VKImage;
typedef struct VKSDOps VKSDOps;
typedef struct VKWinSDOps VKWinSDOps;
typedef char* pchar;
typedef const char* pchar;
#endif //VKTypes_h_Included

View File

@@ -22,10 +22,20 @@
// questions.
#include <assert.h>
#include <math.h>
#include "VKUtil.h"
Color VKUtil_DecodeJavaColor(uint32_t color) {
assert(sizeof(Color) == sizeof(float) * 4);
static RGBA VKUtil_ConvertAlphaType(RGBA rgba, AlphaType newAlphaType) {
float mul = rgba.a;
if (newAlphaType == ALPHA_TYPE_STRAIGHT && mul != 0.0f) mul = 1.0f / mul;
rgba.r *= mul;
rgba.g *= mul;
rgba.b *= mul;
return rgba;
}
Color VKUtil_DecodeJavaColor(uint32_t color, AlphaType alphaType) {
assert(sizeof(RGBA) == sizeof(float) * 4);
// Just map [0, 255] integer colors onto [0, 1] floating-point range, it remains in sRGB color space.
// sRGB gamma correction remains unsupported.
static const float NormTable256[256] = {
@@ -34,17 +44,25 @@ Color VKUtil_DecodeJavaColor(uint32_t color) {
#define NORM64(N) NORM8(N),NORM8(N+8),NORM8(N+16),NORM8(N+24),NORM8(N+32),NORM8(N+40),NORM8(N+48),NORM8(N+56)
NORM64(0),NORM64(64),NORM64(128),NORM64(192)
};
Color srgb = {
.r = NormTable256[(color >> 16) & 0xFF],
.g = NormTable256[(color >> 8) & 0xFF],
.b = NormTable256[ color & 0xFF],
.a = NormTable256[(color >> 24) & 0xFF]
// Decode color in its original type.
static const RGBA NAN_RGBA = {.r = NAN, .g = NAN, .b = NAN, .a = NAN};
Color result = {{ NAN_RGBA, NAN_RGBA }};
result.values[alphaType] = (RGBA) {
.r = NormTable256[(color >> 16) & 0xFF],
.g = NormTable256[(color >> 8) & 0xFF],
.b = NormTable256[ color & 0xFF],
.a = NormTable256[(color >> 24) & 0xFF]
};
// Convert to pre-multiplied alpha.
srgb.r *= srgb.a;
srgb.g *= srgb.a;
srgb.b *= srgb.a;
return srgb;
return result;
}
RGBA VKUtil_GetRGBA(Color color, AlphaType alphaType) {
if (isnan(color.values[alphaType].a)) {
AlphaType otherAlphaType = alphaType ^ 1;
assert(!isnan(color.values[otherAlphaType].a));
color.values[alphaType] = VKUtil_ConvertAlphaType(color.values[otherAlphaType], alphaType);
}
return color.values[alphaType];
}
uint32_t VKUtil_Log2(uint64_t i) {
@@ -55,27 +73,28 @@ uint32_t VKUtil_Log2(uint64_t i) {
LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6),
LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) };
register uint32_t t;
if (t = i >> 56) return 56 + LogTable256[t];
else if (t = i >> 48) return 48 + LogTable256[t];
else if (t = i >> 40) return 40 + LogTable256[t];
else if (t = i >> 32) return 32 + LogTable256[t];
else if (t = i >> 24) return 24 + LogTable256[t];
else if (t = i >> 16) return 16 + LogTable256[t];
else if (t = i >> 8) return 8 + LogTable256[t];
else return LogTable256[i & 0xFF];
if ((t = i >> 56)) return 56 + LogTable256[t];
if ((t = i >> 48)) return 48 + LogTable256[t];
if ((t = i >> 40)) return 40 + LogTable256[t];
if ((t = i >> 32)) return 32 + LogTable256[t];
if ((t = i >> 24)) return 24 + LogTable256[t];
if ((t = i >> 16)) return 16 + LogTable256[t];
if ((t = i >> 8)) return 8 + LogTable256[t];
return LogTable256[i & 0xFF];
}
FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
#define GROUP(SIZE, ...) return ((FormatGroup) { .bytes = SIZE, .aliases[FORMAT_ALIAS_ORIGINAL] = format, __VA_ARGS__ })
#define GROUP(ASPECT, SIZE, ...) return ((FormatGroup) { \
.bytes = SIZE, .aspect = VK_IMAGE_ASPECT_ ## ASPECT ## _BIT, .aliases[FORMAT_ALIAS_ORIGINAL] = format, __VA_ARGS__ })
switch (format) {
case VK_FORMAT_R4G4_UNORM_PACK8: GROUP(1, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R4G4_UNORM_PACK8);
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R4G4B4A4_UNORM_PACK16);
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B4G4R4A4_UNORM_PACK16);
case VK_FORMAT_R5G6B5_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R5G6B5_UNORM_PACK16);
case VK_FORMAT_B5G6R5_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B5G6R5_UNORM_PACK16);
case VK_FORMAT_R5G5B5A1_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R5G5B5A1_UNORM_PACK16);
case VK_FORMAT_B5G5R5A1_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B5G5R5A1_UNORM_PACK16);
case VK_FORMAT_A1R5G5B5_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_A1R5G5B5_UNORM_PACK16);
case VK_FORMAT_R4G4_UNORM_PACK8: GROUP(COLOR, 1, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R4G4_UNORM_PACK8);
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: GROUP(COLOR, 2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R4G4B4A4_UNORM_PACK16);
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: GROUP(COLOR, 2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B4G4R4A4_UNORM_PACK16);
case VK_FORMAT_R5G6B5_UNORM_PACK16: GROUP(COLOR, 2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R5G6B5_UNORM_PACK16);
case VK_FORMAT_B5G6R5_UNORM_PACK16: GROUP(COLOR, 2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B5G6R5_UNORM_PACK16);
case VK_FORMAT_R5G5B5A1_UNORM_PACK16: GROUP(COLOR, 2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R5G5B5A1_UNORM_PACK16);
case VK_FORMAT_B5G5R5A1_UNORM_PACK16: GROUP(COLOR, 2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B5G5R5A1_UNORM_PACK16);
case VK_FORMAT_A1R5G5B5_UNORM_PACK16: GROUP(COLOR, 2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_A1R5G5B5_UNORM_PACK16);
case VK_FORMAT_R8_UNORM:
case VK_FORMAT_R8_SNORM:
case VK_FORMAT_R8_USCALED:
@@ -83,7 +102,7 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_R8_UINT:
case VK_FORMAT_R8_SINT:
case VK_FORMAT_R8_SRGB:
GROUP(1, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_R8_SRGB,
GROUP(COLOR, 1, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_R8_SRGB,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R8_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R8_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R8_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R8_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R8_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R8_SINT);
@@ -94,7 +113,7 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_R8G8_UINT:
case VK_FORMAT_R8G8_SINT:
case VK_FORMAT_R8G8_SRGB:
GROUP(2, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_R8G8_SRGB,
GROUP(COLOR, 2, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_R8G8_SRGB,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R8G8_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R8G8_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R8G8_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R8G8_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R8G8_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R8G8_SINT);
@@ -105,7 +124,7 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_R8G8B8_UINT:
case VK_FORMAT_R8G8B8_SINT:
case VK_FORMAT_R8G8B8_SRGB:
GROUP(3, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_R8G8B8_SRGB,
GROUP(COLOR, 3, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_R8G8B8_SRGB,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R8G8B8_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R8G8B8_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R8G8B8_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R8G8B8_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R8G8B8_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R8G8B8_SINT);
@@ -116,7 +135,7 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_B8G8R8_UINT:
case VK_FORMAT_B8G8R8_SINT:
case VK_FORMAT_B8G8R8_SRGB:
GROUP(3, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_B8G8R8_SRGB,
GROUP(COLOR, 3, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_B8G8R8_SRGB,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B8G8R8_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_B8G8R8_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_B8G8R8_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_B8G8R8_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_B8G8R8_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_B8G8R8_SINT);
@@ -127,7 +146,7 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_R8G8B8A8_UINT:
case VK_FORMAT_R8G8B8A8_SINT:
case VK_FORMAT_R8G8B8A8_SRGB:
GROUP(4, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_R8G8B8A8_SRGB,
GROUP(COLOR, 4, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_R8G8B8A8_SRGB,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R8G8B8A8_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R8G8B8A8_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R8G8B8A8_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R8G8B8A8_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R8G8B8A8_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R8G8B8A8_SINT);
@@ -138,7 +157,7 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_B8G8R8A8_UINT:
case VK_FORMAT_B8G8R8A8_SINT:
case VK_FORMAT_B8G8R8A8_SRGB:
GROUP(4, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_B8G8R8A8_SRGB,
GROUP(COLOR, 4, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_B8G8R8A8_SRGB,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_B8G8R8A8_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_B8G8R8A8_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_B8G8R8A8_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_B8G8R8A8_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_B8G8R8A8_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_B8G8R8A8_SINT);
@@ -149,7 +168,7 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_A8B8G8R8_UINT_PACK32:
case VK_FORMAT_A8B8G8R8_SINT_PACK32:
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
GROUP(4, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_A8B8G8R8_SRGB_PACK32,
GROUP(COLOR, 4, .aliases[FORMAT_ALIAS_SRGB] = VK_FORMAT_A8B8G8R8_SRGB_PACK32,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_A8B8G8R8_UNORM_PACK32, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_A8B8G8R8_SNORM_PACK32,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_A8B8G8R8_USCALED_PACK32, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_A8B8G8R8_SSCALED_PACK32,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_A8B8G8R8_UINT_PACK32, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_A8B8G8R8_SINT_PACK32);
@@ -159,7 +178,7 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_A2R10G10B10_SSCALED_PACK32:
case VK_FORMAT_A2R10G10B10_UINT_PACK32:
case VK_FORMAT_A2R10G10B10_SINT_PACK32:
GROUP(4,
GROUP(COLOR, 4,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_A2R10G10B10_UNORM_PACK32, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_A2R10G10B10_SNORM_PACK32,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_A2R10G10B10_USCALED_PACK32, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_A2R10G10B10_SSCALED_PACK32,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_A2R10G10B10_UINT_PACK32, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_A2R10G10B10_SINT_PACK32);
@@ -169,7 +188,7 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_A2B10G10R10_SSCALED_PACK32:
case VK_FORMAT_A2B10G10R10_UINT_PACK32:
case VK_FORMAT_A2B10G10R10_SINT_PACK32:
GROUP(4,
GROUP(COLOR, 4,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_A2B10G10R10_UNORM_PACK32, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_A2B10G10R10_SNORM_PACK32,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_A2B10G10R10_USCALED_PACK32, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_A2B10G10R10_SSCALED_PACK32,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_A2B10G10R10_UINT_PACK32, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_A2B10G10R10_SINT_PACK32);
@@ -180,7 +199,7 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_R16_UINT:
case VK_FORMAT_R16_SINT:
case VK_FORMAT_R16_SFLOAT:
GROUP(2, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R16_SFLOAT,
GROUP(COLOR, 2, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R16_SFLOAT,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R16_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R16_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R16_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R16_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R16_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R16_SINT);
@@ -191,7 +210,7 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_R16G16_UINT:
case VK_FORMAT_R16G16_SINT:
case VK_FORMAT_R16G16_SFLOAT:
GROUP(4, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R16G16_SFLOAT,
GROUP(COLOR, 4, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R16G16_SFLOAT,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R16G16_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R16G16_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R16G16_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R16G16_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R16G16_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R16G16_SINT);
@@ -202,7 +221,7 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_R16G16B16_UINT:
case VK_FORMAT_R16G16B16_SINT:
case VK_FORMAT_R16G16B16_SFLOAT:
GROUP(6, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R16G16B16_SFLOAT,
GROUP(COLOR, 6, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R16G16B16_SFLOAT,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R16G16B16_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R16G16B16_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R16G16B16_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R16G16B16_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R16G16B16_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R16G16B16_SINT);
@@ -213,62 +232,66 @@ FormatGroup VKUtil_GetFormatGroup(VkFormat format) {
case VK_FORMAT_R16G16B16A16_UINT:
case VK_FORMAT_R16G16B16A16_SINT:
case VK_FORMAT_R16G16B16A16_SFLOAT:
GROUP(8, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R16G16B16A16_SFLOAT,
GROUP(COLOR, 8, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R16G16B16A16_SFLOAT,
.aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R16G16B16A16_UNORM, .aliases[FORMAT_ALIAS_SNORM] = VK_FORMAT_R16G16B16A16_SNORM,
.aliases[FORMAT_ALIAS_USCALED] = VK_FORMAT_R16G16B16A16_USCALED, .aliases[FORMAT_ALIAS_SSCALED] = VK_FORMAT_R16G16B16A16_SSCALED,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R16G16B16A16_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R16G16B16A16_SINT);
case VK_FORMAT_R32_UINT:
case VK_FORMAT_R32_SINT:
case VK_FORMAT_R32_SFLOAT:
GROUP(4, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R32_SFLOAT,
GROUP(COLOR, 4, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R32_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R32_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R32_SINT);
case VK_FORMAT_R32G32_UINT:
case VK_FORMAT_R32G32_SINT:
case VK_FORMAT_R32G32_SFLOAT:
GROUP(8, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R32G32_SFLOAT,
GROUP(COLOR, 8, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R32G32_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R32G32_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R32G32_SINT);
case VK_FORMAT_R32G32B32_UINT:
case VK_FORMAT_R32G32B32_SINT:
case VK_FORMAT_R32G32B32_SFLOAT:
GROUP(12, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R32G32B32_SFLOAT,
GROUP(COLOR, 12, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R32G32B32_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R32G32B32_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R32G32B32_SINT);
case VK_FORMAT_R32G32B32A32_UINT:
case VK_FORMAT_R32G32B32A32_SINT:
case VK_FORMAT_R32G32B32A32_SFLOAT:
GROUP(16, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R32G32B32A32_SFLOAT,
GROUP(COLOR, 16, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R32G32B32A32_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R32G32B32A32_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R32G32B32A32_SINT);
case VK_FORMAT_R64_UINT:
case VK_FORMAT_R64_SINT:
case VK_FORMAT_R64_SFLOAT:
GROUP(8, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R64_SFLOAT,
GROUP(COLOR, 8, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R64_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R64_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R64_SINT);
case VK_FORMAT_R64G64_UINT:
case VK_FORMAT_R64G64_SINT:
case VK_FORMAT_R64G64_SFLOAT:
GROUP(16, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R64G64_SFLOAT,
GROUP(COLOR, 16, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R64G64_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R64G64_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R64G64_SINT);
case VK_FORMAT_R64G64B64_UINT:
case VK_FORMAT_R64G64B64_SINT:
case VK_FORMAT_R64G64B64_SFLOAT:
GROUP(24, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R64G64B64_SFLOAT,
GROUP(COLOR, 24, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R64G64B64_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R64G64B64_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R64G64B64_SINT);
case VK_FORMAT_R64G64B64A64_UINT:
case VK_FORMAT_R64G64B64A64_SINT:
case VK_FORMAT_R64G64B64A64_SFLOAT:
GROUP(32, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R64G64B64A64_SFLOAT,
GROUP(COLOR, 32, .aliases[FORMAT_ALIAS_SFLOAT] = VK_FORMAT_R64G64B64A64_SFLOAT,
.aliases[FORMAT_ALIAS_UINT] = VK_FORMAT_R64G64B64A64_UINT, .aliases[FORMAT_ALIAS_SINT] = VK_FORMAT_R64G64B64A64_SINT);
case VK_FORMAT_R10X6_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R10X6_UNORM_PACK16);
case VK_FORMAT_R10X6G10X6_UNORM_2PACK16: GROUP(4, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R10X6G10X6_UNORM_2PACK16);
case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: GROUP(8, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16);
case VK_FORMAT_R12X4_UNORM_PACK16: GROUP(2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R12X4_UNORM_PACK16);
case VK_FORMAT_R12X4G12X4_UNORM_2PACK16: GROUP(4, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R12X4G12X4_UNORM_2PACK16);
case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16: GROUP(8, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16);
default: GROUP(0);
case VK_FORMAT_R10X6_UNORM_PACK16: GROUP(COLOR, 2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R10X6_UNORM_PACK16);
case VK_FORMAT_R10X6G10X6_UNORM_2PACK16: GROUP(COLOR, 4, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R10X6G10X6_UNORM_2PACK16);
case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: GROUP(COLOR, 8, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16);
case VK_FORMAT_R12X4_UNORM_PACK16: GROUP(COLOR, 2, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R12X4_UNORM_PACK16);
case VK_FORMAT_R12X4G12X4_UNORM_2PACK16: GROUP(COLOR, 4, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R12X4G12X4_UNORM_2PACK16);
case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16: GROUP(COLOR, 8, .aliases[FORMAT_ALIAS_UNORM] = VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16);
case VK_FORMAT_S8_UINT: GROUP(STENCIL, 1);
case VK_FORMAT_D16_UNORM_S8_UINT: GROUP(STENCIL, 3);
case VK_FORMAT_D24_UNORM_S8_UINT: GROUP(STENCIL, 4);
case VK_FORMAT_D32_SFLOAT_S8_UINT: GROUP(STENCIL, 5);
default: return (FormatGroup) { .bytes = 0, .aspect = (VkImageAspectFlagBits) 0, .aliases[FORMAT_ALIAS_ORIGINAL] = format };
}
#undef GROUP
}
void VKUtil_LogResultError(const char* string, VkResult result) {
JNIEXPORT void VKUtil_LogResultError(const char* string, VkResult result) {
const char* r;
switch (result) {
#define RESULT(T) case T: r = #T; break
@@ -311,3 +334,24 @@ void VKUtil_LogResultError(const char* string, VkResult result) {
}
J2dRlsTraceLn1(J2D_TRACE_ERROR, string, r)
}
/**
* Concatenate src transform to dst
* [d00 d01 d02] [s00 s01 s02] [d00s00+d01s10 d00s01+d01s11 d00s02+d01s12+d02]
* [d10 d11 d12] [s10 s11 s12] = [d10s11+d11s10 d10s01+d11s11 d10s02+d11s12+d12]
* [ 0 0 1 ] [ 0 0 1 ] [ 0 0 1 ]
*/
void VKUtil_ConcatenateTransform(VKTransform* dst, const VKTransform* src) {
float s00 = src->m00, s01 = src->m01, s02 = src->m02;
float s10 = src->m10, s11 = src->m11, s12 = src->m12;
float d00 = dst->m00, d01 = dst->m01, d02 = dst->m02;
float d10 = dst->m10, d11 = dst->m11, d12 = dst->m12;
dst->m00 = d00 * s00 + d01 * s10;
dst->m01 = d00 * s01 + d01 * s11;
dst->m02 = d00 * s02 + d01 * s12 + d02;
dst->m10 = d10 * s00 + d11 * s10;
dst->m11 = d10 * s01 + d11 * s11;
dst->m12 = d10 * s02 + d11 * s12 + d12;
}

View File

@@ -24,7 +24,6 @@
#ifndef VKUtil_h_Included
#define VKUtil_h_Included
#include <stdlib.h>
#include <vulkan/vulkan.h>
#include <Trace.h>
#include "awt.h"
#include "jni_util.h"
@@ -43,7 +42,7 @@
#endif
// Useful logging & result checking macros
void VKUtil_LogResultError(const char* string, VkResult result);
JNIEXPORT void VKUtil_LogResultError(const char* string, VkResult result);
inline VkBool32 VKUtil_CheckError(VkResult result, const char* errorMessage) {
if (result != VK_SUCCESS) {
VKUtil_LogResultError(errorMessage, result);
@@ -69,6 +68,19 @@ inline VkBool32 VKUtil_CheckError(VkResult result, const char* errorMessage) {
#define C_ARRAY_UTIL_ALLOCATION_FAILED() VK_FATAL_ERROR("CArrayUtil allocation failed")
#include "CArrayUtil.h"
#define VK_ID_TRANSFORM ((VKTransform)\
{1.0f, 0.0f, 0.0f, \
0.0f, 1.0f, 0.0f})
// 0.0f, 0.0f, 1.0f -- omitted values
#define VK_IS_NEQ_TRANSFORM(PA, PB) \
((PA)->m00 != (PB)->m00 || \
(PA)->m01 != (PB)->m01 || \
(PA)->m02 != (PB)->m02 || \
(PA)->m10 != (PB)->m10 || \
(PA)->m11 != (PB)->m11 || \
(PA)->m12 != (PB)->m12 )
typedef enum {
FORMAT_ALIAS_ORIGINAL = 0,
FORMAT_ALIAS_UNORM = 1,
@@ -82,14 +94,27 @@ typedef enum {
FORMAT_ALIAS_COUNT = 9
} FormatAlias;
#define VK_PACK_SWIZZLE(R, G, B, A) ((VKPackedSwizzle)(R) & 0b111) | (((VKPackedSwizzle)(G) & 0b111) << 3) | \
(((VKPackedSwizzle)(B) & 0b111) << 6) | (((VKPackedSwizzle)(A) & 0b111) << 9)
#define VK_UNPACK_SWIZZLE(S) ((VkComponentMapping){ (VKPackedSwizzle)(S) & 0b111, ((VKPackedSwizzle)(S) >> 3) & 0b111, \
((VKPackedSwizzle)(S) >> 6) & 0b111, ((VKPackedSwizzle)(S) >> 9) & 0b111 })
/**
* Group of format aliases. Use FormatAlias enum values to index into FormatGroup.aliases.
*/
typedef struct {
VkFormat aliases[FORMAT_ALIAS_COUNT];
uint bytes;
VkFormat aliases[FORMAT_ALIAS_COUNT];
uint bytes;
VkImageAspectFlagBits aspect;
} FormatGroup;
typedef struct {
uint32_t barrierCount;
VkPipelineStageFlags srcStages;
VkPipelineStageFlags dstStages;
} VKBarrierBatch;
/**
* Vulkan expects linear colors.
* However Java2D expects legacy behavior, as if colors were blended in sRGB color space.
@@ -101,7 +126,12 @@ typedef struct {
* Note: we receive colors from Java with straight (non-premultiplied) alpha, which is done to prevent precision loss.
* This is controlled by PixelConverter parameter of SurfaceType, see VKSurfaceData.java.
*/
Color VKUtil_DecodeJavaColor(uint32_t color);
Color VKUtil_DecodeJavaColor(uint32_t color, AlphaType alphaType);
/**
* Get floating-point RGBA color components with specified AlphaType.
*/
RGBA VKUtil_GetRGBA(Color color, AlphaType alphaType);
/**
* Integer log2, the same as index of highest set bit.
@@ -113,6 +143,11 @@ uint32_t VKUtil_Log2(uint64_t i);
*/
FormatGroup VKUtil_GetFormatGroup(VkFormat format);
/**
* Apply src transform to dst
*/
void VKUtil_ConcatenateTransform(VKTransform* dst, const VKTransform* src);
/*
* The following macros allow the caller to return (or continue) if the
* provided value is NULL. (The strange else clause is included below to

View File

@@ -24,101 +24,79 @@
*/
package sun.awt.wl;
import jdk.internal.misc.InnocuousThread;
import sun.awt.datatransfer.DataTransferer;
import sun.awt.datatransfer.SunClipboard;
import sun.util.logging.PlatformLogger;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.FlavorTable;
import java.awt.datatransfer.Transferable;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.SortedMap;
public final class WLClipboard extends SunClipboard {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLClipboard");
public static final int INITIAL_MIME_FORMATS_COUNT = 10;
private static final int DEFAULT_BUFFER_SIZE = 4096;
private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
private static final String plainTextUtf8 = "text/plain;charset=utf-8";
private static final String utf8String = "UTF8_STRING";
// A native handle of a Wayland queue dedicated to handling
// data offer-related events
private static final long dataOfferQueuePtr;
private final long ID;
private final WLDataDevice dataDevice;
// true if this is the "primary selection" clipboard,
// false otherwise (the regular clipboard).
private final boolean isPrimary; // used by native
private final boolean isPrimary;
private final Object dataLock = new Object();
// A handle to the native clipboard representation, 0 if not available.
private long clipboardNativePtr; // guarded by dataLock
private long ourOfferNativePtr; // guarded by dataLock
// The list of numeric format IDs the current clipboard is available in;
// could be null or empty.
private List<Long> clipboardFormats; // guarded by dataLock
// Latest active data offer to us, containing up-to-date clipboard content,
// as provided by the Wayland compositor.
// If null, there's no clipboard data available.
// Guarded by dataLock.
private WLDataOffer clipboardDataOfferedToUs;
// The "current" list formats for the new clipboard contents that is about
// to be received from Wayland. Could be empty, but never null.
private List<Long> newClipboardFormats = new ArrayList<>(INITIAL_MIME_FORMATS_COUNT); // guarded by dataLock
// Clipboard data source we are providing to the Wayland compositor.
// If null, we are not offering any clipboard data at the moment.
// Guarded by dataLock.
private WLDataSource ourDataSource;
private static final Thread clipboardDispatcherThread;
static {
initIDs();
dataOfferQueuePtr = createDataOfferQueue();
flavorTable = DataTransferer.adaptFlavorMap(getDefaultFlavorTable());
Thread t = InnocuousThread.newThread(
"AWT-Wayland-clipboard-dispatcher",
WLClipboard::dispatchDataOfferQueue);
t.setDaemon(true);
t.start();
clipboardDispatcherThread = t;
}
private final static FlavorTable flavorTable;
public WLClipboard(String name, boolean isPrimary) {
public WLClipboard(WLDataDevice dataDevice, String name, boolean isPrimary) {
super(name);
this.ID = initNative(isPrimary);
this.isPrimary = isPrimary;
this.dataDevice = dataDevice;
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: Created " + this);
}
}
private static void dispatchDataOfferQueue() {
dispatchDataOfferQueueImpl(dataOfferQueuePtr); // does not return until error or server disconnect
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: data offer dispatcher exited");
private int getProtocol() {
if (isPrimary) {
return WLDataDevice.DATA_TRANSFER_PROTOCOL_PRIMARY_SELECTION;
} else {
return WLDataDevice.DATA_TRANSFER_PROTOCOL_WAYLAND;
}
}
@Override
public String toString() {
return String.format("Clipboard: Wayland %s (%x)", (isPrimary ? "selection clipboard" : "clipboard"), ID);
return String.format("Clipboard: Wayland %s", (isPrimary ? "selection clipboard" : "clipboard"));
}
@Override
public long getID() {
return ID;
return isPrimary ? 2 : 1;
}
/**
* Called when we loose ownership of the clipboard.
* Called when we lose ownership of the clipboard.
*/
@Override
protected void clearNativeContext() {
@@ -164,34 +142,20 @@ public final class WLClipboard extends SunClipboard {
notifyOfNewFormats(formats);
if (formats.length > 0) {
String[] mime = new String[formats.length];
for (int i = 0; i < formats.length; i++) {
mime[i] = wlDataTransferer.getNativeForFormat(formats[i]);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: formats mapping " + formats[i] + " -> " + mime[i]);
}
}
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: Offering new contents (" + contents + ") in these MIME formats: " + Arrays.toString(mime));
log.fine("Clipboard: Offering new contents (" + contents + ")");
}
WLDataSource newOffer = null;
newOffer = new WLDataSource(dataDevice, getProtocol(), contents);
synchronized (dataLock) {
if (ourOfferNativePtr != 0) {
cancelOffer(ourOfferNativePtr);
ourOfferNativePtr = 0;
if (ourDataSource != null) {
ourDataSource.destroy();
}
ourDataSource = newOffer;
dataDevice.setSelection(getProtocol(), newOffer, eventSerial);
}
long newOfferPtr = offerData(eventSerial, mime, contents, dataOfferQueuePtr);
synchronized (dataLock) {
ourOfferNativePtr = newOfferPtr;
}
// Once we have offered the data, someone may come back and ask to provide them.
// In that event, the transferContentsWithType() will be called from native on the
// clipboard dispatch thread.
// A reference to contents is retained until we are notified of the new contents
// by the Wayland server.
}
} else {
this.owner = null;
@@ -200,74 +164,32 @@ public final class WLClipboard extends SunClipboard {
}
/**
* Called from native on EDT when a client has asked to provide the actual data for
* the clipboard that we own in the given format to the given file.
* NB: that client could be us, but we aren't necessarily aware of that once we
* lost keyboard focus at least once after Ctrl-C.
* Returns zero-length array (not null) if the number of available formats is zero.
*
* @param contents a reference to the clipboard's contents to be transferred
* @param mime transfer the contents in this MIME format
* @param destFD transfer the contents to this file descriptor and close it afterward
*
* @throws IOException in case writing to the given file descriptor failed
*/
private void transferContentsWithType(Transferable contents, String mime, int destFD) throws IOException {
assert (Thread.currentThread() == clipboardDispatcherThread);
Objects.requireNonNull(contents);
Objects.requireNonNull(mime);
WLDataTransferer wlDataTransferer = (WLDataTransferer) DataTransferer.getInstance();
SortedMap<Long,DataFlavor> formatMap =
wlDataTransferer.getFormatsForTransferable(contents, flavorTable);
long targetFormat = wlDataTransferer.getFormatForNativeAsLong(mime);
DataFlavor flavor = formatMap.get(targetFormat);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: will write contents (" + contents + ") in format " + mime + " to fd=" + destFD);
log.fine("Clipboard: data flavor: " + flavor);
}
if (flavor != null) {
byte[] bytes = wlDataTransferer.translateTransferable(contents, flavor, targetFormat);
if (bytes == null) return;
FileDescriptor javaDestFD = new FileDescriptor();
jdk.internal.access.SharedSecrets.getJavaIOFileDescriptorAccess().set(javaDestFD, destFD);
try (var out = new FileOutputStream(javaDestFD)) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: about to write " + bytes.length + " bytes to " + out);
}
FileChannel ch = out.getChannel();
ByteBuffer buffer = ByteBuffer.wrap(bytes);
// Need to write with retries because when a pipe is in the non-blocking mode
// writing more than its capacity (usually 16 pages or 64K) fails with EAGAIN.
// Since we receive destFD from the Wayland sever, we can't assume it
// to always be in the blocking mode.
while (buffer.hasRemaining()) {
ch.write(buffer);
}
}
}
}
/**
* @return formats the current clipboard is available in; could be null
* @throws IllegalStateException if formats could not be retrieved
*/
@Override
protected long[] getClipboardFormats() {
WLDataTransferer wlDataTransferer = (WLDataTransferer) DataTransferer.getInstance();
List<String> mimes;
synchronized (dataLock) {
if (clipboardFormats != null && !clipboardFormats.isEmpty()) {
long[] res = new long[clipboardFormats.size()];
for (int i = 0; i < res.length; i++) {
res[i] = clipboardFormats.get(i);
}
return res;
} else {
return null;
if (clipboardDataOfferedToUs == null) {
return new long[0];
}
mimes = clipboardDataOfferedToUs.getMimes();
}
var formatsSet = new HashSet<Long>();
for (var mime : mimes) {
formatsSet.add(wlDataTransferer.getFormatForNativeAsLong(mime));
if (mime.equalsIgnoreCase(plainTextUtf8)) {
// some compositors (WSLg) don't advertise UTF8_STRING
formatsSet.add(wlDataTransferer.getFormatForNativeAsLong(utf8String));
}
}
return formatsSet.stream().mapToLong(Long::longValue).toArray();
}
/**
@@ -279,101 +201,21 @@ public final class WLClipboard extends SunClipboard {
*/
@Override
protected byte[] getClipboardData(long format) throws IOException {
int fd = getClipboardFDIn(format);
if (fd >= 0) {
FileDescriptor javaFD = new FileDescriptor();
jdk.internal.access.SharedSecrets.getJavaIOFileDescriptorAccess().set(javaFD, fd);
try (var in = new FileInputStream(javaFD)) {
byte[] bytes = readAllBytesFrom(in);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: read data from " + fd + ": "
+ (bytes != null ? bytes.length : 0) + " bytes");
final WLDataTransferer wlDataTransferer = (WLDataTransferer) DataTransferer.getInstance();
final long utf8StringFormat = wlDataTransferer.getFormatForNativeAsLong(utf8String);
synchronized (dataLock) {
// Iterate over all mime types, since the mapping between mime types and java formats might not be 1:1
// Also treat text/plain;charset=utf-8 as UTF8_STRING
for (var mime : clipboardDataOfferedToUs.getMimes()) {
long curFormat = wlDataTransferer.getFormatForNativeAsLong(mime);
if (curFormat == format) {
return clipboardDataOfferedToUs.receiveData(mime);
} else if (mime.equalsIgnoreCase(plainTextUtf8) && utf8StringFormat == format) {
return clipboardDataOfferedToUs.receiveData(mime);
}
return bytes;
}
}
return null;
}
private int getClipboardFDIn(long format) {
synchronized (dataLock) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: requested content of clipboard with handle "
+ clipboardNativePtr + " in format " + format);
}
if (clipboardNativePtr != 0) {
WLDataTransferer wlDataTransferer = (WLDataTransferer) DataTransferer.getInstance();
String mime = wlDataTransferer.getNativeForFormat(format);
int fd = requestDataInFormat(clipboardNativePtr, mime);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: will read data from " + fd + " in format " + mime);
}
return fd;
}
}
return -1;
}
/**
* Called from native to notify us of the availability of a new clipboard
* denoted by the native handle in a specific MIME format.
* This method is usually called repeatedly with the same nativePtr and
* different formats. When all formats are announces in this way,
* handleNewClipboard() is called.
*
* @param nativePtr a native handle to the clipboard
* @param mime the MIME format in which this clipboard is available.
*/
private void handleClipboardFormat(long nativePtr, String mime) {
WLDataTransferer wlDataTransferer = (WLDataTransferer) DataTransferer.getInstance();
Long format = wlDataTransferer.getFormatForNativeAsLong(mime);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: new format is available for " + nativePtr + ": " + mime);
}
synchronized (dataLock) {
newClipboardFormats.add(format);
}
}
/**
* Called from native to notify us that a new clipboard content
* has been made available. The list of supported formats
* should have already been received and saved in newClipboardFormats.
*
* @param newClipboardNativePtr a native handle to the clipboard
*/
private void handleNewClipboard(long newClipboardNativePtr) {
// Since we have a new clipboard, the existing one is no longer valid.
// We have no way of knowing whether this "new" one is the same as the "old" one.
lostOwnershipNow(null);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: new clipboard is available: " + newClipboardNativePtr);
}
synchronized (dataLock) {
long oldClipboardNativePtr = clipboardNativePtr;
if (oldClipboardNativePtr != 0) {
// "The client must destroy the previous selection data_offer, if any, upon receiving this event."
destroyClipboard(oldClipboardNativePtr);
}
clipboardFormats = newClipboardFormats;
clipboardNativePtr = newClipboardNativePtr; // Could be NULL
newClipboardFormats = new ArrayList<>(INITIAL_MIME_FORMATS_COUNT);
}
notifyOfNewFormats(getClipboardFormats());
}
private void handleOfferCancelled(long offerNativePtr) {
synchronized (dataLock) {
assert offerNativePtr == ourOfferNativePtr;
ourOfferNativePtr = 0;
}
throw new IOException("No appropriate mime type found for WLClipboard.getClipboardData with format = " + format);
}
@Override
@@ -397,76 +239,14 @@ public final class WLClipboard extends SunClipboard {
}
}
/**
* Reads the given input stream until EOF and returns its contents as an array of bytes.
*/
private byte[] readAllBytesFrom(FileInputStream inputStream) throws IOException {
int len = Integer.MAX_VALUE;
List<byte[]> bufs = null;
byte[] result = null;
int total = 0;
int remaining = len;
int n;
do {
byte[] buf = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)];
int nread = 0;
void handleClipboardOffer(WLDataOffer offer /* nullable */) {
lostOwnershipNow(null);
while ((n = inputStream.read(buf, nread,
Math.min(buf.length - nread, remaining))) > 0) {
nread += n;
remaining -= n;
synchronized (dataLock) {
if (clipboardDataOfferedToUs != null) {
clipboardDataOfferedToUs.destroy();
}
if (nread > 0) {
if (MAX_BUFFER_SIZE - total < nread) {
throw new OutOfMemoryError("Required array size too large");
}
if (nread < buf.length) {
buf = Arrays.copyOfRange(buf, 0, nread);
}
total += nread;
if (result == null) {
result = buf;
} else {
if (bufs == null) {
bufs = new ArrayList<>();
bufs.add(result);
}
bufs.add(buf);
}
}
// if the last call to read returned -1 or the number of bytes
// requested have been read then break
} while (n >= 0 && remaining > 0);
if (bufs == null) {
if (result == null) {
return new byte[0];
}
return result.length == total ?
result : Arrays.copyOf(result, total);
clipboardDataOfferedToUs = offer;
}
result = new byte[total];
int offset = 0;
remaining = total;
for (byte[] b : bufs) {
int count = Math.min(b.length, remaining);
System.arraycopy(b, 0, result, offset, count);
offset += count;
remaining -= count;
}
return result;
}
private static native void initIDs();
private static native long createDataOfferQueue();
private static native void dispatchDataOfferQueueImpl(long dataOfferQueuePtr);
private native long initNative(boolean isPrimary);
private native long offerData(long eventSerial, String[] mime, Object data, long dataOfferQueuePtr);
private native void cancelOffer(long offerNativePtr);
private native int requestDataInFormat(long clipboardNativePtr, String mime);
private native void destroyClipboard(long clipboardNativePtr);
}

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