Compare commits

...

48 Commits

Author SHA1 Message Date
Vladimir Dvorak
c9fdf0eba5 JBR-8609 DCEVM: Fix crash during redefinition of subclass with injected fields 2025-04-14 17:57:07 +02:00
Maxim Kartashev
009e79c25d JBR-8551 Wayland: javax/swing/JSlider/TestJSliderRendering.java: The slider is not rendered properly 2025-04-14 12:20:01 +04:00
Vladimir Dvorak
116cbbbc11 JBR-8575 DCEVM: Fix crash when deleting methods from final classes 2025-04-10 21:36:29 +02:00
Vitaly Provodin
a4190e583c JBR-7797 build fastdebug without jcef
(cherry picked from commit cb322fe90d)
2025-04-09 16:31:22 +04:00
Maxim Kartashev
2f672089a5 JBR-8569 Wayland: two java/awt/Desktop tests throw java.lang.InternalError: Failed to find Wayland toolkit internal classes 2025-04-09 16:05:42 +04:00
Maxim Kartashev
b7ce4c8fb9 JBR-8572 Wayland: java/awt/Desktop/DesktopGtkLoadTest/DesktopGtkLoadTest.java: Wrong GTK library version: null 2025-04-08 17:18:40 +04:00
Vitaly Provodin
fce8863e17 fixup! update exclude list on results of 21.0.6_b946.34 test runs 2025-04-08 06:45:55 +04:00
Vitaly Provodin
5b0e5fee58 update exclude list on results of 21.0.6_b946.34 test runs 2025-04-07 13:53:49 +04:00
Vitaly Provodin
e57d489db3 fixup! JBR-7087 Wayland: GtkFileDialogPeer implementation 2025-04-05 01:34:12 +04:00
Nikita Tsarev
6f47232b48 JBR-8533: Fix wrong keys and modifiers being reported for certain non-function key combinations [WLToolkit] 2025-04-04 15:54:23 +02:00
Maxim Kartashev
7a85ad38eb JBR-7896 Wayland: Deadlock in WLClipboard
Avoid performing blocking I/O while holding a lock
2025-04-04 12:20:20 +04:00
Vitaly Provodin
dd96fe584d update exclude list by adding macosx-15.4 2025-04-02 17:36:07 +04:00
Maxim Kartashev
0c09c93635 JBR-7087 Wayland: GtkFileDialogPeer implementation 2025-04-01 15:49:42 +04:00
Maxim Kartashev
8c769bbc7c JBR-7087 Wayland: Desktop support via GNOME 2025-04-01 15:49:36 +04:00
Maxim Kartashev
2308ce139b JBR-7087 Wayland: GTKLookAndFeel support 2025-04-01 15:48:33 +04:00
Vitaly Provodin
25a97304ae update exclude list on results of 21.0.6_b942.30 test runs 2025-04-01 03:55:01 +04:00
Maxim Kartashev
edfe77a55c JBR-8436 Describe various type of github releases 2025-03-31 17:10:51 +04:00
Vitaly Provodin
aa7902f385 JBR-8315 introduce the option -c that causes builds to fail under CI if XCODE_PATH is invalid 2025-03-29 03:27:15 +04:00
Vitaly Provodin
ca2636829c fixup! update exclude list on results of 21.0.6_b929.24 test runs 2025-03-29 03:21:10 +04:00
Vitaly Provodin
58058b4e5c update exclude list on results of 21.0.6_b929.24 test runs 2025-03-26 07:40:28 +04:00
Nikita Tsarev
1ab48b4b72 JBR-8422: A temporary workaround for crash in SystemHotkey setup on macOS 15.4 beta 2025-03-24 18:57:26 +01:00
Vitaly Provodin
c5d734adcc update exclude list on results of 21.0.6_b924.15 test runs 2025-03-22 11:23:08 +04:00
Nikita Provotorov
16d6b74709 JBR-7659 [macOS] SIGILL at [CoreFoundation+0x1d47c5] CFRunLoopRunSpecific.cold.1+0xe / sun.lwawt.macosx.CAccessibility.getChildrenAndRolesRecursive (2K frames).
Fixes crashes caused by multiple javax.accessibility.AccessibleState.EXPANDED/COLLAPSED changes by making sure AppKit has not more than one event of each type being processed or pending in its queue. The logic can be rolled back via a new system property -Dsun.lwawt.macosx.CAccessible.eventsCoalescingEnabled=false.

(cherry picked from commits 84012b5f39, a2707d4e95, fa8c4705e6, a23ab5a040, fe07d2731a)
2025-03-18 08:58:32 +01:00
vlad20012
7d32ca5b3c JBR-8303 Provide JBR API method to perform GC with more intensive heap shrinking
(cherry picked from commit b9ab56b939b827c84f8c2af43480f4e673271104)
2025-03-14 09:35:16 +04:00
Vitaly Provodin
fb03844dc1 JBR-8315 specify XCODE_PATH for JBR building on macOS 2025-03-14 01:40:14 +04:00
Vitaly Provodin
370f8353a6 update exclude list on results of 21.0.6_b919.9 test runs 2025-03-11 08:00:05 +04:00
Vitaly Provodin
24654f556a update exclude list on results of 21.0.6_b913.7 test runs 2025-03-07 01:32:11 +04:00
Alexey Ushakov
8308c81a9a JBR-8201 Vulkan: crash in VKRenderer_MaskFill
Supported fully opaque mask

(cherry picked from commit 48541714e67868ff66181fce37d9f680fc428a4d)
2025-03-03 21:11:33 +01:00
Nikita Gubarkov
6793c86251 JBR-8350 Vulkan: Refactor pipeline cache & composites
This is needed for the implementation of painters (JBR-7646)
- Request pipelines one-by-one instead of a "pipeline sets"
- Split pipeline key into separate "shader" and "topology" (more items may need to be added later)
- Move management of composites into its own file
2025-03-03 20:31:29 +01:00
Nikita Gubarkov
6214b52d0a JBR-8360 Vulkan: Fix ARRAY_PUSH_BACK usage 2025-03-03 20:23:14 +01:00
Nikita Gubarkov
2e1051cd85 JBR-8359 Vulkan: Put VK_DRAW after VKRenderer_AllocateMaskFillBytes
As VKRenderer_AllocateMaskFillBytes can invalidate draw call state due to overflow, it (and future similar functions) must be called before VK_DRAW.
2025-03-03 20:21:55 +01:00
Nikita Gubarkov
224ccc0fb8 JBR-8358 Vulkan: Framebuffer destruction queue
Can be generalized to destroy arbitrary resources later.
2025-03-03 20:21:54 +01:00
Nikita Gubarkov
e957e7045e JBR-8342 Vulkan: Skip validation setup if extension is unavailable 2025-03-03 18:02:30 +01:00
Nikita Gubarkov
f747edbbdc JBR-8284 Vulkan: fix $VULKAN_SDK autoconf check. 2025-03-03 13:15:02 +01:00
Nikita Gubarkov
534b9afc34 JBR-7565 Vulkan: Implement clip 2025-03-03 13:15:01 +01:00
Nikita Gubarkov
cf90d55b49 JBR-7645 Vulkan: Implement hash table for pipeline sets 2025-03-03 13:15:01 +01:00
Nikita Gubarkov
c673e166b4 JBR-7563 Vulkan: Implement MASK_FILL
Mask bytes are copied to texel buffer to be used in shader.
Up to 256KiB (configurable) of mask can be rendered in a single draw call, with no limit on number of MASK_FILL operations in a single batch.

Also added dirty implementation of greyscale-AA DRAW_GLYPH_LIST and FILL_AAPARALLELOGRAM over MASK_FILL.

(cherry picked from commit 4651c3f096)
2025-03-03 13:12:25 +01:00
Alexey Ushakov
0828954308 JBR-8297 Vulkan: Implement ISO_BLIT
Implemented general logic of the blit, removed extra logging
Corrected clipping logic, updated regression tests
Added some flush and init code for the surfaces
2025-02-28 21:48:57 +01:00
Alexey Ushakov
163d3bf7fa fixup! JBR-8287 Vulkan: enable hw accelerated VolatileImage
Added SuppressWarnings for sun.java2d.vulkan.accelsd property access
2025-02-28 18:52:57 +01:00
Vitaly Provodin
effda670ac fixup! JBR-5989 Wayland: jdk_swing_wayland test group 2025-02-28 18:05:14 +04:00
Alexey Ushakov
d6ebb4f51b JBR-8287 Vulkan: enable hw accelerated VolatileImage
Moved robot pixel grabber into window surface data
Created offscreen surface data
Separated surfaces implementation into two files
Moved offscreen surface to the shared code
2025-02-27 20:18:16 +01:00
Nikita Gubarkov
23b0860f75 JBR-8288 Vulkan: Synchronous render queue flush.
RQ doesn't expose async flush operation. All RQ flushes wait for the queue to be drained, effectively serializing queue flusher and EDT execution while still making it prone to deadlocks.
The periodic flush feature of the queue flusher thread is of no use as well, as every observable effect of RQ operation is already immediately followed by a forced flush.
As Vulkan functions have no restriction on the calling thread, keep it simple - lock the monitor and drain the queue synchronously.

(cherry picked from commit 9e0921c4269568f60156973a804fc12d0f26ab61)
2025-02-27 20:14:35 +01:00
Vitaly Provodin
c5a3e6591e update exclude list on results of 21.0.6_b902.6 test runs 2025-02-27 02:24:01 +04:00
Vitaly Provodin
0c0dd61724 Revert "JBR-7438 tune updateCursorImmediately method"
This reverts commit 07ac32e2ad.
2025-02-25 13:46:02 +04:00
Vitaly Provodin
f5b2a21d22 Revert "JBR-7838 added getTreeLock to updateCursorImpl"
This reverts commit 5a9b8a0488.
2025-02-25 13:46:02 +04:00
Nikita Tsarev
499257dedb JBR-7994: Properly report non-base-level function keys [WLToolkit] 2025-02-24 09:44:23 +01:00
Vladimir Dvorak
5cdc148b68 JBR-8316 use the newest method version in get_line_number() 2025-02-22 13:58:18 +01:00
Vitaly Provodin
6253f4a137 update exclude list on results of 21.0.6_b898.3 test runs 2025-02-21 05:39:14 +04:00
115 changed files with 9452 additions and 1604 deletions

View File

@@ -39,6 +39,21 @@ can be found on the [releases page](https://github.com/JetBrains/JetBrainsRuntim
| 2021.1 | [11.0.11+9-b1341.60](https://github.com/JetBrains/JetBrainsRuntime/issues/171#issuecomment-1248891540)| 15-Jun-2021 |
| 2020.3 | [11_0_10-b1145.115](https://github.com/JetBrains/JetBrainsRuntime/issues/171#issuecomment-1249243977) | 21-Jun-2021 |
## Release Flavours
There are many kinds of JBR bundles available on the [Releases page](https://github.com/JetBrains/JetBrainsRuntime/releases):
| Flavour | Description |
|---------------|---------------------------------------------------------------------------------------------------------------|
| JBR | Contains the Java Runtime Environment (JRE) suitable to _run_ JVM-based programs. |
| JBRSDK | Contains the Software Developmet Kit (SDK) suitable to _develop_ and _run_ JVM-based programs. |
| JBR with JCEF | Contains both JBR and JCEF; this flavour is bundled by default with all IntelliJ IDEs. |
| vanilla | Contains just JBR. |
| fastdebug | The native binaries in this bundle are less optimized and are easier to debug. They also run much slower. |
| FreeType | The bundle includes the freetype library built from sources; normally, the library is provided by the system. |
| Vulkan | The bundle includes experimental Vulkan support. | |
| debug symbols | In addition to the usual contents of the bundle the debug information is also included. |
## Contents
- [Welcome to JetBrains Runtime](#welcome-to-jetbrains-runtime)
- [Why Use JetBrains Runtime?](#why-use-jetbrains-runtime)

View File

@@ -18,10 +18,12 @@ function getVersionProp() {
}
DISABLE_WARNINGS_AS_ERRORS=""
while getopts ":iw?" o; do
CONTINUOUS_INTEGRATION=""
while getopts ":iwc?" o; do
case "${o}" in
i) INC_BUILD=1 ;;
w) DISABLE_WARNINGS_AS_ERRORS="--disable-warnings-as-errors" ;;
c) CONTINUOUS_INTEGRATION=1 ;;
esac
done
shift $((OPTIND-1))

View File

@@ -141,7 +141,7 @@ JBRSDK_BUNDLE=jbrsdk
echo Fixing permissions
chmod -R a+r $JSDK
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
if [ "$bundle_type" == "jcef" ]; then
git apply -p0 < jb/project/tools/patches/add_jcef_module_aarch64.patch || do_exit $?
update_jsdk_mods $JSDK $JCEF_PATH/jmods $JSDK/jmods $JSDK_MODS_DIR || do_exit $?
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
@@ -154,7 +154,7 @@ create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" ||
# create sdk image bundle
modules=$(cat $JSDK/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\n//g) || do_exit $?
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
fi
create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" $JBRSDK_BUNDLE $JSDK_MODS_DIR "$modules" || do_exit $?

View File

@@ -149,7 +149,7 @@ JBRSDK_BUNDLE=jbrsdk
echo Fixing permissions
chmod -R a+r $JSDK
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
if [ "$bundle_type" == "jcef" ]; then
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
update_jsdk_mods $JSDK $JCEF_PATH/jmods $JSDK/jmods $JSDK_MODS_DIR || do_exit $?
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
@@ -162,7 +162,7 @@ create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" ||
# create sdk image bundle
modules=$(cat $JSDK/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\n//g) || do_exit $?
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
if [ "$bundle_type" == "jcef" ]|| [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
fi
create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" $JBRSDK_BUNDLE $JSDK_MODS_DIR "$modules" || do_exit $?

View File

@@ -24,6 +24,25 @@ source jb/project/tools/common/scripts/common.sh
JCEF_PATH=${JCEF_PATH:=./jcef_mac}
BOOT_JDK=${BOOT_JDK:=$(/usr/libexec/java_home -v 17)}
XCODE_PATH=${XCODE_PATH:-}
if [ -d "$XCODE_PATH" ]; then
WITH_XCODE_PATH="--with-xcode-path=$XCODE_PATH"
else
if [ -z "${CONTINUOUS_INTEGRATION:-}" ]; then
WITH_XCODE_PATH=""
if [ -n "${XCODE_PATH}" ]; then
echo "XCode not found in the directory: ${XCODE_PATH}"
echo "default XCode will be used"
fi
else
if [ -z "${XCODE_PATH}" ]; then
echo "specify XCode via setting XCODE_PATH"
else
echo "XCode not found in the directory: ${XCODE_PATH}"
fi
do_exit 1
fi
fi
function do_configure {
sh configure \
@@ -42,6 +61,7 @@ function do_configure {
$STATIC_CONF_ARGS \
$REPRODUCIBLE_BUILD_OPTS \
$WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS \
$WITH_XCODE_PATH \
|| do_exit $?
}
@@ -136,9 +156,11 @@ JSDK_MODS_DIR=$IMAGES_DIR/jmods
JBRSDK_BUNDLE=jbrsdk
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
if [ "$bundle_type" == "jcef" ]; then
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
fi
jbr_name_postfix="_${bundle_type}"
else
@@ -151,7 +173,7 @@ create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" ||
# create sdk image bundle
modules=$(cat "$JSDK"/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\n//g) || do_exit $?
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
fi
create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" "$JBRSDK_BUNDLE" "$JSDK_MODS_DIR" "$modules" || do_exit $?

View File

@@ -127,12 +127,13 @@ if [ $? -eq 0 ]; then
fi
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
git apply -p0 < jb/project/tools/patches/add_jcef_module_aarch64.patch || do_exit $?
update_jsdk_mods "$BUILD_JDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not unchanged
if [ "$bundle_type" == "jcef" ]; then
git apply -p0 < jb/project/tools/patches/add_jcef_module_aarch64.patch || do_exit $?
update_jsdk_mods "$BUILD_JDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not unchanged
cat $JCEF_PATH/jcef.version >> $JSDK/release
fi
jbr_name_postfix="_${bundle_type}"
cat $JCEF_PATH/jcef.version >> $JSDK/release
else
jbr_name_postfix=""
fi
@@ -144,7 +145,7 @@ create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" ||
# create sdk image bundle
modules=$(cat ${JSDK}/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\r//g | sed s/\\n//g) || do_exit $?
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
fi
create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" "$JBRSDK_BUNDLE" "$JSDK_MODS_DIR" "$modules" || do_exit $?

View File

@@ -120,12 +120,13 @@ if [ $? -eq 0 ]; then
fi
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
cp $JCEF_PATH/jmods/* ${JSDK_MODS_DIR} # $JSDK/jmods is not unchanged
if [ "$bundle_type" == "jcef" ]; then
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
cp $JCEF_PATH/jmods/* ${JSDK_MODS_DIR} # $JSDK/jmods is not unchanged
cat $JCEF_PATH/jcef.version >> $JSDK/release
fi
jbr_name_postfix="_${bundle_type}"
cat $JCEF_PATH/jcef.version >> $JSDK/release
else
jbr_name_postfix=""
fi
@@ -137,7 +138,7 @@ create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" ||
# create sdk image bundle
modules=$(cat ${JSDK}/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\r//g | sed s/\\n//g)
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
fi
create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" "$JBRSDK_BUNDLE" "$JSDK_MODS_DIR" "$modules" || do_exit $?

View File

@@ -110,9 +110,11 @@ JSDK_MODS_DIR=$IMAGES_DIR/jmods
JBRSDK_BUNDLE=jbrsdk
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
cp $JCEF_PATH/jmods/* ${JSDK_MODS_DIR} # $JSDK/jmods is not unchanged
if [ "$bundle_type" == "jcef" ]; then
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
cp $JCEF_PATH/jmods/* ${JSDK_MODS_DIR} # $JSDK/jmods is not unchanged
fi
jbr_name_postfix="_${bundle_type}"
else

View File

@@ -127,7 +127,7 @@ AC_DEFUN_ONCE([LIB_SETUP_WAYLAND],
VULKAN_FOUND=no
if test "x${with_vulkan_include}" != x; then
AC_MSG_CHECKING([for vulkan.h])
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"
@@ -138,14 +138,15 @@ AC_DEFUN_ONCE([LIB_SETUP_WAYLAND],
fi
fi
if test "x$VULKAN_FOUND" = xno; then
# Check vulkan sdk location
AC_CHECK_HEADERS([$VULKAN_SDK/include/vulkan/vulkan.h],
[ VULKAN_FOUND=yes
VULKAN_FLAGS="-DVK_USE_PLATFORM_WAYLAND_KHR -I${VULKAN_SDK}/include -DVULKAN_ENABLED"
],
[ VULKAN_FOUND=no; break ]
)
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

View File

@@ -375,6 +375,8 @@ inline int Backtrace::get_line_number(Method* method, int bci) {
// "no LineNumberTable". JDK tests for -2.
line_number = -2;
} else {
// (DCEVM): Line numbers from the newest version must be used
method = method->newest_version();
// Returns -1 if no LineNumberTable, and otherwise actual line number
line_number = method->line_number_from_bci(bci);
}

View File

@@ -226,8 +226,13 @@ size_t G1HeapSizingPolicy::full_collection_resize_amount(bool& expand) {
// results.
_g1h->eden_regions_count() * HeapRegion::GrainBytes;
uintx max_heap_free_ratio = MaxHeapFreeRatio;
if (_g1h->gc_cause() == GCCause::_jbr_shrinking_gc_run) {
max_heap_free_ratio = MAX2(MinHeapFreeRatio, MIN2(JbrShrinkingGcMaxHeapFreeRatio, max_heap_free_ratio));
}
size_t minimum_desired_capacity = target_heap_capacity(used_after_gc, MinHeapFreeRatio);
size_t maximum_desired_capacity = target_heap_capacity(used_after_gc, MaxHeapFreeRatio);
size_t maximum_desired_capacity = target_heap_capacity(used_after_gc, max_heap_free_ratio);
// This assert only makes sense here, before we adjust them
// with respect to the min and max heap size.
@@ -264,7 +269,7 @@ size_t G1HeapSizingPolicy::full_collection_resize_amount(bool& expand) {
log_debug(gc, ergo, heap)("Attempt heap shrinking (capacity higher than max desired capacity). "
"Capacity: " SIZE_FORMAT "B occupancy: " SIZE_FORMAT "B live: " SIZE_FORMAT "B "
"maximum_desired_capacity: " SIZE_FORMAT "B (" UINTX_FORMAT " %%)",
capacity_after_gc, used_after_gc, _g1h->used(), maximum_desired_capacity, MaxHeapFreeRatio);
capacity_after_gc, used_after_gc, _g1h->used(), maximum_desired_capacity, max_heap_free_ratio);
expand = false;
return shrink_bytes;

View File

@@ -279,7 +279,7 @@ class CollectedHeap : public CHeapObj<mtGC> {
DEBUG_ONLY(bool is_in_or_null(const void* p) const { return p == nullptr || is_in(p); })
void set_gc_cause(GCCause::Cause v);
GCCause::Cause gc_cause() { return _gc_cause; }
GCCause::Cause gc_cause() const { return _gc_cause; }
oop obj_allocate(Klass* klass, size_t size, TRAPS);
virtual oop array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS);

View File

@@ -132,6 +132,9 @@ const char* GCCause::to_string(GCCause::Cause cause) {
case _jbr_gc_run:
return "JBR full GC";
case _jbr_shrinking_gc_run:
return "JBR shrinking GC";
default:
return "unknown GCCause";
}

View File

@@ -75,6 +75,7 @@ class GCCause : public AllStatic {
_dcmd_gc_run,
_jbr_gc_run,
_jbr_shrinking_gc_run,
_shenandoah_stop_vm,
_shenandoah_allocation_failure_evac,
@@ -94,7 +95,8 @@ class GCCause : public AllStatic {
inline static bool is_user_requested_gc(GCCause::Cause cause) {
return (cause == GCCause::_java_lang_system_gc ||
cause == GCCause::_dcmd_gc_run ||
cause == GCCause::_jbr_gc_run);
cause == GCCause::_jbr_gc_run ||
cause == GCCause::_jbr_shrinking_gc_run);
}
inline static bool is_explicit_full_gc(GCCause::Cause cause) {

View File

@@ -232,6 +232,7 @@ void XDriver::collect(const XDriverRequest& request) {
case GCCause::_wb_full_gc:
case GCCause::_dcmd_gc_run:
case GCCause::_jbr_gc_run:
case GCCause::_jbr_shrinking_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_scavenge_alot:

View File

@@ -193,6 +193,7 @@ void ZCollectedHeap::collect(GCCause::Cause cause) {
case GCCause::_wb_breakpoint:
case GCCause::_dcmd_gc_run:
case GCCause::_jbr_gc_run:
case GCCause::_jbr_shrinking_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_jvmti_force_gc:

View File

@@ -243,6 +243,7 @@ static bool should_clear_soft_references(GCCause::Cause cause) {
case GCCause::_heap_inspection:
case GCCause::_wb_breakpoint:
case GCCause::_dcmd_gc_run:
case GCCause::_jbr_shrinking_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_jvmti_force_gc:
@@ -278,6 +279,7 @@ static bool should_preclean_young(GCCause::Cause cause) {
case GCCause::_wb_breakpoint:
case GCCause::_dcmd_gc_run:
case GCCause::_jbr_gc_run:
case GCCause::_jbr_shrinking_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_jvmti_force_gc:
@@ -338,6 +340,7 @@ void ZDriverMajor::collect(const ZDriverRequest& request) {
case GCCause::_wb_full_gc:
case GCCause::_dcmd_gc_run:
case GCCause::_jbr_gc_run:
case GCCause::_jbr_shrinking_gc_run:
case GCCause::_java_lang_system_gc:
case GCCause::_full_gc_alot:
case GCCause::_jvmti_force_gc:

View File

@@ -600,6 +600,16 @@ Method* ConstantPoolCacheEntry::get_interesting_method_entry() {
// Enhanced RedefineClasses() API support (DCEVM):
// Clear cached entry, let it be re-resolved
void ConstantPoolCacheEntry::clear_entry() {
if (is_method_entry() && indices() != constant_pool_index()) {
Method* old_method = get_interesting_method_entry();
if (old_method != nullptr && old_method->is_old() && old_method->is_deleted()) {
// clean up entries with deleted methods
initialize_entry(constant_pool_index());
return;
}
}
// Always clear for invokehandle/invokedynamic to re-resolve them
bool clearData = bytecode_1() == Bytecodes::_invokehandle || bytecode_1() == Bytecodes::_invokedynamic;
_indices = constant_pool_index();

View File

@@ -560,6 +560,9 @@ JNI_ENTRY(jint, jni_ThrowNew(JNIEnv *env, jclass clazz, const char *message))
} else if (name->equals("java/lang/Exception$JB$$FullGC")) {
Universe::heap()->collect(GCCause::_jbr_gc_run);
return 0;
} else if (name->equals("java/lang/Exception$JB$$ShrinkingGC")) {
Universe::heap()->collect(GCCause::_jbr_shrinking_gc_run);
return 0;
}
Handle class_loader (THREAD, k->class_loader());

View File

@@ -1526,17 +1526,17 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
}
} else {
FieldInfo internal_field = holder->field(fd->index());
InstanceKlass* old_klass = InstanceKlass::cast(holder->old_version());
int java_fields_count = old_klass->java_fields_count();
InstanceKlass* maybe_old_klass = holder->is_redefining() ? InstanceKlass::cast(holder->old_version()) : holder;
int java_fields_count = maybe_old_klass->java_fields_count();
int num_injected;
const InjectedField* const injected = JavaClasses::get_injected(old_klass->name(), &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 old_field = old_klass->field(i);
if (old_field.field_flags().is_injected() &&
FieldInfo maybe_old_field = maybe_old_klass->field(i);
if (maybe_old_field.field_flags().is_injected() &&
internal_field.field_flags().is_injected() &&
old_field.lookup_symbol(old_field.name_index()) == internal_field.lookup_symbol(internal_field.name_index())) {
copy(old_field.offset(), type2aelembytes(Signature::basic_type(internal_field.signature_injected_dcevm())));
if (old_field.offset() < internal_field.offset()) {
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())));
if (maybe_old_field.offset() < internal_field.offset()) {
_copy_backwards = true;
}
break;

View File

@@ -1439,6 +1439,14 @@ const int ObjectAlignmentInBytes = 8;
range(0, 100) \
constraint(MaxHeapFreeRatioConstraintFunc,AfterErgo) \
\
product(uintx, JbrShrinkingGcMaxHeapFreeRatio, 100, MANAGEABLE, \
"The maximum percentage of heap free after GC to avoid shrinking" \
" when using JBR Shrinking GC run." \
" The value is clamped between MinHeapFreeRatio" \
" and MaxHeapFreeRatio." \
" Defaults to MaxHeapFreeRatio option value.") \
range(0, 100) \
\
product(bool, ShrinkHeapInSteps, true, \
"When disabled, informs the GC to shrink the java heap directly" \
" to the target size at the next full GC rather than requiring" \

View File

@@ -126,4 +126,5 @@ public class Exception extends Throwable {
private static class JB$$Assertion {}
private static class JB$$Event {}
private static class JB$$FullGC {}
private static class JB$$ShrinkingGC {}
}

View File

@@ -2694,4 +2694,7 @@ public final class System {
@JBRApi.Provides("SystemUtils#fullGC")
private static native void $$jb$FullGC();
@JBRApi.Provides("SystemUtils#shrinkingGC")
private static native void $$jb$ShrinkingGC();
}

View File

@@ -335,3 +335,14 @@ Java_java_lang_System__00024_00024jb_00024FullGC(JNIEnv *env, jclass ign)
}
}
JNIEXPORT void JNICALL
Java_java_lang_System__00024_00024jb_00024ShrinkingGC(JNIEnv *env, jclass ign)
{
jclass cls = (*env)->FindClass(env, "java/lang/Exception$JB$$ShrinkingGC");
if (cls != 0) {
// Throwing an exception by this name will trigger a full GC with
// a special cause indicating the need to shrink the heap
(*env)->ThrowNew(env, cls, NULL);
}
}

View File

@@ -26,37 +26,111 @@
package sun.lwawt;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Point;
import java.util.concurrent.atomic.AtomicBoolean;
import sun.awt.AWTAccessor;
import sun.awt.CachedCursorManager;
import sun.awt.SunToolkit;
public abstract class LWCursorManager extends CachedCursorManager {
public abstract class LWCursorManager {
@Override
protected Cursor getCursorByPosition(final Point cursorPos, Component c) {
/**
* A flag to indicate if the update is scheduled, so we don't process it
* twice.
*/
private final AtomicBoolean updatePending = new AtomicBoolean(false);
protected LWCursorManager() {
}
/**
* Sets the cursor to correspond the component currently under mouse.
*
* This method should not be executed on the toolkit thread as it
* calls to user code (e.g. Container.findComponentAt).
*/
public final void updateCursor() {
updatePending.set(false);
updateCursorImpl();
}
/**
* Schedules updating the cursor on the corresponding event dispatch
* thread for the given window.
*
* This method is called on the toolkit thread as a result of a
* native update cursor request (e.g. WM_SETCURSOR on Windows).
*/
public final void updateCursorLater(final LWWindowPeer window) {
if (updatePending.compareAndSet(false, true)) {
Runnable r = new Runnable() {
@Override
public void run() {
updateCursor();
}
};
SunToolkit.executeOnEventHandlerThread(window.getTarget(), r);
}
}
private void updateCursorImpl() {
final Point cursorPos = getCursorPosition();
final Component c = findComponent(cursorPos);
final Cursor cursor;
final Object peer = LWToolkit.targetToPeer(c);
if (peer instanceof LWComponentPeer) {
final LWComponentPeer<?, ?> lwpeer = (LWComponentPeer<?, ?>) peer;
final Point p = lwpeer.getLocationOnScreen();
return lwpeer.getCursor(new Point(cursorPos.x - p.x,
cursorPos.y - p.y));
cursor = lwpeer.getCursor(new Point(cursorPos.x - p.x,
cursorPos.y - p.y));
} else {
cursor = (c != null) ? c.getCursor() : null;
}
return null;
setCursor(cursor);
}
@Override
protected Component getComponentUnderCursor() {
/**
* Returns the first visible, enabled and showing component under cursor.
* Returns null for modal blocked windows.
*
* @param cursorPos Current cursor position.
* @return Component or null.
*/
private static final Component findComponent(final Point cursorPos) {
final LWComponentPeer<?, ?> peer = LWWindowPeer.getPeerUnderCursor();
Component c = null;
if (peer != null && peer.getWindowPeerOrSelf().getBlocker() == null) {
return peer.getTarget();
c = peer.getTarget();
if (c instanceof Container) {
final Point p = peer.getLocationOnScreen();
c = AWTAccessor.getContainerAccessor().findComponentAt(
(Container) c, cursorPos.x - p.x, cursorPos.y - p.y, false);
}
while (c != null) {
final Object p = AWTAccessor.getComponentAccessor().getPeer(c);
if (c.isVisible() && c.isEnabled() && p != null) {
break;
}
c = c.getParent();
}
}
return null;
return c;
}
@Override
protected Point getLocationOnScreen(Component component) {
return AWTAccessor.getComponentAccessor().getPeer(component).getLocationOnScreen();
}
/**
* Returns the current cursor position.
*/
// TODO: make it public to reuse for MouseInfo
protected abstract Point getCursorPosition();
/**
* Sets a cursor. The cursor can be null if the mouse is not over a Java
* window.
* @param cursor the new {@code Cursor}.
*/
protected abstract void setCursor(Cursor cursor);
}

View File

@@ -783,7 +783,7 @@ public class LWWindowPeer
@Override
public void notifyUpdateCursor() {
getLWToolkit().getCursorManager().updateCursorLater(this.getTarget());
getLWToolkit().getCursorManager().updateCursorLater(this);
}
@Override

View File

@@ -29,6 +29,7 @@ import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.security.PrivilegedAction;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.accessibility.*;
import javax.swing.*;
@@ -59,6 +60,10 @@ class CAccessible extends CFRetainedResource implements Accessible {
}
});
SELECTED_CHILDREN_MILLISECONDS = scms >= 0 ? scms : SELECTED_CHILDREN_MILLISECONDS_DEFAULT;
EVENTS_COALESCING_ENABLED = Boolean.parseBoolean(
System.getProperty("sun.lwawt.macosx.CAccessible.eventsCoalescingEnabled", "true")
);
}
public static CAccessible getCAccessible(final Accessible a) {
@@ -83,8 +88,10 @@ class CAccessible extends CFRetainedResource implements Accessible {
private static native void menuOpened(long ptr);
private static native void menuClosed(long ptr);
private static native void menuItemSelected(long ptr);
private static native void treeNodeExpanded(long ptr);
private static native void treeNodeCollapsed(long ptr);
// JBR-7659: don't use this method directly; use {@link #postCoalescedTreeNodeExpanded()} instead
private native void treeNodeExpanded(long ptr);
// JBR-7659: don't use this method directly; use {@link #postCoalescedTreeNodeCollapsed()} instead
private native void treeNodeCollapsed(long ptr);
private static native void selectedCellsChanged(long ptr);
private static native void tableContentCacheClear(long ptr);
private static native void updateZoomCaretFocus(long ptr);
@@ -123,6 +130,133 @@ class CAccessible extends CFRetainedResource implements Accessible {
}
}
// =================================================== JBR-7659 ===================================================
// Since macOS 15 applications running a lot of nested CFRunLoop s has been crashing.
// In AWT case, a new nested CFRunLoop is run whenever macOS asks some a11y info about a UI component.
// macOS usually does that when AWT notifies it about changes in UI components.
// So, if there happen N changes of UI components in a row, the AppKit thread may have N nested CFRunLoops.
// If N is too high (>= ~700), the application will crash.
// In JBR-7659 the problem is observed with UI tree expansion/collapsing events, but AFAIU in general it may happen
// with any kind of UI change events.
// As for now we're covering only those 2 kinds of events to make sure if the solution is effective enough.
// The proposed solution is to make sure there is not more than one event of each kind in the AppKit queue
// for the same UI component (i.e. for the same CAccessible).
/** Is a way to disable the fix */
private static final boolean EVENTS_COALESCING_ENABLED;
/**
* The variables indicate whether there's an "event" posted by
* {@link #treeNodeExpanded}/{@link #treeNodeCollapsed(long)} onto the AppKit thread, but not processed by it yet.
*/
private final AtomicBoolean isProcessingTreeNodeExpandedEvent = new AtomicBoolean(false),
isProcessingTreeNodeCollapsedEvent = new AtomicBoolean(false);
/**
* The variables indicate whether there was an attempt to post another
* {@link #treeNodeExpanded}/{@link #treeNodeCollapsed(long)} while there was already one
* on the AppKit thread (no matter if it's still in the queue or is being processed).
* It's necessary to make sure there won't be unobserved changes of the component.
*/
private final AtomicBoolean hasDelayedTreeNodeExpandedEvent = new AtomicBoolean(false),
hasDelayedTreeNodeCollapsedEvent = new AtomicBoolean(false);
private void postCoalescedTreeNodeExpanded() {
postCoalescedEventImpl(
isProcessingTreeNodeExpandedEvent,
hasDelayedTreeNodeExpandedEvent,
this::treeNodeExpanded,
this
);
}
private void postCoalescedTreeNodeCollapsed() {
postCoalescedEventImpl(
isProcessingTreeNodeCollapsedEvent,
hasDelayedTreeNodeCollapsedEvent,
this::treeNodeCollapsed,
this
);
}
private static void postCoalescedEventImpl(
final AtomicBoolean isProcessingEventFlag,
final AtomicBoolean hasDelayedEventFlag,
final CFNativeAction eventPostingAction,
// a reference to this is passed instead of making the method non-static to make sure the implementation
// doesn't accidentally touch anything of the instance by mistake
final CAccessible self
) {
if (!EVENTS_COALESCING_ENABLED) {
self.execute(eventPostingAction);
return;
}
assert EventQueue.isDispatchThread();
final var result = self.executeGet(ptr -> {
if (isProcessingEventFlag.compareAndSet(false, true)) {
hasDelayedEventFlag.set(false);
try {
eventPostingAction.run(ptr);
} catch (Exception err) {
isProcessingEventFlag.set(false);
throw err;
}
} else {
hasDelayedEventFlag.set(true);
}
return 1;
});
if (result != 1) {
// the routine hasn't been executed because there was no native resource (i.e. {@link #ptr} was 0)
isProcessingEventFlag.set(false);
hasDelayedEventFlag.set(false);
}
}
// Called from native
private void onProcessedTreeNodeExpandedEvent() {
onProcessedEventImpl(
isProcessingTreeNodeExpandedEvent,
hasDelayedTreeNodeExpandedEvent,
this::postCoalescedTreeNodeExpanded
);
}
// Called from native
private void onProcessedTreeNodeCollapsedEvent() {
onProcessedEventImpl(
isProcessingTreeNodeCollapsedEvent,
hasDelayedTreeNodeCollapsedEvent,
this::postCoalescedTreeNodeCollapsed
);
}
private static void onProcessedEventImpl(
final AtomicBoolean isProcessingEventFlag,
final AtomicBoolean hasDelayedEventFlag,
final Runnable postingCoalescedEventRoutine
) {
if (!EVENTS_COALESCING_ENABLED) {
return;
}
isProcessingEventFlag.set(false);
if (hasDelayedEventFlag.compareAndSet(true, false)) {
// We shouldn't call postCoalesced<...> synchronously from here to allow the current CFRunLoop
// to finish, thus reducing the current number of nested CFRunLoop s.
EventQueue.invokeLater(postingCoalescedEventRoutine);
}
}
// =============================================== END of JBR-7659 ================================================
private class AXChangeNotifier implements PropertyChangeListener {
@Override
@@ -184,9 +318,17 @@ class CAccessible extends CFRetainedResource implements Accessible {
}
if (newValue == AccessibleState.EXPANDED) {
execute(ptr -> treeNodeExpanded(ptr));
if (EVENTS_COALESCING_ENABLED) {
postCoalescedTreeNodeExpanded();
} else {
execute(ptr -> treeNodeExpanded(ptr));
}
} else if (newValue == AccessibleState.COLLAPSED) {
execute(ptr -> treeNodeCollapsed(ptr));
if (EVENTS_COALESCING_ENABLED) {
postCoalescedTreeNodeCollapsed();
} else {
execute(ptr -> treeNodeCollapsed(ptr));
}
}
if (thisRole == AccessibleRole.COMBO_BOX) {

View File

@@ -56,7 +56,7 @@ final class CCursorManager extends LWCursorManager {
private CCursorManager() { }
@Override
public Point getCursorPosition() {
protected Point getCursorPosition() {
final Point2D nativePosition = nativeGetCursorPosition();
return new Point((int)nativePosition.getX(), (int)nativePosition.getY());
}

View File

@@ -36,7 +36,6 @@
#import "NSApplicationAWT.h"
#import "PropertiesUtilities.h"
#import "ApplicationDelegate.h"
#import "SystemHotkey.h"
#import "sun_lwawt_macosx_LWCToolkit.h"
@@ -263,8 +262,6 @@ static void setUpAWTAppKit(BOOL installObservers)
CFRelease(notBusyObserver);
setBusy(YES);
[SystemHotkey setUp];
}
JNIEnv* env = [ThreadUtilities getJNIEnv];

View File

@@ -1,34 +0,0 @@
// Copyright 2024 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 JBR_SYSTEMHOTKEY_H
#define JBR_SYSTEMHOTKEY_H
#import <objc/NSObject.h>
@interface SystemHotkey : NSObject
+ (void)setUp;
@end
#endif //JBR_SYSTEMHOTKEY_H

View File

@@ -1,4 +1,4 @@
#include "SystemHotkey.h"
#include <pthread.h>
#import <Foundation/Foundation.h>
#import <Carbon/Carbon.h>
@@ -15,6 +15,10 @@
extern JavaVM *jvm;
@interface SystemHotkey : NSObject
+ (void)subscribeToChanges;
@end
enum LOG_LEVEL {
LL_TRACE,
LL_DEBUG,
@@ -23,7 +27,7 @@ enum LOG_LEVEL {
LL_ERROR
};
void plog(int logLevel, const char *formatMsg, ...) {
static void plog(int logLevel, const char *formatMsg, ...) {
if (!jvm)
return;
if (logLevel < LL_TRACE || logLevel > LL_ERROR || formatMsg == NULL)
@@ -265,12 +269,20 @@ static const struct SymbolicHotKey defaultSymbolicHotKeys[] = {
static const int numSymbolicHotkeys = sizeof(defaultSymbolicHotKeys) / sizeof(defaultSymbolicHotKeys[0]);
// Current state of system shortcuts.
// Should only be read and written inside a @synchronized([SystemHotkey class]) block
static struct SymbolicHotKey currentSymbolicHotkeys[numSymbolicHotkeys];
static struct SystemHotkeyState {
bool symbolicHotkeysFilled;
struct SymbolicHotKey currentSymbolicHotkeys[numSymbolicHotkeys];
// Should only be read and written inside a @synchronized([SystemHotkey class]) block
static bool subscribedToShortcutUpdates = false;
bool initialized;
bool enabled;
pthread_mutex_t mutex;
} state = {
.symbolicHotkeysFilled = false,
.initialized = false,
.enabled = false,
.mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER,
};
@interface DefaultParams: NSObject
@property (assign) BOOL enabled;
@@ -454,9 +466,10 @@ static void updateAppleSymbolicHotkeysCache() {
struct SymbolicHotKey hotkeys[numSymbolicHotkeys];
readAppleSymbolicHotkeys(hotkeys);
@synchronized ([SystemHotkey class]) {
memcpy(currentSymbolicHotkeys, hotkeys, numSymbolicHotkeys * sizeof(struct SymbolicHotKey));
}
pthread_mutex_lock(&state.mutex);
memcpy(state.currentSymbolicHotkeys, hotkeys, sizeof(hotkeys));
state.symbolicHotkeysFilled = true;
pthread_mutex_unlock(&state.mutex);
}
static void iterateAppleSymbolicHotkeys(struct SymbolicHotKey hotkeys[numSymbolicHotkeys], Visitor visitorBlock) {
@@ -556,19 +569,46 @@ static void readPbsHotkeys(Visitor visitorBlock) {
}
}
static void readAppleSymbolicHotkeysCached(struct SymbolicHotKey hotkeys[numSymbolicHotkeys]) {
@synchronized ([SystemHotkey class]) {
if (!subscribedToShortcutUpdates) {
[SystemHotkey setUp];
}
memcpy(hotkeys, currentSymbolicHotkeys, numSymbolicHotkeys * sizeof(struct SymbolicHotKey));
static bool ensureInitializedAndEnabled() {
pthread_mutex_lock(&state.mutex);
if (state.initialized) {
bool enabled = state.enabled;
pthread_mutex_unlock(&state.mutex);
return enabled;
}
// JBR-8422
const NSOperatingSystemVersion macOSVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
if (macOSVersion.majorVersion > 15 || (macOSVersion.majorVersion == 15 && macOSVersion.minorVersion >= 4)) {
state.initialized = true;
state.enabled = false;
pthread_mutex_unlock(&state.mutex);
return false;
}
state.initialized = true;
state.enabled = true;
[SystemHotkey subscribeToChanges];
pthread_mutex_unlock(&state.mutex);
updateAppleSymbolicHotkeysCache();
return true;
}
static void readAppleSymbolicHotkeysCached(struct SymbolicHotKey hotkeys[numSymbolicHotkeys]) {
memset(hotkeys, 0, sizeof(struct SymbolicHotKey) * numSymbolicHotkeys);
pthread_mutex_lock(&state.mutex);
if (state.symbolicHotkeysFilled) {
memcpy(hotkeys, state.currentSymbolicHotkeys, sizeof(struct SymbolicHotKey) * numSymbolicHotkeys);
}
pthread_mutex_unlock(&state.mutex);
}
static void readSystemHotkeysImpl(Visitor visitorBlock) {
// Normally, SystemHotkey would get initialized in LWCToolkit initialization.
// But since we can (theoretically) use this API from headless, let's check again.
if (!ensureInitializedAndEnabled()) {
return;
}
struct SymbolicHotKey hotkeys[numSymbolicHotkeys];
readAppleSymbolicHotkeysCached(hotkeys);
@@ -578,40 +618,32 @@ static void readSystemHotkeysImpl(Visitor visitorBlock) {
}
@implementation SystemHotkey
+ (void)setUp {
// This should be called on LWCToolkit initialization.
+ (void)subscribeToChanges {
// Subscribe to changes
NSUserDefaults *symbolicHotKeys = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.symbolichotkeys"];
[symbolicHotKeys addObserver:self forKeyPath:@"AppleSymbolicHotKeys" options:NSKeyValueObservingOptionNew
context:nil];
@synchronized (self) {
if (subscribedToShortcutUpdates) {
return;
}
// Update cached values
updateAppleSymbolicHotkeysCache();
// Subscribe to changes
NSUserDefaults *symbolicHotKeys = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.symbolichotkeys"];
[symbolicHotKeys addObserver:self forKeyPath:@"AppleSymbolicHotKeys" options:NSKeyValueObservingOptionNew
context:nil];
NSUserDefaults *pbsHotKeys = [[NSUserDefaults alloc] initWithSuiteName:@"pbs"];
[pbsHotKeys addObserver:self forKeyPath:@"NSServicesStatus" options:NSKeyValueObservingOptionNew context:nil];
subscribedToShortcutUpdates = true;
}
NSUserDefaults *pbsHotKeys = [[NSUserDefaults alloc] initWithSuiteName:@"pbs"];
[pbsHotKeys addObserver:self forKeyPath:@"NSServicesStatus" options:NSKeyValueObservingOptionNew context:nil];
}
+ (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context {
// Called after AppleSymbolicHotKeys or pbs hotkeys change.
// This method can be called from any thread.
if ([keyPath isEqualToString:@"AppleSymbolicHotKeys"]) {
updateAppleSymbolicHotkeysCache();
}
// This method should only be called from the main thread, but let's check anyway
if (pthread_main_np() == 0) {
return;
}
// Since this notification is sent *after* the configuration was updated,
// the user can safely re-read the hotkeys info after receiving this callback.
// On the Java side, this simply enqueues the change handler to run on the EDT later.
JNIEnv* env = [ThreadUtilities getJNIEnv];
DECLARE_CLASS(jc_SystemHotkey, "java/awt/desktop/SystemHotkey");
DECLARE_STATIC_METHOD(jsm_onChange, jc_SystemHotkey, "onChange", "()V");
@@ -621,12 +653,13 @@ static void readSystemHotkeysImpl(Visitor visitorBlock) {
@end
bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, int keyCode, NSString *chars) {
struct SymbolicHotKey shortcut;
@synchronized ([SystemHotkey class]) {
if (!subscribedToShortcutUpdates) {
[SystemHotkey setUp];
struct SymbolicHotKey shortcut = defaultSymbolicHotKeys[Shortcut_FocusNextApplicationWindow];
if (ensureInitializedAndEnabled()) {
pthread_mutex_lock(&state.mutex);
if (state.symbolicHotkeysFilled) {
shortcut = state.currentSymbolicHotkeys[Shortcut_FocusNextApplicationWindow];
}
shortcut = currentSymbolicHotkeys[Shortcut_FocusNextApplicationWindow];
pthread_mutex_unlock(&state.mutex);
}
int ignoredModifiers = NSAlphaShiftKeyMask | NSFunctionKeyMask | NSNumericPadKeyMask | NSHelpKeyMask;

View File

@@ -1321,20 +1321,55 @@ static jobject sAccessibilityClass = NULL;
@end
static void treeNodeExpandedCollapsedImpl(
JNIEnv * const env,
const jobject cAccessible,
const jlong ccAxJavaPtr,
// @selector(postTreeNodeExpanded) or @selector(postTreeNodeCollapsed)
const SEL ccAxSelector,
// "onProcessedTreeNodeExpandedEvent" or "onProcessedTreeNodeCollapsedEvent"
const char* const cAccessibleCompletionUpcallName
) {
assert((ccAxSelector != NULL));
assert((cAccessibleCompletionUpcallName != NULL));
JNI_COCOA_ENTER(env);
const jobject cAccessibleGlobal = (*env)->NewGlobalRef(env, cAccessible);
if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
if (cAccessibleGlobal != NULL) {
(*env)->DeleteGlobalRef(env, cAccessibleGlobal);
}
return;
}
const id ccAx = (CommonComponentAccessibility *)jlong_to_ptr(ccAxJavaPtr);
[ThreadUtilities performOnMainThreadWaiting:NO block:^{
[ccAx performSelector:ccAxSelector];
JNIEnv * const env = [ThreadUtilities getJNIEnv];
if ( (env != NULL) && (cAccessibleGlobal != NULL) ) {
(void)JNU_CallMethodByName(env, NULL, cAccessibleGlobal, cAccessibleCompletionUpcallName, "()V");
(*env)->DeleteGlobalRef(env, cAccessibleGlobal);
CHECK_EXCEPTION();
}
}];
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_CAccessible
* Method: treeNodeExpanded
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_treeNodeExpanded
(JNIEnv *env, jclass jklass, jlong element)
(JNIEnv *env, jobject self, jlong element)
{
JNI_COCOA_ENTER(env);
[ThreadUtilities performOnMainThread:@selector(postTreeNodeExpanded)
on:(CommonComponentAccessibility *)jlong_to_ptr(element)
withObject:nil
waitUntilDone:NO];
JNI_COCOA_EXIT(env);
treeNodeExpandedCollapsedImpl(env, self, element, @selector(postTreeNodeExpanded), "onProcessedTreeNodeExpandedEvent");
}
/*
@@ -1343,14 +1378,9 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_treeNodeExpanded
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_treeNodeCollapsed
(JNIEnv *env, jclass jklass, jlong element)
(JNIEnv *env, jobject self, jlong element)
{
JNI_COCOA_ENTER(env);
[ThreadUtilities performOnMainThread:@selector(postTreeNodeCollapsed)
on:(CommonComponentAccessibility *)jlong_to_ptr(element)
withObject:nil
waitUntilDone:NO];
JNI_COCOA_EXIT(env);
treeNodeExpandedCollapsedImpl(env, self, element, @selector(postTreeNodeCollapsed), "onProcessedTreeNodeCollapsedEvent");
}
/*

View File

@@ -39,6 +39,7 @@ import com.sun.java.swing.plaf.gtk.GTKConstants.ShadowType;
import com.sun.java.swing.plaf.gtk.GTKConstants.TextDirection;
import sun.awt.image.SunWritableRaster;
import sun.java2d.SunGraphics2D;
import sun.swing.ImageCache;
/**
@@ -118,9 +119,16 @@ class GTKEngine {
private static HashMap<Region, Object> regionToWidgetTypeMap;
private ImageCache cache = new ImageCache(CACHE_SIZE);
private int x0, y0, w0, h0;
private int scale = 1;
private Graphics graphics;
private Object[] cacheArgs;
private final static boolean isWayland;
static {
isWayland = "sun.awt.wl.WLToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName());
}
private native void native_paint_arrow(
int widgetType, int state, int shadowType, String detail,
int x, int y, int width, int height, int arrowType);
@@ -171,7 +179,7 @@ class GTKEngine {
double min, double max,
double visible);
private native void nativeStartPainting(int w, int h);
private native void nativeStartPainting(int w, int h, int scale);
private native int nativeFinishPainting(int[] buffer, int width, int height);
private native void native_switch_theme();
@@ -533,14 +541,7 @@ class GTKEngine {
native_paint_background(widget, state, x - x0, y - y0, w, h);
}
private static final ColorModel[] COLOR_MODELS = {
// Transparency.OPAQUE
new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000),
// Transparency.BITMASK
new DirectColorModel(25, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x01000000),
// Transparency.TRANSLUCENT
ColorModel.getRGBdefault(),
};
private static final ColorModel[] COLOR_MODELS = new ColorModel[3];
private static final int[][] BAND_OFFSETS = {
{ 0x00ff0000, 0x0000ff00, 0x000000ff }, // OPAQUE
@@ -562,9 +563,10 @@ class GTKEngine {
}
// look for cached image
Image img = cache.getImage(getClass(), null, w, h, args);
Image img = cache.getImage(getClass(), null, w * scale, h * scale, args);
if (img != null) {
g.drawImage(img, x, y, null);
prepareGraphics(g);
g.drawImage(img, x, y, w, h, null);
return true;
}
return false;
@@ -575,7 +577,11 @@ class GTKEngine {
*/
public void startPainting(Graphics g,
int x, int y, int w, int h, Object... args) {
nativeStartPainting(w, h);
if (isWayland) { // The X11 impl of GTKEngine is not HiDPI-aware yet
double scaleX = graphics instanceof SunGraphics2D sg2d ? sg2d.getTransform().getScaleX() : 1.0;
scale = scaleX <= 1.0 ? 1 : (int) scaleX;
}
nativeStartPainting(w, h, scale);
x0 = x;
y0 = y;
w0 = w;
@@ -598,7 +604,9 @@ class GTKEngine {
* and paint it.
*/
public BufferedImage finishPainting(boolean useCache) {
DataBufferInt dataBuffer = new DataBufferInt(w0 * h0);
int nativeW = w0 * scale;
int nativeH = h0 * scale;
DataBufferInt dataBuffer = new DataBufferInt(nativeW * nativeH);
// Note that stealData() requires a markDirty() afterwards
// since we modify the data in it.
int transparency =
@@ -608,17 +616,40 @@ class GTKEngine {
int[] bands = BAND_OFFSETS[transparency - 1];
WritableRaster raster = Raster.createPackedRaster(
dataBuffer, w0, h0, w0, bands, null);
dataBuffer, nativeW, nativeH, nativeW, bands, null);
ColorModel cm = COLOR_MODELS[transparency - 1];
BufferedImage img = new BufferedImage(cm, raster, false, null);
ColorModel cm = colorModelFor(transparency);
BufferedImage img = new BufferedImage(cm, raster, true, null);
if (useCache) {
cache.setImage(getClass(), null, w0, h0, cacheArgs, img);
cache.setImage(getClass(), null, nativeW, nativeH, cacheArgs, img);
}
graphics.drawImage(img, x0, y0, null);
prepareGraphics(graphics);
graphics.drawImage(img, x0, y0, w0, h0, null);
return img;
}
private ColorModel colorModelFor(int transparency) {
synchronized (COLOR_MODELS) {
int index = transparency - 1;
if (COLOR_MODELS[index] == null) {
COLOR_MODELS[index] = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration()
.getColorModel(transparency);
}
return COLOR_MODELS[index];
}
}
private void prepareGraphics(Graphics graphics) {
if (scale > 1 && graphics instanceof SunGraphics2D sg2d) {
sg2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
sg2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
sg2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
}
}
/**
* Notify native layer of theme change, and flush cache
*/

View File

@@ -44,8 +44,6 @@ import java.util.HashMap;
import java.util.Map;
import sun.awt.SunToolkit;
import sun.awt.UNIXToolkit;
import sun.awt.OSInfo;
import sun.awt.X11GraphicsDevice;
import sun.security.action.GetPropertyAction;
import sun.swing.DefaultLayoutStyle;
import sun.swing.SwingAccessor;
@@ -118,6 +116,12 @@ public class GTKLookAndFeel extends SynthLookAndFeel {
return IS_3;
}
private final static boolean isWayland;
static {
isWayland = "sun.awt.wl.WLToolkit".equals(Toolkit.getDefaultToolkit().getClass().getName());
}
/**
* Maps a swing constant to a GTK constant.
*/
@@ -1451,16 +1455,18 @@ public class GTKLookAndFeel extends SynthLookAndFeel {
aaTextInfo = new HashMap<>(2);
SwingUtilities2.putAATextInfo(gtkAAFontSettingsCond, aaTextInfo);
Object value = GTKEngine.INSTANCE.getSetting(GTKEngine.Settings.GTK_XFT_DPI);
if (value instanceof Integer) {
int dpi = ((Integer)value).intValue() / 1024;
if (dpi == -1) {
dpi = 96;
if (!isWayland) {
Object value = GTKEngine.INSTANCE.getSetting(GTKEngine.Settings.GTK_XFT_DPI);
if (value instanceof Integer) {
int dpi = ((Integer) value).intValue() / 1024;
if (dpi == -1) {
dpi = 96;
}
if (dpi < 50) {
dpi = 50;
}
sun.awt.X11GraphicsDevice.setXftDpi(dpi);
}
if (dpi < 50) {
dpi = 50;
}
X11GraphicsDevice.setXftDpi(dpi);
}
}

View File

@@ -4887,9 +4887,6 @@ public abstract class Component implements ImageObserver, MenuContainer,
eventLog.finest("{0}", e);
}
if (id == MouseEvent.MOUSE_ENTERED && getToolkit() instanceof SunToolkit toolkit) {
toolkit.updateLastMouseEventComponent(this);
}
/*
* 0. Set timestamp and modifiers of current event.
*/
@@ -7171,12 +7168,6 @@ public abstract class Component implements ImageObserver, MenuContainer,
setGlobalPermanentFocusOwner(null);
}
if (getToolkit() instanceof SunToolkit toolkit) {
if (toolkit.getLastMouseEventComponent() == this) {
toolkit.updateLastMouseEventComponent(null);
}
}
synchronized (getTreeLock()) {
if (isFocusOwner() && KeyboardFocusManager.isAutoFocusTransferEnabledFor(this)) {
transferFocus(true);

View File

@@ -1,134 +0,0 @@
/*
* Copyright (c) 1996, 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.awt;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Point;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class CachedCursorManager {
/**
* A flag to indicate if the update is scheduled, so we don't process it
* twice.
*/
private final AtomicBoolean updatePending = new AtomicBoolean(false);
/**
* Sets the cursor to correspond the component currently under mouse.
*
* This method should not be executed on the toolkit thread as it
* calls to user code (e.g. Container.findComponentAt).
*/
public final void updateCursor() {
updatePending.set(false);
updateCursorImpl();
}
/**
* Schedules updating the cursor on the corresponding event dispatch
* thread for the given window.
*
* This method is called on the toolkit thread as a result of a
* native update cursor request (e.g. WM_SETCURSOR on Windows).
*/
public final void updateCursorLater(final Component window) {
if (updatePending.compareAndSet(false, true)) {
Runnable r = new Runnable() {
@Override
public void run() {
updateCursor();
}
};
SunToolkit.executeOnEventHandlerThread(window, r);
}
}
protected abstract Cursor getCursorByPosition(final Point cursorPos, Component c);
private void updateCursorImpl() {
final Point cursorPos = getCursorPosition();
final Component component = getComponentUnderCursor();
if (component != null) {
synchronized (component.getTreeLock()) {
final Component parentComponent = findComponent(cursorPos, component);
Cursor cursor = getCursorByPosition(cursorPos, parentComponent);
if (cursor == null) {
cursor = (parentComponent != null) ? parentComponent.getCursor() : null;
}
if (cursor != null) {
setCursor(cursor);
}
}
}
}
protected abstract Component getComponentUnderCursor();
protected abstract Point getLocationOnScreen(Component component);
/**
* Returns the first visible, enabled and showing component under cursor.
* Returns null for modal blocked windows.
*
* @param cursorPos Current cursor position.
* @return Component or null.
*/
private Component findComponent(final Point cursorPos, Component currentComponent) {
Component component = currentComponent;
if (component instanceof Container && component.isShowing()) {
final Point p = getLocationOnScreen(component);
component = AWTAccessor.getContainerAccessor().findComponentAt(
(Container) component, cursorPos.x - p.x, cursorPos.y - p.y, false);
}
while (component != null) {
final Object p = AWTAccessor.getComponentAccessor().getPeer(component);
if (component.isVisible() && component.isEnabled() && p != null) {
break;
}
component = component.getParent();
}
return component;
}
/**
* Returns the current cursor position.
*/
public abstract Point getCursorPosition();
/**
* Sets a cursor. The cursor can be null if the mouse is not over a Java
* window.
* @param cursor the new {@code Cursor}.
*/
protected abstract void setCursor(Cursor cursor);
}

View File

@@ -236,17 +236,6 @@ public abstract class SunToolkit extends Toolkit
AccessController.doPrivileged(new GetBooleanAction("awt.lock.fair")));
private static final Condition AWT_LOCK_COND = AWT_LOCK.newCondition();
/*
* A component where the last mouse event came to. Used by cursor manager to
* find the component under cursor. Currently, uses only on Windows
*/
public void updateLastMouseEventComponent(Component component) {
}
public Component getLastMouseEventComponent() {
return null;
}
public interface AwtLockListener {
void afterAwtLocked();
void beforeAwtUnlocked();

View File

@@ -35,6 +35,7 @@ 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);
@@ -55,10 +56,16 @@ public class VKInstance {
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"));
}
}
@@ -66,4 +73,9 @@ public class VKInstance {
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

@@ -0,0 +1,88 @@
/*
* 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.Image;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
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);
offscreenImage = image;
initOps(width, height);
}
@Override
public SurfaceData getReplacement() {
return restoreContents(offscreenImage);
}
@Override
public GraphicsConfiguration getDeviceConfiguration() {
return null;
}
@Override
public long getNativeResource(int resType) {
return 0;
}
@Override
public Rectangle getBounds() {
return new Rectangle(width, height);
}
/**
* Returns destination Image associated with this SurfaceData.
*/
@Override
public Object getDestination() {
return offscreenImage;
}
@Override
public BufferedContext getContext() {
return getGraphicsConfig().getContext();
}
@Override
public boolean isOnScreen() {
return false;
}
}

View File

@@ -26,89 +26,40 @@
package sun.java2d.vulkan;
import jdk.internal.misc.InnocuousThread;
import sun.java2d.pipe.RenderQueue;
import java.security.AccessController;
import java.security.PrivilegedAction;
import static sun.java2d.pipe.BufferedOpCodes.SYNC;
/**
* VK-specific implementation of RenderQueue. This class provides a
* single (daemon) thread that is responsible for periodically flushing
* the queue.
* VK-specific implementation of RenderQueue.
*/
public class VKRenderQueue extends RenderQueue {
private static VKRenderQueue theInstance;
private final QueueFlusher flusher;
@SuppressWarnings("removal")
private VKRenderQueue() {
/*
* The thread must be a member of a thread group
* which will not get GCed before VM exit.
*/
flusher = AccessController.doPrivileged((PrivilegedAction<QueueFlusher>) QueueFlusher::new);
}
private static final VKRenderQueue theInstance = new VKRenderQueue();
/**
* Returns the single VKRenderQueue instance. If it has not yet been
* initialized, this method will first construct the single instance
* before returning it.
* Returns the single VKRenderQueue instance.
*/
public static synchronized VKRenderQueue getInstance() {
if (theInstance == null) {
theInstance = new VKRenderQueue();
}
public static VKRenderQueue getInstance() {
return theInstance;
}
/**
* Flushes the single VKRenderQueue instance synchronously. If an
* VKRenderQueue has not yet been instantiated, this method is a no-op.
* This method is useful in the case of Toolkit.sync(), in which we want
* to flush the Vulkan pipeline, but only if the Vulkan pipeline is currently
* enabled.
* Flushes the single VKRenderQueue instance synchronously.
*/
public static void sync() {
if (theInstance != null) {
theInstance.lock();
try {
theInstance.ensureCapacity(4);
theInstance.getBuffer().putInt(SYNC);
theInstance.flushNow();
} finally {
theInstance.unlock();
}
theInstance.lock();
try {
theInstance.ensureCapacity(4);
theInstance.getBuffer().putInt(SYNC);
theInstance.flushNow();
} finally {
theInstance.unlock();
}
}
@Override
public void flushNow() {
// assert lock.isHeldByCurrentThread();
try {
flusher.flushNow();
} catch (Exception e) {
System.err.println("exception in flushNow:");
e.printStackTrace();
}
}
public void flushAndInvokeNow(Runnable r) {
// assert lock.isHeldByCurrentThread();
try {
flusher.flushAndInvokeNow(r);
} catch (Exception e) {
System.err.println("exception in flushAndInvokeNow:");
e.printStackTrace();
}
}
private native void flushBuffer(long buf, int limit);
private void flushBuffer() {
// assert lock.isHeldByCurrentThread();
int limit = buf.position();
if (limit > 0) {
@@ -121,97 +72,10 @@ public class VKRenderQueue extends RenderQueue {
refSet.clear();
}
private class QueueFlusher implements Runnable {
private boolean needsFlush;
private Runnable task;
private Error error;
private final Thread thread;
public QueueFlusher() {
thread = InnocuousThread.newThread("Java2D Queue Flusher", this);
thread.setDaemon(true);
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
public synchronized void flushNow() {
// wake up the flusher
needsFlush = true;
notify();
// wait for flush to complete
while (needsFlush) {
try {
wait();
} catch (InterruptedException e) {
}
}
// re-throw any error that may have occurred during the flush
if (error != null) {
throw error;
}
}
public synchronized void flushAndInvokeNow(Runnable task) {
this.task = task;
flushNow();
}
public synchronized void run() {
boolean timedOut = false;
while (true) {
while (!needsFlush) {
try {
timedOut = false;
/*
* Wait until we're woken up with a flushNow() call,
* or the timeout period elapses (so that we can
* flush the queue periodically).
*/
wait(100);
/*
* We will automatically flush the queue if the
* following conditions apply:
* - the wait() timed out
* - we can lock the queue (without blocking)
* - there is something in the queue to flush
* Otherwise, just continue (we'll flush eventually).
*/
if (!needsFlush && (timedOut = tryLock())) {
if (buf.position() > 0) {
needsFlush = true;
} else {
unlock();
}
}
} catch (InterruptedException e) {
}
}
try {
// reset the throwable state
error = null;
// flush the buffer now
flushBuffer();
// if there's a task, invoke that now as well
if (task != null) {
task.run();
}
} catch (Error e) {
error = e;
} catch (Exception x) {
System.err.println("exception in QueueFlusher:");
x.printStackTrace();
} finally {
if (timedOut) {
unlock();
}
task = null;
// allow the waiting thread to continue
needsFlush = false;
notify();
}
}
}
public void flushAndInvokeNow(Runnable r) {
flushNow();
r.run();
}
private native void flushBuffer(long buf, int limit);
}

View File

@@ -34,6 +34,7 @@ import sun.java2d.SurfaceData;
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.ParallelogramPipe;
import sun.java2d.pipe.PixelToParallelogramConverter;
import sun.java2d.pipe.RenderBuffer;
@@ -303,5 +304,22 @@ public abstract class VKSurfaceData extends SurfaceData
return graphicsConfig;
}
protected synchronized void configure() {
VKRenderQueue rq = VKRenderQueue.getInstance();
rq.lock();
try {
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(20, 4);
buf.putInt(CONFIGURE_SURFACE);
buf.putLong(getNativeOps());
buf.putInt(width);
buf.putInt(height);
rq.flushNow();
} finally {
rq.unlock();
}
}
public abstract boolean isOnScreen();
}

View File

@@ -0,0 +1,11 @@
#version 450
layout(push_constant) uniform PushConstants {
vec2 viewportNormalizer; // 2.0 / viewport
} 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);
}

View File

@@ -0,0 +1,18 @@
#version 450
layout(set = 0, binding = 0, r8) uniform readonly restrict imageBuffer u_Mask;
layout(origin_upper_left) in vec4 gl_FragCoord;
layout(location = 0) in flat ivec4 in_OriginOffsetAndScanline;
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;
int offset = in_OriginOffsetAndScanline.z;
int scanline = in_OriginOffsetAndScanline.w;
int maskIndex = offset + scanline * maskPos.y + min(scanline, maskPos.x);
out_Color = in_Color * imageLoad(u_Mask, maskIndex).r;
}

View File

@@ -0,0 +1,20 @@
#version 450
layout(push_constant) uniform PushConstants {
vec2 viewportNormalizer; // 2.0 / viewport
} push;
layout(location = 0) in ivec4 in_PositionOffsetAndScanline;
layout(location = 1) in vec4 in_Color;
// This starts with "Origin" and not "Position" intentionally.
// When drawing, vertices are ordered in a such way, that provoking vertex is always the top-left one.
// This gives us an easy way to calculate offset within the rectangle without additional inputs.
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);
out_OriginOffsetAndScanline = in_PositionOffsetAndScanline;
out_Color = in_Color;
}

View File

@@ -824,8 +824,6 @@ ATexturePoolHandle* ATexturePool_getTexture(ATexturePool* pool,
pool->memoryAllocated += requestedBytes;
pool->totalMemoryAllocated += requestedBytes;
J2dTraceLn6(J2D_TRACE_VERBOSE, "ATexturePool_getTexture: created pool item: tex=%p, w=%d h=%d, pf=%d | allocated memory = %lld Kb (allocs: %d)",
minDeltaTpi->texture, width, height, format, pool->memoryAllocated / UNIT_KB, pool->allocatedCount)
if (TRACE_MEM_API) J2dRlsTraceLn6(J2D_TRACE_VERBOSE, "ATexturePool_getTexture: created pool item: tex=%p, w=%d h=%d, pf=%d | allocated memory = %lld Kb (allocs: %d)",
minDeltaTpi->texture, width, height, format, pool->memoryAllocated / UNIT_KB, pool->allocatedCount)
} else {

View File

@@ -2,55 +2,381 @@
#include <stddef.h>
#include "CArrayUtil.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
#if defined(_MSC_VER)
# include <malloc.h>
# define ALIGNED_ALLOC(ALIGNMENT, SIZE) _aligned_malloc((SIZE), (ALIGNMENT))
# define ALIGNED_FREE(PTR) _aligned_free(PTR)
#else
# include <stdlib.h>
# define ALIGNED_ALLOC(ALIGNMENT, SIZE) aligned_alloc((ALIGNMENT), (SIZE))
# define ALIGNED_FREE(PTR) free(PTR)
#endif
void* CARR_array_alloc(size_t elem_size, size_t capacity) {
CARR_array_t *pvec = malloc(elem_size * capacity + offsetof(CARR_array_t, data));
if (pvec == NULL) {
return NULL;
}
pvec->size = 0;
pvec->capacity = capacity;
return pvec->data;
// === Allocation helpers ===
typedef struct {
size_t total_alignment;
size_t aligned_header_size;
void* new_data;
} CARR_context_t;
static size_t CARR_align_size(size_t alignment, size_t size) {
// assert alignment is power of 2
size_t alignment_mask = alignment - 1;
return (size + alignment_mask) & ~alignment_mask;
}
void* CARR_array_realloc(CARR_array_t* vec, size_t elem_size, size_t new_capacity) {
if (vec->capacity == new_capacity) {
return vec->data;
}
CARR_array_t* new_vec =
(CARR_array_t*)((char*)CARR_array_alloc(elem_size, new_capacity) - offsetof(CARR_array_t, data));
if (new_vec == NULL) {
return vec == NULL ? NULL : vec->data;
}
new_vec->capacity = new_capacity;
new_vec->size = MIN(vec->size, new_capacity);
memcpy(new_vec->data, vec->data, new_vec->size*elem_size);
free(vec);
return new_vec->data;
static CARR_context_t CARR_context_init(size_t header_alignment, size_t header_size, size_t data_alignment) {
CARR_context_t context;
// assert header_alignment and data_alignment are powers of 2
context.total_alignment = CARR_MAX(header_alignment, data_alignment);
// assert header_size is multiple of header_alignment
context.aligned_header_size = CARR_align_size(context.total_alignment, header_size);
context.new_data = NULL;
return context;
}
void* CARR_ring_buffer_realloc(CARR_ring_buffer_t* buf, size_t elem_size, size_t new_capacity) {
if (buf != NULL && buf->capacity == new_capacity) {
return buf->data;
static bool CARR_context_alloc(CARR_context_t* context, size_t data_size) {
void* block = ALIGNED_ALLOC(context->total_alignment, context->aligned_header_size + data_size);
if (block == NULL) return false;
context->new_data = (char*)block + context->aligned_header_size;
return true;
}
static void CARR_context_free(CARR_context_t* context, void* old_data) {
if (old_data != NULL) {
void* block = (char*)old_data - context->aligned_header_size;
ALIGNED_FREE(block);
}
CARR_ring_buffer_t* new_buf =
(CARR_ring_buffer_t*) malloc(elem_size * new_capacity + offsetof(CARR_ring_buffer_t, data));
if (new_buf == NULL) {
return NULL;
}
new_buf->head = new_buf->tail = 0;
new_buf->capacity = new_capacity;
if (buf != NULL) {
if (buf->tail > buf->head) {
new_buf->tail = buf->tail - buf->head;
memcpy(new_buf->data, buf->data + buf->head*elem_size, new_buf->tail*elem_size);
} else if (buf->tail < buf->head) {
new_buf->tail = buf->capacity + buf->tail - buf->head;
memcpy(new_buf->data, buf->data + buf->head*elem_size, (buf->capacity-buf->head)*elem_size);
memcpy(new_buf->data + (new_buf->tail-buf->tail)*elem_size, buf->data, buf->tail*elem_size);
}
// === Arrays ===
bool CARR_array_realloc(void** handle, size_t element_alignment, size_t element_size, size_t new_capacity) {
void* old_data = *handle;
if (old_data != NULL && CARR_ARRAY_T(old_data)->capacity == new_capacity) return true;
CARR_context_t context = CARR_context_init(alignof(CARR_array_t), sizeof(CARR_array_t), element_alignment);
if (new_capacity != 0) {
if (!CARR_context_alloc(&context, element_size * new_capacity)) return false;
CARR_ARRAY_T(context.new_data)->capacity = new_capacity;
if (old_data == NULL) {
CARR_ARRAY_T(context.new_data)->size = 0;
} else {
CARR_ARRAY_T(context.new_data)->size = CARR_MIN(CARR_ARRAY_T(old_data)->size, new_capacity);
memcpy(context.new_data, old_data, element_size * CARR_ARRAY_T(context.new_data)->size);
}
free(buf);
}
return new_buf->data;
CARR_context_free(&context, old_data);
*handle = context.new_data;
return true;
}
// === Ring buffers ===
bool CARR_ring_buffer_realloc(void** handle, size_t element_alignment, size_t element_size, size_t new_capacity) {
void* old_data = *handle;
if (old_data != NULL) {
CARR_ring_buffer_t* old_buf = CARR_RING_BUFFER_T(old_data);
if (old_buf->capacity == new_capacity) return true;
// Shrinking is not supported.
if ((old_buf->capacity + old_buf->tail - old_buf->head) % old_buf->capacity > new_capacity) return false;
}
CARR_context_t context =
CARR_context_init(alignof(CARR_ring_buffer_t), sizeof(CARR_ring_buffer_t), element_alignment);
if (new_capacity != 0) {
if (!CARR_context_alloc(&context, element_size * new_capacity)) return false;
CARR_ring_buffer_t* new_buf = CARR_RING_BUFFER_T(context.new_data);
new_buf->capacity = new_capacity;
new_buf->head = new_buf->tail = 0;
if (old_data != NULL) {
CARR_ring_buffer_t* old_buf = CARR_RING_BUFFER_T(old_data);
if (old_buf->tail > old_buf->head) {
new_buf->tail = old_buf->tail - old_buf->head;
memcpy(context.new_data, (char*)old_data + old_buf->head*element_size, new_buf->tail*element_size);
} else if (old_buf->tail < old_buf->head) {
new_buf->tail = old_buf->capacity + old_buf->tail - old_buf->head;
memcpy(context.new_data, (char*)old_data + old_buf->head*element_size,
(old_buf->capacity-old_buf->head)*element_size);
memcpy((char*)context.new_data + (new_buf->tail-old_buf->tail)*element_size, old_data,
old_buf->tail*element_size);
}
}
}
CARR_context_free(&context, old_data);
*handle = context.new_data;
return true;
}
// === Maps ===
static const size_t CARR_hash_map_primes[] = { 11U, 23U, 47U, 97U, 193U, 389U, 769U, 1543U, 3079U, 6151U,
12289U, 24593U, 49157U, 98317U, 196613U, 393241U, 786433U,
1572869U, 3145739U, 6291469U, 12582917U, 25165843U, 50331653U,
100663319U, 201326611U, 402653189U, 805306457U, 1610612741U };
static size_t CARR_hash_map_find_size(const size_t* table, unsigned int table_length, size_t min) {
for (unsigned int i = 0; i < table_length; ++i) if (table[i] >= min) return table[i];
return 0; // Do not return min, as this may break addressing variants which rely on specific numeric properties.
}
#define HASH_MAP_FIND_SIZE(TABLE, SIZE) CARR_hash_map_find_size(TABLE, SARRAY_COUNT_OF(TABLE), SIZE)
// Check whether memory chunk is non-zero.
static bool CARR_check_range(const void* p, size_t alignment, size_t size) {
switch (alignment) {
case sizeof(uint8_t):
case sizeof(uint16_t):{
const uint8_t* data = p;
for (size_t i = 0; i < size; i++) {
if (data[i] != (uint8_t) 0) return true;
}
}break;
case sizeof(uint32_t):{
size >>= 2;
const uint32_t* data = p;
for (size_t i = 0; i < size; i++) {
if (data[i] != (uint32_t) 0) return true;
}
}break;
default:{
size >>= 3;
const uint64_t* data = p;
for (size_t i = 0; i < size; i++) {
if (data[i] != (uint64_t) 0) return true;
}
}break;
}
return false;
}
static bool CARR_map_insert_all(CARR_MAP_LAYOUT_ARGS, void* src, void* dst) {
if (src == NULL) return true;
const CARR_map_dispatch_t* src_dispatch = ((const CARR_map_dispatch_t**)src)[-1];
const CARR_map_dispatch_t* dst_dispatch = ((const CARR_map_dispatch_t**)dst)[-1];
for (const void* key = NULL; (key = src_dispatch->next_key(CARR_MAP_LAYOUT_PASS, src, key)) != NULL;) {
const void* value = src_dispatch->find(CARR_MAP_LAYOUT_PASS, src, key, NULL, false);
void* new_value = dst_dispatch->find(CARR_MAP_LAYOUT_PASS, dst, key, NULL, true);
if (new_value == NULL) return false; // Cannot insert.
memcpy(new_value, value, value_size);
}
return true;
}
// === Open addressing (probing) hash maps ===
// Probing hash maps keep keys and values separately in two continuous aligned memory blocks.
// This class is the most memory-efficient with no overhead other than fixed size header.
// It provides O(1) lookup even when full, except for the cases of deletion and missing
// key, which degrade down to O(N). This makes it a good choice for caches, which
// only do "find or insert" and never delete elements.
static const uint32_t CARR_hash_map_probing_rehash_bit = 0x80000000;
static const uint32_t CARR_hash_map_probing_limit_mask = 0x7fffffff;
typedef struct {
size_t capacity;
size_t size;
uint32_t probing_limit;
float load_factor;
void* null_key_slot;
CARR_equals_fp equals;
CARR_hash_fp hash;
void* dispatch_placeholder;
} CARR_hash_map_probing_t;
static inline void* CARR_hash_map_probing_value_for(CARR_MAP_LAYOUT_ARGS, const void* data, const void* key_slot) {
if (key_slot == NULL) return NULL;
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) data - 1;
size_t value_block_offset = CARR_align_size(value_alignment, key_size * map->capacity);
return (char*)data + value_block_offset + ((const char*)key_slot - (char*)data) / key_size * value_size;
}
static size_t CARR_hash_map_probing_check_extra_capacity(CARR_hash_map_probing_t* map, size_t count) {
// Run length is a local metric, which directly correlate with lookup performance,
// but can suffer from clustering, bad hash function, or bad luck.
// Load factor is a global metric, which reflects "fullness",
// but doesn't capture local effects, like clustering,
// and is over-conservative for good distributions.
// Therefore, we only rehash when both load factor and probing limit are exceeded.
size_t new_capacity = map->size + count;
if (new_capacity <= map->capacity) {
if (!(map->probing_limit & CARR_hash_map_probing_rehash_bit)) { // Rehashing not requested.
new_capacity = 0;
} else if (map->size < (size_t)(map->load_factor * (float)map->capacity)) {
map->probing_limit &= CARR_hash_map_probing_limit_mask; // Load factor too low, reset rehash flag.
new_capacity = 0;
} else new_capacity = map->capacity + 1;
}
return new_capacity;
}
static const void* CARR_hash_map_probing_next_key(CARR_MAP_LAYOUT_ARGS, const void* data, const void* key_slot) {
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) data - 1;
char* slot;
if (key_slot == NULL) slot = (char*)data;
else if (key_slot < data) return NULL;
else slot = (char*)key_slot + key_size;
char* limit = (char*)data + key_size * (map->capacity - 1);
for (; slot <= limit; slot += key_size) {
if (CARR_check_range(slot, key_alignment, key_size) || slot == map->null_key_slot) return slot;
}
return NULL;
}
static void CARR_hash_map_probing_clear(CARR_MAP_LAYOUT_ARGS, void* data) {
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) data - 1;
memset(data, 0, key_size * map->capacity);
map->probing_limit &= CARR_hash_map_probing_limit_mask;
map->null_key_slot = NULL;
map->size = 0;
}
static void CARR_hash_map_probing_free(CARR_MAP_LAYOUT_ARGS, void* data) {
if (data == NULL) return;
CARR_context_t context = CARR_context_init(alignof(CARR_hash_map_probing_t), sizeof(CARR_hash_map_probing_t),
CARR_MAX(key_alignment, value_alignment));
CARR_context_free(&context, data);
}
// === Linear probing hash map ===
static inline void CARR_hash_map_linear_probing_check_run(CARR_MAP_LAYOUT_ARGS, CARR_hash_map_probing_t* map,
const char* from, const char* to) {
if (map->probing_limit & CARR_hash_map_probing_rehash_bit) return; // Rehashing already requested.
if (map->size < (size_t)(map->load_factor * (float)map->capacity)) return; // Load factor too low.
ptrdiff_t offset = to - from;
if (to < from) offset += (ptrdiff_t)(map->capacity * key_size);
size_t run = (size_t)offset / key_size;
// Set rehash bit if our probing length exceeded the limit.
if (run > (size_t)map->probing_limit) map->probing_limit |= CARR_hash_map_probing_rehash_bit;
}
static void* CARR_hash_map_linear_probing_find(CARR_MAP_LAYOUT_ARGS,
void* data, const void* key, const void** resolved_key, bool insert) {
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) data - 1;
char* wrap = (char*)data + key_size * map->capacity;
if (key >= data && key < (void*) wrap && ((const char*)key - (char*)data) % key_size == 0) {
// Try fast access for resolved key.
if (key == map->null_key_slot || CARR_check_range(key, key_alignment, key_size)) {
if (resolved_key != NULL) *resolved_key = key;
return CARR_hash_map_probing_value_for(CARR_MAP_LAYOUT_PASS, data, key);
}
}
size_t hash = map->hash(key);
char* start = (char*)data + key_size * (hash % map->capacity);
char* slot = start;
for (;;) {
bool is_null = !CARR_check_range(slot, key_alignment, key_size);
if (map->equals(key, slot)) {
// Special case to distinguish null key from missing one.
if (is_null) {
if (map->null_key_slot == NULL && insert) {
map->null_key_slot = slot;
break; // Insert.
}
slot = map->null_key_slot;
}
if (resolved_key != NULL) *resolved_key = slot;
return CARR_hash_map_probing_value_for(CARR_MAP_LAYOUT_PASS, data, slot);
}
if (is_null && slot != map->null_key_slot) { // Key not found.
if (insert) break; // Insert.
return resolved_key != NULL ? (void*)(*resolved_key = NULL) : NULL;
}
slot += key_size;
if (slot == wrap) slot = (char*)data;
if (slot == start) {
return resolved_key != NULL ? (void*)(*resolved_key = NULL) : NULL; // We traversed the whole map.
}
}
// Insert.
void* value = CARR_hash_map_probing_value_for(CARR_MAP_LAYOUT_PASS, data, slot);
memcpy(slot, key, key_size); // Copy key into slot.
memset(value, 0, value_size); // Clear value.
map->size++;
if (resolved_key != NULL) {
*resolved_key = slot;
value = NULL; // Indicate that value was just inserted.
}
CARR_hash_map_linear_probing_check_run(CARR_MAP_LAYOUT_PASS, map, start, slot);
return value;
}
static bool CARR_hash_map_linear_probing_remove(CARR_MAP_LAYOUT_ARGS, void* data, const void* key) {
char* key_slot;
CARR_hash_map_linear_probing_find(CARR_MAP_LAYOUT_PASS, data, key, (const void**) &key_slot, false);
if (key_slot == NULL) return false;
char* start = key_slot;
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) data - 1;
char* wrap = (char*)data + key_size * map->capacity;
for (;;) {
if (map->null_key_slot == key_slot) map->null_key_slot = NULL;
char* slot = key_slot;
for (;;) {
slot += key_size;
if (slot == wrap) slot = (char*)data;
if (slot == start || (!CARR_check_range(slot, key_alignment, key_size) && slot != map->null_key_slot)) {
memset(key_slot, 0, key_size); // Clear key slot.
CARR_hash_map_linear_probing_check_run(CARR_MAP_LAYOUT_PASS, map, start, slot);
return true;
}
size_t hash = map->hash(slot);
char* expected_slot = (char*)data + key_size * (hash % map->capacity);
if (slot >= expected_slot) {
if (key_slot >= expected_slot && key_slot <= slot) break;
} else {
if (key_slot >= expected_slot || key_slot <= slot) break;
}
}
// Move another entry into the gap.
if (map->null_key_slot == slot) map->null_key_slot = key_slot;
memcpy(key_slot, slot, key_size);
memcpy(CARR_hash_map_probing_value_for(CARR_MAP_LAYOUT_PASS, data, key_slot),
CARR_hash_map_probing_value_for(CARR_MAP_LAYOUT_PASS, data, slot), value_size);
key_slot = slot; // Repeat with the new entry.
}
}
static bool CARR_hash_map_linear_probing_ensure_extra_capacity(CARR_MAP_LAYOUT_ARGS, void** handle, size_t count) {
void* data = *handle;
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) data - 1;
size_t new_capacity = CARR_hash_map_probing_check_extra_capacity(map, count);
if (new_capacity == 0) return true;
return CARR_hash_map_linear_probing_rehash(CARR_MAP_LAYOUT_PASS, handle, map->equals, map->hash, new_capacity,
map->probing_limit & CARR_hash_map_probing_limit_mask, map->load_factor);
}
bool CARR_hash_map_linear_probing_rehash(CARR_MAP_LAYOUT_ARGS, void** handle, CARR_equals_fp equals, CARR_hash_fp hash,
size_t new_capacity, uint32_t probing_limit, float load_factor) {
size_t table_capacity = HASH_MAP_FIND_SIZE(CARR_hash_map_primes, new_capacity);
if (table_capacity != 0) new_capacity = table_capacity;
CARR_context_t context = CARR_context_init(alignof(CARR_hash_map_probing_t), sizeof(CARR_hash_map_probing_t),
CARR_MAX(key_alignment, value_alignment));
size_t value_block_offset = CARR_align_size(value_alignment, key_size * new_capacity);
if (!CARR_context_alloc(&context, value_block_offset + value_size * new_capacity)) return false;
CARR_hash_map_probing_t* map = (CARR_hash_map_probing_t*) context.new_data - 1;
*map = (CARR_hash_map_probing_t) {
.capacity = new_capacity,
.size = 0,
.probing_limit = CARR_MIN(probing_limit, CARR_hash_map_probing_limit_mask),
.load_factor = load_factor,
.null_key_slot = NULL,
.equals = equals,
.hash = hash
};
static const CARR_map_dispatch_t dispatch = {
&CARR_hash_map_probing_next_key,
&CARR_hash_map_linear_probing_find,
&CARR_hash_map_linear_probing_remove,
&CARR_hash_map_linear_probing_ensure_extra_capacity,
&CARR_hash_map_probing_clear,
&CARR_hash_map_probing_free,
};
((const CARR_map_dispatch_t**)context.new_data)[-1] = &dispatch;
CARR_hash_map_probing_clear(CARR_MAP_LAYOUT_PASS, context.new_data);
if (!CARR_map_insert_all(CARR_MAP_LAYOUT_PASS, *handle, context.new_data)) {
CARR_context_free(&context, context.new_data);
return false;
}
if (*handle != NULL) ((const CARR_map_dispatch_t**)*handle)[-1]->free(CARR_MAP_LAYOUT_PASS, *handle);
*handle = context.new_data;
return true;
}

View File

@@ -2,72 +2,124 @@
#define C_ARRAY_UTIL_H
#include <malloc.h>
#include <stdalign.h>
#include <stdbool.h>
#include <stdint.h>
// C_ARRAY_UTIL_ALLOCATION_FAILED is called when allocation fails.
// Default implementation calls abort().
// Functions that can call C_ARRAY_UTIL_ALLOCATION_FAILED explicitly state
// this in the documentation. Functions with *_TRY_* return NULL on failure.
// this in the documentation. Functions with *_TRY_* keep the data structure unchanged.
#ifndef C_ARRAY_UTIL_ALLOCATION_FAILED
#include <stdlib.h>
#define C_ARRAY_UTIL_ALLOCATION_FAILED() abort()
#endif
// === Allocation helpers ===
#define CARR_MIN(a,b) (((a)<(b))?(a):(b))
#define CARR_MAX(a,b) (((a)>(b))?(a):(b))
static inline bool CARR_handle_alloc(bool CARR_result, bool CARR_force) {
if (CARR_result || !CARR_force) return CARR_result;
C_ARRAY_UTIL_ALLOCATION_FAILED();
return false;
}
static inline void consume(const void* value) {}
// === Arrays ===
#ifndef ARRAY_CAPACITY_GROW
#define ARRAY_CAPACITY_GROW(C) (((C) * 3 + 1) / 2) // 1.5 multiplier
#endif
#ifndef ARRAY_DEFAULT_CAPACITY
#define ARRAY_DEFAULT_CAPACITY 10
#endif
typedef struct {
size_t size;
size_t capacity;
char data[];
} CARR_array_t;
void* CARR_array_alloc(size_t elem_size, size_t capacity);
void* CARR_array_realloc(CARR_array_t* vec, size_t elem_size, size_t new_capacity);
bool CARR_array_realloc(void** handle, size_t element_alignment, size_t element_size, size_t new_capacity);
typedef struct {
size_t head;
size_t tail;
size_t capacity;
char data[];
} CARR_ring_buffer_t;
#define CARR_ARRAY_T(P) ((CARR_array_t*)(P) - 1) // NULL unsafe!
void* CARR_ring_buffer_realloc(CARR_ring_buffer_t* buf, size_t elem_size, size_t new_capacity);
static inline void* CARR_array_alloc(size_t element_alignment, size_t element_size, size_t new_capacity) {
void* data = NULL;
CARR_array_realloc(&data, element_alignment, element_size, new_capacity);
return data;
}
static inline bool CARR_array_ensure_capacity(void** handle, size_t alignment, size_t size,
size_t new_capacity, bool force) {
void* data = *handle;
if (new_capacity > (data == NULL ? 0 : CARR_ARRAY_T(data)->capacity)) {
return CARR_handle_alloc(CARR_array_realloc(handle, alignment, size, new_capacity), force);
}
return true;
}
static inline bool CARR_array_resize(void** handle, size_t alignment, size_t size, size_t new_size, bool force) {
if (CARR_array_ensure_capacity(handle, alignment, size, new_size, force)) {
void* data = *handle;
if (data != NULL) CARR_ARRAY_T(data)->size = new_size;
return true;
}
return false;
}
static inline void CARR_array_push_back(void** handle, size_t alignment, size_t size) {
void* data = *handle;
if (data == NULL || CARR_ARRAY_T(data)->size >= CARR_ARRAY_T(data)->capacity) {
size_t new_capacity = data == NULL ? ARRAY_DEFAULT_CAPACITY : ARRAY_CAPACITY_GROW(CARR_ARRAY_T(data)->size);
if (!CARR_handle_alloc(CARR_array_realloc(handle, alignment, size, new_capacity), true)) return;
data = *handle; // assert data != NULL
}
CARR_ARRAY_T(data)->size++;
}
/**
* Dynamic array declaration, e.g. ARRAY(int) my_array = NULL;
* @param TYPE type of the array element.
*/
#define ARRAY(TYPE) TYPE*
/**
* Allocate array. Returns NULL on allocation failure.
* @param T type of elements
* @param CAPACITY capacity of the array
* @return pointer to the allocated array, or NULL
*/
#define ARRAY_ALLOC(T, CAPACITY) (T*)CARR_array_alloc(sizeof(T), CAPACITY)
#define ARRAY_T(P) ((CARR_array_t *)((P) == NULL ? NULL : (char*)(P) - offsetof(CARR_array_t, data)))
#define ARRAY_ALLOC(T, CAPACITY) ((T*)CARR_array_alloc(alignof(T), sizeof(T), CAPACITY))
/**
* @param P pointer to the first data element of the array
* @param P array
* @return size of the array
*/
#define ARRAY_SIZE(P) ((P) == NULL ? (size_t) 0 : (ARRAY_T(P))->size)
#define ARRAY_SIZE(P) ((P) == NULL ? (size_t) 0 : (CARR_ARRAY_T(P))->size)
/**
* @param P pointer to the first data element of the array
* @param P array
* @return capacity of the array
*/
#define ARRAY_CAPACITY(P) ((P) == NULL ? (size_t) 0 : (ARRAY_T(P))->capacity)
#define ARRAY_CAPACITY(P) ((P) == NULL ? (size_t) 0 : (CARR_ARRAY_T(P))->capacity)
/**
* @param P pointer to the first data element of the array
* @return last element in the array
* @param P array
* @return dereferenced pointer to the last element in the array
*/
#define ARRAY_LAST(P) ((P)[ARRAY_SIZE(P) - 1])
/**
* Deallocate the vector
* @param P pointer to the first data element of the array
* @param P array
*/
#define ARRAY_FREE(P) free(ARRAY_T(P))
#define ARRAY_FREE(P) ((void)CARR_array_realloc((void**)&(P), alignof(*(P)), sizeof(*(P)), 0))
/**
* Apply function to the array elements
* @param P pointer to the first data element of the array
* @param P array
* @param F function to apply
*/
#define ARRAY_APPLY(P, F) do { \
@@ -76,7 +128,7 @@ void* CARR_ring_buffer_realloc(CARR_ring_buffer_t* buf, size_t elem_size, size_t
/**
* Apply function to the array elements, passing pointer to an element as first parameter
* @param P pointer to the first data element of the array
* @param P array
* @param F function to apply
*/
#define ARRAY_APPLY_LEADING(P, F, ...) do { \
@@ -85,7 +137,7 @@ void* CARR_ring_buffer_realloc(CARR_ring_buffer_t* buf, size_t elem_size, size_t
/**
* Apply function to the array elements, passing pointer to an element as last parameter
* @param P pointer to the first data element of the array
* @param P array
* @param F function to apply
*/
#define ARRAY_APPLY_TRAILING(P, F, ...) do { \
@@ -93,157 +145,367 @@ void* CARR_ring_buffer_realloc(CARR_ring_buffer_t* buf, size_t elem_size, size_t
} while(0)
/**
* Ensure array capacity. Implicitly initializes when array is NULL.
* On allocation failure, array is unchanged.
* @param P pointer to the first data element of the array
* Ensure array capacity. Array is implicitly initialized when necessary.
* On allocation failure, array is left unchanged.
* @param P array
* @param CAPACITY required capacity of the array
* @return true if the operation succeeded
*/
#define ARRAY_TRY_ENSURE_CAPACITY(P, CAPACITY) do { \
if ((P) == NULL) { \
if ((CAPACITY) > 0) (P) = CARR_array_alloc(sizeof((P)[0]), CAPACITY); \
} else if (ARRAY_CAPACITY(P) < (CAPACITY)) { \
(P) = CARR_array_realloc(ARRAY_T(P), sizeof((P)[0]), CAPACITY); \
} \
} while(0)
#define ARRAY_TRY_ENSURE_CAPACITY(P, CAPACITY) \
CARR_array_ensure_capacity((void**)&(P), alignof(*(P)), sizeof(*(P)), (CAPACITY), false)
/**
* Ensure array capacity. Implicitly initializes when array is NULL.
* Ensure array capacity. Array is implicitly initialized when necessary.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P pointer to the first data element of the array
* @param P array
* @param CAPACITY required capacity of the array
*/
#define ARRAY_ENSURE_CAPACITY(P, CAPACITY) do { \
ARRAY_TRY_ENSURE_CAPACITY(P, CAPACITY); \
if (ARRAY_CAPACITY(P) < (CAPACITY)) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
} while(0)
#define ARRAY_ENSURE_CAPACITY(P, CAPACITY) \
((void)CARR_array_ensure_capacity((void**)&(P), alignof(*(P)), sizeof(*(P)), (CAPACITY), true))
/**
* Shrink capacity of the array to its size.
* On allocation failure, array is unchanged.
* @param P pointer to the first data element of the array
* On allocation failure, array is left unchanged.
* @param P array
* @return the array
* @return true if the operation succeeded
*/
#define ARRAY_SHRINK_TO_FIT(P) do { \
if ((P) != NULL) { \
(P) = CARR_array_realloc(ARRAY_T(P), sizeof((P)[0]), ARRAY_SIZE(P)); \
} \
} while(0)
#define ARRAY_RESIZE_IMPL(P, SIZE, ...) do { \
if ((P) != NULL || (SIZE) > 0) { \
ARRAY_ENSURE_CAPACITY(P, SIZE); \
if ((P) != NULL && (ARRAY_T(P))->capacity >= (SIZE)) { \
(ARRAY_T(P))->size = (SIZE); \
} __VA_ARGS__ \
} \
} while(0)
#define ARRAY_SHRINK_TO_FIT(P) CARR_array_realloc((void**)&(P), alignof(*(P)), sizeof(*(P)), ARRAY_SIZE(P))
/**
* Resize an array. Implicitly initializes when array is NULL.
* On allocation failure, array is unchanged.
* @param P pointer to the first data element of the array
* Resize an array. Array is implicitly initialized when necessary.
* On allocation failure, array is left unchanged.
* @param P array
* @param SIZE required size of the array
* @return true if the operation succeeded
*/
#define ARRAY_TRY_RESIZE(P, SIZE) \
CARR_array_resize((void**)&(P), alignof(*(P)), sizeof(*(P)), (SIZE), false)
/**
* Resize an array. Array is implicitly initialized when necessary.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P array
* @param SIZE required size of the array
*/
#define ARRAY_TRY_RESIZE(P, SIZE) ARRAY_RESIZE_IMPL(P, SIZE, )
#define ARRAY_RESIZE(P, SIZE) \
((void)CARR_array_resize((void**)&(P), alignof(*(P)), sizeof(*(P)), (SIZE), true))
/**
* Resize an array. Implicitly initializes when array is NULL.
* Add element to the end of the array. Array is implicitly initialized when necessary.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P pointer to the first data element of the array
* @param SIZE required size of the array
* @param P array
* @return dereferenced pointer to the inserted element
*/
#define ARRAY_RESIZE(P, SIZE) ARRAY_RESIZE_IMPL(P, SIZE, else if ((SIZE) > 0) C_ARRAY_UTIL_ALLOCATION_FAILED();)
#define ARRAY_PUSH_BACK(P) \
(*(CARR_array_push_back((void**)&(P), alignof(*(P)), sizeof(*(P))), (P) + ARRAY_SIZE(P) - 1))
/**
* Add element to the end of the array. Implicitly initializes when array is NULL.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P pointer to the first data element of the array
* Compile-time length of the static array.
*/
#define ARRAY_PUSH_BACK(P, ...) do { \
if ((P) == NULL) { \
(P) = CARR_array_alloc(sizeof((P)[0]), ARRAY_DEFAULT_CAPACITY); \
} else if (ARRAY_SIZE(P) >= ARRAY_CAPACITY(P)) { \
(P) = CARR_array_realloc(ARRAY_T(P), sizeof((P)[0]), ARRAY_CAPACITY_GROW(ARRAY_SIZE(P))); \
} \
if (ARRAY_SIZE(P) >= ARRAY_CAPACITY(P)) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
*((P) + ARRAY_SIZE(P)) = (__VA_ARGS__); \
(ARRAY_T(P))->size++; \
} while(0)
#define SARRAY_COUNT_OF(STATIC_ARRAY) (sizeof(STATIC_ARRAY)/sizeof((STATIC_ARRAY)[0]))
#define RING_BUFFER_T(P) ((CARR_ring_buffer_t *)((P) == NULL ? NULL : (char*)(P) - offsetof(CARR_ring_buffer_t, data)))
// === Ring buffers ===
typedef struct {
size_t head;
size_t tail;
size_t capacity;
} CARR_ring_buffer_t;
bool CARR_ring_buffer_realloc(void** handle, size_t element_alignment, size_t element_size, size_t new_capacity);
#define CARR_RING_BUFFER_T(P) ((CARR_ring_buffer_t*)(P) - 1) // NULL / type unsafe!
#define CARR_RING_BUFFER_IS_NULL(P) (&(P)->CARR_elem == NULL) // Guard against wrong pointer types.
#define CARR_RING_BUFFER_GUARD(P, ...) (consume(&(P)->CARR_elem), __VA_ARGS__) // Guard against wrong pointer types.
static inline size_t CARR_ring_buffer_size(void* data) {
CARR_ring_buffer_t* buffer = CARR_RING_BUFFER_T(data);
return (buffer->capacity + buffer->tail - buffer->head) % buffer->capacity;
}
static inline bool CARR_ring_buffer_ensure_can_push(void** handle, size_t alignment, size_t size, bool force) {
void* data = *handle;
if (data == NULL || CARR_ring_buffer_size(data) + 1 >= CARR_RING_BUFFER_T(data)->capacity) {
size_t new_capacity = data == NULL ?
ARRAY_DEFAULT_CAPACITY : ARRAY_CAPACITY_GROW(CARR_RING_BUFFER_T(data)->capacity);
return CARR_handle_alloc(CARR_ring_buffer_realloc(handle, alignment, size, new_capacity), force);
}
return true;
}
static inline size_t CARR_ring_buffer_push_front(void* data) {
if (data == NULL) return 0;
CARR_ring_buffer_t* buffer = CARR_RING_BUFFER_T(data);
return buffer->head = (buffer->head + buffer->capacity - 1) % buffer->capacity;
}
static inline size_t CARR_ring_buffer_push_back(void* data) {
if (data == NULL) return 0;
CARR_ring_buffer_t* buffer = CARR_RING_BUFFER_T(data);
size_t i = buffer->tail;
buffer->tail = (buffer->tail + 1) % buffer->capacity;
return i;
}
/**
* @param P pointer to the first data element of the ring buffer
* Ring buffer declaration, e.g. RING_BUFFER(int) my_ring = NULL;
* @param TYPE type of the ring buffer element.
*/
#define RING_BUFFER(TYPE) struct { TYPE CARR_elem; }*
/**
* @param P ring buffer
* @return size of the ring buffer
*/
#define RING_BUFFER_SIZE(P) ((P) == NULL ? (size_t) 0 : \
(RING_BUFFER_T(P)->capacity + RING_BUFFER_T(P)->tail - RING_BUFFER_T(P)->head) % RING_BUFFER_T(P)->capacity)
#define RING_BUFFER_SIZE(P) (CARR_RING_BUFFER_IS_NULL(P) ? (size_t) 0 : CARR_ring_buffer_size(P))
/**
* @param P pointer to the first data element of the ring buffer
* @param P ring buffer
* @return capacity of the ring buffer
*/
#define RING_BUFFER_CAPACITY(P) ((P) == NULL ? (size_t) 0 : RING_BUFFER_T(P)->capacity)
#define RING_BUFFER_CAPACITY(P) (CARR_RING_BUFFER_IS_NULL(P) ? (size_t) 0 : CARR_RING_BUFFER_T(P)->capacity)
/**
* Ensure enough capacity to push an element into ring buffer. Implicitly initializes when buffer is NULL.
* On allocation failure, buffer is left unchanged.
* @param P ring buffer
* @return true if the operation succeeded
*/
#define RING_BUFFER_TRY_ENSURE_CAN_PUSH(P) CARR_RING_BUFFER_GUARD((P), \
CARR_ring_buffer_ensure_can_push((void**)&(P), alignof(*(P)), sizeof(*(P)), false))
/**
* Ensure enough capacity to push an element into ring buffer. Implicitly initializes when buffer is NULL.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P ring buffer
*/
#define RING_BUFFER_ENSURE_CAN_PUSH(P) CARR_RING_BUFFER_GUARD((P), \
(void)CARR_ring_buffer_ensure_can_push((void**)&(P), alignof(*(P)), sizeof(*(P)), true))
/**
* Add element to the beginning of the ring buffer. Implicitly initializes when buffer is NULL.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P pointer to the first data element of the buffer
* @param P ring buffer
* @return dereferenced pointer to the inserted element
*/
#define RING_BUFFER_PUSH_FRONT(P, ...) do { \
if ((P) == NULL) (P) = CARR_ring_buffer_realloc(NULL, sizeof((P)[0]), ARRAY_DEFAULT_CAPACITY); \
else if ((RING_BUFFER_T(P)->head + RING_BUFFER_T(P)->capacity - 1) % RING_BUFFER_T(P)->capacity == RING_BUFFER_T(P)->tail) \
(P) = CARR_ring_buffer_realloc(RING_BUFFER_T(P), sizeof((P)[0]), ARRAY_CAPACITY_GROW(RING_BUFFER_T(P)->capacity)); \
if ((P) == NULL) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
RING_BUFFER_T(P)->head = (RING_BUFFER_T(P)->head + RING_BUFFER_T(P)->capacity - 1) % RING_BUFFER_T(P)->capacity; \
(P)[RING_BUFFER_T(P)->head] = (__VA_ARGS__); \
} while(0)
#define RING_BUFFER_PUSH_FRONT(P) \
((RING_BUFFER_ENSURE_CAN_PUSH(P), (P) + CARR_ring_buffer_push_front(P))->CARR_elem)
/**
* Add element to the end of the ring buffer. Implicitly initializes when buffer is NULL.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P pointer to the first data element of the buffer
* @param P ring buffer
* @return dereferenced pointer to the inserted element
*/
#define RING_BUFFER_PUSH_BACK(P, ...) do { \
if ((P) == NULL) (P) = CARR_ring_buffer_realloc(NULL, sizeof((P)[0]), ARRAY_DEFAULT_CAPACITY); \
else if ((RING_BUFFER_T(P)->tail + 1) % RING_BUFFER_T(P)->capacity == RING_BUFFER_T(P)->head) \
(P) = CARR_ring_buffer_realloc(RING_BUFFER_T(P), sizeof((P)[0]), ARRAY_CAPACITY_GROW(RING_BUFFER_T(P)->capacity)); \
if ((P) == NULL) C_ARRAY_UTIL_ALLOCATION_FAILED(); \
(P)[RING_BUFFER_T(P)->tail] = (__VA_ARGS__); \
RING_BUFFER_T(P)->tail = (RING_BUFFER_T(P)->tail + 1) % RING_BUFFER_T(P)->capacity; \
} while(0)
#define RING_BUFFER_PUSH_BACK(P) \
((RING_BUFFER_ENSURE_CAN_PUSH(P), (P) + CARR_ring_buffer_push_back(P))->CARR_elem)
/**
* Get pointer to the first element of the ring buffer.
* @param P pointer to the first data element of the buffer
* @param P ring buffer
* @return pointer to the first element of the ring buffer, or NULL
*/
#define RING_BUFFER_FRONT(P) ((P) == NULL || RING_BUFFER_T(P)->head == RING_BUFFER_T(P)->tail ? NULL : &(P)[RING_BUFFER_T(P)->head])
#define RING_BUFFER_FRONT(P) (CARR_RING_BUFFER_IS_NULL(P) || \
CARR_RING_BUFFER_T(P)->head == CARR_RING_BUFFER_T(P)->tail ? NULL : &(P)[CARR_RING_BUFFER_T(P)->head].CARR_elem)
/**
* Get pointer to the last element of the ring buffer.
* @param P pointer to the first data element of the buffer
* @param P ring buffer
* @return pointer to the last element of the ring buffer, or NULL
*/
#define RING_BUFFER_BACK(P) ((P) == NULL || RING_BUFFER_T(P)->head == RING_BUFFER_T(P)->tail ? NULL : \
&(P)[(RING_BUFFER_T(P)->tail + RING_BUFFER_T(P)->capacity - 1) % RING_BUFFER_T(P)->capacity])
#define RING_BUFFER_BACK(P) (CARR_RING_BUFFER_IS_NULL(P) || \
CARR_RING_BUFFER_T(P)->head == CARR_RING_BUFFER_T(P)->tail ? NULL : \
&(P)[(CARR_RING_BUFFER_T(P)->tail+CARR_RING_BUFFER_T(P)->capacity-1) % CARR_RING_BUFFER_T(P)->capacity].CARR_elem)
/**
* Move beginning of the ring buffer forward (remove first element).
* @param P pointer to the first data element of the buffer
* @param P ring buffer
*/
#define RING_BUFFER_POP_FRONT(P) RING_BUFFER_T(P)->head = (RING_BUFFER_T(P)->head + 1) % RING_BUFFER_T(P)->capacity
#define RING_BUFFER_POP_FRONT(P) CARR_RING_BUFFER_GUARD((P), (void)(CARR_RING_BUFFER_T(P)->head = \
(CARR_RING_BUFFER_T(P)->head + 1) % CARR_RING_BUFFER_T(P)->capacity))
/**
* Move end of the ring buffer backward (remove last element).
* @param P pointer to the first data element of the buffer
* @param P ring buffer
*/
#define RING_BUFFER_POP_BACK(P) \
RING_BUFFER_T(P)->tail = (RING_BUFFER_T(P)->tail + RING_BUFFER_T(P)->capacity - 1) % RING_BUFFER_T(P)->capacity
#define RING_BUFFER_POP_BACK(P) CARR_RING_BUFFER_GUARD((P), (void)(CARR_RING_BUFFER_T(P)->tail = \
(CARR_RING_BUFFER_T(P)->tail + CARR_RING_BUFFER_T(P)->capacity - 1) % CARR_RING_BUFFER_T(P)->capacity))
/**
* Deallocate the ring buffer
* @param P pointer to the first data element of the buffer
* @param P ring buffer
*/
#define RING_BUFFER_FREE(P) free(RING_BUFFER_T(P))
#define RING_BUFFER_FREE(P) CARR_RING_BUFFER_GUARD((P), \
(void)CARR_ring_buffer_realloc((void**)&(P), alignof(*(P)), sizeof(*(P)), 0))
#endif // CARRAYUTILS_H
// === Maps ===
typedef bool (*CARR_equals_fp)(const void* a, const void* b);
typedef size_t (*CARR_hash_fp)(const void* data);
#define CARR_MAP_LAYOUT_ARGS size_t key_alignment, size_t key_size, size_t value_alignment, size_t value_size
#define CARR_MAP_LAYOUT_PASS key_alignment, key_size, value_alignment, value_size
#define CARR_MAP_LAYOUT(P) \
alignof((P)->CARR_keys[0].CARR_key[0]), sizeof((P)->CARR_keys[0].CARR_key[0]), \
alignof((P)->CARR_values[0].CARR_value[0]), sizeof((P)->CARR_values[0].CARR_value[0])
typedef const void* (*CARR_map_dispatch_next_key_fp)(CARR_MAP_LAYOUT_ARGS, const void* data, const void* key_slot);
typedef void* (*CARR_map_dispatch_find_fp)(CARR_MAP_LAYOUT_ARGS,
void* data, const void* key, const void** resolved_key, bool insert);
typedef bool (*CARR_map_dispatch_remove_fp)(CARR_MAP_LAYOUT_ARGS, void* data, const void* key);
typedef bool (*CARR_map_dispatch_ensure_extra_capacity_fp)(CARR_MAP_LAYOUT_ARGS, void** handle, size_t count);
typedef void (*CARR_map_dispatch_clear_fp)(CARR_MAP_LAYOUT_ARGS, void* data);
typedef void (*CARR_map_dispatch_free_fp)(CARR_MAP_LAYOUT_ARGS, void* data);
typedef struct {
CARR_map_dispatch_next_key_fp next_key;
CARR_map_dispatch_find_fp find;
CARR_map_dispatch_remove_fp remove;
CARR_map_dispatch_ensure_extra_capacity_fp ensure_extra_capacity;
CARR_map_dispatch_clear_fp clear;
CARR_map_dispatch_free_fp free;
} CARR_map_dispatch_t;
#define CARR_MAP_KEY_PTR(P, ...) \
(&((true ? NULL : (P))->CARR_keys[((uintptr_t)(__VA_ARGS__) / sizeof((P)->CARR_keys[0]) - 1)].CARR_key[0]))
#define CARR_MAP_VALUE_PTR(P, ...) \
(&((true ? NULL : (P))->CARR_values[((uintptr_t)(__VA_ARGS__) / sizeof((P)->CARR_values[0]) - 1)].CARR_value[0]))
#define CARR_MAP_KEY_GUARD(P, ...) \
(true ? (__VA_ARGS__) : &(P)->CARR_keys[0].CARR_key[0]) // Guard against wrong key types.
#define CARR_MAP_DISPATCH(P, NAME, ...) \
(((const CARR_map_dispatch_t**)(P))[-1]->NAME(CARR_MAP_LAYOUT(P), __VA_ARGS__))
bool CARR_hash_map_linear_probing_rehash(CARR_MAP_LAYOUT_ARGS, void** handle, CARR_equals_fp equals, CARR_hash_fp hash,
size_t new_capacity, uint32_t probing_limit, float load_factor);
/**
* Map declaration, e.g. MAP(int, int) my_map = NULL;
* Map must be explicitly initialized before usage, e.g. via HASH_MAP_REHASH.
* @param KEY_TYPE type of the map key.
* @param VALUE_TYPE type of the map value.
*/
#define MAP(KEY_TYPE, VALUE_TYPE) union { \
struct { char CARR_dummy; const KEY_TYPE CARR_key[]; } CARR_keys[1]; \
struct { char CARR_dummy; VALUE_TYPE CARR_value[]; } CARR_values[1]; \
}*
/**
* Rehash a hash map with given strategy. It will be initialized if NULL.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* List of available strategies with accepted parameters and sensible defaults:
* 1. linear_probing (
* CARR_equals_fp equals, // Key comparison function.
* CARR_hash_fp hash, // Key hash calculation function.
* size_t new_capacity, // New (min) capacity, must not be less than current number of items. Can be 0.
* uint32_t probing_limit, // Search length, triggering rehash. Must not be too low, around 10 should be fine?
* float load_factor // Min load factor needed to allow rehash triggered by probing_limit. 0.75 is fine.
* )
* @param P map
* @param STRATEGY strategy to use
* @param ... parameters for the rehash strategy
*/
#define HASH_MAP_REHASH(P, STRATEGY, ...) \
((void)CARR_handle_alloc(CARR_hash_map_##STRATEGY##_rehash(CARR_MAP_LAYOUT(P), (void**)&(P), __VA_ARGS__), true))
/**
* Rehash a hash map with given strategy. It will be initialized if NULL.
* On allocation failure, map is left unchanged.
* For list of available strategies see HASH_MAP_REHASH.
* @param P map
* @param STRATEGY strategy to use
* @return true if the operation succeeded
*/
#define HASH_MAP_TRY_REHASH(P, STRATEGY, ...) \
(CARR_hash_map_##STRATEGY##_rehash(CARR_MAP_LAYOUT(P), (void**)&(P), __VA_ARGS__))
/**
* Find the next resolved key present in the map, or NULL.
* Enumeration order is implementation-defined.
* @param P map
* @param KEY_PTR pointer to the current resolved key, or NULL
* @return pointer to the next resolved key
*/
#define MAP_NEXT_KEY(P, KEY_PTR) \
CARR_MAP_KEY_PTR((P), CARR_MAP_DISPATCH((P), next_key, (P), CARR_MAP_KEY_GUARD((P), (KEY_PTR))))
/**
* Find a value for the provided key.
* @param P map
* @param KEY 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))
/**
* 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}
* @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))))
/**
* Resolve provided key and find corresponding value.
* Using resolved key addresses speeds up subsequent map operations.
* @param P map
* @param KEY_PTR pointer to the key to find, replaced with resolved key address, or NULL
* @return pointer to the found value, or NULL
*/
#define MAP_RESOLVE(P, KEY_PTR) CARR_MAP_VALUE_PTR((P), \
CARR_MAP_DISPATCH((P), find, (P), CARR_MAP_KEY_GUARD((P), (KEY_PTR)), (const void**) &(KEY_PTR), false))
/**
* Resolve provided key and find corresponding value, or insert a new one.
* Using resolved key addresses speeds up subsequent map operations.
* Returned value pointer may be NULL, indicating that the entry was just inserted, use MAP_FIND or MAP_AT to access it.
* On allocation failure, map is left unchanged.
* @param P map
* @param KEY_PTR pointer to the key to find, replaced with resolved key address
* @return pointer to the found value, or NULL
*/
#define MAP_RESOLVE_OR_INSERT(P, KEY_PTR) (MAP_TRY_ENSURE_EXTRA_CAPACITY((P), 1), CARR_MAP_VALUE_PTR((P), \
CARR_MAP_DISPATCH((P), find, (P), CARR_MAP_KEY_GUARD((P), (KEY_PTR)), (const void**) &(KEY_PTR), true)))
/**
* Remove the provided key, if one exists.
* @param P map
* @param KEY 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)))
/**
* Ensure that map has enough capacity to insert COUNT more items without reallocation.
* On allocation failure, C_ARRAY_UTIL_ALLOCATION_FAILED is called.
* @param P map
* @param COUNT number of new items
*/
#define MAP_ENSURE_EXTRA_CAPACITY(P, COUNT) ((void)CARR_handle_alloc(MAP_TRY_ENSURE_EXTRA_CAPACITY((P), (COUNT)), true))
/**
* Ensure that map has enough capacity to insert COUNT more items without reallocation.
* On allocation failure, map is left unchanged.
* @param P map
* @param COUNT number of new items
* @return true if the operation succeeded
*/
#define MAP_TRY_ENSURE_EXTRA_CAPACITY(P, COUNT) CARR_MAP_DISPATCH((P), ensure_extra_capacity, (void**)&(P), (COUNT))
/**
* Clear the map.
* @param P map
*/
#define MAP_CLEAR(P) CARR_MAP_DISPATCH((P), clear, (P))
/**
* Free the map.
* @param P map
*/
#define MAP_FREE(P) ((P) == NULL ? 0 : CARR_MAP_DISPATCH((P), free, (P)), (void)((P) = NULL))
#endif // C_ARRAY_UTIL_H

View File

@@ -87,12 +87,12 @@ typedef union MemoryHandle {
#define MAX_SHARED_PAGE_SIZE ((1ULL << MAX_BLOCK_LEVEL) * BLOCK_SIZE)
typedef struct {
BlockPair* blockPairs;
void* mappedData;
uint32_t freeLevelIndices[MAX_BLOCK_LEVEL+1]; // Indices start from 1
uint32_t freeBlockPairIndex; // Indices start from 1
uint32_t nextPageIndex;
uint32_t memoryType;
ARRAY(BlockPair) blockPairs;
void* mappedData;
uint32_t freeLevelIndices[MAX_BLOCK_LEVEL+1]; // Indices start from 1
uint32_t freeBlockPairIndex; // Indices start from 1
uint32_t nextPageIndex;
uint32_t memoryType;
} SharedPageData;
typedef struct {
@@ -120,9 +120,9 @@ struct VKAllocator {
VKDevice* device;
VkPhysicalDeviceMemoryProperties memoryProperties;
Page* pages;
uint32_t freePageIndex;
Pool pools[VK_MAX_MEMORY_TYPES];
ARRAY(Page) pages;
uint32_t freePageIndex;
Pool pools[VK_MAX_MEMORY_TYPES];
};
#define NO_PAGE_INDEX (~0U)
@@ -222,7 +222,7 @@ static uint32_t VKAllocator_AllocatePage(VKAllocator* alloc, uint32_t memoryType
} else {
index = ARRAY_SIZE(alloc->pages);
VK_RUNTIME_ASSERT(index < MAX_PAGES);
ARRAY_PUSH_BACK(alloc->pages, (Page) {});
ARRAY_PUSH_BACK(alloc->pages) = (Page) {};
page = &ARRAY_LAST(alloc->pages);
}
assert(page->memory == VK_NULL_HANDLE);
@@ -275,7 +275,7 @@ static uint32_t VKAllocator_PopFreeBlockPair(SharedPageData* data, uint32_t leve
pair = &data->blockPairs[pairIndex-1];
data->freeBlockPairIndex = pair->nextFree;
} else {
ARRAY_PUSH_BACK(data->blockPairs, (BlockPair) {});
ARRAY_PUSH_BACK(data->blockPairs) = (BlockPair) {};
pairIndex = ARRAY_SIZE(data->blockPairs);
pair = &data->blockPairs[pairIndex-1];
}
@@ -369,7 +369,7 @@ static AllocationResult VKAllocator_AllocateForResource(VKMemoryRequirements* re
// Adjust level to ensure proper alignment. Not very optimal, but this is a very rare case.
while (blockSize % alignment != 0) { level++; blockSize <<= 1; }
J2dRlsTraceLn6(J2D_TRACE_VERBOSE,
J2dRlsTraceLn6(J2D_TRACE_VERBOSE2,
"VKAllocator_Allocate: level=%d, blockSize=%d, size=%d, alignment=%d, memoryType=%d, dedicated=%d",
level, blockSize, size, alignment, memoryType, dedicated);
@@ -401,13 +401,13 @@ static AllocationResult VKAllocator_AllocateForResource(VKMemoryRequirements* re
data = page->sharedPageData = (SharedPageData*) calloc(1, sizeof(SharedPageData));
VK_RUNTIME_ASSERT(page->sharedPageData);
data->memoryType = memoryType;
ARRAY_PUSH_BACK(data->blockPairs, (BlockPair) {
.offset = 0,
.parent = 0,
.nextFree = 0,
.firstFree = 1,
.secondFree = 0,
});
ARRAY_PUSH_BACK(data->blockPairs) = (BlockPair) {
.offset = 0,
.parent = 0,
.nextFree = 0,
.firstFree = 1,
.secondFree = 0,
};
data->freeLevelIndices[pageLevel] = 1;
data->nextPageIndex = pool->sharedPagesIndex;
pool->sharedPagesIndex = pageIndex;

View File

@@ -83,6 +83,8 @@ static void vulkanLibClose() {
}
#endif
VKComposites_Destroy(geInstance->composites);
if (geInstance->vkDestroyInstance != NULL) {
geInstance->vkDestroyInstance(geInstance->vkInstance, NULL);
}
@@ -205,13 +207,13 @@ static jboolean VK_InitGraphicsEnvironment(PFN_vkGetInstanceProcAddr vkGetInstan
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, " %s", (char *) extensions[i].extensionName)
}
pchar* enabledLayers = NULL;
pchar* enabledExtensions = NULL;
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);
ARRAY_PUSH_BACK(enabledExtensions) = VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
#endif
ARRAY_PUSH_BACK(enabledExtensions, VK_KHR_SURFACE_EXTENSION_NAME);
ARRAY_PUSH_BACK(enabledExtensions) = VK_KHR_SURFACE_EXTENSION_NAME;
// Check required layers & extensions.
for (uint32_t i = 0; i < ARRAY_SIZE(enabledExtensions); i++) {
@@ -264,8 +266,8 @@ static jboolean VK_InitGraphicsEnvironment(PFN_vkGetInstanceProcAddr vkGetInstan
}
if (foundDebugLayer && foundDebugExt) {
ARRAY_PUSH_BACK(enabledLayers, VALIDATION_LAYER_NAME);
ARRAY_PUSH_BACK(enabledExtensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
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",
@@ -302,6 +304,8 @@ static jboolean VK_InitGraphicsEnvironment(PFN_vkGetInstanceProcAddr vkGetInstan
ARRAY_FREE(enabledLayers);
ARRAY_FREE(enabledExtensions);
geInstance->composites = VKComposites_Create();
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
INSTANCE_PROC(vkGetPhysicalDeviceWaylandPresentationSupportKHR);
INSTANCE_PROC(vkCreateWaylandSurfaceKHR);
@@ -323,25 +327,26 @@ static jboolean VK_InitGraphicsEnvironment(PFN_vkGetInstanceProcAddr vkGetInstan
// Create debug messenger
#if defined(DEBUG)
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)) {}
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;
@@ -473,9 +478,9 @@ static jboolean VK_FindDevices() {
continue;
}
pchar* deviceEnabledLayers = NULL;
pchar* deviceEnabledExtensions = NULL;
ARRAY_PUSH_BACK(deviceEnabledExtensions, VK_KHR_SWAPCHAIN_EXTENSION_NAME);
ARRAY(pchar) deviceEnabledLayers = NULL;
ARRAY(pchar) deviceEnabledExtensions = NULL;
ARRAY_PUSH_BACK(deviceEnabledExtensions) = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
// Validation layer
#ifdef DEBUG
@@ -483,7 +488,7 @@ static jboolean VK_FindDevices() {
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);
ARRAY_PUSH_BACK(deviceEnabledLayers) = VALIDATION_LAYER_NAME;
break;
}
}
@@ -497,15 +502,14 @@ static jboolean VK_FindDevices() {
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
}));
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")
@@ -614,10 +618,14 @@ static jboolean VK_InitDevice(VKDevice* device) {
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);
@@ -625,7 +633,6 @@ static jboolean VK_InitDevice(VKDevice* device) {
DEVICE_PROC(vkCmdBindVertexBuffers);
DEVICE_PROC(vkCreateRenderPass);
DEVICE_PROC(vkDestroyRenderPass);
DEVICE_PROC(vkDestroyBuffer);
DEVICE_PROC(vkFreeMemory);
DEVICE_PROC(vkDestroyImageView);
DEVICE_PROC(vkDestroyImage);

View File

@@ -27,16 +27,18 @@
#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;
pchar* enabledLayers;
pchar* enabledExtensions;
ARRAY(pchar) enabledLayers;
ARRAY(pchar) enabledExtensions;
VkQueue queue;
VKAllocator* allocator;
@@ -92,10 +94,14 @@ struct VKDevice {
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;
@@ -103,7 +109,6 @@ struct VKDevice {
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
PFN_vkCreateRenderPass vkCreateRenderPass;
PFN_vkDestroyRenderPass vkDestroyRenderPass;
PFN_vkDestroyBuffer vkDestroyBuffer;
PFN_vkFreeMemory vkFreeMemory;
PFN_vkDestroyImageView vkDestroyImageView;
PFN_vkDestroyImage vkDestroyImage;
@@ -116,10 +121,12 @@ struct VKDevice {
};
struct VKGraphicsEnvironment {
VkInstance vkInstance;
VkPhysicalDevice* physicalDevices;
VKDevice* devices;
VKDevice* currentDevice;
VkInstance vkInstance;
ARRAY(VkPhysicalDevice) physicalDevices;
ARRAY(VKDevice) devices;
VKDevice* currentDevice;
VKComposites composites;
#if defined(DEBUG)
VkDebugUtilsMessengerEXT debugMessenger;

View File

@@ -37,7 +37,7 @@
#include "Trace.h"
#include "VKImage.h"
#include "VKBuffer.h"
#include "CArrayUtil.h"
#include "VKUtil.h"
static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context, VKImage* dest, const SurfaceDataRasInfo *srcInfo,
int dx1, int dy1, int dx2, int dy2) {
@@ -54,7 +54,7 @@ static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context, VKIma
return;
}
VKTxVertex* vertices = ARRAY_ALLOC(VKTxVertex, 4);
ARRAY(VKTxVertex) vertices = ARRAY_ALLOC(VKTxVertex, 4);
/*
* (p1)---------(p2)
* | |
@@ -67,10 +67,10 @@ static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context, VKIma
double u = (double)sw / VKTexturePoolHandle_GetActualWidth(hnd);
double v = (double)sh / VKTexturePoolHandle_GetActualHeight(hnd);
ARRAY_PUSH_BACK(vertices, ((VKTxVertex) {dx1, dy1, 0.0f, 0.0f}));
ARRAY_PUSH_BACK(vertices, ((VKTxVertex) {dx2, dy1, u, 0.0f}));
ARRAY_PUSH_BACK(vertices, ((VKTxVertex) {dx1, dy2, 0.0f, v}));
ARRAY_PUSH_BACK(vertices, ((VKTxVertex) {dx2, dy2, u, v}));
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx1, dy1, 0.0f, 0.0f};
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx2, dy1, u, 0.0f};
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx1, dy2, 0.0f, v};
ARRAY_PUSH_BACK(vertices) = (VKTxVertex) {dx2, dy2, u, v};
VKBuffer* renderVertexBuffer = ARRAY_TO_VERTEX_BUF(device, vertices);
ARRAY_FREE(vertices);
@@ -137,6 +137,128 @@ static void VKBlitSwToTextureViaPooledTexture(VKRenderingContext* context, VKIma
// 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);
}
static jboolean clipDestCoords(
VKRenderingContext* context,
jdouble *dx1, jdouble *dy1, jdouble *dx2, jdouble *dy2,
jint *sx1, jint *sy1, jint *sx2, jint *sy2,
jint destW, jint destH) {
// Trim destination rect by clip-rect (or dest.bounds)
const jint sw = *sx2 - *sx1;
const jint sh = *sy2 - *sy1;
const jdouble dw = *dx2 - *dx1;
const jdouble dh = *dy2 - *dy1;
VkRect2D* clipRect = &context->clipRect;
jdouble dcx1 = 0;
jdouble dcx2 = destW;
jdouble dcy1 = 0;
jdouble dcy2 = destH;
if (clipRect->offset.x > dcx1)
dcx1 = clipRect->offset.x;
const int maxX = clipRect->offset.x + clipRect->extent.width;
if (dcx2 > maxX)
dcx2 = maxX;
if (clipRect->offset.y > dcy1)
dcy1 = clipRect->offset.y;
const int maxY = clipRect->offset.y + clipRect->extent.height;
if (dcy2 > maxY)
dcy2 = maxY;
if (dcx1 >= dcx2) {
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcx1=%1.2f, dcx2=%1.2f", dcx1, dcx2);
dcx1 = dcx2;
}
if (dcy1 >= dcy2) {
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcy1=%1.2f, dcy2=%1.2f", dcy1, dcy2);
dcy1 = dcy2;
}
if (*dx2 <= dcx1 || *dx1 >= dcx2 || *dy2 <= dcy1 || *dy1 >= dcy2) {
J2dTraceLn(J2D_TRACE_INFO, "\tclipDestCoords: dest rect doesn't intersect clip area");
J2dTraceLn4(J2D_TRACE_INFO, "\tdx2=%1.4f <= dcx1=%1.4f || *dx1=%1.4f >= dcx2=%1.4f", *dx2, dcx1, *dx1, dcx2);
J2dTraceLn4(J2D_TRACE_INFO, "\t*dy2=%1.4f <= dcy1=%1.4f || *dy1=%1.4f >= dcy2=%1.4f", *dy2, dcy1, *dy1, dcy2);
return JNI_FALSE;
}
if (*dx1 < dcx1) {
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx1=%1.2f, will be clipped to %1.2f | sx1+=%d", *dx1, dcx1, (jint)((dcx1 - *dx1) * (sw/dw)));
*sx1 += (jint)((dcx1 - *dx1) * (sw/dw));
*dx1 = dcx1;
}
if (*dx2 > dcx2) {
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx2=%1.2f, will be clipped to %1.2f | sx2-=%d", *dx2, dcx2, (jint)((*dx2 - dcx2) * (sw/dw)));
*sx2 -= (jint)((*dx2 - dcx2) * (sw/dw));
*dx2 = dcx2;
}
if (*dy1 < dcy1) {
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy1=%1.2f, will be clipped to %1.2f | sy1+=%d", *dy1, dcy1, (jint)((dcy1 - *dy1) * (sh/dh)));
*sy1 += (jint)((dcy1 - *dy1) * (sh/dh));
*dy1 = dcy1;
}
if (*dy2 > dcy2) {
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy2=%1.2f, will be clipped to %1.2f | sy2-=%d", *dy2, dcy2, (jint)((*dy2 - dcy2) * (sh/dh)));
*sy2 -= (jint)((*dy2 - dcy2) * (sh/dh));
*dy2 = dcy2;
}
return JNI_TRUE;
}
void VKBlitLoops_IsoBlit(JNIEnv *env,
VKRenderingContext* context, jlong pSrcOps,
@@ -147,75 +269,81 @@ void VKBlitLoops_IsoBlit(JNIEnv *env,
jdouble dx1, jdouble dy1,
jdouble dx2, jdouble dy2)
{
J2dRlsTraceLn8(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT_IsoBlit (%d %d %d %d) -> (%f %f %f %f) ",
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, "VKRenderQueue_flushBuffer: BLIT_IsoBlit texture=%d xform=%d",
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "VKBlitLoops_IsoBlit: texture=%d xform=%d",
texture, xform)
}
static jboolean clipDestCoords(
VKRenderingContext* context,
jdouble *dx1, jdouble *dy1, jdouble *dx2, jdouble *dy2,
jint *sx1, jint *sy1, jint *sx2, jint *sy2,
jint destW, jint destH) {
// Trim destination rect by clip-rect (or dest.bounds)
const jint sw = *sx2 - *sx1;
const jint sh = *sy2 - *sy1;
const jdouble dw = *dx2 - *dx1;
const jdouble dh = *dy2 - *dy1;
VkRect2D* clipRect = &context->clipRect;
jdouble dcx1 = 0;
jdouble dcx2 = destW;
jdouble dcy1 = 0;
jdouble dcy2 = destH;
if (clipRect->offset.x > dcx1)
dcx1 = clipRect->offset.x;
const int maxX = clipRect->offset.x + clipRect->extent.width;
if (dcx2 > maxX)
dcx2 = maxX;
if (clipRect->offset.y > dcy1)
dcy1 = clipRect->offset.y;
const int maxY = clipRect->offset.y + clipRect->extent.height;
if (dcy2 > maxY)
dcy2 = maxY;
if (dcx1 >= dcx2) {
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcx1=%1.2f, dcx2=%1.2f", dcx1, dcx2);
dcx1 = dcx2;
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)
return;
}
if (dcy1 >= dcy2) {
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcy1=%1.2f, dcy2=%1.2f", dcy1, dcy2);
dcy1 = dcy2;
if (srcOps->image == NULL) {
J2dRlsTraceLn(J2D_TRACE_WARNING, "VKBlitLoops_IsoBlit: srcOps->image is null");
return;
}
if (!VKRenderer_Validate(context, SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)) {
J2dTraceLn(J2D_TRACE_INFO, "VKBlitLoops_IsoBlit: VKRenderer_Validate cannot validate renderer");
return;
}
// TODO: check if srctype is supported
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);
}
}
if (*dx2 <= dcx1 || *dx1 >= dcx2 || *dy2 <= dcy1 || *dy1 >= dcy2) {
J2dTraceLn(J2D_TRACE_INFO, "\tclipDestCoords: dest rect doesn't intersect clip area");
J2dTraceLn4(J2D_TRACE_INFO, "\tdx2=%1.4f <= dcx1=%1.4f || *dx1=%1.4f >= dcx2=%1.4f", *dx2, dcx1, *dx1, dcx2);
J2dTraceLn4(J2D_TRACE_INFO, "\t*dy2=%1.4f <= dcy1=%1.4f || *dy1=%1.4f >= dcy2=%1.4f", *dy2, dcy1, *dy1, dcy2);
return JNI_FALSE;
}
if (*dx1 < dcx1) {
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx1=%1.2f, will be clipped to %1.2f | sx1+=%d", *dx1, dcx1, (jint)((dcx1 - *dx1) * (sw/dw)));
*sx1 += (jint)((dcx1 - *dx1) * (sw/dw));
*dx1 = dcx1;
}
if (*dx2 > dcx2) {
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx2=%1.2f, will be clipped to %1.2f | sx2-=%d", *dx2, dcx2, (jint)((*dx2 - dcx2) * (sw/dw)));
*sx2 -= (jint)((*dx2 - dcx2) * (sw/dw));
*dx2 = dcx2;
}
if (*dy1 < dcy1) {
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy1=%1.2f, will be clipped to %1.2f | sy1+=%d", *dy1, dcy1, (jint)((dcy1 - *dy1) * (sh/dh)));
*sy1 += (jint)((dcy1 - *dy1) * (sh/dh));
*dy1 = dcy1;
}
if (*dy2 > dcy2) {
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy2=%1.2f, will be clipped to %1.2f | sy2-=%d", *dy2, dcy2, (jint)((*dy2 - dcy2) * (sh/dh)));
*sy2 -= (jint)((*dy2 - dcy2) * (sh/dh));
*dy2 = dcy2;
}
return JNI_TRUE;
}
void VKBlitLoops_Blit(JNIEnv *env,
VKRenderingContext* context, jlong pSrcOps,
jboolean xform, jint hint,
@@ -240,7 +368,7 @@ void VKBlitLoops_Blit(JNIEnv *env,
}
if (!VKRenderer_Validate(context, PIPELINE_BLIT)) {
if (!VKRenderer_Validate(context, SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)) {
J2dTraceLn(J2D_TRACE_INFO, "VKBlitLoops_Blit: VKRenderer_Validate cannot validate renderer");
return;
}
@@ -333,8 +461,8 @@ VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context,
SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);
SurfaceDataRasInfo srcInfo, dstInfo;
J2dTraceLn6(J2D_TRACE_INFO, "VKBlitLoops_SurfaceToSwBlit: (%d %d %d %d) -> (%d %d)",
srcx, srcy, width, height, dstx, dsty);
J2dTraceLn8(J2D_TRACE_INFO, "VKBlitLoops_SurfaceToSwBlit: (%p) (%d %d %d %d) -> (%p) (%d %d)",
srcOps, srcx, srcy, width, height, dstOps, dstx, dsty);
if (width <= 0 || height <= 0) {
J2dTraceLn(J2D_TRACE_WARNING,

View File

@@ -106,6 +106,77 @@ VKMemory VKBuffer_CreateBuffers(VKDevice* device, VkBufferUsageFlags usageFlags,
return page;
}
static VkDescriptorPool VKBuffer_DestroyTexelBuffersOnFailure(VKDevice* device, VkDescriptorPool pool, uint32_t bufferCount, VKTexelBuffer* texelBuffers) {
assert(device != NULL);
for (uint32_t i = 0; i < bufferCount; i++) {
device->vkDestroyBufferView(device->handle, texelBuffers[i].view, NULL);
}
device->vkDestroyDescriptorPool(device->handle, pool, NULL);
return VK_NULL_HANDLE;
}
VkDescriptorPool VKBuffer_CreateTexelBuffers(VKDevice* device, VkFormat format,
VkDescriptorType descriptorType, VkDescriptorSetLayout descriptorSetLayout,
uint32_t bufferCount, VKBuffer* buffers, VKTexelBuffer* texelBuffers) {
assert(device != NULL);
// Create descriptor pool.
VkDescriptorPoolSize poolSize = { .type = descriptorType, .descriptorCount = bufferCount };
VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.flags = 0,
.maxSets = bufferCount,
.poolSizeCount = 1,
.pPoolSizes = &poolSize
};
VkDescriptorPool pool;
VK_IF_ERROR(device->vkCreateDescriptorPool(device->handle, &descriptorPoolCreateInfo, NULL, &pool)) return VK_NULL_HANDLE;
// Allocate descriptor sets.
VkDescriptorSetLayout layouts[bufferCount];
for (uint32_t i = 0; i < bufferCount; i++) layouts[i] = descriptorSetLayout;
VkDescriptorSetAllocateInfo allocateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = pool,
.descriptorSetCount = bufferCount,
.pSetLayouts = layouts
};
VkDescriptorSet descriptorSets[bufferCount];
VK_IF_ERROR(device->vkAllocateDescriptorSets(device->handle, &allocateInfo, descriptorSets)) {
return VKBuffer_DestroyTexelBuffersOnFailure(device, pool, 0, texelBuffers);
}
// Create buffer views and record them into descriptors.
VkBufferViewCreateInfo bufferViewCreateInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO,
.format = format,
.offset = 0,
.range = VK_WHOLE_SIZE
};
VkWriteDescriptorSet writeDescriptorSets[bufferCount];
for (uint32_t i = 0; i < bufferCount; i++) {
texelBuffers[i] = (VKTexelBuffer) {
.buffer = buffers[i],
.descriptorSet = descriptorSets[i]
};
bufferViewCreateInfo.buffer = buffers[i].handle;
VK_IF_ERROR(device->vkCreateBufferView(device->handle, &bufferViewCreateInfo, NULL, &texelBuffers[i].view)) {
return VKBuffer_DestroyTexelBuffersOnFailure(device, pool, i, texelBuffers);
}
writeDescriptorSets[i] = (VkWriteDescriptorSet) {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptorSets[i],
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = descriptorType,
.pTexelBufferView = &texelBuffers[i].view
};
}
device->vkUpdateDescriptorSets(device->handle, bufferCount, writeDescriptorSets, 0, NULL);
return pool;
}
VKBuffer* VKBuffer_Create(VKDevice* device, VkDeviceSize size,
VkBufferUsageFlags usage, VkMemoryPropertyFlags properties)
{

View File

@@ -42,6 +42,12 @@ struct VKBuffer {
void* data;
};
struct VKTexelBuffer {
VKBuffer buffer;
VkBufferView view;
VkDescriptorSet descriptorSet;
};
/**
* 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.
@@ -54,6 +60,16 @@ VKMemory VKBuffer_CreateBuffers(VKDevice* device, VkBufferUsageFlags usageFlags,
VkDeviceSize bufferSize, VkDeviceSize pageSize,
uint32_t* bufferCount, VKBuffer* buffers);
/**
* Create texel buffers from existing array of buffers.
* It returns created descriptor pool, or VK_NULL_HANDLE on failure.
* Created texel buffers are written in 'texelBuffers',
* original buffers are taken from 'buffers'.
*/
VkDescriptorPool VKBuffer_CreateTexelBuffers(VKDevice* device, VkFormat format,
VkDescriptorType descriptorType, VkDescriptorSetLayout descriptorSetLayout,
uint32_t bufferCount, VKBuffer* buffers, VKTexelBuffer* texelBuffers);
// TODO usage of this function is suboptimal, we need to avoid creating individual buffers.
VKBuffer* VKBuffer_Create(VKDevice* device, VkDeviceSize size,
VkBufferUsageFlags usage, VkMemoryPropertyFlags properties);

View File

@@ -0,0 +1,95 @@
// 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 "VKComposites.h"
#define ALPHA_BLEND(NAME, SRC_COLOR, DST_COLOR, SRC_ALPHA, DST_ALPHA) \
VKComposites_AddState(&map, ALPHA_COMPOSITE_ ## NAME, (VKCompositeState) \
{{ .blendEnable = VK_TRUE, \
.srcColorBlendFactor = VK_BLEND_FACTOR_ ## SRC_COLOR, \
.dstColorBlendFactor = VK_BLEND_FACTOR_ ## DST_COLOR, \
.colorBlendOp = VK_BLEND_OP_ADD, \
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ ## SRC_ALPHA, \
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ ## DST_ALPHA, \
.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 }})
static size_t hash(const void* ptr) {
return (size_t) *(VKCompositeMode*)ptr;
}
static bool equals(const void* ap, const void* bp) {
return *(VKCompositeMode*)ap == *(VKCompositeMode*)bp;
}
VKComposites VKComposites_Create() {
VKComposites map = NULL;
HASH_MAP_REHASH(map, linear_probing, &equals, &hash, ALPHA_COMPOSITE_GROUP + 2, 10, 0.75);
VKComposites_AddState(&map, 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 }});
// NAME | SRC_COLOR | DST_COLOR | SRC_ALPHA | DST_ALPHA ||
ALPHA_BLEND( CLEAR , ZERO , ZERO , ZERO , ZERO );
ALPHA_BLEND( SRC , ONE , ZERO , ONE , ZERO );
ALPHA_BLEND( SRC_OVER , ONE , ONE_MINUS_SRC_ALPHA , ONE , ONE_MINUS_SRC_ALPHA );
ALPHA_BLEND( DST_OVER , ONE_MINUS_DST_ALPHA , ONE , ONE_MINUS_DST_ALPHA , ONE );
ALPHA_BLEND( SRC_IN , DST_ALPHA , ZERO , DST_ALPHA , ZERO );
ALPHA_BLEND( DST_IN , ZERO , SRC_ALPHA , ZERO , SRC_ALPHA );
ALPHA_BLEND( SRC_OUT , ONE_MINUS_DST_ALPHA , ZERO , ONE_MINUS_DST_ALPHA , ZERO );
ALPHA_BLEND( DST_OUT , ZERO , ONE_MINUS_SRC_ALPHA , ZERO , ONE_MINUS_SRC_ALPHA );
ALPHA_BLEND( DST , ZERO , ONE , ZERO , ONE );
ALPHA_BLEND( SRC_ATOP , DST_ALPHA , ONE_MINUS_SRC_ALPHA , ZERO , ONE );
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) {
{ .blendEnable = VK_FALSE,
.colorWriteMask = 0 }, // For stencil-only operations.
{ .logicOpEnable = VK_FALSE }});
return map;
}
void VKComposites_Destroy(VKComposites composites) {
MAP_FREE(composites);
}
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;
}
const VKCompositeState* VKComposites_GetState(VKComposites* composites, VKCompositeMode mode) {
VKCompositeState* state = MAP_FIND(*composites, mode);
state->blendState.pAttachments = &state->attachmentState;
return state;
}

View File

@@ -0,0 +1,70 @@
// 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 VKComposites_h_Included
#define VKComposites_h_Included
#include "java_awt_AlphaComposite.h"
#include "VKUtil.h"
/**
* There are two groups of composite modes:
* - Logic composite - using logicOp.
* - Alpha compisite - using blending.
*/
typedef enum {
LOGIC_COMPOSITE_XOR = 0,
LOGIC_COMPOSITE_GROUP = LOGIC_COMPOSITE_XOR,
ALPHA_COMPOSITE_CLEAR = java_awt_AlphaComposite_CLEAR,
ALPHA_COMPOSITE_SRC = java_awt_AlphaComposite_SRC,
ALPHA_COMPOSITE_DST = java_awt_AlphaComposite_DST,
ALPHA_COMPOSITE_SRC_OVER = java_awt_AlphaComposite_SRC_OVER,
ALPHA_COMPOSITE_DST_OVER = java_awt_AlphaComposite_DST_OVER,
ALPHA_COMPOSITE_SRC_IN = java_awt_AlphaComposite_SRC_IN,
ALPHA_COMPOSITE_DST_IN = java_awt_AlphaComposite_DST_IN,
ALPHA_COMPOSITE_SRC_OUT = java_awt_AlphaComposite_SRC_OUT,
ALPHA_COMPOSITE_DST_OUT = java_awt_AlphaComposite_DST_OUT,
ALPHA_COMPOSITE_SRC_ATOP = java_awt_AlphaComposite_SRC_ATOP,
ALPHA_COMPOSITE_DST_ATOP = java_awt_AlphaComposite_DST_ATOP,
ALPHA_COMPOSITE_XOR = java_awt_AlphaComposite_XOR,
ALPHA_COMPOSITE_GROUP = ALPHA_COMPOSITE_XOR,
NO_COMPOSITE = 0x7FFFFFFF
} VKCompositeMode;
#define COMPOSITE_GROUP(COMPOSITE) ( \
(COMPOSITE) <= LOGIC_COMPOSITE_GROUP ? LOGIC_COMPOSITE_GROUP : \
(COMPOSITE) <= ALPHA_COMPOSITE_GROUP ? ALPHA_COMPOSITE_GROUP : \
NO_COMPOSITE )
typedef struct {
VkPipelineColorBlendAttachmentState attachmentState;
VkPipelineColorBlendStateCreateInfo blendState;
} VKCompositeState;
typedef MAP(VKCompositeMode, VKCompositeState) 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);
#endif //VKComposites_h_Included

View File

@@ -40,7 +40,7 @@ static VkBool32 VKImage_CreateView(VKDevice* device, VKImage* image) {
.image = image->handle,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = image->format,
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.aspectMask = VKImage_GetAspect(image),
.subresourceRange.baseMipLevel = 0,
.subresourceRange.levelCount = 1,
.subresourceRange.baseArrayLayer = 0,
@@ -53,6 +53,11 @@ static VkBool32 VKImage_CreateView(VKDevice* device, VKImage* image) {
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;
}
VKImage* VKImage_Create(VKDevice* device, uint32_t width, uint32_t height,
VkImageCreateFlags flags, VkFormat format,
VkImageTiling tiling, VkImageUsageFlags usage, VkSampleCountFlagBits samples,

View File

@@ -42,6 +42,8 @@ struct VKImage {
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,

View File

@@ -33,9 +33,29 @@
#undef SHADER_ENTRY
#undef BYTECODE_END
typedef struct VKPipelineSet {
VkPipeline pipelines[PIPELINE_COUNT];
} VKPipelineSet;
inline void hash(uint32_t* result, int i) { // Good for hashing enums.
uint32_t x = (uint32_t) i;
x = ((x >> 16U) ^ x) * 0x45d9f3bU;
x = ((x >> 16U) ^ x) * 0x45d9f3bU;
x = (x >> 16U) ^ x;
*result ^= x + 0x9e3779b9U + (*result << 6U) + (*result >> 2U);
}
static size_t pipelineDescriptorHash(const void* ptr) {
const VKPipelineDescriptor* d = ptr;
uint32_t h = 0U;
hash(&h, d->stencilMode);
hash(&h, d->composite);
hash(&h, d->shader);
hash(&h, d->topology);
return (size_t) h;
}
static bool pipelineDescriptorEquals(const void* ap, const void* bp) {
const VKPipelineDescriptor *a = ap, *b = bp;
return a->stencilMode == b->stencilMode &&
a->composite == b->composite &&
a->shader == b->shader &&
a->topology == b->topology;
}
typedef struct VKShaders {
# define SHADER_ENTRY(NAME, TYPE) VkPipelineShaderStageCreateInfo NAME ## _ ## TYPE;
@@ -79,218 +99,183 @@ static VKShaders* VKPipelines_CreateShaders(VKDevice* device) {
return shaders;
}
#define MAKE_INPUT_STATE(NAME, TYPE, ...) \
static const VkVertexInputAttributeDescription INPUT_STATE_ATTRIBUTES_##NAME[] = { __VA_ARGS__ }; \
static const VkVertexInputBindingDescription INPUT_STATE_BINDING_##NAME = { \
.binding = 0, \
.stride = sizeof(TYPE), \
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX \
}; \
static const VkPipelineVertexInputStateCreateInfo INPUT_STATE_##NAME = { \
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, \
.vertexBindingDescriptionCount = 1, \
.pVertexBindingDescriptions = &INPUT_STATE_BINDING_##NAME, \
.vertexAttributeDescriptionCount = SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTES_##NAME), \
.pVertexAttributeDescriptions = INPUT_STATE_ATTRIBUTES_##NAME \
}
#define MAKE_INPUT_STATE(NAME, TYPE, ...) \
const VkFormat INPUT_STATE_ATTRIBUTE_FORMATS_##NAME[] = { __VA_ARGS__ }; \
VkVertexInputAttributeDescription \
INPUT_STATE_ATTRIBUTES_##NAME[SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTE_FORMATS_##NAME)]; \
const VkVertexInputBindingDescription INPUT_STATE_BINDING_##NAME = { \
.binding = 0, \
.stride = sizeof(TYPE), \
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX \
}; \
const VkPipelineVertexInputStateCreateInfo INPUT_STATE_##NAME = { \
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, \
.vertexBindingDescriptionCount = 1, \
.pVertexBindingDescriptions = &INPUT_STATE_BINDING_##NAME, \
.vertexAttributeDescriptionCount = SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTES_##NAME), \
.pVertexAttributeDescriptions = INPUT_STATE_ATTRIBUTES_##NAME \
}; \
uint32_t INPUT_STATE_BINDING_SIZE_##NAME = 0; \
for (uint32_t i = 0; i < SARRAY_COUNT_OF(INPUT_STATE_ATTRIBUTES_##NAME); i++) { \
INPUT_STATE_ATTRIBUTES_##NAME[i] = (VkVertexInputAttributeDescription) { \
i, 0, INPUT_STATE_ATTRIBUTE_FORMATS_##NAME[i], INPUT_STATE_BINDING_SIZE_##NAME}; \
INPUT_STATE_BINDING_SIZE_##NAME += \
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)
typedef struct {
VkGraphicsPipelineCreateInfo createInfo;
VkPipelineMultisampleStateCreateInfo multisampleState;
VkPipelineColorBlendStateCreateInfo colorBlendState;
VkPipelineDynamicStateCreateInfo dynamicState;
VkDynamicState dynamicStates[2];
} PipelineCreateState;
static VkPipeline 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;
typedef struct {
VkPipelineShaderStageCreateInfo createInfos[2]; // vert + frag
} ShaderStages;
/**
* Init default pipeline state. Some members are left uninitialized:
* - pStages (but stageCount is set to 2)
* - pVertexInputState
* - pInputAssemblyState
* - colorBlendState.pAttachments (but attachmentCount is set to 1)
* - createInfo.layout
* - createInfo.renderPass
* - renderingCreateInfo.pColorAttachmentFormats (but colorAttachmentCount is set to 1)
*/
static void VKPipelines_InitPipelineCreateState(PipelineCreateState* state) {
static const VkViewport viewport = {};
static const VkRect2D scissor = {};
static const VkPipelineViewportStateCreateInfo viewportState = {
// Setup pipeline creation structs.
static const uint32_t MAX_DYNAMIC_STATES = 2;
typedef struct {
VkPipelineShaderStageCreateInfo createInfos[2]; // vert + frag
} ShaderStages;
ShaderStages stages[count];
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++) {
// Init default pipeline state. Some members are left uninitialized:
// - pStages (but stageCount is set to 2)
// - pVertexInputState
// - createInfo.layout
inputAssemblyStates[i] = (VkPipelineInputAssemblyStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = descriptors[i].topology
};
static const VkViewport viewport = {};
static const VkRect2D scissor = {};
static const VkPipelineViewportStateCreateInfo viewportState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.pViewports = &viewport,
.scissorCount = 1,
.pScissors = &scissor
};
static const VkPipelineRasterizationStateCreateInfo rasterizationState = {
};
static const VkPipelineRasterizationStateCreateInfo rasterizationState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_NONE,
.lineWidth = 1.0f
};
state->multisampleState = (VkPipelineMultisampleStateCreateInfo) {
};
static const VkPipelineMultisampleStateCreateInfo multisampleState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
};
state->colorBlendState = (VkPipelineColorBlendStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_XOR,
.attachmentCount = 1,
.pAttachments = NULL,
};
state->dynamicState = (VkPipelineDynamicStateCreateInfo) {
};
static const VkStencilOpState stencilOpState = {
.failOp = VK_STENCIL_OP_KEEP,
.passOp = VK_STENCIL_OP_KEEP,
.compareOp = VK_COMPARE_OP_NOT_EQUAL,
.compareMask = 0xFFFFFFFFU,
.writeMask = 0U,
.reference = CLIP_STENCIL_EXCLUDE_VALUE
};
depthStencilStates[i] = (VkPipelineDepthStencilStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.stencilTestEnable = descriptors[i].stencilMode == STENCIL_MODE_ON,
.front = stencilOpState,
.back = stencilOpState
};
dynamicStates[i] = (VkPipelineDynamicStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = 2,
.pDynamicStates = state->dynamicStates
};
state->dynamicStates[0] = VK_DYNAMIC_STATE_VIEWPORT;
state->dynamicStates[1] = VK_DYNAMIC_STATE_SCISSOR;
state->createInfo = (VkGraphicsPipelineCreateInfo) {
.pDynamicStates = dynamicStateValues[i]
};
dynamicStateValues[i][0] = VK_DYNAMIC_STATE_VIEWPORT;
dynamicStateValues[i][1] = VK_DYNAMIC_STATE_SCISSOR;
createInfos[i] = (VkGraphicsPipelineCreateInfo) {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2,
.pStages = stages[i].createInfos,
.pInputAssemblyState = &inputAssemblyStates[i],
.pViewportState = &viewportState,
.pRasterizationState = &rasterizationState,
.pMultisampleState = &state->multisampleState,
.pColorBlendState = &state->colorBlendState,
.pDynamicState = &state->dynamicState,
.pMultisampleState = &multisampleState,
.pDepthStencilState = &depthStencilStates[i],
.pColorBlendState = &VKComposites_GetState(composites, descriptors[i].composite)->blendState,
.pDynamicState = &dynamicStates[i],
.renderPass = renderPassContext->renderPass[descriptors[i].stencilMode != STENCIL_MODE_NONE],
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = -1
};
}
static const VkPipelineInputAssemblyStateCreateInfo INPUT_ASSEMBLY_STATE_TRIANGLE_STRIP = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP
};
static const VkPipelineInputAssemblyStateCreateInfo INPUT_ASSEMBLY_STATE_TRIANGLE_LIST = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
};
static const VkPipelineInputAssemblyStateCreateInfo INPUT_ASSEMBLY_STATE_LINE_LIST = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST
};
// Blend states are hard-coded, but can also be loaded dynamically to implement custom composites.
#define DEF_BLEND(NAME, SRC_COLOR, DST_COLOR, SRC_ALPHA, DST_ALPHA) \
{ .blendEnable = VK_TRUE, \
.srcColorBlendFactor = VK_BLEND_FACTOR_ ## SRC_COLOR, \
.dstColorBlendFactor = VK_BLEND_FACTOR_ ## DST_COLOR, \
.colorBlendOp = VK_BLEND_OP_ADD, \
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ ## SRC_ALPHA, \
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ ## DST_ALPHA, \
.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 }
const VkPipelineColorBlendAttachmentState COMPOSITE_BLEND_STATES[COMPOSITE_COUNT] = {
{ .blendEnable = VK_FALSE, // LOGIC_XOR
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT },
// NAME || SRC_COLOR | DST_COLOR | SRC_ALPHA | DST_ALPHA ||
DEF_BLEND(| CLEAR |, ZERO , ZERO , ZERO , ZERO ),
DEF_BLEND(| SRC |, ONE , ZERO , ONE , ZERO ),
DEF_BLEND(| SRC_OVER |, ONE , ONE_MINUS_SRC_ALPHA , ONE , ONE_MINUS_SRC_ALPHA ),
DEF_BLEND(| DST_OVER |, ONE_MINUS_DST_ALPHA , ONE , ONE_MINUS_DST_ALPHA , ONE ),
DEF_BLEND(| SRC_IN |, DST_ALPHA , ZERO , DST_ALPHA , ZERO ),
DEF_BLEND(| DST_IN |, ZERO , SRC_ALPHA , ZERO , SRC_ALPHA ),
DEF_BLEND(| SRC_OUT |, ONE_MINUS_DST_ALPHA , ZERO , ONE_MINUS_DST_ALPHA , ZERO ),
DEF_BLEND(| DST_OUT |, ZERO , ONE_MINUS_SRC_ALPHA , ZERO , ONE_MINUS_SRC_ALPHA ),
DEF_BLEND(| DST |, ZERO , ONE , ZERO , ONE ),
DEF_BLEND(| SRC_ATOP |, DST_ALPHA , ONE_MINUS_SRC_ALPHA , ZERO , ONE ),
DEF_BLEND(| DST_ATOP |, ONE_MINUS_DST_ALPHA , SRC_ALPHA , ONE , ZERO ),
DEF_BLEND(| XOR |, ONE_MINUS_DST_ALPHA , ONE_MINUS_SRC_ALPHA , ONE_MINUS_DST_ALPHA , ONE_MINUS_SRC_ALPHA ),
};
static const VkVertexInputAttributeDescription INPUT_ATTRIBUTE_POSITION = {
.location = 0,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = 0
};
static const VkVertexInputAttributeDescription INPUT_ATTRIBUTE_COLOR = {
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = sizeof(float) * 2
};
static const VkVertexInputAttributeDescription INPUT_ATTRIBUTE_TEXCOORD = {
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = sizeof(float) * 2
};
MAKE_INPUT_STATE(COLOR_VERTEX, VKColorVertex, INPUT_ATTRIBUTE_POSITION, INPUT_ATTRIBUTE_COLOR);
MAKE_INPUT_STATE(TEXCOORD_VERTEX, VKTxVertex, INPUT_ATTRIBUTE_POSITION, INPUT_ATTRIBUTE_TEXCOORD);
static void VKPipelines_DestroyPipelineSet(VKDevice* device, VKPipelineSet* set) {
assert(device != NULL);
if (set == NULL) return;
for (uint32_t i = 0; i < PIPELINE_COUNT; i++) {
device->vkDestroyPipeline(device->handle, set->pipelines[i], NULL);
}
free(set);
}
static VKPipelineSet* VKPipelines_CreatePipelineSet(VKRenderPassContext* renderPassContext, VKCompositeMode composite) {
assert(renderPassContext != NULL && renderPassContext->pipelineContext != NULL);
assert(composite < COMPOSITE_COUNT);
VKPipelineContext* pipelineContext = renderPassContext->pipelineContext;
VKPipelineSet* set = calloc(1, sizeof(VKPipelineSet));
VK_RUNTIME_ASSERT(set);
VKDevice* device = pipelineContext->device;
VKShaders* shaders = pipelineContext->shaders;
// Setup default pipeline parameters.
PipelineCreateState base;
VKPipelines_InitPipelineCreateState(&base);
base.createInfo.layout = pipelineContext->pipelineLayout;
base.createInfo.renderPass = renderPassContext->renderPass;
base.colorBlendState.pAttachments = &COMPOSITE_BLEND_STATES[composite];
if (COMPOSITE_GROUP(composite) == LOGIC_COMPOSITE_GROUP) base.colorBlendState.logicOpEnable = VK_TRUE;
assert(base.dynamicState.dynamicStateCount <= SARRAY_COUNT_OF(base.dynamicStates));
ShaderStages stages[PIPELINE_COUNT];
VkGraphicsPipelineCreateInfo createInfos[PIPELINE_COUNT];
for (uint32_t i = 0; i < PIPELINE_COUNT; i++) {
createInfos[i] = base.createInfo;
createInfos[i].pStages = stages[i].createInfos;
};
}
{ // Setup plain color pipelines.
createInfos[PIPELINE_DRAW_COLOR].pVertexInputState = createInfos[PIPELINE_FILL_COLOR].pVertexInputState = &INPUT_STATE_COLOR_VERTEX;
createInfos[PIPELINE_FILL_COLOR].pInputAssemblyState = &INPUT_ASSEMBLY_STATE_TRIANGLE_LIST;
createInfos[PIPELINE_DRAW_COLOR].pInputAssemblyState = &INPUT_ASSEMBLY_STATE_LINE_LIST;
stages[PIPELINE_DRAW_COLOR] = stages[PIPELINE_FILL_COLOR] = (ShaderStages) {{ shaders->color_vert, shaders->color_frag }};
}
// Setup input states.
MAKE_INPUT_STATE(COLOR, VKColorVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT);
MAKE_INPUT_STATE(MASK_FILL_COLOR, VKMaskFillColorVertex, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32G32B32A32_SFLOAT);
MAKE_INPUT_STATE(BLIT, VKTxVertex, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32_SFLOAT);
MAKE_INPUT_STATE(CLIP, VKIntVertex, VK_FORMAT_R32G32_SINT);
{ // Setup texture pipeline.
createInfos[PIPELINE_BLIT].pVertexInputState = &INPUT_STATE_TEXCOORD_VERTEX;
createInfos[PIPELINE_BLIT].pInputAssemblyState = &INPUT_ASSEMBLY_STATE_TRIANGLE_STRIP;
createInfos[PIPELINE_BLIT].layout = pipelineContext->texturePipelineLayout;
stages[PIPELINE_BLIT] = (ShaderStages) {{ shaders->blit_vert, shaders->blit_frag }};
for (uint32_t i = 0; i < count; i++) {
// Setup shader-specific pipeline parameters.
switch (descriptors[i].shader) {
case SHADER_COLOR:
createInfos[i].pVertexInputState = &INPUT_STATE_COLOR;
createInfos[i].layout = pipelineContext->colorPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->color_vert, shaders->color_frag }};
break;
case SHADER_MASK_FILL_COLOR:
createInfos[i].pVertexInputState = &INPUT_STATE_MASK_FILL_COLOR;
createInfos[i].layout = pipelineContext->maskFillPipelineLayout;
stages[i] = (ShaderStages) {{ shaders->mask_fill_color_vert, shaders->mask_fill_color_frag }};
break;
case SHADER_BLIT:
createInfos[i].pVertexInputState = &INPUT_STATE_BLIT;
createInfos[i].layout = pipelineContext->texturePipelineLayout;
stages[i] = (ShaderStages) {{ shaders->blit_vert, shaders->blit_frag }};
break;
case SHADER_CLIP:
createInfos[i].pVertexInputState = &INPUT_STATE_CLIP;
static const VkStencilOpState CLIP_STENCIL_OP = {
.failOp = VK_STENCIL_OP_REPLACE,
.passOp = VK_STENCIL_OP_REPLACE,
.compareOp = VK_COMPARE_OP_NEVER,
.compareMask = 0U,
.writeMask = 0xFFFFFFFFU,
.reference = CLIP_STENCIL_INCLUDE_VALUE
};
static const VkPipelineDepthStencilStateCreateInfo CLIP_STENCIL_STATE = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.stencilTestEnable = VK_TRUE,
.front = CLIP_STENCIL_OP,
.back = CLIP_STENCIL_OP
};
createInfos[i].pDepthStencilState = &CLIP_STENCIL_STATE;
createInfos[i].layout = pipelineContext->texturePipelineLayout;
createInfos[i].stageCount = 1;
stages[i] = (ShaderStages) {{ shaders->clip_vert }};
break;
default:
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);
}
// Create pipelines.
// TODO pipeline cache
VK_IF_ERROR(device->vkCreateGraphicsPipelines(device->handle, VK_NULL_HANDLE, PIPELINE_COUNT,
createInfos, NULL, set->pipelines)) VK_UNHANDLED_ERROR();
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_CreatePipelineSet: composite=%d", composite);
return set;
VkPipeline pipelines[count];
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];
}
static VkResult VKPipelines_InitRenderPass(VKDevice* device, VKRenderPassContext* renderPassContext) {
static VkResult VKPipelines_InitRenderPasses(VKDevice* device, VKRenderPassContext* renderPassContext) {
assert(device != NULL && renderPassContext != NULL);
VkAttachmentDescription colorAttachment = {
VkAttachmentDescription attachments[] = {{
.format = renderPassContext->format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
@@ -299,47 +284,61 @@ static VkResult VKPipelines_InitRenderPass(VKDevice* device, VKRenderPassContext
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
}, {
.format = VK_FORMAT_S8_UINT,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE,
.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
}};
VkAttachmentReference colorReference = {
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
VkAttachmentReference stencilReference = {
.attachment = 1,
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
};
VkSubpassDescription subpassDescription = {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = 1,
.pColorAttachments = &colorReference
};
// TODO this is probably not needed?
// // Subpass dependencies for layout transitions
// VkSubpassDependency dependency = {
// .srcSubpass = VK_SUBPASS_EXTERNAL,
// .dstSubpass = 0,
// .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
// .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
// .srcAccessMask = 0,
// .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
// };
VkRenderPassCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &colorAttachment,
.pAttachments = attachments,
.subpassCount = 1,
.pSubpasses = &subpassDescription,
.dependencyCount = 0,
.pDependencies = NULL
};
return device->vkCreateRenderPass(device->handle, &createInfo, NULL, &renderPassContext->renderPass);
for (uint32_t i = 0; i < 2; i++) {
if (i == 1) {
createInfo.attachmentCount = 2;
subpassDescription.pDepthStencilAttachment = &stencilReference;
}
VkResult result = device->vkCreateRenderPass(device->handle, &createInfo, NULL, &renderPassContext->renderPass[i]);
VK_IF_ERROR(result) return result;
}
return VK_SUCCESS;
}
static void VKPipelines_DestroyRenderPassContext(VKRenderPassContext* renderPassContext) {
if (renderPassContext == NULL) return;
VKDevice* device = renderPassContext->pipelineContext->device;
assert(device != NULL);
for (uint32_t i = 0; i < ARRAY_SIZE(renderPassContext->pipelineSets); i++) {
VKPipelines_DestroyPipelineSet(device, renderPassContext->pipelineSets[i]);
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);
}
MAP_FREE(renderPassContext->pipelines);
for (uint32_t i = 0; i < 2; i++) {
device->vkDestroyRenderPass(device->handle, renderPassContext->renderPass[i], NULL);
}
ARRAY_FREE(renderPassContext->pipelineSets);
device->vkDestroyRenderPass(device->handle, renderPassContext->renderPass, NULL);
J2dRlsTraceLn2(J2D_TRACE_INFO, "VKPipelines_DestroyRenderPassContext(%p): format=%d",
renderPassContext, renderPassContext->format);
free(renderPassContext);
@@ -349,14 +348,18 @@ static VKRenderPassContext* VKPipelines_CreateRenderPassContext(VKPipelineContex
assert(pipelineContext != NULL && pipelineContext->device != NULL);
VKRenderPassContext* renderPassContext = calloc(1, sizeof(VKRenderPassContext));
VK_RUNTIME_ASSERT(renderPassContext);
HASH_MAP_REHASH(renderPassContext->pipelines, linear_probing,
&pipelineDescriptorEquals, &pipelineDescriptorHash, 0, 10, 0.75);
renderPassContext->pipelineContext = pipelineContext;
renderPassContext->format = format;
VK_IF_ERROR(VKPipelines_InitRenderPass(pipelineContext->device, renderPassContext)) {
VK_IF_ERROR(VKPipelines_InitRenderPasses(pipelineContext->device, renderPassContext)) {
VKPipelines_DestroyRenderPassContext(renderPassContext);
return NULL;
}
// TODO create few common pipelines in advance? Like default shaders for SRC_OVER composite.
J2dRlsTraceLn2(J2D_TRACE_INFO, "VKPipelines_CreateRenderPassContext(%p): format=%d", renderPassContext, format);
return renderPassContext;
}
@@ -377,7 +380,7 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pushConstantRange
};
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->pipelineLayout);
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->colorPipelineLayout);
VK_IF_ERROR(result) return result;
VkDescriptorSetLayoutBinding textureLayoutBinding = {
@@ -400,6 +403,26 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->texturePipelineLayout);
VK_IF_ERROR(result) return result;
VkDescriptorSetLayoutBinding maskBufferLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &maskBufferLayoutBinding
};
result = device->vkCreateDescriptorSetLayout(device->handle, &descriptorSetLayoutCreateInfo, NULL, &pipelines->maskFillDescriptorSetLayout);
VK_IF_ERROR(result) return result;
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &pipelines->maskFillDescriptorSetLayout;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->maskFillPipelineLayout);
VK_IF_ERROR(result) return result;
return VK_SUCCESS;
}
@@ -451,9 +474,11 @@ void VKPipelines_DestroyContext(VKPipelineContext* pipelineContext) {
VKPipelines_DestroyShaders(device, pipelineContext->shaders);
device->vkDestroySampler(device->handle, pipelineContext->linearRepeatSampler, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->pipelineLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->colorPipelineLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->texturePipelineLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->textureDescriptorSetLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->maskFillPipelineLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->maskFillDescriptorSetLayout, NULL);
J2dRlsTraceLn1(J2D_TRACE_INFO, "VKPipelines_DestroyContext(%p)", pipelineContext);
free(pipelineContext);
@@ -468,20 +493,15 @@ VKRenderPassContext* VKPipelines_GetRenderPassContext(VKPipelineContext* pipelin
}
// Not found, create.
VKRenderPassContext* renderPassContext = VKPipelines_CreateRenderPassContext(pipelineContext, format);
ARRAY_PUSH_BACK(pipelineContext->renderPassContexts, renderPassContext);
ARRAY_PUSH_BACK(pipelineContext->renderPassContexts) = renderPassContext;
return renderPassContext;
}
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKCompositeMode composite, VKPipeline pipeline) {
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKPipelineDescriptor descriptor) {
assert(renderPassContext != NULL);
assert(composite < COMPOSITE_COUNT); // We could append custom composites after that index.
assert(pipeline < PIPELINE_COUNT); // We could append custom pipelines after that index.
// Currently, our pipelines map to composite modes 1-to-1, but this may change in future when we'll add more states.
uint32_t setIndex = (uint32_t) composite;
while (ARRAY_SIZE(renderPassContext->pipelineSets) <= setIndex) ARRAY_PUSH_BACK(renderPassContext->pipelineSets, NULL);
if (renderPassContext->pipelineSets[setIndex] == NULL) {
renderPassContext->pipelineSets[setIndex] = VKPipelines_CreatePipelineSet(renderPassContext, composite);
VkPipeline pipeline = MAP_AT(renderPassContext->pipelines, descriptor);
if (pipeline == VK_NULL_HANDLE) {
pipeline = VKPipelines_CreatePipelines(renderPassContext, 1, &descriptor);
}
return renderPassContext->pipelineSets[setIndex]->pipelines[pipeline];
return pipeline;
}

View File

@@ -24,76 +24,72 @@
#ifndef VKPipelines_h_Included
#define VKPipelines_h_Included
#include "java_awt_AlphaComposite.h"
#include "VKComposites.h"
#include "VKTypes.h"
#include "VKUtil.h"
#define CLIP_STENCIL_INCLUDE_VALUE 0x80U
#define CLIP_STENCIL_EXCLUDE_VALUE 0U
/**
* All pipeline types.
* Shader programs.
*/
typedef enum {
PIPELINE_FILL_COLOR = 0,
PIPELINE_DRAW_COLOR = 1,
PIPELINE_BLIT = 2,
PIPELINE_COUNT = 3,
NO_PIPELINE = 0x7FFFFFFF
} VKPipeline;
SHADER_COLOR,
SHADER_MASK_FILL_COLOR,
SHADER_BLIT,
SHADER_CLIP,
NO_SHADER = 0x7FFFFFFF
} VKShader;
typedef enum {
STENCIL_MODE_NONE = 0, // No stencil attachment.
STENCIL_MODE_OFF = 1, // Has stencil attachment, stencil test disabled.
STENCIL_MODE_ON = 2 // Has stencil attachment, stencil test enabled.
} VKStencilMode;
/**
* There are two groups of composite modes:
* - Logic composite - using logicOp.
* - Alpha compisite - using blending.
* All features describing a pipeline.
* When adding new fields, update hash and comparison in VKPipelines.c.
*/
typedef enum {
LOGIC_COMPOSITE_XOR = 0,
LOGIC_COMPOSITE_GROUP = LOGIC_COMPOSITE_XOR,
ALPHA_COMPOSITE_CLEAR = java_awt_AlphaComposite_CLEAR,
ALPHA_COMPOSITE_SRC = java_awt_AlphaComposite_SRC,
ALPHA_COMPOSITE_DST = java_awt_AlphaComposite_DST,
ALPHA_COMPOSITE_SRC_OVER = java_awt_AlphaComposite_SRC_OVER,
ALPHA_COMPOSITE_DST_OVER = java_awt_AlphaComposite_DST_OVER,
ALPHA_COMPOSITE_SRC_IN = java_awt_AlphaComposite_SRC_IN,
ALPHA_COMPOSITE_DST_IN = java_awt_AlphaComposite_DST_IN,
ALPHA_COMPOSITE_SRC_OUT = java_awt_AlphaComposite_SRC_OUT,
ALPHA_COMPOSITE_DST_OUT = java_awt_AlphaComposite_DST_OUT,
ALPHA_COMPOSITE_SRC_ATOP = java_awt_AlphaComposite_SRC_ATOP,
ALPHA_COMPOSITE_DST_ATOP = java_awt_AlphaComposite_DST_ATOP,
ALPHA_COMPOSITE_XOR = java_awt_AlphaComposite_XOR,
ALPHA_COMPOSITE_GROUP = ALPHA_COMPOSITE_XOR,
COMPOSITE_COUNT = ALPHA_COMPOSITE_GROUP + 1,
NO_COMPOSITE = 0x7FFFFFFF
} VKCompositeMode;
#define COMPOSITE_GROUP(COMPOSITE) ( \
(COMPOSITE) <= LOGIC_COMPOSITE_GROUP ? LOGIC_COMPOSITE_GROUP : \
(COMPOSITE) <= ALPHA_COMPOSITE_GROUP ? ALPHA_COMPOSITE_GROUP : \
NO_COMPOSITE )
extern const VkPipelineColorBlendAttachmentState COMPOSITE_BLEND_STATES[COMPOSITE_COUNT];
typedef struct {
VKStencilMode stencilMode;
VKCompositeMode composite;
VKShader shader;
VkPrimitiveTopology topology;
} VKPipelineDescriptor;
/**
* Global pipeline context.
*/
struct VKPipelineContext {
VKDevice* device;
VkPipelineLayout pipelineLayout;
VkPipelineLayout texturePipelineLayout;
VkDescriptorSetLayout textureDescriptorSetLayout;
VKDevice* device;
VkPipelineLayout colorPipelineLayout;
VkDescriptorSetLayout textureDescriptorSetLayout;
VkPipelineLayout texturePipelineLayout;
VkDescriptorSetLayout maskFillDescriptorSetLayout;
VkPipelineLayout maskFillPipelineLayout;
VkSampler linearRepeatSampler;
VkSampler linearRepeatSampler;
struct VKShaders* shaders;
VKRenderPassContext** renderPassContexts;
struct VKShaders* shaders;
ARRAY(VKRenderPassContext*) renderPassContexts;
};
/**
* Per-format context.
*/
struct VKRenderPassContext {
VKPipelineContext* pipelineContext;
VkFormat format;
VkRenderPass renderPass;
struct VKPipelineSet** pipelineSets; // TODO we will need a real hash map for this in the future.
VKPipelineContext* pipelineContext;
VkFormat format;
VkRenderPass renderPass[2]; // Color-only and color+stencil.
MAP(VKPipelineDescriptor, VkPipeline) pipelines;
};
typedef struct {
int x, y;
} VKIntVertex;
typedef struct {
float x, y;
Color color;
@@ -104,10 +100,15 @@ typedef struct {
float u, v;
} VKTxVertex;
typedef struct {
int x, y, maskOffset, maskScanline;
Color color;
} VKMaskFillColorVertex;
VKPipelineContext* VKPipelines_CreateContext(VKDevice* device);
void VKPipelines_DestroyContext(VKPipelineContext* pipelines);
VKRenderPassContext* VKPipelines_GetRenderPassContext(VKPipelineContext* pipelineContext, VkFormat format);
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKCompositeMode composite, VKPipeline pipeline);
VkPipeline VKPipelines_GetPipeline(VKRenderPassContext* renderPassContext, VKPipelineDescriptor descriptor);
#endif //VKPipelines_h_Included

View File

@@ -26,9 +26,11 @@
#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 "fontscalerdefs.h"
#include "Trace.h"
#include "jlong.h"
#include "VKBase.h"
@@ -91,15 +93,19 @@
#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},
.clipRect = {{0, 0},{INT_MAX, INT_MAX}},
.color = {},
.composite = ALPHA_COMPOSITE_SRC_OVER,
.extraAlpha = 1.0f
.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.
@@ -156,7 +162,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, PIPELINE_DRAW_COLOR, x, y, w, h);
VKRenderer_RenderRect(&context, VK_FALSE, x, y, w, h);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_POLY:
@@ -198,7 +204,7 @@ 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, PIPELINE_DRAW_COLOR, x11, y11, dx21, dy21, dx12, dy12);
VKRenderer_RenderParallelogram(&context, VK_FALSE, x11, y11, dx21, dy21, dx12, dy12);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_AAPARALLELOGRAM:
@@ -226,7 +232,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, PIPELINE_FILL_COLOR, x, y, w, h);
VKRenderer_RenderRect(&context, VK_TRUE, x, y, w, h);
}
break;
case sun_java2d_pipe_BufferedOpCodes_FILL_SPANS:
@@ -249,7 +255,7 @@ 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, PIPELINE_FILL_COLOR, x11, y11, dx21, dy21, dx12, dy12);
VKRenderer_RenderParallelogram(&context, VK_TRUE, x11, y11, dx21, dy21, dx12, dy12);
}
break;
case sun_java2d_pipe_BufferedOpCodes_FILL_AAPARALLELOGRAM:
@@ -263,6 +269,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn6(J2D_TRACE_VERBOSE,
"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);
}
break;
@@ -292,6 +300,25 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
bytesPerGlyph = BYTES_PER_GLYPH_IMAGE;
}
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: DRAW_GLYPH_LIST");
// TODO this is a quick and dirty implementation of greyscale-AA text rendering over MASK_FILL. Need to do better.
jfloat glyphx, glyphy;
for (int i = 0; i < numGlyphs; i++) {
GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));
if (usePositions) {
jfloat posx = NEXT_FLOAT(positions);
jfloat posy = NEXT_FLOAT(positions);
glyphx = glyphListOrigX + posx + ginfo->topLeftX;
glyphy = glyphListOrigY + posy + ginfo->topLeftY;
} else {
glyphx = glyphListOrigX + ginfo->topLeftX;
glyphy = glyphListOrigY + ginfo->topLeftY;
glyphListOrigX += ginfo->advanceX;
glyphListOrigY += ginfo->advanceY;
}
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);
}
SKIP_BYTES(b, numGlyphs * bytesPerGlyph);
}
break;
@@ -348,7 +375,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
dx1, dy1, dx2, dy2);
}
context.surface = oldSurface;
break;
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT %p -> %p ", pSrc, pDst)
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",
@@ -381,7 +408,10 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint maskscan = NEXT_INT(b);
jint masklen = NEXT_INT(b);
unsigned char *pMask = (masklen > 0) ? b : NULL;
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: MASK_FILL");
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);
SKIP_BYTES(b, masklen);
}
break;
@@ -404,18 +434,21 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint y1 = NEXT_INT(b);
jint x2 = NEXT_INT(b);
jint y2 = NEXT_INT(b);
context.clipRect = (VkRect2D){
{x1, y1},
{x2-x1, y2 - y1}};
J2dRlsTraceLn4(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_RECT_CLIP(%d, %d, %d, %d)",
x1, y1, x2, y2);
ARRAY_RESIZE(context.clipSpanVertices, 0);
jint width = x2 - x1, height = y2 - y1;
context.clipRect = (VkRect2D) {{x1, y1}, {CARR_MAX(width, 0), CARR_MAX(height, 0)}};
context.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++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_SHAPE_CLIP_SPANS:
@@ -423,19 +456,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");
SKIP_BYTES(b, count * BYTES_PER_SPAN);
size_t offset = ARRAY_SIZE(context.clipSpanVertices);
ARRAY_RESIZE(context.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};
}
context.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++;
}
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++;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_ALPHA_COMPOSITE:
@@ -504,9 +556,16 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
{
VKSDOps* src = NEXT_SURFACE(b);
VKSDOps* dst = NEXT_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);
}
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_SURFACES");
context.surface = dst;
}
break;
@@ -521,8 +580,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
case sun_java2d_pipe_BufferedOpCodes_FLUSH_SURFACE:
{
VKSDOps* surface = NEXT_SURFACE(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FLUSH_SURFACE");
J2dRlsTraceLn1(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FLUSH_SURFACE (%p)", surface)
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISPOSE_SURFACE:
@@ -559,8 +618,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
VKSDOps* surface = NEXT_SURFACE(b);
jint width = NEXT_INT(b);
jint height = NEXT_INT(b);
J2dRlsTraceLn2(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: CONFIGURE_SURFACE %dx%d", width, height);
J2dRlsTraceLn3(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: CONFIGURE_SURFACE (%p) %dx%d", surface, width, height);
VKRenderer_ConfigureSurface(surface, (VkExtent2D) {width, height});
}
break;
@@ -577,8 +636,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
case sun_java2d_pipe_BufferedOpCodes_FLUSH_BUFFER:
{
VKSDOps* surface = NEXT_SURFACE(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FLUSH_BUFFER");
J2dRlsTraceLn1(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FLUSH_BUFFER (%p)", surface)
VKRenderer_FlushSurface(surface);
}

View File

@@ -27,6 +27,7 @@
#ifndef HEADLESS
#include <assert.h>
#include <string.h>
#include "VKUtil.h"
#include "VKBase.h"
#include "VKAllocator.h"
@@ -39,11 +40,11 @@
* Pool of resources with associated timestamps, guarding their reuse.
* The pool must only be manipulated via POOL_* macros.
*/
#define POOL(TYPE, NAME) \
struct PoolEntry_ ## NAME { \
uint64_t timestamp; \
TYPE value; \
} *NAME
#define POOL(TYPE, NAME) \
RING_BUFFER(struct PoolEntry_ ## NAME { \
uint64_t timestamp; \
TYPE value; \
}) NAME
/**
* Take an available item from the pool. VAR is left unchanged if there is no available item.
@@ -58,14 +59,14 @@ struct PoolEntry_ ## NAME { \
* after the next submitted batch of work completes execution on GPU.
*/
// In debug mode resource reuse will be randomly delayed by 3 timestamps in ~20% cases.
#define POOL_RETURN(RENDERER, NAME, VAR) RING_BUFFER_PUSH_BACK((RENDERER)->NAME, \
#define POOL_RETURN(RENDERER, NAME, VAR) (RING_BUFFER_PUSH_BACK((RENDERER)->NAME) = \
(struct PoolEntry_ ## NAME) { .timestamp = (RENDERER)->writeTimestamp + (VK_DEBUG_RANDOM(20)*3), .value = (VAR) })
/**
* Insert an item into the pool. It is available for POOL_TAKE immediately.
* This is usually used for bulk insertion of newly-created resources.
*/
#define POOL_INSERT(RENDERER, NAME, VAR) RING_BUFFER_PUSH_FRONT((RENDERER)->NAME, \
#define POOL_INSERT(RENDERER, NAME, VAR) (RING_BUFFER_PUSH_FRONT((RENDERER)->NAME) = \
(struct PoolEntry_ ## NAME) { .timestamp = 0ULL, .value = (VAR) })
/**
@@ -90,11 +91,14 @@ struct VKRenderer {
VKDevice* device;
VKPipelineContext* pipelineContext;
POOL(VkCommandBuffer, commandBufferPool);
POOL(VkCommandBuffer, secondaryCommandBufferPool);
POOL(VkSemaphore, semaphorePool);
POOL(VKBuffer, vertexBufferPool);
VKMemory* vertexBufferMemoryPages;
POOL(VkCommandBuffer, commandBufferPool);
POOL(VkCommandBuffer, secondaryCommandBufferPool);
POOL(VkSemaphore, semaphorePool);
POOL(VKBuffer, vertexBufferPool);
POOL(VKTexelBuffer, maskFillBufferPool);
POOL(VkFramebuffer, framebufferDestructionQueue);
ARRAY(VKMemory) bufferMemoryPages;
ARRAY(VkDescriptorPool) descriptorPools;
/**
* Last known timestamp hit by GPU execution. Resources with equal or less timestamp may be safely reused.
@@ -110,14 +114,14 @@ struct VKRenderer {
VkCommandBuffer commandBuffer;
struct Wait {
VkSemaphore* semaphores;
VkPipelineStageFlags* stages;
ARRAY(VkSemaphore) semaphores;
ARRAY(VkPipelineStageFlags) stages;
} wait;
struct PendingPresentation {
VkSwapchainKHR* swapchains;
uint32_t* indices;
VkResult* results;
ARRAY(VkSwapchainKHR) swapchains;
ARRAY(uint32_t) indices;
ARRAY(VkResult) results;
} pendingPresentation;
};
@@ -134,20 +138,23 @@ typedef struct {
*/
struct VKRenderPass {
VKRenderPassContext* context;
VKBuffer* vertexBuffers;
ARRAY(VKBuffer) vertexBuffers;
ARRAY(VKTexelBuffer) maskFillBuffers;
VkRenderPass renderPass; // Non-owning.
VkFramebuffer framebuffer;
VkCommandBuffer commandBuffer;
uint32_t firstVertex;
uint32_t vertexCount;
BufferWritingState vertexBufferWriting;
BufferWritingState maskFillBufferWriting;
VKCompositeMode currentComposite;
VKPipeline currentPipeline;
VkBool32 pendingFlush;
VkBool32 pendingCommands;
VkBool32 pendingClear;
uint64_t lastTimestamp; // When was this surface last used?
VKPipelineDescriptor state;
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?
};
/**
@@ -167,7 +174,10 @@ inline VkBool32 VKRenderer_CheckPoolEntryAvailable(VKRenderer* renderer, void* e
*/
static VkBool32 VKRenderer_CheckPoolDrain(void* pool, void* entry) {
if (entry != NULL) return VK_TRUE;
else if (pool != NULL) RING_BUFFER_FREE(pool);
if (pool != NULL) {
RING_BUFFER(char) ring_buffer = pool;
RING_BUFFER_FREE(ring_buffer);
}
return VK_FALSE;
}
@@ -188,11 +198,39 @@ static VKBuffer VKRenderer_GetVertexBuffer(VKRenderer* renderer) {
VKRenderer_FindVertexBufferMemoryType,
VERTEX_BUFFER_SIZE, VERTEX_BUFFER_PAGE_SIZE, &bufferCount, buffers);
VK_RUNTIME_ASSERT(page);
ARRAY_PUSH_BACK(renderer->vertexBufferMemoryPages, page);
ARRAY_PUSH_BACK(renderer->bufferMemoryPages) = page;
for (uint32_t i = 1; i < bufferCount; i++) POOL_INSERT(renderer, vertexBufferPool, buffers[i]);
return buffers[0];
}
#define MASK_FILL_BUFFER_SIZE (256 * 1024) // 256KiB = 256 typical MASK_FILL tiles
#define MASK_FILL_BUFFER_PAGE_SIZE (4 * 1024 * 1024) // 4MiB - fits 16 buffers
static void VKRenderer_FindMaskFillBufferMemoryType(VKMemoryRequirements* requirements) {
VKAllocator_FindMemoryType(requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
VK_ALL_MEMORY_PROPERTIES);
VKAllocator_FindMemoryType(requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_ALL_MEMORY_PROPERTIES);
}
static VKTexelBuffer VKRenderer_GetMaskFillBuffer(VKRenderer* renderer) {
VKTexelBuffer buffer = { .buffer.handle = VK_NULL_HANDLE };
POOL_TAKE(renderer, maskFillBufferPool, buffer);
if (buffer.buffer.handle != VK_NULL_HANDLE) return buffer;
uint32_t bufferCount = MASK_FILL_BUFFER_PAGE_SIZE / MASK_FILL_BUFFER_SIZE;
VKBuffer buffers[bufferCount];
VKMemory page = VKBuffer_CreateBuffers(renderer->device, VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT,
VKRenderer_FindMaskFillBufferMemoryType,
MASK_FILL_BUFFER_SIZE, MASK_FILL_BUFFER_PAGE_SIZE, &bufferCount, buffers);
VK_RUNTIME_ASSERT(page);
VKTexelBuffer texelBuffers[bufferCount];
VkDescriptorPool descriptorPool = VKBuffer_CreateTexelBuffers(
renderer->device, VK_FORMAT_R8_UNORM, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
renderer->pipelineContext->maskFillDescriptorSetLayout, bufferCount, buffers, texelBuffers);
VK_RUNTIME_ASSERT(descriptorPool);
for (uint32_t i = 1; i < bufferCount; i++) POOL_INSERT(renderer, maskFillBufferPool, texelBuffers[i]);
ARRAY_PUSH_BACK(renderer->bufferMemoryPages) = page;
ARRAY_PUSH_BACK(renderer->descriptorPools) = descriptorPool;
return texelBuffers[0];
}
static VkSemaphore VKRenderer_AddPendingSemaphore(VKRenderer* renderer) {
VKDevice* device = renderer->device;
VkSemaphore semaphore = VK_NULL_HANDLE;
@@ -294,14 +332,26 @@ void VKRenderer_Destroy(VKRenderer* renderer) {
device->vkDestroySemaphore(device->handle, entry->value, NULL);
}
// Destroy vertex buffer pool.
// Destroy buffer pools.
POOL_DRAIN_FOR(renderer, vertexBufferPool, entry) {
device->vkDestroyBuffer(device->handle, entry->value.handle, NULL);
}
for (uint32_t i = 0; i < ARRAY_SIZE(renderer->vertexBufferMemoryPages); i++) {
VKAllocator_Free(device->allocator, renderer->vertexBufferMemoryPages[i]);
POOL_DRAIN_FOR(renderer, maskFillBufferPool, entry) {
// No need to destroy descriptor sets one by one, we will destroy the pool anyway.
device->vkDestroyBufferView(device->handle, entry->value.view, NULL);
device->vkDestroyBuffer(device->handle, entry->value.buffer.handle, NULL);
}
ARRAY_FREE(renderer->vertexBufferMemoryPages);
POOL_DRAIN_FOR(renderer, framebufferDestructionQueue, entry) {
device->vkDestroyFramebuffer(device->handle, entry->value, NULL);
}
for (uint32_t i = 0; i < ARRAY_SIZE(renderer->bufferMemoryPages); i++) {
VKAllocator_Free(device->allocator, renderer->bufferMemoryPages[i]);
}
ARRAY_FREE(renderer->bufferMemoryPages);
for (uint32_t i = 0; i < ARRAY_SIZE(renderer->descriptorPools); i++) {
device->vkDestroyDescriptorPool(device->handle, renderer->descriptorPools[i], NULL);
}
ARRAY_FREE(renderer->descriptorPools);
device->vkDestroySemaphore(device->handle, renderer->timelineSemaphore, NULL);
device->vkDestroyCommandPool(device->handle, renderer->commandPool, NULL);
@@ -314,6 +364,17 @@ void VKRenderer_Destroy(VKRenderer* renderer) {
free(renderer);
}
static void VKRenderer_CleanupPendingResources(VKRenderer* renderer) {
VKDevice* device = renderer->device;
for (;;) {
VkFramebuffer framebuffer = VK_NULL_HANDLE;
POOL_TAKE(renderer, framebufferDestructionQueue, framebuffer);
if (framebuffer == VK_NULL_HANDLE) break;
device->vkDestroyFramebuffer(device->handle, framebuffer, NULL);
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_CleanupPendingResources(%p): framebuffer destroyed", renderer);
}
}
/**
* Record commands into primary command buffer (outside of a render pass).
* Recorded commands will be sent for execution via VKRenderer_Flush.
@@ -351,6 +412,7 @@ VkCommandBuffer VKRenderer_Record(VKRenderer* renderer) {
void VKRenderer_Flush(VKRenderer* renderer) {
if (renderer == NULL) return;
VKRenderer_CleanupPendingResources(renderer);
VKDevice* device = renderer->device;
size_t pendingPresentations = ARRAY_SIZE(renderer->pendingPresentation.swapchains);
@@ -436,7 +498,7 @@ void VKRenderer_AddImageBarrier(VkImageMemoryBarrier* barriers, VKBarrierBatch*
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image->handle,
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }
.subresourceRange = { VKImage_GetAspect(image), 0, 1, 0, 1 }
};
batch->barrierCount++;
batch->srcStages |= image->lastStage;
@@ -465,20 +527,28 @@ inline void VKRenderer_FlushDraw(VKSDOps* surface) {
*/
static void VKRenderer_ResetDrawing(VKSDOps* surface) {
assert(surface != NULL && surface->renderPass != NULL);
surface->renderPass->currentComposite = NO_COMPOSITE;
surface->renderPass->currentPipeline = NO_PIPELINE;
surface->renderPass->state.composite = NO_COMPOSITE;
surface->renderPass->state.shader = NO_SHADER;
surface->renderPass->firstVertex = 0;
surface->renderPass->vertexCount = 0;
surface->renderPass->vertexBufferWriting = (BufferWritingState) {NULL, 0, VK_FALSE};
surface->renderPass->maskFillBufferWriting = (BufferWritingState) {NULL, 0, VK_FALSE};
size_t vertexBufferCount = ARRAY_SIZE(surface->renderPass->vertexBuffers);
if (vertexBufferCount == 0) return;
VkMappedMemoryRange memoryRanges[vertexBufferCount];
size_t maskFillBufferCount = ARRAY_SIZE(surface->renderPass->maskFillBuffers);
if (vertexBufferCount == 0 && maskFillBufferCount == 0) return;
VkMappedMemoryRange memoryRanges[vertexBufferCount + maskFillBufferCount];
for (uint32_t i = 0; i < vertexBufferCount; i++) {
memoryRanges[i] = surface->renderPass->vertexBuffers[i].range;
POOL_RETURN(surface->device->renderer, vertexBufferPool, surface->renderPass->vertexBuffers[i]);
}
for (uint32_t i = 0; i < maskFillBufferCount; i++) {
memoryRanges[vertexBufferCount + i] = surface->renderPass->maskFillBuffers[i].buffer.range;
POOL_RETURN(surface->device->renderer, maskFillBufferPool, surface->renderPass->maskFillBuffers[i]);
}
ARRAY_RESIZE(surface->renderPass->vertexBuffers, 0);
VK_IF_ERROR(surface->device->vkFlushMappedMemoryRanges(surface->device->handle, vertexBufferCount, memoryRanges)) {}
ARRAY_RESIZE(surface->renderPass->maskFillBuffers, 0);
VK_IF_ERROR(surface->device->vkFlushMappedMemoryRanges(surface->device->handle,
vertexBufferCount + maskFillBufferCount, memoryRanges)) {}
}
/**
@@ -502,6 +572,7 @@ void VKRenderer_DestroyRenderPass(VKSDOps* surface) {
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.
device->vkDestroyFramebuffer(device->handle, surface->renderPass->framebuffer, NULL);
@@ -509,6 +580,7 @@ void VKRenderer_DestroyRenderPass(VKSDOps* surface) {
POOL_RETURN(device->renderer, secondaryCommandBufferPool, surface->renderPass->commandBuffer);
}
ARRAY_FREE(surface->renderPass->vertexBuffers);
ARRAY_FREE(surface->renderPass->maskFillBuffers);
}
free(surface->renderPass);
surface->renderPass = NULL;
@@ -533,10 +605,15 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
VKRenderPass* renderPass = surface->renderPass = malloc(sizeof(VKRenderPass));
VK_RUNTIME_ASSERT(renderPass);
(*renderPass) = (VKRenderPass) {
.state = {
.stencilMode = STENCIL_MODE_NONE,
.composite = NO_COMPOSITE,
.shader = NO_SHADER
},
.clipModCount = 0,
.pendingFlush = VK_FALSE,
.pendingCommands = VK_FALSE,
.pendingClear = VK_TRUE, // Clear the surface by default
.currentComposite = NO_COMPOSITE,
.currentPipeline = NO_PIPELINE,
.lastTimestamp = 0
};
@@ -545,23 +622,48 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
renderPass->context = VKPipelines_GetRenderPassContext(renderer->pipelineContext, surface->image->format);
}
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_InitRenderPass(%p)", surface);
return VK_TRUE;
}
/**
* Initialize surface framebuffer.
* This function can be called between render passes of a single frame, unlike VKRenderer_InitRenderPass.
*/
static void VKRenderer_InitFramebuffer(VKSDOps* surface) {
assert(surface != NULL && surface->device != NULL && surface->renderPass != NULL);
VKDevice* device = surface->device;
VKRenderPass* renderPass = surface->renderPass;
if (renderPass->state.stencilMode == STENCIL_MODE_NONE && surface->stencil != NULL) {
// Queue outdated color-only framebuffer for destruction.
POOL_RETURN(device->renderer, framebufferDestructionQueue, renderPass->framebuffer);
renderPass->framebuffer = VK_NULL_HANDLE;
renderPass->state.stencilMode = STENCIL_MODE_OFF;
}
// Initialize framebuffer.
if (renderPass->framebuffer == VK_NULL_HANDLE) {
renderPass->renderPass = renderPass->context->renderPass[surface->stencil != NULL];
VkImageView views[] = { surface->image->view, VK_NULL_HANDLE };
VkFramebufferCreateInfo framebufferCreateInfo = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = renderPass->context->renderPass,
.renderPass = renderPass->renderPass,
.attachmentCount = 1,
.pAttachments = &surface->image->view,
.pAttachments = views,
.width = surface->image->extent.width,
.height = surface->image->extent.height,
.layers = 1
};
if (surface->stencil != NULL) {
framebufferCreateInfo.attachmentCount = 2;
views[1] = surface->stencil->view;
}
VK_IF_ERROR(device->vkCreateFramebuffer(device->handle, &framebufferCreateInfo, NULL,
&renderPass->framebuffer)) VK_UNHANDLED_ERROR();
}
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_InitRenderPass(%p)", surface);
return VK_TRUE;
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_InitFramebuffer(%p)", surface);
}
}
/**
@@ -569,6 +671,7 @@ static VkBool32 VKRenderer_InitRenderPass(VKSDOps* surface) {
*/
static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
assert(surface != NULL && surface->renderPass != NULL && !surface->renderPass->pendingCommands);
VKRenderer_InitFramebuffer(surface);
// We may have a pending flush, which is already obsolete.
surface->renderPass->pendingFlush = VK_FALSE;
VKDevice* device = surface->device;
@@ -595,7 +698,7 @@ static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
// Begin recording render pass commands.
VkCommandBufferInheritanceInfo inheritanceInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
.renderPass = surface->renderPass->context->renderPass,
.renderPass = surface->renderPass->renderPass,
.subpass = 0,
.framebuffer = surface->renderPass->framebuffer
};
@@ -624,7 +727,7 @@ static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
surface->renderPass->pendingClear = VK_FALSE;
}
// Set viewport and scissor.
// Set viewport.
VkViewport viewport = {
.x = 0.0f,
.y = 0.0f,
@@ -633,15 +736,13 @@ static void VKRenderer_BeginRenderPass(VKSDOps* surface) {
.minDepth = 0.0f,
.maxDepth = 1.0f
};
VkRect2D scissor = {{0, 0}, surface->image->extent};
device->vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
device->vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
// Calculate inverse viewport for vertex shader.
viewport.width = 2.0f / viewport.width;
viewport.height = 2.0f / viewport.height;
device->vkCmdPushConstants(
commandBuffer,
renderer->pipelineContext->pipelineLayout,
renderer->pipelineContext->colorPipelineLayout, // TODO what if our pipeline layout differs?
VK_SHADER_STAGE_VERTEX_BIT,
0,
sizeof(float) * 2,
@@ -666,30 +767,36 @@ void VKRenderer_FlushRenderPass(VKSDOps* surface) {
surface->renderPass->lastTimestamp = renderer->writeTimestamp;
VkCommandBuffer cb = VKRenderer_Record(renderer);
// Insert barrier to prepare surface for rendering.
VkImageMemoryBarrier barriers[1];
// Insert barriers to prepare surface for rendering.
VkImageMemoryBarrier barriers[2];
VKBarrierBatch barrierBatch = {};
VKRenderer_AddImageBarrier(barriers, &barrierBatch, surface->image,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
if (surface->stencil != NULL) {
VKRenderer_AddImageBarrier(barriers, &barrierBatch, surface->stencil,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
}
if (barrierBatch.barrierCount > 0) {
device->vkCmdPipelineBarrier(cb, barrierBatch.srcStages, barrierBatch.dstStages,
0, 0, NULL, 0, NULL, barrierBatch.barrierCount, barriers);
}
// If there is a pending clear, record it into render pass.
if (clear) VKRenderer_BeginRenderPass(surface);
// Begin render pass.
VkRenderPassBeginInfo renderPassInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = surface->renderPass->context->renderPass,
.renderPass = surface->renderPass->renderPass,
.framebuffer = surface->renderPass->framebuffer,
.renderArea = (VkRect2D) {{0, 0}, surface->image->extent},
.clearValueCount = 0,
.pClearValues = NULL
};
device->vkCmdBeginRenderPass(cb, &renderPassInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
// If there is a pending clear, record it into render pass.
if (clear) VKRenderer_BeginRenderPass(surface);
// Execute render pass commands.
if (surface->renderPass->pendingCommands) {
@@ -740,8 +847,8 @@ void VKRenderer_FlushSurface(VKSDOps* surface) {
// Acquire swapchain image.
VkSemaphore acquireSemaphore = VKRenderer_AddPendingSemaphore(renderer);
ARRAY_PUSH_BACK(renderer->wait.semaphores, acquireSemaphore);
ARRAY_PUSH_BACK(renderer->wait.stages, VK_PIPELINE_STAGE_TRANSFER_BIT); // Acquire image before blitting content onto swapchain
ARRAY_PUSH_BACK(renderer->wait.semaphores) = acquireSemaphore;
ARRAY_PUSH_BACK(renderer->wait.stages) = VK_PIPELINE_STAGE_TRANSFER_BIT; // Acquire image before blitting content onto swapchain
uint32_t imageIndex;
VkResult acquireImageResult = device->vkAcquireNextImageKHR(device->handle, win->swapchain, UINT64_MAX,
@@ -802,8 +909,8 @@ void VKRenderer_FlushSurface(VKSDOps* surface) {
}
// Add pending presentation request.
ARRAY_PUSH_BACK(renderer->pendingPresentation.swapchains, win->swapchain);
ARRAY_PUSH_BACK(renderer->pendingPresentation.indices, imageIndex);
ARRAY_PUSH_BACK(renderer->pendingPresentation.swapchains) = win->swapchain;
ARRAY_PUSH_BACK(renderer->pendingPresentation.indices) = imageIndex;
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "VKRenderer_FlushSurface(%p): queued for presentation", surface);
}
}
@@ -854,8 +961,10 @@ inline BufferWritingState VKRenderer_AllocateBufferData(VKSDOps* surface, Buffer
* This function must not be used directly, use VK_DRAW macro instead.
* It is responsibility of the caller to pass correct vertexSize, matching current pipeline.
* This function cannot draw more vertices than fits into single vertex buffer at once.
* This function must be called after all dynamic allocation functions,
* which can invalidate drawing state, e.g. VKRenderer_AllocateMaskFillBytes.
*/
static void* VKRenderer_AllocateVertices(VKRenderingContext* context, uint32_t vertices, size_t vertexSize) {
static void* VKRenderer_AllocateVertices(const VKRenderingContext* context, uint32_t vertices, size_t vertexSize) {
assert(vertices > 0 && vertexSize > 0);
assert(vertexSize * vertices <= VERTEX_BUFFER_SIZE);
VKSDOps* surface = context->surface;
@@ -864,7 +973,7 @@ static void* VKRenderer_AllocateVertices(VKRenderingContext* context, uint32_t v
if (!state.bound) {
if (state.data == NULL) {
VKBuffer buffer = VKRenderer_GetVertexBuffer(surface->device->renderer);
ARRAY_PUSH_BACK(surface->renderPass->vertexBuffers, buffer);
ARRAY_PUSH_BACK(surface->renderPass->vertexBuffers) = buffer;
surface->renderPass->vertexBufferWriting.data = state.data = buffer.data;
}
assert(ARRAY_SIZE(surface->renderPass->vertexBuffers) > 0);
@@ -878,14 +987,100 @@ static void* VKRenderer_AllocateVertices(VKRenderingContext* context, uint32_t v
/**
* Allocate vertices from vertex buffer, providing pointer for writing.
* VKRenderer_Validate must have been called before.
* This function cannot draw more vertices than fits into single vertex buffer at once.
* 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]))
/**
* 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.
* This function can invalidate drawing state, always call it before VK_DRAW.
*/
static BufferWritingState VKRenderer_AllocateMaskFillBytes(const VKRenderingContext* context, uint32_t size) {
assert(size > 0);
assert(size <= MASK_FILL_BUFFER_SIZE);
VKSDOps* surface = context->surface;
BufferWritingState state = VKRenderer_AllocateBufferData(
surface, &surface->renderPass->maskFillBufferWriting, size, MASK_FILL_BUFFER_SIZE);
if (!state.bound) {
if (state.data == NULL) {
VKTexelBuffer buffer = VKRenderer_GetMaskFillBuffer(surface->device->renderer);
ARRAY_PUSH_BACK(surface->renderPass->maskFillBuffers) = buffer;
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->renderer->pipelineContext->maskFillPipelineLayout,
0, 1, &ARRAY_LAST(surface->renderPass->maskFillBuffers).descriptorSet, 0, NULL);
}
state.data = (void*) ((uint8_t*) state.data + state.offset);
return state;
}
/**
* Setup stencil attachment according to the context clip state.
* If there is a clip shape, attachment is cleared with "fail" value and then
* pixels inside the clip shape are set to "pass".
* If there is no clip shape, whole attachment is cleared with "pass" value.
*/
static void VKRenderer_SetupStencil(const VKRenderingContext* context) {
assert(context != NULL && context->surface != NULL && context->surface->renderPass != NULL);
VKSDOps* surface = context->surface;
VKRenderPass* renderPass = surface->renderPass;
VkCommandBuffer cb = renderPass->commandBuffer;
VKRenderer_FlushDraw(surface);
// Clear stencil attachment.
VkClearAttachment clearAttachment = {
.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT,
.clearValue.depthStencil.stencil = ARRAY_SIZE(context->clipSpanVertices) > 0 ?
CLIP_STENCIL_EXCLUDE_VALUE : CLIP_STENCIL_INCLUDE_VALUE
};
VkClearRect clearRect = {
.rect = {{0, 0}, surface->stencil->extent},
.baseArrayLayer = 0,
.layerCount = 1
};
surface->device->vkCmdClearAttachments(cb, 1, &clearAttachment, 1, &clearRect);
// Bind the clip pipeline.
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
VKPipelines_GetPipeline(surface->renderPass->context, (VKPipelineDescriptor) {
.stencilMode = STENCIL_MODE_ON,
.composite = NO_COMPOSITE,
.shader = SHADER_CLIP,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
}));
// Reset vertex buffer binding.
renderPass->vertexBufferWriting.bound = VK_FALSE;
// Rasterize clip spans.
const uint32_t MAX_VERTICES_PER_DRAW = (VERTEX_BUFFER_SIZE / sizeof(VKIntVertex) / 3) * 3;
VKIntVertex* vs;
for (uint32_t drawn = 0;;) {
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);
memcpy(vs, context->clipSpanVertices + drawn, currentDraw * sizeof(VKIntVertex));
drawn += currentDraw;
}
VKRenderer_FlushDraw(surface);
// Reset pipeline state.
renderPass->state.shader = NO_SHADER;
}
/**
* Setup pipeline for drawing. Returns FALSE if surface is not yet ready for drawing.
*/
VkBool32 VKRenderer_Validate(VKRenderingContext* context, VKPipeline pipeline) {
VkBool32 VKRenderer_Validate(const VKRenderingContext* context, VKShader shader, VkPrimitiveTopology topology) {
assert(context != NULL && context->surface != NULL);
VKSDOps* surface = context->surface;
@@ -898,46 +1093,72 @@ VkBool32 VKRenderer_Validate(VKRenderingContext* context, VKPipeline pipeline) {
VKRenderPass* renderPass = surface->renderPass;
// Validate render pass state.
if (renderPass->currentComposite != context->composite) {
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;
VKCompositeMode oldComposite = renderPass->currentComposite;
VKCompositeMode oldComposite = renderPass->state.composite;
VkBool32 clipChanged = renderPass->clipModCount != context->clipModCount;
// Init stencil attachment, if needed.
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->currentComposite = context->composite;
renderPass->state.composite = context->composite;
renderPass->clipModCount = context->clipModCount;
// Begin render pass.
if (!renderPass->pendingCommands) VKRenderer_BeginRenderPass(surface);
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);
if (clipChanged) {
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.
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating composite, old=%d, new=%d", oldComposite, context->composite);
// Reset the pipeline.
renderPass->currentPipeline = NO_PIPELINE;
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 pipeline.
if (renderPass->currentPipeline != pipeline) {
if (renderPass->state.shader != shader || renderPass->state.topology != topology) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "VKRenderer_Validate: updating pipeline, old=%d, new=%d",
renderPass->currentPipeline, pipeline);
renderPass->state.shader, shader);
VKRenderer_FlushDraw(surface);
VkCommandBuffer cb = renderPass->commandBuffer;
renderPass->currentPipeline = pipeline;
renderPass->state.shader = shader;
renderPass->state.topology = topology;
surface->device->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
VKPipelines_GetPipeline(renderPass->context, context->composite, pipeline));
VKPipelines_GetPipeline(renderPass->context, renderPass->state));
renderPass->vertexBufferWriting.bound = VK_FALSE;
renderPass->maskFillBufferWriting.bound = VK_FALSE;
}
return VK_TRUE;
}
// Drawing operations.
void VKRenderer_RenderRect(VKRenderingContext* context, VKPipeline pipeline, jint x, jint y, jint w, jint h) {
VKRenderer_RenderParallelogram(context, pipeline, (float) x, (float) y, (float) w, 0, 0, (float) h);
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_RenderParallelogram(VKRenderingContext* context, VKPipeline pipeline,
void VKRenderer_RenderParallelogram(const VKRenderingContext* context, VkBool32 fill,
jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12) {
if (!VKRenderer_Validate(context, pipeline)) return; // Not ready.
if (!VKRenderer_Validate(context, SHADER_COLOR,
fill ? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST : VK_PRIMITIVE_TOPOLOGY_LINE_LIST)) return; // Not ready.
Color c = context->color;
/* dx21
* (p1)---------(p2) | (p1)------
@@ -955,23 +1176,23 @@ void VKRenderer_RenderParallelogram(VKRenderingContext* context, VKPipeline pipe
VKColorVertex p4 = {x11 + dx12, y11 + dy12, c};
VKColorVertex* vs;
VK_DRAW(vs, context, pipeline == PIPELINE_DRAW_COLOR ? 8 : 6);
VK_DRAW(vs, context, fill ? 6 : 8);
uint32_t i = 0;
vs[i++] = p1;
vs[i++] = p2;
vs[i++] = p3;
vs[i++] = p4;
vs[i++] = p1;
if (pipeline == PIPELINE_DRAW_COLOR) {
if (!fill) {
vs[i++] = p4;
vs[i++] = p2;
}
vs[i++] = p3;
}
void VKRenderer_FillSpans(VKRenderingContext* context, jint spanCount, jint *spans) {
void VKRenderer_FillSpans(const VKRenderingContext* context, jint spanCount, jint *spans) {
if (spanCount == 0) return;
if (!VKRenderer_Validate(context, PIPELINE_FILL_COLOR)) return; // Not ready.
if (!VKRenderer_Validate(context, SHADER_COLOR, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)) return; // Not ready.
Color c = context->color;
jfloat x1 = (float)*(spans++);
@@ -998,9 +1219,9 @@ void VKRenderer_FillSpans(VKRenderingContext* context, jint spanCount, jint *spa
}
}
void VKRenderer_TextureRender(VKRenderingContext* context, VKImage *destImage, VKImage *srcImage,
void VKRenderer_TextureRender(const VKRenderingContext* context, VKImage *destImage, VKImage *srcImage,
VkBuffer vertexBuffer, uint32_t vertexNum) {
if (!VKRenderer_Validate(context, PIPELINE_BLIT)) return; // Not ready.
if (!VKRenderer_Validate(context, SHADER_BLIT, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)) return; // Not ready.
VKSDOps* surface = (VKSDOps*)context->surface;
VKRenderPass* renderPass = surface->renderPass;
VkCommandBuffer cb = renderPass->commandBuffer;
@@ -1060,4 +1281,37 @@ void VKRenderer_TextureRender(VKRenderingContext* context, VKImage *destImage, V
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.
// 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/
// masklen is the size of the whole mask tile, it may be way bigger, than number of actually needed bytes.
uint32_t byteCount = maskscan * h;
if (mask == NULL) {
maskscan = 0;
byteCount = 1;
}
BufferWritingState maskState = VKRenderer_AllocateMaskFillBytes(context, byteCount);
if (mask != NULL) {
memcpy(maskState.data, mask + maskoff, byteCount);
} else {
// Special case, fully opaque mask
*((char *)maskState.data) = 0xFF;
}
VKMaskFillColorVertex* vs;
VK_DRAW(vs, context, 6);
Color c = context->color;
int offset = (int) maskState.offset;
VKMaskFillColorVertex p1 = {x, y, offset, maskscan, c};
VKMaskFillColorVertex p2 = {x + w, y, offset, maskscan, c};
VKMaskFillColorVertex p3 = {x + w, y + h, offset, maskscan, c};
VKMaskFillColorVertex p4 = {x, y + h, offset, maskscan, c};
// Always keep p1 as provoking vertex for correct origin calculation in vertex shader.
vs[0] = p1; vs[1] = p3; vs[2] = p2;
vs[3] = p1; vs[4] = p3; vs[5] = p4;
}
#endif /* !HEADLESS */

View File

@@ -33,12 +33,14 @@
struct VKRenderingContext {
VKSDOps* surface;
VKTransform transform;
VkRect2D clipRect;
Color color;
VKCompositeMode composite;
// Extra alpha is not used when painting with plain color,
// in this case color.a already includes it.
float extraAlpha;
uint64_t clipModCount; // Used to track changes to the clip.
VkRect2D clipRect;
ARRAY(VKIntVertex) clipSpanVertices;
};
typedef struct {
@@ -52,7 +54,7 @@ VKRenderer* VKRenderer_Create(VKDevice* device);
/**
* Setup pipeline for drawing. Returns FALSE if surface is not yet ready for drawing.
*/
VkBool32 VKRenderer_Validate(VKRenderingContext* context, VKPipeline pipeline);
VkBool32 VKRenderer_Validate(const VKRenderingContext* context, VKShader shader, VkPrimitiveTopology topology);
/**
* Record commands into primary command buffer (outside of a render pass).
@@ -102,19 +104,23 @@ void VKRenderer_FlushRenderPass(VKSDOps* surface);
// Blit operations.
void VKRenderer_TextureRender(VKRenderingContext* context,
void VKRenderer_TextureRender(const VKRenderingContext* context,
VKImage *destImage, VKImage *srcImage,
VkBuffer vertexBuffer, uint32_t vertexNum);
// Drawing operations.
void VKRenderer_RenderRect(VKRenderingContext* context, VKPipeline pipeline, jint x, jint y, jint w, jint h);
void VKRenderer_RenderRect(const VKRenderingContext* context, VkBool32 fill,
jint x, jint y, jint w, jint h);
void VKRenderer_RenderParallelogram(VKRenderingContext* context, VKPipeline pipeline,
void VKRenderer_RenderParallelogram(const VKRenderingContext* context, VkBool32 fill,
jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12);
void VKRenderer_FillSpans(VKRenderingContext* context, jint spanCount, jint *spans);
void VKRenderer_FillSpans(const VKRenderingContext* context, 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);
#endif //VKRenderer_h_Included

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* 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
@@ -40,9 +40,10 @@ static void VKSD_ResetImageSurface(VKSDOps* vksdo) {
// DestroyRenderPass also waits while the surface resources are being used by device.
VKRenderer_DestroyRenderPass(vksdo);
if (vksdo->device != NULL && vksdo->image != NULL) {
if (vksdo->device != NULL) {
VKImage_Destroy(vksdo->device, vksdo->stencil);
VKImage_Destroy(vksdo->device, vksdo->image);
vksdo->image = NULL;
vksdo->image = vksdo->stencil = NULL;
}
}
@@ -90,7 +91,8 @@ VkBool32 VKSD_ConfigureImageSurface(VKSDOps* vksdo) {
VKImage* image = VKImage_Create(device, vksdo->requestedExtent.width, vksdo->requestedExtent.height,
0, format, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT,
VK_SAMPLE_COUNT_1_BIT, VKSD_FindImageSurfaceMemoryType);
VK_RUNTIME_ASSERT(image);
VKSD_ResetImageSurface(vksdo);
@@ -100,6 +102,25 @@ VkBool32 VKSD_ConfigureImageSurface(VKSDOps* vksdo) {
return vksdo->image != NULL;
}
VkBool32 VKSD_ConfigureImageSurfaceStencil(VKSDOps* vksdo) {
// Check that image is ready.
if (vksdo->image == NULL) {
J2dRlsTraceLn1(J2D_TRACE_WARNING, "VKSD_ConfigureImageSurfaceStencil(%p): image is not ready", vksdo);
return VK_FALSE;
}
// Initialize stencil image.
if (vksdo->stencil == NULL) {
vksdo->stencil = VKImage_Create(vksdo->device, vksdo->image->extent.width, vksdo->image->extent.height,
0, VK_FORMAT_S8_UINT, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
VK_SAMPLE_COUNT_1_BIT, VKSD_FindImageSurfaceMemoryType);
VK_RUNTIME_ASSERT(vksdo->stencil);
J2dRlsTraceLn3(J2D_TRACE_INFO, "VKSD_ConfigureImageSurfaceStencil(%p): stencil image updated %dx%d",
vksdo, vksdo->stencil->extent.width, vksdo->stencil->extent.height);
}
return vksdo->stencil != NULL;
}
VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
// Check that image is ready.
if (vkwinsdo->vksdOps.image == NULL) {
@@ -273,4 +294,23 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
return VK_TRUE;
}
/*
* Class: sun_java2d_vulkan_VKOffScreenSurfaceData
* Method: initOps
* Signature: (II)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});
}
#endif /* !HEADLESS */

View File

@@ -29,6 +29,7 @@
#include "SurfaceData.h"
#include "sun_java2d_pipe_hw_AccelSurface.h"
#include "VKUtil.h"
#include "VKTypes.h"
#include "VKRenderer.h"
@@ -50,6 +51,7 @@ struct VKSDOps {
jint drawableType;
VKDevice* device;
VKImage* image;
VKImage* stencil;
Color background;
VkExtent2D requestedExtent;
@@ -66,7 +68,7 @@ struct VKWinSDOps {
VKSDOps vksdOps;
VkSurfaceKHR surface;
VkSwapchainKHR swapchain;
VkImage* swapchainImages;
ARRAY(VkImage) swapchainImages;
VKDevice* swapchainDevice;
VkExtent2D swapchainExtent;
VKWinSD_SurfaceResizeCallback resizeCallback;
@@ -84,6 +86,12 @@ void VKSD_ResetSurface(VKSDOps* vksdo);
*/
VkBool32 VKSD_ConfigureImageSurface(VKSDOps* vksdo);
/**
* [Re]configure stencil attachment of the image surface.
* VKSD_ConfigureImageSurface must have been called before.
*/
VkBool32 VKSD_ConfigureImageSurfaceStencil(VKSDOps* vksdo);
/**
* Configure window surface. This [re]initializes the swapchain.
* VKSD_ConfigureImageSurface must have been called before.

View File

@@ -56,6 +56,7 @@ typedef struct VKRenderPass VKRenderPass;
typedef struct VKRenderingContext VKRenderingContext;
typedef struct VKPipelineContext VKPipelineContext;
typedef struct VKRenderPassContext VKRenderPassContext;
typedef struct VKTexelBuffer VKTexelBuffer;
typedef struct VKBuffer VKBuffer;
typedef struct VKImage VKImage;
typedef struct VKSDOps VKSDOps;

View File

@@ -30,9 +30,6 @@
#include "jni_util.h"
#include "VKTypes.h"
#define C_ARRAY_UTIL_ALLOCATION_FAILED() VK_FATAL_ERROR("CArrayUtil allocation failed")
#include "CArrayUtil.h"
// VK_DEBUG_RANDOM may be used to randomly tune some parameters and turn off some features,
// which would allow to cover wider range of scenarios and catch configuration-specific errors early.
// In debug builds it returns 1 with approximately CHANCE_PERCENT chance, on release builds it is always 0.
@@ -69,6 +66,9 @@ inline VkBool32 VKUtil_CheckError(VkResult result, const char* errorMessage) {
#define VK_UNHANDLED_ERROR() VK_FATAL_ERROR("Unhandled Vulkan error")
#define VK_RUNTIME_ASSERT(...) if (!(__VA_ARGS__)) VK_FATAL_ERROR("Vulkan assertion failed: " #__VA_ARGS__)
#define C_ARRAY_UTIL_ALLOCATION_FAILED() VK_FATAL_ERROR("CArrayUtil allocation failed")
#include "CArrayUtil.h"
typedef enum {
FORMAT_ALIAS_ORIGINAL = 0,
FORMAT_ALIAS_UNORM = 1,

View File

@@ -0,0 +1,196 @@
/*
* 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.
*/
package sun.awt.wl;
import jdk.internal.misc.InnocuousThread;
import java.awt.FileDialog;
import java.awt.peer.FileDialogPeer;
import java.io.File;
import java.io.FilenameFilter;
import sun.awt.AWTAccessor;
import sun.awt.SunToolkit;
final class GtkFileDialogPeer extends WLDialogPeer implements FileDialogPeer {
// A pointer to the native GTK FileChooser widget
private volatile long widget;
private volatile boolean quit;
GtkFileDialogPeer(FileDialog fd) {
super(fd);
}
private static native void initIDs();
private native void run(String title, int mode, String dir, String file,
FilenameFilter filter, boolean isMultipleMode);
private native void quit();
private native void toFrontImpl(long timestamp);
static {
initIDs();
}
@Override
public void toFront() {
long timestamp = WLToolkit.getInputState().getTimestamp();
toFrontImpl(timestamp);
}
@Override
public native void setBounds(int x, int y, int width, int height, int op);
/**
* Called exclusively by the native C code.
*/
private void setFileInternal(String directory, String[] filenames) {
AWTAccessor.FileDialogAccessor accessor = AWTAccessor.getFileDialogAccessor();
FileDialog fd = (FileDialog) target;
if (filenames == null) {
accessor.setDirectory(fd, null);
accessor.setFile(fd, null);
accessor.setFiles(fd, null);
} else {
// Fix 6987233: add the trailing slash if it's absent
String withSeparator = directory;
if (directory != null) {
withSeparator = directory.endsWith(File.separator) ?
directory : (directory + File.separator);
}
accessor.setDirectory(fd, withSeparator);
accessor.setFile(fd, filenames[0]);
File[] files = new File[filenames.length];
for (int i = 0; i < filenames.length; i++) {
files[i] = new File(directory, filenames[i]);
}
accessor.setFiles(fd, files);
}
}
/**
* Called exclusively by the native C code.
*/
private boolean filenameFilterCallback(String fullname) {
FileDialog fd = (FileDialog) target;
if (fd.getFilenameFilter() == null) {
// no filter, accept all.
return true;
}
File file = new File(fullname);
return fd.getFilenameFilter().accept(new File(file.getParent()), file.getName());
}
@Override
public void setVisible(boolean b) {
SunToolkit.awtLock();
try {
quit = !b;
if (b) {
InnocuousThread.newThread("ShowGtkFileDialog", this::showNativeDialog).start();
} else {
quit();
FileDialog fd = (FileDialog) target;
fd.setVisible(false);
}
} finally {
SunToolkit.awtUnlock();
}
}
@Override
public void dispose() {
SunToolkit.awtLock();
try {
quit = true;
quit();
}
finally {
SunToolkit.awtUnlock();
}
super.dispose();
}
@Override
public void setDirectory(String dir) {
// We do not implement this method because we
// have delegated to FileDialog#setDirectory
}
@Override
public void setFile(String file) {
// We do not implement this method because we
// have delegated to FileDialog#setFile
}
@Override
public void setFilenameFilter(FilenameFilter filter) {
// We do not implement this method because we
// have delegated to FileDialog#setFilenameFilter
}
private void showNativeDialog() {
FileDialog fd = (FileDialog) target;
String dirname = fd.getDirectory();
// File path has a priority over the directory path.
String filename = fd.getFile();
if (filename != null) {
final File file = new File(filename);
if (fd.getMode() == FileDialog.LOAD
&& dirname != null
&& file.getParent() == null) {
filename = dirname + (dirname.endsWith(File.separator) ? "" :
File.separator) + filename;
}
if (fd.getMode() == FileDialog.SAVE && file.getParent() != null) {
filename = file.getName();
dirname = file.getParent();
}
}
if (!quit) {
run(fd.getTitle(), fd.getMode(), dirname, filename,
fd.getFilenameFilter(), fd.isMultipleMode());
}
}
/**
* Called by native code when GTK dialog is created.
*/
boolean setWindow() {
return !quit && widget != 0;
}
/**
* Called by native code when GTK dialog is closing.
*/
private void onClose() {
widget = 0;
FileDialog fd = (FileDialog) target;
fd.setVisible(false);
}
}

View File

@@ -61,19 +61,20 @@ public final class WLClipboard extends SunClipboard {
// false otherwise (the regular clipboard).
private final boolean isPrimary; // used by native
private final Object dataLock = new Object();
// A handle to the native clipboard representation, 0 if not available.
private long clipboardNativePtr; // guarded by 'this'
private long ourOfferNativePtr; // guarded by 'this'
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 'this'
private List<Long> clipboardFormats; // guarded by dataLock
// 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 'this'
private List<Long> newClipboardFormats = new ArrayList<>(INITIAL_MIME_FORMATS_COUNT); // guarded by dataLock
private static Thread clipboardDispatcherThread;
private static final Thread clipboardDispatcherThread;
static {
initIDs();
dataOfferQueuePtr = createDataOfferQueue();
@@ -175,11 +176,15 @@ public final class WLClipboard extends SunClipboard {
log.fine("Clipboard: Offering new contents (" + contents + ") in these MIME formats: " + Arrays.toString(mime));
}
synchronized (this) {
synchronized (dataLock) {
if (ourOfferNativePtr != 0) {
cancelOffer(ourOfferNativePtr);
ourOfferNativePtr = 0;
}
ourOfferNativePtr = offerData(eventSerial, mime, contents, dataOfferQueuePtr);
}
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.
@@ -252,7 +257,7 @@ public final class WLClipboard extends SunClipboard {
*/
@Override
protected long[] getClipboardFormats() {
synchronized (this) {
synchronized (dataLock) {
if (clipboardFormats != null && !clipboardFormats.isEmpty()) {
long[] res = new long[clipboardFormats.size()];
for (int i = 0; i < res.length; i++) {
@@ -274,7 +279,25 @@ public final class WLClipboard extends SunClipboard {
*/
@Override
protected byte[] getClipboardData(long format) throws IOException {
synchronized (this) {
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");
}
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);
@@ -286,21 +309,10 @@ public final class WLClipboard extends SunClipboard {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: will read data from " + fd + " in format " + mime);
}
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");
}
return bytes;
}
}
return fd;
}
}
return null;
return -1;
}
/**
@@ -321,7 +333,7 @@ public final class WLClipboard extends SunClipboard {
log.fine("Clipboard: new format is available for " + nativePtr + ": " + mime);
}
synchronized (this) {
synchronized (dataLock) {
newClipboardFormats.add(format);
}
}
@@ -342,7 +354,7 @@ public final class WLClipboard extends SunClipboard {
log.fine("Clipboard: new clipboard is available: " + newClipboardNativePtr);
}
synchronized (this) {
synchronized (dataLock) {
long oldClipboardNativePtr = clipboardNativePtr;
if (oldClipboardNativePtr != 0) {
// "The client must destroy the previous selection data_offer, if any, upon receiving this event."
@@ -352,13 +364,13 @@ public final class WLClipboard extends SunClipboard {
clipboardNativePtr = newClipboardNativePtr; // Could be NULL
newClipboardFormats = new ArrayList<>(INITIAL_MIME_FORMATS_COUNT);
notifyOfNewFormats(getClipboardFormats());
}
notifyOfNewFormats(getClipboardFormats());
}
private void handleOfferCancelled(long offerNativePtr) {
synchronized (this) {
synchronized (dataLock) {
assert offerNativePtr == ourOfferNativePtr;
ourOfferNativePtr = 0;
}

View File

@@ -509,7 +509,7 @@ public class WLComponentPeer implements ComponentPeer {
SurfaceData.convertTo(WLSurfaceDataExt.class, surfaceData).commit();
}
});
Toolkit.getDefaultToolkit().sync();
((WLToolkit) Toolkit.getDefaultToolkit()).flush();
}
private boolean canPaintRoundedCorners() {

View File

@@ -0,0 +1,124 @@
/*
* 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.
*/
package sun.awt.wl;
import sun.awt.SunToolkit;
import sun.awt.UNIXToolkit;
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.awt.Desktop.Action;
import java.awt.peer.DesktopPeer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class WLDesktopPeer implements DesktopPeer {
// supportedActions may be changed from native within the init() call
private static final List<Desktop.Action> supportedActions
= new ArrayList<>(Arrays.asList(Action.OPEN, Action.MAIL, Action.BROWSE));
private static boolean nativeLibraryLoaded = false;
private static boolean initExecuted = false;
private static void initWithLock(){
SunToolkit.awtLock();
try {
if (!initExecuted) {
nativeLibraryLoaded = init(
UNIXToolkit.getEnabledGtkVersion().getNumber(),
UNIXToolkit.isGtkVerbose());
}
} finally {
initExecuted = true;
SunToolkit.awtUnlock();
}
}
WLDesktopPeer(){
initWithLock();
}
static boolean isDesktopSupported() {
initWithLock();
return nativeLibraryLoaded && !supportedActions.isEmpty();
}
public boolean isSupported(Action type) {
return supportedActions.contains(type);
}
public void open(File file) throws IOException {
try {
launch(file.toURI());
} catch (MalformedURLException e) {
throw new IOException(file.toString());
}
}
public void edit(File file) throws IOException {
throw new UnsupportedOperationException("The current platform " +
"doesn't support the EDIT action.");
}
public void print(File file) throws IOException {
throw new UnsupportedOperationException("The current platform " +
"doesn't support the PRINT action.");
}
public void mail(URI uri) throws IOException {
launch(uri);
}
public void browse(URI uri) throws IOException {
launch(uri);
}
private void launch(URI uri) throws IOException {
byte[] uriByteArray = ( uri.toString() + '\0' ).getBytes();
boolean result = false;
SunToolkit.awtLock();
try {
if (!nativeLibraryLoaded) {
throw new IOException("Failed to load native libraries.");
}
result = gnome_url_show(uriByteArray);
} finally {
SunToolkit.awtUnlock();
}
if (!result) {
throw new IOException("Failed to show URI:" + uri);
}
}
private native boolean gnome_url_show(byte[] url);
private static native boolean init(int gtkVersion, boolean verbose);
}

View File

@@ -26,6 +26,8 @@
package sun.awt.wl;
import java.awt.event.InputEvent;
/**
* MouseEvent objects cannot be created directly from WLPointerEvent because they require
* the information of certain events from the past like keyboard modifiers keys getting
@@ -347,4 +349,8 @@ record WLInputState(WLPointerEvent eventWithSurface,
public int getModifiers() {
return modifiers;
}
public int getNonKeyboardModifiers() {
return modifiers & ~(InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK | InputEvent.META_DOWN_MASK | InputEvent.ALT_DOWN_MASK);
}
}

View File

@@ -112,49 +112,15 @@ class WLKeyboard {
}
}
public static final int XKB_SHIFT_MASK = 1 << 0;
public static final int XKB_CAPS_LOCK_MASK = 1 << 1;
public static final int XKB_CTRL_MASK = 1 << 2;
public static final int XKB_ALT_MASK = 1 << 3;
public static final int XKB_NUM_LOCK_MASK = 1 << 4;
public static final int XKB_MOD3_MASK = 1 << 5;
public static final int XKB_META_MASK = 1 << 6;
public static final int XKB_MOD5_MASK = 1 << 7;
private final KeyRepeatManager keyRepeatManager;
private native void initialize(KeyRepeatManager keyRepeatManager);
public int getModifiers() {
int result = 0;
int mask = getXKBModifiersMask();
public native int getModifiers();
if ((mask & XKB_SHIFT_MASK) != 0) {
result |= InputEvent.SHIFT_DOWN_MASK;
}
public native boolean isCapsLockPressed();
if ((mask & XKB_CTRL_MASK) != 0) {
result |= InputEvent.CTRL_DOWN_MASK;
}
if ((mask & XKB_ALT_MASK) != 0) {
result |= InputEvent.ALT_DOWN_MASK;
}
if ((mask & XKB_META_MASK) != 0) {
result |= InputEvent.META_DOWN_MASK;
}
return result;
}
public boolean isCapsLockPressed() {
return (getXKBModifiersMask() & XKB_CAPS_LOCK_MASK) != 0;
}
public boolean isNumLockPressed() {
return (getXKBModifiersMask() & XKB_NUM_LOCK_MASK) != 0;
}
public native boolean isNumLockPressed();
public void onLostFocus() {
assert EventQueue.isDispatchThread();

View File

@@ -37,6 +37,7 @@ import sun.awt.SunToolkit;
import sun.awt.UNIXToolkit;
import sun.awt.datatransfer.DataTransferer;
import sun.java2d.vulkan.VKInstance;
import sun.java2d.vulkan.VKRenderQueue;
import sun.util.logging.PlatformLogger;
import java.awt.*;
@@ -160,6 +161,8 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
private static Cursor currentCursor;
private static Boolean sunAwtDisableGtkFileDialogs = null;
private static native void initIDs(long displayPtr);
static {
@@ -202,6 +205,13 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
}
}
public static synchronized boolean getSunAwtDisableGtkFileDialogs() {
if (sunAwtDisableGtkFileDialogs == null) {
sunAwtDisableGtkFileDialogs = Boolean.getBoolean("sun.awt.disableGtkFileDialogs");
}
return sunAwtDisableGtkFileDialogs;
}
private static void initSystemProperties() {
final String extraButtons = "sun.awt.enableExtraMouseButtons";
areExtraMouseButtonsEnabled =
@@ -343,7 +353,8 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
int keyLocation,
int rawCode,
int extendedKeyCode,
char keyChar) {
char keyChar,
int modifiers) {
// Invoked from the native code
assert EventQueue.isDispatchThread();
@@ -375,7 +386,8 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
keyChar,
keyLocation,
rawCode,
extendedKeyCode
extendedKeyCode,
modifiers
);
}
}
@@ -391,9 +403,11 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
char keyChar,
int keyLocation,
long rawCode,
int extendedKeyCode) {
int extendedKeyCode,
int modifiers) {
int mergedModifiers = inputState.getNonKeyboardModifiers() | modifiers;
final KeyEvent keyEvent = new KeyEvent(source, id, timestamp,
inputState.getModifiers(), keyCode, keyChar, keyLocation);
mergedModifiers, keyCode, keyChar, keyLocation);
AWTAccessor.KeyEventAccessor kea = AWTAccessor.getKeyEventAccessor();
kea.setRawCode(keyEvent, rawCode);
@@ -676,10 +690,17 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
@Override
public FileDialogPeer createFileDialog(FileDialog target) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Not implemented: WLToolkit.createFileDialog()");
FileDialogPeer peer = null;
if (!getSunAwtDisableGtkFileDialogs() && checkGtkVersion(3, 0, 0)) {
peer = new GtkFileDialogPeer(target);
targetCreatedPeer(target, peer);
return peer;
} else {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Not implemented: WLToolkit.createFileDialog()");
}
return null;
}
return null;
}
@Override
@@ -980,18 +1001,12 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
*/
@Override
public boolean isDesktopSupported() {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Not implemented: WLToolkit.isDesktopSupported()");
}
return false;
return WLDesktopPeer.isDesktopSupported();
}
@Override
public DesktopPeer createDesktopPeer(Desktop target) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Not implemented: WLToolkit.createDesktopPeer()");
}
return null;
return new WLDesktopPeer();
}
@Override
@@ -1040,12 +1055,14 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
}
@Override
public boolean isNativeGTKAvailable() {
return false;
public void sync() {
if(VKInstance.isVulkanEnabled()) {
VKRenderQueue.sync();
}
flushImpl();
}
@Override
public void sync() {
public void flush() {
flushImpl();
}

View File

@@ -219,7 +219,7 @@ public final class WLVKGraphicsConfig extends WLGraphicsConfig
@Override
public SurfaceData createSurfaceData(WLComponentPeer peer) {
return WLVKSurfaceData.createData(peer);
return new WLVKWindowSurfaceData(peer);
}
/**

View File

@@ -47,8 +47,9 @@ public class WLVKVolatileSurfaceManager extends VolatileSurfaceManager {
* if the image is not bitmask
*/
int transparency = vImg.getTransparency();
// TODO: enable acceleration
accelerationEnabled = false; // transparency != Transparency.BITMASK;
accelerationEnabled = VKInstance.isSurfaceDataAccelerated() &&
transparency != Transparency.BITMASK;
}
protected boolean isAccelerationEnabled() {
@@ -60,7 +61,6 @@ public class WLVKVolatileSurfaceManager extends VolatileSurfaceManager {
* of an existing window if this is a double buffered GraphicsConfig)
*/
protected SurfaceData initAcceleratedSurface() {
/* TODO
try {
WLVKGraphicsConfig gc =
(WLVKGraphicsConfig)vImg.getGraphicsConfig();
@@ -71,15 +71,11 @@ public class WLVKVolatileSurfaceManager extends VolatileSurfaceManager {
if (type == AccelSurface.UNDEFINED) {
type = AccelSurface.RT_TEXTURE;
}
return WLVKSurfaceData.createData(gc,
vImg.getWidth(),
vImg.getHeight(),
cm, vImg, type);
return new VKOffScreenSurfaceData(
gc, vImg, cm, type, vImg.getWidth(), vImg.getHeight());
} catch (NullPointerException | OutOfMemoryError ignored) {
return null;
}
*/
return null;
}
@Override

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
@@ -28,39 +27,31 @@ package sun.java2d.vulkan;
import java.awt.AlphaComposite;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import sun.awt.image.BufImgSurfaceData;
import sun.awt.wl.WLComponentPeer;
import sun.java2d.SurfaceData;
import sun.java2d.loops.Blit;
import sun.java2d.loops.CompositeType;
import sun.java2d.loops.SurfaceType;
import sun.java2d.pipe.BufferedContext;
import static sun.java2d.pipe.BufferedOpCodes.FLUSH_BUFFER;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.wl.WLPixelGrabberExt;
import sun.java2d.wl.WLSurfaceDataExt;
import sun.util.logging.PlatformLogger;
import static sun.java2d.pipe.BufferedOpCodes.FLUSH_BUFFER;
import static sun.java2d.pipe.BufferedOpCodes.CONFIGURE_SURFACE;
public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurfaceDataExt, WLPixelGrabberExt {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.java2d.vulkan.WLVKSurfaceData");
public class WLVKWindowSurfaceData extends VKSurfaceData
implements WLPixelGrabberExt, WLSurfaceDataExt
{
protected WLComponentPeer peer;
private native void initOps(int backgroundRGB);
private native void assignWlSurface(long surfacePtr);
protected WLVKSurfaceData(WLComponentPeer peer, WLVKGraphicsConfig gc,
SurfaceType sType, ColorModel cm, int type)
public WLVKWindowSurfaceData(WLComponentPeer peer)
{
super(gc, cm, type, 0, 0);
super((VKGraphicsConfig) peer.getGraphicsConfiguration(), peer.getColorModel(), WINDOW, 0, 0);
this.peer = peer;
final int backgroundRGB = peer.getBackground() != null
? peer.getBackground().getRGB()
@@ -68,10 +59,46 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
initOps(backgroundRGB);
}
private void bufferAttached() {
// Called from the native code when a buffer has just been attached to this surface
// but the surface has not been committed yet.
peer.updateSurfaceSize();
public SurfaceData getReplacement() {
return null;
}
@Override
public long getNativeResource(int resType) {
return 0;
}
public Rectangle getBounds() {
Rectangle r = peer.getBufferBounds();
r.x = r.y = 0;
return r;
}
/**
* Returns destination Component associated with this SurfaceData.
*/
public Object getDestination() {
return peer.getTarget();
}
@Override
public double getDefaultScaleX() {
return scale;
}
@Override
public double getDefaultScaleY() {
return scale;
}
@Override
public BufferedContext getContext() {
return ((WLVKGraphicsConfig) getDeviceConfiguration()).getContext();
}
@Override
public boolean isOnScreen() {
return true;
}
@Override
@@ -79,6 +106,7 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
assignWlSurface(surfacePtr);
if (surfacePtr != 0) configure();
}
@Override
public void revalidate(int width, int height, int scale) {
this.width = width;
@@ -87,23 +115,6 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
configure();
}
private synchronized void configure() {
VKRenderQueue rq = VKRenderQueue.getInstance();
rq.lock();
try {
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(20, 4);
buf.putInt(CONFIGURE_SURFACE);
buf.putLong(getNativeOps());
buf.putInt(width);
buf.putInt(height);
rq.flushNow();
} finally {
rq.unlock();
}
}
@Override
public synchronized void commit() {
VKRenderQueue rq = VKRenderQueue.getInstance();
@@ -119,38 +130,16 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
rq.unlock();
}
}
@Override
public boolean isOnScreen() {
return false;
}
@Override
public GraphicsConfiguration getDeviceConfiguration() {
return peer.getGraphicsConfiguration();
}
/**
* Creates a SurfaceData object representing surface of on-screen Window.
*/
public static WLVKWindowSurfaceData createData(WLComponentPeer peer) {
WLVKGraphicsConfig gc = getGC(peer);
return new WLVKWindowSurfaceData(peer, gc);
private void bufferAttached() {
// Called from the native code when a buffer has just been attached to this surface
// but the surface has not been committed yet.
peer.updateSurfaceSize();
}
public static WLVKGraphicsConfig getGC(WLComponentPeer peer) {
if (peer != null) {
return (WLVKGraphicsConfig) peer.getGraphicsConfiguration();
} else {
// REMIND: this should rarely (never?) happen, but what if
// default config is not WLVK?
GraphicsEnvironment env =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = env.getDefaultScreenDevice();
return (WLVKGraphicsConfig)gd.getDefaultConfiguration();
}
}
public int getRGBPixelAt(int x, int y) {
Rectangle r = peer.getBufferBounds();
if (x < r.x || x >= r.x + r.width || y < r.y || y >= r.y + r.height) {
@@ -163,7 +152,7 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
Blit blit = Blit.getFromCache(getSurfaceType(), CompositeType.SrcNoEa,
resData.getSurfaceType());
blit.Blit(this, resData, AlphaComposite.Src, null,
x, y, 0, 0, 1, 1);
x, y, 0, 0, 1, 1);
return resImg.getRGB(0, 0);
}
@@ -194,54 +183,4 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
resImg.getRGB(0, 0, b.width, b.height, pixels, 0, b.width);
return pixels;
}
public static class WLVKWindowSurfaceData extends WLVKSurfaceData {
public WLVKWindowSurfaceData(WLComponentPeer peer, WLVKGraphicsConfig gc)
{
super(peer, gc, gc.getSurfaceType(), peer.getColorModel(), WINDOW);
}
public SurfaceData getReplacement() {
return null;
}
@Override
public long getNativeResource(int resType) {
return 0;
}
public Rectangle getBounds() {
Rectangle r = peer.getBufferBounds();
r.x = r.y = 0;
return r;
}
/**
* Returns destination Component associated with this SurfaceData.
*/
public Object getDestination() {
return peer.getTarget();
}
@Override
public double getDefaultScaleX() {
return scale;
}
@Override
public double getDefaultScaleY() {
return scale;
}
@Override
public BufferedContext getContext() {
return ((WLVKGraphicsConfig) getDeviceConfiguration()).getContext();
}
@Override
public boolean isOnScreen() {
return true;
}
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* 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
@@ -35,8 +35,15 @@ static void WLVKSurfaceData_OnResize(VKWinSDOps* surface, VkExtent2D extent) {
JNU_CallMethodByName(env, NULL, surface->vksdOps.sdOps.sdObject, "bufferAttached", "()V");
}
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_initOps(JNIEnv *env, jobject vksd, jint backgroundRGB) {
J2dTraceLn1(J2D_TRACE_VERBOSE, "WLVKSurfaceData_initOps(%p)", vksd);
/*
* Class: sun_java2d_vulkan_WLVKSurfaceData_WLVKWindowSurfaceData
* Method: initOps
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKWindowSurfaceData_initOps(
JNIEnv *env, jobject vksd, jint backgroundRGB)
{
J2dTraceLn1(J2D_TRACE_VERBOSE, "WLVKWindowsSurfaceData_initOps(%p)", vksd);
VKWinSDOps* sd = (VKWinSDOps*)SurfaceData_InitOps(env, vksd, sizeof(VKWinSDOps));
if (sd == NULL) {
JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
@@ -48,17 +55,28 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_initOps(JNIEnv *en
VKSD_ResetSurface(&sd->vksdOps);
}
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_assignWlSurface(JNIEnv *env, jobject vksd, jlong wlSurfacePtr) {
J2dRlsTraceLn2(J2D_TRACE_INFO, "WLVKSurfaceData_assignWlSurface(%p): wl_surface=%p", (void*)vksd, wlSurfacePtr);
/*
* Class: sun_java2d_vulkan_WLVKSurfaceData_WLVKWindowSurfaceData
* Method: assignWlSurface
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKWindowSurfaceData_assignWlSurface(
JNIEnv *env, jobject vksd, jlong wlSurfacePtr)
{
VKWinSDOps* sd = (VKWinSDOps*)SurfaceData_GetOps(env, vksd);
J2dRlsTraceLn2(J2D_TRACE_INFO, "WLVKWindowsSurfaceData_assignWlSurface(%p): wl_surface=%p",
(void*)sd, wlSurfacePtr);
if (sd == NULL) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "WLVKSurfaceData_assignWlSurface(%p): VKWinSDOps is NULL", vksd);
J2dRlsTraceLn1(J2D_TRACE_ERROR,
"WLVKWindowSurfaceData_assignWlSurface(%p): VKWinSDOps is NULL", vksd);
VK_UNHANDLED_ERROR();
}
if (sd->surface != VK_NULL_HANDLE) {
VKSD_ResetSurface(&sd->vksdOps);
J2dRlsTraceLn1(J2D_TRACE_INFO, "WLVKSurfaceData_assignWlSurface(%p): surface reset", vksd);
J2dRlsTraceLn1(J2D_TRACE_INFO,
"WLVKWindowSurfaceData_assignWlSurface(%p): surface reset", vksd);
}
struct wl_surface* wl_surface = (struct wl_surface*)jlong_to_ptr(wlSurfacePtr);
@@ -73,7 +91,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_assignWlSurface(JN
VK_IF_ERROR(ge->vkCreateWaylandSurfaceKHR(ge->vkInstance, &surfaceCreateInfo, NULL, &sd->surface)) {
VK_UNHANDLED_ERROR();
}
J2dRlsTraceLn1(J2D_TRACE_INFO, "WLVKSurfaceData_assignWlSurface(%p): surface created", vksd);
J2dRlsTraceLn1(J2D_TRACE_INFO,
"WLVKWindowSurfaceData_assignWlSurface(%p): surface created", vksd);
// Swapchain will be created later after CONFIGURE_SURFACE.
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2023 JetBrains s.r.o.
// Copyright 2023-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
@@ -27,6 +27,8 @@
#include "WLKeyboard.h"
#include "WLToolkit.h"
#include "java_awt_event_InputEvent.h"
#include <sun_awt_wl_WLKeyboard.h>
#include <stdint.h>
@@ -48,6 +50,15 @@ extern JNIEnv *getEnv();
#define XKB_MOD_NAME_NUM "Mod2"
#define XKB_MOD_NAME_LOGO "Mod4"
#define XKB_SHIFT_MASK (1 << 0)
#define XKB_CAPS_LOCK_MASK (1 << 1)
#define XKB_CTRL_MASK (1 << 2)
#define XKB_ALT_MASK (1 << 3)
#define XKB_NUM_LOCK_MASK (1 << 4)
#define XKB_MOD3_MASK (1 << 5)
#define XKB_META_MASK (1 << 6)
#define XKB_MOD5_MASK (1 << 7)
#define XKB_LED_NAME_CAPS "Caps Lock"
#define XKB_LED_NAME_NUM "Num Lock"
#define XKB_LED_NAME_SCROLL "Scroll Lock"
@@ -201,7 +212,7 @@ static struct WLKeyboardState {
// Report KEY_PRESS/KEY_RELEASE events on non-ASCII capable layouts
// as if they happen on the QWERTY layout
bool useNationalLayouts;
bool reportNonAsciiAsQwerty;
// Report dead keys not as KeyEvent.VK_DEAD_something, but as the corresponding 'normal' Java keycode
bool reportDeadKeysAsNormal;
@@ -1026,6 +1037,7 @@ convertDeadKey(xkb_keysym_t keysym, enum ConvertDeadKeyType type) {
}
enum TranslateKeycodeType {
TRANSLATE_USING_ACTIVE_STATE,
TRANSLATE_USING_ACTIVE_LAYOUT,
TRANSLATE_USING_QWERTY,
};
@@ -1043,22 +1055,65 @@ translateKeycodeToKeysym(uint32_t keycode, enum TranslateKeycodeType type) {
const uint32_t xkbKeycode = keycode + 8;
struct xkb_state *state;
xkb_layout_index_t group;
if (keyboard.qwertyKeymap && type == TRANSLATE_USING_QWERTY) {
state = keyboard.tmpQwertyState;
group = 0;
if (type == TRANSLATE_USING_ACTIVE_STATE) {
state = keyboard.state;
} else {
state = keyboard.tmpState;
group = getKeyboardLayoutIndex();
xkb_layout_index_t group;
bool numLock;
if (keyboard.qwertyKeymap && type == TRANSLATE_USING_QWERTY) {
state = keyboard.tmpQwertyState;
group = 0;
numLock = true;
} else {
state = keyboard.tmpState;
group = getKeyboardLayoutIndex();
numLock = xkb.state_mod_name_is_active(keyboard.state, XKB_MOD_NAME_NUM, XKB_STATE_MODS_EFFECTIVE) == 1;
}
xkb.state_update_mask(state, 0, 0, numLock ? XKB_NUM_LOCK_MASK : 0, 0, 0, group);
}
bool numLock = xkb.state_mod_name_is_active(keyboard.state, XKB_MOD_NAME_NUM, XKB_STATE_MODS_EFFECTIVE) == 1;
xkb.state_update_mask(state, 0, 0, numLock ? sun_awt_wl_WLKeyboard_XKB_NUM_LOCK_MASK : 0, 0, 0, group);
return xkb.state_key_get_one_sym(state, xkbKeycode);
}
static bool
isFunctionKeysym(xkb_keysym_t keysym) {
// https://www.x.org/releases/current/doc/xproto/x11protocol.html#keysym_encoding
// https://www.x.org/releases/X11R7.7/doc/kbproto/xkbproto.html#New_KeySyms
// I think it's enough to check if the keysym belongs to the "Keyboard" set of function keys.
return keysym >= 0xff00 && keysym <= 0xffff;
}
static unsigned
getXKBModifiers(void) {
return xkb.state_serialize_mods(keyboard.state, XKB_STATE_MODS_EFFECTIVE);
}
static int
convertXKBModifiersToJavaModifiers(xkb_mod_mask_t mask) {
int result = 0;
if ((mask & XKB_SHIFT_MASK) != 0) {
result |= java_awt_event_InputEvent_SHIFT_DOWN_MASK;
}
if ((mask & XKB_CTRL_MASK) != 0) {
result |= java_awt_event_InputEvent_CTRL_DOWN_MASK;
}
if ((mask & XKB_ALT_MASK) != 0) {
result |= java_awt_event_InputEvent_ALT_DOWN_MASK;
}
if ((mask & XKB_META_MASK) != 0) {
result |= java_awt_event_InputEvent_META_DOWN_MASK;
}
return result;
}
static void
convertKeysymToJavaCode(xkb_keysym_t keysym, int *javaKeyCode, int *javaKeyLocation) {
if (javaKeyCode) {
@@ -1269,19 +1324,21 @@ handleKey(long serial, long timestamp, uint32_t keycode, bool isPressed, bool is
xkb_keycode_t xkbKeycode = keycode + 8;
bool keyRepeats = xkb.keymap_key_repeats(keyboard.keymap, xkbKeycode);
xkb_keysym_t keysym = translateKeycodeToKeysym(keycode, TRANSLATE_USING_ACTIVE_LAYOUT);
xkb_mod_mask_t consumedModifiers = xkb.state_key_get_consumed_mods2(keyboard.state, xkbKeycode, XKB_CONSUMED_MODE_GTK);
xkb_keysym_t actualKeysym = translateKeycodeToKeysym(keycode, TRANSLATE_USING_ACTIVE_STATE);
xkb_keysym_t noModsKeysym = translateKeycodeToKeysym(keycode, TRANSLATE_USING_ACTIVE_LAYOUT);
xkb_keysym_t qwertyKeysym = translateKeycodeToKeysym(keycode, TRANSLATE_USING_QWERTY);
#ifdef WL_KEYBOARD_DEBUG
char buf[256];
xkb.keysym_get_name(keysym, buf, sizeof buf);
fprintf(stderr, "handleKey: keysym = %d (%s)\n", keysym, buf);
xkb.keysym_get_name(actualKeysym, buf, sizeof buf);
fprintf(stderr, "handleKey: actualKeysym = %d (%s)\n", actualKeysym, buf);
xkb.keysym_get_name(noModsKeysym, buf, sizeof buf);
fprintf(stderr, "handleKey: noModsKeysym = %d (%s)\n", noModsKeysym, buf);
xkb.keysym_get_name(qwertyKeysym, buf, sizeof buf);
fprintf(stderr, "handleKey: qwertyKeysym = %d (%s)\n", qwertyKeysym, buf);
#endif
int javaKeyCode, javaExtendedKeyCode, javaKeyLocation;
// If the national layouts support is enabled, and the current keyboard is not ascii-capable,
// we need to set the extended key code properly.
//
@@ -1292,19 +1349,33 @@ handleKey(long serial, long timestamp, uint32_t keycode, bool isPressed, bool is
// swap will be lost when attempting to translate what they're typing on the non-ascii-capable
// layout to the QWERTY key map. Hence, the 'qwertyKeysym <= 0x7f' check.
if (keyboard.useNationalLayouts && !keyboard.asciiCapable && qwertyKeysym <= 0x7f) {
int javaKeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
int javaExtendedKeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
int javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD;
xkb_keysym_t reportedKeysym = noModsKeysym;
xkb_mod_mask_t modifiers = getXKBModifiers();
bool reportQwerty = keyboard.reportNonAsciiAsQwerty && !keyboard.asciiCapable && qwertyKeysym <= 0x7f;
if (isFunctionKeysym(actualKeysym) && actualKeysym != noModsKeysym) {
// Emulating pressing a function key, for example AltGr+F being mapped to Right on German Neo 2
modifiers &= ~consumedModifiers;
reportedKeysym = actualKeysym;
reportQwerty = false;
}
if (reportQwerty) {
convertKeysymToJavaCode(qwertyKeysym, &javaKeyCode, &javaKeyLocation);
javaExtendedKeyCode = javaKeyCode;
} else {
xkb_keysym_t report = keysym;
if (keyboard.reportDeadKeysAsNormal) {
xkb_keysym_t converted = convertDeadKey(keysym, CONVERT_TO_NON_COMBINING);
xkb_keysym_t converted = convertDeadKey(reportedKeysym, CONVERT_TO_NON_COMBINING);
if (converted != 0) {
report = converted;
reportedKeysym = converted;
}
}
convertKeysymToJavaCode(report, &javaExtendedKeyCode, &javaKeyLocation);
convertKeysymToJavaCode(reportedKeysym, &javaExtendedKeyCode, &javaKeyLocation);
if (javaExtendedKeyCode >= 0x1000000 && !keyboard.reportJavaKeyCodeForActiveLayout) {
convertKeysymToJavaCode(qwertyKeysym, &javaKeyCode, NULL);
} else {
@@ -1312,8 +1383,11 @@ handleKey(long serial, long timestamp, uint32_t keycode, bool isPressed, bool is
}
}
int javaModifiers = convertXKBModifiersToJavaModifiers(modifiers);
#ifdef WL_KEYBOARD_DEBUG
fprintf(stderr, "handleKey: javaKeyCode = %d\n", javaKeyCode);
fprintf(stderr, "handleKey: javaExtendedKeyCode = %d\n", javaExtendedKeyCode);
#endif
struct WLKeyEvent event = {
@@ -1325,6 +1399,7 @@ handleKey(long serial, long timestamp, uint32_t keycode, bool isPressed, bool is
.rawCode = (int)xkbKeycode,
.extendedKeyCode = javaExtendedKeyCode,
.keyChar = getJavaKeyCharForKeycode(xkbKeycode),
.modifiers = javaModifiers,
};
wlPostKeyEvent(&event);
@@ -1433,7 +1508,7 @@ Java_sun_awt_wl_WLKeyboard_initialize(JNIEnv *env, jobject instance, jobject key
return;
}
keyboard.useNationalLayouts = true;
keyboard.reportNonAsciiAsQwerty = true;
keyboard.remapExtraKeycodes = true;
keyboard.reportDeadKeysAsNormal = false;
keyboard.reportJavaKeyCodeForActiveLayout = true;
@@ -1462,9 +1537,18 @@ Java_sun_awt_wl_WLKeyboard_cancelCompose(JNIEnv *env, jobject instance) {
}
JNIEXPORT jint JNICALL
Java_sun_awt_wl_WLKeyboard_getXKBModifiersMask(JNIEnv *env, jobject instance) {
xkb_mod_mask_t mods = xkb.state_serialize_mods(keyboard.state, XKB_STATE_MODS_EFFECTIVE);
return (jint) mods;
Java_sun_awt_wl_WLKeyboard_getModifiers(JNIEnv *env, jobject instance) {
return convertXKBModifiersToJavaModifiers(getXKBModifiers());
}
JNIEXPORT jboolean JNICALL
Java_sun_awt_wl_WLKeyboard_isCapsLockPressed(JNIEnv *env, jobject instance) {
return (getXKBModifiers() & XKB_CAPS_LOCK_MASK) != 0;
}
JNIEXPORT jboolean JNICALL
Java_sun_awt_wl_WLKeyboard_isNumLockPressed(JNIEnv *env, jobject instance) {
return (getXKBModifiers() & XKB_NUM_LOCK_MASK) != 0;
}
void

View File

@@ -1,4 +1,4 @@
// Copyright 2023 JetBrains s.r.o.
// Copyright 2023-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
@@ -40,6 +40,7 @@ struct WLKeyEvent {
int rawCode;
int extendedKeyCode;
uint16_t keyChar;
int modifiers;
};
void wlSetKeymap(const char* serializedKeymap);

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2022-2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022-2024, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2022-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
@@ -443,7 +443,8 @@ wlPostKeyEvent(const struct WLKeyEvent* event)
event->keyLocation,
event->rawCode,
event->extendedKeyCode,
event->keyChar
event->keyChar,
event->modifiers
);
JNU_CHECK_EXCEPTION(env);
}
@@ -691,7 +692,7 @@ initJavaRefs(JNIEnv *env, jclass clazz)
JNI_FALSE);
CHECK_NULL_RETURN(dispatchKeyboardKeyEventMID = (*env)->GetStaticMethodID(env, tkClass,
"dispatchKeyboardKeyEvent",
"(JJIIIIIC)V"),
"(JJIIIIICI)V"),
JNI_FALSE);
CHECK_NULL_RETURN(dispatchKeyboardModifiersEventMID = (*env)->GetStaticMethodID(env, tkClass,
"dispatchKeyboardModifiersEvent",
@@ -1130,23 +1131,6 @@ Java_java_awt_Event_initIDs(JNIEnv *env, jclass cls)
}
JNIEXPORT void JNICALL
Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
{
typedef void (*SplashClose_t)();
SplashClose_t splashClose;
void* hSplashLib = dlopen(0, RTLD_LAZY);
if (!hSplashLib) {
return;
}
splashClose = (SplashClose_t)dlsym(hSplashLib,
"SplashClose");
if (splashClose) {
splashClose();
}
dlclose(hSplashLib);
}
void awt_output_flush()
{
wlFlushToServer(getEnv());

View File

@@ -0,0 +1,132 @@
/*
* 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.
*/
#ifdef HEADLESS
#error This file should not be included in headless library
#endif
#include <dlfcn.h>
#include <jni.h>
#include <jvm_md.h>
#include "Trace.h"
#include "jni_util.h"
#include "gtk_interface.h"
typedef gboolean (GNOME_URL_SHOW_TYPE)(const char *, void **);
typedef gboolean (GNOME_VFS_INIT_TYPE)(void);
static GNOME_URL_SHOW_TYPE *gnome_url_show = NULL;
static gboolean gnome_load(void) {
void *vfs_handle;
void *gnome_handle;
const char *errmsg;
GNOME_VFS_INIT_TYPE *gnome_vfs_init;
vfs_handle = dlopen(VERSIONED_JNI_LIB_NAME("gnomevfs-2", "0"), RTLD_LAZY);
if (vfs_handle == NULL) {
vfs_handle = dlopen(JNI_LIB_NAME("gnomevfs-2"), RTLD_LAZY);
if (vfs_handle == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "can not load libgnomevfs-2.so");
return FALSE;
}
}
dlerror(); /* Clear errors */
gnome_vfs_init = dlsym(vfs_handle, "gnome_vfs_init");
if (gnome_vfs_init == NULL){
J2dTraceLn(J2D_TRACE_ERROR, "dlsym( gnome_vfs_init) returned NULL");
return FALSE;
}
if ((errmsg = dlerror()) != NULL) {
J2dTraceLn1(J2D_TRACE_ERROR, "can not find symbol gnome_vfs_init %s", errmsg);
return FALSE;
}
// call gonme_vfs_init()
(*gnome_vfs_init)();
gnome_handle = dlopen(VERSIONED_JNI_LIB_NAME("gnome-2", "0"), RTLD_LAZY);
if (gnome_handle == NULL) {
gnome_handle = dlopen(JNI_LIB_NAME("gnome-2"), RTLD_LAZY);
if (gnome_handle == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "can not load libgnome-2.so");
return FALSE;
}
}
dlerror(); /* Clear errors */
gnome_url_show = dlsym(gnome_handle, "gnome_url_show");
if ((errmsg = dlerror()) != NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "can not find symble gnome_url_show");
return FALSE;
}
return TRUE;
}
static gboolean gtk_has_been_loaded = FALSE;
static gboolean gnome_has_been_loaded = FALSE;
JNIEXPORT jboolean JNICALL Java_sun_awt_wl_WLDesktopPeer_init
(JNIEnv *env, jclass cls, jint version, jboolean verbose)
{
if (gtk_has_been_loaded || gnome_has_been_loaded) {
return JNI_TRUE;
}
if (gtk_load(env, version, verbose) && gtk->show_uri_load(env)) {
gtk_has_been_loaded = TRUE;
return JNI_TRUE;
} else if (gnome_load()) {
gnome_has_been_loaded = TRUE;
return JNI_TRUE;
}
return JNI_FALSE;
}
JNIEXPORT jboolean JNICALL Java_sun_awt_wl_WLDesktopPeer_gnome_1url_1show
(JNIEnv *env, jobject obj, jbyteArray url_j)
{
gboolean success = FALSE;
const gchar* url_c = (gchar*)(*env)->GetByteArrayElements(env, url_j, NULL);
if (url_c == NULL) {
JNU_ThrowOutOfMemoryError(env, 0);
return JNI_FALSE;
}
if (gtk_has_been_loaded) {
gtk->gdk_threads_enter();
success = gtk->gtk_show_uri_on_window(NULL, url_c, GDK_CURRENT_TIME, NULL);
gtk->gdk_threads_leave();
} else if (gnome_has_been_loaded) {
success = (*gnome_url_show)(url_c, NULL);
}
(*env)->ReleaseByteArrayElements(env, url_j, (jbyte*)url_c, 0);
return success ? JNI_TRUE : JNI_FALSE;
}

View File

@@ -0,0 +1,378 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* 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.
*/
#ifdef HEADLESS
#error This file should not be included in headless library
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include <jni_util.h>
#include "gtk_interface.h"
#include "sun_awt_wl_GtkFileDialogPeer.h"
#include "java_awt_FileDialog.h"
#include "debug_assert.h"
typedef void GtkWidget;
static JavaVM *jvm;
static jmethodID filenameFilterCallbackMethodID = NULL;
static jmethodID setFileInternalMethodID = NULL;
static jfieldID widgetFieldID = NULL;
static jmethodID setWindowMethodID = NULL;
static jmethodID onCloseMethodID = NULL;
JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFileDialogPeer_initIDs
(JNIEnv *env, jclass cx)
{
filenameFilterCallbackMethodID = (*env)->GetMethodID(env, cx,
"filenameFilterCallback", "(Ljava/lang/String;)Z");
DASSERT(filenameFilterCallbackMethodID != NULL);
CHECK_NULL(filenameFilterCallbackMethodID);
setFileInternalMethodID = (*env)->GetMethodID(env, cx,
"setFileInternal", "(Ljava/lang/String;[Ljava/lang/String;)V");
DASSERT(setFileInternalMethodID != NULL);
CHECK_NULL(setFileInternalMethodID);
widgetFieldID = (*env)->GetFieldID(env, cx, "widget", "J");
DASSERT(widgetFieldID != NULL);
CHECK_NULL(widgetFieldID);
setWindowMethodID = (*env)->GetMethodID(env, cx, "setWindow", "()Z");
DASSERT(setWindowMethodID != NULL);
onCloseMethodID = (*env)->GetMethodID(env, cx, "onClose", "()V");
DASSERT(onCloseMethodID != NULL);
}
static gboolean filenameFilterCallback(const GtkFileFilterInfo * filter_info, gpointer obj)
{
JNIEnv *env;
jstring filename;
env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
filename = (*env)->NewStringUTF(env, filter_info->filename);
JNU_CHECK_EXCEPTION_RETURN(env, FALSE);
return (*env)->CallBooleanMethod(env, obj, filenameFilterCallbackMethodID, filename);
}
static void quit(JNIEnv * env, jobject jpeer, gboolean isSignalHandler)
{
jthrowable pendingException;
if ((pendingException = (*env)->ExceptionOccurred(env)) != NULL) {
(*env)->ExceptionClear(env);
}
GtkWidget * dialog = (GtkWidget*)jlong_to_ptr(
(*env)->GetLongField(env, jpeer, widgetFieldID));
(*env)->CallVoidMethod(env, jpeer, onCloseMethodID);
if (dialog != NULL) {
// Callbacks from GTK signals are made within the GTK lock
// So, within a signal handler there is no need to call
// gdk_threads_enter() / gtk->gdk_threads_leave()
if (!isSignalHandler) {
gtk->gdk_threads_enter();
}
gtk->gtk_widget_hide (dialog);
gtk->gtk_widget_destroy (dialog);
gtk->gtk_main_quit ();
if (!isSignalHandler) {
gtk->gdk_threads_leave();
}
}
if (pendingException) {
(*env)->Throw(env, pendingException);
}
}
JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFileDialogPeer_quit
(JNIEnv * env, jobject jpeer)
{
quit(env, jpeer, FALSE);
}
JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFileDialogPeer_toFrontImpl
(JNIEnv * env, jobject jpeer, jlong timestamp)
{
gtk->gdk_threads_enter();
GtkWidget * dialog = jlong_to_ptr((*env)->GetLongField(env, jpeer, widgetFieldID));
if (dialog != NULL) {
gtk->gtk_window_present_with_time((GtkWindow*)dialog, (guint32)timestamp);
}
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFileDialogPeer_setBounds
(JNIEnv * env, jobject jpeer, jint x, jint y, jint width, jint height, jint op)
{
gtk->gdk_threads_enter();
GtkWindow* dialog = jlong_to_ptr((*env)->GetLongField(env, jpeer, widgetFieldID));
if (dialog != NULL) {
// Note: gtk_window_move() will not work in Wayland anyway, so the coordinates
// are ignored intentionally here.
if (width > 0 && height > 0) {
gtk->gtk_window_resize(dialog, (gint)width, (gint)height);
}
}
gtk->gdk_threads_leave();
}
/*
* baseDir should be freed by user.
*/
static gboolean isFromSameDirectory(GSList* list, gchar** baseDir) {
GSList *it = list;
gchar* prevDir = NULL;
gboolean isAllDirsSame = TRUE;
while (it) {
gchar* dir = gtk->g_path_get_dirname((gchar*) it->data);
if (prevDir && strcmp(prevDir, dir) != 0) {
isAllDirsSame = FALSE;
gtk->g_free(dir);
break;
}
if (!prevDir) {
prevDir = strdup(dir);
}
gtk->g_free(dir);
it = it->next;
}
if (isAllDirsSame) {
*baseDir = prevDir;
} else {
free(prevDir);
*baseDir = strdup("/");
}
return isAllDirsSame;
}
/**
* Convert a GSList to an array of filenames
*/
static jobjectArray toFilenamesArray(JNIEnv *env, GSList* list, jstring* jcurrent_folder)
{
jstring str;
jclass stringCls;
GSList *iterator;
jobjectArray array;
int i;
gchar* entry;
gchar * baseDir;
gboolean isFromSameDir;
if (list == NULL) {
return NULL;
}
stringCls = (*env)->FindClass(env, "java/lang/String");
if (stringCls == NULL) {
(*env)->ExceptionClear(env);
JNU_ThrowInternalError(env, "Could not get java.lang.String class");
return NULL;
}
array = (*env)->NewObjectArray(env, gtk->gtk_g_slist_length(list), stringCls, NULL);
if (array == NULL) {
(*env)->ExceptionClear(env);
JNU_ThrowInternalError(env, "Could not instantiate array files array");
return NULL;
}
isFromSameDir = isFromSameDirectory(list, &baseDir);
*jcurrent_folder = (*env)->NewStringUTF(env, baseDir);
if (*jcurrent_folder == NULL) {
free(baseDir);
return NULL;
}
for (iterator = list, i=0; iterator; iterator = iterator->next, i++) {
entry = (gchar*) iterator->data;
if (isFromSameDir) {
entry = strrchr(entry, '/') + 1;
} else if (entry[0] == '/') {
entry++;
}
str = (*env)->NewStringUTF(env, entry);
if((*env)->ExceptionCheck(env)){
break;
}
if (str) {
(*env)->SetObjectArrayElement(env, array, i, str);
if((*env)->ExceptionCheck(env)){
break;
}
}
}
free(baseDir);
return array;
}
static void handle_response(GtkWidget* aDialog, gint responseId, gpointer obj)
{
JNIEnv *env;
GSList *filenames;
jstring jcurrent_folder = NULL;
jobjectArray jfilenames;
env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
filenames = NULL;
if (responseId == GTK_RESPONSE_ACCEPT) {
filenames = gtk->gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(aDialog));
}
jfilenames = toFilenamesArray(env, filenames, &jcurrent_folder);
if (!(*env)->ExceptionCheck(env)) {
(*env)->CallVoidMethod(env, obj, setFileInternalMethodID, jcurrent_folder, jfilenames);
}
quit(env, (jobject)obj, TRUE);
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_GtkFileDialogPeer_run(JNIEnv * env, jobject jpeer,
jstring jtitle, jint mode, jstring jdir, jstring jfile,
jobject jfilter, jboolean multiple)
{
GtkWidget *dialog = NULL;
GtkFileFilter *filter;
if (jvm == NULL) {
(*env)->GetJavaVM(env, &jvm);
JNU_CHECK_EXCEPTION(env);
}
gtk->gdk_threads_enter();
const char *title = jtitle == NULL? "": (*env)->GetStringUTFChars(env, jtitle, 0);
if (title == NULL) {
(*env)->ExceptionClear(env);
JNU_ThrowOutOfMemoryError(env, "Could not get title");
return;
}
if (mode == java_awt_FileDialog_SAVE) {
dialog = gtk->gtk_file_chooser_dialog_new(title, NULL,
GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
}
else {
dialog = gtk->gtk_file_chooser_dialog_new(title, NULL,
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
/* Set multiple selection mode, that is allowed only in OPEN action */
if (multiple) {
gtk->gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), multiple);
}
}
if (jtitle != NULL) {
(*env)->ReleaseStringUTFChars(env, jtitle, title);
}
/* Set the directory */
if (jdir != NULL) {
const char *dir = (*env)->GetStringUTFChars(env, jdir, 0);
if (dir == NULL) {
(*env)->ExceptionClear(env);
JNU_ThrowOutOfMemoryError(env, "Could not get dir");
return;
}
gtk->gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dir);
(*env)->ReleaseStringUTFChars(env, jdir, dir);
}
/* Set the filename */
if (jfile != NULL) {
const char *filename = (*env)->GetStringUTFChars(env, jfile, 0);
if (filename == NULL) {
(*env)->ExceptionClear(env);
JNU_ThrowOutOfMemoryError(env, "Could not get filename");
return;
}
if (mode == java_awt_FileDialog_SAVE) {
gtk->gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename);
} else {
gtk->gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), filename);
}
(*env)->ReleaseStringUTFChars(env, jfile, filename);
}
/* Set the file filter */
if (jfilter != NULL) {
filter = gtk->gtk_file_filter_new();
gtk->gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME,
filenameFilterCallback, jpeer, NULL);
gtk->gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
}
/* Other Properties */
gtk->gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
// Note: the initial location will be ignored by the Wayland server anyway, so
// we are not doing gtk_window_move() here
gtk->g_signal_connect_data(dialog, "response", G_CALLBACK( handle_response), jpeer, 0, 0);
(*env)->SetLongField(env, jpeer, widgetFieldID, ptr_to_jlong(dialog));
gtk->gtk_widget_show(dialog);
if ((*env)->CallBooleanMethod(env, jpeer, setWindowMethodID, 0)) {
gtk->gtk_main();
}
gtk->gdk_threads_leave();
}

View File

@@ -0,0 +1,214 @@
/*
* 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.
*/
#ifdef HEADLESS
#error This file should not be included in headless library
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <jni.h>
#include <sizecalc.h>
#include "sun_awt_UNIXToolkit.h"
#include "awt.h"
#include "gtk_interface.h"
static jclass toolkitClass = NULL;
static jmethodID loadIconMID = NULL;
JNIEXPORT jboolean JNICALL
Java_sun_awt_UNIXToolkit_check_1gtk(JNIEnv *env, jclass klass, jint version)
{
return (jboolean)gtk_check_version(version);
}
JNIEXPORT jboolean JNICALL
Java_sun_awt_UNIXToolkit_load_1gtk(JNIEnv *env, jclass klass, jint version, jboolean verbose)
{
return (jboolean)gtk_load(env, version, verbose);
}
JNIEXPORT jboolean JNICALL
Java_sun_awt_UNIXToolkit_unload_1gtk(JNIEnv *env, jclass klass)
{
return (jboolean)gtk->unload();
}
static jboolean init_method(JNIEnv *env, jobject this)
{
if (toolkitClass == NULL) {
toolkitClass = (*env)->NewGlobalRef(env,
(*env)->GetObjectClass(env, this));
CHECK_NULL_RETURN(toolkitClass, JNI_FALSE);
loadIconMID = (*env)->GetMethodID(env, toolkitClass,
"loadIconCallback", "([BIIIIIZ)V");
CHECK_NULL_RETURN(loadIconMID, JNI_FALSE);
}
return JNI_TRUE;
}
/*
* Class: sun_awt_UNIXToolkit
* Method: load_gtk_icon
* Signature: (Ljava/lang/String)Z
*
* This method assumes that GTK libs are present.
*/
JNIEXPORT jboolean JNICALL
Java_sun_awt_UNIXToolkit_load_1gtk_1icon(JNIEnv *env, jobject this,
jstring filename)
{
int len;
jsize jlen;
char *filename_str = NULL;
GError **error = NULL;
if (filename == NULL)
{
return JNI_FALSE;
}
len = (*env)->GetStringUTFLength(env, filename);
jlen = (*env)->GetStringLength(env, filename);
filename_str = (char *)SAFE_SIZE_ARRAY_ALLOC(malloc,
sizeof(char), len + 1);
if (filename_str == NULL) {
JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
return JNI_FALSE;
}
if (!init_method(env, this) ) {
free(filename_str);
return JNI_FALSE;
}
(*env)->GetStringUTFRegion(env, filename, 0, jlen, filename_str);
jboolean result = gtk->get_file_icon_data(env, filename_str, error,
loadIconMID, this);
/* Release the strings we've allocated. */
free(filename_str);
return result;
}
/*
* Class: sun_awt_UNIXToolkit
* Method: load_stock_icon
* Signature: (ILjava/lang/String;IILjava/lang/String;)Z
*
* This method assumes that GTK libs are present.
*/
JNIEXPORT jboolean JNICALL
Java_sun_awt_UNIXToolkit_load_1stock_1icon(JNIEnv *env, jobject this,
jint widget_type, jstring stock_id, jint icon_size,
jint text_direction, jstring detail)
{
int len;
jsize jlen;
char *stock_id_str = NULL;
char *detail_str = NULL;
jboolean result = JNI_FALSE;
if (stock_id == NULL)
{
return JNI_FALSE;
}
len = (*env)->GetStringUTFLength(env, stock_id);
jlen = (*env)->GetStringLength(env, stock_id);
stock_id_str = (char *)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(char), len + 1);
if (stock_id_str == NULL) {
JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
return JNI_FALSE;
}
(*env)->GetStringUTFRegion(env, stock_id, 0, jlen, stock_id_str);
/* Detail isn't required so check for NULL. */
if (detail != NULL)
{
len = (*env)->GetStringUTFLength(env, detail);
jlen = (*env)->GetStringLength(env, detail);
detail_str = (char *)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(char), len + 1);
if (detail_str == NULL) {
free(stock_id_str);
JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
return JNI_FALSE;
}
(*env)->GetStringUTFRegion(env, detail, 0, jlen, detail_str);
}
if (init_method(env, this)) {
result = gtk->get_icon_data(env, widget_type, stock_id_str,
icon_size, text_direction, detail_str,
loadIconMID, this);
}
free(stock_id_str);
free(detail_str);
return result;
}
JNIEXPORT void JNICALL
Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
{
typedef void (*SplashClose_t)();
SplashClose_t splashClose;
void* hSplashLib = dlopen(0, RTLD_LAZY);
if (!hSplashLib) {
return;
}
splashClose = (SplashClose_t)dlsym(hSplashLib,
"SplashClose");
if (splashClose) {
splashClose();
}
dlclose(hSplashLib);
}
JNIEXPORT jboolean JNICALL
Java_sun_awt_UNIXToolkit_gtkCheckVersionImpl(JNIEnv *env, jobject this, jint major, jint minor, jint micro)
{
char *ret;
ret = gtk->gtk_check_version(major, minor, micro);
if (ret == NULL) {
return TRUE;
}
return FALSE;
}
JNIEXPORT jint JNICALL
Java_sun_awt_UNIXToolkit_get_1gtk_1version(JNIEnv *env, jclass klass)
{
return gtk ? gtk->version : GTK_ANY;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,755 @@
/*
* Copyright (c) 2005, 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.
*/
#ifdef HEADLESS
#error This file should not be included in headless library
#endif
#ifndef _GTK3_INTERFACE_H
#define _GTK3_INTERFACE_H
#include <stdlib.h>
#include <jni.h>
#include "gtk_interface.h"
#define LIGHTNESS_MULT 1.3
#define DARKNESS_MULT 0.7
#define G_PI 3.1415926535897932384626433832795028841971693993751
typedef enum
{
GTK_STATE_FLAG_NORMAL = 0,
GTK_STATE_FLAG_ACTIVE = 1 << 0,
GTK_STATE_FLAG_PRELIGHT = 1 << 1,
GTK_STATE_FLAG_SELECTED = 1 << 2,
GTK_STATE_FLAG_INSENSITIVE = 1 << 3,
GTK_STATE_FLAG_INCONSISTENT = 1 << 4,
GTK_STATE_FLAG_FOCUSED = 1 << 5,
GTK_STATE_FLAG_BACKDROP = 1 << 6,
GTK_STATE_FLAG_DIR_LTR = 1 << 7,
GTK_STATE_FLAG_DIR_RTL = 1 << 8,
GTK_STATE_FLAG_LINK = 1 << 9,
GTK_STATE_FLAG_VISITED = 1 << 10,
GTK_STATE_FLAG_CHECKED = 1 << 11
} GtkStateFlags;
typedef enum {
GTK_JUNCTION_NONE = 0,
GTK_JUNCTION_CORNER_TOPLEFT = 1 << 0,
GTK_JUNCTION_CORNER_TOPRIGHT = 1 << 1,
GTK_JUNCTION_CORNER_BOTTOMLEFT = 1 << 2,
GTK_JUNCTION_CORNER_BOTTOMRIGHT = 1 << 3,
GTK_JUNCTION_TOP =
(GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_TOPRIGHT),
GTK_JUNCTION_BOTTOM =
(GTK_JUNCTION_CORNER_BOTTOMLEFT | GTK_JUNCTION_CORNER_BOTTOMRIGHT),
GTK_JUNCTION_LEFT =
(GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_BOTTOMLEFT),
GTK_JUNCTION_RIGHT =
(GTK_JUNCTION_CORNER_TOPRIGHT | GTK_JUNCTION_CORNER_BOTTOMRIGHT)
} GtkJunctionSides;
typedef enum {
GTK_REGION_EVEN = 1 << 0,
GTK_REGION_ODD = 1 << 1,
GTK_REGION_FIRST = 1 << 2,
GTK_REGION_LAST = 1 << 3,
GTK_REGION_ONLY = 1 << 4,
GTK_REGION_SORTED = 1 << 5
} GtkRegionFlags;
typedef enum
{
GTK_WINDOW_TOPLEVEL,
GTK_WINDOW_POPUP
} GtkWindowType;
typedef enum
{
G_PARAM_READABLE = 1 << 0,
G_PARAM_WRITABLE = 1 << 1,
G_PARAM_CONSTRUCT = 1 << 2,
G_PARAM_CONSTRUCT_ONLY = 1 << 3,
G_PARAM_LAX_VALIDATION = 1 << 4,
G_PARAM_STATIC_NAME = 1 << 5
} GParamFlags;
typedef enum
{
GTK_ICON_LOOKUP_NO_SVG = 1 << 0,
GTK_ICON_LOOKUP_FORCE_SVG = 1 << 1,
GTK_ICON_LOOKUP_USE_BUILTIN = 1 << 2,
GTK_ICON_LOOKUP_GENERIC_FALLBACK = 1 << 3,
GTK_ICON_LOOKUP_FORCE_SIZE = 1 << 4
} GtkIconLookupFlags;
typedef enum
{
GTK_UPDATE_CONTINUOUS,
GTK_UPDATE_DISCONTINUOUS,
GTK_UPDATE_DELAYED
} GtkUpdateType;
typedef enum
{
GTK_PROGRESS_CONTINUOUS,
GTK_PROGRESS_DISCRETE
} GtkProgressBarStyle;
typedef enum
{
GTK_PROGRESS_LEFT_TO_RIGHT,
GTK_PROGRESS_RIGHT_TO_LEFT,
GTK_PROGRESS_BOTTOM_TO_TOP,
GTK_PROGRESS_TOP_TO_BOTTOM
} GtkProgressBarOrientation;
typedef enum {
CAIRO_FORMAT_INVALID = -1,
CAIRO_FORMAT_ARGB32 = 0,
CAIRO_FORMAT_RGB24 = 1,
CAIRO_FORMAT_A8 = 2,
CAIRO_FORMAT_A1 = 3,
CAIRO_FORMAT_RGB16_565 = 4
} cairo_format_t;
typedef enum _cairo_status {
CAIRO_STATUS_SUCCESS = 0,
CAIRO_STATUS_NO_MEMORY,
CAIRO_STATUS_INVALID_RESTORE,
CAIRO_STATUS_INVALID_POP_GROUP,
CAIRO_STATUS_NO_CURRENT_POINT,
CAIRO_STATUS_INVALID_MATRIX,
CAIRO_STATUS_INVALID_STATUS,
CAIRO_STATUS_NULL_POINTER,
CAIRO_STATUS_INVALID_STRING,
CAIRO_STATUS_INVALID_PATH_DATA,
CAIRO_STATUS_READ_ERROR,
CAIRO_STATUS_WRITE_ERROR,
CAIRO_STATUS_SURFACE_FINISHED,
CAIRO_STATUS_SURFACE_TYPE_MISMATCH,
CAIRO_STATUS_PATTERN_TYPE_MISMATCH,
CAIRO_STATUS_INVALID_CONTENT,
CAIRO_STATUS_INVALID_FORMAT,
CAIRO_STATUS_INVALID_VISUAL,
CAIRO_STATUS_FILE_NOT_FOUND,
CAIRO_STATUS_INVALID_DASH,
CAIRO_STATUS_INVALID_DSC_COMMENT,
CAIRO_STATUS_INVALID_INDEX,
CAIRO_STATUS_CLIP_NOT_REPRESENTABLE,
CAIRO_STATUS_TEMP_FILE_ERROR,
CAIRO_STATUS_INVALID_STRIDE,
CAIRO_STATUS_FONT_TYPE_MISMATCH,
CAIRO_STATUS_USER_FONT_IMMUTABLE,
CAIRO_STATUS_USER_FONT_ERROR,
CAIRO_STATUS_NEGATIVE_COUNT,
CAIRO_STATUS_INVALID_CLUSTERS,
CAIRO_STATUS_INVALID_SLANT,
CAIRO_STATUS_INVALID_WEIGHT,
CAIRO_STATUS_INVALID_SIZE,
CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED,
CAIRO_STATUS_DEVICE_TYPE_MISMATCH,
CAIRO_STATUS_DEVICE_ERROR,
CAIRO_STATUS_LAST_STATUS
} cairo_status_t;
/* We define all structure pointers to be void* */
typedef void GVfs;
typedef void GdkColormap;
typedef void GdkDrawable;
typedef void GdkGC;
typedef void GdkPixmap;
typedef void GtkStyleContext;
typedef void GtkFixed;
typedef void GtkMenuItem;
typedef void GtkMenuShell;
typedef void GtkWidgetClass;
typedef void PangoFontDescription;
typedef void GtkSettings;
typedef void GtkStyleProvider;
typedef void cairo_pattern_t;
typedef void cairo_t;
typedef void cairo_surface_t;
typedef void GtkScrolledWindow;
typedef void GtkIconTheme;
typedef void GtkWidget;
typedef void GtkMisc;
typedef void GtkContainer;
typedef void GtkBin;
typedef void GtkAdjustment;
typedef void GtkRange;
typedef void GtkProgressBar;
typedef void GtkProgress;
typedef void GtkWidgetPath;
typedef void GtkPaned;
/* Some real structures */
typedef struct
{
guint32 pixel;
guint16 red;
guint16 green;
guint16 blue;
} GdkColor;
typedef struct
{
gdouble red;
gdouble green;
gdouble blue;
gdouble alpha;
} GdkRGBA;
typedef struct {
gint fd;
gushort events;
gushort revents;
} GPollFD;
typedef struct {
int x, y;
int width, height;
} GtkAllocation;
typedef struct {
gint width;
gint height;
} GtkRequisition;
typedef struct {
GtkWidgetClass *g_class;
} GTypeInstance;
typedef struct {
gint16 left;
gint16 right;
gint16 top;
gint16 bottom;
} GtkBorder;
typedef struct
{
GType g_type;
union {
gint v_int;
guint v_uint;
glong v_long;
gulong v_ulong;
gint64 v_int64;
guint64 v_uint64;
gfloat v_float;
gdouble v_double;
gpointer v_pointer;
} data[2];
} GValue;
typedef struct {
GTypeInstance g_type_instance;
const gchar *name;
GParamFlags flags;
GType value_type;
GType owner_type;
} GParamSpec;
static gchar* (*fp_glib_check_version)(guint required_major,
guint required_minor, guint required_micro);
/**
* Returns :
* NULL if the GTK+ library is compatible with the given version, or a string
* describing the version mismatch.
*/
static gchar* (*fp_gtk_check_version)(guint required_major, guint
required_minor, guint required_micro);
static void (*fp_g_free)(gpointer mem);
static void (*fp_g_object_unref)(gpointer object);
static int (*fp_gdk_pixbuf_get_bits_per_sample)(const GdkPixbuf *pixbuf);
static guchar *(*fp_gdk_pixbuf_get_pixels)(const GdkPixbuf *pixbuf);
static gboolean (*fp_gdk_pixbuf_get_has_alpha)(const GdkPixbuf *pixbuf);
static int (*fp_gdk_pixbuf_get_height)(const GdkPixbuf *pixbuf);
static int (*fp_gdk_pixbuf_get_n_channels)(const GdkPixbuf *pixbuf);
static int (*fp_gdk_pixbuf_get_rowstride)(const GdkPixbuf *pixbuf);
static int (*fp_gdk_pixbuf_get_width)(const GdkPixbuf *pixbuf);
static GdkPixbuf *(*fp_gdk_pixbuf_new_from_file)(const char *filename,
GError **error);
static GdkColorspace (*fp_gdk_pixbuf_get_colorspace)(const GdkPixbuf *pixbuf);
static GdkPixbuf *(*fp_gdk_pixbuf_get_from_drawable)(GdkWindow *window,
int src_x, int src_y, int width, int height);
static GdkPixbuf *(*fp_gdk_pixbuf_scale_simple)(GdkPixbuf *src,
int dest_width, int dest_heigh, GdkInterpType interp_type);
static void (*fp_gtk_widget_destroy)(void *widget);
static void (*fp_gtk_window_present)(GtkWindow *window);
static void (*fp_gtk_window_present_with_time)(GtkWindow *window, guint32 timestamp);
static void (*fp_gtk_window_move)(GtkWindow *window, gint x, gint y);
static void (*fp_gtk_window_resize)(GtkWindow *window, gint width, gint height);
/**
* Function Pointers for GtkFileChooser
*/
static gchar* (*fp_gtk_file_chooser_get_filename)(GtkFileChooser *chooser);
static void (*fp_gtk_widget_hide)(void *widget);
static void (*fp_gtk_main_quit)(void);
static void* (*fp_gtk_file_chooser_dialog_new)(const gchar *title,
GtkWindow *parent, GtkFileChooserAction action,
const gchar *first_button_text, ...);
static gboolean (*fp_gtk_file_chooser_set_current_folder)
(GtkFileChooser *chooser, const gchar *filename);
static gboolean (*fp_gtk_file_chooser_set_filename)(GtkFileChooser *chooser, const char *filename);
static void (*fp_gtk_file_chooser_set_current_name)(GtkFileChooser *chooser, const gchar *name);
static void (*fp_gtk_file_filter_add_custom)(GtkFileFilter *filter,
GtkFileFilterFlags needed, GtkFileFilterFunc func, gpointer data,
GDestroyNotify notify);
static void (*fp_gtk_file_chooser_set_filter)(GtkFileChooser *chooser, GtkFileFilter *filter);
static GType (*fp_gtk_file_chooser_get_type)(void);
static GtkFileFilter* (*fp_gtk_file_filter_new)(void);
static void (*fp_gtk_file_chooser_set_do_overwrite_confirmation)(
GtkFileChooser *chooser, gboolean do_overwrite_confirmation);
static void (*fp_gtk_file_chooser_set_select_multiple)(
GtkFileChooser *chooser, gboolean select_multiple);
static gchar* (*fp_gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser);
static GSList* (*fp_gtk_file_chooser_get_filenames)(GtkFileChooser *chooser);
static guint (*fp_gtk_g_slist_length)(GSList *list);
static gulong (*fp_g_signal_connect_data)(gpointer instance,
const gchar *detailed_signal, GCallback c_handler, gpointer data,
GClosureNotify destroy_data, GConnectFlags connect_flags);
static void (*fp_gtk_widget_show)(void *widget);
static void (*fp_gtk_main)(void);
static guint (*fp_gtk_main_level)(void);
static gchar* (*fp_g_path_get_dirname) (const gchar *file_name);
static GList* (*fp_g_list_append) (GList *list, gpointer data);
static void (*fp_g_list_free) (GList *list);
static void (*fp_g_list_free_full) (GList *list, GDestroyNotify free_func);
static void (*fp_gdk_threads_enter)(void);
static void (*fp_gdk_threads_leave)(void);
static gboolean (*fp_gtk_show_uri_on_window)(GdkWindow *parent, const gchar *uri,
guint32 timestamp, GError **error);
// Implementation functions prototypes
static GValue* (*fp_g_value_init)(GValue *value, GType g_type);
static gboolean (*fp_g_type_is_a)(GType type, GType is_a_type);
static gboolean (*fp_g_value_get_boolean)(const GValue *value);
static gchar (*fp_g_value_get_char)(const GValue *value);
static guchar (*fp_g_value_get_uchar)(const GValue *value);
static gint (*fp_g_value_get_int)(const GValue *value);
static guint (*fp_g_value_get_uint)(const GValue *value);
static glong (*fp_g_value_get_long)(const GValue *value);
static gulong (*fp_g_value_get_ulong)(const GValue *value);
static gint64 (*fp_g_value_get_int64)(const GValue *value);
static guint64 (*fp_g_value_get_uint64)(const GValue *value);
static gfloat (*fp_g_value_get_float)(const GValue *value);
static gdouble (*fp_g_value_get_double)(const GValue *value);
static const gchar* (*fp_g_value_get_string)(const GValue *value);
static gint (*fp_g_value_get_enum)(const GValue *value);
static guint (*fp_g_value_get_flags)(const GValue *value);
static GParamSpec* (*fp_g_value_get_param)(const GValue *value);
static gpointer* (*fp_g_value_get_boxed)(const GValue *value);
static gpointer* (*fp_g_value_get_pointer)(const GValue *value);
static void (*fp_g_object_get)(gpointer object, const gchar* fpn, ...);
static void (*fp_g_object_set)(gpointer object, const gchar *first_property_name, ...);
static gboolean (*fp_g_main_context_iteration)(GMainContext *context, gboolean may_block);
static GMainContext *(*fp_g_main_context_default)();
static gboolean (*fp_g_main_context_is_owner)(GMainContext* context);
static gboolean (*fp_g_str_has_prefix)(const gchar *str, const gchar *prefix);
static gchar** (*fp_g_strsplit)(const gchar *string, const gchar *delimiter, gint max_tokens);
static void (*fp_g_strfreev)(gchar **str_array);
static cairo_surface_t* (*fp_cairo_image_surface_create)(cairo_format_t format,
int width, int height);
static void (*fp_cairo_surface_destroy)(cairo_surface_t *surface);
static cairo_status_t (*fp_cairo_surface_status)(cairo_surface_t *surface);
static cairo_t* (*fp_cairo_create)(cairo_surface_t *target);
static void (*fp_cairo_destroy)(cairo_t *cr);
static cairo_status_t (*fp_cairo_status)(cairo_t *cr);
static void (*fp_cairo_fill)(cairo_t *cr);
static void (*fp_cairo_surface_flush)(cairo_surface_t *surface);
static void (*fp_cairo_rectangle)(cairo_t *cr, double x, double y, double width, double height);
static void (*fp_cairo_set_source_rgb)(cairo_t *cr, double red, double green, double blue);
static void (*fp_cairo_set_source_rgba)(cairo_t *cr, double red, double green, double blue, double alpha);
static void (*fp_cairo_paint)(cairo_t *cr);
static void (*fp_cairo_clip)(cairo_t *cr);
static unsigned char* (*fp_cairo_image_surface_get_data)(
cairo_surface_t *surface);
static int (*fp_cairo_image_surface_get_stride) (cairo_surface_t *surface);
static GdkPixbuf* (*fp_gdk_pixbuf_get_from_surface)(cairo_surface_t *surface,
gint src_x, gint src_y, gint width, gint height);
static GtkStateType (*fp_gtk_widget_get_state)(GtkWidget *widget);
static void (*fp_gtk_widget_set_state)(GtkWidget *widget, GtkStateType state);
static gboolean (*fp_gtk_widget_is_focus)(GtkWidget *widget);
static void (*fp_gtk_widget_set_allocation)(GtkWidget *widget, const GtkAllocation *allocation);
static GtkWidget* (*fp_gtk_widget_get_parent)(GtkWidget *widget);
static GtkStyleContext* (*fp_gtk_widget_get_style_context)(GtkWidget *widget);
static void (*fp_gtk_style_context_get_color)(GtkStyleContext *context,
GtkStateFlags state, GdkRGBA *color);
static void (*fp_gtk_style_context_get_background_color)
(GtkStyleContext *context, GtkStateFlags state, GdkRGBA *color);
static void (*fp_gtk_style_context_get)(GtkStyleContext *context, GtkStateFlags state, ...);
static GtkStateFlags (*fp_gtk_widget_get_state_flags)(GtkWidget* widget);
static void (*fp_gtk_style_context_set_state)(GtkStyleContext* style, GtkStateFlags flags);
static void (*fp_gtk_style_context_add_class)(GtkStyleContext *context, const gchar *class_name);
static void (*fp_gtk_style_context_save)(GtkStyleContext *context);
static void (*fp_gtk_style_context_restore)(GtkStyleContext *context);
static void (*fp_gtk_render_check)(GtkStyleContext *context, cairo_t *cr,
gdouble x, gdouble y, gdouble width, gdouble height);
static void (*fp_gtk_render_option)(GtkStyleContext *context, cairo_t *cr,
gdouble x, gdouble y, gdouble width, gdouble height);
static void (*fp_gtk_render_extension)(GtkStyleContext *context, cairo_t *cr,
gdouble x, gdouble y, gdouble width, gdouble height,
GtkPositionType gap_side);
static void (*fp_gtk_render_expander)(GtkStyleContext *context, cairo_t *cr,
gdouble x, gdouble y, gdouble width, gdouble height);
static void (*fp_gtk_render_frame_gap)(GtkStyleContext *context, cairo_t *cr,
gdouble x, gdouble y, gdouble width, gdouble height,
GtkPositionType gap_side, gdouble xy0_gap,
gdouble xy1_gap);
static void (*fp_gtk_render_line)(GtkStyleContext *context, cairo_t *cr,
gdouble x0, gdouble y0, gdouble x1, gdouble y1);
static GdkPixbuf* (*fp_gtk_widget_render_icon_pixbuf)(GtkWidget *widget,
const gchar *stock_id, GtkIconSize size);
static cairo_surface_t* (*fp_gdk_window_create_similar_image_surface)(
GdkWindow *window, cairo_format_t format, int width,
int height, int scale);
static cairo_surface_t* (*fp_gdk_window_create_similar_surface)(
GdkWindow *window, cairo_format_t format,
int width, int height);
static GdkWindow* (*fp_gtk_widget_get_window)(GtkWidget *widget);
static GtkSettings *(*fp_gtk_settings_get_for_screen)(GdkScreen *screen);
static GdkScreen *(*fp_gtk_widget_get_screen)(GtkWidget *widget);
static GtkStyleProvider* (*fp_gtk_css_provider_get_named)(const gchar *name,
const gchar *variant);
static void (*fp_gtk_style_context_add_provider)(GtkStyleContext *context,
GtkStyleProvider *provider, guint priority);
static void (*fp_gtk_render_frame)(GtkStyleContext *context,cairo_t *cr,
gdouble x, gdouble y, gdouble width, gdouble height);
static void (*fp_gtk_render_focus)(GtkStyleContext *context,cairo_t *cr,
gdouble x, gdouble y, gdouble width, gdouble height);
static void (*fp_gtk_render_handle)(GtkStyleContext *context,cairo_t *cr,
gdouble x, gdouble y, gdouble width, gdouble height);
static void (*fp_gtk_style_context_get_property)(GtkStyleContext *context,
const gchar *property, GtkStateFlags state, GValue *value);
static void (*fp_gtk_render_activity)(GtkStyleContext *context, cairo_t *cr,
gdouble x, gdouble y, gdouble width, gdouble height);
static void (*fp_gtk_render_background)(GtkStyleContext *context, cairo_t *cr,
gdouble x, gdouble y, gdouble width, gdouble height);
static gboolean (*fp_gtk_style_context_has_class)(GtkStyleContext *context,
const gchar *class_name);
static void (*fp_gtk_style_context_set_junction_sides)(GtkStyleContext *context,
GtkJunctionSides sides);
static void (*fp_gtk_style_context_add_region)(GtkStyleContext *context,
const gchar *region_name, GtkRegionFlags flags);
static void (*fp_gtk_render_arrow)(GtkStyleContext *context, cairo_t *cr,
gdouble angle, gdouble x, gdouble y, gdouble size);
static void (*fp_gtk_bin_set_child)(GtkBin *bin, GtkWidget *widget);
static void (*fp_gtk_scrolled_window_set_shadow_type)(
GtkScrolledWindow *scrolled_window, GtkShadowType type);
static void (*fp_gtk_render_slider)(GtkStyleContext *context, cairo_t *cr,
gdouble x, gdouble y, gdouble width, gdouble height,
GtkOrientation orientation);
static void (*fp_gtk_style_context_get_padding)(GtkStyleContext *self,
GtkStateFlags state, GtkBorder* padding);
static void (*fp_gtk_range_set_inverted)(GtkRange *range, gboolean setting);
static PangoFontDescription* (*fp_gtk_style_context_get_font)(
GtkStyleContext *context, GtkStateFlags state);
static int (*fp_gtk_widget_get_allocated_width)(GtkWidget *widget);
static int (*fp_gtk_widget_get_allocated_height)(GtkWidget *widget);
static GtkIconTheme* (*fp_gtk_icon_theme_get_default)(void);
static GdkPixbuf* (*fp_gtk_icon_theme_load_icon)(GtkIconTheme *icon_theme,
const gchar *icon_name, gint size,
GtkIconLookupFlags flags, GError **error);
static void (*fp_gtk_adjustment_set_lower)(GtkAdjustment *adjustment, gdouble lower);
static void (*fp_gtk_adjustment_set_page_increment)(GtkAdjustment *adjustment,
gdouble page_increment);
static void (*fp_gtk_adjustment_set_page_size)(GtkAdjustment *adjustment, gdouble page_size);
static void (*fp_gtk_adjustment_set_step_increment)(GtkAdjustment *adjustment, gdouble step_increment);
static void (*fp_gtk_adjustment_set_upper)(GtkAdjustment *adjustment, gdouble upper);
static void (*fp_gtk_adjustment_set_value)(GtkAdjustment *adjustment, gdouble value);
static GdkGC *(*fp_gdk_gc_new)(GdkDrawable*);
static void (*fp_gdk_rgb_gc_set_foreground)(GdkGC*, guint32);
static void (*fp_gdk_draw_rectangle)(GdkDrawable*, GdkGC*, gboolean, gint, gint, gint, gint);
static GdkPixbuf *(*fp_gdk_pixbuf_new)(GdkColorspace colorspace,
gboolean has_alpha, int bits_per_sample, int width, int height);
static GdkPixbuf *(*fp_gdk_pixbuf_new_from_data)(
const guchar *data,
GdkColorspace colorspace,
gboolean has_alpha,
int bits_per_sample,
int width,
int height,
int rowstride,
GdkPixbufDestroyNotify destroy_fn,
gpointer destroy_fn_data
);
static void (*fp_gdk_pixbuf_copy_area) (
const GdkPixbuf* src_pixbuf,
int src_x,
int src_y,
int width,
int height,
GdkPixbuf* dest_pixbuf,
int dest_x,
int dest_y
);
static void (*fp_gdk_drawable_get_size)(GdkDrawable *drawable, gint* width, gint* height);
static gboolean (*fp_gtk_init_check)(int* argc, char** argv);
/* Widget creation */
static GtkWidget* (*fp_gtk_arrow_new)(GtkArrowType arrow_type, GtkShadowType shadow_type);
static GtkWidget* (*fp_gtk_button_new)();
static GtkWidget* (*fp_gtk_check_button_new)();
static GtkWidget* (*fp_gtk_check_menu_item_new)();
static GtkWidget* (*fp_gtk_color_selection_dialog_new)(const gchar* title);
static GtkWidget* (*fp_gtk_combo_box_new)();
static GtkWidget* (*fp_gtk_combo_box_entry_new)();
static GtkWidget* (*fp_gtk_entry_new)();
static GtkWidget* (*fp_gtk_fixed_new)();
static GtkWidget* (*fp_gtk_handle_box_new)();
static GtkWidget* (*fp_gtk_paned_new)(GtkOrientation orientation);
static GtkWidget* (*fp_gtk_scale_new)(GtkOrientation orientation, GtkAdjustment* adjustment);
static GtkWidget* (*fp_gtk_hscrollbar_new)(GtkAdjustment* adjustment);
static GtkWidget* (*fp_gtk_vscrollbar_new)(GtkAdjustment* adjustment);
static GtkWidget* (*fp_gtk_hseparator_new)();
static GtkWidget* (*fp_gtk_vseparator_new)();
static GtkWidget* (*fp_gtk_image_new)();
static GtkWidget* (*fp_gtk_label_new)(const gchar* str);
static GtkWidget* (*fp_gtk_menu_new)();
static GtkWidget* (*fp_gtk_menu_bar_new)();
static GtkWidget* (*fp_gtk_menu_item_new)();
static GtkWidget* (*fp_gtk_notebook_new)();
static GtkWidget* (*fp_gtk_progress_bar_new)();
static GtkWidget* (*fp_gtk_progress_bar_set_orientation)(
GtkProgressBar *pbar,
GtkProgressBarOrientation orientation);
static GtkWidget* (*fp_gtk_radio_button_new)(GSList *group);
static GtkWidget* (*fp_gtk_radio_menu_item_new)(GSList *group);
static GtkWidget* (*fp_gtk_scrolled_window_new)(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment);
static GtkWidget* (*fp_gtk_separator_menu_item_new)();
static GtkWidget* (*fp_gtk_separator_tool_item_new)();
static GtkWidget* (*fp_gtk_text_view_new)();
static GtkWidget* (*fp_gtk_toggle_button_new)();
static GtkWidget* (*fp_gtk_toolbar_new)();
static GtkWidget* (*fp_gtk_tree_view_new)();
static GtkWidget* (*fp_gtk_viewport_new)(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment);
static GtkWidget* (*fp_gtk_window_new)(GtkWindowType type);
static GtkWidget* (*fp_gtk_dialog_new)();
static GtkWidget* (*fp_gtk_spin_button_new)(GtkAdjustment *adjustment,
gdouble climb_rate, guint digits);
static GtkWidget* (*fp_gtk_frame_new)(const gchar *label);
/* Other widget operations */
static GtkAdjustment* (*fp_gtk_adjustment_new)(gdouble value,
gdouble lower, gdouble upper, gdouble step_increment,
gdouble page_increment, gdouble page_size);
static void (*fp_gtk_container_add)(GtkContainer *window, GtkWidget *widget);
static void (*fp_gtk_menu_shell_append)(GtkMenuShell *menu_shell, GtkWidget *child);
static void (*fp_gtk_menu_item_set_submenu)(GtkMenuItem *menu_item, GtkWidget *submenu);
static void (*fp_gtk_widget_realize)(GtkWidget *widget);
static GdkPixbuf* (*fp_gtk_widget_render_icon)(GtkWidget *widget,
const gchar *stock_id, GtkIconSize size, const gchar *detail);
static void (*fp_gtk_widget_set_name)(GtkWidget *widget, const gchar *name);
static void (*fp_gtk_widget_set_parent)(GtkWidget *widget, GtkWidget *parent);
static void (*fp_gtk_widget_set_direction)(GtkWidget *widget, GtkTextDirection direction);
static void (*fp_gtk_widget_style_get)(GtkWidget *widget, const gchar *first_property_name, ...);
static void (*fp_gtk_widget_class_install_style_property)(GtkWidgetClass* class, GParamSpec *pspec);
static GParamSpec* (*fp_gtk_widget_class_find_style_property)(
GtkWidgetClass* class, const gchar* property_name);
static void (*fp_gtk_widget_style_get_property)(GtkWidget* widget,
const gchar* property_name, GValue* value);
static char* (*fp_pango_font_description_to_string)(const PangoFontDescription* fd);
static GtkSettings* (*fp_gtk_settings_get_default)();
static GtkSettings* (*fp_gtk_widget_get_settings)(GtkWidget *widget);
static GType (*fp_gtk_border_get_type)();
static void (*fp_gtk_arrow_set)(GtkWidget* arrow,
GtkArrowType arrow_type,
GtkShadowType shadow_type);
static void (*fp_gtk_widget_size_request)(GtkWidget *widget, GtkRequisition *requisition);
static GtkAdjustment* (*fp_gtk_range_get_adjustment)(GtkRange* range);
static GtkWidgetPath* (*fp_gtk_widget_path_copy)(const GtkWidgetPath *path);
static const GtkWidgetPath* (*fp_gtk_style_context_get_path)(GtkStyleContext *context);
static GtkWidgetPath* (*fp_gtk_widget_path_new) (void);
static gint (*fp_gtk_widget_path_append_type)(GtkWidgetPath *path, GType type);
static void (*fp_gtk_widget_path_iter_set_object_name)(GtkWidgetPath *path, gint pos, const char *name);
static void (*fp_gtk_style_context_set_path)(GtkStyleContext *context, GtkWidgetPath *path);
static void (*fp_gtk_widget_path_unref) (GtkWidgetPath *path);
static GtkStyleContext* (*fp_gtk_style_context_new) (void);
// ---------- fp_g_dbus_* ----------
static GVariant *(*fp_g_dbus_proxy_call_sync)(
GDBusProxy *proxy,
const gchar *method_name,
GVariant *parameters,
GDBusCallFlags flags,
gint timeout_msec,
GCancellable *cancellable,
GError **error
);
static GDBusProxy *(*fp_g_dbus_proxy_new_sync)(
GDBusConnection *connection,
GDBusProxyFlags flags,
GDBusInterfaceInfo *info,
const gchar *name,
const gchar *object_path,
const gchar *interface_name,
GCancellable *cancellable,
GError **error
);
static const gchar *(*fp_g_dbus_connection_get_unique_name)(
GDBusConnection *connection
);
static GDBusConnection *(*fp_g_bus_get_sync)(GBusType bus_type,
GCancellable *cancellable,
GError **error);
static guint (*fp_g_dbus_connection_signal_subscribe)(
GDBusConnection *connection,
const gchar *sender,
const gchar *interface_name,
const gchar *member,
const gchar *object_path,
const gchar *arg0,
GDBusSignalFlags flags,
GDBusSignalCallback callback,
gpointer user_data,
GDestroyNotify user_data_free_func
);
static void (*fp_g_dbus_connection_signal_unsubscribe)(
GDBusConnection *connection,
guint subscription_id
);
static GVariant *(*fp_g_dbus_proxy_call_with_unix_fd_list_sync)(
GDBusProxy *proxy,
const gchar *method_name,
GVariant *parameters,
GDBusCallFlags flags,
gint timeout_msec,
GUnixFDList *fd_list,
GUnixFDList **out_fd_list,
GCancellable *cancellable,
GError **error
);
static GVariant *(*fp_g_dbus_connection_call_sync)(
GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
const GVariantType *reply_type,
GDBusCallFlags flags,
gint timeout_msec,
GCancellable *cancellable,
GError **error
);
// ---------- fp_g_variant_* ----------
static GVariant *(*fp_g_variant_new)(const gchar *format_string, ...);
static GVariant *(*fp_g_variant_new_string)(const gchar *string);
static GVariant *(*fp_g_variant_new_boolean)(gboolean value);
static GVariant *(*fp_g_variant_new_uint32)(guint32 value);
static void (*fp_g_variant_get)(GVariant *value, const gchar *format_string, ...);
static const gchar *(*fp_g_variant_get_string)(GVariant *value, gsize *length);
static guint32 (*fp_g_variant_get_uint32)(GVariant *value);
static gboolean (*fp_g_variant_lookup)(GVariant *dictionary,
const gchar *key,
const gchar *format_string,
...);
static gboolean (*fp_g_variant_iter_loop)(GVariantIter *iter, const gchar *format_string, ...);
static void (*fp_g_variant_unref)(GVariant *value);
static void (*fp_g_variant_builder_init)(GVariantBuilder *builder, const GVariantType *type);
static void (*fp_g_variant_builder_add)(GVariantBuilder *builder, const gchar *format_string, ...);
static GVariant *(*fp_g_variant_lookup_value)(GVariant *dictionary,
const gchar *key,
const GVariantType *expected_type);
static gsize (*fp_g_variant_iter_init)(GVariantIter *iter, GVariant *value);
static gsize (*fp_g_variant_iter_n_children)(GVariantIter *iter);
// ---------- fp_g_string_* ----------
static GString *(*fp_g_string_new)(const gchar *init);
static GString *(*fp_g_string_erase)(GString *string, gssize pos, gssize len);
static GString *(*fp_g_string_set_size)(GString* string, gsize len);
static gchar *(*fp_g_string_free)(GString *string, gboolean free_segment);
static guint (*fp_g_string_replace)(GString *string,
const gchar *find,
const gchar *replace,
guint limit);
static void *(*fp_g_string_printf)(GString *string, const gchar *format, ...);
static gboolean (*fp_g_uuid_string_is_valid)(const gchar *str);
// ---------- * ----------
static void (*fp_g_error_free)(GError *error);
static gint (*fp_g_unix_fd_list_get)(GUnixFDList *list, gint index_, GError **error);
#endif /* !_GTK3_INTERFACE_H */

View File

@@ -0,0 +1,164 @@
/*
* Copyright (c) 2005, 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.
*/
#ifdef HEADLESS
#error This file should not be included in headless library
#endif
#include <dlfcn.h>
#include <stdlib.h>
#include "jvm_md.h"
#include "gtk_interface.h"
GtkApi* gtk3_load(JNIEnv *env, const char* lib_name);
gboolean gtk3_check(const char* lib_name, gboolean load);
GtkApi *gtk;
typedef struct {
GtkVersion version;
const char* name;
const char* vname;
GtkApi* (*load)(JNIEnv *env, const char* lib_name);
gboolean (*check)(const char* lib_name, gboolean load);
} GtkLib;
static GtkLib gtk_libs[] = {
{
GTK_3,
JNI_LIB_NAME("gtk-3"),
VERSIONED_JNI_LIB_NAME("gtk-3", "0"),
&gtk3_load,
&gtk3_check
},
};
static GtkLib** get_libs_order(GtkVersion version) {
static GtkLib** load_order;
static int n_libs = 0;
if (!n_libs) {
n_libs = sizeof(gtk_libs) / sizeof(GtkLib);
load_order = calloc(n_libs + 1, sizeof(GtkLib *));
if (load_order == NULL) {
return NULL;
}
}
int i, first = 0;
for (i = 0; i < n_libs; i++) {
load_order[i] = &gtk_libs[i];
if (load_order[i]->version == version) {
first = i;
}
}
if (first) {
for (i = first; i > 0; i--) {
load_order[i] = load_order[i - 1];
}
load_order[0] = &gtk_libs[first];
}
return load_order;
}
static GtkLib* get_loaded() {
GtkLib** libs = get_libs_order(GTK_ANY);
if (libs == NULL) return NULL;
while(!gtk && *libs) {
GtkLib* lib = *libs++;
if (lib->check(lib->vname, /* load = */FALSE)) {
return lib;
}
if (lib->check(lib->name, /* load = */FALSE)) {
return lib;
}
}
return NULL;
}
gboolean gtk_load(JNIEnv *env, GtkVersion version, gboolean verbose) {
if (gtk == NULL) {
GtkLib* lib = get_loaded();
if (lib) {
if (verbose) {
fprintf(stderr, "Looking for GTK%d library...\n", lib->version);
}
gtk = lib->load(env, lib->vname);
if (!gtk) {
gtk = lib->load(env, lib->name);
}
} else {
GtkLib** libs = get_libs_order(version);
while (!gtk && libs && *libs) {
lib = *libs++;
if (version == GTK_ANY || lib->version == version) {
if (verbose) {
fprintf(stderr, "Looking for GTK%d library...\n", lib->version);
}
gtk = lib->load(env, lib->vname);
if (!gtk) {
gtk = lib->load(env, lib->name);
}
if (verbose && !gtk) {
fprintf(stderr, "Not found.\n");
}
}
}
}
if (verbose) {
if (gtk) {
fprintf(stderr, "GTK%d library loaded.\n", lib->version);
} else {
fprintf(stderr, "Failed to load GTK library.\n");
}
}
}
return gtk != NULL;
}
static gboolean check_version(GtkVersion version) {
GtkLib** libs = get_libs_order(version);
if (libs == NULL) return FALSE;
while (*libs) {
GtkLib* lib = *libs++;
if (lib->check(lib->vname, /* load = */TRUE)) {
return TRUE;
}
if (lib->check(lib->name, /* load = */TRUE)) {
return TRUE;
}
}
return FALSE;
}
gboolean gtk_check_version(GtkVersion version) {
if (gtk || get_loaded()) {
return TRUE;
}
return check_version(version);
}

View File

@@ -0,0 +1,835 @@
/*
* Copyright (c) 2005, 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.
*/
#ifdef HEADLESS
#error This file should not be included in headless library
#endif
#ifndef _GTK_INTERFACE_H
#define _GTK_INTERFACE_H
#include <jni.h>
#ifndef FALSE
#define FALSE (0)
#define TRUE (!FALSE)
#endif
#define _G_TYPE_CIC(ip, gt, ct) ((ct*) ip)
#define G_TYPE_CHECK_INSTANCE_CAST(instance, g_type, c_type) \
(_G_TYPE_CIC ((instance), (g_type), c_type))
#define GTK_TYPE_FILE_CHOOSER (fp_gtk_file_chooser_get_type ())
#define GTK_FILE_CHOOSER(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_CHOOSER, GtkFileChooser))
#define G_CALLBACK(f) ((GCallback) (f))
#define G_TYPE_FUNDAMENTAL_SHIFT (2)
#define G_TYPE_MAKE_FUNDAMENTAL(x) ((GType) ((x) << G_TYPE_FUNDAMENTAL_SHIFT))
#define G_TYPE_OBJECT G_TYPE_MAKE_FUNDAMENTAL (20)
#define GTK_STOCK_CANCEL "gtk-cancel"
#define GTK_STOCK_SAVE "gtk-save"
#define GTK_STOCK_OPEN "gtk-open"
#define GDK_CURRENT_TIME 0L
#define G_TYPE_INVALID G_TYPE_MAKE_FUNDAMENTAL (0)
#define G_TYPE_NONE G_TYPE_MAKE_FUNDAMENTAL (1)
#define G_TYPE_INTERFACE G_TYPE_MAKE_FUNDAMENTAL (2)
#define G_TYPE_CHAR G_TYPE_MAKE_FUNDAMENTAL (3)
#define G_TYPE_UCHAR G_TYPE_MAKE_FUNDAMENTAL (4)
#define G_TYPE_BOOLEAN G_TYPE_MAKE_FUNDAMENTAL (5)
#define G_TYPE_INT G_TYPE_MAKE_FUNDAMENTAL (6)
#define G_TYPE_UINT G_TYPE_MAKE_FUNDAMENTAL (7)
#define G_TYPE_LONG G_TYPE_MAKE_FUNDAMENTAL (8)
#define G_TYPE_ULONG G_TYPE_MAKE_FUNDAMENTAL (9)
#define G_TYPE_INT64 G_TYPE_MAKE_FUNDAMENTAL (10)
#define G_TYPE_UINT64 G_TYPE_MAKE_FUNDAMENTAL (11)
#define G_TYPE_ENUM G_TYPE_MAKE_FUNDAMENTAL (12)
#define G_TYPE_FLAGS G_TYPE_MAKE_FUNDAMENTAL (13)
#define G_TYPE_FLOAT G_TYPE_MAKE_FUNDAMENTAL (14)
#define G_TYPE_DOUBLE G_TYPE_MAKE_FUNDAMENTAL (15)
#define G_TYPE_STRING G_TYPE_MAKE_FUNDAMENTAL (16)
#define G_TYPE_POINTER G_TYPE_MAKE_FUNDAMENTAL (17)
#define G_TYPE_BOXED G_TYPE_MAKE_FUNDAMENTAL (18)
#define G_TYPE_PARAM G_TYPE_MAKE_FUNDAMENTAL (19)
#define G_TYPE_OBJECT G_TYPE_MAKE_FUNDAMENTAL (20)
#define GTK_TYPE_BORDER ((*fp_gtk_border_get_type)())
#define G_TYPE_FUNDAMENTAL_SHIFT (2)
#define G_TYPE_MAKE_FUNDAMENTAL(x) ((GType) ((x) << G_TYPE_FUNDAMENTAL_SHIFT))
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#define CONV_BUFFER_SIZE 128
#define NO_SYMBOL_EXCEPTION 1
/* basic types */
typedef char gchar;
typedef short gshort;
typedef int gint;
typedef long glong;
typedef float gfloat;
typedef double gdouble;
typedef void* gpointer;
typedef gint gboolean;
typedef signed char gint8;
typedef signed short gint16;
typedef signed int gint32;
typedef unsigned char guchar;
typedef unsigned char guint8;
typedef unsigned short gushort;
typedef unsigned short guint16;
typedef unsigned int guint;
typedef unsigned int guint32;
typedef unsigned long gsize;
typedef unsigned long gulong;
typedef signed long long gint64;
typedef unsigned long long guint64;
typedef gulong GType;
typedef struct _GList GList;
struct _GList
{
gpointer data;
GList *next;
GList *prev;
};
typedef struct _GSList GSList;
struct _GSList {
gpointer data;
GSList *next;
};
typedef signed long gssize;
typedef struct _GString GString;
struct _GString
{
gchar *str;
gsize len;
gsize allocated_len;
};
typedef struct _GVariant GVariant;
typedef struct _GVariantIter GVariantIter;
struct _GVariantIter {
/*< private >*/
gsize x[16];
};
typedef struct _GVariantType GVariantType;
typedef struct _GVariantBuilder GVariantBuilder;
struct _GVariantBuilder {
/*< private >*/
union
{
struct {
gsize partial_magic;
const GVariantType *type;
gsize y[14];
} s;
gsize x[16];
} u;
};
#define G_VARIANT_TYPE_VARDICT ((const GVariantType *) "a{sv}")
#define G_VARIANT_TYPE_ARRAY ((const GVariantType *) "a*")
#define G_VARIANT_TYPE_STRING ((const GVariantType *) "s")
typedef struct _GDBusProxy GDBusProxy;
typedef enum {
G_DBUS_CALL_FLAGS_NONE = 0,
G_DBUS_CALL_FLAGS_NO_AUTO_START = (1<<0),
G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION = (1<<1)
} GDBusCallFlags;
typedef void GMainContext;
typedef void GUnixFDList;
typedef void GDBusConnection;
typedef enum /*< flags >*/
{
G_DBUS_SIGNAL_FLAGS_NONE = 0,
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE = (1<<0),
G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE = (1<<1),
G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH = (1<<2)
} GDBusSignalFlags;
typedef void (*GDBusSignalCallback) (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data);
typedef struct _GCancellable GCancellable;
typedef enum
{
G_BUS_TYPE_STARTER = -1,
G_BUS_TYPE_NONE = 0,
G_BUS_TYPE_SYSTEM = 1,
G_BUS_TYPE_SESSION = 2
} GBusType;
typedef enum
{
G_DBUS_PROXY_FLAGS_NONE = 0,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0),
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1),
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START = (1<<2),
G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES = (1<<3),
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION = (1<<4)
} GDBusProxyFlags;
typedef struct _GDBusInterfaceInfo GDBusInterfaceInfo;
typedef enum {
BUTTON, /* GtkButton */
CHECK_BOX, /* GtkCheckButton */
CHECK_BOX_MENU_ITEM, /* GtkCheckMenuItem */
COLOR_CHOOSER, /* GtkColorSelectionDialog */
COMBO_BOX, /* GtkComboBox */
COMBO_BOX_ARROW_BUTTON, /* GtkComboBoxEntry */
COMBO_BOX_TEXT_FIELD, /* GtkComboBoxEntry */
DESKTOP_ICON, /* GtkLabel */
DESKTOP_PANE, /* GtkContainer */
EDITOR_PANE, /* GtkTextView */
FORMATTED_TEXT_FIELD, /* GtkEntry */
HANDLE_BOX, /* GtkHandleBox */
HPROGRESS_BAR, /* GtkProgressBar */
HSCROLL_BAR, /* GtkHScrollbar */
HSCROLL_BAR_BUTTON_LEFT, /* GtkHScrollbar */
HSCROLL_BAR_BUTTON_RIGHT, /* GtkHScrollbar */
HSCROLL_BAR_TRACK, /* GtkHScrollbar */
HSCROLL_BAR_THUMB, /* GtkHScrollbar */
HSEPARATOR, /* GtkHSeparator */
HSLIDER, /* GtkHScale */
HSLIDER_TRACK, /* GtkHScale */
HSLIDER_THUMB, /* GtkHScale */
HSPLIT_PANE_DIVIDER, /* GtkHPaned */
INTERNAL_FRAME, /* GtkWindow */
INTERNAL_FRAME_TITLE_PANE, /* GtkLabel */
IMAGE, /* GtkImage */
LABEL, /* GtkLabel */
LIST, /* GtkTreeView */
MENU, /* GtkMenu */
MENU_BAR, /* GtkMenuBar */
MENU_ITEM, /* GtkMenuItem */
MENU_ITEM_ACCELERATOR, /* GtkLabel */
OPTION_PANE, /* GtkMessageDialog */
PANEL, /* GtkContainer */
PASSWORD_FIELD, /* GtkEntry */
POPUP_MENU, /* GtkMenu */
POPUP_MENU_SEPARATOR, /* GtkSeparatorMenuItem */
RADIO_BUTTON, /* GtkRadioButton */
RADIO_BUTTON_MENU_ITEM, /* GtkRadioMenuItem */
ROOT_PANE, /* GtkContainer */
SCROLL_PANE, /* GtkScrolledWindow */
SPINNER, /* GtkSpinButton */
SPINNER_ARROW_BUTTON, /* GtkSpinButton */
SPINNER_TEXT_FIELD, /* GtkSpinButton */
SPLIT_PANE, /* GtkPaned */
TABBED_PANE, /* GtkNotebook */
TABBED_PANE_TAB_AREA, /* GtkNotebook */
TABBED_PANE_CONTENT, /* GtkNotebook */
TABBED_PANE_TAB, /* GtkNotebook */
TABLE, /* GtkTreeView */
TABLE_HEADER, /* GtkButton */
TEXT_AREA, /* GtkTextView */
TEXT_FIELD, /* GtkEntry */
TEXT_PANE, /* GtkTextView */
TITLED_BORDER, /* GtkFrame */
TOGGLE_BUTTON, /* GtkToggleButton */
TOOL_BAR, /* GtkToolbar */
TOOL_BAR_DRAG_WINDOW, /* GtkToolbar */
TOOL_BAR_SEPARATOR, /* GtkSeparatorToolItem */
TOOL_TIP, /* GtkWindow */
TREE, /* GtkTreeView */
TREE_CELL, /* GtkTreeView */
VIEWPORT, /* GtkViewport */
VPROGRESS_BAR, /* GtkProgressBar */
VSCROLL_BAR, /* GtkVScrollbar */
VSCROLL_BAR_BUTTON_UP, /* GtkVScrollbar */
VSCROLL_BAR_BUTTON_DOWN, /* GtkVScrollbar */
VSCROLL_BAR_TRACK, /* GtkVScrollbar */
VSCROLL_BAR_THUMB, /* GtkVScrollbar */
VSEPARATOR, /* GtkVSeparator */
VSLIDER, /* GtkVScale */
VSLIDER_TRACK, /* GtkVScale */
VSLIDER_THUMB, /* GtkVScale */
VSPLIT_PANE_DIVIDER, /* GtkVPaned */
WIDGET_TYPE_SIZE
} WidgetType;
typedef enum
{
_GTK_ARROW_TYPE,
_GTK_BUTTON_TYPE,
_GTK_CHECK_BUTTON_TYPE,
_GTK_CHECK_MENU_ITEM_TYPE,
_GTK_COLOR_SELECTION_DIALOG_TYPE,
_GTK_COMBO_BOX_TYPE,
_GTK_COMBO_BOX_ARROW_BUTTON_TYPE,
_GTK_COMBO_BOX_TEXT_FIELD_TYPE,
_GTK_CONTAINER_TYPE,
_GTK_ENTRY_TYPE,
_GTK_FRAME_TYPE,
_GTK_HANDLE_BOX_TYPE,
_GTK_HPANED_TYPE,
_GTK_HPROGRESS_BAR_TYPE,
_GTK_HSCALE_TYPE,
_GTK_HSCROLLBAR_TYPE,
_GTK_HSEPARATOR_TYPE,
_GTK_IMAGE_TYPE,
_GTK_MENU_TYPE,
_GTK_MENU_BAR_TYPE,
_GTK_MENU_ITEM_TYPE,
_GTK_NOTEBOOK_TYPE,
_GTK_LABEL_TYPE,
_GTK_RADIO_BUTTON_TYPE,
_GTK_RADIO_MENU_ITEM_TYPE,
_GTK_SCROLLED_WINDOW_TYPE,
_GTK_SEPARATOR_MENU_ITEM_TYPE,
_GTK_SEPARATOR_TOOL_ITEM_TYPE,
_GTK_SPIN_BUTTON_TYPE,
_GTK_TEXT_VIEW_TYPE,
_GTK_TOGGLE_BUTTON_TYPE,
_GTK_TOOLBAR_TYPE,
_GTK_TOOLTIP_TYPE,
_GTK_TREE_VIEW_TYPE,
_GTK_VIEWPORT_TYPE,
_GTK_VPANED_TYPE,
_GTK_VPROGRESS_BAR_TYPE,
_GTK_VSCALE_TYPE,
_GTK_VSCROLLBAR_TYPE,
_GTK_VSEPARATOR_TYPE,
_GTK_WINDOW_TYPE,
_GTK_DIALOG_TYPE,
_GTK_WIDGET_TYPE_SIZE
} GtkWidgetType;
typedef enum
{
GTK_STATE_NORMAL,
GTK_STATE_ACTIVE,
GTK_STATE_PRELIGHT,
GTK_STATE_SELECTED,
GTK_STATE_INSENSITIVE,
GTK_STATE_INCONSISTENT,
GTK_STATE_FOCUSED
} GtkStateType;
typedef enum
{
GTK_SHADOW_NONE,
GTK_SHADOW_IN,
GTK_SHADOW_OUT,
GTK_SHADOW_ETCHED_IN,
GTK_SHADOW_ETCHED_OUT
} GtkShadowType;
typedef enum
{
GTK_EXPANDER_COLLAPSED,
GTK_EXPANDER_SEMI_COLLAPSED,
GTK_EXPANDER_SEMI_EXPANDED,
GTK_EXPANDER_EXPANDED
} GtkExpanderStyle;
typedef enum
{
GTK_ICON_SIZE_INVALID,
GTK_ICON_SIZE_MENU,
GTK_ICON_SIZE_SMALL_TOOLBAR,
GTK_ICON_SIZE_LARGE_TOOLBAR,
GTK_ICON_SIZE_BUTTON,
GTK_ICON_SIZE_DND,
GTK_ICON_SIZE_DIALOG
} GtkIconSize;
typedef enum
{
GTK_ORIENTATION_HORIZONTAL,
GTK_ORIENTATION_VERTICAL
} GtkOrientation;
typedef enum
{
FOREGROUND,
BACKGROUND,
TEXT_FOREGROUND,
TEXT_BACKGROUND,
FOCUS,
LIGHT,
DARK,
MID,
BLACK,
WHITE
} ColorType;
typedef enum
{
GTK_FONT_NAME,
GTK_ICON_SIZES,
GTK_XFT_DPI,
GTK_CURSOR_BLINK,
GTK_CURSOR_BLINK_TIME
} Setting;
typedef enum
{
GTK_ARROW_UP,
GTK_ARROW_DOWN,
GTK_ARROW_LEFT,
GTK_ARROW_RIGHT,
GTK_ARROW_NONE
} GtkArrowType;
typedef enum
{
GTK_TEXT_DIR_NONE,
GTK_TEXT_DIR_LTR,
GTK_TEXT_DIR_RTL
} GtkTextDirection;
typedef enum
{
GTK_POS_LEFT,
GTK_POS_RIGHT,
GTK_POS_TOP,
GTK_POS_BOTTOM
} GtkPositionType;
/* SynthConstants */
static const gint ENABLED = 1 << 0;
static const gint MOUSE_OVER = 1 << 1;
static const gint PRESSED = 1 << 2;
static const gint DISABLED = 1 << 3;
static const gint FOCUSED = 1 << 8;
static const gint SELECTED = 1 << 9;
static const gint DEFAULT = 1 << 10;
typedef enum
{
GTK_ANY = 0,
GTK_3 = 3
} GtkVersion;
typedef enum {
GTK_RESPONSE_NONE = -1,
GTK_RESPONSE_REJECT = -2,
GTK_RESPONSE_ACCEPT = -3,
GTK_RESPONSE_DELETE_EVENT = -4,
GTK_RESPONSE_OK = -5,
GTK_RESPONSE_CANCEL = -6,
GTK_RESPONSE_CLOSE = -7,
GTK_RESPONSE_YES = -8,
GTK_RESPONSE_NO = -9,
GTK_RESPONSE_APPLY = -10,
GTK_RESPONSE_HELP = -11
} GtkResponseType;
typedef enum {
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
} GtkFileChooserAction;
typedef enum {
GTK_FILE_FILTER_FILENAME = 1 << 0,
GTK_FILE_FILTER_URI = 1 << 1,
GTK_FILE_FILTER_DISPLAY_NAME = 1 << 2,
GTK_FILE_FILTER_MIME_TYPE = 1 << 3
} GtkFileFilterFlags;
typedef enum {
GDK_COLORSPACE_RGB
} GdkColorspace;
typedef enum {
GDK_INTERP_NEAREST,
GDK_INTERP_TILES,
GDK_INTERP_BILINEAR,
GDK_INTERP_HYPER
} GdkInterpType;
typedef enum {
G_CONNECT_AFTER = 1 << 0, G_CONNECT_SWAPPED = 1 << 1
} GConnectFlags;
//------------------------------
typedef guint32 GQuark;
struct _GError
{
GQuark domain;
gint code;
gchar *message;
};
typedef struct _GError GError;
typedef void GdkScreen;
typedef void GtkWindow;
typedef void GdkWindow;
typedef void GClosure;
typedef void GtkFileChooser;
typedef void GtkFileFilter;
typedef struct {
gint x;
gint y;
gint width;
gint height;
} GdkRectangle;
typedef struct {
GtkFileFilterFlags contains;
const gchar *filename;
const gchar *uri;
const gchar *display_name;
const gchar *mime_type;
} GtkFileFilterInfo;
typedef gboolean (*GtkFileFilterFunc)(const GtkFileFilterInfo *filter_info,
gpointer data);
typedef void (*GClosureNotify)(gpointer data, GClosure *closure);
typedef void (*GDestroyNotify)(gpointer data);
typedef void (*GCallback)(void);
typedef void GdkPixbuf;
typedef void (* GdkPixbufDestroyNotify) (guchar *pixels, gpointer data);
typedef struct GtkApi {
int version;
gboolean (*show_uri_load)(JNIEnv *env);
gboolean (*unload)();
void (*flush_event_loop)();
gchar* (*gtk_check_version)(guint required_major, guint required_minor, guint required_micro);
jobject (*get_setting)(JNIEnv *env, Setting property);
void (*paint_arrow)(WidgetType widget_type, GtkStateType state_type,
GtkShadowType shadow_type, const gchar *detail,
gint x, gint y, gint width, gint height,
GtkArrowType arrow_type, gboolean fill);
void (*paint_box)(WidgetType widget_type, GtkStateType state_type,
GtkShadowType shadow_type, const gchar *detail,
gint x, gint y, gint width, gint height,
gint synth_state, GtkTextDirection dir);
void (*paint_box_gap)(WidgetType widget_type, GtkStateType state_type,
GtkShadowType shadow_type, const gchar *detail,
gint x, gint y, gint width, gint height,
GtkPositionType gap_side, gint gap_x, gint gap_width);
void (*paint_expander)(WidgetType widget_type, GtkStateType state_type,
const gchar *detail, gint x, gint y, gint width, gint height,
GtkExpanderStyle expander_style);
void (*paint_extension)(WidgetType widget_type, GtkStateType state_type,
GtkShadowType shadow_type, const gchar *detail,
gint x, gint y, gint width, gint height, GtkPositionType gap_side);
void (*paint_flat_box)(WidgetType widget_type, GtkStateType state_type,
GtkShadowType shadow_type, const gchar *detail,
gint x, gint y, gint width, gint height, gboolean has_focus);
void (*paint_focus)(WidgetType widget_type, GtkStateType state_type,
const char *detail, gint x, gint y, gint width, gint height);
void (*paint_handle)(WidgetType widget_type, GtkStateType state_type,
GtkShadowType shadow_type, const gchar *detail,
gint x, gint y, gint width, gint height, GtkOrientation orientation);
void (*paint_hline)(WidgetType widget_type, GtkStateType state_type,
const gchar *detail, gint x, gint y, gint width, gint height);
void (*paint_vline)(WidgetType widget_type, GtkStateType state_type,
const gchar *detail, gint x, gint y, gint width, gint height);
void (*paint_option)(WidgetType widget_type, gint synth_state,
const gchar *detail, gint x, gint y, gint width, gint height);
void (*paint_shadow)(WidgetType widget_type, GtkStateType state_type,
GtkShadowType shadow_type, const gchar *detail,
gint x, gint y, gint width, gint height,
gint synth_state, GtkTextDirection dir);
void (*paint_slider)(WidgetType widget_type, GtkStateType state_type,
GtkShadowType shadow_type, const gchar *detail,
gint x, gint y, gint width, gint height, GtkOrientation orientation,
gboolean has_focus);
void (*paint_background)(WidgetType widget_type, GtkStateType state_type,
gint x, gint y, gint width, gint height);
void (*paint_check)(WidgetType widget_type, gint synth_state,
const gchar *detail, gint x, gint y, gint width, gint height);
void (*set_range_value)(WidgetType widget_type, jdouble value,
jdouble min, jdouble max, jdouble visible);
void (*init_painting)(JNIEnv *env, gint w, gint h, gint scale);
gint (*copy_image)(gint *dest, gint width, gint height);
gint (*get_xthickness)(JNIEnv *env, WidgetType widget_type);
gint (*get_ythickness)(JNIEnv *env, WidgetType widget_type);
gint (*get_color_for_state)(JNIEnv *env, WidgetType widget_type,
GtkStateType state_type, ColorType color_type);
jobject (*get_class_value)(JNIEnv *env, WidgetType widget_type,
const char* key);
jstring (*get_pango_font_name)(JNIEnv *env, WidgetType widget_type);
jboolean (*get_icon_data)(JNIEnv *env, gint widget_type,
const gchar *stock_id, GtkIconSize size,
GtkTextDirection direction, const char *detail,
jmethodID icon_upcall_method, jobject this);
jboolean (*get_file_icon_data)(JNIEnv *env, const char *filename,
GError **error, jmethodID icon_upcall_method, jobject this);
void (*gdk_threads_enter)(void);
void (*gdk_threads_leave)(void);
gboolean (*gtk_show_uri_on_window)(GdkWindow *parent, const gchar *uri,
guint32 timestamp, GError **error);
void (*g_free)(gpointer mem);
gchar* (*gtk_file_chooser_get_filename)(GtkFileChooser *chooser);
void (*gtk_widget_hide)(void* widget);
void (*gtk_main_quit)(void);
void* (*gtk_file_chooser_dialog_new)(const gchar *title,
GtkWindow *parent, GtkFileChooserAction action,
const gchar *first_button_text, ...);
gboolean (*gtk_file_chooser_set_current_folder)(GtkFileChooser *chooser, const gchar *filename);
gboolean (*gtk_file_chooser_set_filename)(GtkFileChooser *chooser, const char *filename);
void (*gtk_file_chooser_set_current_name)(GtkFileChooser *chooser, const gchar *name);
void (*gtk_file_filter_add_custom)(GtkFileFilter *filter,
GtkFileFilterFlags needed, GtkFileFilterFunc func, gpointer data,
GDestroyNotify notify);
void (*gtk_file_chooser_set_filter)(GtkFileChooser *chooser, GtkFileFilter *filter);
GType (*gtk_file_chooser_get_type)(void);
GtkFileFilter* (*gtk_file_filter_new)(void);
void (*gtk_file_chooser_set_do_overwrite_confirmation)(
GtkFileChooser *chooser, gboolean do_overwrite_confirmation);
void (*gtk_file_chooser_set_select_multiple)(
GtkFileChooser *chooser, gboolean select_multiple);
gchar* (*gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser);
GSList* (*gtk_file_chooser_get_filenames)(GtkFileChooser *chooser);
guint (*gtk_g_slist_length)(GSList *list);
gulong (*g_signal_connect_data)(gpointer instance,
const gchar *detailed_signal, GCallback c_handler, gpointer data,
GClosureNotify destroy_data, GConnectFlags connect_flags);
void (*gtk_widget_show)(void *widget);
void (*gtk_main)(void);
guint (*gtk_main_level)(void);
gchar* (*g_path_get_dirname) (const gchar *file_name);
void (*gtk_widget_destroy)(void *widget);
void (*gtk_window_present)(void *window);
void (*gtk_window_present_with_time)(void *window, guint32 timestamp);
void (*gtk_window_move)(void *window, gint x, gint y);
void (*gtk_window_resize)(void *window, gint width, gint height);
void (*g_object_unref)(gpointer object);
GList* (*g_list_append) (GList *list, gpointer data);
void (*g_list_free) (GList *list);
void (*g_list_free_full) (GList *list, GDestroyNotify free_func);
/* <for screencast, used only with GTK3> */
GVariant *(*g_dbus_proxy_call_sync)(
GDBusProxy *proxy,
const gchar *method_name,
GVariant *parameters,
GDBusCallFlags flags,
gint timeout_msec,
GCancellable *cancellable,
GError **error);
GVariant *(*g_variant_new)(const gchar *format_string, ...);
GVariant *(*g_variant_new_string)(const gchar *string);
GVariant *(*g_variant_new_boolean)(gboolean value);
GVariant *(*g_variant_new_uint32)(guint32 value);
void (*g_variant_get)(GVariant *value,
const gchar *format_string,
...);
const gchar *(*g_variant_get_string)(GVariant *value, gsize *length);
guint32 (*g_variant_get_uint32)(GVariant *value);
gboolean (*g_variant_lookup)(GVariant *dictionary,
const gchar *key,
const gchar *format_string,
...);
gboolean (*g_variant_iter_loop)(GVariantIter *iter,
const gchar *format_string,
...);
void (*g_variant_unref)(GVariant *value);
void (*g_variant_builder_init)(GVariantBuilder *builder, //+
const GVariantType *type);
void (*g_variant_builder_add)(GVariantBuilder *builder, //+
const gchar *format_string,
...);
GVariant *(*g_variant_lookup_value)(GVariant *dictionary,
const gchar *key,
const GVariantType *expected_type);
gsize (*g_variant_iter_init)(GVariantIter *iter, GVariant *value);
gsize (*g_variant_iter_n_children)(GVariantIter *iter);
GString *(*g_string_new)(const gchar *init);
GString *(*g_string_erase)(GString *string, gssize pos, gssize len);
GString *(*g_string_set_size)(GString* string, gsize len);
gchar *(*g_string_free)(GString *string, gboolean free_segment);
guint (*g_string_replace)(GString *string,
const gchar *find,
const gchar *replace,
guint limit);
void *(*g_string_printf)(GString *string, const gchar *format, ...);
gboolean (*g_uuid_string_is_valid)(const gchar *str);
GDBusConnection *(*g_bus_get_sync)(GBusType bus_type,
GCancellable *cancellable,
GError **error);
GDBusProxy *(*g_dbus_proxy_new_sync)(GDBusConnection *connection,
GDBusProxyFlags flags,
GDBusInterfaceInfo *info,
const gchar *name,
const gchar *object_path,
const gchar *interface_name,
GCancellable *cancellable,
GError **error);
const gchar *(*g_dbus_connection_get_unique_name)(GDBusConnection *connection);
guint (*g_dbus_connection_signal_subscribe)(GDBusConnection *connection,
const gchar *sender,
const gchar *interface_name,
const gchar *member,
const gchar *object_path,
const gchar *arg0,
GDBusSignalFlags flags,
GDBusSignalCallback callback,
gpointer user_data,
GDestroyNotify user_data_free_func);
void (*g_dbus_connection_signal_unsubscribe)(GDBusConnection *connection,
guint subscription_id);
GVariant *(*g_dbus_proxy_call_with_unix_fd_list_sync)(GDBusProxy *proxy,
const gchar *method_name,
GVariant *parameters,
GDBusCallFlags flags,
gint timeout_msec,
GUnixFDList *fd_list,
GUnixFDList **out_fd_list,
GCancellable *cancellable,
GError **error);
GVariant *(*g_dbus_connection_call_sync)(GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
const GVariantType *reply_type,
GDBusCallFlags flags,
gint timeout_msec,
GCancellable *cancellable,
GError **error);
gboolean (*g_main_context_iteration)(GMainContext *context,
gboolean may_block);
GMainContext *(*g_main_context_default)();
gboolean (*g_main_context_is_owner)(GMainContext* context);
void (*g_error_free)(GError *error);
gint (*g_unix_fd_list_get)(GUnixFDList *list,
gint index_,
GError **error);
GdkPixbuf *(*gdk_pixbuf_new)(GdkColorspace colorspace,
gboolean has_alpha,
int bits_per_sample,
int width,
int height);
GdkPixbuf *(*gdk_pixbuf_new_from_data)(
const guchar *data,
GdkColorspace colorspace,
gboolean has_alpha,
int bits_per_sample,
int width,
int height,
int rowstride,
GdkPixbufDestroyNotify destroy_fn,
gpointer destroy_fn_data
);
GdkPixbuf *(*gdk_pixbuf_scale_simple)(GdkPixbuf *src,
int dest_width,
int dest_heigh,
GdkInterpType interp_type
);
guchar* (*gdk_pixbuf_get_pixels) (const GdkPixbuf* pixbuf);
void (*gdk_pixbuf_copy_area) (
const GdkPixbuf* src_pixbuf,
int src_x,
int src_y,
int width,
int height,
GdkPixbuf* dest_pixbuf,
int dest_x,
int dest_y
);
/* </for screencast, used only with GTK3> */
} GtkApi;
gboolean gtk_load(JNIEnv *env, GtkVersion version, gboolean verbose);
gboolean gtk_check_version(GtkVersion version);
typedef struct _GThreadFunctions GThreadFunctions;
static gboolean (*fp_g_thread_get_initialized)(void);
static void (*fp_g_thread_init)(GThreadFunctions *vtable);
static void (*fp_gdk_threads_init)(void);
static void (*fp_gdk_threads_enter)(void);
static void (*fp_gdk_threads_leave)(void);
extern GtkApi* gtk;
#endif /* !_GTK_INTERFACE_H */

View File

@@ -0,0 +1,291 @@
/*
* Copyright (c) 2005, 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.
*/
#ifdef HEADLESS
#error This file should not be included in headless library
#endif
#include "gtk_interface.h"
#include "com_sun_java_swing_plaf_gtk_GTKEngine.h"
#include <jni_util.h>
#include <stdlib.h>
#include <string.h>
/* Static buffer for conversion from java.lang.String to UTF-8 */
static char conversionBuffer[(CONV_BUFFER_SIZE - 1) * 3 + 1];
const char *getStrFor(JNIEnv *env, jstring val)
{
int length = (*env)->GetStringLength(env, val);
if (length > CONV_BUFFER_SIZE-1) {
length = CONV_BUFFER_SIZE-1;
}
memset(conversionBuffer, 0, sizeof(conversionBuffer));
(*env)->GetStringUTFRegion(env, val, 0, length, conversionBuffer);
return conversionBuffer;
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1arrow(
JNIEnv *env, jobject this,
jint widget_type, jint state, jint shadow_type, jstring detail,
jint x, jint y, jint w, jint h, jint arrow_type)
{
gtk->gdk_threads_enter();
gtk->paint_arrow(widget_type, state, shadow_type, getStrFor(env, detail),
x, y, w, h, arrow_type, TRUE);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1box(
JNIEnv *env, jobject this,
jint widget_type, jint state, jint shadow_type, jstring detail,
jint x, jint y, jint w, jint h,
jint synth_state, jint dir)
{
gtk->gdk_threads_enter();
gtk->paint_box(widget_type, state, shadow_type, getStrFor(env, detail),
x, y, w, h, synth_state, dir);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1box_1gap(
JNIEnv *env, jobject this,
jint widget_type, jint state, jint shadow_type, jstring detail,
jint x, jint y, jint w, jint h,
jint gap_side, jint gap_x, jint gap_w)
{
gtk->gdk_threads_enter();
gtk->paint_box_gap(widget_type, state, shadow_type, getStrFor(env, detail),
x, y, w, h, gap_side, gap_x, gap_w);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1check(
JNIEnv *env, jobject this,
jint widget_type, jint synth_state, jstring detail,
jint x, jint y, jint w, jint h)
{
gtk->gdk_threads_enter();
gtk->paint_check(widget_type, synth_state, getStrFor(env, detail),
x, y, w, h);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1expander(
JNIEnv *env, jobject this,
jint widget_type, jint state, jstring detail,
jint x, jint y, jint w, jint h, jint expander_style)
{
gtk->gdk_threads_enter();
gtk->paint_expander(widget_type, state, getStrFor(env, detail),
x, y, w, h, expander_style);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1extension(
JNIEnv *env, jobject this,
jint widget_type, jint state, jint shadow_type, jstring detail,
jint x, jint y, jint w, jint h, jint placement)
{
gtk->gdk_threads_enter();
gtk->paint_extension(widget_type, state, shadow_type,
getStrFor(env, detail), x, y, w, h, placement);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1flat_1box(
JNIEnv *env, jobject this,
jint widget_type, jint state, jint shadow_type, jstring detail,
jint x, jint y, jint w, jint h, jboolean has_focus)
{
gtk->gdk_threads_enter();
gtk->paint_flat_box(widget_type, state, shadow_type,
getStrFor(env, detail), x, y, w, h, has_focus);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1focus(
JNIEnv *env, jobject this,
jint widget_type, jint state, jstring detail,
jint x, jint y, jint w, jint h)
{
gtk->gdk_threads_enter();
gtk->paint_focus(widget_type, state, getStrFor(env, detail),
x, y, w, h);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1handle(
JNIEnv *env, jobject this,
jint widget_type, jint state, jint shadow_type, jstring detail,
jint x, jint y, jint w, jint h, jint orientation)
{
gtk->gdk_threads_enter();
gtk->paint_handle(widget_type, state, shadow_type, getStrFor(env, detail),
x, y, w, h, orientation);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1hline(
JNIEnv *env, jobject this,
jint widget_type, jint state, jstring detail,
jint x, jint y, jint w, jint h)
{
gtk->gdk_threads_enter();
gtk->paint_hline(widget_type, state, getStrFor(env, detail),
x, y, w, h);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1option(
JNIEnv *env, jobject this,
jint widget_type, jint synth_state, jstring detail,
jint x, jint y, jint w, jint h)
{
gtk->gdk_threads_enter();
gtk->paint_option(widget_type, synth_state, getStrFor(env, detail),
x, y, w, h);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1shadow(
JNIEnv *env, jobject this,
jint widget_type, jint state, jint shadow_type, jstring detail,
jint x, jint y, jint w, jint h,
jint synth_state, jint dir)
{
gtk->gdk_threads_enter();
gtk->paint_shadow(widget_type, state, shadow_type, getStrFor(env, detail),
x, y, w, h, synth_state, dir);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1slider(
JNIEnv *env, jobject this,
jint widget_type, jint state, jint shadow_type, jstring detail,
jint x, jint y, jint w, jint h, jint orientation, jboolean has_focus)
{
gtk->gdk_threads_enter();
gtk->paint_slider(widget_type, state, shadow_type, getStrFor(env, detail),
x, y, w, h, orientation, has_focus);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1vline(
JNIEnv *env, jobject this,
jint widget_type, jint state, jstring detail,
jint x, jint y, jint w, jint h)
{
gtk->gdk_threads_enter();
gtk->paint_vline(widget_type, state, getStrFor(env, detail),
x, y, w, h);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1background(
JNIEnv *env, jobject this, jint widget_type, jint state,
jint x, jint y, jint w, jint h)
{
gtk->gdk_threads_enter();
gtk->paint_background(widget_type, state, x, y, w, h);
gtk->gdk_threads_leave();
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_nativeStartPainting(
JNIEnv *env, jobject this, jint w, jint h, jint scale)
{
if (w > 0x7FFF || h > 0x7FFF || (uintptr_t)4 * w * h > 0x7FFFFFFFL) {
// Same limitation as in X11SurfaceData.c
JNU_ThrowOutOfMemoryError(env, "Can't create offscreen surface");
return;
}
gtk->gdk_threads_enter();
gtk->init_painting(env, w, h, scale);
gtk->gdk_threads_leave();
}
JNIEXPORT jint JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_nativeFinishPainting(
JNIEnv *env, jobject this, jintArray dest, jint width, jint height)
{
jint transparency;
gint *buffer = (gint*) (*env)->GetPrimitiveArrayCritical(env, dest, 0);
if (buffer == 0) {
(*env)->ExceptionClear(env);
JNU_ThrowOutOfMemoryError(env, "Could not get image buffer");
return -1;
}
gtk->gdk_threads_enter();
transparency = gtk->copy_image(buffer, width, height);
gtk->gdk_threads_leave();
(*env)->ReleasePrimitiveArrayCritical(env, dest, buffer, 0);
return transparency;
}
JNIEXPORT void JNICALL Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1switch_1theme(
JNIEnv *env, jobject this)
{
// Note that gtk->flush_event_loop takes care of locks (7053002), gdk_threads_enter/gdk_threads_leave should not be used.
gtk->flush_event_loop();
}
JNIEXPORT jobject JNICALL Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1get_1gtk_1setting(
JNIEnv *env, jobject this, jint property)
{
jobject obj;
gtk->gdk_threads_enter();
obj = gtk->get_setting(env, property);
gtk->gdk_threads_leave();
return obj;
}
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_nativeSetRangeValue(
JNIEnv *env, jobject this, jint widget_type,
jdouble value, jdouble min, jdouble max, jdouble visible)
{
gtk->gdk_threads_enter();
gtk->set_range_value(widget_type, value, min, max, visible);
gtk->gdk_threads_leave();
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 2005, 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.
*/
#ifdef HEADLESS
#error This file should not be included in headless library
#endif
#include <stdlib.h>
#include <stdio.h>
#include "gtk_interface.h"
#include "com_sun_java_swing_plaf_gtk_GTKStyle.h"
const char *getStrFor(JNIEnv *env, jstring val);
JNIEXPORT jint JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKStyle_nativeGetXThickness(
JNIEnv *env, jclass klass, jint widget_type)
{
jint ret;
gtk->gdk_threads_enter();
ret = gtk->get_xthickness(env, widget_type);
gtk->gdk_threads_leave();
return ret;
}
JNIEXPORT jint JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKStyle_nativeGetYThickness(
JNIEnv *env, jclass klass, jint widget_type)
{
jint ret;
gtk->gdk_threads_enter();
ret = gtk->get_ythickness(env, widget_type);
gtk->gdk_threads_leave();
return ret;
}
JNIEXPORT jint JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKStyle_nativeGetColorForState(
JNIEnv *env, jclass klass, jint widget_type,
jint state_type, jint type_id)
{
jint ret;
gtk->gdk_threads_enter();
ret = gtk->get_color_for_state(env, widget_type, state_type, type_id);
gtk->gdk_threads_leave();
return ret;
}
JNIEXPORT jobject JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKStyle_nativeGetClassValue(
JNIEnv *env, jclass klass, jint widget_type, jstring key)
{
jobject ret;
gtk->gdk_threads_enter();
ret = gtk->get_class_value(env, widget_type, getStrFor(env, key));
gtk->gdk_threads_leave();
return ret;
}
JNIEXPORT jstring JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKStyle_nativeGetPangoFontName(
JNIEnv *env, jclass klass, jint widget_type)
{
jstring ret;
gtk->gdk_threads_enter();
ret = gtk->get_pango_font_name(env, widget_type);
gtk->gdk_threads_leave();
return ret;
}

View File

@@ -313,6 +313,7 @@ static GdkPixbuf *(*fp_gdk_pixbuf_scale_simple)(GdkPixbuf *src,
static void (*fp_gtk_widget_destroy)(void *widget);
static void (*fp_gtk_window_present)(GtkWindow *window);
static void (*fp_gtk_window_present_with_time)(GtkWindow *window, guint32 timestamp);
static void (*fp_gtk_window_move)(GtkWindow *window, gint x, gint y);
static void (*fp_gtk_window_resize)(GtkWindow *window, gint width, gint height);

View File

@@ -312,7 +312,7 @@ Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1background(
*/
JNIEXPORT void JNICALL
Java_com_sun_java_swing_plaf_gtk_GTKEngine_nativeStartPainting(
JNIEnv *env, jobject this, jint w, jint h)
JNIEnv *env, jobject this, jint w, jint h, jint scale)
{
if (w > 0x7FFF || h > 0x7FFF || (uintptr_t)4 * w * h > 0x7FFFFFFFL) {
// Same limitation as in X11SurfaceData.c

View File

@@ -30,7 +30,6 @@ import java.awt.BufferCapabilities;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
@@ -74,8 +73,6 @@ import sun.java2d.opengl.OGLSurfaceData;
import sun.java2d.pipe.Region;
import sun.util.logging.PlatformLogger;
import static sun.awt.windows.WToolkit.getWToolkit;
public abstract class WComponentPeer extends WObjectPeer
implements ComponentPeer, DropTargetPeer
{
@@ -132,10 +129,6 @@ public abstract class WComponentPeer extends WObjectPeer
@Override
public native Point getLocationOnScreen();
Cursor getCursor(final Point p) {
return getTarget() instanceof Component ? ((Component)getTarget()).getCursor() : null;
}
/* New 1.1 API */
@Override
public void setVisible(boolean b) {
@@ -715,10 +708,9 @@ public abstract class WComponentPeer extends WObjectPeer
_setFont(f);
}
synchronized native void _setFont(Font f);
@Override
public void updateCursorImmediately() {
WGlobalCursorManager.getInstance().updateCursor();
WGlobalCursorManager.getCursorManager().updateCursorImmediately();
}
// TODO: consider moving it to KeyboardFocusManagerPeerImpl

View File

@@ -25,68 +25,37 @@
package sun.awt.windows;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.*;
import sun.awt.GlobalCursorManager;
import sun.awt.CachedCursorManager;
final class WGlobalCursorManager extends GlobalCursorManager {
private static WGlobalCursorManager manager;
import static sun.awt.windows.WToolkit.getWToolkit;
final class WGlobalCursorManager extends CachedCursorManager {
private Component lastMouseEventComponent;
private WGlobalCursorManager() {
}
public void setMouseEventComponent(Component component) {
lastMouseEventComponent = component;
}
public Component getMouseEventComponent() {
return lastMouseEventComponent;
}
private static final WGlobalCursorManager theInstance = new WGlobalCursorManager();
public static WGlobalCursorManager getInstance() {
return theInstance;
}
private native void getCursorPos(Point p);
private native void setCursor(Cursor cursor, boolean u);
@Override
protected native Point getLocationOnScreen(Component component);
@Override
protected Cursor getCursorByPosition(Point cursorPos, Component c) {
final Object peer = WToolkit.targetToPeer(c);
if (peer instanceof WComponentPeer && c.isShowing()) {
final WComponentPeer wpeer = (WComponentPeer) peer;
final Point p = getLocationOnScreen((Component) wpeer.getTarget());
return wpeer.getCursor(new Point(cursorPos.x - p.x,
cursorPos.y - p.y));
public static GlobalCursorManager getCursorManager() {
if (manager == null) {
manager = new WGlobalCursorManager();
}
return null;
return manager;
}
public static void nativeUpdateCursor(Component component) {
getInstance().updateCursorLater(component);
/**
* Should be called in response to a native mouse enter or native mouse
* button released message. Should not be called during a mouse drag.
*/
public static void nativeUpdateCursor(Component heavy) {
WGlobalCursorManager.getCursorManager().updateCursorLater(heavy);
}
@Override
protected Component getComponentUnderCursor() {
return lastMouseEventComponent;
}
protected native void setCursor(Component comp, Cursor cursor, boolean u);
@Override
public Point getCursorPosition() {
Point cursorPos = new Point();
getCursorPos(cursorPos);
return cursorPos;
}
protected native void getCursorPos(Point p);
/*
* two native methods to call corresponding methods in Container and
* Component
*/
@Override
protected void setCursor(Cursor cursor) {
setCursor(cursor, true);
}
protected native Component findHeavyweightUnderCursor(boolean useCache);
@Override
protected native Point getLocationOnScreen(Component com);
}

View File

@@ -1300,14 +1300,4 @@ public final class WToolkit extends SunToolkit implements Runnable {
public boolean needUpdateWindow() {
return true;
}
@Override
public void updateLastMouseEventComponent(Component component) {
WGlobalCursorManager.getInstance().setMouseEventComponent(component);
}
@Override
public Component getLastMouseEventComponent() {
return WGlobalCursorManager.getInstance().getMouseEventComponent();
}
}

View File

@@ -528,11 +528,11 @@ static void GlobalSetCursor(void* pStruct) {
/*
* Class: sun_awt_windows_WGlobalCursorManager
* Method: setCursor
* Signature: (Ljava/awt/Cursor;Z)V
* Signature: (Ljava/awt/Component;Ljava/awt/Cursor;)V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WGlobalCursorManager_setCursor(JNIEnv *env, jobject,
jobject cursor, jboolean u)
jobject, jobject cursor, jboolean u)
{
TRY;

View File

@@ -55,6 +55,7 @@ public enum GCCause {
_dcmd_gc_run ("Diagnostic Command"),
_jbr_gc_run("JBR full GC"),
_jbr_shrinking_gc_run("JBR shrinking GC"),
_z_timer ("Timer"),
_z_warmup ("Warmup"),

View File

@@ -203,6 +203,7 @@ serviceability/sa/ClhsdbFindPC.java#xcomp-core 8294316,8267433,JBR-6259 macosx-x
serviceability/sa/ClhsdbFindPC.java#no-xcomp-core 8294316,8267433,JBR-6259 macosx-x64,linux_aarch64,windows-aarch64
serviceability/sa/ClhsdbJdis.java initial_run windows-aarch64
serviceability/sa/ClhsdbJstack.java#id0 JBR-8573 windows-aarch64
serviceability/sa/ClhsdbJstack.java#id1 initial_run windows-aarch64
serviceability/sa/ClhsdbPmap.java#core 8294316,8267433,JBR-6259 macosx-x64,windows-aarch64
serviceability/sa/ClhsdbPmap.java#id1 initial_run generic-all

View File

@@ -846,7 +846,6 @@ jdk_awt_wayland = \
-java/awt/datatransfer/SystemSelection/SystemSelectionSwingTest.java \
-java/awt/datatransfer/UnicodeTransferTest/UnicodeTransferTest.java \
-java/awt/Debug \
-java/awt/Desktop \
-java/awt/Dialog \
-java/awt/dnd \
-java/awt/event/ComponentEvent/ComponentItemEventTest.java \
@@ -904,7 +903,7 @@ jdk_awt_wayland = \
-java/awt/EventDispatchThread/LoopRobustness/LoopRobustness.java \
-java/awt/EventDispatchThread/PropertyPermissionOnEDT/PropertyPermissionOnEDT.java \
-java/awt/EventQueue/6980209/bug6980209.java \
-java/awt/FileDialog \
-java/awt/FileDialog/ModalFocus/FileDialogModalFocusTest.java \
-java/awt/FlowLayout \
-java/awt/Focus/6378278 \
-java/awt/Focus/6382144 \
@@ -1244,8 +1243,10 @@ jdk_swing_wayland= \
-javax/swing/JSplitPane/4820080/JSplitPaneDragColorTest.java \
-javax/swing/JSplitPane/4885629/bug4885629.java \
-javax/swing/JSplitPane/bug4870674.java \
-javax/swing/JTabbedPane/6495408/bug6495408.java \
-javax/swing/JTabbedPane/7024235/Test7024235.java \
-javax/swing/JTabbedPane/7161568/bug7161568.java \
-javax/swing/JTabbedPane/bug4703690.java \
-javax/swing/JTabbedPane/TabbedPaneBug.java \
-javax/swing/JTabbedPane/TabProb.java \
-javax/swing/JTable/4220171/bug4220171.java \

View File

@@ -1 +1,3 @@
;
grant {
permission java.lang.RuntimePermission "loadLibrary.awt";
};

View File

@@ -28,9 +28,12 @@
* @summary AWT_Desktop/Automated/Exceptions/BasicTest loads incorrect GTK
* version when jdk.gtk.version=3
* @requires (os.family == "linux")
* @library /test/lib
* @run main DesktopGtkLoadTest
*/
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import java.awt.*;
import java.io.*;
@@ -43,26 +46,14 @@ public class DesktopGtkLoadTest {
}
public static void main(String[] args) throws Exception {
Process p = Runtime.getRuntime().exec(System.getProperty("java.home") +
"/bin/java -Djdk.gtk.version=3 -Djdk.gtk.verbose=true " +
"-cp " + System.getProperty("java.class.path", ".") +
" DesktopGtkLoadTest$RunDesktop");
p.waitFor();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(p.getErrorStream()))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
if (line.contains("Looking for GTK2 library")) {
break;
}
if (line.contains("Looking for GTK3 library")) {
return;
}
}
throw new RuntimeException("Wrong GTK library version: \n" + line);
}
final ProcessBuilder pbJava = ProcessTools.createTestJavaProcessBuilder(
"-Djdk.gtk.version=3",
"-Djdk.gtk.verbose=true",
RunDesktop.class.getName()
);
final OutputAnalyzer output = ProcessTools.executeProcess(pbJava);
output.shouldNotContain("Looking for GTK2 library");
output.shouldContain("Looking for GTK3 library");
}
}

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