JBR-8287 Vulkan: enable hw accelerated VolatileImage

Moved robot pixel grabber into windows surface data
Created offscreen surface data
Separated surfaces implementation into two files
Moved offscreen surface to the shared code

Fix
This commit is contained in:
Alexey Ushakov
2025-08-06 02:31:16 +02:00
committed by jbrbot
parent 6ef3f59b88
commit eea8a175c3
9 changed files with 225 additions and 137 deletions

View File

@@ -35,6 +35,7 @@ public class VKInstance {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.java2d.vulkan.VKInstance");
private static Boolean initialized;
private static Boolean sdAccelerated;
private static native boolean initNative(long nativePtr, boolean verbose, int deviceNumber);
@@ -54,10 +55,15 @@ public class VKInstance {
final int deviceNumber = parsedDeviceNumber;
final boolean verbose = "True".equals(vulkanOption);
System.loadLibrary("awt");
String sdOption = AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty("sun.java2d.vulkan.accelsd", ""));
initialized = initNative(nativePtr, verbose, deviceNumber);
sdAccelerated = initialized && "true".equalsIgnoreCase(sdOption);
} else initialized = false;
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Vulkan rendering enabled: " + (initialized ? "YES" : "NO"));
log.fine("Vulkan accelerated surface data enabled: " + (sdAccelerated ? "YES" : "NO"));
}
}
@@ -65,4 +71,9 @@ public class VKInstance {
if (initialized == null) throw new RuntimeException("Vulkan not initialized");
return initialized;
}
public static boolean isSurfaceDataAccelerated() {
if (initialized == null) throw new RuntimeException("Vulkan not initialized");
return sdAccelerated;
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2025 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.vulkan;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
import sun.java2d.SurfaceData;
import sun.java2d.pipe.BufferedContext;
/**
* SurfaceData object representing an off-screen buffer
*/
public class VKOffScreenSurfaceData extends VKSurfaceData {
private final Image offscreenImage;
private native void initOps(int width, int height);
public VKOffScreenSurfaceData(VKGraphicsConfig gc, Image image, ColorModel cm,
int type, int width, int height)
{
super(gc, cm, type, width, height);
offscreenImage = image;
initOps(width, height);
}
@Override
public SurfaceData getReplacement() {
return restoreContents(offscreenImage);
}
@Override
public GraphicsConfiguration getDeviceConfiguration() {
return null;
}
@Override
public long getNativeResource(int resType) {
return 0;
}
@Override
public Rectangle getBounds() {
return new Rectangle(width, height);
}
/**
* Returns destination Image associated with this SurfaceData.
*/
@Override
public Object getDestination() {
return offscreenImage;
}
@Override
public BufferedContext getContext() {
return getGraphicsConfig().getContext();
}
@Override
public boolean isOnScreen() {
return false;
}
}

View File

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

View File

@@ -558,7 +558,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer
VKSDOps* dst = NEXT_SURFACE(b);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"VKRenderQueue_flushBuffer: SET_SURFACES");
"VKRenderQueue_flushBuffer: SET_SURFACES src=%p dst=%p", src, dst);
context.surface = dst;
}
break;

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -293,4 +293,22 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
return VK_TRUE;
}
/*
* Class: sun_java2d_vulkan_VKOffScreenSurfaceData
* Method: initOps
* Signature: (II)V
*/
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKOffScreenSurfaceData_initOps
(JNIEnv *env, jobject vksd, jint width, jint height) {
VKSDOps * sd = (VKSDOps*)SurfaceData_InitOps(env, vksd, sizeof(VKSDOps));
J2dTraceLn(J2D_TRACE_VERBOSE, "VKOffScreenSurfaceData_initOps(%p)", sd);
if (sd == NULL) {
JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
return;
}
sd->drawableType = VKSD_RT_TEXTURE;
sd->background = VKUtil_DecodeJavaColor(0);
VKRenderer_ConfigureSurface(sd, (VkExtent2D){width, height});
}
#endif /* !HEADLESS */

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,8 +35,15 @@ static void WLVKSurfaceData_OnResize(VKWinSDOps* surface, VkExtent2D extent) {
JNU_CallMethodByName(env, NULL, surface->vksdOps.sdOps.sdObject, "bufferAttached", "()V");
}
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_initOps(JNIEnv *env, jobject vksd, jint backgroundRGB) {
J2dTraceLn(J2D_TRACE_VERBOSE, "WLVKSurfaceData_initOps(%p)", vksd);
/*
* Class: sun_java2d_vulkan_WLVKSurfaceData_WLVKWindowSurfaceData
* Method: initOps
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKWindowSurfaceData_initOps(
JNIEnv *env, jobject vksd, jint backgroundRGB)
{
J2dTraceLn(J2D_TRACE_VERBOSE, "WLVKWindowsSurfaceData_initOps(%p)", vksd);
VKWinSDOps* sd = (VKWinSDOps*)SurfaceData_InitOps(env, vksd, sizeof(VKWinSDOps));
if (sd == NULL) {
JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
@@ -48,17 +55,27 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_initOps(JNIEnv *en
VKSD_ResetSurface(&sd->vksdOps);
}
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_assignWlSurface(JNIEnv *env, jobject vksd, jlong wlSurfacePtr) {
J2dRlsTraceLn(J2D_TRACE_INFO, "WLVKSurfaceData_assignWlSurface(%p): wl_surface=%p", (void*)vksd, wlSurfacePtr);
/*
* Class: sun_java2d_vulkan_WLVKSurfaceData_WLVKWindowSurfaceData
* Method: assignWlSurface
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKWindowSurfaceData_assignWlSurface(
JNIEnv *env, jobject vksd, jlong wlSurfacePtr)
{
J2dRlsTraceLn(J2D_TRACE_INFO, "WLVKWindowsSurfaceData_assignWlSurface(%p): wl_surface=%p",
(void*)vksd, wlSurfacePtr);
VKWinSDOps* sd = (VKWinSDOps*)SurfaceData_GetOps(env, vksd);
if (sd == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "WLVKSurfaceData_assignWlSurface(%p): VKWinSDOps is NULL", vksd);
J2dRlsTraceLn(J2D_TRACE_ERROR,
"WLVKWindowSurfaceData_assignWlSurface(%p): VKWinSDOps is NULL", vksd);
VK_UNHANDLED_ERROR();
}
if (sd->surface != VK_NULL_HANDLE) {
VKSD_ResetSurface(&sd->vksdOps);
J2dRlsTraceLn(J2D_TRACE_INFO, "WLVKSurfaceData_assignWlSurface(%p): surface reset", vksd);
J2dRlsTraceLn(J2D_TRACE_INFO,
"WLVKWindowSurfaceData_assignWlSurface(%p): surface reset", vksd);
}
struct wl_surface* wl_surface = (struct wl_surface*)jlong_to_ptr(wlSurfacePtr);
@@ -73,7 +90,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_assignWlSurface(JN
VK_IF_ERROR(ge->vkCreateWaylandSurfaceKHR(ge->vkInstance, &surfaceCreateInfo, NULL, &sd->surface)) {
VK_UNHANDLED_ERROR();
}
J2dRlsTraceLn(J2D_TRACE_INFO, "WLVKSurfaceData_assignWlSurface(%p): surface created", vksd);
J2dRlsTraceLn(J2D_TRACE_INFO,
"WLVKWindowSurfaceData_assignWlSurface(%p): surface created", vksd);
// Swapchain will be created later after CONFIGURE_SURFACE.
}
}