From 5e10372a5fbc0ec2ca27b971deecb2805c3e441b Mon Sep 17 00:00:00 2001 From: Alexey Ushakov Date: Mon, 13 Jan 2025 21:43:45 +0100 Subject: [PATCH] JBR-7990 Vulkan: Robot pixel grabbing for Vulkan surfaces Implemented grabbing pixel via partly supported SurfaceToSwBlit --- .../native/common/java2d/vulkan/VKBlitLoops.c | 112 ++++++++++++++++++ .../native/common/java2d/vulkan/VKBlitLoops.h | 5 + .../common/java2d/vulkan/VKRenderQueue.c | 1 + .../sun/java2d/vulkan/WLVKSurfaceData.java | 21 +++- .../common/java2d/vulkan/WLVKSurfaceData.c | 4 +- 5 files changed, 139 insertions(+), 4 deletions(-) diff --git a/src/java.desktop/share/native/common/java2d/vulkan/VKBlitLoops.c b/src/java.desktop/share/native/common/java2d/vulkan/VKBlitLoops.c index 94fe8d2cd3f2..5ed68a830b21 100644 --- a/src/java.desktop/share/native/common/java2d/vulkan/VKBlitLoops.c +++ b/src/java.desktop/share/native/common/java2d/vulkan/VKBlitLoops.c @@ -31,6 +31,7 @@ #include "VKBlitLoops.h" #include "VKSurfaceData.h" #include "VKRenderer.h" +#include "GraphicsPrimitiveMgr.h" #include "Trace.h" @@ -316,4 +317,115 @@ void VKBlitLoops_Blit(JNIEnv *env, SurfaceData_InvokeRelease(env, srcOps, &srcInfo); } SurfaceData_InvokeUnlock(env, srcOps, &srcInfo); +} + +/** + * Specialized blit method for copying a native OpenGL "Surface" (pbuffer, + * window, etc.) to a system memory ("Sw") surface. + */ +void +VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context, + jlong pSrcOps, jlong pDstOps, jint dsttype, + jint srcx, jint srcy, jint dstx, jint dsty, + jint width, jint height) +{ + VKSDOps *srcOps = (VKSDOps *)jlong_to_ptr(pSrcOps); + SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps); + SurfaceDataRasInfo srcInfo, dstInfo; + + J2dTraceLn(J2D_TRACE_INFO, "VKBlitLoops_SurfaceToSwBlit"); + + if (width <= 0 || height <= 0) { + J2dTraceLn(J2D_TRACE_WARNING, + "VKBlitLoops_SurfaceToSwBlit: dimensions are non-positive"); + return; + } + + RETURN_IF_NULL(srcOps); + RETURN_IF_NULL(dstOps); + RETURN_IF_NULL(context); + + VKDevice* device = srcOps->device; + VKImage* image = srcOps->image; + + if (image == NULL || device == NULL) { + J2dRlsTraceLn2(J2D_TRACE_ERROR, + "VKBlitLoops_SurfaceToSwBlit: image(%p) or device(%p) is NULL", image, device) + return; + } + + srcInfo.bounds.x1 = srcx; + srcInfo.bounds.y1 = srcy; + srcInfo.bounds.x2 = srcx + width; + srcInfo.bounds.y2 = srcy + height; + dstInfo.bounds.x1 = dstx; + dstInfo.bounds.y1 = dsty; + dstInfo.bounds.x2 = dstx + width; + dstInfo.bounds.y2 = dsty + height; + + if (dstOps->Lock(env, dstOps, &dstInfo, SD_LOCK_WRITE) != SD_SUCCESS) { + J2dTraceLn(J2D_TRACE_WARNING, + "VKBlitLoops_SurfaceToSwBlit: could not acquire dst lock"); + return; + } + + SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds, + 0, 0, srcOps->image->extent.width, srcOps->image->extent.height); + SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds, + srcx - dstx, srcy - dsty); + + if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && + srcInfo.bounds.y2 > srcInfo.bounds.y1) + { + dstOps->GetRasInfo(env, dstOps, &dstInfo); + if (dstInfo.rasBase) { + void *pDst = dstInfo.rasBase; + srcx = srcInfo.bounds.x1; + srcy = srcInfo.bounds.y1; + dstx = dstInfo.bounds.x1; + dsty = dstInfo.bounds.y1; + width = srcInfo.bounds.x2 - srcInfo.bounds.x1; + height = srcInfo.bounds.y2 - srcInfo.bounds.y1; + jsize bufferSizeInPixels = width * height; + + pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride); + pDst = PtrPixelsRow(pDst, dsty, dstInfo.scanStride); + + VKRenderer_FlushRenderPass(srcOps); + VkCommandBuffer cb = VKRenderer_Record(device->renderer); + + VKBuffer* buffer = VKBuffer_Create(device, bufferSizeInPixels * sizeof(jint), + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + VkBufferImageCopy region = { + .bufferOffset = 0, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource= { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1 + }, + .imageOffset = {srcInfo.bounds.x1, srcInfo.bounds.y1, 0}, + .imageExtent = {srcInfo.bounds.x2 - srcInfo.bounds.x1, + srcInfo.bounds.y2 - srcInfo.bounds.y1, 1} + }; + + device->vkCmdCopyImageToBuffer(cb, image->handle, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + buffer->handle, + 1, ®ion); + VKRenderer_Flush(device->renderer); + VKRenderer_Sync(device->renderer); + void* pixelData; + device->vkMapMemory(device->handle, buffer->range.memory, 0, VK_WHOLE_SIZE, 0, &pixelData); + memcpy(pDst, pixelData, bufferSizeInPixels * sizeof(jint)); + device->vkUnmapMemory(device->handle, buffer->range.memory); + VKBuffer_Destroy(device, buffer); + } + SurfaceData_InvokeRelease(env, dstOps, &dstInfo); + } + SurfaceData_InvokeUnlock(env, dstOps, &dstInfo); } \ No newline at end of file diff --git a/src/java.desktop/share/native/common/java2d/vulkan/VKBlitLoops.h b/src/java.desktop/share/native/common/java2d/vulkan/VKBlitLoops.h index 40968e844b02..b655b599eef0 100644 --- a/src/java.desktop/share/native/common/java2d/vulkan/VKBlitLoops.h +++ b/src/java.desktop/share/native/common/java2d/vulkan/VKBlitLoops.h @@ -49,5 +49,10 @@ void VKBlitLoops_Blit(JNIEnv *env, jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2); +void +VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context, + jlong pSrcOps, jlong pDstOps, jint dsttype, + jint srcx, jint srcy, jint dstx, jint dsty, + jint width, jint height); #endif /* VKBlitLoops_h_Included */ diff --git a/src/java.desktop/share/native/common/java2d/vulkan/VKRenderQueue.c b/src/java.desktop/share/native/common/java2d/vulkan/VKRenderQueue.c index fddf193a3fa4..2178b791f5f0 100644 --- a/src/java.desktop/share/native/common/java2d/vulkan/VKRenderQueue.c +++ b/src/java.desktop/share/native/common/java2d/vulkan/VKRenderQueue.c @@ -395,6 +395,7 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_VKRenderQueue_flushBuffer jlong pSrc = NEXT_LONG(b); jlong pDst = NEXT_LONG(b); J2dRlsTraceLn(J2D_TRACE_VERBOSE, "VKRenderQueue_flushBuffer: SURFACE_TO_SW_BLIT"); + VKBlitLoops_SurfaceToSwBlit(env, &context, pSrc, pDst, dsttype, sx, sy, dx, dy, w, h); } break; case sun_java2d_pipe_BufferedOpCodes_MASK_FILL: diff --git a/src/java.desktop/unix/classes/sun/java2d/vulkan/WLVKSurfaceData.java b/src/java.desktop/unix/classes/sun/java2d/vulkan/WLVKSurfaceData.java index a77bdd1d39fd..018fd3ca64c3 100644 --- a/src/java.desktop/unix/classes/sun/java2d/vulkan/WLVKSurfaceData.java +++ b/src/java.desktop/unix/classes/sun/java2d/vulkan/WLVKSurfaceData.java @@ -26,13 +26,18 @@ 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 sun.java2d.pipe.RenderBuffer; @@ -147,8 +152,20 @@ public static WLVKGraphicsConfig getGC(WLComponentPeer peer) { } public int getRGBPixelAt(int x, int y) { - int pixel = pixelAt(x, y); - return getSurfaceType().rgbFor(pixel, getColorModel()); + Rectangle r = peer.getBufferBounds(); + if (x < r.x || x >= r.x + r.width || y < r.y || y >= r.y + r.height) { + throw new ArrayIndexOutOfBoundsException("x,y outside of buffer bounds"); + } + + BufferedImage resImg = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + SurfaceData resData = BufImgSurfaceData.createData(resImg); + + Blit blit = Blit.getFromCache(getSurfaceType(), CompositeType.SrcNoEa, + resData.getSurfaceType()); + blit.Blit(this, resData, AlphaComposite.Src, null, + x, y, 0, 0, 1, 1); + + return resImg.getRGB(0, 0); } public int [] getRGBPixelsAt(Rectangle bounds) { diff --git a/src/java.desktop/unix/native/common/java2d/vulkan/WLVKSurfaceData.c b/src/java.desktop/unix/native/common/java2d/vulkan/WLVKSurfaceData.c index 2c05b812047e..1241e00b48a8 100644 --- a/src/java.desktop/unix/native/common/java2d/vulkan/WLVKSurfaceData.c +++ b/src/java.desktop/unix/native/common/java2d/vulkan/WLVKSurfaceData.c @@ -150,8 +150,6 @@ Java_sun_java2d_vulkan_WLVKSurfaceData_pixelsAt(JNIEnv *env, jobject vksd, jint J2dTraceLn(J2D_TRACE_INFO, "Java_sun_java2d_vulkan_WLVKSurfaceData_pixelsAt") VKWinSDOps* sd = (VKWinSDOps*)SurfaceData_GetOps(env, vksd); - JNU_CHECK_EXCEPTION_RETURN(env, NULL); - if (sd == NULL) { return NULL; } @@ -181,6 +179,8 @@ Java_sun_java2d_vulkan_WLVKSurfaceData_pixelsAt(JNIEnv *env, jobject vksd, jint jsize bufferSizeInPixels = width * height; arrayObj = (*env)->NewIntArray(env, bufferSizeInPixels); + JNU_CHECK_EXCEPTION_RETURN(env, NULL); + VKRenderer_FlushRenderPass(&sd->vksdOps); VkCommandBuffer cb = VKRenderer_Record(device->renderer);