Compare commits

..

41 Commits

Author SHA1 Message Date
Vitaly Provodin
10839395fa JBR-4658 add tier4_no_desktop test group 2024-06-04 02:51:52 +04:00
Vitaly Provodin
1939def8e5 update exclude list on results of 21.0.3_b465.3 test runs 2024-06-04 02:50:20 +04:00
Vitaly Provodin
9bffde9d6e Revert "JBR-7232 Refactor deriveFontWithFeatures & JBRFileDialog JBR API"
This reverts commit c430c1e0b7.
2024-06-01 11:00:35 +04:00
Vitaly Provodin
3f91b5c418 Revert "fixup! JBR-7232 Refactor deriveFontWithFeatures & JBRFileDialog JBR API"
This reverts commit c28643392f.
2024-06-01 11:00:31 +04:00
Vitaly Provodin
86c050e988 update exclude list on results of 21.0.3_b458.1 test runs 2024-06-01 11:00:18 +04:00
Sam James
340680d3b2 8324243: Compilation failures in java.desktop module with gcc 14
Reviewed-by: jwaters, ihse, kbarrett, prr
2024-05-31 09:15:10 +02:00
Kim Barrett
0d64fee1c6 8328997: Remove unnecessary template parameter lists in GrowableArray
Reviewed-by: iwalulya, epeter
2024-05-31 09:15:10 +02:00
Jan Kratochvil
3dc09c7888 8331352: error: template-id not allowed for constructor/destructor in C++20
Reviewed-by: kbarrett, stefank
2024-05-31 09:15:10 +02:00
Maxim Kartashev
d8ece060f6 JBR-7202 wayland: memory leak when resizing windows 2024-05-31 09:12:42 +04:00
Maxim Kartashev
0aa17e5e25 JBR-7206 Wayland: Stylepad demo flickers when resizing on KDE 2024-05-31 09:12:11 +04:00
Nikita Gubarkov
c28643392f fixup! JBR-7232 Refactor deriveFontWithFeatures & JBRFileDialog JBR API 2024-05-31 00:25:54 +02:00
Nikita Gubarkov
c430c1e0b7 JBR-7232 Refactor deriveFontWithFeatures & JBRFileDialog JBR API 2024-05-30 22:01:07 +02:00
Nikita Tsarev
c5dbd5d1a3 JBR-7134: Fix InputMethodTests on macOS 2024-05-30 17:45:41 +02:00
Nikita Gubarkov
c1394c1688 JBR-5615 add sun.java2d.logDisplays VM option
It prints to stdout whenever display configuration is changed.
2024-05-30 01:05:00 +02:00
Alexey Ushakov
3f05010c31 JBR-6543 Vulkan: migrate current code to pure c (#267)
JBR-6543 Vulkan: migrate current code to pure c

Replaced C++ vulkan rendering with C one
2024-05-29 19:47:58 +02:00
Maxim Kartashev
ca6a54d1ff JBR-7209 Wayland: modernize window decorations 2024-05-29 13:17:22 +04:00
Maxim Kartashev
8afe214424 JBR-7201 Wayland: update copyright in files generated by wayland-scanner 2024-05-24 15:59:17 +04:00
Maxim Kartashev
17ef8fd65e JBR-7198 Wayland: jvm crashes under KDE
Do not copy the buffer if the drawing buffer has not been resized yet as
the size will not match that of the show buffer.
Also, properly guard against the size change by another thread while
copying.
2024-05-24 15:50:10 +04:00
Maxim Kartashev
3647144b39 Added proper copyright headers 2024-05-24 15:49:20 +04:00
Maxim Kartashev
1e3fb0289d JBR-7158 Wayland: scale with wp_viewport instead of buffer scale 2024-05-24 15:49:20 +04:00
Dmitrii Morskii
56bd776f20 JBR-7128 Use the correct WmSize event type for JFrame moved to another monitor
author: Sergei Tachenov
2024-05-23 13:20:01 +01:00
Viktor Klang
036ef4917f JBR-7182 backport 8332154: Memory leak in SynchronousQueue 2024-05-22 08:42:32 +04:00
Vitaly Provodin
2c287a5f36 update exclude list on results of 21.0.3_b453.2 test runs 2024-05-19 16:43:34 +04:00
Sergey Shelomentsev
d8c179cf77 JBR-7117 Set initial display mode after test execution 2024-05-17 16:00:47 +03:00
Maxim Kartashev
469130b7ec JBR-7151 Test PropertyPermissionOnEDT and others fail with ExceptionInInitializerError 2024-05-15 12:11:29 +04:00
Vitaly Provodin
535416746d update exclude list on results of 21.0.3_b450.1 test runs 2024-05-15 09:14:22 +04:00
Maxim Kartashev
9d011d23df JBR-7028 Implement FPS counter on Linux
Use -Dawt.window.counters to enable.
To output counters per second to stdout/stderr,
use -Dawt.window.counters=stdout or =stderr.

A counter by the name swing.RepaintManager.updateWindows
is always available for Swing applications, but it does not
accurately correspond to frames per second.

Toolkit-dependent counters provide much better accuracy.
On Wayland with memory buffers as the backend two are available:
java2d.native.frames - frames delivered to the Wayland server
java2d.native.framesDropped - fully formed frames that were not
delivered to the Wayland server
2024-05-14 20:35:07 +04:00
Maxim Kartashev
3399c59635 JBR-7047 Deadlock on git fetch on Wayland 2024-05-14 20:09:40 +04:00
Maxim Kartashev
4bd4fcabf8 JBR-6576 Wayland: exception when double-clicking dialog title bar 2024-05-14 20:08:06 +04:00
Nikita Tsarev
01df339ac6 JBR-7119: respect replacementRange in IME events on macOS 2024-05-10 23:30:56 +02:00
Dmitry Drobotov
6dfa6444b1 JBR-6808 Don't create AccessibleJTreeNode for the tree root if it's not visible
* This fixes an issue with AccessibleJTreeNode#getBounds, which adjusts the node's bounds according to the parent node. For nodes whose parent is the invisible root, getBounds was returning null, and it caused issues with assistive technology like macOS Accessibility Zoom.
* Additionally, NVDA will now report correct tree depth levels because the root node won't add to the levels count (JDK-8249806).

(cherry picked from commit f7c47bf3cf)
2024-05-10 18:02:35 +02:00
Vitaly Provodin
69866f39d9 update exclude list on results of 21.0.3_b446.1 test runs 2024-05-09 04:54:55 +04:00
Nikita Provotorov
13d8c351a2 JBR-6456 Sudden keyboard death on Linux using iBus.
Add a workaround for the iBus's bug which leads to the issue.

(cherry picked from commit b8e9dbf8c9)
2024-05-08 00:30:20 +02:00
Dmitrii Morskii
2324820a9b JBR-6376: implement detecting of OS theme on linux 2024-05-07 15:45:16 +02:00
Dmitrii Morskii
96b270b25f Revert "JBR-6372: implement detecting of OS theme on linux"
This reverts commit 51d67613bb.

Commit 51d676 has a wrong issue number in the commit message. To avoid confusion 51d676 commit reverted
2024-05-07 15:45:07 +02:00
Vitaly Provodin
d133624956 JBR-6620 restore displayMode to the state that was before running the test 2024-05-07 15:08:12 +03:00
Vitaly Provodin
9bd0b744be JBR-5989 Wayland: excluded failing tests came with 21.0.3 2024-05-04 02:11:17 +04:00
Vitaly Provodin
0d099c3d27 update exclude list on results of 21.0.3_b442.1 test runs 2024-05-04 02:11:17 +04:00
Maxim Kartashev
ebaf3f9293 JBR-5611 Window header is visible but body not on Linux Ubuntu with external display 2024-05-03 17:53:12 +04:00
Maxim Kartashev
a79bb6107a JBR-7058 Wayland: IDE hang on the popup appearance
Clean up the damage list when resizing a surface.
Additionally, clamp the damaged area before copying to its current
actual size in order to safeguard against invalid external input.
2024-05-02 15:39:37 +03:00
Nikita Gubarkov
0e564fd8b4 JBR-7046 Tolerate subpixelResolution=0 in Metal and OGL 2024-05-02 09:12:35 +02:00
137 changed files with 6690 additions and 3024 deletions

55
make/autoconf/lib-dbus.m4 Normal file
View File

@@ -0,0 +1,55 @@
#
# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
################################################################################
# Check if a potential dbus library match is correct and usable
################################################################################
AC_DEFUN_ONCE([LIB_SETUP_DBUS],
[
AC_ARG_WITH(dbus-includes, [AS_HELP_STRING([--with-dbus-includes],
[specify include directories for the dbus files as list separated by space])])
if test "x$NEEDS_LIB_DBUS" = xfalse; then
DBUS_CFLAGS=
DBUS_FOUND=false
else
if test "x${with_dbus_includes}" != x; then
DBUS_FOUND=true
DBUS_CFLAGS=""
for include in $with_dbus_includes; do
DBUS_CFLAGS="${DBUS_CFLAGS}-I${include} "
done
else
PKG_CHECK_MODULES(DBUS, dbus-1, [DBUS_FOUND=true], [
DBUS_FOUND=false
AC_MSG_NOTICE([Can't find dbus-1 library. This library is needed to use some features. You can install dbus-1 library or specify include directories manually by giving --with-dbus-includes option.])
])
fi
fi
AC_SUBST(DBUS_CFLAGS)
AC_SUBST(DBUS_FOUND)
])

View File

@@ -106,9 +106,6 @@ AC_DEFUN_ONCE([LIB_SETUP_WAYLAND],
AC_ARG_WITH(vulkan-include, [AS_HELP_STRING([--with-vulkan-include],
[specify directory for the vulkan include files ({with-vulkan-include}/vulkan/vulkan.h)])])
AC_ARG_WITH(vulkan-hpp, [AS_HELP_STRING([--with-vulkan-hpp],
[specify directory for the vulkan-hpp include files ({with-vulkan-hpp}/vulkan/vulkan_raii.hpp)])])
AC_ARG_WITH(vulkan-shader-compiler, [AS_HELP_STRING([--with-vulkan-shader-compiler],
[specify which shader compiler to use: glslc/glslangValidator])])
@@ -139,27 +136,11 @@ AC_DEFUN_ONCE([LIB_SETUP_WAYLAND],
AC_MSG_RESULT([no])
AC_MSG_ERROR([Can't find 'vulkan/vulkan.h' under '${with_vulkan_include}'])
fi
AC_MSG_CHECKING([for vulkan_raii.hpp])
if test "x${with_vulkan_hpp}" != x; then
VULKAN_FLAGS="-I${with_vulkan_hpp} ${VULKAN_FLAGS}"
VULKAN_HPP_DIR=${with_vulkan_hpp}
else
VULKAN_HPP_DIR=${with_vulkan_include}
fi
if test -s "$VULKAN_HPP_DIR/vulkan/vulkan_raii.hpp"; then
VULKAN_FOUND=yes
AC_MSG_RESULT([yes])
else
VULKAN_FOUND=no
AC_MSG_RESULT([no])
AC_MSG_ERROR([Can't find 'vulkan/vulkan_raii.hpp' under '$VULKAN_HPP_DIR'])
fi
fi
AC_LANG_PUSH([C++])
if test "x$VULKAN_FOUND" = xno; then
# Check vulkan sdk location
AC_CHECK_HEADERS([$VULKAN_SDK/include/vulkan/vulkan.h $VULKAN_SDK/include/vulkan/vulkan_raii.hpp],
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"
],
@@ -169,14 +150,13 @@ AC_DEFUN_ONCE([LIB_SETUP_WAYLAND],
if test "x$VULKAN_FOUND" = xno; then
# Check default /usr/include location
AC_CHECK_HEADERS([vulkan/vulkan.h vulkan/vulkan_raii.hpp],
AC_CHECK_HEADERS([vulkan/vulkan.h],
[ VULKAN_FOUND=yes
VULKAN_FLAGS="-DVK_USE_PLATFORM_WAYLAND_KHR -DVULKAN_ENABLED"
],
[ VULKAN_FOUND=no; break ]
)
fi
AC_LANG_POP([C++])
if test "x$VULKAN_FOUND" = xno; then
HELP_MSG_MISSING_DEPENDENCY([vulkan])

View File

@@ -37,7 +37,7 @@ m4_include([lib-fontconfig.m4])
m4_include([lib-speechd.m4])
m4_include([lib-nvdacontrollerclient.m4])
m4_include([lib-wayland.m4])
m4_include([lib-dbus.m4])
m4_include([lib-tests.m4])
################################################################################
@@ -90,11 +90,13 @@ AC_DEFUN_ONCE([LIB_DETERMINE_DEPENDENCIES],
NEEDS_LIB_FREETYPE=true
fi
# Check if alsa is needed
# Check if alsa and dbus is needed
if test "x$OPENJDK_TARGET_OS" = xlinux; then
NEEDS_LIB_ALSA=true
NEEDS_LIB_DBUS=true
else
NEEDS_LIB_ALSA=false
NEEDS_LIB_DBUS=false
fi
# Check if ffi is needed
@@ -152,7 +154,7 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBRARIES],
LIB_SETUP_SPEECHD
LIB_SETUP_NVDACONTROLLERCLIENT
LIB_SETUP_WAYLAND
LIB_SETUP_DBUS
LIB_TESTS_SETUP_GTEST
BASIC_JDKLIB_LIBS=""

View File

@@ -467,6 +467,10 @@ UBSAN_LDFLAGS:=@UBSAN_LDFLAGS@
X_CFLAGS:=@X_CFLAGS@
X_LIBS:=@X_LIBS@
# Necessary additional compiler flags to compile dbus
DBUS_CFLAGS := @DBUS_CFLAGS@
DBUS_FOUND := @DBUS_FOUND@
# Linux speechd a11y announcer
A11Y_SPEECHD_ANNOUNCING_ENABLED:=@A11Y_SPEECHD_ANNOUNCING_ENABLED@
SPEECHD_CFLAGS:=@SPEECHD_CFLAGS@

View File

@@ -78,6 +78,10 @@ ifeq ($(call isTargetOs, windows), true)
#
endif
ifeq ($(DBUS_FOUND), false)
LIBAWT_EXFILES += dbus_interface.c dbus_interface.h
endif
ifeq ($(call isTargetOs, linux macosx aix), true)
LIBAWT_EXFILES += awt_Font.c CUPSfuncs.c fontpath.c X11Color.c
endif
@@ -111,6 +115,10 @@ LIBAWT_CFLAGS += -D__MEDIALIB_OLD_NAMES -D__USE_J2D_NAMES $(X_CFLAGS)
LIBAWT_CFLAGS += -DMLIB_NO_LIBSUNMATH
ifeq ($(DBUS_FOUND), true)
LIBAWT_CFLAGS += -DDBUS_FOUND
endif
ifeq ($(call isTargetOs, windows), true)
LIBAWT_CFLAGS += -EHsc -DUNICODE -D_UNICODE
ifeq ($(call isTargetCpuBits, 64), true)
@@ -162,7 +170,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
EXCLUDES := $(LIBAWT_EXCLUDES), \
EXCLUDE_FILES := $(LIBAWT_EXFILES), \
OPTIMIZATION := HIGHEST, \
CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_CFLAGS), \
CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_CFLAGS) $(DBUS_CFLAGS), \
EXTRA_HEADER_DIRS := $(LIBAWT_EXTRA_HEADER_DIRS), \
DISABLED_WARNINGS_gcc_awt_LoadLibrary.c := unused-result, \
DISABLED_WARNINGS_gcc_debug_mem.c := format-nonliteral, \
@@ -313,7 +321,7 @@ ifeq ($(call isTargetOs, windows macosx), false)
EXTRA_HEADER_DIRS := $(LIBAWT_XAWT_EXTRA_HEADER_DIRS), \
EXCLUDES := $(LIBAWT_XAWT_EXCLUDES), \
OPTIMIZATION := LOW, \
CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_XAWT_CFLAGS) \
CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_XAWT_CFLAGS) $(DBUS_CFLAGS) \
$(X_CFLAGS), \
WARNINGS_AS_ERRORS_xlc := false, \
DISABLED_WARNINGS_gcc := int-to-pointer-cast, \
@@ -571,7 +579,7 @@ ifeq ($(call isTargetOs, windows macosx), false)
EXCLUDES := $(LIBAWT_HEADLESS_EXCLUDES), \
OPTIMIZATION := LOW, \
CFLAGS := $(CFLAGS_JDKLIB) \
$(LIBAWT_HEADLESS_CFLAGS), \
$(LIBAWT_HEADLESS_CFLAGS) $(DBUS_CFLAGS), \
CXXFLAGS := $(CXXFLAGS_JDKLIB), \
EXTRA_HEADER_DIRS := $(LIBAWT_HEADLESS_EXTRA_HEADER_DIRS), \
DISABLED_WARNINGS_gcc_X11Renderer.c := unused-function, \
@@ -663,8 +671,10 @@ else
# noexcept-type required for GCC 7 builds. Not required for GCC 8+.
# expansion-to-defined required for GCC 9 builds. Not required for GCC 10+.
# maybe-uninitialized required for GCC 8 builds. Not required for GCC 9+.
# calloc-transposed-args required for GCC 14 builds. (fixed upstream in Harfbuzz 032c931e1c0cfb20f18e5acb8ba005775242bd92)
HARFBUZZ_DISABLED_WARNINGS_CXX_gcc := class-memaccess noexcept-type \
expansion-to-defined dangling-reference maybe-uninitialized
expansion-to-defined dangling-reference maybe-uninitialized \
calloc-transposed-args
HARFBUZZ_DISABLED_WARNINGS_clang := missing-field-initializers range-loop-analysis
HARFBUZZ_DISABLED_WARNINGS_microsoft := 4267 4244

View File

@@ -96,7 +96,7 @@ ZActivatedArray<T>::ZActivatedArray(bool locked)
_array() {}
template <typename T>
ZActivatedArray<T>::~ZActivatedArray<T>() {
ZActivatedArray<T>::~ZActivatedArray() {
FreeHeap(_lock);
}

View File

@@ -44,7 +44,7 @@ template <class T, MEMFLAGS F> class ChunkedList : public CHeapObj<F> {
}
public:
ChunkedList<T, F>() : _top(_values), _next_used(nullptr), _next_free(nullptr) {}
ChunkedList() : _top(_values), _next_used(nullptr), _next_free(nullptr) {}
bool is_full() const {
return _top == end();

View File

@@ -99,7 +99,7 @@ template <class T> class EventLogBase : public EventLog {
EventRecord<T>* _records;
public:
EventLogBase<T>(const char* name, const char* handle, int length = LogEventsBufferEntries):
EventLogBase(const char* name, const char* handle, int length = LogEventsBufferEntries):
_mutex(Mutex::event, name),
_name(name),
_handle(handle),

View File

@@ -118,7 +118,7 @@ class GrowableArrayView : public GrowableArrayBase {
protected:
E* _data; // data array
GrowableArrayView<E>(E* data, int capacity, int initial_len) :
GrowableArrayView(E* data, int capacity, int initial_len) :
GrowableArrayBase(capacity, initial_len), _data(data) {}
~GrowableArrayView() {}
@@ -126,7 +126,7 @@ protected:
public:
const static GrowableArrayView EMPTY;
bool operator==(const GrowableArrayView<E>& rhs) const {
bool operator==(const GrowableArrayView& rhs) const {
if (_len != rhs._len)
return false;
for (int i = 0; i < _len; i++) {
@@ -137,7 +137,7 @@ public:
return true;
}
bool operator!=(const GrowableArrayView<E>& rhs) const {
bool operator!=(const GrowableArrayView& rhs) const {
return !(*this == rhs);
}
@@ -345,7 +345,7 @@ template <typename E>
class GrowableArrayFromArray : public GrowableArrayView<E> {
public:
GrowableArrayFromArray<E>(E* data, int len) :
GrowableArrayFromArray(E* data, int len) :
GrowableArrayView<E>(data, len, len) {}
};
@@ -480,7 +480,7 @@ public:
return this->at(location);
}
void swap(GrowableArrayWithAllocator<E, Derived>* other) {
void swap(GrowableArrayWithAllocator* other) {
::swap(this->_data, other->_data);
::swap(this->_len, other->_len);
::swap(this->_capacity, other->_capacity);
@@ -682,8 +682,8 @@ public:
// See: init_checks.
template <typename E>
class GrowableArray : public GrowableArrayWithAllocator<E, GrowableArray<E> > {
friend class GrowableArrayWithAllocator<E, GrowableArray<E> >;
class GrowableArray : public GrowableArrayWithAllocator<E, GrowableArray<E>> {
friend class GrowableArrayWithAllocator<E, GrowableArray>;
friend class GrowableArrayTest;
static E* allocate(int max) {
@@ -731,7 +731,7 @@ public:
GrowableArray() : GrowableArray(2 /* initial_capacity */) {}
explicit GrowableArray(int initial_capacity) :
GrowableArrayWithAllocator<E, GrowableArray<E> >(
GrowableArrayWithAllocator<E, GrowableArray>(
allocate(initial_capacity),
initial_capacity),
_metadata() {
@@ -739,7 +739,7 @@ public:
}
GrowableArray(int initial_capacity, MEMFLAGS memflags) :
GrowableArrayWithAllocator<E, GrowableArray<E> >(
GrowableArrayWithAllocator<E, GrowableArray>(
allocate(initial_capacity, memflags),
initial_capacity),
_metadata(memflags) {
@@ -747,7 +747,7 @@ public:
}
GrowableArray(int initial_capacity, int initial_len, const E& filler) :
GrowableArrayWithAllocator<E, GrowableArray<E> >(
GrowableArrayWithAllocator<E, GrowableArray>(
allocate(initial_capacity),
initial_capacity, initial_len, filler),
_metadata() {
@@ -755,7 +755,7 @@ public:
}
GrowableArray(int initial_capacity, int initial_len, const E& filler, MEMFLAGS memflags) :
GrowableArrayWithAllocator<E, GrowableArray<E> >(
GrowableArrayWithAllocator<E, GrowableArray>(
allocate(initial_capacity, memflags),
initial_capacity, initial_len, filler),
_metadata(memflags) {
@@ -763,7 +763,7 @@ public:
}
GrowableArray(Arena* arena, int initial_capacity, int initial_len, const E& filler) :
GrowableArrayWithAllocator<E, GrowableArray<E> >(
GrowableArrayWithAllocator<E, GrowableArray>(
allocate(initial_capacity, arena),
initial_capacity, initial_len, filler),
_metadata(arena) {
@@ -847,15 +847,15 @@ class GrowableArrayIterator : public StackObj {
public:
GrowableArrayIterator() : _array(nullptr), _position(0) { }
GrowableArrayIterator<E>& operator++() { ++_position; return *this; }
E operator*() { return _array->at(_position); }
GrowableArrayIterator& operator++() { ++_position; return *this; }
E operator*() { return _array->at(_position); }
bool operator==(const GrowableArrayIterator<E>& rhs) {
bool operator==(const GrowableArrayIterator& rhs) {
assert(_array == rhs._array, "iterator belongs to different array");
return _position == rhs._position;
}
bool operator!=(const GrowableArrayIterator<E>& rhs) {
bool operator!=(const GrowableArrayIterator& rhs) {
assert(_array == rhs._array, "iterator belongs to different array");
return _position != rhs._position;
}

View File

@@ -82,7 +82,7 @@ template <class E> class LinkedListNode : public AnyObj {
template <class E> class LinkedList : public AnyObj {
protected:
LinkedListNode<E>* _head;
NONCOPYABLE(LinkedList<E>);
NONCOPYABLE(LinkedList);
public:
LinkedList() : _head(nullptr) { }

View File

@@ -194,6 +194,8 @@ public class SynchronousQueue<E> extends AbstractQueue<E>
if ((m = s.await(e, ns, this, // spin if (nearly) empty
p == null || p.waiter == null)) == e)
unspliceLifo(s); // cancelled
else if (m != null)
s.selfLinkItem();
break;
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "dbus_interface.h"
#include <dlfcn.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdbool.h>
#include "jvm_md.h"
#define DBUS_LIB JNI_LIB_NAME("dbus-1")
#define DBUS_LIB_VERSIONED VERSIONED_JNI_LIB_NAME("dbus-1", "3")
static bool isCurrentVersionSupported(DBusApi* dBusApi) {
int major = 0, minor = 0, micro = 0;
dBusApi->dbus_get_version(&major, &minor, &micro);
return major == 1;
}
static bool DBusApi_init(DBusApi* dBusApi, void *libhandle) {
dBusApi->dbus_get_version = dlsym(libhandle, "dbus_get_version");
if (dBusApi->dbus_get_version == NULL || !isCurrentVersionSupported(dBusApi)) {
return false;
}
dBusApi->dbus_error_init = dlsym(libhandle, "dbus_error_init");
dBusApi->dbus_bus_get = dlsym(libhandle, "dbus_bus_get");
dBusApi->dbus_error_is_set = dlsym(libhandle, "dbus_error_is_set");
dBusApi->dbus_bus_request_name = dlsym(libhandle, "dbus_bus_request_name");
dBusApi->dbus_connection_flush = dlsym(libhandle, "dbus_connection_flush");
dBusApi->dbus_message_new_method_call = dlsym(libhandle, "dbus_message_new_method_call");
dBusApi->dbus_message_set_destination = dlsym(libhandle, "dbus_message_set_destination");
dBusApi->dbus_message_iter_init_append = dlsym(libhandle, "dbus_message_iter_init_append");
dBusApi->dbus_message_iter_append_basic = dlsym(libhandle, "dbus_message_iter_append_basic");
dBusApi->dbus_connection_send_with_reply_and_block = dlsym(libhandle, "dbus_connection_send_with_reply_and_block");
dBusApi->dbus_message_iter_init = dlsym(libhandle, "dbus_message_iter_init");
dBusApi->dbus_message_iter_get_arg_type = dlsym(libhandle, "dbus_message_iter_get_arg_type");
dBusApi->dbus_message_iter_get_basic = dlsym(libhandle, "dbus_message_iter_get_basic");
dBusApi->dbus_message_iter_recurse = dlsym(libhandle, "dbus_message_iter_recurse");
dBusApi->dbus_message_iter_next = dlsym(libhandle, "dbus_message_iter_next");
dBusApi->dbus_message_unref = dlsym(libhandle, "dbus_message_unref");
dBusApi->dbus_error_free = dlsym(libhandle, "dbus_error_free");
return dBusApi->dbus_error_init != NULL && dBusApi->dbus_bus_get != NULL && dBusApi->dbus_error_is_set != NULL &&
dBusApi->dbus_bus_request_name != NULL && dBusApi->dbus_connection_flush != NULL &&
dBusApi->dbus_message_set_destination != NULL && dBusApi->dbus_message_iter_init_append != NULL &&
dBusApi->dbus_message_iter_append_basic != NULL && dBusApi->dbus_connection_send_with_reply_and_block != NULL &&
dBusApi->dbus_message_iter_init != NULL && dBusApi->dbus_message_iter_get_arg_type != NULL &&
dBusApi->dbus_message_iter_get_basic != NULL && dBusApi->dbus_message_iter_recurse != NULL &&
dBusApi->dbus_message_iter_next != NULL && dBusApi->dbus_message_unref != NULL &&
dBusApi->dbus_message_new_method_call != NULL && dBusApi->dbus_error_free != NULL;
}
DBusApi* DBusApi_setupDBus(void *libhandle) {
DBusApi *dBusApi = (DBusApi*)malloc(sizeof(DBusApi));
if (dBusApi == NULL || !DBusApi_init(dBusApi, libhandle)) {
free(dBusApi);
dBusApi = NULL;
}
return dBusApi;
}
DBusApi* DBusApi_setupDBusDefault() {
void *dbus_libhandle = dlopen(DBUS_LIB, RTLD_LAZY | RTLD_LOCAL);
if (dbus_libhandle == NULL) {
dbus_libhandle = dlopen(DBUS_LIB_VERSIONED, RTLD_LAZY | RTLD_LOCAL);
if (dbus_libhandle == NULL) {
return NULL;
}
}
return DBusApi_setupDBus(dbus_libhandle);
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef JETBRAINSRUNTIME_DBUS_INTERFACE_H
#define JETBRAINSRUNTIME_DBUS_INTERFACE_H
#include <dbus/dbus.h>
typedef struct DBusApi {
void (*dbus_get_version)(int *major_version_p, int *minor_version_p, int *micro_version_p);
void (*dbus_error_init)(DBusError *error);
DBusConnection *(*dbus_bus_get)(DBusBusType type, DBusError *error);
dbus_bool_t (*dbus_error_is_set)(const DBusError *error);
void (*dbus_error_free)(DBusError *error);
int (*dbus_bus_request_name)(DBusConnection *connection, const char *name, unsigned int flags, DBusError *error);
void (*dbus_connection_flush)(DBusConnection *connection);
DBusMessage* (*dbus_message_new_method_call)(const char *bus_name, const char *path,
const char *iface, const char *method);
dbus_bool_t (*dbus_message_set_destination)(DBusMessage *message, const char *destination);
void (*dbus_message_iter_init_append)(DBusMessage *message, DBusMessageIter *iter);
dbus_bool_t (*dbus_message_iter_append_basic)(DBusMessageIter *iter, int type, const void *value);
DBusMessage *(*dbus_connection_send_with_reply_and_block)(DBusConnection *connection, DBusMessage *message,
int timeout_milliseconds, DBusError *error);
dbus_bool_t (*dbus_message_iter_init)(DBusMessage *message, DBusMessageIter *iter);
int (*dbus_message_iter_get_arg_type)(DBusMessageIter *iter);
void (*dbus_message_iter_get_basic)(DBusMessageIter *iter, void *value);
void (*dbus_message_iter_recurse)(DBusMessageIter *iter, DBusMessageIter *sub);
dbus_bool_t (*dbus_message_iter_next)(DBusMessageIter *iter);
void (*dbus_message_unref)(DBusMessage *message);
} DBusApi;
DBusApi* DBusApi_setupDBus(void *libhandle);
DBusApi* DBusApi_setupDBusDefault();
#endif //JETBRAINSRUNTIME_DBUS_INTERFACE_H

View File

@@ -0,0 +1,265 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "system_properties.h"
#ifdef DBUS_FOUND
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define UNKNOWN_RESULT -1
#define SETTING_INTERFACE "org.freedesktop.portal.Settings"
#define SETTING_INTERFACE_METHOD "Read"
#define DESKTOP_DESTINATION "org.freedesktop.portal.Desktop"
#define DESKTOP_PATH "/org/freedesktop/portal/desktop"
#define REPLY_TIMEOUT 150
static DBusConnection *connection = NULL;
static JNIEnv *env = NULL;
static DBusApi *dBus = NULL;
static DBusMessage *msg_freedesktop_appearance = NULL;
static DBusMessage *msg_gnome_desktop = NULL;
static bool initialized = false;
static bool logEnabled = true;
extern JavaVM *jvm;
static void printError(const char* fmt, ...) {
if (!logEnabled) {
return;
}
env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
char* buf = (char*)malloc(1024);
if (env && buf) {
va_list vargs;
va_start(vargs, fmt);
vsnprintf(buf, 1024, fmt, vargs);
jstring text = JNU_NewStringPlatform(env, buf);
free(buf);
va_end(vargs);
jboolean ignoreException;
JNU_CallStaticMethodByName(env, &ignoreException, "sun/awt/UNIXToolkit", "printError",
"(Ljava/lang/String;)V", text);
}
}
static bool dbusCheckError(DBusError *err, const char *msg) {
bool is_error_set = dBus->dbus_error_is_set(err);
if (is_error_set) {
printError("DBus error: %s. %s\n", msg, err->message);
dBus->dbus_error_free(err);
}
return is_error_set;
}
// current implementation of object decomposition supports only
// primitive types (including a recursive type wrapper)
static bool decomposeDBusReply(void *val, DBusMessageIter *iter, int demand_type) {
int cur_type = dBus->dbus_message_iter_get_arg_type(iter);
switch (cur_type)
{
case DBUS_TYPE_INT16:
case DBUS_TYPE_UINT16:
case DBUS_TYPE_INT32:
case DBUS_TYPE_UINT32:
case DBUS_TYPE_INT64:
case DBUS_TYPE_UINT64:
case DBUS_TYPE_STRING:
{
if (cur_type != demand_type) {
return false;
}
dBus->dbus_message_iter_get_basic(iter, val);
return true;
}
case DBUS_TYPE_VARIANT:
{
DBusMessageIter unwrap_iter;
dBus->dbus_message_iter_recurse(iter, &unwrap_iter);
bool res = decomposeDBusReply(val, &unwrap_iter, demand_type);
// current implementation doesn't support types with multiple fields
if (dBus->dbus_message_iter_next(iter)) {
return false;
}
return res;
}
case DBUS_TYPE_INVALID:
default:
return false;
}
}
static DBusMessage *createDBusMessage(const char *messages[], int message_count) {
DBusMessage *msg = NULL;
DBusMessageIter msg_iter;
if ((msg = dBus->dbus_message_new_method_call(NULL, DESKTOP_PATH, SETTING_INTERFACE, SETTING_INTERFACE_METHOD)) == NULL) {
printError("DBus error: cannot allocate message\n");
goto cleanup;
}
if (!dBus->dbus_message_set_destination(msg, DESKTOP_DESTINATION)) {
printError("DBus error: cannot set destination\n");
goto cleanup;
}
dBus->dbus_message_iter_init_append(msg, &msg_iter);
for (int i = 0; i < message_count; i++) {
if (!dBus->dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &messages[i])) {
printError("DBus error: cannot append to message\n");
goto cleanup;
}
}
return msg;
cleanup:
if (msg) {
dBus->dbus_message_unref(msg);
}
return NULL;
}
static bool sendDBusMessageWithReply(DBusMessage *msg, void *val, int demand_type) {
DBusError error;
DBusMessage *msg_reply = NULL;
DBusMessageIter msg_iter;
bool res = false;
dBus->dbus_error_init(&error);
if ((msg_reply = dBus->dbus_connection_send_with_reply_and_block(connection, msg, REPLY_TIMEOUT, &error)) == NULL) {
printError("DBus error: cannot get msg_reply or sent message. %s\n", dBus->dbus_error_is_set(&error) ? error.message : "");
goto cleanup;
}
if (!dBus->dbus_message_iter_init(msg_reply, &msg_iter)) {
printError("DBus error: cannot process message\n");
goto cleanup;
}
res = decomposeDBusReply(val, &msg_iter, demand_type);
cleanup:
if (msg_reply) {
dBus->dbus_message_unref(msg_reply);
}
return res;
}
JNIEXPORT jint JNICALL Java_sun_awt_UNIXToolkit_isSystemDarkColorScheme() {
static int use_freedesktop_appearance = -1;
if (!initialized) {
return UNKNOWN_RESULT;
}
if (use_freedesktop_appearance == -1) {
unsigned int res = 0;
logEnabled = false;
use_freedesktop_appearance =
sendDBusMessageWithReply(msg_freedesktop_appearance, &res, DBUS_TYPE_UINT32);
logEnabled = true;
}
if (use_freedesktop_appearance) {
unsigned int res = 0;
if (!sendDBusMessageWithReply(msg_freedesktop_appearance, &res, DBUS_TYPE_UINT32)) {
return UNKNOWN_RESULT;
}
/* From org.freedesktop.portal color-scheme specs:
* 0: No preference, 1: Prefer dark appearance, 2: Prefer light appearance
*/
return res == 1;
} else {
char *res = NULL;
if (!sendDBusMessageWithReply(msg_gnome_desktop, &res, DBUS_TYPE_STRING)) {
return UNKNOWN_RESULT;
}
return (res != NULL) ? strstr(res, "dark") != NULL : UNKNOWN_RESULT;
}
}
jboolean SystemProperties_setup(DBusApi *dBus_, JNIEnv *env_) {
env = env_;
dBus = dBus_;
DBusError err;
int ret;
dBus->dbus_error_init(&err);
if ((connection = dBus->dbus_bus_get(DBUS_BUS_SESSION, &err)) == NULL) {
printError("DBus error: connection is Null\n");
return JNI_FALSE;
}
if (dbusCheckError(&err, "connection error")) {
return JNI_FALSE;
}
ret = dBus->dbus_bus_request_name(connection, "dbus.JBR.server", DBUS_NAME_FLAG_REPLACE_EXISTING , &err);
if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER && ret != DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
printError("DBus error: Failed to acquire service name \n");
return JNI_FALSE;
}
if (dbusCheckError(&err, "error request 'dbus.JBR.server' name on the bus")) {
return JNI_FALSE;
}
dBus->dbus_connection_flush(connection);
const char *freedesktop_appearance_messages[] = {"org.freedesktop.appearance", "color-scheme"};
const char *gnome_desktop_messages[] = {"org.gnome.desktop.interface", "gtk-theme"};
msg_freedesktop_appearance = createDBusMessage(freedesktop_appearance_messages, 2);
msg_gnome_desktop = createDBusMessage(gnome_desktop_messages, 2);
if (msg_freedesktop_appearance == NULL || msg_gnome_desktop == NULL) {
return JNI_FALSE;
}
initialized = true;
return JNI_TRUE;
}
JNIEXPORT jboolean JNICALL Java_sun_awt_UNIXToolkit_dbusInit() {
DBusApi *dBus = DBusApi_setupDBusDefault();
if (dBus) {
return SystemProperties_setup(dBus, env);
}
return JNI_FALSE;
}
#else
JNIEXPORT jint JNICALL Java_sun_awt_UNIXToolkit_isSystemDarkColorScheme() {
return -1;
}
JNIEXPORT jboolean JNICALL Java_sun_awt_UNIXToolkit_dbusInit() {
return JNI_FALSE;
}
#endif

View File

@@ -1,6 +1,7 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,19 +25,20 @@
* questions.
*/
#ifndef VKPipeline_h_Included
#define VKPipeline_h_Included
#ifndef JETBRAINSRUNTIME_SYSTEM_PROPERTIES_H
#define JETBRAINSRUNTIME_SYSTEM_PROPERTIES_H
#include "VKShader.h"
#include <stdbool.h>
#include <jni.h>
#include <jni_util.h>
struct VKPipelines {
VKShaders shaders;
// TODO we need a pool of pipelines and (optionally) render passes for different formats.
vk::raii::RenderPass renderPass = nullptr; // Render pass is only needed if dynamic rendering is off.
vk::raii::PipelineLayout testLayout = nullptr;
vk::raii::Pipeline test = nullptr;
#ifdef DBUS_FOUND
void init(const vk::raii::Device& device, bool dynamicRendering);
};
#include "dbus_interface.h"
#endif //VKPipeline_h_Included
jboolean SystemProperties_setup(DBusApi *dBus_, JNIEnv *env_);
void SystemProperties_pullEvent(void);
#endif
#endif //JETBRAINSRUNTIME_SYSTEM_PROPERTIES_H

View File

@@ -218,7 +218,7 @@ MidiMessage* MIDI_IN_GetMessage(MidiDeviceHandle* handle) {
return NULL;
}
}
jdk_message = (MidiMessage*) calloc(sizeof(MidiMessage), 1);
jdk_message = (MidiMessage*) calloc(1, sizeof(MidiMessage));
if (!jdk_message) {
ERROR0("< ERROR: MIDI_IN_GetMessage(): out of memory\n");
return NULL;

View File

@@ -383,7 +383,7 @@ INT32 openMidiDevice(snd_rawmidi_stream_t direction, INT32 deviceIndex,
TRACE0("> openMidiDevice()\n");
(*handle) = (MidiDeviceHandle*) calloc(sizeof(MidiDeviceHandle), 1);
(*handle) = (MidiDeviceHandle*) calloc(1, sizeof(MidiDeviceHandle));
if (!(*handle)) {
ERROR0("ERROR: openDevice: out of memory\n");
return MIDI_OUT_OF_MEMORY;

View File

@@ -159,6 +159,12 @@ public final class CGraphicsEnvironment extends SunGraphicsEnvironment {
/* Populate the device table */
rebuildDevices();
if (LogDisplay.ENABLED) {
for (CGraphicsDevice gd : devices.values()) {
LogDisplay.ADDED.log(gd.getDisplayID(), gd.getBounds(), gd.getScaleFactor());
}
}
/* Register our display reconfiguration listener */
displayReconfigContext = registerDisplayReconfiguration();
if (displayReconfigContext == 0L) {
@@ -185,13 +191,25 @@ public final class CGraphicsEnvironment extends SunGraphicsEnvironment {
* @param displayId CoreGraphics displayId
* @param removed true if displayId was removed, false otherwise.
*/
void _displayReconfiguration(int displayId, boolean removed) {
void _displayReconfiguration(int displayId, int flags) {
// See CGDisplayChangeSummaryFlags
LogDisplay log = !LogDisplay.ENABLED ? null :
(flags & (1 << 4)) != 0 ? LogDisplay.ADDED :
(flags & (1 << 5)) != 0 ? LogDisplay.REMOVED : LogDisplay.CHANGED;
if (log == LogDisplay.REMOVED) {
CGraphicsDevice gd = devices.get(displayId);
log.log(displayId, gd != null ? gd.getBounds() : "UNKNOWN", gd != null ? gd.getScaleFactor() : Double.NaN);
}
// we ignore the passed parameters and check removed devices ourself
// Note that it is possible that this callback is called when the
// monitors are not added nor removed, but when the video card is
// switched to/from the discrete video card, so we should try to map the
// old to the new devices.
rebuildDevices();
if (log != null && log != LogDisplay.REMOVED) {
CGraphicsDevice gd = devices.get(displayId);
log.log(displayId, gd != null ? gd.getBounds() : "UNKNOWN", gd != null ? gd.getScaleFactor() : Double.NaN);
}
}
@Override

View File

@@ -515,25 +515,23 @@ public class CInputMethod extends InputMethodAdapter {
fCurrentText.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT, theHighlight, begin, end);
}
/* Called from JNI to select the previously typed glyph during press and hold */
private void selectPreviousGlyph() {
if (fIMContext == null || fAwtFocussedComponent == null) return; // ???
private void selectRange(int selectionStart, int length) {
if (fIMContext == null || fAwtFocussedComponent == null) {
return;
}
final int selectionEnd = selectionStart + length;
try {
LWCToolkit.invokeLater(new Runnable() {
public void run() {
final int offset = fIMContext.getInsertPositionOffset();
if (offset < 1) return; // ???
if (fAwtFocussedComponent instanceof JTextComponent) {
((JTextComponent) fAwtFocussedComponent).select(offset - 1, offset);
((JTextComponent) fAwtFocussedComponent).select(selectionStart, selectionEnd);
return;
}
if (fAwtFocussedComponent instanceof TextComponent) {
((TextComponent) fAwtFocussedComponent).select(offset - 1, offset);
((TextComponent) fAwtFocussedComponent).select(selectionStart, selectionEnd);
return;
}
// TODO: Ideally we want to disable press-and-hold in this case
}
}, fAwtFocussedComponent);
} catch (Exception e) {

View File

@@ -60,7 +60,6 @@ static unichar lastCtrlCombo;
// Uncomment this line to see fprintfs of each InputMethod API being called on this View
//#define IM_DEBUG TRUE
//#define EXTRA_DEBUG
//#define LOG_KEY_EVENTS
static BOOL shouldUsePressAndHold() {
@@ -95,7 +94,6 @@ extern bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, i
fEnablePressAndHold = shouldUsePressAndHold();
fInPressAndHold = NO;
fPAHNeedsToSelect = NO;
mouseIsOver = NO;
[self resetTrackingArea];
@@ -365,6 +363,8 @@ static void debugPrintNSEvent(NSEvent* event, const char* comment) {
fprintf(stderr, "\tmodifierFlags: 0x%08x\n", (unsigned)[event modifierFlags]);
TISInputSourceRef is = TISCopyCurrentKeyboardLayoutInputSource();
fprintf(stderr, "\tTISCopyCurrentKeyboardLayoutInputSource: %s\n", is == nil ? "(nil)" : [(NSString*) TISGetInputSourceProperty(is, kTISPropertyInputSourceID) UTF8String]);
fprintf(stderr, "\twillBeHandledByComplexInputMethod: %s\n", [event willBeHandledByComplexInputMethod] ? "true" : "false");
CFRelease(is);
}
#endif
@@ -376,6 +376,7 @@ static void debugPrintNSEvent(NSEvent* event, const char* comment) {
fKeyEventsNeeded = YES;
NSString *eventCharacters = [event characters];
unsigned mods = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
if (([event modifierFlags] & NSControlKeyMask) && [eventCharacters length] == 1) {
lastCtrlCombo = [eventCharacters characterAtIndex:0];
@@ -395,7 +396,6 @@ static void debugPrintNSEvent(NSEvent* event, const char* comment) {
fProcessingKeystroke = NO;
if (!fInPressAndHold) {
fInPressAndHold = YES;
fPAHNeedsToSelect = YES;
} else {
// Abandon input to reset IM and unblock input after canceling
// input accented symbols
@@ -1157,7 +1157,9 @@ static jclass jc_CInputMethod = NULL;
- (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
{
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [insertText]: %s\n", [aString UTF8String]);
fprintf(stderr,
"AWTView InputMethod Selector Called : [insertText]: %s, replacementRange: location=%lu, length=%lu\n",
[aString UTF8String], replacementRange.location, replacementRange.length);
#endif // IM_DEBUG
NSMutableString * useString = [self parseString:aString];
@@ -1195,12 +1197,12 @@ static jclass jc_CInputMethod = NULL;
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS();
// We need to select the previous glyph so that it is overwritten.
if (fPAHNeedsToSelect) {
DECLARE_METHOD(jm_selectPreviousGlyph, jc_CInputMethod, "selectPreviousGlyph", "()V");
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_selectPreviousGlyph);
if (replacementRange.length > 0) {
DECLARE_METHOD(jm_selectRange, jc_CInputMethod, "selectRange", "(II)V");
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_selectRange, replacementRange.location,
replacementRange.length);
CHECK_EXCEPTION();
fPAHNeedsToSelect = NO;
}
if (usingComplexIM) {
@@ -1217,7 +1219,6 @@ static jclass jc_CInputMethod = NULL;
actualCharacters = [useString copy];
fKeyEventsNeeded = YES;
}
fPAHNeedsToSelect = NO;
// Abandon input to reset IM and unblock input after entering accented
// symbols
@@ -1256,7 +1257,9 @@ static jclass jc_CInputMethod = NULL;
NSAttributedString *attrString = (isAttributedString ? (NSAttributedString *)aString : nil);
NSString *incomingString = (isAttributedString ? [aString string] : aString);
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [setMarkedText] \"%s\", loc=%lu, length=%lu\n", [incomingString UTF8String], (unsigned long)selectionRange.location, (unsigned long)selectionRange.length);
fprintf(stderr, "AWTView InputMethod Selector Called :[setMarkedText] \"%s\","
"selectionRange(%lu, %lu), replacementRange(%lu, %lu)\n", [incomingString UTF8String],
selectionRange.location, selectionRange.length, replacementRange.location, replacementRange.length);
#endif // IM_DEBUG
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS();
@@ -1264,6 +1267,14 @@ static jclass jc_CInputMethod = NULL;
DECLARE_METHOD(jm_addAttribute, jc_CInputMethod, "addAttribute", "(ZZII)V");
DECLARE_METHOD(jm_dispatchText, jc_CInputMethod, "dispatchText", "(IIZ)V");
if (replacementRange.length > 0) {
DECLARE_METHOD(jm_selectRange, jc_CInputMethod, "selectRange", "(II)V");
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_selectRange, replacementRange.location,
replacementRange.length);
CHECK_EXCEPTION();
}
// NSInputContext already did the analysis of the TSM event and created attributes indicating
// the underlining and color that should be done to the string. We need to look at the underline
// style and color to determine what kind of Java highlighting needs to be done.
@@ -1299,14 +1310,6 @@ static jclass jc_CInputMethod = NULL;
}
}
DECLARE_METHOD(jm_selectPreviousGlyph, jc_CInputMethod, "selectPreviousGlyph", "()V");
// We need to select the previous glyph so that it is overwritten.
if (fPAHNeedsToSelect) {
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_selectPreviousGlyph);
CHECK_EXCEPTION();
fPAHNeedsToSelect = NO;
}
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_dispatchText,
selectionRange.location, selectionRange.length, JNI_FALSE);
CHECK_EXCEPTION();

View File

@@ -122,9 +122,9 @@ static void displaycb_handle
if (graphicsEnv == NULL) return; // ref already GC'd
DECLARE_CLASS(jc_CGraphicsEnvironment, "sun/awt/CGraphicsEnvironment");
DECLARE_METHOD(jm_displayReconfiguration,
jc_CGraphicsEnvironment, "_displayReconfiguration","(IZ)V");
jc_CGraphicsEnvironment, "_displayReconfiguration","(II)V");
(*env)->CallVoidMethod(env, graphicsEnv, jm_displayReconfiguration,
(jint) display, (jboolean) flags & kCGDisplayRemoveFlag);
(jint) display, (jint) flags);
(*env)->DeleteLocalRef(env, graphicsEnv);
CHECK_EXCEPTION();
}];

View File

@@ -660,19 +660,17 @@ MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, BMTLSDOps *dstOps,
int ry = ginfo->subpixelResolutionY;
ADJUST_SUBPIXEL_GLYPH_POSITION(glyphx, rx);
ADJUST_SUBPIXEL_GLYPH_POSITION(glyphy, ry);
int subx = 0, suby = 0;
// see DrawGlyphList.c FLOOR_ASSIGN & getSubpixelGlyphImage
if (glyphx >= 0.0f && glyphy >= 0.0f) {
x = (int) glyphx;
y = (int) glyphy;
subx = ((int) (glyphx * (float) rx)) % rx;
suby = ((int) (glyphy * (float) ry)) % ry;
float fx = floor(glyphx);
float fy = floor(glyphy);
x = (int) fx;
y = (int) fy;
int subimage;
if ((rx == 1 && ry == 1) || rx <= 0 || ry <= 0) {
subimage = 0;
} else {
float fx = floor(glyphx), fy = floor(glyphy);
x = (int) fx;
y = (int) fy;
subx = (int) ((glyphx - fx) * (float) rx);
suby = (int) ((glyphy - fy) * (float) ry);
int subx = (int) ((glyphx - fx) * (float) rx);
int suby = (int) ((glyphy - fy) * (float) ry);
subimage = subx + suby * rx;
}
if (ginfo->image == NULL) {
@@ -684,12 +682,6 @@ MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, BMTLSDOps *dstOps,
J2dTraceLn1(J2D_TRACE_INFO, "rowBytes = %d", ginfo->rowBytes);
if (ginfo->format == sun_font_StrikeCache_PIXEL_FORMAT_GREYSCALE) {
// grayscale or monochrome glyph data
int subimage;
if ((rx == 1 && ry == 1) || rx <= 0 || ry <= 0) {
subimage = 0;
} else {
subimage = subx + suby * rx;
}
if (ginfo->width <= MTLTR_CACHE_CELL_WIDTH &&
ginfo->height <= MTLTR_CACHE_CELL_HEIGHT)
{

View File

@@ -410,6 +410,7 @@ public class Window extends Container implements Accessible {
private static final PlatformLogger log = PlatformLogger.getLogger("java.awt.Window");
private static final PlatformLogger focusRequestLog = PlatformLogger.getLogger("jb.focus.requests");
private static final PlatformLogger perfLog = PlatformLogger.getLogger("awt.window.counters");
private static final boolean locationByPlatformProp;
@@ -4459,6 +4460,10 @@ public class Window extends Container implements Accessible {
}
static {
@SuppressWarnings("removal")
String counters = AccessController.doPrivileged(
new GetPropertyAction("awt.window.counters"));
AWTAccessor.setWindowAccessor(new AWTAccessor.WindowAccessor() {
public void updateWindow(Window window) {
window.updateWindow();
@@ -4503,6 +4508,92 @@ public class Window extends Container implements Accessible {
public Window[] getOwnedWindows(Window w) {
return w.getOwnedWindows_NoClientCode();
}
public boolean countersEnabled(Window w) {
// May want to selectively enable or disable counters per window
return counters != null;
}
public void bumpCounter(Window w, String counterName) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
PerfCounter newCounter;
long curTimeNanos = System.nanoTime();
synchronized (w.perfCounters) {
newCounter = w.perfCounters.compute(counterName, (k, v) ->
v == null
? new PerfCounter(curTimeNanos, 1L)
: new PerfCounter(curTimeNanos, v.value + 1));
}
PerfCounter prevCounter;
synchronized (w.perfCountersPrev) {
prevCounter = w.perfCountersPrev.putIfAbsent(counterName, newCounter);
}
if (prevCounter != null) {
long nanosInSecond = java.util.concurrent.TimeUnit.SECONDS.toNanos(1);
long timeDeltaNanos = curTimeNanos - prevCounter.updateTimeNanos;
if (timeDeltaNanos > nanosInSecond) {
long valPerSecond = (long) ((double) (newCounter.value - prevCounter.value)
* nanosInSecond / timeDeltaNanos);
boolean traceAllCounters = Objects.equals(counters, "")
|| Objects.equals(counters, "stdout")
|| Objects.equals(counters, "stderr");
boolean traceEnabled = traceAllCounters || (counters != null && counters.contains(counterName));
if (traceEnabled) {
if (counters.contains("stderr")) {
System.err.println(counterName + " per second: " + valPerSecond);
} else {
System.out.println(counterName + " per second: " + valPerSecond);
}
}
if (perfLog.isLoggable(PlatformLogger.Level.FINE)) {
perfLog.fine(counterName + " per second: " + valPerSecond);
}
synchronized (w.perfCountersPrev) {
w.perfCountersPrev.put(counterName, newCounter);
}
}
}
}
public long getCounter(Window w, String counterName) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
synchronized (w.perfCounters) {
PerfCounter counter = w.perfCounters.get(counterName);
return counter != null ? counter.value : -1L;
}
}
public long getCounterPerSecond(Window w, String counterName) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
PerfCounter newCounter;
PerfCounter prevCounter;
synchronized (w.perfCounters) {
newCounter = w.perfCounters.get(counterName);
}
synchronized (w.perfCountersPrev) {
prevCounter = w.perfCountersPrev.get(counterName);
}
if (newCounter != null && prevCounter != null) {
long timeDeltaNanos = newCounter.updateTimeNanos - prevCounter.updateTimeNanos;
// Note that this time delta will usually be above one second.
if (timeDeltaNanos > 0) {
long nanosInSecond = java.util.concurrent.TimeUnit.SECONDS.toNanos(1);
long valPerSecond = (long) ((double) (newCounter.value - prevCounter.value)
* nanosInSecond / timeDeltaNanos);
return valPerSecond;
}
}
return -1;
}
}); // WindowAccessor
} // static
@@ -4510,6 +4601,11 @@ public class Window extends Container implements Accessible {
@Override
void updateZOrder() {}
private record PerfCounter(Long updateTimeNanos, Long value) {}
private transient final Map<String, PerfCounter> perfCounters = new HashMap<>(4);
private transient final Map<String, PerfCounter> perfCountersPrev = new HashMap<>(4);
} // class Window

View File

@@ -4996,9 +4996,15 @@ public class JTree extends JComponent implements Scrollable, Accessible
if (treeModel != null) {
index = treeModel.getIndexOfChild(objParent, obj);
}
accessibleParent = new AccessibleJTreeNode(tree,
parentPath,
null);
// If the root is not visible and the parent is the root,
// don't create an accessible node for it.
if (!isRootVisible() && parentPath.getParentPath() == null) {
accessibleParent = tree;
} else {
accessibleParent = new AccessibleJTreeNode(tree,
parentPath,
null);
}
this.setAccessibleParent(accessibleParent);
} else if (treeModel != null) {
accessibleParent = tree; // we're the top!

View File

@@ -49,6 +49,8 @@ import sun.security.action.GetPropertyAction;
import com.sun.java.swing.SwingUtilities3;
import java.awt.geom.AffineTransform;
import java.util.stream.Collectors;
import sun.java2d.SunGraphics2D;
import sun.java2d.pipe.Region;
import sun.swing.SwingAccessor;
@@ -811,7 +813,14 @@ public class RepaintManager
for (Window window : windows) {
AWTAccessor.getWindowAccessor().updateWindow(window);
AWTAccessor.getWindowAccessor().bumpCounter(window, "swing.RepaintManager.updateWindows");
}
} else {
dirtyComponents.keySet().stream()
.map(c -> c instanceof Window w ? w : SwingUtilities.getWindowAncestor(c))
.filter(Objects::nonNull)
.forEach(w -> AWTAccessor.getWindowAccessor()
.bumpCounter(w, "swing.RepaintManager.updateWindows"));
}
}

View File

@@ -346,6 +346,11 @@ public final class AWTAccessor {
* window currently owns.
*/
Window[] getOwnedWindows(Window w);
boolean countersEnabled(Window w);
void bumpCounter(Window w, String counterName);
long getCounter(Window w, String counterName);
long getCounterPerSecond(Window w, String counterName);
}
/**

View File

@@ -53,6 +53,7 @@ import sun.font.FontManagerFactory;
import sun.font.FontManagerForSGE;
import sun.font.FontUtilities;
import sun.java2d.pipe.Region;
import sun.security.action.GetBooleanAction;
import sun.security.action.GetPropertyAction;
/**
@@ -88,6 +89,21 @@ public abstract class SunGraphicsEnvironment extends GraphicsEnvironment
debugScale = uiScaleEnabled ? getScaleFactor("sun.java2d.uiScale") : -1;
}
protected enum LogDisplay {
ADDED,
REMOVED,
CHANGED;
@SuppressWarnings("removal")
public static final boolean ENABLED = AccessController.doPrivileged(new GetBooleanAction("sun.java2d.logDisplays"));
public void log(int id, Object bounds, double scale) {
if (!ENABLED) return;
System.out.println("DISPLAY " + this + ": #" + id +
", " + bounds + ", scale=" + scale);
}
}
protected GraphicsDevice[] screens;
private static boolean isWindows_8_1_orUpper() {

View File

@@ -0,0 +1,9 @@
#version 450
layout(binding = 0) uniform sampler2D texSampler;
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 outColor;
void main() {
outColor = texture(texSampler, fragTexCoord);
}

View File

@@ -0,0 +1,10 @@
#version 450
layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec2 texPosition;
layout(location = 0) out vec2 fragTexCoord;
void main() {
gl_Position = vec4(inPosition, 0.0, 1.0);
fragTexCoord = texPosition;
}

View File

@@ -1,8 +1,8 @@
#version 450
layout(location = 0) in vec4 inColor;
layout(location = 0) in flat vec4 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = inColor;
outColor = fragColor;
}

View File

@@ -0,0 +1,10 @@
#version 450
layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec4 inColor;
layout(location = 0) out flat vec4 fragColor;
void main() {
gl_Position = vec4(inPosition, 0.0, 1.0);
fragColor = inColor;
}

View File

@@ -0,0 +1,8 @@
#version 450
layout(location = 0) in flat vec4 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = fragColor;
}

View File

@@ -0,0 +1,19 @@
#version 450
layout(push_constant) uniform PushConstants {
vec4 fragColor;
} pushConstants;
const vec2 positions[4] = vec2[4](
vec2(-1.0, -1.0),
vec2( 1.0, -1.0),
vec2(-1.0, 1.0),
vec2( 1.0, 1.0)
);
layout(location = 0) out flat vec4 fragColor;
void main() {
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
fragColor = pushConstants.fragColor;
}

View File

@@ -1,21 +0,0 @@
#version 450
layout(push_constant) uniform Push {
vec2 invViewport2; // 2.0/viewport
} push;
vec4 colors[3] = vec4[](
vec4(1,0,0,1),
vec4(0,1,0,1),
vec4(0,0,1,1)
);
layout(location = 0) in vec2 inPosition;
layout(location = 0) out vec4 outColor;
void main() {
outColor = colors[gl_VertexIndex % 3];
gl_Position = vec4(inPosition * push.invViewport2 - 1.0, 0.0, 1.0);
gl_PointSize = 1.0f;
}

View File

@@ -1297,19 +1297,17 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
int ry = ginfo->subpixelResolutionY;
ADJUST_SUBPIXEL_GLYPH_POSITION(glyphx, rx);
ADJUST_SUBPIXEL_GLYPH_POSITION(glyphy, ry);
int subx = 0, suby = 0;
// see DrawGlyphList.c FLOOR_ASSIGN & getSubpixelGlyphImage
if (glyphx >= 0.0f && glyphy >= 0.0f) {
x = (int) glyphx;
y = (int) glyphy;
subx = ((int) (glyphx * (float) rx)) % rx;
suby = ((int) (glyphy * (float) ry)) % ry;
float fx = floor(glyphx);
float fy = floor(glyphy);
x = (int) fx;
y = (int) fy;
int subimage;
if ((rx == 1 && ry == 1) || rx <= 0 || ry <= 0) {
subimage = 0;
} else {
float fx = floor(glyphx), fy = floor(glyphy);
x = (int) fx;
y = (int) fy;
subx = (int) ((glyphx - fx) * (float) rx);
suby = (int) ((glyphy - fy) * (float) ry);
int subx = (int) ((glyphx - fx) * (float) rx);
int suby = (int) ((glyphy - fy) * (float) ry);
subimage = subx + suby * rx;
}
if (ginfo->image == NULL) {
@@ -1321,12 +1319,6 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
OGLContext_InitGrayRenderHints(env, oglc);
}
// grayscale or monochrome glyph data
int subimage;
if ((rx == 1 && ry == 1) || rx <= 0 || ry <= 0) {
subimage = 0;
} else {
subimage = subx + suby * rx;
}
if (ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)
{

View File

@@ -0,0 +1,32 @@
#include <memory.h>
#include "CArrayUtil.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
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->elem_size = elem_size;
pvec->size = 0;
pvec->capacity = capacity;
return pvec->data;
}
void* CARR_array_realloc(CARR_array_t* vec, size_t new_capacity) {
if (vec->capacity == new_capacity) {
return vec->data;
}
CARR_array_t* new_vec =
(CARR_array_t*)((char*)CARR_array_alloc(vec->elem_size, new_capacity) - offsetof(CARR_array_t, data));
if (new_vec == NULL) {
return NULL;
}
new_vec->capacity = new_capacity;
new_vec->size = MIN(vec->size, new_capacity);
new_vec->elem_size = vec->elem_size;
memcpy(new_vec->data, vec->data, new_vec->size*new_vec->elem_size);
free(vec);
return new_vec->data;
}

View File

@@ -0,0 +1,84 @@
#ifndef C_ARRAY_UTIL_H
#define C_ARRAY_UTIL_H
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#define ARRAY_CAPACITY_MULT 2
typedef struct {
size_t elem_size;
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 new_capacity);
/**
* Allocate array
* @param T type of elements
* @param SIZE size of the array
*/
#define ARRAY_ALLOC(T, SIZE) (T*)CARR_array_alloc(sizeof(T), SIZE)
#define ARRAY_T(P) (CARR_array_t *)((char*)P - offsetof(CARR_array_t, data))
/**
* @param P pointer to the first data element of the array
* @return size of the array
*/
#define ARRAY_SIZE(P) (ARRAY_T(P))->size
/**
* @param P pointer to the first data element of the array
* @return capacity of the array
*/
#define ARRAY_CAPACITY(P) (ARRAY_T(P))->capacity
/**
* @param P pointer to the first data element of the array
* @return last element in the array
*/
#define ARRAY_LAST(P) (P[(ARRAY_T(P))->size - 1])
/**
* Deallocate the vector
* @param P pointer to the first data element of the array
*/
#define ARRAY_FREE(P) free(ARRAY_T(P))
/**
* Apply function to the vector
* @param P pointer to the first data element of the array
* @param F function to apply
*/
#define ARRAY_APPLY(P, F) do { \
for (uint32_t _i = 0; _i < ARRAY_SIZE(P); _i++) F(&(P[_i])); \
} while(0)
/**
* Shrink capacity of the array to its size
* @param PP pointer to the pointer to the first data element of the array
*/
#define ARRAY_SHRINK_TO_FIT(PP) do { \
*PP = CARR_array_realloc(ARRAY_T(*PP), ARRAY_SIZE(*PP)); \
} while(0)
/**
* Add element to the end of the array
* @param PP pointer to the pointer to the first data element of the array
*/
#define ARRAY_PUSH_BACK(PP, D) do { \
if (ARRAY_SIZE(*PP) >= ARRAY_CAPACITY(*PP)) { \
*PP = CARR_array_realloc(ARRAY_T(*PP), ARRAY_SIZE(*PP)*ARRAY_CAPACITY_MULT);\
} \
*(*PP + ARRAY_SIZE(*PP)) = (D); \
ARRAY_SIZE(*PP)++; \
} while(0)
#define SARRAY_COUNT_OF(STATIC_ARRAY) (sizeof(STATIC_ARRAY)/sizeof(STATIC_ARRAY[0]))
#endif // CARRAYUTILS_H

View File

@@ -0,0 +1,812 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <malloc.h>
#include <Trace.h>
#include "jvm_md.h"
#include "VKBase.h"
#include "VKVertex.h"
#include "CArrayUtil.h"
#include <vulkan/vulkan.h>
#include <dlfcn.h>
#include <string.h>
#define VULKAN_DLL JNI_LIB_NAME("vulkan")
#define VULKAN_1_DLL VERSIONED_JNI_LIB_NAME("vulkan", "1")
static const uint32_t REQUIRED_VULKAN_VERSION = VK_MAKE_API_VERSION(0, 1, 2, 0);
#define MAX_ENABLED_LAYERS 5
#define MAX_ENABLED_EXTENSIONS 5
#define VALIDATION_LAYER_NAME "VK_LAYER_KHRONOS_validation"
#define COUNT_OF(x) (sizeof(x)/sizeof(x[0]))
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
extern struct wl_display *wl_display;
#endif
static jboolean verbose;
static jint requestedDeviceNumber = -1;
static VKGraphicsEnvironment* geInstance = NULL;
static void* pVulkanLib = NULL;
#define DEBUG
#define INCLUDE_BYTECODE
#define SHADER_ENTRY(NAME, TYPE) static uint32_t NAME ## _ ## TYPE ## _data[] = {
#define BYTECODE_END };
#include "vulkan/shader_list.h"
#undef INCLUDE_BYTECODE
#undef SHADER_ENTRY
#undef BYTECODE_END
#define DEF_VK_PROC_RET_IF_ERR(INST, NAME, RETVAL) PFN_ ## NAME NAME = (PFN_ ## NAME ) vulkanLibProc(INST, #NAME); \
if (NAME == NULL) { \
J2dRlsTraceLn1(J2D_TRACE_ERROR, "Required api is not supported. %s is missing.", #NAME)\
vulkanLibClose(); \
return RETVAL; \
}
#define VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(SNAME, NAME) do { \
SNAME->NAME = (PFN_ ## NAME ) vulkanLibProc( SNAME->vkInstance, #NAME); \
if (SNAME->NAME == NULL) { \
J2dRlsTraceLn1(J2D_TRACE_ERROR, "Required api is not supported. %s is missing.", #NAME)\
vulkanLibClose(); \
return NULL; \
} \
} while (0)
static void vulkanLibClose() {
if (pVulkanLib != NULL) {
if (geInstance != NULL) {
if (geInstance->layers != NULL) {
free(geInstance->layers);
}
if (geInstance->extensions != NULL) {
free(geInstance->extensions);
}
ARRAY_FREE(geInstance->physicalDevices);
if (geInstance->devices != NULL) {
PFN_vkDestroyDevice vkDestroyDevice = vulkanLibProc(geInstance->vkInstance, "vkDestroyDevice");
for (uint32_t i = 0; i < ARRAY_SIZE(geInstance->devices); i++) {
if (geInstance->devices[i].enabledExtensions != NULL) {
free(geInstance->devices[i].enabledExtensions);
}
if (geInstance->devices[i].enabledLayers != NULL) {
free(geInstance->devices[i].enabledLayers);
}
if (geInstance->devices[i].name != NULL) {
free(geInstance->devices[i].name);
}
if (vkDestroyDevice != NULL && geInstance->devices[i].device != NULL) {
vkDestroyDevice(geInstance->devices[i].device, NULL);
}
}
free(geInstance->devices);
}
if (geInstance->vkInstance != NULL) {
PFN_vkDestroyInstance vkDestroyInstance = vulkanLibProc(geInstance->vkInstance, "vkDestroyInstance");
if (vkDestroyInstance != NULL) {
vkDestroyInstance(geInstance->vkInstance, NULL);
}
}
free(geInstance);
geInstance = NULL;
}
dlclose(pVulkanLib);
pVulkanLib = NULL;
}
}
void* vulkanLibProc(VkInstance vkInstance, char* procName) {
static PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
if (pVulkanLib == NULL) {
pVulkanLib = dlopen(VULKAN_DLL, RTLD_NOW);
if (pVulkanLib == NULL) {
pVulkanLib = dlopen(VULKAN_1_DLL, RTLD_NOW);
}
if (pVulkanLib == NULL) {
J2dRlsTrace1(J2D_TRACE_ERROR, "Failed to load %s\n", VULKAN_DLL)
return NULL;
}
if (!vkGetInstanceProcAddr) {
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) dlsym(pVulkanLib, "vkGetInstanceProcAddr");
if (vkGetInstanceProcAddr == NULL) {
J2dRlsTrace1(J2D_TRACE_ERROR,
"Failed to get proc address of vkGetInstanceProcAddr from %s\n", VULKAN_DLL)
return NULL;
}
}
}
void* vkProc = vkGetInstanceProcAddr(vkInstance, procName);
if (vkProc == NULL) {
J2dRlsTrace1(J2D_TRACE_ERROR, "%s is not supported\n", procName)
return NULL;
}
return vkProc;
}
jboolean VK_Init(jboolean verb, jint requestedDevice) {
verbose = verb;
if (VKGE_graphics_environment() == NULL) {
return JNI_FALSE;
}
if (!VK_FindDevices()) {
return JNI_FALSE;
}
if (!VK_CreateLogicalDevice(requestedDevice)) {
return JNI_FALSE;
}
return VK_CreateLogicalDeviceRenderers();
}
static const char* physicalDeviceTypeString(VkPhysicalDeviceType type)
{
switch (type)
{
#define STR(r) case VK_PHYSICAL_DEVICE_TYPE_ ##r: return #r
STR(OTHER);
STR(INTEGRATED_GPU);
STR(DISCRETE_GPU);
STR(VIRTUAL_GPU);
STR(CPU);
#undef STR
default: return "UNKNOWN_DEVICE_TYPE";
}
}
VKGraphicsEnvironment* VKGE_graphics_environment() {
if (geInstance == NULL) {
DEF_VK_PROC_RET_IF_ERR(VK_NULL_HANDLE, vkEnumerateInstanceVersion, NULL);
DEF_VK_PROC_RET_IF_ERR(VK_NULL_HANDLE, vkEnumerateInstanceExtensionProperties, NULL);
DEF_VK_PROC_RET_IF_ERR(VK_NULL_HANDLE, vkEnumerateInstanceLayerProperties, NULL);
DEF_VK_PROC_RET_IF_ERR(VK_NULL_HANDLE, vkCreateInstance, NULL);
uint32_t apiVersion = 0;
if (vkEnumerateInstanceVersion(&apiVersion) != VK_SUCCESS) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: unable to enumerate Vulkan instance version\n")
vulkanLibClose();
return NULL;
}
J2dRlsTrace3(J2D_TRACE_INFO, "Vulkan: Available (%d.%d.%d)\n",
VK_API_VERSION_MAJOR(apiVersion),
VK_API_VERSION_MINOR(apiVersion),
VK_API_VERSION_PATCH(apiVersion))
if (apiVersion < REQUIRED_VULKAN_VERSION) {
J2dRlsTrace3(J2D_TRACE_ERROR, "Vulkan: Unsupported version. Required at least (%d.%d.%d)\n",
VK_API_VERSION_MAJOR(REQUIRED_VULKAN_VERSION),
VK_API_VERSION_MINOR(REQUIRED_VULKAN_VERSION),
VK_API_VERSION_PATCH(REQUIRED_VULKAN_VERSION))
vulkanLibClose();
return NULL;
}
geInstance = (VKGraphicsEnvironment*)malloc(sizeof(VKGraphicsEnvironment));
if (geInstance == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Cannot allocate VKGraphicsEnvironment\n")
vulkanLibClose();
return NULL;
}
*geInstance = (VKGraphicsEnvironment) {};
uint32_t extensionsCount;
// Get the number of extensions and layers
if (vkEnumerateInstanceExtensionProperties(NULL,
&extensionsCount,
NULL) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: vkEnumerateInstanceExtensionProperties fails\n")
vulkanLibClose();
return NULL;
}
geInstance->extensions = ARRAY_ALLOC(VkExtensionProperties, extensionsCount);
if (geInstance->extensions == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Cannot allocate VkExtensionProperties\n")
vulkanLibClose();
return NULL;
}
if (vkEnumerateInstanceExtensionProperties(NULL, &extensionsCount,
geInstance->extensions) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: vkEnumerateInstanceExtensionProperties fails\n")
vulkanLibClose();
return NULL;
}
ARRAY_SIZE(geInstance->extensions) = extensionsCount;
uint32_t layersCount;
if (vkEnumerateInstanceLayerProperties(&layersCount, NULL) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: vkEnumerateInstanceLayerProperties fails\n")
vulkanLibClose();
return NULL;
}
geInstance->layers = ARRAY_ALLOC(VkLayerProperties, layersCount);
if (geInstance->layers == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Cannot allocate VkLayerProperties\n")
vulkanLibClose();
return NULL;
}
if (vkEnumerateInstanceLayerProperties(&layersCount,
geInstance->layers) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: vkEnumerateInstanceLayerProperties fails\n")
vulkanLibClose();
return NULL;
}
ARRAY_SIZE(geInstance->layers) = layersCount;
J2dRlsTrace(J2D_TRACE_VERBOSE, " Supported instance layers:\n")
for (uint32_t i = 0; i < layersCount; i++) {
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char *) geInstance->layers[i].layerName)
}
J2dRlsTrace(J2D_TRACE_VERBOSE, " Supported instance extensions:\n")
for (uint32_t i = 0; i < extensionsCount; i++) {
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char *) geInstance->extensions[i].extensionName)
}
pchar* enabledLayers = ARRAY_ALLOC(pchar, MAX_ENABLED_LAYERS);
pchar* enabledExtensions = ARRAY_ALLOC(pchar, MAX_ENABLED_EXTENSIONS);
void *pNext = NULL;
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
ARRAY_PUSH_BACK(&enabledExtensions, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
#endif
ARRAY_PUSH_BACK(&enabledExtensions, VK_KHR_SURFACE_EXTENSION_NAME);
// Check required layers & extensions.
for (uint32_t i = 0; i < ARRAY_SIZE(enabledExtensions); i++) {
int notFound = 1;
for (uint32_t j = 0; j < extensionsCount; j++) {
if (strcmp((char *) geInstance->extensions[j].extensionName, enabledExtensions[i]) == 0) {
notFound = 0;
break;
}
}
if (notFound) {
J2dRlsTrace1(J2D_TRACE_ERROR, "Vulkan: Required extension %s not found\n", enabledExtensions[i])
vulkanLibClose();
return NULL;
}
}
// Configure validation
#ifdef DEBUG
VkValidationFeatureEnableEXT enables[] = {
// VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,
// VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT,
VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
// VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT,
VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT
};
VkValidationFeaturesEXT features = {};
features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
features.enabledValidationFeatureCount = COUNT_OF(enables);
features.pEnabledValidationFeatures = enables;
// Includes the validation features into the instance creation process
int foundDebugLayer = 0;
for (uint32_t i = 0; i < layersCount; i++) {
if (strcmp((char *) geInstance->layers[i].layerName, VALIDATION_LAYER_NAME) == 0) {
foundDebugLayer = 1;
break;
}
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char *) geInstance->layers[i].layerName)
}
int foundDebugExt = 0;
for (uint32_t i = 0; i < extensionsCount; i++) {
if (strcmp((char *) geInstance->extensions[i].extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) {
foundDebugExt = 1;
break;
}
}
if (foundDebugLayer && foundDebugExt) {
ARRAY_PUSH_BACK(&enabledLayers, VALIDATION_LAYER_NAME);
ARRAY_PUSH_BACK(&enabledExtensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
pNext = &features;
} else {
J2dRlsTrace2(J2D_TRACE_WARNING, "Vulkan: %s and %s are not supported\n",
VALIDATION_LAYER_NAME, VK_EXT_DEBUG_UTILS_EXTENSION_NAME)
}
#endif
VkApplicationInfo applicationInfo = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = NULL,
.pApplicationName = "OpenJDK",
.applicationVersion = 0,
.pEngineName = "OpenJDK",
.engineVersion = 0,
.apiVersion = REQUIRED_VULKAN_VERSION
};
VkInstanceCreateInfo instanceCreateInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext =pNext,
.flags = 0,
.pApplicationInfo = &applicationInfo,
.enabledLayerCount = ARRAY_SIZE(enabledLayers),
.ppEnabledLayerNames = (const char *const *) enabledLayers,
.enabledExtensionCount = ARRAY_SIZE(enabledExtensions),
.ppEnabledExtensionNames = (const char *const *) enabledExtensions
};
if (vkCreateInstance(&instanceCreateInfo, NULL, &geInstance->vkInstance) != VK_SUCCESS) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Failed to create Vulkan instance\n")
vulkanLibClose();
ARRAY_FREE(enabledLayers);
ARRAY_FREE(enabledExtensions);
return NULL;
} else {
J2dRlsTrace(J2D_TRACE_INFO, "Vulkan: Instance Created\n")
}
ARRAY_FREE(enabledLayers);
ARRAY_FREE(enabledExtensions);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkEnumeratePhysicalDevices);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkGetPhysicalDeviceFeatures2);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkGetPhysicalDeviceProperties2);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkGetPhysicalDeviceQueueFamilyProperties);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkEnumerateDeviceLayerProperties);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkEnumerateDeviceExtensionProperties);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateShaderModule);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreatePipelineLayout);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateGraphicsPipelines);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkDestroyShaderModule);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkGetPhysicalDeviceSurfaceFormatsKHR);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkGetPhysicalDeviceSurfacePresentModesKHR);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateSwapchainKHR);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkGetSwapchainImagesKHR);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateImageView);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateFramebuffer);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateCommandPool);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkAllocateCommandBuffers);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateSemaphore);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateFence);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkGetDeviceQueue);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkWaitForFences);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkResetFences);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkAcquireNextImageKHR);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkResetCommandBuffer);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkQueueSubmit);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkQueuePresentKHR);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkBeginCommandBuffer);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCmdBeginRenderPass);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCmdBindPipeline);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCmdSetViewport);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCmdSetScissor);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCmdDraw);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkEndCommandBuffer);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCmdEndRenderPass);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateImage);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateSampler);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkAllocateMemory);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkGetPhysicalDeviceMemoryProperties);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkBindImageMemory);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateDescriptorSetLayout);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkUpdateDescriptorSets);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateDescriptorPool);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkAllocateDescriptorSets);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCmdBindDescriptorSets);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkGetImageMemoryRequirements);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateBuffer);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkGetBufferMemoryRequirements);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkBindBufferMemory);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkMapMemory);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkUnmapMemory);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCmdBindVertexBuffers);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateRenderPass);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkDestroyBuffer);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkFreeMemory);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkDestroyImageView);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkDestroyImage);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkDestroyFramebuffer);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkFlushMappedMemoryRanges);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCmdPushConstants);
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkGetPhysicalDeviceWaylandPresentationSupportKHR);
VKGE_INIT_VK_PROC_RET_NULL_IF_ERR(geInstance, vkCreateWaylandSurfaceKHR);
#endif
}
return geInstance;
}
jboolean VK_FindDevices() {
uint32_t physicalDevicesCount;
if (geInstance->vkEnumeratePhysicalDevices(geInstance->vkInstance,
&physicalDevicesCount,
NULL) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: vkEnumeratePhysicalDevices fails\n")
vulkanLibClose();
return JNI_FALSE;
}
if (physicalDevicesCount == 0) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Failed to find GPUs with Vulkan support\n")
vulkanLibClose();
return JNI_FALSE;
} else {
J2dRlsTrace1(J2D_TRACE_INFO, "Vulkan: Found %d physical devices:\n", physicalDevicesCount)
}
geInstance->physicalDevices = ARRAY_ALLOC(VkPhysicalDevice, physicalDevicesCount);
if (geInstance->physicalDevices == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Cannot allocate VkPhysicalDevice\n")
vulkanLibClose();
return JNI_FALSE;
}
if (geInstance->vkEnumeratePhysicalDevices(
geInstance->vkInstance,
&physicalDevicesCount,
geInstance->physicalDevices) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: vkEnumeratePhysicalDevices fails\n")
vulkanLibClose();
return JNI_FALSE;
}
geInstance->devices = ARRAY_ALLOC(VKLogicalDevice, physicalDevicesCount);
if (geInstance->devices == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Cannot allocate VKLogicalDevice\n")
vulkanLibClose();
return JNI_FALSE;
}
for (uint32_t i = 0; i < physicalDevicesCount; i++) {
VkPhysicalDeviceVulkan12Features device12Features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.pNext = NULL
};
VkPhysicalDeviceFeatures2 deviceFeatures2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = &device12Features
};
geInstance->vkGetPhysicalDeviceFeatures2(geInstance->physicalDevices[i], &deviceFeatures2);
VkPhysicalDeviceProperties2 deviceProperties2 = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
geInstance->vkGetPhysicalDeviceProperties2(geInstance->physicalDevices[i], &deviceProperties2);
J2dRlsTrace5(J2D_TRACE_INFO, "\t- %s (%d.%d.%d, %s) ",
(const char *) deviceProperties2.properties.deviceName,
VK_API_VERSION_MAJOR(deviceProperties2.properties.apiVersion),
VK_API_VERSION_MINOR(deviceProperties2.properties.apiVersion),
VK_API_VERSION_PATCH(deviceProperties2.properties.apiVersion),
physicalDeviceTypeString(deviceProperties2.properties.deviceType))
if (!deviceFeatures2.features.logicOp) {
J2dRlsTrace(J2D_TRACE_INFO, " - hasLogicOp not supported, skipped \n")
continue;
}
if (!device12Features.timelineSemaphore) {
J2dRlsTrace(J2D_TRACE_INFO, " - hasTimelineSemaphore not supported, skipped \n")
continue;
}
J2dRlsTrace(J2D_TRACE_INFO, "\n")
uint32_t queueFamilyCount = 0;
geInstance->vkGetPhysicalDeviceQueueFamilyProperties(
geInstance->physicalDevices[i], &queueFamilyCount, NULL);
VkQueueFamilyProperties *queueFamilies = (VkQueueFamilyProperties*)calloc(queueFamilyCount,
sizeof(VkQueueFamilyProperties));
if (queueFamilies == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Cannot allocate VkQueueFamilyProperties\n")
vulkanLibClose();
return JNI_FALSE;
}
geInstance->vkGetPhysicalDeviceQueueFamilyProperties(
geInstance->physicalDevices[i], &queueFamilyCount, queueFamilies);
int64_t queueFamily = -1;
for (uint32_t j = 0; j < queueFamilyCount; j++) {
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
VkBool32 presentationSupported =
geInstance->vkGetPhysicalDeviceWaylandPresentationSupportKHR(
geInstance->physicalDevices[i], j, wl_display);
#endif
char logFlags[5] = {
queueFamilies[j].queueFlags & VK_QUEUE_GRAPHICS_BIT ? 'G' : '-',
queueFamilies[j].queueFlags & VK_QUEUE_COMPUTE_BIT ? 'C' : '-',
queueFamilies[j].queueFlags & VK_QUEUE_TRANSFER_BIT ? 'T' : '-',
queueFamilies[j].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT ? 'S' : '-',
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
presentationSupported ? 'P' : '-'
#else
'-'
#endif
};
J2dRlsTrace3(J2D_TRACE_INFO, " %d queues in family (%.*s)\n", queueFamilies[j].queueCount, 5,
logFlags)
// TODO use compute workloads? Separate transfer-only DMA queue?
if (queueFamily == -1 && (queueFamilies[j].queueFlags & VK_QUEUE_GRAPHICS_BIT)
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
&& presentationSupported
#endif
) {
queueFamily = j;
}
}
free(queueFamilies);
if (queueFamily == -1) {
J2dRlsTrace(J2D_TRACE_INFO, " --------------------- Suitable queue not found, skipped \n")
continue;
}
uint32_t layerCount;
geInstance->vkEnumerateDeviceLayerProperties(geInstance->physicalDevices[i], &layerCount, NULL);
VkLayerProperties *layers = (VkLayerProperties *) calloc(layerCount, sizeof(VkLayerProperties));
if (layers == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Cannot allocate VkLayerProperties\n")
vulkanLibClose();
return JNI_FALSE;
}
geInstance->vkEnumerateDeviceLayerProperties(geInstance->physicalDevices[i], &layerCount, layers);
J2dRlsTrace(J2D_TRACE_VERBOSE, " Supported device layers:\n")
for (uint32_t j = 0; j < layerCount; j++) {
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char *) layers[j].layerName)
}
uint32_t extensionCount;
geInstance->vkEnumerateDeviceExtensionProperties(geInstance->physicalDevices[i], NULL, &extensionCount, NULL);
VkExtensionProperties *extensions = (VkExtensionProperties *) calloc(
extensionCount, sizeof(VkExtensionProperties));
if (extensions == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Cannot allocate VkExtensionProperties\n")
vulkanLibClose();
return JNI_FALSE;
}
geInstance->vkEnumerateDeviceExtensionProperties(
geInstance->physicalDevices[i], NULL, &extensionCount, extensions);
J2dRlsTrace(J2D_TRACE_VERBOSE, " Supported device extensions:\n")
VkBool32 hasSwapChain = VK_FALSE;
for (uint32_t j = 0; j < extensionCount; j++) {
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char *) extensions[j].extensionName)
hasSwapChain = hasSwapChain ||
strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, extensions[j].extensionName) == 0;
}
free(extensions);
J2dRlsTrace(J2D_TRACE_VERBOSE, " Found:\n")
if (hasSwapChain) {
J2dRlsTrace(J2D_TRACE_VERBOSE, " VK_KHR_SWAPCHAIN_EXTENSION_NAME\n")
}
if (!hasSwapChain) {
J2dRlsTrace(J2D_TRACE_INFO,
" --------------------- Required VK_KHR_SWAPCHAIN_EXTENSION_NAME not found, skipped \n")
continue;
}
pchar* deviceEnabledLayers = ARRAY_ALLOC(pchar, MAX_ENABLED_LAYERS);
if (deviceEnabledLayers == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Cannot allocate deviceEnabledLayers array\n")
vulkanLibClose();
return JNI_FALSE;
}
pchar* deviceEnabledExtensions = ARRAY_ALLOC(pchar, MAX_ENABLED_EXTENSIONS);
if (deviceEnabledExtensions == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Cannot allocate deviceEnabledExtensions array\n")
vulkanLibClose();
return JNI_FALSE;
}
ARRAY_PUSH_BACK(&deviceEnabledExtensions, VK_KHR_SWAPCHAIN_EXTENSION_NAME);
// Validation layer
#ifdef DEBUG
int validationLayerNotSupported = 1;
for (uint32_t j = 0; j < layerCount; j++) {
if (strcmp(VALIDATION_LAYER_NAME, layers[j].layerName) == 0) {
validationLayerNotSupported = 0;
ARRAY_PUSH_BACK(&deviceEnabledLayers, VALIDATION_LAYER_NAME);
break;
}
}
if (validationLayerNotSupported) {
J2dRlsTrace1(J2D_TRACE_INFO, " %s device layer is not supported\n", VALIDATION_LAYER_NAME)
}
#endif
free(layers);
char* deviceName = strdup(deviceProperties2.properties.deviceName);
if (deviceName == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Cannot duplicate deviceName\n")
vulkanLibClose();
return JNI_FALSE;
}
ARRAY_PUSH_BACK(&geInstance->devices,
((VKLogicalDevice) {
.name = deviceName,
.device = VK_NULL_HANDLE,
.physicalDevice = geInstance->physicalDevices[i],
.queueFamily = queueFamily,
.enabledLayers = deviceEnabledLayers,
.enabledExtensions = deviceEnabledExtensions,
}));
}
if (ARRAY_SIZE(geInstance->devices) == 0) {
J2dRlsTrace(J2D_TRACE_ERROR, "No compatible device found\n")
vulkanLibClose();
return JNI_FALSE;
}
return JNI_TRUE;
}
jboolean VK_CreateLogicalDevice(jint requestedDevice) {
requestedDeviceNumber = requestedDevice;
if (geInstance == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: VKGraphicsEnvironment is not initialized\n")
return JNI_FALSE;
}
DEF_VK_PROC_RET_IF_ERR(geInstance->vkInstance, vkCreateDevice, JNI_FALSE)
DEF_VK_PROC_RET_IF_ERR(geInstance->vkInstance, vkCreatePipelineCache, JNI_FALSE)
DEF_VK_PROC_RET_IF_ERR(geInstance->vkInstance, vkCreateRenderPass, JNI_FALSE)
requestedDeviceNumber = (requestedDeviceNumber == -1) ? 0 : requestedDeviceNumber;
if (requestedDeviceNumber < 0 || (uint32_t)requestedDeviceNumber >= ARRAY_SIZE(geInstance->devices)) {
if (verbose) {
fprintf(stderr, " Requested device number (%d) not found, fallback to 0\n", requestedDeviceNumber);
}
requestedDeviceNumber = 0;
}
geInstance->enabledDeviceNum = requestedDeviceNumber;
if (verbose) {
for (uint32_t i = 0; i < ARRAY_SIZE(geInstance->devices); i++) {
fprintf(stderr, " %c%d: %s\n", i == geInstance->enabledDeviceNum ? '*' : ' ',
i, geInstance->devices[i].name);
}
fprintf(stderr, "\n");
}
VKLogicalDevice* logicalDevice = &geInstance->devices[geInstance->enabledDeviceNum];
float queuePriority = 1.0f;
VkDeviceQueueCreateInfo queueCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = logicalDevice->queueFamily, // obtained separately
.queueCount = 1,
.pQueuePriorities = &queuePriority
};
VkPhysicalDeviceFeatures features10 = { .logicOp = VK_TRUE };
VkDeviceCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queueCreateInfo,
.enabledLayerCount = ARRAY_SIZE(logicalDevice->enabledLayers),
.ppEnabledLayerNames = (const char *const *) logicalDevice->enabledLayers,
.enabledExtensionCount = ARRAY_SIZE(logicalDevice->enabledExtensions),
.ppEnabledExtensionNames = (const char *const *) logicalDevice->enabledExtensions,
.pEnabledFeatures = &features10
};
if (vkCreateDevice(logicalDevice->physicalDevice, &createInfo, NULL, &logicalDevice->device) != VK_SUCCESS)
{
J2dRlsTrace1(J2D_TRACE_ERROR, "Cannot create device:\n %s\n",
geInstance->devices[geInstance->enabledDeviceNum].name)
vulkanLibClose();
return JNI_FALSE;
}
VkDevice device = logicalDevice->device;
J2dRlsTrace1(J2D_TRACE_INFO, "Logical device (%s) created\n", logicalDevice->name)
// Create command pool
VkCommandPoolCreateInfo poolInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = logicalDevice->queueFamily
};
if (geInstance->vkCreateCommandPool(device, &poolInfo, NULL, &logicalDevice->commandPool) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_INFO, "failed to create command pool!")
return JNI_FALSE;
}
// Create command buffer
VkCommandBufferAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = logicalDevice->commandPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1
};
if (geInstance->vkAllocateCommandBuffers(device, &allocInfo, &logicalDevice->commandBuffer) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_INFO, "failed to allocate command buffers!");
return JNI_FALSE;
}
// Create semaphores
VkSemaphoreCreateInfo semaphoreInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
};
VkFenceCreateInfo fenceInfo = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT
};
if (geInstance->vkCreateSemaphore(device, &semaphoreInfo, NULL, &logicalDevice->imageAvailableSemaphore) != VK_SUCCESS ||
geInstance->vkCreateSemaphore(device, &semaphoreInfo, NULL, &logicalDevice->renderFinishedSemaphore) != VK_SUCCESS ||
geInstance->vkCreateFence(device, &fenceInfo, NULL, &logicalDevice->inFlightFence) != VK_SUCCESS)
{
J2dRlsTraceLn(J2D_TRACE_INFO, "failed to create semaphores!");
return JNI_FALSE;
}
geInstance->vkGetDeviceQueue(device, logicalDevice->queueFamily, 0, &logicalDevice->queue);
if (logicalDevice->queue == NULL) {
J2dRlsTraceLn(J2D_TRACE_INFO, "failed to get device queue!");
return JNI_FALSE;
}
VKTxVertex* vertices = ARRAY_ALLOC(VKTxVertex, 4);
ARRAY_PUSH_BACK(&vertices, ((VKTxVertex){-1.0f, -1.0f, 0.0f, 0.0f}));
ARRAY_PUSH_BACK(&vertices, ((VKTxVertex){1.0f, -1.0f, 1.0f, 0.0f}));
ARRAY_PUSH_BACK(&vertices, ((VKTxVertex){-1.0f, 1.0f, 0.0f, 1.0f}));
ARRAY_PUSH_BACK(&vertices, ((VKTxVertex){1.0f, 1.0f, 1.0f, 1.0f}));
logicalDevice->blitVertexBuffer = ARRAY_TO_VERTEX_BUF(vertices);
if (!logicalDevice->blitVertexBuffer) {
J2dRlsTrace(J2D_TRACE_ERROR, "Cannot create vertex buffer\n")
return JNI_FALSE;
}
ARRAY_FREE(vertices);
return JNI_TRUE;
}
JNIEXPORT void JNICALL JNI_OnUnload(__attribute__((unused)) JavaVM *vm, __attribute__((unused)) void *reserved) {
vulkanLibClose();
}

View File

@@ -1,453 +0,0 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "VKBase.h"
#include <Trace.h>
#include <set>
#if defined(DEBUG)
#include <csignal>
#endif
#define VALIDATION_LAYER_NAME "VK_LAYER_KHRONOS_validation"
static const uint32_t REQUIRED_VULKAN_VERSION = VK_MAKE_API_VERSION(0, 1, 2, 0);
bool VKGraphicsEnvironment::_verbose = false;
int VKGraphicsEnvironment::_requested_device_number = -1;
std::unique_ptr<VKGraphicsEnvironment> VKGraphicsEnvironment::_ge_instance = nullptr;
// ========== Graphics environment ==========
#if defined(DEBUG)
static VkBool32 debugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData
) {
auto data = (const vk::DebugUtilsMessengerCallbackDataEXT*) pCallbackData;
if (data == nullptr) return 0;
// Here we can filter messages like this:
// if (std::strcmp(data->pMessageIdName, "UNASSIGNED-BestPractices-DrawState-ClearCmdBeforeDraw") == 0) return 0;
int level = J2D_TRACE_OFF;
if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) level = J2D_TRACE_VERBOSE;
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) level = J2D_TRACE_INFO;
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) level = J2D_TRACE_WARNING;
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) level = J2D_TRACE_ERROR;
J2dRlsTraceLn(level, data->pMessage);
if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
raise(SIGABRT);
}
return 0;
}
#endif
VKGraphicsEnvironment *VKGraphicsEnvironment::graphics_environment() {
if (!_ge_instance) {
try {
_ge_instance = std::unique_ptr<VKGraphicsEnvironment>(new VKGraphicsEnvironment());
}
catch (std::exception& e) {
J2dRlsTrace1(J2D_TRACE_ERROR, "Vulkan: %s\n", e.what());
return nullptr;
}
}
return _ge_instance.get();
}
VKGraphicsEnvironment::VKGraphicsEnvironment() :
_vk_context(), _vk_instance(nullptr), _default_device(nullptr) {
// Load library.
uint32_t version = _vk_context.enumerateInstanceVersion();
J2dRlsTrace3(J2D_TRACE_INFO, "Vulkan: Available (%d.%d.%d)\n",
VK_API_VERSION_MAJOR(version), VK_API_VERSION_MINOR(version), VK_API_VERSION_PATCH(version));
if (version < REQUIRED_VULKAN_VERSION) {
throw std::runtime_error("Vulkan: Unsupported version");
}
// Populate maps and log supported layers & extensions.
std::set<std::string> layers, extensions;
J2dRlsTrace(J2D_TRACE_VERBOSE, " Supported instance layers:\n");
for (auto &l: _vk_context.enumerateInstanceLayerProperties()) {
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char *) l.layerName);
layers.emplace((char *) l.layerName);
}
J2dRlsTrace(J2D_TRACE_VERBOSE, " Supported instance extensions:\n");
for (auto &e: _vk_context.enumerateInstanceExtensionProperties(nullptr)) {
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char *) e.extensionName);
extensions.emplace((char *) e.extensionName);
}
std::vector<const char *> enabledLayers, enabledExtensions;
const void *pNext = nullptr;
// Check required layers & extensions.
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
enabledExtensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
#endif
enabledExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
for (auto e: enabledExtensions) {
if (extensions.find(e) == extensions.end()) {
throw std::runtime_error(std::string("Vulkan: Required instance extension not supported:") +
(char *) e);
}
}
// Configure validation
#ifdef DEBUG
std::array<vk::ValidationFeatureEnableEXT, 2> enabledValidationFeatures = {
// vk::ValidationFeatureEnableEXT::eGpuAssisted, // TODO GPU assisted validation is available only from Vulkan 1.1
// vk::ValidationFeatureEnableEXT::eGpuAssistedReserveBindingSlot,
vk::ValidationFeatureEnableEXT::eBestPractices,
vk::ValidationFeatureEnableEXT::eSynchronizationValidation
};
vk::ValidationFeaturesEXT validationFeatures {enabledValidationFeatures};
if (layers.find(VALIDATION_LAYER_NAME) != layers.end() &&
extensions.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != extensions.end()) {
enabledLayers.push_back(VALIDATION_LAYER_NAME);
enabledExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
pNext = &validationFeatures;
} else {
J2dRlsTrace2(J2D_TRACE_WARNING, "Vulkan: %s and %s are not supported\n",
VALIDATION_LAYER_NAME, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
#endif
vk::ApplicationInfo applicationInfo{
/*pApplicationName*/ "OpenJDK",
/*applicationVersion*/ 0,
/*pEngineName*/ "OpenJDK",
/*engineVersion*/ 0,
/*apiVersion*/ REQUIRED_VULKAN_VERSION
};
vk::InstanceCreateInfo instanceCreateInfo{
/*flags*/ {},
/*pApplicationInfo*/ &applicationInfo,
/*ppEnabledLayerNames*/ enabledLayers,
/*ppEnabledExtensionNames*/ enabledExtensions,
/*pNext*/ pNext
};
// Save context object at persistent address before passing it further.
_vk_instance = vk::raii::Instance(_vk_context, instanceCreateInfo);
J2dRlsTrace(J2D_TRACE_INFO, "Vulkan: Instance created\n");
// Create debug messenger
#if defined(DEBUG)
if (pNext) {
_debugMessenger = vk::raii::DebugUtilsMessengerEXT(_vk_instance, vk::DebugUtilsMessengerCreateInfoEXT {
/*flags*/ {},
/*messageSeverity*/ vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose,
/*messageType*/ vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
/*pfnUserCallback*/ &debugCallback
});
}
#endif
// Find suitable devices.
for (auto &handle: _vk_instance.enumeratePhysicalDevices()) {
VKDevice device {*_vk_instance, std::move(handle)};
if (device.supported()) {
_devices.push_back(std::make_unique<VKDevice>(std::move(device)));
}
}
if (_devices.empty()) {
throw std::runtime_error("Vulkan: No suitable device found");
}
// Create virtual device for a physical device.
// TODO integrated/discrete presets
// TODO performance/power saving mode switch on the fly?
int _default_device_number = 0; // TODO pick first just to check that virtual device creation works
if (_verbose) {
fprintf(stderr, "Vulkan graphics devices:\n");
}
_requested_device_number = (_requested_device_number == -1) ? 0 : _requested_device_number;
if (_requested_device_number < 0 || _requested_device_number >= static_cast<int>(_devices.size())) {
if (_verbose) {
fprintf(stderr, " Requested device number (%d) not found, fallback to 0\n", _requested_device_number);
}
_requested_device_number = 0;
}
_default_device_number = _requested_device_number;
if (_verbose) {
for (auto devIter = _devices.begin(); devIter != _devices.end(); devIter++) {
auto devNum = std::distance(begin(_devices), devIter);
fprintf(stderr, " %c%ld: %s\n", devNum == _default_device_number ? '*' : ' ',
devNum, (*devIter)->name().c_str());
}
fprintf(stderr, "\n");
}
_default_device = &*_devices[_default_device_number]; // TODO pick first just to check hat virtual device creation works
_default_device->init();
}
vk::raii::Instance& VKGraphicsEnvironment::vk_instance() {
return _vk_instance;
}
void VKGraphicsEnvironment::dispose() {
_ge_instance.reset();
}
VKDevice& VKGraphicsEnvironment::default_device() {
return *_default_device;
}
// ========== Vulkan device ==========
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
extern struct wl_display *wl_display;
#endif
VKDevice::VKDevice(vk::Instance instance, vk::raii::PhysicalDevice&& handle) :
vk::raii::Device(nullptr), vk::raii::PhysicalDevice(nullptr), _instance(instance) {
auto featuresChain = handle.getFeatures2<vk::PhysicalDeviceFeatures2,
vk::PhysicalDeviceVulkan11Features,
vk::PhysicalDeviceVulkan12Features>();
const auto& features10 = featuresChain.get<vk::PhysicalDeviceFeatures2>().features;
const auto& features11 = featuresChain.get<vk::PhysicalDeviceVulkan11Features>();
const auto& features12 = featuresChain.get<vk::PhysicalDeviceVulkan12Features>();
auto propertiesChain = handle.getProperties2<vk::PhysicalDeviceProperties2,
vk::PhysicalDeviceVulkan11Properties,
vk::PhysicalDeviceVulkan12Properties>();
const auto& properties10 = propertiesChain.get<vk::PhysicalDeviceProperties2>().properties;
const auto& properties11 = propertiesChain.get<vk::PhysicalDeviceVulkan11Properties>();
const auto& properties12 = propertiesChain.get<vk::PhysicalDeviceVulkan12Properties>();
const auto& queueFamilies = handle.getQueueFamilyProperties();
_name = (const char*) properties10.deviceName;
J2dRlsTrace5(J2D_TRACE_INFO, "Vulkan: Found device %s (%d.%d.%d, %s)\n",
(const char*) properties10.deviceName,
VK_API_VERSION_MAJOR(properties10.apiVersion),
VK_API_VERSION_MINOR(properties10.apiVersion),
VK_API_VERSION_PATCH(properties10.apiVersion),
vk::to_string(properties10.deviceType).c_str());
// Check API version.
if (properties10.apiVersion < REQUIRED_VULKAN_VERSION) {
J2dRlsTrace(J2D_TRACE_INFO, " Unsupported Vulkan version\n");
return;
}
// Check supported features.
if (!features10.logicOp) {
J2dRlsTrace(J2D_TRACE_INFO, " Logic op not supported\n");
return;
}
if (!features12.timelineSemaphore) {
J2dRlsTrace(J2D_TRACE_INFO, " Timeline semaphore not supported\n");
return;
}
// Check supported queue families.
for (unsigned int i = 0; i < queueFamilies.size(); i++) {
const auto& family = queueFamilies[i];
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
bool presentationSupported = handle.getWaylandPresentationSupportKHR(i, *wl_display);
#endif
char logFlags[5] {
family.queueFlags & vk::QueueFlagBits::eGraphics ? 'G' : '-',
family.queueFlags & vk::QueueFlagBits::eCompute ? 'C' : '-',
family.queueFlags & vk::QueueFlagBits::eTransfer ? 'T' : '-',
family.queueFlags & vk::QueueFlagBits::eSparseBinding ? 'S' : '-',
presentationSupported ? 'P' : '-'
};
J2dRlsTrace3(J2D_TRACE_INFO, " %d queues in family (%.*s)\n", family.queueCount, 5, logFlags);
// TODO use compute workloads? Separate transfer-only DMA queue?
if (_queue_family == -1 && (family.queueFlags & vk::QueueFlagBits::eGraphics) && presentationSupported) {
_queue_family = i;
}
}
if (_queue_family == -1) {
J2dRlsTrace(J2D_TRACE_INFO, " No suitable queue\n");
return;
}
// Populate maps and log supported layers & extensions.
std::set<std::string> layers, extensions;
J2dRlsTrace(J2D_TRACE_VERBOSE, " Supported device layers:\n");
for (auto& l : handle.enumerateDeviceLayerProperties()) {
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char*) l.layerName);
layers.emplace((char*) l.layerName);
}
J2dRlsTrace(J2D_TRACE_VERBOSE, " Supported device extensions:\n");
for (auto& e : handle.enumerateDeviceExtensionProperties(nullptr)) {
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char*) e.extensionName);
extensions.emplace((char*) e.extensionName);
}
// Check required layers & extensions.
_enabled_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
bool requiredNotFound = false;
for (auto e : _enabled_extensions) {
if (extensions.find(e) == extensions.end()) {
J2dRlsTrace1(J2D_TRACE_INFO, " Required device extension not supported: %s\n", (char*) e);
requiredNotFound = true;
}
}
if (requiredNotFound) return;
_ext_memory_budget = extensions.find(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) != extensions.end();
if (_ext_memory_budget) _enabled_extensions.push_back(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME);
_khr_synchronization2 = extensions.find(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME) != extensions.end();
if (_khr_synchronization2) _enabled_extensions.push_back(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
_khr_dynamic_rendering = extensions.find(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME) != extensions.end();
if (_khr_dynamic_rendering) _enabled_extensions.push_back(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
// Validation layer
#ifdef DEBUG
if (layers.find(VALIDATION_LAYER_NAME) != layers.end()) {
_enabled_layers.push_back(VALIDATION_LAYER_NAME);
} else {
J2dRlsTrace1(J2D_TRACE_INFO, " %s device layer is not supported\n", VALIDATION_LAYER_NAME);
}
#endif
// This device is supported
((vk::raii::PhysicalDevice&) *this) = std::move(handle);
}
void VKDevice::init() {
float queuePriorities[1] {1.0f}; // We only use one queue for now
std::vector<vk::DeviceQueueCreateInfo> queueCreateInfos;
queueCreateInfos.push_back(vk::DeviceQueueCreateInfo {
{}, queue_family(), 1, &queuePriorities[0]
});
vk::PhysicalDeviceFeatures features10;
features10.logicOp = true;
vk::PhysicalDeviceVulkan12Features features12;
features12.timelineSemaphore = true;
void *pNext = &features12;
vk::PhysicalDeviceSynchronization2FeaturesKHR synchronization2Features;
if (_khr_synchronization2) {
synchronization2Features.synchronization2 = true;
synchronization2Features.pNext = pNext;
pNext = &synchronization2Features;
}
vk::PhysicalDeviceDynamicRenderingFeaturesKHR dynamicRenderingFeatures;
if (_khr_dynamic_rendering) {
dynamicRenderingFeatures.dynamicRendering = true;
dynamicRenderingFeatures.pNext = pNext;
pNext = &dynamicRenderingFeatures;
}
vk::DeviceCreateInfo deviceCreateInfo {
/*flags*/ {},
/*pQueueCreateInfos*/ queueCreateInfos,
/*ppEnabledLayerNames*/ _enabled_layers,
/*ppEnabledExtensionNames*/ _enabled_extensions,
/*pEnabledFeatures*/ &features10,
/*pNext*/ pNext
};
((vk::raii::Device&) *this) = {*this, deviceCreateInfo};
_memory.init(_instance, *this, *this, REQUIRED_VULKAN_VERSION, _ext_memory_budget);
_pipelines.init((vk::raii::Device&) *this, _khr_dynamic_rendering);
_queue = getQueue(queue_family(), 0);
_commandPool = createCommandPool(vk::CommandPoolCreateInfo {
vk::CommandPoolCreateFlagBits::eTransient | vk::CommandPoolCreateFlagBits::eResetCommandBuffer,
queue_family()
});
vk::SemaphoreTypeCreateInfo semaphoreTypeCreateInfo { vk::SemaphoreType::eTimeline, 0 };
_timelineSemaphore = createSemaphore(vk::SemaphoreCreateInfo {{}, &semaphoreTypeCreateInfo});
_timelineCounter = 0;
J2dRlsTrace1(J2D_TRACE_INFO, "Vulkan: Device created %s\n", _name.c_str());
}
VKBuffer VKDevice::getVertexBuffer() {
auto b = popPending<VKBuffer>(_pendingVertexBuffers);
if (*b) {
b.position() = 0;
return b;
} else {
return _memory.allocateBuffer(64 * 1024, vk::BufferUsageFlagBits::eVertexBuffer,
vma::AllocationCreateFlagBits::eMapped | vma::AllocationCreateFlagBits::eHostAccessSequentialWrite,
vma::MemoryUsage::eAutoPreferHost);
}
}
vk::raii::CommandBuffer VKDevice::getCommandBuffer(vk::CommandBufferLevel level) {
auto b = popPending<vk::raii::CommandBuffer>(level == vk::CommandBufferLevel::ePrimary ?
_pendingPrimaryBuffers : _pendingSecondaryBuffers);
if (*b) {
b.reset({});
return b;
} else {
return std::move(allocateCommandBuffers({*_commandPool, level, 1})[0]);
}
}
void VKDevice::submitCommandBuffer(vk::raii::CommandBuffer&& primary,
std::vector<vk::raii::CommandBuffer>& secondary,
std::vector<VKBuffer>& vertexBuffers,
std::vector<vk::Semaphore>& waitSemaphores,
std::vector<vk::PipelineStageFlags>& waitStages,
std::vector<vk::Semaphore>& signalSemaphores) {
_timelineCounter++;
signalSemaphores.insert(signalSemaphores.begin(), *_timelineSemaphore);
vk::TimelineSemaphoreSubmitInfo timelineInfo { 0, nullptr, (uint32_t) signalSemaphores.size(), &_timelineCounter };
queue().submit(vk::SubmitInfo {
waitSemaphores, waitStages, *primary, signalSemaphores, &timelineInfo
}, nullptr);
pushPending(_pendingPrimaryBuffers, std::move(primary));
pushPending(_pendingSecondaryBuffers, secondary);
pushPending(_pendingVertexBuffers, vertexBuffers);
signalSemaphores.clear();
waitSemaphores.clear();
waitStages.clear();
}
extern "C" jboolean VK_Init(jboolean verbose, jint requestedDevice) {
VKGraphicsEnvironment::set_verbose(verbose);
VKGraphicsEnvironment::set_requested_device(requestedDevice);
if (VKGraphicsEnvironment::graphics_environment() != nullptr) {
return true;
}
return false;
}
extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
VKGraphicsEnvironment::dispose();
}

View File

@@ -26,139 +26,124 @@
#ifndef VKBase_h_Included
#define VKBase_h_Included
#ifdef __cplusplus
#define VK_NO_PROTOTYPES
#define VULKAN_HPP_NO_DEFAULT_DISPATCHER
#include <queue>
#include <vulkan/vulkan_raii.hpp>
#include <vulkan/vulkan.h>
#include "CArrayUtil.h"
#include "jni.h"
#include "VKMemory.h"
#include "VKPipeline.h"
#include "VKBuffer.h"
class VKDevice : public vk::raii::Device, public vk::raii::PhysicalDevice {
friend class VKGraphicsEnvironment;
typedef char* pchar;
vk::Instance _instance;
std::string _name;
std::vector<const char*> _enabled_layers, _enabled_extensions;
bool _ext_memory_budget, _khr_synchronization2, _khr_dynamic_rendering;
int _queue_family = -1;
typedef struct {
VkRenderPass renderPass;
VkDescriptorSetLayout descriptorSetLayout;
VkDescriptorPool descriptorPool;
VkDescriptorSet descriptorSets;
VkPipelineLayout pipelineLayout;
VkPipeline graphicsPipeline;
} VKRenderer;
// Logical device state
VKMemory _memory;
VKPipelines _pipelines;
vk::raii::Queue _queue = nullptr;
vk::raii::CommandPool _commandPool = nullptr;
vk::raii::Semaphore _timelineSemaphore = nullptr;
uint64_t _timelineCounter = 0;
uint64_t _lastReadTimelineCounter = 0;
typedef struct {
VkDevice device;
VkPhysicalDevice physicalDevice;
VKRenderer* fillTexturePoly;
VKRenderer* fillColorPoly;
VKRenderer* fillMaxColorPoly;
char* name;
uint32_t queueFamily;
pchar* enabledLayers;
pchar* enabledExtensions;
VkCommandPool commandPool;
VkCommandBuffer commandBuffer;
VkSemaphore imageAvailableSemaphore;
VkSemaphore renderFinishedSemaphore;
VkFence inFlightFence;
VkQueue queue;
VkSampler textureSampler;
VKBuffer* blitVertexBuffer;
} VKLogicalDevice;
template <typename T> struct Pending {
T resource;
uint64_t counter;
using Queue = std::queue<Pending<T>>;
};
Pending<vk::raii::CommandBuffer>::Queue _pendingPrimaryBuffers, _pendingSecondaryBuffers;
Pending<VKBuffer>::Queue _pendingVertexBuffers;
template <typename T> T popPending(typename Pending<T>::Queue& queue) {
if (!queue.empty()) {
auto& f = queue.front();
if (_lastReadTimelineCounter >= f.counter ||
(_lastReadTimelineCounter = _timelineSemaphore.getCounterValue()) >= f.counter) {
T resource = std::move(f.resource);
queue.pop();
return resource;
}
}
return T(nullptr);
}
template <typename T> void pushPending(typename Pending<T>::Queue& queue, T&& resource) {
queue.push({std::move(resource), _timelineCounter});
}
template <typename T> void pushPending(typename Pending<T>::Queue& queue, std::vector<T>& resources) {
for (T& r : resources) {
pushPending(queue, std::move(r));
}
resources.clear();
}
typedef struct {
VkInstance vkInstance;
VkPhysicalDevice* physicalDevices;
VKLogicalDevice* devices;
uint32_t enabledDeviceNum;
VkExtensionProperties* extensions;
VkLayerProperties* layers;
explicit VKDevice(vk::Instance instance, vk::raii::PhysicalDevice&& handle);
public:
bool synchronization2() {
return _khr_synchronization2;
}
bool dynamicRendering() {
return _khr_dynamic_rendering;
}
VKPipelines& pipelines() {
return _pipelines;
}
uint32_t queue_family() const {
return (uint32_t) _queue_family;
}
const vk::raii::Queue& queue() const {
return _queue;
}
void init(); // Creates actual logical device
VKBuffer getVertexBuffer();
vk::raii::CommandBuffer getCommandBuffer(vk::CommandBufferLevel level);
void submitCommandBuffer(vk::raii::CommandBuffer&& primary,
std::vector<vk::raii::CommandBuffer>& secondary,
std::vector<VKBuffer>& vertexBuffers,
std::vector<vk::Semaphore>& waitSemaphores,
std::vector<vk::PipelineStageFlags>& waitStages,
std::vector<vk::Semaphore>& signalSemaphores);
bool supported() const { // Supported or not
return *((const vk::raii::PhysicalDevice&) *this);
}
explicit operator bool() const { // Initialized or not
return *((const vk::raii::Device&) *this);
}
const std::string& name() {
return _name;
}
};
class VKGraphicsEnvironment {
vk::raii::Context _vk_context;
vk::raii::Instance _vk_instance;
#if defined(DEBUG)
vk::raii::DebugUtilsMessengerEXT _debugMessenger = nullptr;
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2;
PFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2;
PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties;
PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties;
PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR;
PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
#endif
std::vector<std::unique_ptr<VKDevice>> _devices;
VKDevice* _default_device;
static bool _verbose;
static int _requested_device_number;
static std::unique_ptr<VKGraphicsEnvironment> _ge_instance;
VKGraphicsEnvironment();
public:
static void set_verbose(bool verbose) { _verbose = verbose; }
static void set_requested_device(int requested_device) { _requested_device_number = requested_device; }
static VKGraphicsEnvironment* graphics_environment();
static void dispose();
VKDevice& default_device();
vk::raii::Instance& vk_instance();
};
PFN_vkCreateShaderModule vkCreateShaderModule;
PFN_vkCreatePipelineLayout vkCreatePipelineLayout;
PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
PFN_vkDestroyShaderModule vkDestroyShaderModule;
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR;
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
PFN_vkCreateImageView vkCreateImageView;
PFN_vkCreateFramebuffer vkCreateFramebuffer;
PFN_vkCreateCommandPool vkCreateCommandPool;
PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
PFN_vkCreateSemaphore vkCreateSemaphore;
PFN_vkCreateFence vkCreateFence;
PFN_vkGetDeviceQueue vkGetDeviceQueue;
PFN_vkWaitForFences vkWaitForFences;
PFN_vkResetFences vkResetFences;
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
PFN_vkResetCommandBuffer vkResetCommandBuffer;
PFN_vkQueueSubmit vkQueueSubmit;
PFN_vkQueuePresentKHR vkQueuePresentKHR;
PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
PFN_vkCmdBindPipeline vkCmdBindPipeline;
PFN_vkCmdSetViewport vkCmdSetViewport;
PFN_vkCmdSetScissor vkCmdSetScissor;
PFN_vkCmdDraw vkCmdDraw;
PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
PFN_vkEndCommandBuffer vkEndCommandBuffer;
PFN_vkCreateImage vkCreateImage;
PFN_vkCreateSampler vkCreateSampler;
PFN_vkAllocateMemory vkAllocateMemory;
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
PFN_vkBindImageMemory vkBindImageMemory;
PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets;
PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets;
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
PFN_vkCreateBuffer vkCreateBuffer;
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
PFN_vkBindBufferMemory vkBindBufferMemory;
PFN_vkMapMemory vkMapMemory;
PFN_vkUnmapMemory vkUnmapMemory;
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
PFN_vkCreateRenderPass vkCreateRenderPass;
PFN_vkDestroyBuffer vkDestroyBuffer;
PFN_vkFreeMemory vkFreeMemory;
PFN_vkDestroyImageView vkDestroyImageView;
PFN_vkDestroyImage vkDestroyImage;
PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
PFN_vkCmdPushConstants vkCmdPushConstants;
} VKGraphicsEnvironment;
extern "C" {
#endif //__cplusplus
jboolean VK_Init(jboolean verbose, jint requestedDevice);
jboolean VK_FindDevices();
jboolean VK_CreateLogicalDevice(jint requestedDeviceNumber);
jboolean VK_CreateLogicalDeviceRenderers();
VKGraphicsEnvironment* VKGE_graphics_environment();
void* vulkanLibProc(VkInstance vkInstance, char* procName);
#ifdef __cplusplus
}
#endif //__cplusplus
#endif //VKBase_h_Included

View File

@@ -0,0 +1,149 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <string.h>
#include <Trace.h>
#include "CArrayUtil.h"
#include "VKBase.h"
#include "VKBuffer.h"
VkResult VKBuffer_FindMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter,
VkMemoryPropertyFlags properties, uint32_t* pMemoryType) {
VkPhysicalDeviceMemoryProperties memProperties;
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
ge->vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
*pMemoryType = i;
return VK_SUCCESS;
}
}
return VK_ERROR_UNKNOWN;
}
VKBuffer* VKBuffer_Create(VkDeviceSize size, VkBufferUsageFlags usage,
VkMemoryPropertyFlags properties)
{
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VKBuffer* buffer = malloc(sizeof (VKBuffer));
VkBufferCreateInfo bufferInfo = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = size,
.usage = usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
};
if (ge->vkCreateBuffer(logicalDevice->device, &bufferInfo, NULL, &buffer->buffer) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "failed to allocate descriptor sets!")
return NULL;
}
buffer->size = size;
VkMemoryRequirements memRequirements;
ge->vkGetBufferMemoryRequirements(logicalDevice->device, buffer->buffer, &memRequirements);
uint32_t memoryType;
if (VKBuffer_FindMemoryType(logicalDevice->physicalDevice,
memRequirements.memoryTypeBits,
properties, &memoryType) != VK_SUCCESS)
{
J2dRlsTraceLn(J2D_TRACE_ERROR, "failed to find memory!")
return NULL;
}
VkMemoryAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memRequirements.size,
.memoryTypeIndex = memoryType
};
if (ge->vkAllocateMemory(logicalDevice->device, &allocInfo, NULL, &buffer->memory) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "failed to allocate buffer memory!");
return NULL;
}
if (ge->vkBindBufferMemory(logicalDevice->device, buffer->buffer, buffer->memory, 0) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "failed to bind buffer memory!");
return NULL;
}
return buffer;
}
VKBuffer* VKBuffer_CreateFromData(void* vertices, VkDeviceSize bufferSize)
{
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VKBuffer* buffer = VKBuffer_Create(bufferSize,
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
void* data;
if (ge->vkMapMemory(logicalDevice->device, buffer->memory, 0, bufferSize, 0, &data) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "failed to map memory!");
return NULL;
}
memcpy(data, vertices, bufferSize);
VkMappedMemoryRange memoryRange = {
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.pNext = NULL,
.memory = buffer->memory,
.offset = 0,
.size = VK_WHOLE_SIZE
};
if (ge->vkFlushMappedMemoryRanges(logicalDevice->device, 1, &memoryRange) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "failed to flush memory!");
return NULL;
}
ge->vkUnmapMemory(logicalDevice->device, buffer->memory);
buffer->size = bufferSize;
return buffer;
}
void VKBuffer_free(VKBuffer* buffer) {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
if (buffer != NULL) {
if (buffer->buffer != VK_NULL_HANDLE) {
ge->vkDestroyBuffer(logicalDevice->device, buffer->buffer, NULL);
}
if (buffer->memory != VK_NULL_HANDLE) {
ge->vkFreeMemory(logicalDevice->device, buffer->memory, NULL);
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,24 +24,23 @@
* questions.
*/
#include "VKShader.h"
#ifndef VKBuffer_h_Included
#define VKBuffer_h_Included
#include <vulkan/vulkan.h>
// Inline bytecode of all shaders
#define INCLUDE_BYTECODE
#define SHADER_ENTRY(NAME, TYPE) static uint32_t NAME ## _ ## TYPE ## _data[] = {
#define BYTECODE_END };
#include "vulkan/shader_list.h"
#undef INCLUDE_BYTECODE
#undef SHADER_ENTRY
#undef BYTECODE_END
typedef struct {
VkBuffer buffer;
VkDeviceMemory memory;
VkDeviceSize size;
} VKBuffer;
void VKShaders::init(const vk::raii::Device& device) {
// Declare file extensions as stage flags
auto vert = vk::ShaderStageFlagBits::eVertex;
auto frag = vk::ShaderStageFlagBits::eFragment;
// Init all shader modules
# define SHADER_ENTRY(NAME, TYPE) \
NAME ## _ ## TYPE.init(device, sizeof NAME ## _ ## TYPE ## _data, NAME ## _ ## TYPE ## _data, TYPE);
# include "vulkan/shader_list.h"
# undef SHADER_ENTRY
}
VkResult VKBuffer_FindMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter,
VkMemoryPropertyFlags properties, uint32_t* pMemoryType);
VKBuffer* VKBuffer_Create(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties);
VKBuffer* VKBuffer_CreateFromData(void* vertices, VkDeviceSize bufferSize);
void VKBuffer_free(VKBuffer* buffer);
#endif // VKBuffer_h_Included

View File

@@ -0,0 +1,233 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <Trace.h>
#include "VKBase.h"
#include "VKBuffer.h"
#include "VKImage.h"
VkBool32 VKImage_CreateView(VKImage* image) {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image->image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = image->format,
.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.subresourceRange.baseMipLevel = 0,
.subresourceRange.levelCount = 1,
.subresourceRange.baseArrayLayer = 0,
.subresourceRange.layerCount = 1,
};
if (ge->vkCreateImageView(logicalDevice->device, &viewInfo, NULL, &image->view) != VK_SUCCESS) {
J2dRlsTrace(J2D_TRACE_ERROR, "Cannot surface image view\n");
return VK_FALSE;
}
return VK_TRUE;
}
VkBool32 VKImage_CreateFramebuffer(VKImage *image, VkRenderPass renderPass) {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VkImageView attachments[] = {
image->view
};
VkFramebufferCreateInfo framebufferInfo = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = renderPass,
.attachmentCount = 1,
.pAttachments = attachments,
.width = image->extent.width,
.height = image->extent.height,
.layers = 1
};
if (ge->vkCreateFramebuffer(logicalDevice->device, &framebufferInfo, NULL,
&image->framebuffer) != VK_SUCCESS)
{
J2dRlsTraceLn(J2D_TRACE_ERROR, "failed to create framebuffer!")
return VK_FALSE;
}
return VK_TRUE;
}
VKImage* VKImage_Create(uint32_t width, uint32_t height,
VkFormat format, VkImageTiling tiling,
VkImageUsageFlags usage,
VkMemoryPropertyFlags properties)
{
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VKImage* image = malloc(sizeof (VKImage));
if (!image) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Cannot allocate data for image")
return NULL;
}
image->format = format;
image->extent = (VkExtent2D) {width, height};
VkImageCreateInfo imageInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.extent.width = width,
.extent.height = height,
.extent.depth = 1,
.mipLevels = 1,
.arrayLayers = 1,
.format = format,
.tiling = tiling,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.usage = usage,
.samples = VK_SAMPLE_COUNT_1_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
};
if (ge->vkCreateImage(logicalDevice->device, &imageInfo, NULL, &image->image) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Cannot create surface image")
VKImage_free(image);
return NULL;
}
VkMemoryRequirements memRequirements;
ge->vkGetImageMemoryRequirements(logicalDevice->device, image->image, &memRequirements);
uint32_t memoryType;
if (VKBuffer_FindMemoryType(logicalDevice->physicalDevice,
memRequirements.memoryTypeBits,
properties, &memoryType) != VK_SUCCESS)
{
J2dRlsTraceLn(J2D_TRACE_ERROR, "Failed to find memory")
VKImage_free(image);
return NULL;
}
VkMemoryAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memRequirements.size,
.memoryTypeIndex = memoryType
};
if (ge->vkAllocateMemory(logicalDevice->device, &allocInfo, NULL, &image->memory) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Failed to allocate image memory");
VKImage_free(image);
return NULL;
}
ge->vkBindImageMemory(logicalDevice->device, image->image, image->memory, 0);
if (!VKImage_CreateView(image)) {
VKImage_free(image);
return NULL;
}
return image;
}
VKImage* VKImage_CreateImageArrayFromSwapChain(VkSwapchainKHR swapchainKhr, VkRenderPass renderPass,
VkFormat format, VkExtent2D extent)
{
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
uint32_t swapChainImagesCount;
if (ge->vkGetSwapchainImagesKHR(logicalDevice->device, swapchainKhr, &swapChainImagesCount,
NULL) != VK_SUCCESS) {
J2dRlsTrace(J2D_TRACE_ERROR, "Cannot get swapchain images\n");
return NULL;
}
if (swapChainImagesCount == 0) {
J2dRlsTrace(J2D_TRACE_ERROR, "No swapchain images found\n");
return NULL;
}
VkImage swapChainImages[swapChainImagesCount];
if (ge->vkGetSwapchainImagesKHR(logicalDevice->device, swapchainKhr, &swapChainImagesCount,
swapChainImages) != VK_SUCCESS) {
J2dRlsTrace(J2D_TRACE_ERROR, "Cannot get swapchain images\n");
return NULL;
}
VKImage* images = ARRAY_ALLOC(VKImage, swapChainImagesCount);
for (uint32_t i = 0; i < swapChainImagesCount; i++) {
ARRAY_PUSH_BACK(&images, ((VKImage){
.image = swapChainImages[i],
.memory = VK_NULL_HANDLE,
.format = format,
.extent = extent,
.noImageDealloc = VK_TRUE
}));
if (!VKImage_CreateView(&ARRAY_LAST(images))) {
ARRAY_APPLY(images, VKImage_dealloc);
ARRAY_FREE(images);
return NULL;
}
if (!VKImage_CreateFramebuffer(&ARRAY_LAST(images), renderPass)) {
ARRAY_APPLY(images, VKImage_dealloc);
ARRAY_FREE(images);
return NULL;
}
}
return images;
}
void VKImage_dealloc(VKImage* image) {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
if (!image) return;
if (image->framebuffer != VK_NULL_HANDLE) {
ge->vkDestroyFramebuffer(logicalDevice->device, image->framebuffer, NULL);
}
if (image->view != VK_NULL_HANDLE) {
ge->vkDestroyImageView(logicalDevice->device, image->view, NULL);
}
if (image->memory != VK_NULL_HANDLE) {
ge->vkFreeMemory(logicalDevice->device, image->memory, NULL);
}
if (image->image != VK_NULL_HANDLE && !image->noImageDealloc) {
ge->vkDestroyImage(logicalDevice->device, image->image, NULL);
}
}
void VKImage_free(VKImage* image) {
VKImage_dealloc(image);
free(image);
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef VKImage_h_Included
#define VKImage_h_Included
#include <vulkan/vulkan.h>
typedef struct {
VkImage image;
VkDeviceMemory memory;
VkFramebuffer framebuffer;
VkImageView view;
VkFormat format;
VkExtent2D extent;
VkBool32 noImageDealloc;
} VKImage;
VKImage* VKImage_Create(uint32_t width, uint32_t height,
VkFormat format, VkImageTiling tiling,
VkImageUsageFlags usage,
VkMemoryPropertyFlags properties);
VKImage* VKImage_CreateImageArrayFromSwapChain(VkSwapchainKHR swapchainKhr,
VkRenderPass renderPass,
VkFormat format,
VkExtent2D extent);
VkBool32 VKImage_CreateFramebuffer(VKImage *image, VkRenderPass renderPass);
void VKImage_free(VKImage* image);
void VKImage_dealloc(VKImage* image);
#endif // VKImage_h_Included

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,7 +24,9 @@
* questions.
*/
#include "jni.h"
#include "VKBase.h"
#ifndef VKInit_h_Included
#define VKInit_h_Included
// TODO ?
jboolean VK_Init(jboolean verbose, jint requestedDevice);
#endif //VKInit_h_Included

View File

@@ -1,59 +0,0 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#define VMA_IMPLEMENTATION
#include "VKMemory.h"
void VKMemory::init(vk::Instance instance, const vk::raii::PhysicalDevice& physicalDevice,
const vk::raii::Device& device, uint32_t apiVersion, bool extMemoryBudget) {
vma::VulkanFunctions functions = vma::functionsFromDispatcher(physicalDevice.getDispatcher(), device.getDispatcher());
vma::AllocatorCreateInfo createInfo {};
createInfo.instance = instance;
createInfo.physicalDevice = *physicalDevice;
createInfo.device = *device;
createInfo.pVulkanFunctions = &functions;
createInfo.vulkanApiVersion = apiVersion;
if (extMemoryBudget) {
createInfo.flags |= vma::AllocatorCreateFlagBits::eExtMemoryBudget;
}
_allocator = vma::createAllocatorUnique(createInfo);
*((vma::Allocator*) this) = *_allocator;
}
VKBuffer VKMemory::allocateBuffer(uint32_t size, vk::BufferUsageFlags usage,
vma::AllocationCreateFlags flags, vma::MemoryUsage memoryUsage) {
VKBuffer b = nullptr;
auto pair = createBufferUnique(vk::BufferCreateInfo {
{}, size, usage, vk::SharingMode::eExclusive, {}
}, vma::AllocationCreateInfo {
flags,
memoryUsage, {}, {}, (uint32_t) -1
}, b._allocationInfo);
b._buffer = std::move(pair.first);
b._allocation = std::move(pair.second);
b._size = size;
return b;
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef VKMemory_h_Included
#define VKMemory_h_Included
#define VK_NO_PROTOTYPES
#define VULKAN_HPP_NO_DEFAULT_DISPATCHER
#include <vulkan/vulkan_raii.hpp>
#include <vk_mem_alloc.hpp>
class VKBuffer {
friend class VKMemory;
vma::UniqueBuffer _buffer;
vma::UniqueAllocation _allocation;
vma::AllocationInfo _allocationInfo;
uint32_t _size = 0;
uint32_t _position = 0;
public:
VKBuffer(nullptr_t) {}
vk::Buffer operator*() const {
return *_buffer;
}
uint32_t size() const {
return _size;
}
uint32_t& position() {
return _position;
}
void* data() {
return _allocationInfo.pMappedData;
}
};
class VKMemory : vma::Allocator {
vma::UniqueAllocator _allocator;
public:
void init(vk::Instance instance, const vk::raii::PhysicalDevice& physicalDevice,
const vk::raii::Device& device, uint32_t apiVersion, bool extMemoryBudget);
VKBuffer allocateBuffer(uint32_t size, vk::BufferUsageFlags usage,
vma::AllocationCreateFlags flags, vma::MemoryUsage memoryUsage);
};
#endif //VKMemory_h_Included

View File

@@ -1,127 +0,0 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "VKPipeline.h"
void VKPipelines::init(const vk::raii::Device& device, bool dynamicRendering) {
shaders.init(device);
vk::Format format = vk::Format::eB8G8R8A8Unorm; // TODO
if (!dynamicRendering) {
vk::AttachmentDescription attachmentDescription {
/*flags*/ {},
/*format*/ format,
/*samples*/ vk::SampleCountFlagBits::e1,
/*loadOp*/ vk::AttachmentLoadOp::eLoad,
/*storeOp*/ vk::AttachmentStoreOp::eStore,
/*stencilLoadOp*/ vk::AttachmentLoadOp::eDontCare,
/*stencilStoreOp*/ vk::AttachmentStoreOp::eDontCare,
/*initialLayout*/ vk::ImageLayout::eColorAttachmentOptimal,
/*finalLayout*/ vk::ImageLayout::eColorAttachmentOptimal
};
vk::AttachmentReference attachmentReference { 0, vk::ImageLayout::eColorAttachmentOptimal };
vk::SubpassDescription subpassDescription {
/*flags*/ {},
/*pipelineBindPoint*/ vk::PipelineBindPoint::eGraphics,
/*inputAttachmentCount*/ 0,
/*pInputAttachments*/ nullptr,
/*colorAttachmentCount*/ 1,
/*pColorAttachments*/ &attachmentReference,
/*pResolveAttachments*/ nullptr,
/*pDepthStencilAttachment*/ nullptr,
/*preserveAttachmentCount*/ 0,
/*pPreserveAttachments*/ nullptr,
};
// We don't know in advance, which operations to synchronize
// with before and after the render pass, so do a full sync.
std::array<vk::SubpassDependency, 2> subpassDependencies {vk::SubpassDependency{
/*srcSubpass*/ VK_SUBPASS_EXTERNAL,
/*dstSubpass*/ 0,
/*srcStageMask*/ vk::PipelineStageFlagBits::eBottomOfPipe,
/*dstStageMask*/ vk::PipelineStageFlagBits::eColorAttachmentOutput,
/*srcAccessMask*/ {},
/*dstAccessMask*/ vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
/*dependencyFlags*/ {},
}, vk::SubpassDependency{
/*srcSubpass*/ 0,
/*dstSubpass*/ VK_SUBPASS_EXTERNAL,
/*srcStageMask*/ vk::PipelineStageFlagBits::eColorAttachmentOutput,
/*dstStageMask*/ vk::PipelineStageFlagBits::eTopOfPipe,
/*srcAccessMask*/ vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
/*dstAccessMask*/ {},
/*dependencyFlags*/ {},
}};
renderPass = device.createRenderPass(vk::RenderPassCreateInfo{
/*flags*/ {},
/*pAttachments*/ attachmentDescription,
/*pSubpasses*/ subpassDescription,
/*pDependencies*/ subpassDependencies
});
}
vk::PushConstantRange pushConstantRange {vk::ShaderStageFlagBits::eVertex, 0, sizeof(float) * 2};
testLayout = device.createPipelineLayout(vk::PipelineLayoutCreateInfo {{}, {}, pushConstantRange});
std::array<vk::PipelineShaderStageCreateInfo, 2> testStages {shaders.test_vert.stage(), shaders.test_frag.stage()};
vk::VertexInputBindingDescription vertexInputBindingDescription {0, 8, vk::VertexInputRate::eVertex};
vk::VertexInputAttributeDescription vertexInputAttributeDescription {0, 0, vk::Format::eR32G32Sfloat, 0};
vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo {{}, vertexInputBindingDescription, vertexInputAttributeDescription};
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo {{}, vk::PrimitiveTopology::eTriangleFan, false};
vk::Viewport viewport;
vk::Rect2D scissor;
vk::PipelineViewportStateCreateInfo viewportStateCreateInfo {{}, viewport, scissor};
vk::PipelineRasterizationStateCreateInfo rasterizationStateCreateInfo {
{}, false, false, vk::PolygonMode::eFill, vk::CullModeFlagBits::eNone,
vk::FrontFace::eClockwise, false, 0, 0, 0, 1
};
vk::PipelineMultisampleStateCreateInfo multisampleStateCreateInfo {};
vk::PipelineColorBlendAttachmentState colorBlendAttachmentState {false}; // TODO No blending yet
colorBlendAttachmentState.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
vk::PipelineColorBlendStateCreateInfo colorBlendStateCreateInfo {{}, false, vk::LogicOp::eXor, colorBlendAttachmentState};
std::array<vk::DynamicState, 2> dynamicStates {vk::DynamicState::eViewport, vk::DynamicState::eScissor};
vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo {{}, dynamicStates};
vk::PipelineRenderingCreateInfoKHR renderingCreateInfo {0, format};
auto pipelines = device.createGraphicsPipelines(nullptr, {
vk::GraphicsPipelineCreateInfo {
{}, testStages,
&vertexInputStateCreateInfo,
&inputAssemblyStateCreateInfo,
nullptr,
&viewportStateCreateInfo,
&rasterizationStateCreateInfo,
&multisampleStateCreateInfo,
nullptr,
&colorBlendStateCreateInfo,
&dynamicStateCreateInfo,
*testLayout,
*renderPass, 0, nullptr, 0,
dynamicRendering ? &renderingCreateInfo : nullptr
}
});
// TODO pipeline cache
test = std::move(pipelines[0]);
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,7 +33,9 @@
#include "Trace.h"
#include "jlong.h"
#include "VKRenderQueue.h"
#include "VKSurfaceData.h"
#include "VKRenderer.h"
#include "VKVertex.h"
#define BYTES_PER_POLY_POINT \
sun_java2d_pipe_BufferedRenderPipe_BYTES_PER_POLY_POINT
@@ -61,12 +63,13 @@
#define OFFSET_XFORM sun_java2d_vulkan_VKBlitLoops_OFFSET_XFORM
#define OFFSET_ISOBLIT sun_java2d_vulkan_VKBlitLoops_OFFSET_ISOBLIT
static VKRenderer renderer;
static VKSDOps *dstOps = NULL;
extern "C" JNIEXPORT void JNICALL
Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
(JNIEnv *env, jobject oglrq,
jlong buf, jint limit)
// TODO move this property to special drawing context structure
static int color = -1;
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
(JNIEnv *env, jobject oglrq, jlong buf, jint limit)
{
unsigned char *b, *end;
@@ -103,7 +106,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn4(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: DRAW_LINE(%d, %d, %d, %d)",
x1, y1, x2, y2);
renderer.drawLine(x1, y1, x2, y2);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_RECT:
@@ -115,7 +117,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn4(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: DRAW_RECT(%d, %d, %d, %d)",
x, y, w, h);
renderer.drawRect(x, y, w, h);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_POLY:
@@ -128,7 +129,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint *yPoints = ((jint *)b) + nPoints;
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: DRAW_POLY");
SKIP_BYTES(b, nPoints * BYTES_PER_POLY_POINT);
renderer.drawPoly();
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_PIXEL:
@@ -136,7 +136,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint x = NEXT_INT(b);
jint y = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: DRAW_PIXEL");
renderer.drawPixel(x, y);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_SCANLINES:
@@ -144,7 +143,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint count = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: DRAW_SCANLINES");
SKIP_BYTES(b, count * BYTES_PER_SCANLINE);
renderer.drawScanlines();
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_PARALLELOGRAM:
@@ -160,7 +158,6 @@ 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);
renderer.drawParallelogram(x11, y11, dx21, dy21, dx12, dy12, lwr21, lwr12);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DRAW_AAPARALLELOGRAM:
@@ -188,7 +185,7 @@ 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);
renderer.fillRect(x, y, w, h);
VKRenderer_FillRect(x, y, w, h);
}
break;
case sun_java2d_pipe_BufferedOpCodes_FILL_SPANS:
@@ -197,7 +194,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FILL_SPANS");
SKIP_BYTES(b, count * BYTES_PER_SPAN);
renderer.fillSpans();
}
break;
case sun_java2d_pipe_BufferedOpCodes_FILL_PARALLELOGRAM:
@@ -211,7 +207,6 @@ 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);
renderer.fillParallelogram(x11, y11, dx21, dy21, dx12, dy12);
}
break;
case sun_java2d_pipe_BufferedOpCodes_FILL_AAPARALLELOGRAM:
@@ -225,7 +220,59 @@ 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);
renderer.fillAAParallelogram(x11, y11, dx21, dy21, dx12, dy12);
// TODO: For now rendering bounding box. Implement correct rendering using shaders
if (dstOps != NULL) {
VKSDOps *vksdOps = (VKSDOps *)dstOps;
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
float width = vksdOps->width;
float height = vksdOps->height;
J2dRlsTraceLn2(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FILL_AAPARALLELOGRAM(W=%f, H=%f)",
width, height);
VKCVertex* cVertices = ARRAY_ALLOC(VKCVertex, 4);
float px11 = -1.0f + x11 / width;
float py11 = -1.0f + y11 / height;
float px21 = -1.0f + (x11 + dx21) / width;
float py21 = -1.0f + (y11 + dy21) / height;
ARRAY_PUSH_BACK(&cVertices, ((VKCVertex){px11,
py11,
RGBA_TO_L4(color)}));
ARRAY_PUSH_BACK(&cVertices, ((VKCVertex){px21,
py11,
RGBA_TO_L4(color)}));
ARRAY_PUSH_BACK(&cVertices, ((VKCVertex){px11,
py21,
RGBA_TO_L4(color)}));
ARRAY_PUSH_BACK(&cVertices, ((VKCVertex){px21,
py21,
RGBA_TO_L4(color)}));
J2dRlsTraceLn5(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FILL_AAPARALLELOGRAM(color=%x, px11=%f, py11=%f, px21=%f, py21=%f)",
color, px11, py11, px21, py21);
VKBuffer* fillVertexBuffer = ARRAY_TO_VERTEX_BUF(cVertices);
if (!fillVertexBuffer) {
J2dRlsTrace(J2D_TRACE_ERROR, "Cannot create vertex buffer\n")
break;
}
ARRAY_FREE(cVertices);
ge->vkWaitForFences(logicalDevice->device, 1, &logicalDevice->inFlightFence, VK_TRUE, UINT64_MAX);
ge->vkResetFences(logicalDevice->device, 1, &logicalDevice->inFlightFence);
ge->vkResetCommandBuffer(logicalDevice->commandBuffer, 0);
VKRenderer_BeginRendering();
VKRenderer_ColorRender(
vksdOps->image,
fillVertexBuffer->buffer, 4
);
VKRenderer_EndRendering(VK_FALSE, VK_FALSE);
}
}
break;
@@ -256,7 +303,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
}
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: DRAW_GLYPH_LIST");
SKIP_BYTES(b, numGlyphs * bytesPerGlyph);
renderer.drawGlyphList();
}
break;
@@ -272,7 +318,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn6(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: COPY_AREA(%d, %d, %d, %d, %d, %d)",
x, y, w, h, dx, dy);
renderer.copyArea(x, y, w, h, dx, dy);
}
break;
case sun_java2d_pipe_BufferedOpCodes_BLIT:
@@ -298,7 +343,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jboolean isoblit = EXTRACT_BOOLEAN(packedParams,
OFFSET_ISOBLIT);
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: BLIT");
renderer.blit();
}
break;
case sun_java2d_pipe_BufferedOpCodes_SURFACE_TO_SW_BLIT:
@@ -313,7 +357,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jlong pSrc = NEXT_LONG(b);
jlong pDst = NEXT_LONG(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: SURFACE_TO_SW_BLIT");
renderer.surfaceToSwBlit();
}
break;
case sun_java2d_pipe_BufferedOpCodes_MASK_FILL:
@@ -328,7 +371,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
unsigned char *pMask = (masklen > 0) ? b : NULL;
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: MASK_FILL");
SKIP_BYTES(b, masklen);
renderer.maskFill();
}
break;
case sun_java2d_pipe_BufferedOpCodes_MASK_BLIT:
@@ -340,7 +382,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint masklen = width * height * sizeof(jint);
J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: MASK_BLIT");
SKIP_BYTES(b, masklen);
renderer.maskBlit();
}
break;
@@ -354,14 +395,12 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn4(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_RECT_CLIP(%d, %d, %d, %d)",
x1, y1, x2, y2);
renderer.setRectClip(x1, y1, x2, y2);
}
break;
case sun_java2d_pipe_BufferedOpCodes_BEGIN_SHAPE_CLIP:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: BEGIN_SHAPE_CLIP");
renderer.beginShapeClip();
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_SHAPE_CLIP_SPANS:
@@ -370,21 +409,18 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_SHAPE_CLIP_SPANS");
SKIP_BYTES(b, count * BYTES_PER_SPAN);
renderer.setShapeClipSpans();
}
break;
case sun_java2d_pipe_BufferedOpCodes_END_SHAPE_CLIP:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: END_SHAPE_CLIP");
renderer.endShapeClip();
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_CLIP:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_CLIP");
renderer.resetClip();
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_ALPHA_COMPOSITE:
@@ -394,7 +430,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint flags = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_ALPHA_COMPOSITE");
renderer.setAlphaComposite();
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_XOR_COMPOSITE:
@@ -402,14 +437,12 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint xorPixel = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_XOR_COMPOSITE");
renderer.setXorComposite();
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_COMPOSITE:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_COMPOSITE");
renderer.resetComposite();
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_TRANSFORM:
@@ -422,25 +455,40 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jdouble m12 = NEXT_DOUBLE(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_TRANSFORM");
renderer.setTransform(m00, m10, m01, m11, m02, m12);
}
break;
case sun_java2d_pipe_BufferedOpCodes_RESET_TRANSFORM:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_TRANSFORM");
renderer.resetTransform();
}
break;
// context-related ops
case sun_java2d_pipe_BufferedOpCodes_SET_SURFACES:
{
VKSurfaceData* src = NEXT_SURFACE(b);
VKSurfaceData* dst = NEXT_SURFACE(b);
VKSDOps* src = NEXT_SURFACE(b);
VKSDOps* dst = NEXT_SURFACE(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_SURFACES");
renderer.setSurfaces(*src, *dst);
dstOps = (VKSDOps *) jlong_to_ptr(dst);
if (dstOps != NULL && dstOps->drawableType == VKSD_WINDOW && dstOps->bgColorUpdated) {
VKWinSDOps *winDstOps = (VKWinSDOps *)dstOps;
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
ge->vkWaitForFences(logicalDevice->device, 1, &logicalDevice->inFlightFence, VK_TRUE, UINT64_MAX);
ge->vkResetFences(logicalDevice->device, 1, &logicalDevice->inFlightFence);
ge->vkResetCommandBuffer(logicalDevice->commandBuffer, 0);
VKRenderer_BeginRendering();
VKRenderer_ColorRenderMaxRect(winDstOps->vksdOps.image, winDstOps->vksdOps.bgColor);
VKRenderer_EndRendering(VK_FALSE, VK_FALSE);
}
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_SCRATCH_SURFACE:
@@ -448,15 +496,14 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jlong pConfigInfo = NEXT_LONG(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_SCRATCH_SURFACE");
renderer.setScratchSurface();
dstOps = NULL;
}
break;
case sun_java2d_pipe_BufferedOpCodes_FLUSH_SURFACE:
{
VKSurfaceData* surface = NEXT_SURFACE(b);
VKSDOps* surface = NEXT_SURFACE(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: FLUSH_SURFACE");
renderer.flushSurface(*surface);
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISPOSE_SURFACE:
@@ -464,29 +511,27 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jlong pData = NEXT_LONG(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: DISPOSE_SURFACE");
renderer.disposeSurface();
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISPOSE_CONFIG:
{
jlong pConfigInfo = NEXT_LONG(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: DISPOSE_CONFIG");
renderer.disposeConfig();
"VKRenderQueue_flushBuffer: DISPOSE_CONFIG")
dstOps = NULL;
}
break;
case sun_java2d_pipe_BufferedOpCodes_INVALIDATE_CONTEXT:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: INVALIDATE_CONTEXT");
renderer.invalidateContext();
dstOps = NULL;
}
break;
case sun_java2d_pipe_BufferedOpCodes_SYNC:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SYNC");
renderer.sync();
}
break;
@@ -496,7 +541,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jlong window = NEXT_LONG(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SWAP_BUFFERS");
renderer.swapBuffers();
}
break;
@@ -513,15 +557,14 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: RESET_PAINT");
renderer.resetPaint();
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_COLOR:
{
jint pixel = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_COLOR");
renderer.setColor((uint32_t) pixel);
color = pixel;
J2dRlsTraceLn1(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_COLOR %d", pixel);
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_GRADIENT_PAINT:
@@ -535,7 +578,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jint pixel2 = NEXT_INT(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_GRADIENT_PAINT");
renderer.setGradientPaint();
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_LINEAR_GRADIENT_PAINT:
@@ -552,7 +594,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
pixels = b; SKIP_BYTES(b, numStops * sizeof(jint));
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_LINEAR_GRADIENT_PAINT");
renderer.setLinearGradientPaint();
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_RADIAL_GRADIENT_PAINT:
@@ -573,7 +614,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
pixels = b; SKIP_BYTES(b, numStops * sizeof(jint));
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_RADIAL_GRADIENT_PAINT");
renderer.setRadialGradientPaint();
}
break;
case sun_java2d_pipe_BufferedOpCodes_SET_TEXTURE_PAINT:
@@ -589,7 +629,6 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
jdouble yp3 = NEXT_DOUBLE(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_TEXTURE_PAINT");
renderer.setTexturePaint();
}
break;
@@ -603,14 +642,12 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: ENABLE_CONVOLVE_OP");
SKIP_BYTES(b, kernelWidth * kernelHeight * sizeof(jfloat));
renderer.enableConvolveOp();
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISABLE_CONVOLVE_OP:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: DISABLE_CONVOLVE_OP");
renderer.disableConvolveOp();
}
break;
case sun_java2d_pipe_BufferedOpCodes_ENABLE_RESCALE_OP:
@@ -623,14 +660,12 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: ENABLE_RESCALE_OP");
SKIP_BYTES(b, numFactors * sizeof(jfloat) * 2);
renderer.enableRescaleOp();
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISABLE_RESCALE_OP:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: DISABLE_RESCALE_OP");
renderer.disableRescaleOp();
}
break;
case sun_java2d_pipe_BufferedOpCodes_ENABLE_LOOKUP_OP:
@@ -646,14 +681,12 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: ENABLE_LOOKUP_OP");
SKIP_BYTES(b, numBands * bandLength * bytesPerElem);
renderer.enableLookupOp();
}
break;
case sun_java2d_pipe_BufferedOpCodes_DISABLE_LOOKUP_OP:
{
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: DISABLE_LOOKUP_OP");
renderer.disableLookupOp();
}
break;
@@ -663,7 +696,46 @@ Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
return;
}
}
renderer.flush();
if (dstOps != NULL && dstOps->drawableType == VKSD_WINDOW) {
VKWinSDOps *winDstOps = (VKWinSDOps *)dstOps;
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
ge->vkWaitForFences(logicalDevice->device, 1, &logicalDevice->inFlightFence, VK_TRUE, UINT64_MAX);
ge->vkResetFences(logicalDevice->device, 1, &logicalDevice->inFlightFence);
uint32_t imageIndex;
ge->vkAcquireNextImageKHR(logicalDevice->device, winDstOps->swapchainKhr, UINT64_MAX,
logicalDevice->imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
ge->vkResetCommandBuffer(logicalDevice->commandBuffer, 0);
VKRenderer_BeginRendering();
VKRenderer_TextureRender(
&winDstOps->swapChainImages[imageIndex],
winDstOps->vksdOps.image,
logicalDevice->blitVertexBuffer->buffer, 4
);
VKRenderer_EndRendering(VK_TRUE, VK_TRUE);
VkSemaphore signalSemaphores[] = {logicalDevice->renderFinishedSemaphore};
VkSwapchainKHR swapChains[] = {winDstOps->swapchainKhr};
VkPresentInfoKHR presentInfo = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = signalSemaphores,
.swapchainCount = 1,
.pSwapchains = swapChains,
.pImageIndices = &imageIndex
};
ge->vkQueuePresentKHR(logicalDevice->queue, &presentInfo);
}
}
#endif /* !HEADLESS */

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -38,12 +38,12 @@
#define NEXT_BOOLEAN(buf) (jboolean)NEXT_INT(buf)
#define NEXT_LONG(buf) NEXT_VAL(buf, jlong)
#define NEXT_DOUBLE(buf) NEXT_VAL(buf, jdouble)
#define NEXT_SURFACE(buf) ((VKSurfaceData*) (SurfaceDataOps*) jlong_to_ptr(NEXT_LONG(buf)))
#define NEXT_SURFACE(buf) ((VKSDOps*) (SurfaceDataOps*) jlong_to_ptr(NEXT_LONG(buf)))
/*
* Increments a pointer (buf) by the given number of bytes.
*/
#define SKIP_BYTES(buf, numbytes) (buf) += (numbytes)
#define SKIP_BYTES(buf, numbytes) (buf) = ((unsigned char*)buf) + (numbytes)
/*
* Extracts a value at the given offset from the provided packed value.

View File

@@ -0,0 +1,855 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <jlong.h>
#include "Trace.h"
#include "VKVertex.h"
#include "VKRenderer.h"
#define INCLUDE_BYTECODE
#define SHADER_ENTRY(NAME, TYPE) static uint32_t NAME ## _ ## TYPE ## _data[] = {
#define BYTECODE_END };
#include "vulkan/shader_list.h"
#undef INCLUDE_BYTECODE
#undef SHADER_ENTRY
#undef BYTECODE_END
VkRenderPassCreateInfo* VKRenderer_GetGenericRenderPassInfo() {
static VkAttachmentDescription colorAttachment = {
.format = VK_FORMAT_B8G8R8A8_UNORM, //TODO: swapChain colorFormat
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
};
static VkAttachmentReference colorReference = {
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
static VkSubpassDescription subpassDescription = {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.colorAttachmentCount = 1,
.pColorAttachments = &colorReference
};
// Subpass dependencies for layout transitions
static 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
};
static VkRenderPassCreateInfo renderPassInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &colorAttachment,
.subpassCount = 1,
.pSubpasses = &subpassDescription,
.dependencyCount = 1,
.pDependencies = &dependency
};
return &renderPassInfo;
}
VkShaderModule createShaderModule(VkDevice device, uint32_t* shader, uint32_t sz) {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VkShaderModuleCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = sz;
createInfo.pCode = (uint32_t*)shader;
VkShaderModule shaderModule;
if (ge->vkCreateShaderModule(device, &createInfo, NULL, &shaderModule) != VK_SUCCESS) {
J2dRlsTrace(J2D_TRACE_ERROR, "failed to create shader module\n")
return VK_NULL_HANDLE;
}
return shaderModule;
}
VKRenderer* VKRenderer_CreateFillTexturePoly() {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VKRenderer* fillTexturePoly = malloc(sizeof (VKRenderer ));
VkDevice device = logicalDevice->device;
if (ge->vkCreateRenderPass(logicalDevice->device, VKRenderer_GetGenericRenderPassInfo(),
NULL, &fillTexturePoly->renderPass) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_INFO, "Cannot create render pass for device")
return JNI_FALSE;
}
// Create graphics pipeline
VkShaderModule vertShaderModule = createShaderModule(device, blit_vert_data, sizeof (blit_vert_data));
VkShaderModule fragShaderModule = createShaderModule(device, blit_frag_data, sizeof (blit_frag_data));
VkPipelineShaderStageCreateInfo vertShaderStageInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vertShaderModule,
.pName = "main"
};
VkPipelineShaderStageCreateInfo fragShaderStageInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = fragShaderModule,
.pName = "main"
};
VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
VKVertexDescr vertexDescr = VKVertex_GetTxVertexDescr();
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = vertexDescr.bindingDescriptionCount,
.vertexAttributeDescriptionCount = vertexDescr.attributeDescriptionCount,
.pVertexBindingDescriptions = vertexDescr.bindingDescriptions,
.pVertexAttributeDescriptions = vertexDescr.attributeDescriptions
};
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
.primitiveRestartEnable = VK_FALSE
};
VkPipelineViewportStateCreateInfo viewportState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1
};
VkPipelineRasterizationStateCreateInfo rasterizer = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.lineWidth = 1.0f,
.cullMode = VK_CULL_MODE_NONE,
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.0f,
.depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f
};
VkPipelineMultisampleStateCreateInfo multisampling = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.sampleShadingEnable = VK_FALSE,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
};
VkPipelineColorBlendAttachmentState colorBlendAttachment = {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT,
.blendEnable = VK_FALSE
};
VkPipelineColorBlendStateCreateInfo colorBlending = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = 1,
.pAttachments = &colorBlendAttachment,
.blendConstants[0] = 0.0f,
.blendConstants[1] = 0.0f,
.blendConstants[2] = 0.0f,
.blendConstants[3] = 0.0f
};
VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = 2,
.pDynamicStates = dynamicStates
};
VkDescriptorSetLayoutBinding samplerLayoutBinding = {
.binding = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImmutableSamplers = NULL,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
};
VkDescriptorSetLayoutCreateInfo layoutInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &samplerLayoutBinding
};
if (ge->vkCreateDescriptorSetLayout(device, &layoutInfo, NULL, &fillTexturePoly->descriptorSetLayout) != VK_SUCCESS) {
J2dRlsTrace(J2D_TRACE_INFO, "failed to create descriptor set layout!");
return JNI_FALSE;
}
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = &fillTexturePoly->descriptorSetLayout,
.pushConstantRangeCount = 0
};
if (ge->vkCreatePipelineLayout(device, &pipelineLayoutInfo, NULL,
&fillTexturePoly->pipelineLayout) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_INFO, "failed to create pipeline layout!\n")
return JNI_FALSE;
}
VkGraphicsPipelineCreateInfo pipelineInfo = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2,
.pStages = shaderStages,
.pVertexInputState = &vertexInputInfo,
.pInputAssemblyState = &inputAssembly,
.pViewportState = &viewportState,
.pRasterizationState = &rasterizer,
.pMultisampleState = &multisampling,
.pColorBlendState = &colorBlending,
.pDynamicState = &dynamicState,
.layout = fillTexturePoly->pipelineLayout,
.renderPass = fillTexturePoly->renderPass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = -1
};
if (ge->vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL,
&fillTexturePoly->graphicsPipeline) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_INFO, "failed to create graphics pipeline!\n")
return JNI_FALSE;
}
ge->vkDestroyShaderModule(device, fragShaderModule, NULL);
ge->vkDestroyShaderModule(device, vertShaderModule, NULL);
VkSamplerCreateInfo samplerInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.anisotropyEnable = VK_FALSE,
.maxAnisotropy = 1.0f,
.compareEnable = VK_FALSE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.mipLodBias = 0.0f,
.minLod = 0.0f,
.maxLod = 0.0f
};
if (ge->vkCreateSampler(device, &samplerInfo, NULL, &logicalDevice->textureSampler) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_INFO, "failed to create texture sampler!");
return JNI_FALSE;
}
VkDescriptorPoolSize poolSize = {
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1
};
VkDescriptorPoolCreateInfo descrPoolInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.poolSizeCount = 1,
.pPoolSizes = &poolSize,
.maxSets = 1
};
if (ge->vkCreateDescriptorPool(device, &descrPoolInfo, NULL, &fillTexturePoly->descriptorPool) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_INFO, "failed to create descriptor pool!")
return JNI_FALSE;
}
VkDescriptorSetAllocateInfo descrAllocInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = fillTexturePoly->descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &fillTexturePoly->descriptorSetLayout
};
if (ge->vkAllocateDescriptorSets(device, &descrAllocInfo, &fillTexturePoly->descriptorSets) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "failed to allocate descriptor sets!");
return JNI_FALSE;
}
return fillTexturePoly;
}
VKRenderer* VKRenderer_CreateFillColorPoly() {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VKRenderer* fillColorPoly = malloc(sizeof (VKRenderer ));
VkDevice device = logicalDevice->device;
if (ge->vkCreateRenderPass(logicalDevice->device, VKRenderer_GetGenericRenderPassInfo(),
NULL, &fillColorPoly->renderPass) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_INFO, "Cannot create render pass for device")
return JNI_FALSE;
}
// Create graphics pipeline
VkShaderModule vertShaderModule = createShaderModule(device, color_vert_data, sizeof (color_vert_data));
VkShaderModule fragShaderModule = createShaderModule(device, color_frag_data, sizeof (color_frag_data));
VkPipelineShaderStageCreateInfo vertShaderStageInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vertShaderModule,
.pName = "main"
};
VkPipelineShaderStageCreateInfo fragShaderStageInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = fragShaderModule,
.pName = "main"
};
VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
VKVertexDescr vertexDescr = VKVertex_GetCVertexDescr();
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = vertexDescr.bindingDescriptionCount,
.vertexAttributeDescriptionCount = vertexDescr.attributeDescriptionCount,
.pVertexBindingDescriptions = vertexDescr.bindingDescriptions,
.pVertexAttributeDescriptions = vertexDescr.attributeDescriptions
};
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
.primitiveRestartEnable = VK_FALSE
};
VkPipelineViewportStateCreateInfo viewportState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1
};
VkPipelineRasterizationStateCreateInfo rasterizer = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.lineWidth = 1.0f,
.cullMode = VK_CULL_MODE_NONE,
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.0f,
.depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f
};
VkPipelineMultisampleStateCreateInfo multisampling = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.sampleShadingEnable = VK_FALSE,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
};
VkPipelineColorBlendAttachmentState colorBlendAttachment = {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT,
.blendEnable = VK_FALSE
};
VkPipelineColorBlendStateCreateInfo colorBlending = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = 1,
.pAttachments = &colorBlendAttachment,
.blendConstants[0] = 0.0f,
.blendConstants[1] = 0.0f,
.blendConstants[2] = 0.0f,
.blendConstants[3] = 0.0f
};
VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = 2,
.pDynamicStates = dynamicStates
};
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0,
.pushConstantRangeCount = 0
};
if (ge->vkCreatePipelineLayout(device, &pipelineLayoutInfo, NULL,
&fillColorPoly->pipelineLayout) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_INFO, "failed to create pipeline layout!\n")
return JNI_FALSE;
}
VkGraphicsPipelineCreateInfo pipelineInfo = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2,
.pStages = shaderStages,
.pVertexInputState = &vertexInputInfo,
.pInputAssemblyState = &inputAssembly,
.pViewportState = &viewportState,
.pRasterizationState = &rasterizer,
.pMultisampleState = &multisampling,
.pColorBlendState = &colorBlending,
.pDynamicState = &dynamicState,
.layout = fillColorPoly->pipelineLayout,
.renderPass = fillColorPoly->renderPass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = -1
};
if (ge->vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL,
&fillColorPoly->graphicsPipeline) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_INFO, "failed to create graphics pipeline!\n")
return JNI_FALSE;
}
ge->vkDestroyShaderModule(device, fragShaderModule, NULL);
ge->vkDestroyShaderModule(device, vertShaderModule, NULL);
return fillColorPoly;
}
VKRenderer* VKRenderer_CreateFillMaxColorPoly() {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VKRenderer* fillColorPoly = malloc(sizeof (VKRenderer ));
VkDevice device = logicalDevice->device;
if (ge->vkCreateRenderPass(logicalDevice->device, VKRenderer_GetGenericRenderPassInfo(),
NULL, &fillColorPoly->renderPass) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_INFO, "Cannot create render pass for device")
return JNI_FALSE;
}
// Create graphics pipeline
VkShaderModule vertShaderModule = createShaderModule(device, color_max_rect_vert_data, sizeof (color_max_rect_vert_data));
VkShaderModule fragShaderModule = createShaderModule(device, color_max_rect_frag_data, sizeof (color_max_rect_frag_data));
VkPipelineShaderStageCreateInfo vertShaderStageInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = vertShaderModule,
.pName = "main"
};
VkPipelineShaderStageCreateInfo fragShaderStageInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = fragShaderModule,
.pName = "main"
};
VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 0,
.vertexAttributeDescriptionCount = 0,
};
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
.primitiveRestartEnable = VK_FALSE
};
VkPipelineViewportStateCreateInfo viewportState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1
};
VkPipelineRasterizationStateCreateInfo rasterizer = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = VK_POLYGON_MODE_FILL,
.lineWidth = 1.0f,
.cullMode = VK_CULL_MODE_NONE,
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.0f,
.depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f
};
VkPipelineMultisampleStateCreateInfo multisampling = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.sampleShadingEnable = VK_FALSE,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
};
VkPipelineColorBlendAttachmentState colorBlendAttachment = {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT,
.blendEnable = VK_FALSE
};
VkPipelineColorBlendStateCreateInfo colorBlending = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = 1,
.pAttachments = &colorBlendAttachment,
.blendConstants[0] = 0.0f,
.blendConstants[1] = 0.0f,
.blendConstants[2] = 0.0f,
.blendConstants[3] = 0.0f
};
VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = 2,
.pDynamicStates = dynamicStates
};
VkPushConstantRange pushConstantRange = {
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0,
.size = sizeof(float) * 4
};
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pushConstantRange
};
if (ge->vkCreatePipelineLayout(device, &pipelineLayoutInfo, NULL,
&fillColorPoly->pipelineLayout) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_INFO, "failed to create pipeline layout!\n")
return JNI_FALSE;
}
VkGraphicsPipelineCreateInfo pipelineInfo = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2,
.pStages = shaderStages,
.pVertexInputState = &vertexInputInfo,
.pInputAssemblyState = &inputAssembly,
.pViewportState = &viewportState,
.pRasterizationState = &rasterizer,
.pMultisampleState = &multisampling,
.pColorBlendState = &colorBlending,
.pDynamicState = &dynamicState,
.layout = fillColorPoly->pipelineLayout,
.renderPass = fillColorPoly->renderPass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = -1
};
if (ge->vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL,
&fillColorPoly->graphicsPipeline) != VK_SUCCESS)
{
J2dRlsTrace(J2D_TRACE_INFO, "failed to create graphics pipeline!\n")
return JNI_FALSE;
}
ge->vkDestroyShaderModule(device, fragShaderModule, NULL);
ge->vkDestroyShaderModule(device, vertShaderModule, NULL);
return fillColorPoly;
}
void VKRenderer_BeginRendering() {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
if (ge->vkBeginCommandBuffer(logicalDevice->commandBuffer, &beginInfo) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "failed to begin recording command buffer!");
return;
}
}
void VKRenderer_EndRendering(VkBool32 notifyRenderFinish, VkBool32 waitForDisplayImage) {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
if (ge->vkEndCommandBuffer(logicalDevice->commandBuffer) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "failed to record command buffer!")
return;
}
VkSemaphore waitSemaphores[] = {logicalDevice->imageAvailableSemaphore};
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
VkSemaphore signalSemaphores[] = {logicalDevice->renderFinishedSemaphore};
VkSubmitInfo submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = (waitForDisplayImage ? 1 : 0),
.pWaitSemaphores = waitSemaphores,
.pWaitDstStageMask = waitStages,
.commandBufferCount = 1,
.pCommandBuffers = &logicalDevice->commandBuffer,
.signalSemaphoreCount = (notifyRenderFinish ? 1 : 0),
.pSignalSemaphores = signalSemaphores
};
if (ge->vkQueueSubmit(logicalDevice->queue, 1, &submitInfo, logicalDevice->inFlightFence) != VK_SUCCESS) {
J2dRlsTraceLn(J2D_TRACE_ERROR,"failed to submit draw command buffer!")
return;
}
}
void VKRenderer_TextureRender(VKImage *destImage, VKImage *srcImage, VkBuffer vertexBuffer, uint32_t vertexNum)
{
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VkDescriptorImageInfo imageInfo = {
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = srcImage->view,
.sampler = logicalDevice->textureSampler
};
VkWriteDescriptorSet descriptorWrites = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = logicalDevice->fillTexturePoly->descriptorSets,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.pImageInfo = &imageInfo
};
ge->vkUpdateDescriptorSets(logicalDevice->device, 1, &descriptorWrites, 0, NULL);
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
VkRenderPassBeginInfo renderPassInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = logicalDevice->fillTexturePoly->renderPass,
.framebuffer = destImage->framebuffer,
.renderArea.offset = (VkOffset2D){0, 0},
.renderArea.extent = destImage->extent,
.clearValueCount = 1,
.pClearValues = &clearColor
};
ge->vkCmdBeginRenderPass(logicalDevice->commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
ge->vkCmdBindPipeline(logicalDevice->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
logicalDevice->fillTexturePoly->graphicsPipeline);
VkBuffer vertexBuffers[] = {vertexBuffer};
VkDeviceSize offsets[] = {0};
ge->vkCmdBindVertexBuffers(logicalDevice->commandBuffer, 0, 1, vertexBuffers, offsets);
VkViewport viewport = {
.x = 0.0f,
.y = 0.0f,
.width = destImage->extent.width,
.height = destImage->extent.height,
.minDepth = 0.0f,
.maxDepth = 1.0f
};
ge->vkCmdSetViewport(logicalDevice->commandBuffer, 0, 1, &viewport);
VkRect2D scissor = {
.offset = (VkOffset2D){0, 0},
.extent = destImage->extent,
};
ge->vkCmdSetScissor(logicalDevice->commandBuffer, 0, 1, &scissor);
ge->vkCmdBindDescriptorSets(logicalDevice->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
logicalDevice->fillTexturePoly->pipelineLayout, 0, 1, &logicalDevice->fillTexturePoly->descriptorSets, 0, NULL);
ge->vkCmdDraw(logicalDevice->commandBuffer, vertexNum, 1, 0, 0);
ge->vkCmdEndRenderPass(logicalDevice->commandBuffer);
}
void VKRenderer_ColorRender(VKImage *destImage, VkBuffer vertexBuffer, uint32_t vertexNum)
{
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
VkRenderPassBeginInfo renderPassInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = logicalDevice->fillColorPoly->renderPass,
.framebuffer = destImage->framebuffer,
.renderArea.offset = (VkOffset2D){0, 0},
.renderArea.extent = destImage->extent,
.clearValueCount = 1,
.pClearValues = &clearColor
};
ge->vkCmdBeginRenderPass(logicalDevice->commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
ge->vkCmdBindPipeline(logicalDevice->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
logicalDevice->fillColorPoly->graphicsPipeline);
VkBuffer vertexBuffers[] = {vertexBuffer};
VkDeviceSize offsets[] = {0};
ge->vkCmdBindVertexBuffers(logicalDevice->commandBuffer, 0, 1, vertexBuffers, offsets);
VkViewport viewport = {
.x = 0.0f,
.y = 0.0f,
.width = destImage->extent.width,
.height = destImage->extent.height,
.minDepth = 0.0f,
.maxDepth = 1.0f
};
ge->vkCmdSetViewport(logicalDevice->commandBuffer, 0, 1, &viewport);
VkRect2D scissor = {
.offset = (VkOffset2D){0, 0},
.extent = destImage->extent,
};
ge->vkCmdSetScissor(logicalDevice->commandBuffer, 0, 1, &scissor);
ge->vkCmdDraw(logicalDevice->commandBuffer, vertexNum, 1, 0, 0);
ge->vkCmdEndRenderPass(logicalDevice->commandBuffer);
}
void VKRenderer_ColorRenderMaxRect(VKImage *destImage, uint32_t rgba) {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
VkRenderPassBeginInfo renderPassInfo = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = logicalDevice->fillMaxColorPoly->renderPass,
.framebuffer = destImage->framebuffer,
.renderArea.offset = (VkOffset2D){0, 0},
.renderArea.extent = destImage->extent,
.clearValueCount = 1,
.pClearValues = &clearColor
};
ge->vkCmdBeginRenderPass(logicalDevice->commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
ge->vkCmdBindPipeline(logicalDevice->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
logicalDevice->fillMaxColorPoly->graphicsPipeline);
struct PushConstants {
float r, g, b, a;
} pushConstants;
pushConstants = (struct PushConstants){RGBA_TO_L4(rgba)};
ge->vkCmdPushConstants(
logicalDevice->commandBuffer,
logicalDevice->fillMaxColorPoly->pipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT,
0,
sizeof(struct PushConstants),
&pushConstants
);
VkViewport viewport = {
.x = 0.0f,
.y = 0.0f,
.width = destImage->extent.width,
.height = destImage->extent.height,
.minDepth = 0.0f,
.maxDepth = 1.0f
};
ge->vkCmdSetViewport(logicalDevice->commandBuffer, 0, 1, &viewport);
VkRect2D scissor = {
.offset = (VkOffset2D){0, 0},
.extent = destImage->extent,
};
ge->vkCmdSetScissor(logicalDevice->commandBuffer, 0, 1, &scissor);
ge->vkCmdDraw(logicalDevice->commandBuffer, 4, 1, 0, 0);
ge->vkCmdEndRenderPass(logicalDevice->commandBuffer);
}
void
VKRenderer_FillRect(jint x, jint y, jint w, jint h)
{
J2dTraceLn4(J2D_TRACE_INFO, "VKRenderer_FillRect %d %d %d %d", x, y, w, h);
if (w <= 0 || h <= 0) {
return;
}
}
jboolean VK_CreateLogicalDeviceRenderers() {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
logicalDevice->fillTexturePoly = VKRenderer_CreateFillTexturePoly();
logicalDevice->fillColorPoly = VKRenderer_CreateFillColorPoly();
logicalDevice->fillMaxColorPoly = VKRenderer_CreateFillMaxColorPoly();
if (!logicalDevice->fillTexturePoly || !logicalDevice->fillColorPoly || !logicalDevice->fillMaxColorPoly) {
return JNI_FALSE;
}
return JNI_TRUE;
}
#endif /* !HEADLESS */

View File

@@ -1,282 +0,0 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "VKRenderer.h"
VKRecorder::Vertex* VKRecorder::draw(uint32_t numVertices) {
uint32_t bytes = numVertices * sizeof(VKRecorder::Vertex);
if (_renderPass.vertexBuffer == nullptr && !_vertexBuffers.empty()) {
_renderPass.vertexBuffer = &_vertexBuffers.back();
_renderPass.commandBuffer->bindVertexBuffers(0, **_renderPass.vertexBuffer, vk::DeviceSize(0));
}
if (_renderPass.vertexBuffer == nullptr ||
_renderPass.vertexBuffer->position() + bytes > _renderPass.vertexBuffer->size()) {
_vertexBuffers.push_back(device().getVertexBuffer());
_renderPass.vertexBuffer = &_vertexBuffers.back(); // TODO check that our number of vertices fit into single buffer at all
_renderPass.commandBuffer->bindVertexBuffers(0, **_renderPass.vertexBuffer, vk::DeviceSize(0));
}
auto data = (uintptr_t) _renderPass.vertexBuffer->data() + _renderPass.vertexBuffer->position();
uint32_t firstVertex = _renderPass.vertexBuffer->position() / sizeof(VKRecorder::Vertex);
_renderPass.vertexBuffer->position() += bytes;
_renderPass.commandBuffer->draw(numVertices, 1, firstVertex, 0);
return (VKRecorder::Vertex*) data;
}
VKDevice* VKRecorder::setDevice(VKDevice *device) {
if (device != _device) {
if (_device != nullptr) {
flush();
}
std::swap(_device, device);
}
return device;
}
void VKRecorder::waitSemaphore(vk::Semaphore semaphore, vk::PipelineStageFlags stage) {
_waitSemaphores.push_back(semaphore);
_waitSemaphoreStages.push_back(stage);
}
void VKRecorder::signalSemaphore(vk::Semaphore semaphore) {
_signalSemaphores.push_back(semaphore);
}
const vk::raii::CommandBuffer& VKRecorder::record(bool flushRenderPass) {
if (!*_commandBuffer) {
_commandBuffer = device().getCommandBuffer(vk::CommandBufferLevel::ePrimary);
_commandBuffer.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit });
}
if (flushRenderPass && _renderPass.commandBuffer != nullptr) {
_renderPass.commandBuffer->end();
vk::Rect2D renderArea {{0, 0}, {_renderPass.surface->width(), _renderPass.surface->height()}};
if (device().dynamicRendering()) {
vk::RenderingAttachmentInfoKHR colorAttachmentInfo {
_renderPass.surfaceView, vk::ImageLayout::eColorAttachmentOptimal,
vk::ResolveModeFlagBits::eNone, {}, {},
_renderPass.attachmentLoadOp, vk::AttachmentStoreOp::eStore,
_renderPass.clearValue
};
_commandBuffer.beginRenderingKHR(vk::RenderingInfoKHR{
vk::RenderingFlagBitsKHR::eContentsSecondaryCommandBuffers,
renderArea, 1, 0, colorAttachmentInfo, {}, {}
});
} else {
_commandBuffer.beginRenderPass(vk::RenderPassBeginInfo{
/*renderPass*/ *device().pipelines().renderPass,
/*framebuffer*/ _renderPass.surfaceFramebuffer,
/*renderArea*/ renderArea,
/*clearValueCount*/ 0,
/*pClearValues*/ nullptr
}, vk::SubpassContents::eSecondaryCommandBuffers);
}
_commandBuffer.executeCommands(**_renderPass.commandBuffer);
if (device().dynamicRendering()) {
_commandBuffer.endRenderingKHR();
} else {
_commandBuffer.endRenderPass();
}
_renderPass = {};
}
return _commandBuffer;
}
const vk::raii::CommandBuffer& VKRecorder::render(VKSurfaceData& surface, vk::ClearColorValue* clear) {
if (_renderPass.surface != &surface) {
if (_renderPass.commandBuffer != nullptr) {
record(true); // Flush current render pass
}
VKSurfaceImage i = surface.access(*this,
vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::AccessFlagBits::eColorAttachmentWrite,
vk::ImageLayout::eColorAttachmentOptimal);
_renderPass.surface = &surface;
_renderPass.surfaceView = i.view;
_renderPass.surfaceFramebuffer = i.framebuffer;
_renderPass.attachmentLoadOp = vk::AttachmentLoadOp::eLoad;
}
if (clear != nullptr) {
_renderPass.clearValue = *clear;
_renderPass.attachmentLoadOp = vk::AttachmentLoadOp::eClear;
}
if (_renderPass.commandBuffer == nullptr || clear != nullptr) {
if (_renderPass.commandBuffer == nullptr) {
_secondaryBuffers.push_back(device().getCommandBuffer(vk::CommandBufferLevel::eSecondary));
_renderPass.commandBuffer = &_secondaryBuffers.back();
} else {
// We already recorded some rendering commands, but it doesn't matter, as we'll clear the surface anyway.
_renderPass.commandBuffer->reset({});
}
vk::Format format = surface.format();
vk::CommandBufferInheritanceRenderingInfoKHR inheritanceRenderingInfo {
vk::RenderingFlagBitsKHR::eContentsSecondaryCommandBuffers,
0, format
};
vk::CommandBufferInheritanceInfo inheritanceInfo;
if (device().dynamicRendering()) {
inheritanceInfo.pNext = &inheritanceRenderingInfo;
} else {
inheritanceInfo.renderPass = *device().pipelines().renderPass;
inheritanceInfo.subpass = 0;
inheritanceInfo.framebuffer = _renderPass.surfaceFramebuffer;
}
_renderPass.commandBuffer->begin({ vk::CommandBufferUsageFlagBits::eOneTimeSubmit |
vk::CommandBufferUsageFlagBits::eRenderPassContinue, &inheritanceInfo });
if (clear != nullptr && !device().dynamicRendering()) {
// Our static render pass uses loadOp=LOAD, so clear attachment manually.
_renderPass.commandBuffer->clearAttachments(vk::ClearAttachment {
vk::ImageAspectFlagBits::eColor, 0, _renderPass.clearValue
}, vk::ClearRect {vk::Rect2D{{0, 0}, {_renderPass.surface->width(), _renderPass.surface->height()}}, 0, 1});
}
}
return *_renderPass.commandBuffer;
}
void VKRecorder::flush() {
if (!*_commandBuffer && _renderPass.commandBuffer == nullptr) {
return;
}
record(true).end();
device().submitCommandBuffer(std::move(_commandBuffer), _secondaryBuffers, _vertexBuffers,
_waitSemaphores, _waitSemaphoreStages, _signalSemaphores);
}
// draw ops
void VKRenderer::drawLine(jint x1, jint y1, jint x2, jint y2) {/*TODO*/}
void VKRenderer::drawRect(jint x, jint y, jint w, jint h) {/*TODO*/}
void VKRenderer::drawPoly(/*TODO*/) {/*TODO*/}
void VKRenderer::drawPixel(jint x, jint y) {/*TODO*/}
void VKRenderer::drawScanlines(/*TODO*/) {/*TODO*/}
void VKRenderer::drawParallelogram(jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lwr21, jfloat lwr12) {/*TODO*/}
void VKRenderer::drawAAParallelogram(jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lwr21, jfloat lwr12) {/*TODO*/}
// fill ops
void VKRenderer::fillRect(jint xi, jint yi, jint wi, jint hi) {
// TODO
auto& cb = render(*_dstSurface);
// cb.clearAttachments(vk::ClearAttachment {vk::ImageAspectFlagBits::eColor, 0, _color},
// vk::ClearRect {vk::Rect2D {{x, y}, {(uint32_t) w, (uint32_t) h}}, 0, 1});
cb.bindPipeline(vk::PipelineBindPoint::eGraphics, *device().pipelines().test);
cb.pushConstants<float>(*device().pipelines().testLayout, vk::ShaderStageFlagBits::eVertex, 0, {
2.0f/(float)_dstSurface->width(), 2.0f/(float)_dstSurface->height()
});
vk::Viewport viewport {0, 0, (float) _dstSurface->width(), (float) _dstSurface->height(), 0, 1};
cb.setViewport(0, viewport);
vk::Rect2D scissor {{0, 0}, {_dstSurface->width(), _dstSurface->height()}};
cb.setScissor(0, scissor);
auto x = (float) xi, y = (float) yi, w = (float) wi, h = (float) hi;
auto v = draw(4);
v[0] = {x, y};
v[1] = {x+w, y};
v[2] = {x+w, y+h};
v[3] = {x, y+h};
}
void VKRenderer::fillSpans(/*TODO*/) {/*TODO*/}
void VKRenderer::fillParallelogram(jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12) {/*TODO*/}
void VKRenderer::fillAAParallelogram(jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12) {/*TODO*/}
// text-related ops
void VKRenderer::drawGlyphList(/*TODO*/) {/*TODO*/}
// copy-related ops
void VKRenderer::copyArea(jint x, jint y, jint w, jint h, jint dx, jint dy) {/*TODO*/}
void VKRenderer::blit(/*TODO*/) {/*TODO*/}
void VKRenderer::surfaceToSwBlit(/*TODO*/) {/*TODO*/}
void VKRenderer::maskFill(/*TODO*/) {/*TODO*/}
void VKRenderer::maskBlit(/*TODO*/) {/*TODO*/}
// state-related ops
void VKRenderer::setRectClip(jint x1, jint y1, jint x2, jint y2) {/*TODO*/}
void VKRenderer::beginShapeClip() {/*TODO*/}
void VKRenderer::setShapeClipSpans(/*TODO*/) {/*TODO*/}
void VKRenderer::endShapeClip() {/*TODO*/}
void VKRenderer::resetClip() {/*TODO*/}
void VKRenderer::setAlphaComposite(/*TODO*/) {/*TODO*/}
void VKRenderer::setXorComposite(/*TODO*/) {/*TODO*/}
void VKRenderer::resetComposite() {/*TODO*/}
void VKRenderer::setTransform(jdouble m00, jdouble m10,
jdouble m01, jdouble m11,
jdouble m02, jdouble m12) {/*TODO*/}
void VKRenderer::resetTransform() {/*TODO*/}
// context-related ops
void VKRenderer::setSurfaces(VKSurfaceData& src, VKSurfaceData& dst) {
if (&src.device() != &dst.device()) {
throw std::runtime_error("src and dst surfaces use different devices!");
}
setDevice(&dst.device());
_dstSurface = &dst;
_srcSurface = &src;
}
void VKRenderer::setScratchSurface(/*TODO*/) {/*TODO*/}
void VKRenderer::flushSurface(VKSurfaceData& surface) {
VKDevice* old = setDevice(&surface.device());
surface.flush(*this);
setDevice(old);
}
void VKRenderer::disposeSurface(/*TODO*/) {/*TODO*/}
void VKRenderer::disposeConfig(/*TODO*/) {/*TODO*/}
void VKRenderer::invalidateContext() {/*TODO*/}
void VKRenderer::sync() {/*TODO*/}
// multibuffering ops
void VKRenderer::swapBuffers(/*TODO*/) {/*TODO*/}
// paint-related ops
void VKRenderer::resetPaint() {/*TODO*/}
void VKRenderer::setColor(uint32_t pixel) {
_color = pixel;
}
void VKRenderer::setGradientPaint(/*TODO*/) {/*TODO*/}
void VKRenderer::setLinearGradientPaint(/*TODO*/) {/*TODO*/}
void VKRenderer::setRadialGradientPaint(/*TODO*/) {/*TODO*/}
void VKRenderer::setTexturePaint(/*TODO*/) {/*TODO*/}
// BufferedImageOp-related ops
void VKRenderer::enableConvolveOp(/*TODO*/) {/*TODO*/}
void VKRenderer::disableConvolveOp() {/*TODO*/}
void VKRenderer::enableRescaleOp(/*TODO*/) {/*TODO*/}
void VKRenderer::disableRescaleOp() {/*TODO*/}
void VKRenderer::enableLookupOp() {/*TODO*/}
void VKRenderer::disableLookupOp() {/*TODO*/}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,149 +26,23 @@
#ifndef VKRenderer_h_Included
#define VKRenderer_h_Included
#ifdef __cplusplus
#include "j2d_md.h"
#include "VKBase.h"
#include "VKSurfaceData.h"
class VKRecorder{
VKDevice *_device;
vk::raii::CommandBuffer _commandBuffer = nullptr;
std::vector<vk::raii::CommandBuffer> _secondaryBuffers;
std::vector<vk::Semaphore> _waitSemaphores, _signalSemaphores;
std::vector<vk::PipelineStageFlags> _waitSemaphoreStages;
std::vector<VKBuffer> _vertexBuffers;
struct RenderPass {
vk::raii::CommandBuffer *commandBuffer = nullptr;
VKSurfaceData *surface = nullptr;
VKBuffer *vertexBuffer = nullptr;
vk::ImageView surfaceView;
vk::Framebuffer surfaceFramebuffer; // Only if dynamic rendering is off.
vk::AttachmentLoadOp attachmentLoadOp;
vk::ClearValue clearValue;
} _renderPass {};
VKRenderer* VKRenderer_CreateFillTexturePoly();
protected:
struct Vertex {
float x, y;
};
VKRenderer* VKRenderer_CreateFillColorPoly();
Vertex* draw(uint32_t numVertices);
VKRenderer* VKRenderer_CreateFillMaxColorPoly();
VKDevice& device() {
return *_device;
}
VKDevice* setDevice(VKDevice *device);
void VKRenderer_BeginRendering();
void VKRenderer_EndRendering(VkBool32 notifyRenderFinish, VkBool32 waitForDisplayImage);
void VKRenderer_TextureRender(VKImage *destImage, VKImage *srcImage, VkBuffer vertexBuffer, uint32_t vertexNum);
void VKRenderer_ColorRender(VKImage *destImage, VkBuffer vertexBuffer, uint32_t vertexNum);
void VKRenderer_ColorRenderMaxRect(VKImage *destImage, uint32_t rgba);
// fill ops
void VKRenderer_FillRect(jint x, jint y, jint w, jint h);
public:
void waitSemaphore(vk::Semaphore semaphore, vk::PipelineStageFlags stage);
void signalSemaphore(vk::Semaphore semaphore);
const vk::raii::CommandBuffer& record(bool flushRenderPass = true); // Prepare for ordinary commands
const vk::raii::CommandBuffer& render(VKSurfaceData& surface,
vk::ClearColorValue* clear = nullptr); // Prepare for render pass commands
void flush();
};
class VKRenderer : private VKRecorder{
VKSurfaceData *_srcSurface, *_dstSurface;
struct alignas(16) Color {
float r, g, b, a;
Color& operator=(uint32_t c) {
r = (float) ((c >> 16) & 0xff) / 255.0f;
g = (float) ((c >> 8) & 0xff) / 255.0f;
b = (float) (c & 0xff) / 255.0f;
a = (float) ((c >> 24) & 0xff) / 255.0f;
return *this;
}
operator vk::ClearValue() const {
return vk::ClearColorValue {r, g, b, a};
}
} _color;
public:
using VKRecorder::flush;
// draw ops
void drawLine(jint x1, jint y1, jint x2, jint y2);
void drawRect(jint x, jint y, jint w, jint h);
void drawPoly(/*TODO*/);
void drawPixel(jint x, jint y);
void drawScanlines(/*TODO*/);
void drawParallelogram(jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lwr21, jfloat lwr12);
void drawAAParallelogram(jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lwr21, jfloat lwr12);
// fill ops
void fillRect(jint x, jint y, jint w, jint h);
void fillSpans(/*TODO*/);
void fillParallelogram(jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12);
void fillAAParallelogram(jfloat x11, jfloat y11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12);
// text-related ops
void drawGlyphList(/*TODO*/);
// copy-related ops
void copyArea(jint x, jint y, jint w, jint h, jint dx, jint dy);
void blit(/*TODO*/);
void surfaceToSwBlit(/*TODO*/);
void maskFill(/*TODO*/);
void maskBlit(/*TODO*/);
// state-related ops
void setRectClip(jint x1, jint y1, jint x2, jint y2);
void beginShapeClip();
void setShapeClipSpans(/*TODO*/);
void endShapeClip();
void resetClip();
void setAlphaComposite(/*TODO*/);
void setXorComposite(/*TODO*/);
void resetComposite();
void setTransform(jdouble m00, jdouble m10,
jdouble m01, jdouble m11,
jdouble m02, jdouble m12);
void resetTransform();
// context-related ops
void setSurfaces(VKSurfaceData& src, VKSurfaceData& dst);
void setScratchSurface(/*TODO*/);
void flushSurface(VKSurfaceData& surface);
void disposeSurface(/*TODO*/);
void disposeConfig(/*TODO*/);
void invalidateContext();
void sync();
// multibuffering ops
void swapBuffers(/*TODO*/);
// paint-related ops
void resetPaint();
void setColor(uint32_t pixel);
void setGradientPaint(/*TODO*/);
void setLinearGradientPaint(/*TODO*/);
void setRadialGradientPaint(/*TODO*/);
void setTexturePaint(/*TODO*/);
// BufferedImageOp-related ops
void enableConvolveOp(/*TODO*/);
void disableConvolveOp();
void enableRescaleOp(/*TODO*/);
void disableRescaleOp();
void enableLookupOp();
void disableLookupOp();
};
#endif //__cplusplus
#endif //VKRenderer_h_Included

View File

@@ -1,57 +0,0 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef VKShader_h_Included
#define VKShader_h_Included
#define VK_NO_PROTOTYPES
#define VULKAN_HPP_NO_DEFAULT_DISPATCHER
#include <vulkan/vulkan_raii.hpp>
class VKShader : public vk::raii::ShaderModule {
friend struct VKShaders;
vk::ShaderStageFlagBits _stage;
void init(const vk::raii::Device& device, size_t size, const uint32_t* data, vk::ShaderStageFlagBits stage) {
*((vk::raii::ShaderModule*) this) = device.createShaderModule({{}, size, data});
_stage = stage;
}
public:
VKShader() : vk::raii::ShaderModule(nullptr), _stage() {}
vk::PipelineShaderStageCreateInfo stage(const vk::SpecializationInfo *specializationInfo = nullptr) {
return vk::PipelineShaderStageCreateInfo {{}, _stage, **this, "main", specializationInfo};
}
};
struct VKShaders {
// Actual list of shaders is autogenerated from source file names
# define SHADER_ENTRY(NAME, TYPE) VKShader NAME ## _ ## TYPE;
# include "vulkan/shader_list.h"
# undef SHADER_ENTRY
void init(const vk::raii::Device& device);
};
#endif //VKShader_h_Included

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include "jlong.h"
#include "SurfaceData.h"
#include "VKSurfaceData.h"
#include "VKVertex.h"
#include "VKImage.h"
#include <Trace.h>
void VKSD_InitImageSurface(VKSDOps *vksdo) {
if (vksdo->image != VK_NULL_HANDLE) {
return;
}
vksdo->image = VKImage_Create(vksdo->width, vksdo->height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_LINEAR,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
if (!vksdo->image)
{
J2dRlsTrace(J2D_TRACE_ERROR, "Cannot create image\n");
return;
}
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
if (!VKImage_CreateFramebuffer(vksdo->image, logicalDevice->fillTexturePoly->renderPass)) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Cannot create framebuffer for window surface")
return;
}
}
void VKSD_InitWindowSurface(VKWinSDOps *vkwinsdo) {
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
VKLogicalDevice* logicalDevice = &ge->devices[ge->enabledDeviceNum];
VkPhysicalDevice physicalDevice = logicalDevice->physicalDevice;
if (vkwinsdo->swapchainKhr == VK_NULL_HANDLE) {
ge->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, vkwinsdo->surface, &vkwinsdo->capabilitiesKhr);
ge->vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, vkwinsdo->surface, &vkwinsdo->formatsKhrCount, NULL);
if (vkwinsdo->formatsKhrCount == 0) {
J2dRlsTrace(J2D_TRACE_ERROR, "No formats for swapchain found\n");
return;
}
vkwinsdo->formatsKhr = calloc(vkwinsdo->formatsKhrCount, sizeof(VkSurfaceFormatKHR));
ge->vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, vkwinsdo->surface, &vkwinsdo->formatsKhrCount,
vkwinsdo->formatsKhr);
ge->vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, vkwinsdo->surface,
&vkwinsdo->presentModeKhrCount, NULL);
if (vkwinsdo->presentModeKhrCount == 0) {
J2dRlsTrace(J2D_TRACE_ERROR, "No present modes found\n");
return;
}
vkwinsdo->presentModesKhr = calloc(vkwinsdo->presentModeKhrCount, sizeof(VkPresentModeKHR));
ge->vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, vkwinsdo->surface, &vkwinsdo->presentModeKhrCount,
vkwinsdo->presentModesKhr);
VkExtent2D extent = {
(uint32_t) (vkwinsdo->vksdOps.width),
(uint32_t) (vkwinsdo->vksdOps.height)
};
uint32_t imageCount = vkwinsdo->capabilitiesKhr.minImageCount + 1;
if (vkwinsdo->capabilitiesKhr.maxImageCount > 0 && imageCount > vkwinsdo->capabilitiesKhr.maxImageCount) {
imageCount = vkwinsdo->capabilitiesKhr.maxImageCount;
}
VkSwapchainCreateInfoKHR createInfoKhr = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = vkwinsdo->surface,
.minImageCount = imageCount,
.imageFormat = vkwinsdo->formatsKhr[0].format,
.imageColorSpace = vkwinsdo->formatsKhr[0].colorSpace,
.imageExtent = extent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = NULL,
.preTransform = vkwinsdo->capabilitiesKhr.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = VK_PRESENT_MODE_FIFO_KHR,
.clipped = VK_TRUE
};
if (ge->vkCreateSwapchainKHR(logicalDevice->device, &createInfoKhr, NULL, &vkwinsdo->swapchainKhr) != VK_SUCCESS) {
J2dRlsTrace(J2D_TRACE_ERROR, "Cannot create swapchain\n");
return;
}
vkwinsdo->swapChainImages = VKImage_CreateImageArrayFromSwapChain(
vkwinsdo->swapchainKhr,
logicalDevice->fillTexturePoly->renderPass,
vkwinsdo->formatsKhr[0].format, extent);
if (!vkwinsdo->swapChainImages) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Cannot get swapchain images");
return;
}
}
}
#endif /* !HEADLESS */

View File

@@ -1,211 +0,0 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni_util.h"
#include "Disposer.h"
#include "Trace.h"
#include "VKSurfaceData.h"
#include "VKRenderer.h"
void VKSurfaceData::attachToJavaSurface(JNIEnv *env, jobject javaSurfaceData) {
// SurfaceData utility functions operate on C structures and malloc/free,
// But we are using C++ classes, so set up the disposer manually.
// This is a C++ analogue of SurfaceData_InitOps / SurfaceData_SetOps.
jboolean exception = false;
if (JNU_GetFieldByName(env, &exception, javaSurfaceData, "pData", "J").j == 0 && !exception) {
jlong ptr = ptr_to_jlong((SurfaceDataOps*) this);
JNU_SetFieldByName(env, &exception, javaSurfaceData, "pData", "J", ptr);
/* Register the data for disposal */
Disposer_AddRecord(env, javaSurfaceData, [](JNIEnv *env, jlong ops) {
if (ops != 0) {
auto sd = (SurfaceDataOps*)jlong_to_ptr(ops);
jobject sdObject = sd->sdObject;
sd->Dispose(env, sd);
if (sdObject != nullptr) {
env->DeleteWeakGlobalRef(sdObject);
}
}
}, ptr);
} else if (!exception) {
throw std::runtime_error("Attempting to set SurfaceData ops twice");
}
if (exception) {
throw std::runtime_error("VKSurfaceData::attachToJavaSurface error");
}
sdObject = env->NewWeakGlobalRef(javaSurfaceData);
}
VKSurfaceData::VKSurfaceData(uint32_t w, uint32_t h, uint32_t s, uint32_t bgc)
: SurfaceDataOps(), _width(w), _height(h), _scale(s), _bg_color(bgc), _device(nullptr) {
Lock = [](JNIEnv *env, SurfaceDataOps *ops, SurfaceDataRasInfo *rasInfo, jint lockFlags) {
((VKSurfaceData*) ops)->_mutex.lock();
return SD_SUCCESS;
};
Unlock = [](JNIEnv *env, SurfaceDataOps *ops, SurfaceDataRasInfo *rasInfo) {
((VKSurfaceData*) ops)->_mutex.unlock();
};
Dispose = [](JNIEnv *env, SurfaceDataOps *ops) {
delete (VKSurfaceData*) ops;
};
}
bool VKSurfaceData::barrier(VKRecorder& recorder, vk::Image image,
vk::PipelineStageFlags stage, vk::AccessFlags access, vk::ImageLayout layout) {
// TODO consider write/read access
if (_lastStage != stage || _lastAccess != access || _layout != layout) {
if (_device->synchronization2()) {
vk::ImageMemoryBarrier2KHR barrier {
(vk::PipelineStageFlags2KHR) (VkFlags) _lastStage,
(vk::AccessFlags2KHR) (VkFlags) _lastAccess,
(vk::PipelineStageFlags2KHR) (VkFlags) stage,
(vk::AccessFlags2KHR) (VkFlags) access,
_layout, layout,
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
image, vk::ImageSubresourceRange {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}
};
recorder.record(false).pipelineBarrier2KHR(vk::DependencyInfoKHR {{}, {}, {}, barrier});
} else {
vk::ImageMemoryBarrier barrier {
_lastAccess, access,
_layout, layout,
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
image, vk::ImageSubresourceRange {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}
};
recorder.record(false).pipelineBarrier(_lastStage, stage, {}, {}, {}, barrier);
}
_lastStage = stage;
_lastAccess = access;
_layout = layout;
// TODO check write access
return true;
} else return false;
}
void VKSwapchainSurfaceData::revalidate(uint32_t w, uint32_t h, uint32_t s) {
if (*_swapchain && s == scale() && w == width() && h == height() ) {
J2dTraceLn1(J2D_TRACE_INFO,
"VKSwapchainSurfaceData_revalidate is skipped: swapchain(%p)",
*_swapchain);
return;
}
VKSurfaceData::revalidate(w, h, s);
if (!_device || !*_surface) {
J2dTraceLn2(J2D_TRACE_ERROR,
"VKSwapchainSurfaceData_revalidate is skipped: device(%p) surface(%p)",
_device, *_surface);
return;
}
vk::SurfaceCapabilitiesKHR surfaceCapabilities = device().getSurfaceCapabilitiesKHR(*_surface);
_format = vk::Format::eB8G8R8A8Unorm; // TODO?
// TODO all these parameters must be checked against device & surface capabilities
vk::SwapchainCreateInfoKHR swapchainCreateInfo{
{},
*_surface,
surfaceCapabilities.minImageCount,
format(),
vk::ColorSpaceKHR::eVkColorspaceSrgbNonlinear,
{width(), height()}, // TODO According to spec we need to use surfaceCapabilities.currentExtent, which is not available at this point (it gives -1)... We'll figure this out later
1,
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst,
vk::SharingMode::eExclusive,
0,
nullptr,
vk::SurfaceTransformFlagBitsKHR::eIdentity,
vk::CompositeAlphaFlagBitsKHR::eOpaque,
vk::PresentModeKHR::eImmediate,
true, *_swapchain
};
device().waitIdle(); // TODO proper synchronization in case there are rendering operations for old swapchain in flight
_images.clear();
_swapchain = device().createSwapchainKHR(swapchainCreateInfo);
for (vk::Image image : _swapchain.getImages()) {
_images.push_back({image, device().createImageView(vk::ImageViewCreateInfo {
{}, image, vk::ImageViewType::e2D, format(), {},
vk::ImageSubresourceRange {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}
})});
if (!device().dynamicRendering()) {
_images.back().framebuffer = device().createFramebuffer(vk::FramebufferCreateInfo{
/*flags*/ {},
/*renderPass*/ *device().pipelines().renderPass,
/*attachmentCount*/ 1,
/*pAttachments*/ &*_images.back().view,
/*width*/ width(),
/*height*/ height(),
/*layers*/ 1
});
}
}
_currentImage = (uint32_t) -1;
_freeSemaphore = nullptr;
// TODO Now we need to repaint our surface... How is it done? No idea
}
VKSurfaceImage VKSwapchainSurfaceData::access(VKRecorder& recorder,
vk::PipelineStageFlags stage,
vk::AccessFlags access,
vk::ImageLayout layout) {
// Acquire image
bool semaphorePending = false;
if (_currentImage == (uint32_t) -1) {
if (!*_freeSemaphore) {
_freeSemaphore = device().createSemaphore({});
}
auto img = _swapchain.acquireNextImage(-1, *_freeSemaphore, nullptr);
vk::resultCheck(img.first, "vk::SwapchainKHR::acquireNextImage");
_layout = vk::ImageLayout::eUndefined;
_lastStage = _lastWriteStage = vk::PipelineStageFlagBits::eTopOfPipe;
_lastAccess = _lastWriteAccess = {};
_currentImage = (int) img.second;
std::swap(_images[_currentImage].semaphore, _freeSemaphore);
semaphorePending = true;
}
// Insert barrier & semaphore
auto& current = _images[_currentImage];
barrier(recorder, current.image, stage, access, layout);
if (semaphorePending) {
recorder.waitSemaphore(*current.semaphore,
vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eTransfer);
}
return {current.image, *current.view, *current.framebuffer};
}
void VKSwapchainSurfaceData::flush(VKRecorder& recorder) {
if (_currentImage == (uint32_t) -1) {
return; // Nothing to flush
}
recorder.record(true); // Force flush current render pass before accessing image with present layout.
access(recorder, vk::PipelineStageFlagBits::eTopOfPipe, {}, vk::ImageLayout::ePresentSrcKHR);
auto& current = _images[_currentImage];
recorder.signalSemaphore(*current.semaphore);
recorder.flush();
device().queue().presentKHR(vk::PresentInfoKHR {
*current.semaphore, *_swapchain, _currentImage
});
_currentImage = (uint32_t) -1;
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,10 +27,13 @@
#ifndef VKSurfaceData_h_Included
#define VKSurfaceData_h_Included
#include <mutex>
#include <pthread.h>
#include "jni.h"
#include "SurfaceData.h"
#include "sun_java2d_pipe_hw_AccelSurface.h"
#include "VKBase.h"
#include "VKBuffer.h"
#include "VKImage.h"
/**
* These are shorthand names for the surface type constants defined in
@@ -38,125 +41,64 @@
*/
#define VKSD_UNDEFINED sun_java2d_pipe_hw_AccelSurface_UNDEFINED
#define VKSD_WINDOW sun_java2d_pipe_hw_AccelSurface_WINDOW
#define VKSD_TEXTURE sun_java2d_pipe_hw_AccelSurface_TEXTURE
#define VKSD_RT_TEXTURE sun_java2d_pipe_hw_AccelSurface_RT_TEXTURE
class VKRecorder;
struct VKSurfaceImage {
vk::Image image;
vk::ImageView view;
vk::Framebuffer framebuffer; // Only if dynamic rendering is off.
};
class VKSurfaceData : private SurfaceDataOps {
std::recursive_mutex _mutex;
uint32_t _width;
uint32_t _height;
uint32_t _scale; // TODO Is it needed there at all?
uint32_t _bg_color;
protected:
VKDevice* _device;
vk::Format _format;
vk::ImageLayout _layout = vk::ImageLayout::eUndefined;
/**
* The VKSDOps structure describes a native Vulkan surface and contains all
* information pertaining to the native surface.
*/
typedef struct {
SurfaceDataOps sdOps;
jint drawableType;
pthread_mutex_t mutex;
uint32_t width;
uint32_t height;
uint32_t scale; // TODO Is it needed there at all?
uint32_t bgColor;
VkBool32 bgColorUpdated;
VKLogicalDevice* device;
VKImage* image;
// We track any access and write access separately, as read-read access does not need synchronization.
vk::PipelineStageFlags _lastStage = vk::PipelineStageFlagBits::eTopOfPipe;
vk::PipelineStageFlags _lastWriteStage = vk::PipelineStageFlagBits::eTopOfPipe;
vk::AccessFlags _lastAccess = {};
vk::AccessFlags _lastWriteAccess = {};
VkPipelineStageFlagBits lastStage;
VkPipelineStageFlagBits lastWriteStage;
VkAccessFlagBits lastAccess;
VkAccessFlagBits lastWriteAccess;
} VKSDOps;
/// Insert barrier if needed for given access and layout.
bool barrier(VKRecorder& recorder, vk::Image image,
vk::PipelineStageFlags stage, vk::AccessFlags access, vk::ImageLayout layout);
public:
VKSurfaceData(uint32_t w, uint32_t h, uint32_t s, uint32_t bgc);
// No need to move, as object must only be created with "new".
VKSurfaceData(VKSurfaceData&&) = delete;
VKSurfaceData& operator=(VKSurfaceData&&) = delete;
/**
* The VKWinSDOps structure describes a native Vulkan surface connected with a window.
* Some information about the more important/different fields:
*
* void *privOps;
* Pointer to native-specific SurfaceData info, such as the
* native Drawable handle and GraphicsConfig data.
*/
typedef struct {
VKSDOps vksdOps;
void *privOps;
VkSurfaceKHR surface;
VkSurfaceCapabilitiesKHR capabilitiesKhr;
VkSurfaceFormatKHR* formatsKhr;
uint32_t formatsKhrCount;
VkPresentModeKHR* presentModesKhr;
uint32_t presentModeKhrCount;
VkSwapchainKHR swapchainKhr;
VKImage* swapChainImages;
} VKWinSDOps;
void attachToJavaSurface(JNIEnv *env, jobject javaSurfaceData);
/**
* Exported methods.
*/
jint VKSD_Lock(JNIEnv *env,
SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo,
jint lockflags);
void VKSD_GetRasInfo(JNIEnv *env,
SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo);
void VKSD_Unlock(JNIEnv *env,
SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo);
void VKSD_Dispose(JNIEnv *env, SurfaceDataOps *ops);
void VKSD_Delete(JNIEnv *env, VKSDOps *oglsdo);
VKDevice& device() const {
return *_device;
}
vk::Format format() const {
return _format;
}
uint32_t width() const {
return _width;
}
uint32_t height() const {
return _height;
}
uint32_t scale() const {
return _scale;
}
uint32_t bg_color() const {
return _bg_color;
}
void set_bg_color(uint32_t bg_color) {
if (_bg_color != bg_color) {
_bg_color = bg_color;
// TODO now we need to repaint the surface???
}
}
virtual ~VKSurfaceData() = default;
virtual void revalidate(uint32_t w, uint32_t h, uint32_t s) {
_width = w;
_height = h;
_scale = s;
}
/// Prepare image for access (necessary barriers & layout transitions).
virtual VKSurfaceImage access(VKRecorder& recorder,
vk::PipelineStageFlags stage,
vk::AccessFlags access,
vk::ImageLayout layout) = 0;
/// Flush all pending changes to the surface, including screen presentation for on-screen surfaces.
virtual void flush(VKRecorder& recorder) = 0;
};
class VKSwapchainSurfaceData : public VKSurfaceData {
struct Image {
vk::Image image;
vk::raii::ImageView view;
vk::raii::Framebuffer framebuffer = nullptr; // Only if dynamic rendering is off.
vk::raii::Semaphore semaphore = nullptr;
};
vk::raii::SurfaceKHR _surface = nullptr;
vk::raii::SwapchainKHR _swapchain = nullptr;
std::vector<Image> _images;
uint32_t _currentImage = (uint32_t) -1;
vk::raii::Semaphore _freeSemaphore = nullptr;
protected:
void reset(VKDevice& device, vk::raii::SurfaceKHR surface) {
_images.clear();
_swapchain = nullptr;
_surface = std::move(surface);
_device = &device;
}
public:
VKSwapchainSurfaceData(uint32_t w, uint32_t h, uint32_t s, uint32_t bgc)
: VKSurfaceData(w, h, s, bgc) {};
virtual void revalidate(uint32_t w, uint32_t h, uint32_t s);
virtual VKSurfaceImage access(VKRecorder& recorder,
vk::PipelineStageFlags stage,
vk::AccessFlags access,
vk::ImageLayout layout);
virtual void flush(VKRecorder& recorder);
};
void VKSD_InitImageSurface(VKSDOps *vksdo);
void VKSD_InitWindowSurface(VKWinSDOps *vkwinsdo);
#endif /* VKSurfaceData_h_Included */

View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <string.h>
#include "CArrayUtil.h"
#include "VKVertex.h"
VKVertexDescr VKVertex_GetTxVertexDescr() {
static VkVertexInputBindingDescription bindingDescriptions[] = {
{
.binding = 0,
.stride = sizeof(VKTxVertex),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX
}
};
static VkVertexInputAttributeDescription attributeDescriptions [] = {
{
.binding = 0,
.location = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = offsetof(VKTxVertex, px)
},
{
.binding = 0,
.location = 1,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = offsetof(VKTxVertex, u)
}
};
return (VKVertexDescr) {
.attributeDescriptions = attributeDescriptions,
.attributeDescriptionCount = SARRAY_COUNT_OF(attributeDescriptions),
.bindingDescriptions = bindingDescriptions,
.bindingDescriptionCount = SARRAY_COUNT_OF(bindingDescriptions)
};
}
VKVertexDescr VKVertex_GetCVertexDescr() {
static VkVertexInputBindingDescription bindingDescriptions[] = {
{
.binding = 0,
.stride = sizeof(VKCVertex),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX
}
};
static VkVertexInputAttributeDescription attributeDescriptions [] = {
{
.binding = 0,
.location = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = offsetof(VKCVertex, px)
},
{
.binding = 0,
.location = 1,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = offsetof(VKCVertex, r)
}
};
return (VKVertexDescr) {
.attributeDescriptions = attributeDescriptions,
.attributeDescriptionCount = SARRAY_COUNT_OF(attributeDescriptions),
.bindingDescriptions = bindingDescriptions,
.bindingDescriptionCount = SARRAY_COUNT_OF(bindingDescriptions)
};
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef VKVertex_h_Included
#define VKVertex_h_Included
#include <vulkan/vulkan.h>
#include "VKBuffer.h"
#define RGBA_TO_L4(c) \
(((c) >> 16) & (0xFF))/255.0f, \
(((c) >> 8) & 0xFF)/255.0f, \
((c) & 0xFF)/255.0f, \
(((c) >> 24) & 0xFF)/255.0f
#define ARRAY_TO_VERTEX_BUF(vertices) \
VKBuffer_CreateFromData(vertices, ARRAY_SIZE(vertices)*sizeof (vertices[0]))
typedef struct {
VkVertexInputAttributeDescription *attributeDescriptions;
uint32_t attributeDescriptionCount;
VkVertexInputBindingDescription* bindingDescriptions;
uint32_t bindingDescriptionCount;
} VKVertexDescr;
typedef struct {
float px, py;
float u, v;
} VKTxVertex;
typedef struct {
float px, py;
float r, g, b, a;
} VKCVertex;
VKVertexDescr VKVertex_GetTxVertexDescr();
VKVertexDescr VKVertex_GetCVertexDescr();
#endif //VKVertex_h_Included

View File

@@ -67,7 +67,7 @@ int isNullScalerContext(void *context) {
*/
JNIEXPORT jlong JNICALL Java_sun_font_NullFontScaler_getGlyphImage
(JNIEnv *env, jobject scaler, jlong pContext, jint glyphCode) {
void *nullscaler = calloc(sizeof(GlyphInfo), 1);
void *nullscaler = calloc(1, sizeof(GlyphInfo));
return ptr_to_jlong(nullscaler);
}

View File

@@ -55,11 +55,13 @@ import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import jdk.internal.misc.InnocuousThread;
import sun.awt.X11.XBaseWindow;
import sun.security.action.GetIntegerAction;
import com.sun.java.swing.plaf.gtk.GTKConstants.TextDirection;
import sun.java2d.opengl.OGLRenderQueue;
import sun.security.action.GetPropertyAction;
import sun.util.logging.PlatformLogger;
public abstract class UNIXToolkit extends SunToolkit
{
@@ -112,6 +114,16 @@ public abstract class UNIXToolkit extends SunToolkit
private Boolean nativeGTKAvailable;
private Boolean nativeGTKLoaded;
private BufferedImage tmpImage = null;
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.UNIXToolkit");
private static void printError(String str) {
log.fine(str);
}
@Override
protected void initializeDesktopProperties() {
initSystemPropertyWatcher();
}
public static int getDatatransferTimeout() {
@SuppressWarnings("removal")
@@ -548,6 +560,8 @@ public abstract class UNIXToolkit extends SunToolkit
return result;
}
private static native int isSystemDarkColorScheme();
@Override
public boolean isRunningOnXWayland() {
return isOnXWayland();
@@ -566,6 +580,43 @@ public abstract class UNIXToolkit extends SunToolkit
// application icons).
private static final WindowFocusListener waylandWindowFocusListener;
private static final String OS_THEME_IS_DARK = "awt.os.theme.isDark";
private static Thread systemPropertyWatcher = null;
private static native boolean dbusInit();
private void initSystemPropertyWatcher() {
@SuppressWarnings("removal")
String dbusEnabled = AccessController.doPrivileged(
new GetPropertyAction("jbr.dbus.enabled", "true")).toLowerCase();
if (!"true".equals(dbusEnabled) || !dbusInit()) {
return;
}
int initialSystemDarkColorScheme = isSystemDarkColorScheme();
if (initialSystemDarkColorScheme >= 0) {
setDesktopProperty(OS_THEME_IS_DARK, initialSystemDarkColorScheme != 0);
systemPropertyWatcher = InnocuousThread.newThread("SystemPropertyWatcher",
() -> {
while (true) {
try {
int isSystemDarkColorScheme = isSystemDarkColorScheme();
if (isSystemDarkColorScheme >= 0) {
setDesktopProperty(OS_THEME_IS_DARK, isSystemDarkColorScheme != 0);
}
Thread.sleep(1000);
} catch (Exception ignored) {
}
}
});
systemPropertyWatcher.setDaemon(true);
systemPropertyWatcher.start();
}
}
static {
if (isOnXWayland()) {
waylandWindowFocusListener = new WindowAdapter() {

View File

@@ -33,12 +33,15 @@ import java.awt.im.spi.InputMethodContext;
import java.awt.peer.ComponentPeer;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.ArrayDeque;
import java.util.Iterator;
import java.util.Objects;
import java.util.Queue;
import java.util.function.Supplier;
import java.util.stream.Stream;
import sun.awt.AWTAccessor;
import sun.awt.SunToolkit;
import sun.awt.X11GraphicsDevice;
import sun.awt.X11GraphicsEnvironment;
import sun.awt.X11InputMethod;
@@ -303,6 +306,155 @@ public class XInputMethod extends X11InputMethod {
}
// JBR-6456: Sudden keyboard death on Linux using iBus.
// xicDestroyMustBeDelayed, XIC_DELAYED_TO_BE_DESTROYED_CAPACITY, xicDelayedToBeDestroyed can only be accessed
// under the AWT lock
// See the #disposeXIC method for the purpose of these fields
private static boolean xicDestroyMustBeDelayed = false;
private static final int XIC_DELAYED_TO_BE_DESTROYED_CAPACITY = 16;
private static final Queue<Long> xicDelayedToBeDestroyed = new ArrayDeque<>(XIC_DELAYED_TO_BE_DESTROYED_CAPACITY);
static void delayAllXICDestroyUntilAFurtherNotice() {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("delayAllXICDestroyUntilAFurtherNotice(): is being called", new Throwable("Stacktrace"));
}
XToolkit.awtLock();
try {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("delayAllXICDestroyUntilAFurtherNotice(): xicDestroyMustBeDelayed=={0}", xicDestroyMustBeDelayed);
}
xicDestroyMustBeDelayed = true;
} finally {
XToolkit.awtUnlock();
}
}
static void delayedXICDestroyShouldBeDone() {
XToolkit.awtLock();
try {
xicDestroyMustBeDelayed = false;
doDelayedXICDestroy(false, -1);
} finally {
XToolkit.awtUnlock();
}
}
private static void doDelayedXICDestroy(boolean forced, int maxCountToDestroy) {
final boolean isFineLoggable = log.isLoggable(PlatformLogger.Level.FINE);
if (isFineLoggable) {
log.fine(
"doDelayedXICDestroy(forced==" + forced + ", maxCountToDestroy==" + maxCountToDestroy + "): is being called",
new Throwable("Stacktrace")
);
}
assert(SunToolkit.isAWTLockHeldByCurrentThread());
assert(forced || !xicDestroyMustBeDelayed);
while ( (maxCountToDestroy != 0) && !xicDelayedToBeDestroyed.isEmpty() ) {
final long pX11IMData = xicDelayedToBeDestroyed.remove();
--maxCountToDestroy;
if (isFineLoggable) {
log.fine("doDelayedXICDestroy(): destroying pX11IMData={0}", pX11IMData);
}
assert(pX11IMData != 0);
delayedDisposeXIC_disposeXICNative(pX11IMData);
}
}
@Override
protected void disposeXIC() {
awtLock();
try {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("disposeXIC(): xicDestroyMustBeDelayed=={0}", xicDestroyMustBeDelayed);
}
if (!xicDestroyMustBeDelayed) {
// JBR-6456: Sudden keyboard death on Linux using iBus.
// iBus's X11 frontend being run in the async mode (IBUS_ENABLE_SYNC_MODE=0) has a bug leading to a
// violation of the communication protocol between iBus and Xlib (so-called "XIM protocol"),
// later causing Xlib to behave unexpectedly from iBus's point of view, breaking iBus's
// internal state. After all, iBus starts to "steal" all the keyboard events
// (so that each call of XFilterEvent(...) with an instance of XKeyEvent returns True).
// The initial iBus's bug only appears when XDestroyIC(...) gets called right after a call of
// XFilterEvent(...) with an instance of XKeyEvent returned True,
// meaning that iBus has started, but hasn't finished yet processing of the key event.
// In case of AWT/Swing apps, XDestroyIC gets called whenever a focused HW window gets closed
// (because it leads to disposing of the associated input context,
// see java.awt.Window#doDispose and sun.awt.im.InputContext#dispose)
// So, to work around iBus's bug, we have to avoid calling XDestroyIC until iBus finishes processing of
// all the keyboard events it has already started processing of, i.e. until a call of
// XFilterEvent(...) returns False.
// To achieve that, the implemented fix delays destroying of input contexts whenever a call of
// XFilterEvent(...) with an instance of XKeyEvent returns True until one of the next calls of
// XFilterEvent(...) with the same instance of XKeyEvent returns False.
// The delaying is implemented via storing the native pointers to the input contexts to
// xicDelayedToBeDestroyed instead of applying XDestroyIC(...) immediately.
// The xicDelayedToBeDestroyed's size is explicitly limited to
// XIC_DELAYED_TO_BE_DESTROYED_CAPACITY. If the limit gets reached, a few input contexts gets
// pulled from there and destroyed regardless of the current value of xicDestroyMustBeDelayed.
// The xicDestroyMustBeDelayed field is responsible for indication whether it's required to delay
// the destroying or not. It gets set in #delayAllXICDestroyUntilAFurtherNotice
// and unset in delayedXICDestroyShouldBeDone; both are called by sun.awt.X11.XToolkit depending on
// the value returned by the calls of sun.awt.X11.XlibWrapper#XFilterEvent.
super.disposeXIC();
return;
}
final long pX11IMData = pData;
// To make sure that the delayed to be destroyed input context won't get used by AWT/Swing or Xlib
// by a mistake, the following things are done:
// 1. The input method focus gets detached from the input context (via a call of XUnsetICFocus)
// 2. All the native pointers to this instance of XInputMethod
// (now it's just the variable currentX11InputMethodInstance in awt_InputMethod.c) get unset
// 3. All the java pointers to the native context (now it's just sun.awt.X11InputMethodBase#pData)
// get unset as well
delayedDisposeXIC_preparation_unsetFocusAndDetachCurrentXICNative();
// 4. The state of the native context gets reset (effectively via a call of XmbResetIC)
delayedDisposeXIC_preparation_resetSpecifiedCtxNative(pX11IMData);
if (pX11IMData == 0) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("disposeXIC(): pX11IMData==NULL, skipped");
}
return;
}
// If the storage is full, a few input context are pulled from there and destroyed regardless of
// the value of xicDestroyMustBeDelayed
if (xicDelayedToBeDestroyed.size() >= XIC_DELAYED_TO_BE_DESTROYED_CAPACITY) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine(
"disposeXIC(): xicDelayedToBeDestroyed.size()=={0} >= XIC_DELAYED_TO_BE_DESTROYED_CAPACITY",
xicDelayedToBeDestroyed.size()
);
}
doDelayedXICDestroy(true, xicDelayedToBeDestroyed.size() - XIC_DELAYED_TO_BE_DESTROYED_CAPACITY + 1);
}
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine(
"disposeXIC(): adding pX11IMData=={0} to xicDelayedToBeDestroyed (which already contains {1} elements)",
pX11IMData, xicDelayedToBeDestroyed.size()
);
}
xicDelayedToBeDestroyed.add(pX11IMData);
} finally {
awtUnlock();
}
}
static void onXKeyEventFiltering(final boolean isXKeyEventFiltered) {
// Fix of JBR-1573, JBR-2444, JBR-4394 (a.k.a. IDEA-246833).
// Input method is considered broken if and only if all the following statements are true:
@@ -616,6 +768,15 @@ public class XInputMethod extends X11InputMethod {
private native void setXICFocusNative(long window, boolean value, boolean active);
private native void adjustStatusWindow(long window);
// 1. Applies XUnsetICFocus to the current input context
// 2. Unsets currentX11InputMethodInstance if it's set to this instance of XInputMethod
// 3. Unsets sun.awt.X11InputMethodBase#pData
private native void delayedDisposeXIC_preparation_unsetFocusAndDetachCurrentXICNative();
// Applies XmbResetIC to the passed input context
private static native void delayedDisposeXIC_preparation_resetSpecifiedCtxNative(long pX11IMData);
// Applies XDestroyIC to the passed input context
private static native void delayedDisposeXIC_disposeXICNative(long pX11IMData);
private native boolean doesFocusedXICSupportMovingCandidatesNativeWindow();
private native void adjustCandidatesNativeWindowPosition(int x, int y);

View File

@@ -1020,11 +1020,22 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
final boolean isKeyEvent = ( (ev.get_type() == XConstants.KeyPress) ||
(ev.get_type() == XConstants.KeyRelease) );
final long keyEventSerial = isKeyEvent ? ev.get_xkey().get_serial() : -1;
if (keyEventLog.isLoggable(PlatformLogger.Level.FINE) && isKeyEvent) {
keyEventLog.fine("before XFilterEvent:" + ev);
}
if (XlibWrapper.XFilterEvent(ev.getPData(), w)) {
if (isKeyEvent) {
if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
keyEventLog.fine(
"Setting lastFilteredKeyEventSerial=={0} to {1}",
lastFilteredKeyEventSerial, keyEventSerial
);
}
lastFilteredKeyEventSerial = keyEventSerial;
XInputMethod.delayAllXICDestroyUntilAFurtherNotice();
XInputMethod.onXKeyEventFiltering(true);
}
continue;
@@ -1036,6 +1047,14 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
if (isKeyEvent) {
XInputMethod.onXKeyEventFiltering(false);
if (keyEventSerial == lastFilteredKeyEventSerial) {
// JBR-6456: Sudden keyboard death on Linux using iBus.
// If more than 1 key events are being processed by iBus
// (i.e. more than one in a row calls of XFilterEvent(...) with instances of XKeyEvent have
// returned true),
// we have to postpone destroying until the very last one is completely processed)
XInputMethod.delayedXICDestroyShouldBeDone();
}
}
dispatchEvent(ev);
@@ -1107,6 +1126,14 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
}
// JBR-6456: Sudden keyboard death on Linux using iBus.
// The field holds the value of sun.awt.X11.XKeyEvent#get_serial of the last key event, which
// XFilterEvent(...) returned True for.
// See the usages of the variable for more info.
// See sun.awt.X11.XInputMethod#disposeXIC for the detailed explanation of the whole fix.
private long lastFilteredKeyEventSerial = -1;
/**
* Listener installed to detect display changes.
*/
@@ -1215,7 +1242,9 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
}
X11GraphicsDevice x11gd = (X11GraphicsDevice) gd;
int screenNum = x11gd.getScreen();
if (localEnv.runningXinerama() && screenNum != 0) {
Rectangle screen = gc.getBounds();
boolean isFirstScreen = screen.x == 0 && screen.y == 0;
if (localEnv.runningXinerama() && !isFirstScreen) {
// We cannot estimate insets for non-default screen,
// there are often none.
return new Insets(0, 0, 0, 0);
@@ -1224,7 +1253,6 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
XToolkit.awtLock();
try {
Rectangle workArea = getWorkArea(XlibUtil.getRootWindow(screenNum));
Rectangle screen = gc.getBounds();
if (workArea != null) {
Point p = x11gd.scaleDown(workArea.x, workArea.y);
workArea.x = p.x;
@@ -1233,8 +1261,8 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
workArea.height = x11gd.scaleDown(workArea.height);
workArea = workArea.intersection(screen);
if (!workArea.isEmpty()) {
int top = workArea.y - screen.y;
int left = workArea.x - screen.x;
int top = workArea.y;
int left = workArea.x;
int bottom = screen.height - workArea.height - top;
int right = screen.width - workArea.width - left;
return new Insets(top, left, bottom, right);
@@ -1923,6 +1951,7 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
localEnv.displayChanged());
}
}
super.initializeDesktopProperties();
}
/**

View File

@@ -83,12 +83,12 @@ public final class X11GraphicsDevice extends GraphicsDevice
public X11GraphicsDevice(int screennum) {
this.screen = screennum;
this.bounds = getBoundsImpl();
int scaleFactor = initScaleFactor(-1);
synchronized (isScaleFactorDefault) {
isScaleFactorDefault.set(scaleFactor == -1);
this.scale = isScaleFactorDefault.get() ? 1 : scaleFactor;
}
this.bounds = getBoundsImpl();
}
static {

View File

@@ -256,11 +256,19 @@ public final class X11GraphicsEnvironment extends SunGraphicsEnvironment {
updateWaylandMonitorScaling();
for (int id = 0; id < numScreens; ++id) {
devices.put(id, old.containsKey(id) ? old.remove(id) :
new X11GraphicsDevice(id));
X11GraphicsDevice reused = old.remove(id);
X11GraphicsDevice gd = reused != null ? reused : new X11GraphicsDevice(id);
devices.put(id, gd);
if (LogDisplay.ENABLED) {
LogDisplay log = reused != null ? LogDisplay.CHANGED : LogDisplay.ADDED;
log.log(id, gd.getBounds(), gd.getScaleFactor());
}
}
// if a device was not reused it should be invalidated
for (X11GraphicsDevice gd : old.values()) {
if (LogDisplay.ENABLED) {
LogDisplay.REMOVED.log(gd.getScreen(), gd.getBounds(), gd.getScaleFactor());
}
oldDevices.add(new WeakReference<>(gd));
}
// Need to notify old devices, in case the user hold the reference to it

View File

@@ -29,6 +29,7 @@ package sun.awt.wl;
import sun.awt.AWTAccessor;
import sun.awt.AWTAccessor.ComponentAccessor;
import sun.awt.PaintEventDispatcher;
import sun.awt.SunToolkit;
import sun.awt.event.IgnorePaintEvent;
import sun.awt.image.SunVolatileImage;
import sun.java2d.SunGraphics2D;
@@ -119,8 +120,8 @@ public class WLComponentPeer implements ComponentPeer {
boolean visible = false;
private final Object dataLock = new Object();
int width; // protected by dataLock
int height; // protected by dataLock
int width; // in native pixels, protected by dataLock
int height; // in native pixels, protected by dataLock
int wlBufferScale; // protected by dataLock
double effectiveScale; // protected by dataLock
@@ -354,7 +355,47 @@ public class WLComponentPeer implements ComponentPeer {
void updateSurfaceData() {
SurfaceData.convertTo(WLSurfaceDataExt.class, surfaceData).revalidate(
getBufferWidth(), getBufferHeight(), getBufferScale());
updateWindowGeometry();
}
public void updateSurfaceSize() {
assert SunToolkit.isAWTLockHeldByCurrentThread();
// Note: must be called after a buffer of proper size has been attached to the surface,
// but the surface has not yet been committed. Otherwise, the sizes may get out of sync,
// which may result in visual artifacts.
int thisWidth = javaUnitsToSurfaceUnits(getWidth());
int thisHeight = javaUnitsToSurfaceUnits(getHeight());
Rectangle nativeVisibleBounds = getVisibleBounds();
nativeVisibleBounds.x = javaUnitsToSurfaceUnits(nativeVisibleBounds.x);
nativeVisibleBounds.y = javaUnitsToSurfaceUnits(nativeVisibleBounds.y);
nativeVisibleBounds.width = javaUnitsToSurfaceUnits(nativeVisibleBounds.width);
nativeVisibleBounds.height = javaUnitsToSurfaceUnits(nativeVisibleBounds.height);
Dimension nativeMinSize = constrainSize(getMinimumSize());
nativeMinSize.width = javaUnitsToSurfaceUnits(nativeMinSize.width);
nativeMinSize.height = javaUnitsToSurfaceUnits(nativeMinSize.height);
Dimension maxSize = target.isMaximumSizeSet() ? target.getMaximumSize() : null;
Dimension nativeMaxSize = maxSize != null ? constrainSize(maxSize) : null;
if (nativeMaxSize != null) {
nativeMaxSize.width = javaUnitsToSurfaceUnits(nativeMaxSize.width);
nativeMaxSize.height = javaUnitsToSurfaceUnits(nativeMaxSize.height);
}
nativeSetSurfaceSize(nativePtr, thisWidth, thisHeight);
if (!surfaceData.getColorModel().hasAlpha()) {
nativeSetOpaqueRegion(nativePtr,
nativeVisibleBounds.x, nativeVisibleBounds.y,
nativeVisibleBounds.width, nativeVisibleBounds.height);
}
nativeSetWindowGeometry(nativePtr,
nativeVisibleBounds.x, nativeVisibleBounds.y,
nativeVisibleBounds.width, nativeVisibleBounds.height);
nativeSetMinimumSize(nativePtr, nativeMinSize.width, nativeMinSize.height);
if (nativeMaxSize != null) {
nativeSetMaximumSize(nativePtr, nativeMaxSize.width, nativeMaxSize.height);
}
}
void configureWLSurface() {
@@ -523,11 +564,10 @@ public class WLComponentPeer implements ComponentPeer {
}
/**
* Returns the scale of wl_buffer attached to this component's wl_surface.
* Buffer coordinate space is linearly scaled wrt the component (or surface)
* coordinate space, so component's coordinates have to be translated
* to buffers' whenever Wayland protocol requires "buffer-local" coordinates.
* See wl_surface.set_buffer_scale in wayland.xml for more details.
* Represents the scale ratio of Wayland's backing buffer in pixel units
* to surface units. Wayland events are generated in surface units, while
* painting should be performed in pixel units.
* The ratio is enforced with nativeSetSurfaceSize().
*/
int getBufferScale() {
synchronized(dataLock) {
@@ -553,22 +593,6 @@ public class WLComponentPeer implements ComponentPeer {
}
}
private void updateWindowGeometry() {
// From xdg-shell.xml:
// "The window geometry of a surface is its "visible bounds" from the
// user's perspective. Client-side decorations often have invisible
// portions like drop-shadows which should be ignored for the
// purposes of aligning, placing and constraining windows"
Rectangle nativeVisibleBounds = getVisibleBounds();
nativeVisibleBounds.x = javaUnitsToSurfaceUnits(nativeVisibleBounds.x);
nativeVisibleBounds.y = javaUnitsToSurfaceUnits(nativeVisibleBounds.y);
nativeVisibleBounds.width = javaUnitsToSurfaceUnits(nativeVisibleBounds.width);
nativeVisibleBounds.height = javaUnitsToSurfaceUnits(nativeVisibleBounds.height);
performLocked(() -> nativeSetWindowGeometry(nativePtr,
nativeVisibleBounds.x, nativeVisibleBounds.y,
nativeVisibleBounds.width, nativeVisibleBounds.height));
}
public void coalescePaintEvent(PaintEvent e) {
Rectangle r = e.getUpdateRect();
if (!(e instanceof IgnorePaintEvent)) {
@@ -728,20 +752,6 @@ public class WLComponentPeer implements ComponentPeer {
public Dimension getMinimumSize() {
return target.getSize();
}
void setMinimumSizeTo(Dimension minSize) {
Dimension nativeSize = constrainSize(minSize);
nativeSize.width = javaUnitsToSurfaceUnits(nativeSize.width);
nativeSize.height = javaUnitsToSurfaceUnits(nativeSize.height);
performLocked(() -> nativeSetMinimumSize(nativePtr, nativeSize.width, nativeSize.height));
}
void setMaximumSizeTo(Dimension maxSize) {
Dimension nativeSize = constrainSize(maxSize);
nativeSize.width = javaUnitsToSurfaceUnits(nativeSize.width);
nativeSize.height = javaUnitsToSurfaceUnits(nativeSize.height);
performLocked(() -> nativeSetMaximumSize(nativePtr, nativeSize.width, nativeSize.height));
}
void showWindowMenu(int x, int y) {
int xNative = javaUnitsToSurfaceUnits(x);
@@ -1023,6 +1033,8 @@ public class WLComponentPeer implements ComponentPeer {
private native void nativeRequestFullScreen(long ptr, int wlID);
private native void nativeRequestUnsetFullScreen(long ptr);
private native void nativeSetSurfaceSize(long ptr, int width, int height);
private native void nativeSetOpaqueRegion(long ptr, int x, int y, int width, int height);
private native void nativeSetWindowGeometry(long ptr, int x, int y, int width, int height);
private native void nativeSetMinimumSize(long ptr, int width, int height);
private native void nativeSetMaximumSize(long ptr, int width, int height);

View File

@@ -47,6 +47,7 @@ public abstract class WLDecoratedPeer extends WLWindowPeer {
public abstract boolean isResizable();
public abstract boolean isInteractivelyResizable();
public abstract boolean isFrameStateSupported(int state);
public abstract void setState(int newState);
public abstract int getState();
public abstract void setExtendedState(int newState);

View File

@@ -59,9 +59,14 @@ public class WLDialogPeer extends WLDecoratedPeer implements DialogPeer {
return ((Dialog)target).getTitle();
}
@Override
public boolean isFrameStateSupported(int state) {
return state == Frame.NORMAL;
}
@Override
public void setState(int newState) {
throw new UnsupportedOperationException();
// Ignored
}
@Override
@@ -71,6 +76,6 @@ public class WLDialogPeer extends WLDecoratedPeer implements DialogPeer {
@Override
public void setExtendedState(int newState) {
throw new UnsupportedOperationException();
// Ignored
}
}

View File

@@ -33,19 +33,28 @@ import java.util.function.Supplier;
public class WLFrameDecoration {
private static final int HEIGHT = 30;
private static final int BUTTON_ICON_SIZE = 3;
private static final int BUTTON_CIRCLE_SIZE = 9;
private static final int BUTTON_MAXIMIZED_LINE_GAP = 2;
private static final int BUTTON_ICON_SIZE = 4;
private static final int BUTTON_CIRCLE_SIZE = 10;
private static final Font FONT = new Font(Font.DIALOG, Font.BOLD, 12);
private static final Color BACKGROUND = Color.white;
private static final Color ICON_BACKGROUND = new Color(0xe8e8e8);
private static final Color ICON_HOVERED_BACKGROUND = new Color(0xe0e0e0);
private static final Color ICON_PRESSED_BACKGROUND = Color.lightGray;
private static final Color ACTIVE_BACKGROUND = new Color(0xebebeb);
private static final Color ACTIVE_BACKGROUND_DARK = new Color(0x222222);
private static final Color INACTIVE_BACKGROUND = new Color(0xfafafa);
private static final Color INACTIVE_BACKGROUND_DARK = new Color(0x2c2c2c);
private static final Color ICON_BACKGROUND = ACTIVE_BACKGROUND;
private static final Color ICON_BACKGROUND_DARK = ACTIVE_BACKGROUND_DARK;
private static final Color ICON_HOVERED_BACKGROUND = new Color(0xd1d1d1);
private static final Color ICON_HOVERED_BACKGROUND_DARK = new Color(0x373737);
private static final Color ICON_PRESSED_BACKGROUND = new Color(0xc0c0c0);
private static final Color ICON_PRESSED_BACKGROUND_DARK = new Color(0x565656);
private static final Color ACTIVE_FOREGROUND = Color.darkGray;
private static final Color ACTIVE_FOREGROUND_DARK = new Color(0xf7f7f7);
private static final Color INACTIVE_FOREGROUND = Color.gray;
private static final Color INACTIVE_FOREGROUND_DARK = new Color(0xb5b5b5);
private static final int SIGNIFICANT_DRAG_DISTANCE = 4;
private static final int RESIZE_EDGE_THICKNESS = 5;
private static volatile boolean isDarkTheme = false;
private static final int XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1;
private static final int XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2;
private static final int XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4;
@@ -144,6 +153,43 @@ public class WLFrameDecoration {
return numButtons * HEIGHT;
}
private static boolean isDarkTheme() {
return isDarkTheme;
}
private static void updateTheme() {
Boolean isDark = (Boolean) Toolkit.getDefaultToolkit().getDesktopProperty("awt.os.theme.isDark");
isDarkTheme = isDark != null && isDark;
}
private static Color getBackgroundColor(boolean isActive) {
if (isActive) {
return isDarkTheme() ? ACTIVE_BACKGROUND_DARK : ACTIVE_BACKGROUND;
} else {
return isDarkTheme() ? INACTIVE_BACKGROUND_DARK : INACTIVE_BACKGROUND;
}
}
private static Color getIconBackground() {
return isDarkTheme() ? ICON_BACKGROUND_DARK : ICON_BACKGROUND;
}
private static Color getIconHoveredBackground() {
return isDarkTheme() ? ICON_HOVERED_BACKGROUND_DARK : ICON_HOVERED_BACKGROUND;
}
private static Color getIconPressedBackground() {
return isDarkTheme() ? ICON_PRESSED_BACKGROUND_DARK : ICON_PRESSED_BACKGROUND;
}
private static Color getForeground(boolean isActive) {
if (isActive) {
return isDarkTheme() ? ACTIVE_FOREGROUND_DARK : ACTIVE_FOREGROUND;
} else {
return isDarkTheme() ? INACTIVE_FOREGROUND_DARK : INACTIVE_FOREGROUND;
}
}
public void paint(final Graphics g) {
if (isUndecorated) return;
@@ -152,6 +198,7 @@ public class WLFrameDecoration {
if (width <= 0 || height <= 0) return;
Graphics2D g2d = (Graphics2D) g.create(0, 0, width, HEIGHT);
try {
updateTheme();
doPaint(g2d);
} finally {
g2d.dispose();
@@ -162,12 +209,12 @@ public class WLFrameDecoration {
private void doPaint(Graphics2D g) {
int width = peer.getWidth();
String title = peer.getTitle();
Color foregroundColor = active ? ACTIVE_FOREGROUND : INACTIVE_FOREGROUND;
Color foregroundColor = getForeground(active);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(BACKGROUND);
g.setColor(getBackgroundColor(active));
g.fillRect(0, 0, width, HEIGHT);
paintTitle(g, title, foregroundColor, width);
@@ -204,8 +251,8 @@ public class WLFrameDecoration {
private void paintButtonBackground(Graphics2D g, Point center, ButtonState state) {
if (active) {
g.setColor(state.pressed ? ICON_PRESSED_BACKGROUND :
state.hovered ? ICON_HOVERED_BACKGROUND : ICON_BACKGROUND);
g.setColor(state.pressed ? getIconPressedBackground() :
state.hovered ? getIconHoveredBackground() : getIconBackground());
g.fill(new Ellipse2D.Float(center.x - BUTTON_CIRCLE_SIZE + .5f,
center.y - BUTTON_CIRCLE_SIZE + .5f,
2 * BUTTON_CIRCLE_SIZE, 2 * BUTTON_CIRCLE_SIZE));
@@ -223,22 +270,28 @@ public class WLFrameDecoration {
private void paintMaximizeButton(Graphics2D g, Point center, Color foregroundColor) {
g.setColor(foregroundColor);
if (peer.getState() == Frame.MAXIMIZED_BOTH) {
g.drawRect(center.x - BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE + BUTTON_MAXIMIZED_LINE_GAP,
2 * BUTTON_ICON_SIZE - BUTTON_MAXIMIZED_LINE_GAP, 2 * BUTTON_ICON_SIZE - BUTTON_MAXIMIZED_LINE_GAP);
g.drawLine(center.x - BUTTON_ICON_SIZE + BUTTON_MAXIMIZED_LINE_GAP, center.y - BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE);
g.drawLine(center.x + BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE - BUTTON_MAXIMIZED_LINE_GAP);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y,
center.x, center.y - BUTTON_ICON_SIZE);
g.drawLine(center.x, center.y - BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y,
center.x, center.y + BUTTON_ICON_SIZE);
g.drawLine(center.x, center.y + BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y);
} else {
g.drawRect(center.x - BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE,
2 * BUTTON_ICON_SIZE, 2 * BUTTON_ICON_SIZE);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE / 2,
center.x, center.y - BUTTON_ICON_SIZE / 2);
g.drawLine(center.x, center.y - BUTTON_ICON_SIZE / 2,
center.x + BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE / 2);
}
}
private void paintMinimizeButton(Graphics2D g, Point center, Color foregroundColor) {
g.setColor(foregroundColor);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE / 2,
center.x, center.y + BUTTON_ICON_SIZE / 2);
g.drawLine(center.x, center.y + BUTTON_ICON_SIZE / 2,
center.x + BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE / 2);
}
@SuppressWarnings("deprecation")
@@ -320,7 +373,7 @@ public class WLFrameDecoration {
} else if (e.getID() == MouseEvent.MOUSE_DRAGGED && pressedInDragStartArea() && isSignificantDrag(point)) {
peer.startDrag();
} else if (e.getID() == MouseEvent.MOUSE_CLICKED && e.getClickCount() == 2 && pressedInDragStartArea()
&& peer.isResizable()) {
&& peer.isFrameStateSupported(Frame.MAXIMIZED_BOTH)) {
toggleMaximizedState();
} else if (e.getID() == MouseEvent.MOUSE_MOVED && !pointerInside) {
peer.updateCursorImmediately();

View File

@@ -83,6 +83,15 @@ public class WLFramePeer extends WLDecoratedPeer implements FramePeer {
return getFrame().getTitle();
}
@Override
public boolean isFrameStateSupported(int state) {
return switch (state) {
case Frame.NORMAL, Frame.ICONIFIED, Frame.MAXIMIZED_BOTH, Frame.MAXIMIZED_HORIZ, Frame.MAXIMIZED_VERT ->
true;
default -> false;
};
}
@Override
public void setState(int newState) {
if (!isVisible()) return;

View File

@@ -162,6 +162,11 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment {
width, height, widthMm, heightMm, scale);
devices.add(gd);
}
if (LogDisplay.ENABLED) {
double effectiveScale = effectiveScaleFrom(scale);
LogDisplay log = newOutput ? LogDisplay.ADDED : LogDisplay.CHANGED;
log.log(wlID, (int) (width / effectiveScale) + "x" + (int) (height / effectiveScale), effectiveScale);
}
}
updateTotalDisplayBounds();
@@ -201,6 +206,11 @@ public class WLGraphicsEnvironment extends SunGraphicsEnvironment {
.findFirst();
if (deviceOptional.isPresent()) {
final WLGraphicsDevice destroyedDevice = deviceOptional.get();
if (LogDisplay.ENABLED) {
WLGraphicsConfig config = (WLGraphicsConfig) destroyedDevice.getDefaultConfiguration();
Rectangle bounds = config.getBounds();
LogDisplay.REMOVED.log(wlID, bounds.width + "x" + bounds.height, config.getEffectiveScale());
}
devices.remove(destroyedDevice);
final WLGraphicsDevice similarDevice = getSimilarDevice(destroyedDevice);
if (similarDevice != null) destroyedDevice.invalidate(similarDevice);

View File

@@ -89,13 +89,6 @@ public class WLWindowPeer extends WLComponentPeer implements WindowPeer {
}
}
@Override
void configureWLSurface() {
super.configureWLSurface();
updateMinimumSize();
updateMaximumSize();
}
@Override
public Insets getInsets() {
return new Insets(0, 0, 0, 0);
@@ -138,21 +131,7 @@ public class WLWindowPeer extends WLComponentPeer implements WindowPeer {
@Override
public void updateMinimumSize() {
final Dimension minSize = getMinimumSize();
super.setMinimumSizeTo(minSize);
}
public void updateMaximumSize() {
// TODO: make sure this is called when our target's maximum size changes
final Dimension maxSize = target.isMaximumSizeSet() ? target.getMaximumSize() : null;
if (maxSize != null) super.setMaximumSizeTo(maxSize);
}
@Override
void updateSurfaceData() {
updateMinimumSize();
updateMaximumSize();
super.updateSurfaceData();
// No op, it gets updated at each resize
}
@Override

View File

@@ -101,7 +101,7 @@ public abstract class WLVKSurfaceData extends VKSurfaceData implements WLSurface
}
public SurfaceData getReplacement() {
throw new UnsupportedOperationException("Not supported yet");
return null;
}
@Override

View File

@@ -26,11 +26,14 @@
package sun.java2d.wl;
import java.awt.Component;
import java.awt.Window;
import java.awt.GraphicsConfiguration;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import sun.awt.AWTAccessor;
import sun.awt.wl.WLComponentPeer;
import sun.awt.wl.WLSMGraphicsConfig;
import sun.java2d.SurfaceData;
@@ -46,9 +49,14 @@ public class WLSMSurfaceData extends SurfaceData implements WLSurfaceDataExt {
public native void assignSurface(long surfacePtr);
private native void initOps(int width, int height, int scale, int backgroundRGB, int wlShmFormat);
private native void initOps(int width, int height, int backgroundRGB, int wlShmFormat, boolean perfCountersEnabled);
private static native void initIDs();
private WLSMSurfaceData(WLComponentPeer peer, SurfaceType surfaceType, ColorModel colorModel, int scale, int wlShmFormat) {
static {
initIDs();
}
private WLSMSurfaceData(WLComponentPeer peer, SurfaceType surfaceType, ColorModel colorModel, int wlShmFormat, boolean perfCountersEnabled) {
super(surfaceType, colorModel);
this.peer = peer;
@@ -60,10 +68,9 @@ public class WLSMSurfaceData extends SurfaceData implements WLSurfaceDataExt {
int height = peer.getBufferHeight();
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine(String.format("Shared memory surface data %dx%d x%d scale, format %d", width, height, scale, wlShmFormat));
log.fine(String.format("Shared memory surface data %dx%d, format %d", width, height, wlShmFormat));
}
initOps(width, height, scale, backgroundPixel, wlShmFormat);
initOps(width, height, backgroundPixel, wlShmFormat, perfCountersEnabled);
}
/**
@@ -75,7 +82,9 @@ public class WLSMSurfaceData extends SurfaceData implements WLSurfaceDataExt {
}
ColorModel cm = graphicsConfig.getColorModel();
SurfaceType surfaceType = graphicsConfig.getSurfaceType();
return new WLSMSurfaceData(peer, surfaceType, cm, graphicsConfig.getWlScale(), graphicsConfig.getWlShmFormat());
Window target = peer.getTarget() instanceof Window ? (Window)peer.getTarget() : null;
boolean perfCountersEnabled = target != null && AWTAccessor.getWindowAccessor().countersEnabled(target);
return new WLSMSurfaceData(peer, surfaceType, cm, graphicsConfig.getWlShmFormat(), perfCountersEnabled);
}
@Override
@@ -141,6 +150,30 @@ public class WLSMSurfaceData extends SurfaceData implements WLSurfaceDataExt {
return pixels;
}
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();
}
private void countNewFrame() {
// Called from the native code when this surface data has been sent to the Wayland server
Component target = peer.getTarget();
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.frames");
}
}
private void countDroppedFrame() {
// Called from the native code when an attempt was made to send this surface data to
// the Wayland server, but that attempt was not successful. This can happen, for example,
// when those attempts are too frequent.
Component target = peer.getTarget();
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.framesDropped");
}
}
private native int pixelAt(int x, int y);
private native int [] pixelsAt(int x, int y, int width, int height);
}

View File

@@ -26,25 +26,19 @@
package sun.java2d.wl;
import java.awt.Component;
import java.awt.GraphicsConfiguration;
import java.awt.ImageCapabilities;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.awt.geom.AffineTransform;
import java.awt.image.VolatileImage;
import java.util.Objects;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.SurfaceData;
public class WLVolatileSurfaceManager extends VolatileSurfaceManager implements PropertyChangeListener {
private static final String SCALE_PROPERTY_NAME = "graphicsContextScaleTransform";
public class WLVolatileSurfaceManager extends VolatileSurfaceManager {
public WLVolatileSurfaceManager(SunVolatileImage vImg, Object context) {
super(vImg, context);
Component component = vImg.getComponent();
if (component != null) {
component.addPropertyChangeListener(SCALE_PROPERTY_NAME, this);
}
}
protected boolean isAccelerationEnabled() {
@@ -63,9 +57,12 @@ public class WLVolatileSurfaceManager extends VolatileSurfaceManager implements
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
assert SCALE_PROPERTY_NAME.equals(evt.getPropertyName());
displayChanged();
public int validate(GraphicsConfiguration gc) {
AffineTransform newTx = gc.getDefaultTransform();
if (!Objects.equals(atCurrent, newTx)) {
// May need a different size on another display
return VolatileImage.IMAGE_INCOMPATIBLE;
}
return super.validate(gc);
}
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,93 +24,114 @@
* questions.
*/
#include <wayland-client.h>
#include <stdlib.h>
#include <string.h>
#include "jni.h"
#include "WLVKSurfaceData.h"
#include <jni_util.h>
#include <Trace.h>
#include <SurfaceData.h>
#include "VKSurfaceData.h"
#include "VKBase.h"
#include <jni_util.h>
#include <cstdlib>
#include <string>
#include "VKSurfaceData.h"
#include "WLVKSurfaceData.h"
extern struct wl_display *wl_display;
extern "C" JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_initOps
(JNIEnv *env, jobject vksd, jint width, jint height, jint scale, jint backgroundRGB) {
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_initOps
(JNIEnv *env, jobject vksd, jint width, jint height, jint scale, jint backgroundRGB) {
#ifndef HEADLESS
J2dTrace3(J2D_TRACE_INFO, "Create WLVKSurfaceData with size %d x %d and scale %d\n", width, height, scale);
width /= scale; // TODO This is incorrect, but we'll deal with this later, we probably need to do something on Wayland side for app-controlled scaling
height /= scale; // TODO This is incorrect, but we'll deal with this later, we probably need to do something on Wayland side for app-controlled scaling
auto *sd = new WLVKSurfaceData(width, height, scale, backgroundRGB);
sd->attachToJavaSurface(env, vksd);
#endif /* !HEADLESS */
}
VKWinSDOps *vkwinsdo = (VKWinSDOps *)SurfaceData_InitOps(env, vksd, sizeof(VKWinSDOps));
vkwinsdo->vksdOps.drawableType = VKSD_WINDOW;
extern "C" JNIEXPORT void JNICALL
Java_sun_java2d_vulkan_WLVKSurfaceData_assignSurface(JNIEnv *env, jobject wsd, jlong wlSurfacePtr)
{
#ifndef HEADLESS
auto sd = (WLVKSurfaceData*)SurfaceData_GetOps(env, wsd);
if (sd == nullptr) {
if (vkwinsdo == NULL) {
JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
return;
}
auto wlSurface = (struct wl_surface*)jlong_to_ptr(wlSurfacePtr);
J2dTraceLn2(J2D_TRACE_INFO, "WLVKSurfaceData_assignSurface wl_surface(%p) wl_display(%p)",
wlSurface, wl_display);
WLVKSDOps *wlvksdo = (WLVKSDOps *)malloc(sizeof(WLVKSDOps));
try {
sd->validate(wlSurface);
} catch (std::exception& e) {
J2dRlsTrace1(J2D_TRACE_ERROR, "WLVKSurfaceData_assignSurface: %s\n", e.what());
if (wlvksdo == NULL) {
JNU_ThrowOutOfMemoryError(env, "creating native WLVK ops");
return;
}
vkwinsdo->privOps = wlvksdo;
wlvksdo->wl_surface = NULL;
width /= scale; // TODO This is incorrect, but we'll deal with this later, we probably need to do something on Wayland side for app-controlled scaling
height /= scale; // TODO This is incorrect, but we'll deal with this later, we probably need to do something on Wayland side for app-controlled scaling
vkwinsdo->vksdOps.width = width;
vkwinsdo->vksdOps.height = height;
vkwinsdo->vksdOps.scale = scale;
vkwinsdo->vksdOps.bgColor = backgroundRGB;
vkwinsdo->vksdOps.bgColorUpdated = VK_TRUE;
#endif /* !HEADLESS */
}
extern "C" JNIEXPORT void JNICALL
JNIEXPORT void JNICALL
Java_sun_java2d_vulkan_WLVKSurfaceData_assignSurface(JNIEnv *env, jobject wsd, jlong wlSurfacePtr)
{
#ifndef HEADLESS
VKWinSDOps* vkwinsdo = (VKWinSDOps *)SurfaceData_GetOps(env, wsd);
if (vkwinsdo == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "WLVKSurfaceData_assignSurface: VKSDOps is NULL");
return;
}
WLVKSDOps* wlvksdo = (WLVKSDOps *)vkwinsdo->privOps;
if (wlvksdo == NULL) {
J2dRlsTrace(J2D_TRACE_ERROR, "WLVKSurfaceData_assignSurface: WLVKSDOps is NULL");
return;
}
VKGraphicsEnvironment* ge = VKGE_graphics_environment();
wlvksdo->wl_surface = (struct wl_surface*)jlong_to_ptr(wlSurfacePtr);
if (vkwinsdo->surface == VK_NULL_HANDLE) {
VkWaylandSurfaceCreateInfoKHR surfaceCreateInfo = {};
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
surfaceCreateInfo.display = wl_display;
surfaceCreateInfo.surface = wlvksdo->wl_surface;
if (ge->vkCreateWaylandSurfaceKHR(ge->vkInstance,
&surfaceCreateInfo,
NULL,
&vkwinsdo->surface) != VK_SUCCESS) {
J2dRlsTrace(J2D_TRACE_ERROR, "WLVKSurfaceData_assignSurface: WLVKSDOps is NULL");
return;
}
}
VKSD_InitImageSurface(&vkwinsdo->vksdOps);
VKSD_InitWindowSurface(vkwinsdo);
J2dRlsTraceLn(J2D_TRACE_INFO, "WLVKSurfaceData_assignSurface: Created WaylandSurfaceKHR");
J2dTraceLn2(J2D_TRACE_INFO, "WLVKSurfaceData_assignSurface wl_surface(%p) wl_display(%p)", wlSurface, wl_display);
#endif /* !HEADLESS */
}
JNIEXPORT void JNICALL
Java_sun_java2d_vulkan_WLVKSurfaceData_flush(JNIEnv *env, jobject wsd)
{
#ifndef HEADLESS
J2dTrace(J2D_TRACE_INFO, "WLVKSurfaceData_flush\n");
// TODO?
// TODO?
#endif /* !HEADLESS */
}
extern "C" JNIEXPORT void JNICALL
JNIEXPORT void JNICALL
Java_sun_java2d_vulkan_WLVKSurfaceData_revalidate(JNIEnv *env, jobject wsd,
jint width, jint height, jint scale)
jint width, jint height, jint scale)
{
width /= scale; // TODO This is incorrect, but we'll deal with this later, we probably need to do something on Wayland side for app-controlled scaling
height /= scale; // TODO This is incorrect, but we'll deal with this later, we probably need to do something on Wayland side for app-controlled scaling
#ifndef HEADLESS
auto sd = (WLVKSurfaceData*)SurfaceData_GetOps(env, wsd);
if (sd == nullptr) {
VKSDOps* vksdo = (VKSDOps*)SurfaceData_GetOps(env, wsd);
if (vksdo == NULL) {
return;
}
J2dTrace3(J2D_TRACE_INFO, "WLVKSurfaceData_revalidate to size %d x %d and scale %d\n", width, height, scale);
try {
sd->revalidate(width, height, scale);
} catch (std::exception& e) {
J2dRlsTrace1(J2D_TRACE_ERROR, "WLVKSurfaceData_revalidate: %s\n", e.what());
}
vksdo->width = width;
vksdo->height = height;
vksdo->scale = scale;
#endif /* !HEADLESS */
}
void WLVKSurfaceData::validate(wl_surface* wls)
{
if (wls ==_wl_surface) {
return;
}
auto& device = VKGraphicsEnvironment::graphics_environment()->default_device();
device.waitIdle(); // TODO wait until device is done with old swapchain
auto surface = VKGraphicsEnvironment::graphics_environment()->vk_instance()
.createWaylandSurfaceKHR({{}, wl_display, wls});
_wl_surface = wls;
reset(device, std::move(surface));
revalidate(width(), height(), scale());
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,18 +27,17 @@
#ifndef WLVKSurfaceData_h_Included
#define WLVKSurfaceData_h_Included
#include "awt_p.h"
#include "VKSurfaceData.h"
#ifndef HEADLESS
#ifdef HEADLESS
#define WLVKSDOps void
#else /* HEADLESS */
class WLVKSurfaceData : public VKSwapchainSurfaceData {
wl_surface* _wl_surface;
public:
WLVKSurfaceData(uint32_t w, uint32_t h, uint32_t s, uint32_t bgc)
: VKSwapchainSurfaceData(w, h, s, bgc), _wl_surface(nullptr) {}
void validate(wl_surface* wls);
};
typedef struct _WLVKSDOps {
struct wl_surface* wl_surface;
} WLVKSDOps;
#endif /* HEADLESS */
#endif /* WLVKSurfaceData_h_Included */
#endif /* WLVKSurfaceData_h_Included */

View File

@@ -73,6 +73,8 @@ AssertDrawLockIsHeld(WLSurfaceBufferManager* manager, const char * file, int lin
#define MUTEX_LOCK(m) if (pthread_mutex_lock(&(m))) { WL_FATAL_ERROR("Failed to lock mutex"); }
#define MUTEX_UNLOCK(m) if (pthread_mutex_unlock(&(m))) { WL_FATAL_ERROR("Failed to unlock mutex"); }
#define CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val)))
/**
* The maximum number of buffers that can be simultaneously in use by Wayland.
* When a new frame is ready to be sent to Wayland and the number of buffers
@@ -88,7 +90,6 @@ AssertDrawLockIsHeld(WLSurfaceBufferManager* manager, const char * file, int lin
const int MAX_BUFFERS_IN_USE = 2;
static bool traceEnabled; // set the J2D_STATS env var to enable
static bool traceFPSEnabled; // set the J2D_FPS env var to enable
/**
* Represents one rectangular area linked into a list.
@@ -269,9 +270,6 @@ struct WLSurfaceBufferManager {
/// Does not exceed MAX_BUFFERS_IN_USE elements.
WLSurfaceBuffer * buffersInUse; // only accessed under showLock
/// The scale of wl_surface (see Wayland docs for more info on that).
jint scale; // only accessed under showLock
pthread_mutex_t drawLock;
/**
@@ -280,6 +278,11 @@ struct WLSurfaceBufferManager {
* on the screen.
*/
WLDrawBuffer bufferForDraw; // only accessed under drawLock
jobject surfaceDataWeakRef;
BufferEventCallback frameSentCallback;
BufferEventCallback frameDroppedCallback;
BufferEventCallback bufferAttachedCallback;
};
static inline void
@@ -304,7 +307,7 @@ AssertShowLockIsHeld(WLSurfaceBufferManager* manager, const char * file, int lin
static jlong
GetJavaTimeNanos(void) {
jlong result = 0;
if (traceEnabled || traceFPSEnabled) {
if (traceEnabled) {
struct timespec tp;
const jlong NANOSECS_PER_SEC = 1000000000L;
int status = clock_gettime(CLOCK_MONOTONIC, &tp);
@@ -330,24 +333,6 @@ WLBufferTrace(WLSurfaceBufferManager* manager, const char *fmt, ...)
}
}
static void
WLBufferTraceFrame(WLSurfaceBufferManager* manager)
{
if (traceFPSEnabled) {
static jlong lastFrameTime = 0;
static int frameCount = 0;
jlong curTime = GetJavaTimeNanos();
frameCount++;
if (curTime - lastFrameTime > 1000000000L) {
fprintf(stderr, "FPS: %d\n", frameCount);
fflush(stderr);
lastFrameTime = curTime;
frameCount = 0;
}
}
}
static inline size_t
SurfaceBufferSizeInBytes(WLSurfaceBuffer * buffer)
{
@@ -426,16 +411,14 @@ SurfaceBufferDestroy(WLSurfaceBuffer * buffer)
static WLSurfaceBuffer *
SurfaceBufferCreate(WLSurfaceBufferManager * manager)
{
ASSERT_DRAW_LOCK_IS_HELD(manager);
WLBufferTrace(manager, "SurfaceBufferCreate");
WLSurfaceBuffer * buffer = calloc(1, sizeof(WLSurfaceBuffer));
if (!buffer) return NULL;
MUTEX_LOCK(manager->drawLock);
buffer->width = manager->bufferForDraw.width;
buffer->height = manager->bufferForDraw.height;
MUTEX_UNLOCK(manager->drawLock);
buffer->bytesAllocated = SurfaceBufferSizeInBytes(buffer);
buffer->wlPool = CreateShmPool(buffer->bytesAllocated, "jwlshm", (void**)&buffer->data, &buffer->fd);
if (! buffer->wlPool) {
@@ -467,32 +450,21 @@ SurfaceBufferCreate(WLSurfaceBufferManager * manager)
return buffer;
}
static bool
SurfaceBufferNeedsResize(WLSurfaceBufferManager * manager, WLSurfaceBuffer* buffer)
{
assert(buffer != NULL);
MUTEX_LOCK(manager->drawLock);
jint newWidth = manager->bufferForDraw.width;
jint newHeight = manager->bufferForDraw.height;
MUTEX_UNLOCK(manager->drawLock);
return newWidth != buffer->width || newHeight != buffer->height;
}
static bool
SurfaceBufferResize(WLSurfaceBufferManager * manager, WLSurfaceBuffer* buffer)
{
ASSERT_DRAW_LOCK_IS_HELD(manager);
assert(buffer != NULL);
assert(buffer->wlBuffer != NULL);
MUTEX_LOCK(manager->drawLock);
jint newWidth = manager->bufferForDraw.width;
jint newHeight = manager->bufferForDraw.height;
MUTEX_UNLOCK(manager->drawLock);
wl_buffer_destroy(buffer->wlBuffer);
buffer->wlBuffer = NULL;
DamageList_FreeAll(buffer->damageList);
buffer->damageList = NULL;
buffer->width = newWidth;
buffer->height = newHeight;
@@ -584,6 +556,8 @@ SurfaceBufferNotifyReleased(WLSurfaceBufferManager * manager, struct wl_buffer *
static void
ShowBufferChooseFromFree(WLSurfaceBufferManager * manager)
{
ASSERT_SHOW_LOCK_IS_HELD(manager);
assert(manager->buffersFree != NULL);
manager->bufferForShow.wlSurfaceBuffer = manager->buffersFree;
@@ -594,12 +568,20 @@ ShowBufferChooseFromFree(WLSurfaceBufferManager * manager)
static bool
ShowBufferNeedsResize(WLSurfaceBufferManager * manager)
{
return SurfaceBufferNeedsResize(manager, manager->bufferForShow.wlSurfaceBuffer);
ASSERT_SHOW_LOCK_IS_HELD(manager);
ASSERT_DRAW_LOCK_IS_HELD(manager);
jint newWidth = manager->bufferForDraw.width;
jint newHeight = manager->bufferForDraw.height;
return newWidth != manager->bufferForShow.wlSurfaceBuffer->width || newHeight != manager->bufferForShow.wlSurfaceBuffer->height;
}
static bool
ShowBufferResize(WLSurfaceBufferManager * manager)
{
ASSERT_SHOW_LOCK_IS_HELD(manager);
ASSERT_DRAW_LOCK_IS_HELD(manager);
if (!SurfaceBufferResize(manager, manager->bufferForShow.wlSurfaceBuffer)) {
SurfaceBufferDestroy(manager->bufferForShow.wlSurfaceBuffer);
manager->bufferForShow.wlSurfaceBuffer = NULL;
@@ -614,6 +596,8 @@ static bool
ShowBufferCreate(WLSurfaceBufferManager * manager)
{
ASSERT_SHOW_LOCK_IS_HELD(manager);
ASSERT_DRAW_LOCK_IS_HELD(manager);
assert(manager->bufferForShow.wlSurfaceBuffer == NULL);
WLSurfaceBuffer* buffer = SurfaceBufferCreate(manager);
@@ -630,6 +614,7 @@ static bool
ShowBufferIsAvailable(WLSurfaceBufferManager * manager)
{
ASSERT_SHOW_LOCK_IS_HELD(manager);
ASSERT_DRAW_LOCK_IS_HELD(manager);
// Skip sending the next frame if the number of buffers that
// had been sent to Wayland for displaying earlier is too large.
@@ -660,19 +645,35 @@ ShowBufferIsAvailable(WLSurfaceBufferManager * manager)
return false; // failed to resize, likely due to OOM
}
}
} else {
if (manager->frameDroppedCallback != NULL) {
manager->frameDroppedCallback(manager->surfaceDataWeakRef);
}
}
return canSendMoreBuffers;
}
static void
TrySendShowBufferToWayland(WLSurfaceBufferManager * manager, bool sendNow)
static bool
TryCopyDrawBufferToShowBuffer(WLSurfaceBufferManager * manager, bool sendNow)
{
WLBufferTrace(manager, "TrySendShowBufferToWayland(%s)", sendNow ? "now" : "later");
MUTEX_LOCK(manager->drawLock); // So that the size doesn't change while we copy
sendNow = sendNow && ShowBufferIsAvailable(manager);
if (sendNow) {
CopyDrawBufferToShowBuffer(manager);
}
MUTEX_UNLOCK(manager->drawLock);
return sendNow;
}
static void
TrySendShowBufferToWayland(WLSurfaceBufferManager * manager, bool sendNow)
{
ASSERT_SHOW_LOCK_IS_HELD(manager);
WLBufferTrace(manager, "TrySendShowBufferToWayland(%s)", sendNow ? "now" : "later");
if (TryCopyDrawBufferToShowBuffer(manager, sendNow)) {
SendShowBufferToWayland(manager);
} else {
ScheduleFrameCallback(manager);
@@ -767,7 +768,8 @@ SendShowBufferToWayland(WLSurfaceBufferManager * manager)
// wl_buffer_listener will release bufferForShow when Wayland's done with it
wl_surface_attach(manager->wlSurface, buffer->wlBuffer, 0, 0);
wl_surface_set_buffer_scale(manager->wlSurface, manager->scale);
//NB: do not specify scale for the buffer; we scale with wp_viewporter
manager->bufferAttachedCallback(manager->surfaceDataWeakRef);
// Better wait for the frame event so as not to overwhelm Wayland with
// frequent surface updates that it cannot deliver to the screen anyway.
@@ -785,32 +787,40 @@ SendShowBufferToWayland(WLSurfaceBufferManager * manager)
jlong endTime = GetJavaTimeNanos();
WLBufferTrace(manager, "SendShowBufferToWayland (%lldns)", endTime - startTime);
WLBufferTraceFrame(manager);
if (manager->frameSentCallback != NULL) {
manager->frameSentCallback(manager->surfaceDataWeakRef);
}
}
static void
CopyDamagedArea(WLSurfaceBufferManager * manager, jint x, jint y, jint width, jint height)
{
ASSERT_SHOW_LOCK_IS_HELD(manager);
ASSERT_DRAW_LOCK_IS_HELD(manager);
assert(manager->bufferForShow.wlSurfaceBuffer != NULL);
assert(manager->bufferForShow.wlSurfaceBuffer->data != NULL);
assert(manager->bufferForDraw.data != NULL);
assert(manager->bufferForDraw.width == manager->bufferForShow.wlSurfaceBuffer->width);
assert(manager->bufferForDraw.height == manager->bufferForShow.wlSurfaceBuffer->height);
assert(x >= 0);
assert(y >= 0);
assert(width >= 0);
assert(height >= 0);
assert(height + y >= 0);
assert(width + x >= 0);
assert(width <= manager->bufferForDraw.width);
assert(height <= manager->bufferForDraw.height);
assert(manager->bufferForShow.wlSurfaceBuffer->bytesAllocated >= DrawBufferSizeInBytes(manager));
jint bufferWidth = manager->bufferForDraw.width;
jint bufferHeight = manager->bufferForDraw.height;
// Clamp the damaged area to the size of the destination in order not to crash in case of
// an error on the client side that "damaged" an area larger than our buffer.
x = CLAMP(x, 0, bufferWidth - 1);
y = CLAMP(y, 0, bufferHeight - 1);
width = CLAMP(width, 0, bufferWidth - x);
height = CLAMP(height, 0, bufferHeight - y);
pixel_t * dest = manager->bufferForShow.wlSurfaceBuffer->data;
pixel_t * src = manager->bufferForDraw.data;
for (jint i = y; i < height + y; i++) {
pixel_t * dest_row = &dest[i * manager->bufferForDraw.width];
pixel_t * src_row = &src [i * manager->bufferForDraw.width];
pixel_t * dest_row = &dest[i * bufferWidth];
pixel_t * src_row = &src [i * bufferWidth];
for (jint j = x; j < width + x; j++) {
dest_row[j] = src_row[j];
}
@@ -831,9 +841,11 @@ static void
CopyDrawBufferToShowBuffer(WLSurfaceBufferManager * manager)
{
ASSERT_SHOW_LOCK_IS_HELD(manager);
MUTEX_LOCK(manager->drawLock);
ASSERT_DRAW_LOCK_IS_HELD(manager);
if (manager->bufferForShow.wlSurfaceBuffer == NULL || manager->bufferForDraw.data == NULL) {
if (manager->bufferForShow.wlSurfaceBuffer == NULL
|| manager->bufferForDraw.data == NULL
|| manager->bufferForDraw.resizePending) {
return;
}
@@ -874,8 +886,6 @@ CopyDrawBufferToShowBuffer(WLSurfaceBufferManager * manager)
jlong endTime = GetJavaTimeNanos();
WLBufferTrace(manager, "CopyDrawBufferToShowBuffer: copied %d area(s) in %lldns", count, endTime - startTime);
MUTEX_UNLOCK(manager->drawLock);
}
static void
@@ -934,10 +944,19 @@ HaveEnoughMemoryForWindow(jint width, jint height)
}
WLSurfaceBufferManager *
WLSBM_Create(jint width, jint height, jint scale, jint bgPixel, jint wlShmFormat)
WLSBM_Create(jint width,
jint height,
jint bgPixel,
jint wlShmFormat,
jobject surfaceDataWeakRef,
BufferEventCallback frameSentCallback,
BufferEventCallback frameDroppedCallback,
BufferEventCallback bufferAttachedCallback)
{
assert (surfaceDataWeakRef != NULL);
assert (bufferAttachedCallback != NULL);
traceEnabled = getenv("J2D_STATS");
traceFPSEnabled = getenv("J2D_FPS");
if (!HaveEnoughMemoryForWindow(width, height)) {
return NULL;
@@ -950,9 +969,12 @@ WLSBM_Create(jint width, jint height, jint scale, jint bgPixel, jint wlShmFormat
manager->bufferForDraw.width = width;
manager->bufferForDraw.height = height;
manager->scale = scale;
manager->bgPixel = bgPixel;
manager->format = wlShmFormat;
manager->surfaceDataWeakRef = surfaceDataWeakRef;
manager->frameSentCallback = frameSentCallback;
manager->frameDroppedCallback = frameDroppedCallback;
manager->bufferAttachedCallback = bufferAttachedCallback;
pthread_mutex_init(&manager->showLock, NULL);
@@ -996,6 +1018,9 @@ WLSBM_Destroy(WLSurfaceBufferManager * manager)
{
J2dTrace1(J2D_TRACE_INFO, "WLSBM_Destroy: manger %p\n", manager);
JNIEnv* env = getEnv();
(*env)->DeleteWeakGlobalRef(env, manager->surfaceDataWeakRef);
// NB: must never be called in parallel with the Wayland event handlers
// because their callbacks retain a pointer to this manager.
MUTEX_LOCK(manager->showLock);
@@ -1047,7 +1072,7 @@ WLDrawBuffer *
WLSBM_BufferAcquireForDrawing(WLSurfaceBufferManager * manager)
{
WLBufferTrace(manager, "WLSBM_BufferAcquireForDrawing(%d)", manager->bufferForDraw.frameID);
MUTEX_LOCK(manager->drawLock);
MUTEX_LOCK(manager->drawLock); // unlocked in WLSBM_BufferReturn()
if (manager->bufferForDraw.resizePending) {
WLBufferTrace(manager, "WLSBM_BufferAcquireForDrawing - creating a new draw buffer because the size has changed");
DrawBufferResize(manager);
@@ -1059,7 +1084,7 @@ void
WLSBM_BufferReturn(WLSurfaceBufferManager * manager, WLDrawBuffer * buffer)
{
if (&manager->bufferForDraw == buffer) {
MUTEX_UNLOCK(buffer->manager->drawLock);
MUTEX_UNLOCK(buffer->manager->drawLock); // locked in WLSBM_BufferAcquireForDrawing()
WLBufferTrace(manager, "WLSBM_BufferReturn(%d)", manager->bufferForDraw.frameID);
} else {
WL_FATAL_ERROR("WLSBM_BufferReturn() called with an unidentified buffer");
@@ -1105,22 +1130,16 @@ WLSB_DataGet(WLDrawBuffer * buffer)
}
void
WLSBM_SizeChangeTo(WLSurfaceBufferManager * manager, jint width, jint height, jint scale)
WLSBM_SizeChangeTo(WLSurfaceBufferManager * manager, jint width, jint height)
{
if (!HaveEnoughMemoryForWindow(width, height)) {
JNU_ThrowOutOfMemoryError(getEnv(), "Wayland surface buffer too large");
return;
}
MUTEX_LOCK(manager->drawLock);
MUTEX_LOCK(manager->showLock);
const bool change_needed =
manager->bufferForDraw.width != width
|| manager->bufferForDraw.height != height
|| manager->scale != scale;
manager->scale = scale;
if (change_needed) {
MUTEX_LOCK(manager->drawLock);
if (manager->bufferForDraw.width != width || manager->bufferForDraw.height != height) {
manager->bufferForDraw.width = width;
manager->bufferForDraw.height = height;
manager->bufferForDraw.resizePending = true;
@@ -1136,8 +1155,8 @@ WLSBM_SizeChangeTo(WLSurfaceBufferManager * manager, jint width, jint height, ji
WLBufferTrace(manager, "WLSBM_SizeChangeTo %dx%d", width, height);
}
MUTEX_UNLOCK(manager->showLock);
MUTEX_UNLOCK(manager->drawLock);
MUTEX_UNLOCK(manager->showLock);
}
#endif

View File

@@ -48,6 +48,9 @@ typedef struct WLDrawBuffer WLDrawBuffer;
*/
typedef uint32_t pixel_t;
typedef void (*BufferEventCallback)(jobject);
/**
* Create a WayLand Surface Buffer Manager for a surface of size width x height
* pixels with the given background 32-bit pixel value and wl_shm_format.
@@ -61,7 +64,11 @@ typedef uint32_t pixel_t;
* the drawing to displaying buffer, synchronization, and sending
* the appropriate notifications to Wayland.
*/
WLSurfaceBufferManager * WLSBM_Create(jint width, jint height, jint scale, jint bgPixel, jint wlShmFormat);
WLSurfaceBufferManager * WLSBM_Create(jint width, jint height, jint bgPixel, jint wlShmFormat,
jobject surfaceDataWeakRef,
BufferEventCallback frameSentCallback,
BufferEventCallback frameDroppedCallback,
BufferEventCallback bufferAttachedCallback);
/**
* Free all resources allocated for the WayLand Surface Buffer Manager,
@@ -108,7 +115,7 @@ jint WLSBM_HeightGet(WLSurfaceBufferManager *);
* displays things will change later, but before the changes in drawing buffer
* are propagated to the display buffer.
*/
void WLSBM_SizeChangeTo(WLSurfaceBufferManager *, jint width, jint height, jint scale);
void WLSBM_SizeChangeTo(WLSurfaceBufferManager *, jint width, jint height);
/**
* Returns a buffer managed by the WayLand Surface Buffer Manager

View File

@@ -36,6 +36,7 @@
#include "Trace.h"
#include "WLSMSurfaceData.h"
#include "WLBuffers.h"
#include "WLToolkit.h"
struct WLSDOps {
SurfaceDataOps sdOps;
@@ -63,6 +64,23 @@ typedef struct WLSDPrivate {
WLDrawBuffer * wlBuffer;
} WLSDPrivate;
static jmethodID countNewFrameMID;
static jmethodID countDroppedFrameMID;
static jmethodID bufferAttachedMID;
JNIEXPORT void JNICALL
Java_sun_java2d_wl_WLSMSurfaceData_initIDs
(JNIEnv *env, jclass clazz)
{
// NB: don't care if those "count" methods are found.
countNewFrameMID = (*env)->GetMethodID(env, clazz, "countNewFrame", "()V");
countDroppedFrameMID = (*env)->GetMethodID(env, clazz, "countDroppedFrame", "()V");
CHECK_NULL_THROW_IE(env,
bufferAttachedMID = (*env)->GetMethodID(env, clazz, "bufferAttached", "()V"),
"Failed to find method WLSMSurfaceData.bufferAttached"
);
}
JNIEXPORT WLSDOps * JNICALL
WLSMSurfaceData_GetOps(JNIEnv *env, jobject sData)
{
@@ -117,7 +135,7 @@ Java_sun_java2d_wl_WLSMSurfaceData_revalidate(JNIEnv *env, jobject wsd,
return;
}
WLSBM_SizeChangeTo(wsdo->bufferManager, width, height, scale);
WLSBM_SizeChangeTo(wsdo->bufferManager, width, height);
#endif /* !HEADLESS */
}
@@ -177,6 +195,7 @@ Java_sun_java2d_wl_WLSMSurfaceData_pixelsAt(JNIEnv *env, jobject wsd, jint x, ji
}
if (rasInfo.bounds.x2 - rasInfo.bounds.x1 < width || rasInfo.bounds.y2 - rasInfo.bounds.y1 < height) {
SurfaceData_InvokeUnlock(env, ops, &rasInfo);
JNU_ThrowByName(env, "java/lang/ArrayIndexOutOfBoundsException", "Surface too small");
return NULL;
}
@@ -313,6 +332,46 @@ WLSD_Dispose(JNIEnv *env, SurfaceDataOps *ops)
#endif
}
static void
BufferAttachedHandler(jobject surfaceDataWeakRef)
{
JNIEnv *env = getEnv();
jobject surfaceData = (*env)->NewLocalRef(env, surfaceDataWeakRef);
if (surfaceData != NULL) {
(*env)->CallVoidMethod(env, surfaceData, bufferAttachedMID);
(*env)->DeleteLocalRef(env, surfaceData);
JNU_CHECK_EXCEPTION(env);
}
}
static void
CountFrameSent(jobject surfaceDataWeakRef)
{
if (countNewFrameMID != NULL) {
JNIEnv *env = getEnv();
jobject surfaceData = (*env)->NewLocalRef(env, surfaceDataWeakRef);
if (surfaceData != NULL) {
(*env)->CallVoidMethod(env, surfaceData, countNewFrameMID);
(*env)->DeleteLocalRef(env, surfaceData);
JNU_CHECK_EXCEPTION(env);
}
}
}
static void
CountFrameDropped(jobject surfaceDataWeakRef)
{
if (countDroppedFrameMID != NULL) {
JNIEnv *env = getEnv();
jobject surfaceData = (*env)->NewLocalRef(env, surfaceDataWeakRef);
if (surfaceData != NULL) {
(*env)->CallVoidMethod(env, surfaceData, countDroppedFrameMID);
(*env)->DeleteLocalRef(env, surfaceData);
JNU_CHECK_EXCEPTION(env);
}
}
}
/*
* Class: sun_java2d_wl_WLSMSurfaceData
* Method: initOps
@@ -322,9 +381,9 @@ JNIEXPORT void JNICALL
Java_sun_java2d_wl_WLSMSurfaceData_initOps(JNIEnv *env, jobject wsd,
jint width,
jint height,
jint scale,
jint backgroundRGB,
jint wlShmFormat)
jint wlShmFormat,
jboolean perfCountersEnabled)
{
#ifndef HEADLESS
@@ -343,11 +402,19 @@ Java_sun_java2d_wl_WLSMSurfaceData_initOps(JNIEnv *env, jobject wsd,
height = 1;
}
jobject surfaceDataWeakRef = NULL;
surfaceDataWeakRef = (*env)->NewWeakGlobalRef(env, wsd);
JNU_CHECK_EXCEPTION(env);
wsdo->sdOps.Lock = WLSD_Lock;
wsdo->sdOps.Unlock = WLSD_Unlock;
wsdo->sdOps.GetRasInfo = WLSD_GetRasInfo;
wsdo->sdOps.Dispose = WLSD_Dispose;
wsdo->bufferManager = WLSBM_Create(width, height, scale, backgroundRGB, wlShmFormat);
wsdo->bufferManager = WLSBM_Create(width, height, backgroundRGB, wlShmFormat,
surfaceDataWeakRef,
perfCountersEnabled ? CountFrameSent : NULL,
perfCountersEnabled ? CountFrameDropped : NULL,
BufferAttachedHandler);
if (wsdo->bufferManager == NULL) {
JNU_ThrowOutOfMemoryError(env, "Failed to create Wayland surface buffer manager");
return;

View File

@@ -90,6 +90,7 @@ static void delete_all_tokens(struct activation_token_list_item *list) {
struct WLFrame {
jobject nativeFramePeer; // weak reference
struct wl_surface *wl_surface;
struct wp_viewport *wp_viewport;
struct xdg_surface *xdg_surface;
struct gtk_surface1 *gtk_surface;
struct WLFrame *parent;
@@ -451,13 +452,14 @@ Java_sun_awt_wl_WLComponentPeer_nativeCreateWLSurface
if (frame->wl_surface) return;
frame->wl_surface = wl_compositor_create_surface(wl_compositor);
CHECK_NULL(frame->wl_surface);
frame->wp_viewport = wp_viewporter_get_viewport(wp_viewporter, frame->wl_surface);
CHECK_NULL(frame->wp_viewport);
frame->xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, frame->wl_surface);
CHECK_NULL(frame->xdg_surface);
if (gtk_shell1 != NULL) {
frame->gtk_surface = gtk_shell1_get_gtk_surface(gtk_shell1, frame->wl_surface);
CHECK_NULL(frame->gtk_surface);
}
wl_surface_add_listener(frame->wl_surface, &wl_surface_listener, frame);
xdg_surface_add_listener(frame->xdg_surface, &xdg_surface_listener, frame);
frame->toplevel = JNI_TRUE;
@@ -530,6 +532,8 @@ Java_sun_awt_wl_WLComponentPeer_nativeCreateWLPopup
if (frame->wl_surface) return;
frame->wl_surface = wl_compositor_create_surface(wl_compositor);
CHECK_NULL(frame->wl_surface);
frame->wp_viewport = wp_viewporter_get_viewport(wp_viewporter, frame->wl_surface);
CHECK_NULL(frame->wp_viewport);
frame->xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, frame->wl_surface);
CHECK_NULL(frame->xdg_surface);
@@ -544,7 +548,6 @@ Java_sun_awt_wl_WLComponentPeer_nativeCreateWLPopup
CHECK_NULL(frame->xdg_popup);
xdg_popup_add_listener(frame->xdg_popup, &xdg_popup_listener, frame);
xdg_positioner_destroy(xdg_positioner);
// From xdg-shell.xml: "After creating a role-specific object and
// setting it up, the client must perform an initial commit
// without any buffer attached"
@@ -586,6 +589,7 @@ DoHide(JNIEnv *env, struct WLFrame *frame)
if (frame->gtk_surface != NULL) {
gtk_surface1_destroy(frame->gtk_surface);
}
wp_viewport_destroy(frame->wp_viewport);
xdg_surface_destroy(frame->xdg_surface);
wl_surface_destroy(frame->wl_surface);
delete_all_tokens(frame->activation_token_list);
@@ -596,6 +600,7 @@ DoHide(JNIEnv *env, struct WLFrame *frame)
frame->xdg_surface = NULL;
frame->xdg_toplevel = NULL;
frame->xdg_popup = NULL;
frame->wp_viewport = NULL;
frame->toplevel = JNI_FALSE;
}
}
@@ -644,6 +649,37 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeStartResize
}
}
/**
* Specifies the size of the Wayland's surface in surface units.
* For the resulting image on the screen to look sharp this size should be
* multiple of backing buffer's size with the ratio matching the display scale.
*/
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetSurfaceSize
(JNIEnv *env, jobject obj, jlong ptr, jint width, jint height)
{
struct WLFrame *frame = jlong_to_ptr(ptr);
if (frame->wp_viewport != NULL) {
wp_viewport_set_destination(frame->wp_viewport, width, height);
// Do not flush here as this update needs to be committed together with the change
// of the buffer's size and scale, if any.
}
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetOpaqueRegion
(JNIEnv *env, jobject obj, jlong ptr, jint x, jint y, jint width, jint height)
{
struct WLFrame *frame = jlong_to_ptr(ptr);
if (frame->wl_surface != NULL) {
struct wl_region* region = wl_compositor_create_region(wl_compositor);
wl_region_add(region, x, y, width, height);
wl_surface_set_opaque_region(frame->wl_surface, region);
wl_region_destroy(region);
// Do not flush here as this update needs to be committed together with the change
// of the buffer's size and scale, if any.
}
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetWindowGeometry
(JNIEnv *env, jobject obj, jlong ptr, jint x, jint y, jint width, jint height)
{

View File

@@ -31,7 +31,7 @@
#include "JNIUtilities.h"
#include "WLToolkit.h"
#include "VKBase.h"
#include "VKInit.h"
typedef struct WLOutput {
struct WLOutput * next;

View File

@@ -64,6 +64,7 @@ struct wl_display *wl_display = NULL;
struct wl_shm *wl_shm = NULL;
struct wl_compositor *wl_compositor = NULL;
struct xdg_wm_base *xdg_wm_base = NULL;
struct wp_viewporter *wp_viewporter = NULL;
struct xdg_activation_v1 *xdg_activation_v1 = NULL;
struct gtk_shell1* gtk_shell1 = NULL;
struct wl_seat *wl_seat = NULL;
@@ -524,6 +525,8 @@ registry_global(void *data, struct wl_registry *wl_registry,
wl_ddm = wl_registry_bind(wl_registry, name,&wl_data_device_manager_interface, 3);
} else if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) {
zwp_selection_dm = wl_registry_bind(wl_registry, name, &zwp_primary_selection_device_manager_v1_interface, 1);
} else if (strcmp(interface, wp_viewporter_interface.name) == 0) {
wp_viewporter = wl_registry_bind(wl_registry, name, &wp_viewporter_interface, 1);
}
#ifdef WAKEFIELD_ROBOT

View File

@@ -28,6 +28,7 @@
#include "xdg-shell-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
#include "primary-selection-client-protocol.h"
#include "viewporter-client-protocol.h"
#include "jvm_md.h"
#include "jni_util.h"
@@ -54,6 +55,7 @@ extern struct wl_display *wl_display;
extern struct wl_pointer *wl_pointer;
extern struct wl_compositor *wl_compositor;
extern struct xdg_wm_base *xdg_wm_base;
extern struct wp_viewporter *wp_viewporter;
extern struct xdg_activation_v1 *xdg_activation_v1;
extern struct gtk_shell1* gtk_shell1; // optional, check for NULL before use

View File

@@ -1,3 +1,8 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright 2023-2024 JetBrains s.r.o.
*/
/* Generated by wayland-scanner 1.19.0 */
#ifndef GTK_CLIENT_PROTOCOL_H

View File

@@ -1,3 +1,8 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright 2023-2024 JetBrains s.r.o.
*/
/* Generated by wayland-scanner 1.19.0 */
#include <stdlib.h>

View File

@@ -1,3 +1,8 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright 2023-2024 JetBrains s.r.o.
*/
/* Generated by wayland-scanner 1.19.0 */
#ifndef WP_PRIMARY_SELECTION_UNSTABLE_V1_CLIENT_PROTOCOL_H

View File

@@ -1,3 +1,8 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright 2023-2024 JetBrains s.r.o.
*/
/* Generated by wayland-scanner 1.19.0 */
/*

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright 2023-2024 JetBrains s.r.o.
*/
/* Generated by wayland-scanner 1.19.0 */
/*
* Copyright © 2013-2016 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdint.h>
#include "wayland-util.h"
#ifndef __has_attribute
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
#else
#define WL_PRIVATE
#endif
extern const struct wl_interface wl_surface_interface;
extern const struct wl_interface wp_viewport_interface;
static const struct wl_interface *viewporter_types[] = {
NULL,
NULL,
NULL,
NULL,
&wp_viewport_interface,
&wl_surface_interface,
};
static const struct wl_message wp_viewporter_requests[] = {
{ "destroy", "", viewporter_types + 0 },
{ "get_viewport", "no", viewporter_types + 4 },
};
WL_PRIVATE const struct wl_interface wp_viewporter_interface = {
"wp_viewporter", 1,
2, wp_viewporter_requests,
0, NULL,
};
static const struct wl_message wp_viewport_requests[] = {
{ "destroy", "", viewporter_types + 0 },
{ "set_source", "ffff", viewporter_types + 0 },
{ "set_destination", "ii", viewporter_types + 0 },
};
WL_PRIVATE const struct wl_interface wp_viewport_interface = {
"wp_viewport", 1,
3, wp_viewport_requests,
0, NULL,
};

View File

@@ -0,0 +1,407 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright 2023-2024 JetBrains s.r.o.
*/
/* Generated by wayland-scanner 1.19.0 */
#ifndef VIEWPORTER_CLIENT_PROTOCOL_H
#define VIEWPORTER_CLIENT_PROTOCOL_H
#include <stdint.h>
#include <stddef.h>
#include "wayland-client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @page page_viewporter The viewporter protocol
* @section page_ifaces_viewporter Interfaces
* - @subpage page_iface_wp_viewporter - surface cropping and scaling
* - @subpage page_iface_wp_viewport - crop and scale interface to a wl_surface
* @section page_copyright_viewporter Copyright
* <pre>
*
* Copyright © 2013-2016 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
* </pre>
*/
struct wl_surface;
struct wp_viewport;
struct wp_viewporter;
#ifndef WP_VIEWPORTER_INTERFACE
#define WP_VIEWPORTER_INTERFACE
/**
* @page page_iface_wp_viewporter wp_viewporter
* @section page_iface_wp_viewporter_desc Description
*
* The global interface exposing surface cropping and scaling
* capabilities is used to instantiate an interface extension for a
* wl_surface object. This extended interface will then allow
* cropping and scaling the surface contents, effectively
* disconnecting the direct relationship between the buffer and the
* surface size.
* @section page_iface_wp_viewporter_api API
* See @ref iface_wp_viewporter.
*/
/**
* @defgroup iface_wp_viewporter The wp_viewporter interface
*
* The global interface exposing surface cropping and scaling
* capabilities is used to instantiate an interface extension for a
* wl_surface object. This extended interface will then allow
* cropping and scaling the surface contents, effectively
* disconnecting the direct relationship between the buffer and the
* surface size.
*/
extern const struct wl_interface wp_viewporter_interface;
#endif
#ifndef WP_VIEWPORT_INTERFACE
#define WP_VIEWPORT_INTERFACE
/**
* @page page_iface_wp_viewport wp_viewport
* @section page_iface_wp_viewport_desc Description
*
* An additional interface to a wl_surface object, which allows the
* client to specify the cropping and scaling of the surface
* contents.
*
* This interface works with two concepts: the source rectangle (src_x,
* src_y, src_width, src_height), and the destination size (dst_width,
* dst_height). The contents of the source rectangle are scaled to the
* destination size, and content outside the source rectangle is ignored.
* This state is double-buffered, and is applied on the next
* wl_surface.commit.
*
* The two parts of crop and scale state are independent: the source
* rectangle, and the destination size. Initially both are unset, that
* is, no scaling is applied. The whole of the current wl_buffer is
* used as the source, and the surface size is as defined in
* wl_surface.attach.
*
* If the destination size is set, it causes the surface size to become
* dst_width, dst_height. The source (rectangle) is scaled to exactly
* this size. This overrides whatever the attached wl_buffer size is,
* unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface
* has no content and therefore no size. Otherwise, the size is always
* at least 1x1 in surface local coordinates.
*
* If the source rectangle is set, it defines what area of the wl_buffer is
* taken as the source. If the source rectangle is set and the destination
* size is not set, then src_width and src_height must be integers, and the
* surface size becomes the source rectangle size. This results in cropping
* without scaling. If src_width or src_height are not integers and
* destination size is not set, the bad_size protocol error is raised when
* the surface state is applied.
*
* The coordinate transformations from buffer pixel coordinates up to
* the surface-local coordinates happen in the following order:
* 1. buffer_transform (wl_surface.set_buffer_transform)
* 2. buffer_scale (wl_surface.set_buffer_scale)
* 3. crop and scale (wp_viewport.set*)
* This means, that the source rectangle coordinates of crop and scale
* are given in the coordinates after the buffer transform and scale,
* i.e. in the coordinates that would be the surface-local coordinates
* if the crop and scale was not applied.
*
* If src_x or src_y are negative, the bad_value protocol error is raised.
* Otherwise, if the source rectangle is partially or completely outside of
* the non-NULL wl_buffer, then the out_of_buffer protocol error is raised
* when the surface state is applied. A NULL wl_buffer does not raise the
* out_of_buffer error.
*
* If the wl_surface associated with the wp_viewport is destroyed,
* all wp_viewport requests except 'destroy' raise the protocol error
* no_surface.
*
* If the wp_viewport object is destroyed, the crop and scale
* state is removed from the wl_surface. The change will be applied
* on the next wl_surface.commit.
* @section page_iface_wp_viewport_api API
* See @ref iface_wp_viewport.
*/
/**
* @defgroup iface_wp_viewport The wp_viewport interface
*
* An additional interface to a wl_surface object, which allows the
* client to specify the cropping and scaling of the surface
* contents.
*
* This interface works with two concepts: the source rectangle (src_x,
* src_y, src_width, src_height), and the destination size (dst_width,
* dst_height). The contents of the source rectangle are scaled to the
* destination size, and content outside the source rectangle is ignored.
* This state is double-buffered, and is applied on the next
* wl_surface.commit.
*
* The two parts of crop and scale state are independent: the source
* rectangle, and the destination size. Initially both are unset, that
* is, no scaling is applied. The whole of the current wl_buffer is
* used as the source, and the surface size is as defined in
* wl_surface.attach.
*
* If the destination size is set, it causes the surface size to become
* dst_width, dst_height. The source (rectangle) is scaled to exactly
* this size. This overrides whatever the attached wl_buffer size is,
* unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface
* has no content and therefore no size. Otherwise, the size is always
* at least 1x1 in surface local coordinates.
*
* If the source rectangle is set, it defines what area of the wl_buffer is
* taken as the source. If the source rectangle is set and the destination
* size is not set, then src_width and src_height must be integers, and the
* surface size becomes the source rectangle size. This results in cropping
* without scaling. If src_width or src_height are not integers and
* destination size is not set, the bad_size protocol error is raised when
* the surface state is applied.
*
* The coordinate transformations from buffer pixel coordinates up to
* the surface-local coordinates happen in the following order:
* 1. buffer_transform (wl_surface.set_buffer_transform)
* 2. buffer_scale (wl_surface.set_buffer_scale)
* 3. crop and scale (wp_viewport.set*)
* This means, that the source rectangle coordinates of crop and scale
* are given in the coordinates after the buffer transform and scale,
* i.e. in the coordinates that would be the surface-local coordinates
* if the crop and scale was not applied.
*
* If src_x or src_y are negative, the bad_value protocol error is raised.
* Otherwise, if the source rectangle is partially or completely outside of
* the non-NULL wl_buffer, then the out_of_buffer protocol error is raised
* when the surface state is applied. A NULL wl_buffer does not raise the
* out_of_buffer error.
*
* If the wl_surface associated with the wp_viewport is destroyed,
* all wp_viewport requests except 'destroy' raise the protocol error
* no_surface.
*
* If the wp_viewport object is destroyed, the crop and scale
* state is removed from the wl_surface. The change will be applied
* on the next wl_surface.commit.
*/
extern const struct wl_interface wp_viewport_interface;
#endif
#ifndef WP_VIEWPORTER_ERROR_ENUM
#define WP_VIEWPORTER_ERROR_ENUM
enum wp_viewporter_error {
/**
* the surface already has a viewport object associated
*/
WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS = 0,
};
#endif /* WP_VIEWPORTER_ERROR_ENUM */
#define WP_VIEWPORTER_DESTROY 0
#define WP_VIEWPORTER_GET_VIEWPORT 1
/**
* @ingroup iface_wp_viewporter
*/
#define WP_VIEWPORTER_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_wp_viewporter
*/
#define WP_VIEWPORTER_GET_VIEWPORT_SINCE_VERSION 1
/** @ingroup iface_wp_viewporter */
static inline void
wp_viewporter_set_user_data(struct wp_viewporter *wp_viewporter, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wp_viewporter, user_data);
}
/** @ingroup iface_wp_viewporter */
static inline void *
wp_viewporter_get_user_data(struct wp_viewporter *wp_viewporter)
{
return wl_proxy_get_user_data((struct wl_proxy *) wp_viewporter);
}
static inline uint32_t
wp_viewporter_get_version(struct wp_viewporter *wp_viewporter)
{
return wl_proxy_get_version((struct wl_proxy *) wp_viewporter);
}
/**
* @ingroup iface_wp_viewporter
*
* Informs the server that the client will not be using this
* protocol object anymore. This does not affect any other objects,
* wp_viewport objects included.
*/
static inline void
wp_viewporter_destroy(struct wp_viewporter *wp_viewporter)
{
wl_proxy_marshal((struct wl_proxy *) wp_viewporter,
WP_VIEWPORTER_DESTROY);
wl_proxy_destroy((struct wl_proxy *) wp_viewporter);
}
/**
* @ingroup iface_wp_viewporter
*
* Instantiate an interface extension for the given wl_surface to
* crop and scale its content. If the given wl_surface already has
* a wp_viewport object associated, the viewport_exists
* protocol error is raised.
*/
static inline struct wp_viewport *
wp_viewporter_get_viewport(struct wp_viewporter *wp_viewporter, struct wl_surface *surface)
{
struct wl_proxy *id;
id = wl_proxy_marshal_constructor((struct wl_proxy *) wp_viewporter,
WP_VIEWPORTER_GET_VIEWPORT, &wp_viewport_interface, NULL, surface);
return (struct wp_viewport *) id;
}
#ifndef WP_VIEWPORT_ERROR_ENUM
#define WP_VIEWPORT_ERROR_ENUM
enum wp_viewport_error {
/**
* negative or zero values in width or height
*/
WP_VIEWPORT_ERROR_BAD_VALUE = 0,
/**
* destination size is not integer
*/
WP_VIEWPORT_ERROR_BAD_SIZE = 1,
/**
* source rectangle extends outside of the content area
*/
WP_VIEWPORT_ERROR_OUT_OF_BUFFER = 2,
/**
* the wl_surface was destroyed
*/
WP_VIEWPORT_ERROR_NO_SURFACE = 3,
};
#endif /* WP_VIEWPORT_ERROR_ENUM */
#define WP_VIEWPORT_DESTROY 0
#define WP_VIEWPORT_SET_SOURCE 1
#define WP_VIEWPORT_SET_DESTINATION 2
/**
* @ingroup iface_wp_viewport
*/
#define WP_VIEWPORT_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_wp_viewport
*/
#define WP_VIEWPORT_SET_SOURCE_SINCE_VERSION 1
/**
* @ingroup iface_wp_viewport
*/
#define WP_VIEWPORT_SET_DESTINATION_SINCE_VERSION 1
/** @ingroup iface_wp_viewport */
static inline void
wp_viewport_set_user_data(struct wp_viewport *wp_viewport, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wp_viewport, user_data);
}
/** @ingroup iface_wp_viewport */
static inline void *
wp_viewport_get_user_data(struct wp_viewport *wp_viewport)
{
return wl_proxy_get_user_data((struct wl_proxy *) wp_viewport);
}
static inline uint32_t
wp_viewport_get_version(struct wp_viewport *wp_viewport)
{
return wl_proxy_get_version((struct wl_proxy *) wp_viewport);
}
/**
* @ingroup iface_wp_viewport
*
* The associated wl_surface's crop and scale state is removed.
* The change is applied on the next wl_surface.commit.
*/
static inline void
wp_viewport_destroy(struct wp_viewport *wp_viewport)
{
wl_proxy_marshal((struct wl_proxy *) wp_viewport,
WP_VIEWPORT_DESTROY);
wl_proxy_destroy((struct wl_proxy *) wp_viewport);
}
/**
* @ingroup iface_wp_viewport
*
* Set the source rectangle of the associated wl_surface. See
* wp_viewport for the description, and relation to the wl_buffer
* size.
*
* If all of x, y, width and height are -1.0, the source rectangle is
* unset instead. Any other set of values where width or height are zero
* or negative, or x or y are negative, raise the bad_value protocol
* error.
*
* The crop and scale state is double-buffered state, and will be
* applied on the next wl_surface.commit.
*/
static inline void
wp_viewport_set_source(struct wp_viewport *wp_viewport, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height)
{
wl_proxy_marshal((struct wl_proxy *) wp_viewport,
WP_VIEWPORT_SET_SOURCE, x, y, width, height);
}
/**
* @ingroup iface_wp_viewport
*
* Set the destination size of the associated wl_surface. See
* wp_viewport for the description, and relation to the wl_buffer
* size.
*
* If width is -1 and height is -1, the destination size is unset
* instead. Any other pair of values for width and height that
* contains zero or negative values raises the bad_value protocol
* error.
*
* The crop and scale state is double-buffered state, and will be
* applied on the next wl_surface.commit.
*/
static inline void
wp_viewport_set_destination(struct wp_viewport *wp_viewport, int32_t width, int32_t height)
{
wl_proxy_marshal((struct wl_proxy *) wp_viewport,
WP_VIEWPORT_SET_DESTINATION, width, height);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,3 +1,8 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright 2023-2024 JetBrains s.r.o.
*/
/* Generated by wayland-scanner 1.19.0 */
/*

View File

@@ -1,3 +1,8 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright 2023-2024 JetBrains s.r.o.
*/
/* Generated by wayland-scanner 1.19.0 */
#ifndef WAKEFIELD_CLIENT_PROTOCOL_H

View File

@@ -1,3 +1,8 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright 2023-2024 JetBrains s.r.o.
*/
/* Generated by wayland-scanner 1.18.0 */
#ifndef XDG_ACTIVATION_V1_CLIENT_PROTOCOL_H

View File

@@ -1,3 +1,8 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright 2023-2024 JetBrains s.r.o.
*/
/* Generated by wayland-scanner 1.18.0 */
/*

View File

@@ -1638,6 +1638,81 @@ Java_sun_awt_X11_XInputMethod_releaseXICNative(JNIEnv *env,
}
JNIEXPORT void JNICALL
Java_sun_awt_X11_XInputMethod_delayedDisposeXIC_1preparation_1unsetFocusAndDetachCurrentXICNative
(JNIEnv *env, jobject this)
{
DASSERT(env != NULL);
X11InputMethodData *pX11IMData = NULL;
AWT_LOCK();
pX11IMData = getX11InputMethodData(env, this);
if (pX11IMData == NULL) {
AWT_UNLOCK();
return;
}
if (pX11IMData->ic_active.xic != (XIC)0) {
setXICFocus(pX11IMData->ic_active.xic, False);
}
if ( (pX11IMData->ic_passive.xic != (XIC)0) && (pX11IMData->ic_passive.xic != pX11IMData->ic_active.xic) ) {
setXICFocus(pX11IMData->ic_passive.xic, False);
}
pX11IMData->current_ic = (XIC)0;
setX11InputMethodData(env, this, NULL);
if ( (*env)->IsSameObject(env, pX11IMData->x11inputmethod, currentX11InputMethodInstance) == JNI_TRUE ) {
// currentX11InputMethodInstance never holds a "unique" java ref - it only holds a "weak" copy of
// _X11InputMethodData::x11inputmethod, so we mustn't DeleteGlobalRef here
currentX11InputMethodInstance = NULL;
currentFocusWindow = 0;
}
AWT_UNLOCK();
}
JNIEXPORT void JNICALL
Java_sun_awt_X11_XInputMethod_delayedDisposeXIC_1preparation_1resetSpecifiedCtxNative
(JNIEnv *env, jclass clazz, const jlong pData)
{
X11InputMethodData * const pX11IMData = (X11InputMethodData *)pData;
char* preeditText = NULL;
if (pX11IMData == NULL) {
return;
}
AWT_LOCK();
if (pX11IMData->ic_active.xic != (XIC)0) {
if ( (preeditText = XmbResetIC(pX11IMData->ic_active.xic)) != NULL ) {
(void)XFree(preeditText); preeditText = NULL;
}
}
if ( (pX11IMData->ic_passive.xic != (XIC)0) && (pX11IMData->ic_passive.xic != pX11IMData->ic_active.xic) ) {
if ( (preeditText = XmbResetIC(pX11IMData->ic_passive.xic)) != NULL ) {
(void)XFree(preeditText); preeditText = NULL;
}
}
AWT_UNLOCK();
}
JNIEXPORT void JNICALL
Java_sun_awt_X11_XInputMethod_delayedDisposeXIC_1disposeXICNative(JNIEnv *env, jclass clazz, jlong pData)
{
X11InputMethodData *pX11IMData = (X11InputMethodData *)pData;
if (pX11IMData == NULL) {
return;
}
AWT_LOCK();
destroyX11InputMethodData(env, pX11IMData); pX11IMData = NULL; pData = 0;
AWT_UNLOCK();
}
JNIEXPORT void JNICALL
Java_sun_awt_X11_XInputMethod_setXICFocusNative(JNIEnv *env,
jobject this,

View File

@@ -154,6 +154,9 @@ public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment {
}
for (int i = 0; i < screens.length; i++) {
if (screens[i] instanceof Win32GraphicsDevice gd) {
if (LogDisplay.ENABLED) {
LogDisplay.REMOVED.log(i, "UNKNOWN", Double.NaN);
}
oldDevices.add(new WeakReference<>(gd));
} else {
// REMIND: can we ever have anything other than Win32GD?
@@ -205,13 +208,16 @@ public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment {
*/
protected GraphicsDevice makeScreenDevice(int screennum) {
GraphicsDevice device = null;
Win32GraphicsDevice device = null;
if (WindowsFlags.isD3DEnabled()) {
device = D3DGraphicsDevice.createDevice(screennum);
}
if (device == null) {
device = new Win32GraphicsDevice(screennum);
}
if (LogDisplay.ENABLED) {
LogDisplay.ADDED.log(device.getScreen(), device.getBounds(), device.getDefaultScaleX());
}
return device;
}

View File

@@ -2029,6 +2029,11 @@ void AwtFrame::_UpdateCustomTitleBar(void* p) {
// {end} Custom title bar support
UINT AwtFrame::GetCurrentWmSizeType()
{
return isZoomed() ? SIZE_MAXIMIZED : SIZENORMAL;
}
/************************************************************************
* WFramePeer native methods
*/

View File

@@ -242,6 +242,8 @@ private:
RECT GetSysInsets();
LRESULT HitTestNCA(int x, int y);
virtual UINT GetCurrentWmSizeType();
/*
* Hashtable<Thread, BlockedThreadStruct> - a table that contains all the
* information about non-toolkit threads with modal blocked embedded

View File

@@ -1901,7 +1901,7 @@ void AwtWindow::WmDPIChanged(const LPARAM &lParam) {
RECT *r = (RECT *) lParam;
ReshapeNoScale(r->left, r->top, r->right - r->left, r->bottom - r->top);
CheckIfOnNewScreen(true);
WmSize(SIZENORMAL, r->right - r->left, r->bottom - r->top);
WmSize(GetCurrentWmSizeType(), r->right - r->left, r->bottom - r->top);
}
MsgRouting AwtWindow::WmEraseBkgnd(HDC hDC, BOOL& didErase)
@@ -2324,6 +2324,10 @@ BOOL AwtWindow::CheckIfOnNewScreenWithDifferentScale() {
return FALSE;
}
UINT AwtWindow::GetCurrentWmSizeType() {
return SIZENORMAL;
}
/*
* Check to see if we've been moved onto another screen.
* If so, update internal data, surfaces, etc.

View File

@@ -138,6 +138,7 @@ public:
virtual int GetScreenImOn();
virtual BOOL CheckIfOnNewScreen(BOOL force);
virtual BOOL CheckIfOnNewScreenWithDifferentScale();
virtual UINT GetCurrentWmSizeType();
virtual void Grab();
virtual void Ungrab();
virtual void Ungrab(BOOL doPost);

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