mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-01-31 05:00:35 +00:00
[vulkan] Implement screenshot
Finally, I can brag about my progress on irc :)
This commit is contained in:
parent
51d6ec7c8c
commit
c5c44da727
11 changed files with 109 additions and 18 deletions
|
@ -164,6 +164,7 @@ DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdPipelineBarrier)
|
|||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyBuffer)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyBufferToImage)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyImageToBuffer)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyImage)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBlitImage)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBeginRenderPass)
|
||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdNextSubpass)
|
||||
|
|
|
@ -66,6 +66,8 @@ VkImageView QFV_CreateImageView (struct qfv_device_s *device,
|
|||
VkImage image, VkImageViewType type,
|
||||
VkFormat format, VkImageAspectFlags aspect);
|
||||
|
||||
size_t QFV_GetImageSize (struct qfv_device_s *device, VkImage image);
|
||||
|
||||
/** Generate all mipmaps for a given texture down to a 1x1 pixel.
|
||||
*
|
||||
* Uses the GPU blit command from one mip level to the next, thus the base mip
|
||||
|
|
|
@ -60,6 +60,7 @@ enum {
|
|||
struct vulkan_ctx_s;
|
||||
void Vulkan_DestroyFrames (struct vulkan_ctx_s *ctx);
|
||||
void Vulkan_CreateFrames (struct vulkan_ctx_s *ctx);
|
||||
void Vulkan_CreateCapture (struct vulkan_ctx_s *ctx);
|
||||
void Vulkan_CreateFramebuffers (struct vulkan_ctx_s *ctx);
|
||||
void Vulkan_DestroyFramebuffers (struct vulkan_ctx_s *ctx);
|
||||
void Vulkan_CreateRenderPass (struct vulkan_ctx_s *ctx);
|
||||
|
|
|
@ -8,6 +8,7 @@ typedef struct qfv_swapchain_s {
|
|||
VkFormat format;
|
||||
VkExtent2D extent;
|
||||
int32_t numImages;
|
||||
VkImageUsageFlags usage;
|
||||
struct qfv_imageset_s *images;
|
||||
struct qfv_imageviewset_s *imageViews;
|
||||
} qfv_swapchain_t;
|
||||
|
|
|
@ -92,6 +92,9 @@ typedef struct vulkan_ctx_s {
|
|||
size_t curFrame;
|
||||
vulkan_frameset_t frames;
|
||||
|
||||
struct qfv_capture_s *capture;
|
||||
void (*capture_callback) (const byte *data, int width, int height);
|
||||
|
||||
struct qfv_tex_s *default_black;
|
||||
struct qfv_tex_s *default_white;
|
||||
struct qfv_tex_s *default_magenta;
|
||||
|
|
|
@ -87,21 +87,6 @@ static void vulkan_brush_clear (model_t *mod, void *data)
|
|||
dfunc->vkFreeMemory (device->dev, mctx->texture_memory, 0);
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_image_size (VkImage image, qfv_device_t *device)
|
||||
{
|
||||
qfv_devfuncs_t *dfunc = device->funcs;
|
||||
size_t size;
|
||||
size_t align;
|
||||
|
||||
VkMemoryRequirements requirements;
|
||||
dfunc->vkGetImageMemoryRequirements (device->dev, image, &requirements);
|
||||
size = requirements.size;
|
||||
align = requirements.alignment - 1;
|
||||
size = (size + align) & ~(align);
|
||||
return size;
|
||||
}
|
||||
|
||||
static void
|
||||
transfer_mips (byte *dst, const void *_src, const texture_t *tx, byte *palette)
|
||||
{
|
||||
|
@ -193,7 +178,7 @@ load_textures (model_t *mod, vulkan_ctx_t *ctx)
|
|||
}
|
||||
vulktex_t *tex = tx->render;
|
||||
tex->tex->offset = memsize;
|
||||
memsize += get_image_size (tex->tex->image, device);
|
||||
memsize += QFV_GetImageSize (device, tex->tex->image);
|
||||
image_count++;
|
||||
copy_count += MIPLEVELS;
|
||||
if (strncmp (tx->name, "sky", 3) == 0) {
|
||||
|
@ -204,7 +189,7 @@ load_textures (model_t *mod, vulkan_ctx_t *ctx)
|
|||
if (tex->glow) {
|
||||
copy_count += MIPLEVELS;
|
||||
tex->glow->offset = memsize;
|
||||
memsize += get_image_size (tex->glow->image, device);
|
||||
memsize += QFV_GetImageSize (device, tex->glow->image);
|
||||
image_count++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -250,6 +250,7 @@ libs_video_renderer_librender_vulkan_la_SOURCES = \
|
|||
libs/video/renderer/vulkan/barrier.c \
|
||||
libs/video/renderer/vulkan/buffer.c \
|
||||
libs/video/renderer/vulkan/command.c \
|
||||
libs/video/renderer/vulkan/capture.c \
|
||||
libs/video/renderer/vulkan/descriptor.c \
|
||||
libs/video/renderer/vulkan/device.c \
|
||||
libs/video/renderer/vulkan/image.c \
|
||||
|
|
|
@ -31,7 +31,10 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "QF/darray.h"
|
||||
#include "QF/dstring.h"
|
||||
#include "QF/quakefs.h"
|
||||
#include "QF/sys.h"
|
||||
#include "QF/va.h"
|
||||
|
||||
#include "QF/plugin/general.h"
|
||||
#include "QF/plugin/vid_render.h"
|
||||
|
@ -46,6 +49,7 @@
|
|||
#include "QF/Vulkan/qf_particles.h"
|
||||
#include "QF/Vulkan/qf_texture.h"
|
||||
#include "QF/Vulkan/qf_vid.h"
|
||||
#include "QF/Vulkan/capture.h"
|
||||
#include "QF/Vulkan/command.h"
|
||||
#include "QF/Vulkan/device.h"
|
||||
#include "QF/Vulkan/image.h"
|
||||
|
@ -90,6 +94,7 @@ vulkan_R_Init (void)
|
|||
Vulkan_CreateMatrices (vulkan_ctx);
|
||||
Vulkan_CreateSwapchain (vulkan_ctx);
|
||||
Vulkan_CreateFrames (vulkan_ctx);
|
||||
Vulkan_CreateCapture (vulkan_ctx);
|
||||
Vulkan_CreateRenderPass (vulkan_ctx);
|
||||
Vulkan_CreateFramebuffers (vulkan_ctx);
|
||||
Vulkan_Texture_Init (vulkan_ctx);
|
||||
|
@ -165,6 +170,12 @@ vulkan_R_RenderFrame (SCR_Func scr_3dfunc, SCR_Func *scr_funcs)
|
|||
}
|
||||
|
||||
dfunc->vkCmdEndRenderPass (frame->cmdBuffer);
|
||||
if (vulkan_ctx->capture_callback) {
|
||||
VkImage srcImage = vulkan_ctx->swapchain->images->a[imageIndex];
|
||||
VkCommandBuffer cmd = QFV_CaptureImage (vulkan_ctx->capture, srcImage,
|
||||
vulkan_ctx->curFrame);
|
||||
dfunc->vkCmdExecuteCommands (frame->cmdBuffer, 1, &cmd);
|
||||
}
|
||||
dfunc->vkEndCommandBuffer (frame->cmdBuffer);
|
||||
|
||||
VkPipelineStageFlags waitStage
|
||||
|
@ -178,6 +189,17 @@ vulkan_R_RenderFrame (SCR_Func scr_3dfunc, SCR_Func *scr_funcs)
|
|||
dfunc->vkResetFences (dev, 1, &frame->fence);
|
||||
dfunc->vkQueueSubmit (queue->queue, 1, &submitInfo, frame->fence);
|
||||
|
||||
if (vulkan_ctx->capture_callback) {
|
||||
//FIXME look into "threading" this rather than waiting here
|
||||
dfunc->vkWaitForFences (device->dev, 1, &frame->fence, VK_TRUE,
|
||||
1000000000ull);
|
||||
vulkan_ctx->capture_callback (QFV_CaptureData (vulkan_ctx->capture,
|
||||
vulkan_ctx->curFrame),
|
||||
vulkan_ctx->capture->extent.width,
|
||||
vulkan_ctx->capture->extent.height);
|
||||
vulkan_ctx->capture_callback = 0;
|
||||
}
|
||||
|
||||
VkPresentInfoKHR presentInfo = {
|
||||
VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 0,
|
||||
1, &frame->renderDoneSemaphore,
|
||||
|
@ -358,9 +380,55 @@ vulkan_R_InitParticles (void)
|
|||
Vulkan_InitParticles (vulkan_ctx);
|
||||
}
|
||||
|
||||
static int
|
||||
is_bgr (VkFormat format)
|
||||
{
|
||||
return (format >= VK_FORMAT_B8G8R8A8_UNORM
|
||||
&& format <= VK_FORMAT_B8G8R8A8_SRGB);
|
||||
}
|
||||
|
||||
static void
|
||||
capture_screenshot (const byte *data, int width, int height)
|
||||
{
|
||||
dstring_t *name = dstring_new ();
|
||||
// find a file name to save it to
|
||||
if (!QFS_NextFilename (name, va (vulkan_ctx->va_ctx, "%s/qf",
|
||||
qfs_gamedir->dir.shots),
|
||||
".ppm")) {
|
||||
Sys_Printf ("SCR_ScreenShot_f: Couldn't create a ppm file\n");
|
||||
} else {
|
||||
QFile *file = QFS_Open (name->str, "wb");
|
||||
if (!file) {
|
||||
Sys_Printf ("Couldn't open %s\n", name->str);
|
||||
} else {
|
||||
Qprintf (file, "P6\n%d\n%d\n255\n", width, height);
|
||||
if (vulkan_ctx->capture->canBlit ||
|
||||
!is_bgr (vulkan_ctx->swapchain->format)) {
|
||||
for (int count = width * height; count-- > 0; ) {
|
||||
Qwrite (file, data, 3);
|
||||
data += 4;
|
||||
}
|
||||
} else {
|
||||
for (int count = width * height; count-- > 0; ) {
|
||||
byte rgb[] = { data[2], data[1], data[0] };
|
||||
Qwrite (file, rgb, 3);
|
||||
data += 4;
|
||||
}
|
||||
}
|
||||
Qclose (file);
|
||||
}
|
||||
}
|
||||
dstring_delete (name);
|
||||
}
|
||||
|
||||
static void
|
||||
vulkan_SCR_ScreenShot_f (void)
|
||||
{
|
||||
if (!vulkan_ctx->capture) {
|
||||
Sys_Printf ("Screenshot not supported\n");
|
||||
return;
|
||||
}
|
||||
vulkan_ctx->capture_callback = capture_screenshot;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -194,6 +194,21 @@ QFV_CreateImageView (qfv_device_t *device, VkImage image,
|
|||
return view;
|
||||
}
|
||||
|
||||
size_t
|
||||
QFV_GetImageSize (qfv_device_t *device, VkImage image)
|
||||
{
|
||||
qfv_devfuncs_t *dfunc = device->funcs;
|
||||
size_t size;
|
||||
size_t align;
|
||||
|
||||
VkMemoryRequirements requirements;
|
||||
dfunc->vkGetImageMemoryRequirements (device->dev, image, &requirements);
|
||||
size = requirements.size;
|
||||
align = requirements.alignment - 1;
|
||||
size = (size + align) & ~(align);
|
||||
return size;
|
||||
}
|
||||
|
||||
void
|
||||
QFV_GenerateMipMaps (qfv_device_t *device, VkCommandBuffer cmd,
|
||||
VkImage image, unsigned mips,
|
||||
|
|
|
@ -83,8 +83,10 @@ QFV_CreateSwapchain (vulkan_ctx_t *ctx, VkSwapchainKHR old_swapchain)
|
|||
imageSize.width, imageSize.height);
|
||||
|
||||
VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
imageUsage &= surfCaps.supportedUsageFlags;
|
||||
Sys_MaskPrintf (SYS_VULKAN, "%x %x\n", imageUsage,
|
||||
surfCaps.supportedUsageFlags);
|
||||
|
||||
VkSurfaceTransformFlagBitsKHR surfTransform
|
||||
= VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
|
@ -147,6 +149,7 @@ QFV_CreateSwapchain (vulkan_ctx_t *ctx, VkSwapchainKHR old_swapchain)
|
|||
sc->format = useFormat.format;
|
||||
sc->extent = imageSize;
|
||||
sc->numImages = numImages;
|
||||
sc->usage = imageUsage;
|
||||
sc->images = DARRAY_ALLOCFIXED (qfv_imageset_t, numImages, malloc);
|
||||
sc->imageViews = DARRAY_ALLOCFIXED (qfv_imageviewset_t, numImages, malloc);
|
||||
dfuncs->vkGetSwapchainImagesKHR (dev, swapchain, &numImages, sc->images->a);
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "QF/Vulkan/qf_vid.h"
|
||||
#include "QF/Vulkan/barrier.h"
|
||||
#include "QF/Vulkan/buffer.h"
|
||||
#include "QF/Vulkan/capture.h"
|
||||
#include "QF/Vulkan/debug.h"
|
||||
#include "QF/Vulkan/descriptor.h"
|
||||
#include "QF/Vulkan/device.h"
|
||||
|
@ -189,6 +190,9 @@ Vulkan_Shutdown_Common (vulkan_ctx_t *ctx)
|
|||
{
|
||||
PL_Free (ctx->pipelineDef);
|
||||
PL_Free (ctx->renderpassDef);
|
||||
if (ctx->capture) {
|
||||
QFV_DestroyCapture (ctx->capture);
|
||||
}
|
||||
if (ctx->frames.size) {
|
||||
Vulkan_DestroyFrames (ctx);
|
||||
}
|
||||
|
@ -535,6 +539,13 @@ Vulkan_CreateFrames (vulkan_ctx_t *ctx)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Vulkan_CreateCapture (vulkan_ctx_t *ctx)
|
||||
{
|
||||
ctx->capture = QFV_CreateCapture (ctx->device, ctx->frames.size,
|
||||
ctx->swapchain, ctx->cmdpool);
|
||||
}
|
||||
|
||||
void
|
||||
Vulkan_DestroyFrames (vulkan_ctx_t *ctx)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue