mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-10 15:22:04 +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 (vkCmdCopyBuffer)
|
||||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyBufferToImage)
|
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyBufferToImage)
|
||||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyImageToBuffer)
|
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyImageToBuffer)
|
||||||
|
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdCopyImage)
|
||||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBlitImage)
|
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBlitImage)
|
||||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBeginRenderPass)
|
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdBeginRenderPass)
|
||||||
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdNextSubpass)
|
DEVICE_LEVEL_VULKAN_FUNCTION (vkCmdNextSubpass)
|
||||||
|
|
|
@ -66,6 +66,8 @@ VkImageView QFV_CreateImageView (struct qfv_device_s *device,
|
||||||
VkImage image, VkImageViewType type,
|
VkImage image, VkImageViewType type,
|
||||||
VkFormat format, VkImageAspectFlags aspect);
|
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.
|
/** 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
|
* 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;
|
struct vulkan_ctx_s;
|
||||||
void Vulkan_DestroyFrames (struct vulkan_ctx_s *ctx);
|
void Vulkan_DestroyFrames (struct vulkan_ctx_s *ctx);
|
||||||
void Vulkan_CreateFrames (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_CreateFramebuffers (struct vulkan_ctx_s *ctx);
|
||||||
void Vulkan_DestroyFramebuffers (struct vulkan_ctx_s *ctx);
|
void Vulkan_DestroyFramebuffers (struct vulkan_ctx_s *ctx);
|
||||||
void Vulkan_CreateRenderPass (struct vulkan_ctx_s *ctx);
|
void Vulkan_CreateRenderPass (struct vulkan_ctx_s *ctx);
|
||||||
|
|
|
@ -8,6 +8,7 @@ typedef struct qfv_swapchain_s {
|
||||||
VkFormat format;
|
VkFormat format;
|
||||||
VkExtent2D extent;
|
VkExtent2D extent;
|
||||||
int32_t numImages;
|
int32_t numImages;
|
||||||
|
VkImageUsageFlags usage;
|
||||||
struct qfv_imageset_s *images;
|
struct qfv_imageset_s *images;
|
||||||
struct qfv_imageviewset_s *imageViews;
|
struct qfv_imageviewset_s *imageViews;
|
||||||
} qfv_swapchain_t;
|
} qfv_swapchain_t;
|
||||||
|
|
|
@ -92,6 +92,9 @@ typedef struct vulkan_ctx_s {
|
||||||
size_t curFrame;
|
size_t curFrame;
|
||||||
vulkan_frameset_t frames;
|
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_black;
|
||||||
struct qfv_tex_s *default_white;
|
struct qfv_tex_s *default_white;
|
||||||
struct qfv_tex_s *default_magenta;
|
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);
|
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
|
static void
|
||||||
transfer_mips (byte *dst, const void *_src, const texture_t *tx, byte *palette)
|
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;
|
vulktex_t *tex = tx->render;
|
||||||
tex->tex->offset = memsize;
|
tex->tex->offset = memsize;
|
||||||
memsize += get_image_size (tex->tex->image, device);
|
memsize += QFV_GetImageSize (device, tex->tex->image);
|
||||||
image_count++;
|
image_count++;
|
||||||
copy_count += MIPLEVELS;
|
copy_count += MIPLEVELS;
|
||||||
if (strncmp (tx->name, "sky", 3) == 0) {
|
if (strncmp (tx->name, "sky", 3) == 0) {
|
||||||
|
@ -204,7 +189,7 @@ load_textures (model_t *mod, vulkan_ctx_t *ctx)
|
||||||
if (tex->glow) {
|
if (tex->glow) {
|
||||||
copy_count += MIPLEVELS;
|
copy_count += MIPLEVELS;
|
||||||
tex->glow->offset = memsize;
|
tex->glow->offset = memsize;
|
||||||
memsize += get_image_size (tex->glow->image, device);
|
memsize += QFV_GetImageSize (device, tex->glow->image);
|
||||||
image_count++;
|
image_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,6 +250,7 @@ libs_video_renderer_librender_vulkan_la_SOURCES = \
|
||||||
libs/video/renderer/vulkan/barrier.c \
|
libs/video/renderer/vulkan/barrier.c \
|
||||||
libs/video/renderer/vulkan/buffer.c \
|
libs/video/renderer/vulkan/buffer.c \
|
||||||
libs/video/renderer/vulkan/command.c \
|
libs/video/renderer/vulkan/command.c \
|
||||||
|
libs/video/renderer/vulkan/capture.c \
|
||||||
libs/video/renderer/vulkan/descriptor.c \
|
libs/video/renderer/vulkan/descriptor.c \
|
||||||
libs/video/renderer/vulkan/device.c \
|
libs/video/renderer/vulkan/device.c \
|
||||||
libs/video/renderer/vulkan/image.c \
|
libs/video/renderer/vulkan/image.c \
|
||||||
|
|
|
@ -31,7 +31,10 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "QF/darray.h"
|
#include "QF/darray.h"
|
||||||
|
#include "QF/dstring.h"
|
||||||
|
#include "QF/quakefs.h"
|
||||||
#include "QF/sys.h"
|
#include "QF/sys.h"
|
||||||
|
#include "QF/va.h"
|
||||||
|
|
||||||
#include "QF/plugin/general.h"
|
#include "QF/plugin/general.h"
|
||||||
#include "QF/plugin/vid_render.h"
|
#include "QF/plugin/vid_render.h"
|
||||||
|
@ -46,6 +49,7 @@
|
||||||
#include "QF/Vulkan/qf_particles.h"
|
#include "QF/Vulkan/qf_particles.h"
|
||||||
#include "QF/Vulkan/qf_texture.h"
|
#include "QF/Vulkan/qf_texture.h"
|
||||||
#include "QF/Vulkan/qf_vid.h"
|
#include "QF/Vulkan/qf_vid.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"
|
||||||
#include "QF/Vulkan/image.h"
|
#include "QF/Vulkan/image.h"
|
||||||
|
@ -90,6 +94,7 @@ vulkan_R_Init (void)
|
||||||
Vulkan_CreateMatrices (vulkan_ctx);
|
Vulkan_CreateMatrices (vulkan_ctx);
|
||||||
Vulkan_CreateSwapchain (vulkan_ctx);
|
Vulkan_CreateSwapchain (vulkan_ctx);
|
||||||
Vulkan_CreateFrames (vulkan_ctx);
|
Vulkan_CreateFrames (vulkan_ctx);
|
||||||
|
Vulkan_CreateCapture (vulkan_ctx);
|
||||||
Vulkan_CreateRenderPass (vulkan_ctx);
|
Vulkan_CreateRenderPass (vulkan_ctx);
|
||||||
Vulkan_CreateFramebuffers (vulkan_ctx);
|
Vulkan_CreateFramebuffers (vulkan_ctx);
|
||||||
Vulkan_Texture_Init (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);
|
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);
|
dfunc->vkEndCommandBuffer (frame->cmdBuffer);
|
||||||
|
|
||||||
VkPipelineStageFlags waitStage
|
VkPipelineStageFlags waitStage
|
||||||
|
@ -178,6 +189,17 @@ vulkan_R_RenderFrame (SCR_Func scr_3dfunc, SCR_Func *scr_funcs)
|
||||||
dfunc->vkResetFences (dev, 1, &frame->fence);
|
dfunc->vkResetFences (dev, 1, &frame->fence);
|
||||||
dfunc->vkQueueSubmit (queue->queue, 1, &submitInfo, 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 = {
|
VkPresentInfoKHR presentInfo = {
|
||||||
VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 0,
|
VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 0,
|
||||||
1, &frame->renderDoneSemaphore,
|
1, &frame->renderDoneSemaphore,
|
||||||
|
@ -358,9 +380,55 @@ vulkan_R_InitParticles (void)
|
||||||
Vulkan_InitParticles (vulkan_ctx);
|
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
|
static void
|
||||||
vulkan_SCR_ScreenShot_f (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
|
static void
|
||||||
|
|
|
@ -194,6 +194,21 @@ QFV_CreateImageView (qfv_device_t *device, VkImage image,
|
||||||
return view;
|
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
|
void
|
||||||
QFV_GenerateMipMaps (qfv_device_t *device, VkCommandBuffer cmd,
|
QFV_GenerateMipMaps (qfv_device_t *device, VkCommandBuffer cmd,
|
||||||
VkImage image, unsigned mips,
|
VkImage image, unsigned mips,
|
||||||
|
|
|
@ -83,8 +83,10 @@ QFV_CreateSwapchain (vulkan_ctx_t *ctx, VkSwapchainKHR old_swapchain)
|
||||||
imageSize.width, imageSize.height);
|
imageSize.width, imageSize.height);
|
||||||
|
|
||||||
VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
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;
|
imageUsage &= surfCaps.supportedUsageFlags;
|
||||||
|
Sys_MaskPrintf (SYS_VULKAN, "%x %x\n", imageUsage,
|
||||||
|
surfCaps.supportedUsageFlags);
|
||||||
|
|
||||||
VkSurfaceTransformFlagBitsKHR surfTransform
|
VkSurfaceTransformFlagBitsKHR surfTransform
|
||||||
= VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
= VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||||
|
@ -147,6 +149,7 @@ QFV_CreateSwapchain (vulkan_ctx_t *ctx, VkSwapchainKHR old_swapchain)
|
||||||
sc->format = useFormat.format;
|
sc->format = useFormat.format;
|
||||||
sc->extent = imageSize;
|
sc->extent = imageSize;
|
||||||
sc->numImages = numImages;
|
sc->numImages = numImages;
|
||||||
|
sc->usage = imageUsage;
|
||||||
sc->images = DARRAY_ALLOCFIXED (qfv_imageset_t, numImages, malloc);
|
sc->images = DARRAY_ALLOCFIXED (qfv_imageset_t, numImages, malloc);
|
||||||
sc->imageViews = DARRAY_ALLOCFIXED (qfv_imageviewset_t, numImages, malloc);
|
sc->imageViews = DARRAY_ALLOCFIXED (qfv_imageviewset_t, numImages, malloc);
|
||||||
dfuncs->vkGetSwapchainImagesKHR (dev, swapchain, &numImages, sc->images->a);
|
dfuncs->vkGetSwapchainImagesKHR (dev, swapchain, &numImages, sc->images->a);
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
#include "QF/Vulkan/qf_vid.h"
|
#include "QF/Vulkan/qf_vid.h"
|
||||||
#include "QF/Vulkan/barrier.h"
|
#include "QF/Vulkan/barrier.h"
|
||||||
#include "QF/Vulkan/buffer.h"
|
#include "QF/Vulkan/buffer.h"
|
||||||
|
#include "QF/Vulkan/capture.h"
|
||||||
#include "QF/Vulkan/debug.h"
|
#include "QF/Vulkan/debug.h"
|
||||||
#include "QF/Vulkan/descriptor.h"
|
#include "QF/Vulkan/descriptor.h"
|
||||||
#include "QF/Vulkan/device.h"
|
#include "QF/Vulkan/device.h"
|
||||||
|
@ -189,6 +190,9 @@ Vulkan_Shutdown_Common (vulkan_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
PL_Free (ctx->pipelineDef);
|
PL_Free (ctx->pipelineDef);
|
||||||
PL_Free (ctx->renderpassDef);
|
PL_Free (ctx->renderpassDef);
|
||||||
|
if (ctx->capture) {
|
||||||
|
QFV_DestroyCapture (ctx->capture);
|
||||||
|
}
|
||||||
if (ctx->frames.size) {
|
if (ctx->frames.size) {
|
||||||
Vulkan_DestroyFrames (ctx);
|
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
|
void
|
||||||
Vulkan_DestroyFrames (vulkan_ctx_t *ctx)
|
Vulkan_DestroyFrames (vulkan_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue