mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-22 20:41:20 +00:00
[vulkan] Use vkCmdCopyImageToBuffer for screenshot capture
I had missed that vkCmdCopyImage requires the source and destination images to have exactly the same size, and I guess assumed that the swapchain images would always be the size they said they were, but this is not the case for tiled-optimal images. However, vkCmdCopyImageToBuffer does the right thing regardless of the source image size. This fixes the skewed screenshots when the window size is not a multiple of 8 (for me, might differ for others).
This commit is contained in:
parent
950d6d1472
commit
71e07e6454
3 changed files with 46 additions and 92 deletions
|
@ -10,9 +10,8 @@
|
||||||
#include "QF/qtypes.h"
|
#include "QF/qtypes.h"
|
||||||
|
|
||||||
typedef struct qfv_capture_image_s {
|
typedef struct qfv_capture_image_s {
|
||||||
VkImage image;
|
|
||||||
VkImageLayout layout;
|
|
||||||
VkCommandBuffer cmd;
|
VkCommandBuffer cmd;
|
||||||
|
VkBuffer buffer;
|
||||||
byte *data;
|
byte *data;
|
||||||
} qfv_capture_image_t;
|
} qfv_capture_image_t;
|
||||||
|
|
||||||
|
@ -25,7 +24,6 @@ typedef struct qfv_capture_image_set_s
|
||||||
typedef struct qfv_capture_s {
|
typedef struct qfv_capture_s {
|
||||||
struct qfv_device_s *device;
|
struct qfv_device_s *device;
|
||||||
|
|
||||||
int canBlit;
|
|
||||||
VkExtent2D extent;
|
VkExtent2D extent;
|
||||||
qfv_capture_image_set_t *image_set;
|
qfv_capture_image_set_t *image_set;
|
||||||
size_t imgsize;
|
size_t imgsize;
|
||||||
|
|
|
@ -558,8 +558,7 @@ capture_screenshot (const byte *data, int width, int height)
|
||||||
tex->flagbits = 0;
|
tex->flagbits = 0;
|
||||||
tex->loaded = 1;
|
tex->loaded = 1;
|
||||||
|
|
||||||
if (!vulkan_ctx->capture->canBlit ||
|
if (is_bgr (vulkan_ctx->swapchain->format)) {
|
||||||
is_bgr (vulkan_ctx->swapchain->format)) {
|
|
||||||
tex->bgr = 1;
|
tex->bgr = 1;
|
||||||
}
|
}
|
||||||
const byte *src = data;
|
const byte *src = data;
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
# include "config.h"
|
# include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "QF/Vulkan/buffer.h"
|
||||||
#include "QF/Vulkan/capture.h"
|
#include "QF/Vulkan/capture.h"
|
||||||
#include "QF/Vulkan/command.h"
|
#include "QF/Vulkan/command.h"
|
||||||
#include "QF/Vulkan/device.h"
|
#include "QF/Vulkan/device.h"
|
||||||
|
@ -42,7 +43,6 @@ QFV_CreateCapture (qfv_device_t *device, int numframes,
|
||||||
qfv_instfuncs_t *ifunc = device->physDev->instance->funcs;
|
qfv_instfuncs_t *ifunc = device->physDev->instance->funcs;
|
||||||
qfv_devfuncs_t *dfunc = device->funcs;
|
qfv_devfuncs_t *dfunc = device->funcs;
|
||||||
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM;
|
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||||
int canBlit = 1;
|
|
||||||
|
|
||||||
VkFormatProperties format_props;
|
VkFormatProperties format_props;
|
||||||
ifunc->vkGetPhysicalDeviceFormatProperties (device->physDev->dev,
|
ifunc->vkGetPhysicalDeviceFormatProperties (device->physDev->dev,
|
||||||
|
@ -52,66 +52,38 @@ QFV_CreateCapture (qfv_device_t *device, int numframes,
|
||||||
Sys_Printf ("Swapchain does not support reading. FIXME\n");
|
Sys_Printf ("Swapchain does not support reading. FIXME\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!(format_props.optimalTilingFeatures
|
|
||||||
& VK_FORMAT_FEATURE_BLIT_SRC_BIT)) {
|
|
||||||
Sys_MaskPrintf (SYS_vulkan,
|
|
||||||
"Device does not support blitting from optimal tiled "
|
|
||||||
"images.\n");
|
|
||||||
canBlit = 0;
|
|
||||||
}
|
|
||||||
ifunc->vkGetPhysicalDeviceFormatProperties (device->physDev->dev, format,
|
ifunc->vkGetPhysicalDeviceFormatProperties (device->physDev->dev, format,
|
||||||
&format_props);
|
&format_props);
|
||||||
if (!(format_props.linearTilingFeatures
|
|
||||||
& VK_FORMAT_FEATURE_BLIT_DST_BIT)) {
|
|
||||||
Sys_MaskPrintf (SYS_vulkan,
|
|
||||||
"Device does not support blitting from optimal tiled "
|
|
||||||
"images.\n");
|
|
||||||
canBlit = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
qfv_capture_t *capture = malloc (sizeof (qfv_capture_t));
|
qfv_capture_t *capture = malloc (sizeof (qfv_capture_t));
|
||||||
capture->device = device;
|
capture->device = device;
|
||||||
capture->canBlit = canBlit;
|
|
||||||
capture->extent = swapchain->extent;
|
capture->extent = swapchain->extent;
|
||||||
capture->image_set = QFV_AllocCaptureImageSet (numframes, malloc);
|
capture->image_set = QFV_AllocCaptureImageSet (numframes, malloc);
|
||||||
|
|
||||||
__auto_type cmdset = QFV_AllocCommandBufferSet (numframes, alloca);
|
__auto_type cmdset = QFV_AllocCommandBufferSet (numframes, alloca);
|
||||||
QFV_AllocateCommandBuffers (device, cmdPool, 1, cmdset);
|
QFV_AllocateCommandBuffers (device, cmdPool, 1, cmdset);
|
||||||
|
|
||||||
VkImageCreateInfo createInfo = {
|
capture->imgsize = QFV_GetImageSize (device,
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
swapchain->images->a[0]);
|
||||||
.imageType = VK_IMAGE_TYPE_2D,
|
|
||||||
.format = format,
|
|
||||||
.extent = { swapchain->extent.width, swapchain->extent.height, 1 },
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.mipLevels = 1,
|
|
||||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
||||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
|
||||||
.tiling = VK_IMAGE_TILING_LINEAR,
|
|
||||||
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < numframes; i++) {
|
for (int i = 0; i < numframes; i++) {
|
||||||
__auto_type image = &capture->image_set->a[i];
|
__auto_type image = &capture->image_set->a[i];
|
||||||
dfunc->vkCreateImage (device->dev, &createInfo, 0, &image->image);
|
image->buffer = QFV_CreateBuffer (device, capture->imgsize,
|
||||||
image->layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT);
|
||||||
image->cmd = cmdset->a[i];
|
|
||||||
}
|
}
|
||||||
capture->imgsize = QFV_GetImageSize (device,
|
|
||||||
capture->image_set->a[0].image);
|
|
||||||
capture->memsize = numframes * capture->imgsize;
|
capture->memsize = numframes * capture->imgsize;
|
||||||
capture->memory = QFV_AllocImageMemory (device,
|
capture->memory = QFV_AllocBufferMemory (device,
|
||||||
capture->image_set->a[0].image,
|
capture->image_set->a[0].buffer,
|
||||||
VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
|
VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
|
||||||
capture->memsize, 0);
|
capture->memsize, 0);
|
||||||
dfunc->vkMapMemory (device->dev, capture->memory, 0, capture->memsize, 0,
|
dfunc->vkMapMemory (device->dev, capture->memory, 0, capture->memsize, 0,
|
||||||
(void **) &capture->data);
|
(void **) &capture->data);
|
||||||
|
|
||||||
for (int i = 0; i < numframes; i++) {
|
for (int i = 0; i < numframes; i++) {
|
||||||
__auto_type image = &capture->image_set->a[i];
|
__auto_type image = &capture->image_set->a[i];
|
||||||
|
image->cmd = cmdset->a[i];
|
||||||
image->data = capture->data + i * capture->imgsize;
|
image->data = capture->data + i * capture->imgsize;
|
||||||
dfunc->vkBindImageMemory (device->dev, image->image, capture->memory,
|
QFV_BindBufferMemory (device, image->buffer, capture->memory,
|
||||||
image->data - capture->data);
|
i * capture->imgsize);
|
||||||
}
|
}
|
||||||
return capture;
|
return capture;
|
||||||
}
|
}
|
||||||
|
@ -124,7 +96,7 @@ QFV_DestroyCapture (qfv_capture_t *capture)
|
||||||
|
|
||||||
for (size_t i = 0; i < capture->image_set->size; i++) {
|
for (size_t i = 0; i < capture->image_set->size; i++) {
|
||||||
__auto_type image = &capture->image_set->a[i];
|
__auto_type image = &capture->image_set->a[i];
|
||||||
dfunc->vkDestroyImage (device->dev, image->image, 0);
|
dfunc->vkDestroyBuffer (device->dev, image->buffer, 0);
|
||||||
}
|
}
|
||||||
dfunc->vkUnmapMemory (device->dev, capture->memory);
|
dfunc->vkUnmapMemory (device->dev, capture->memory);
|
||||||
dfunc->vkFreeMemory (device->dev, capture->memory, 0);
|
dfunc->vkFreeMemory (device->dev, capture->memory, 0);
|
||||||
|
@ -132,35 +104,21 @@ QFV_DestroyCapture (qfv_capture_t *capture)
|
||||||
free (capture);
|
free (capture);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
blit_image (qfv_capture_t *capture, qfv_devfuncs_t *dfunc,
|
|
||||||
VkImage scImage, qfv_capture_image_t *image)
|
|
||||||
{
|
|
||||||
VkImageBlit blit = {
|
|
||||||
{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
|
||||||
{ { }, { capture->extent.width, capture->extent.height, 1 } },
|
|
||||||
{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
|
||||||
{ { }, { capture->extent.width, capture->extent.height, 1 } },
|
|
||||||
};
|
|
||||||
dfunc->vkCmdBlitImage (image->cmd,
|
|
||||||
scImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
||||||
image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
||||||
1, &blit, VK_FILTER_NEAREST);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
copy_image (qfv_capture_t *capture, qfv_devfuncs_t *dfunc,
|
copy_image (qfv_capture_t *capture, qfv_devfuncs_t *dfunc,
|
||||||
VkImage scImage, qfv_capture_image_t *image)
|
VkImage scImage, qfv_capture_image_t *image)
|
||||||
{
|
{
|
||||||
VkImageCopy copy = {
|
VkBufferImageCopy copy = {
|
||||||
{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }, { },
|
.bufferOffset = 0,
|
||||||
{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }, { },
|
.bufferRowLength = 0,
|
||||||
{ capture->extent.width, capture->extent.height, 1 },
|
.bufferImageHeight = 0,
|
||||||
|
.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 },
|
||||||
|
.imageOffset = { },
|
||||||
|
.imageExtent = { capture->extent.width, capture->extent.height, 1 },
|
||||||
};
|
};
|
||||||
dfunc->vkCmdCopyImage (image->cmd,
|
dfunc->vkCmdCopyImageToBuffer (image->cmd, scImage,
|
||||||
scImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
image->buffer, 1, ©);
|
||||||
1, ©);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VkCommandBuffer
|
VkCommandBuffer
|
||||||
|
@ -181,16 +139,17 @@ QFV_CaptureImage (qfv_capture_t *capture, VkImage scImage, int frame)
|
||||||
};
|
};
|
||||||
dfunc->vkBeginCommandBuffer (image->cmd, &beginInfo);
|
dfunc->vkBeginCommandBuffer (image->cmd, &beginInfo);
|
||||||
|
|
||||||
VkImageMemoryBarrier start_barriers[] = {
|
VkBufferMemoryBarrier start_buffer_barriers[] = {
|
||||||
{
|
{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||||
.srcAccessMask = 0,
|
.srcAccessMask = 0,
|
||||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
.oldLayout = image->layout,
|
.buffer = image->buffer,
|
||||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
.offset = 0,
|
||||||
.image = image->image,
|
.size = VK_WHOLE_SIZE,
|
||||||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 },
|
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
VkImageMemoryBarrier start_image_barriers[] = {
|
||||||
{
|
{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
|
.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
|
||||||
|
@ -201,16 +160,17 @@ QFV_CaptureImage (qfv_capture_t *capture, VkImage scImage, int frame)
|
||||||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 },
|
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
VkImageMemoryBarrier end_barriers[] = {
|
VkBufferMemoryBarrier end_buffer_barriers[] = {
|
||||||
{
|
{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
|
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
.buffer = image->buffer,
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
.offset = 0,
|
||||||
.image = image->image,
|
.size = VK_WHOLE_SIZE,
|
||||||
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 },
|
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
VkImageMemoryBarrier end_image_barriers[] = {
|
||||||
{
|
{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
|
@ -225,21 +185,18 @@ QFV_CaptureImage (qfv_capture_t *capture, VkImage scImage, int frame)
|
||||||
dfunc->vkCmdPipelineBarrier (image->cmd,
|
dfunc->vkCmdPipelineBarrier (image->cmd,
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0,
|
||||||
2, start_barriers);
|
1, start_buffer_barriers,
|
||||||
|
1, start_image_barriers);
|
||||||
|
|
||||||
if (capture->canBlit) {
|
copy_image (capture, dfunc, scImage, image);
|
||||||
blit_image (capture, dfunc, scImage, image);
|
|
||||||
} else {
|
|
||||||
copy_image (capture, dfunc, scImage, image);
|
|
||||||
}
|
|
||||||
|
|
||||||
dfunc->vkCmdPipelineBarrier (image->cmd,
|
dfunc->vkCmdPipelineBarrier (image->cmd,
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
0, 0, 0, 0, 0,
|
0, 0, 0,
|
||||||
2, end_barriers);
|
1, end_buffer_barriers,
|
||||||
image->layout = VK_IMAGE_LAYOUT_GENERAL;
|
1, end_image_barriers);
|
||||||
|
|
||||||
dfunc->vkEndCommandBuffer (image->cmd);
|
dfunc->vkEndCommandBuffer (image->cmd);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue