Skip to content

Commit

Permalink
JBR-7990 Vulkan: Robot pixel grabbing for Vulkan surfaces
Browse files Browse the repository at this point in the history
Implemented grabbing pixels via partly supported SurfaceToSwBlit, removed pixelAt and pixelsAt native implementations from WLVKSurfaceData
  • Loading branch information
avu authored and jbrbot committed Jan 15, 2025
1 parent 3a1e73c commit 42c8cf7
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 159 deletions.
12 changes: 6 additions & 6 deletions src/java.desktop/share/native/common/java2d/vulkan/VKBlitLoops.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,8 @@ void VKBlitLoops_Blit(JNIEnv *env,
}

/**
* Specialized blit method for copying a native OpenGL "Surface" (pbuffer,
* window, etc.) to a system memory ("Sw") surface.
* Specialized blit method for copying a native Vulkan "Surface" to a system
* memory ("Sw") surface.
*/
void
VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context,
Expand All @@ -333,7 +333,8 @@ VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context,
SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);
SurfaceDataRasInfo srcInfo, dstInfo;

J2dTraceLn(J2D_TRACE_INFO, "VKBlitLoops_SurfaceToSwBlit");
J2dTraceLn6(J2D_TRACE_INFO, "VKBlitLoops_SurfaceToSwBlit: (%d %d %d %d) -> (%d %d)",
srcx, srcy, width, height, dstx, dsty);

if (width <= 0 || height <= 0) {
J2dTraceLn(J2D_TRACE_WARNING,
Expand Down Expand Up @@ -409,9 +410,8 @@ VKBlitLoops_SurfaceToSwBlit(JNIEnv *env, VKRenderingContext* context,
.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}
.imageOffset = {srcx, srcy, 0},
.imageExtent = {width, height, 1}
};

device->vkCmdCopyImageToBuffer(cb, image->handle, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,29 @@ public int getRGBPixelAt(int x, int y) {
}

public int [] getRGBPixelsAt(Rectangle bounds) {
int [] pixels = pixelsAt(bounds.x, bounds.y, bounds.width, bounds.height);
var surfaceType = getSurfaceType();
if (surfaceType.equals(SurfaceType.IntArgbPre) || surfaceType.equals(SurfaceType.IntRgb)) {
// No conversion is necessary, can return raw pixels
} else {
var cm = getColorModel();
for (int i = 0; i < pixels.length; i++) {
pixels[i] = surfaceType.rgbFor(pixels[i], cm);
}
Rectangle r = peer.getBufferBounds();

if ((long)bounds.width * (long)bounds.height > Integer.MAX_VALUE) {
throw new IndexOutOfBoundsException("Dimensions (width=" + bounds.width +
" height=" + bounds.height + ") are too large");
}

Rectangle b = bounds.intersection(r);

if (b.isEmpty()) {
throw new IndexOutOfBoundsException("Requested bounds are outside of surface bounds");
}

BufferedImage resImg = new BufferedImage(b.width, b.height, 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,
b.x, b.y, 0, 0, b.width, b.height);

int [] pixels = new int[b.width * b.height];
resImg.getRGB(0, 0, b.width, b.height, pixels, 0, b.width);
return pixels;
}

Expand Down Expand Up @@ -231,7 +244,4 @@ public boolean isOnScreen() {
return true;
}
}

private native int pixelAt(int x, int y);
private native int [] pixelsAt(int x, int y, int width, int height);
}
141 changes: 0 additions & 141 deletions src/java.desktop/unix/native/common/java2d/vulkan/WLVKSurfaceData.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,144 +81,3 @@ JNIEXPORT void JNICALL Java_sun_java2d_vulkan_WLVKSurfaceData_assignWlSurface(JN
}
}

JNIEXPORT jint JNICALL
Java_sun_java2d_vulkan_WLVKSurfaceData_pixelAt(JNIEnv *env, jobject vksd, jint x, jint y)
{
J2dTraceLn(J2D_TRACE_INFO, "Java_sun_java2d_vulkan_WLVKSurfaceData_pixelAt")
jint pixel = 0xFFB6C1; // the color pink to make errors visible

VKWinSDOps* sd = (VKWinSDOps*)SurfaceData_GetOps(env, vksd);
JNU_CHECK_EXCEPTION_RETURN(env, pixel);
if (sd == NULL) {
return pixel;
}

VKDevice* device = sd->vksdOps.device;
VKImage* image = sd->vksdOps.image;

if (image == NULL || device == NULL) {
J2dRlsTraceLn2(J2D_TRACE_ERROR,
"Java_sun_java2d_vulkan_WLVKSurfaceData_pixelAt: image(%p) or device(%p) is NULL", image, device)
return 0x0; // black color if we don't have any content so far
}

SurfaceDataBounds bounds = {x, y, x + 1, y + 1};
SurfaceData_IntersectBoundsXYWH(&bounds, 0, 0, (jint)image->extent.width, (jint)image->extent.height);
if (bounds.x2 <= bounds.x1 || bounds.y2 <= bounds.y1) {
JNU_ThrowByName(env, "java/lang/ArrayIndexOutOfBoundsException", "Coordinate out of bounds");
return pixel;
}

VKRenderer_FlushRenderPass(&sd->vksdOps);
VkCommandBuffer cb = VKRenderer_Record(device->renderer);

VKBuffer* buffer = VKBuffer_Create(device, 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 = {x, y, 0},
.imageExtent = {1, 1, 1}
};

device->vkCmdCopyImageToBuffer(cb, image->handle, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
buffer->handle,
1, &region);
VKRenderer_Flush(device->renderer);
VKRenderer_Sync(device->renderer);
void* pixelData;
device->vkMapMemory(device->handle, buffer->range.memory, 0, VK_WHOLE_SIZE, 0, &pixelData);
pixel = *(int*)pixelData;
device->vkUnmapMemory(device->handle, buffer->range.memory);
VKBuffer_Destroy(device, buffer);
return pixel;
}

JNIEXPORT jarray JNICALL
Java_sun_java2d_vulkan_WLVKSurfaceData_pixelsAt(JNIEnv *env, jobject vksd, jint x, jint y, jint width, jint height)
{
J2dTraceLn(J2D_TRACE_INFO, "Java_sun_java2d_vulkan_WLVKSurfaceData_pixelsAt")
VKWinSDOps* sd = (VKWinSDOps*)SurfaceData_GetOps(env, vksd);

if (sd == NULL) {
return NULL;
}

VKDevice* device = sd->vksdOps.device;
VKImage* image = sd->vksdOps.image;

if (image == NULL || device == NULL) {
J2dRlsTraceLn2(J2D_TRACE_ERROR,
"Java_sun_java2d_vulkan_WLVKSurfaceData_pixelAt: image(%p) or device(%p) is NULL", image, device)
return NULL;
}

SurfaceDataBounds bounds = {x, y, x + width, y + height};
SurfaceData_IntersectBoundsXYWH(&bounds, 0, 0, (jint)image->extent.width, (jint)image->extent.height);
if (bounds.x2 <= bounds.x1 || bounds.y2 <= bounds.y1) {
JNU_ThrowByName(env, "java/lang/ArrayIndexOutOfBoundsException", "Coordinate out of bounds");
return NULL;
}

if (bounds.x2 - bounds.x1 < width || bounds.y2 - bounds.y1 < height) {
JNU_ThrowByName(env, "java/lang/ArrayIndexOutOfBoundsException", "Surface too small");
return NULL;
}

jintArray arrayObj = NULL;
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);

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 = {bounds.x1, bounds.y1, 0},
.imageExtent = {bounds.x2 - bounds.x1, bounds.y2 - bounds.y1, 1}
};

device->vkCmdCopyImageToBuffer(cb, image->handle, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
buffer->handle,
1, &region);
VKRenderer_Flush(device->renderer);
VKRenderer_Sync(device->renderer);
void* pixelData;
device->vkMapMemory(device->handle, buffer->range.memory, 0, VK_WHOLE_SIZE, 0, &pixelData);
jint *array = (*env)->GetPrimitiveArrayCritical(env, arrayObj, NULL);
if (array == NULL) {
JNU_ThrowOutOfMemoryError(env, "Wayland window pixels capture");
} else {
memcpy(array, pixelData, bufferSizeInPixels * sizeof(jint));
(*env)->ReleasePrimitiveArrayCritical(env, arrayObj, array, 0);
}
device->vkUnmapMemory(device->handle, buffer->range.memory);
VKBuffer_Destroy(device, buffer);
return arrayObj;
}

0 comments on commit 42c8cf7

Please sign in to comment.