[renderer] Make screen capture support asynchronous operation

This fixes (*ahem*) the vulkan renderer segfaulting when attempting to
take a screenshot. However, the image is upside down. Also, remote
snapshots and demo capture are broken for the moment.
This commit is contained in:
Bill Currie 2022-03-31 19:07:15 +09:00
parent a29836cc2c
commit acffacc59b
15 changed files with 183 additions and 266 deletions

View file

@ -76,6 +76,9 @@ typedef struct vid_model_funcs_s {
void (*Skin_InitTranslations) (void);
} vid_model_funcs_t;
struct tex_s;
typedef void (*capfunc_t) (struct tex_s *screencap, void *data);
typedef struct vid_render_funcs_s {
void (*init) (void);
void (*Draw_Character) (int x, int y, unsigned ch);
@ -99,7 +102,6 @@ typedef struct vid_render_funcs_s {
void (*Draw_Picf) (float x, float y, qpic_t *pic);
void (*Draw_SubPic) (int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height);
struct tex_s *(*SCR_CaptureBGR) (void);
struct psystem_s *(*ParticleSystem) (void);
void (*R_Init) (void);
@ -124,6 +126,8 @@ typedef struct vid_render_funcs_s {
// x and y are tan(f/2) for fov_x and fov_y
void (*set_fov) (float x, float y);
void (*capture_screen) (capfunc_t callback, void *data);
vid_model_funcs_t *model_funcs;
} vid_render_funcs_t;

View file

@ -32,9 +32,6 @@ typedef struct gl_framebuffer_s {
extern gl_ctx_t *gl_ctx;
extern gl_ctx_t *glsl_ctx;
struct tex_s *gl_SCR_CaptureBGR (void);
struct tex_s *glsl_SCR_CaptureBGR (void);
void gl_Fog_SetupFrame (void);
void gl_Fog_EnableGFog (void);
void gl_Fog_DisableGFog (void);

View file

@ -20,6 +20,4 @@ typedef struct sw_framebuffer_s {
extern sw_ctx_t *sw_ctx;
struct tex_s *sw_SCR_CaptureBGR (void);
#endif//__vid_sw_h

View file

@ -79,6 +79,9 @@ typedef struct vulkan_ctx_s {
struct qfv_capture_s *capture;
void (*capture_callback) (const byte *data, int width, int height);
// make a queue?
void *capture_complete;// really capfunc_t
void *capture_complete_data;
struct qfv_tex_s *default_black;
struct qfv_tex_s *default_white;

View file

@ -80,7 +80,6 @@ libs_video_renderer_librender_gl_la_SOURCES = \
libs/video/renderer/gl/gl_rmain.c \
libs/video/renderer/gl/gl_rmisc.c \
libs/video/renderer/gl/gl_rsurf.c \
libs/video/renderer/gl/gl_screen.c \
libs/video/renderer/gl/gl_sky.c \
libs/video/renderer/gl/gl_sky_clip.c \
libs/video/renderer/gl/gl_textures.c \
@ -116,7 +115,6 @@ libs_video_renderer_librender_glsl_la_SOURCES = \
libs/video/renderer/glsl/glsl_lightmap.c \
libs/video/renderer/glsl/glsl_main.c \
libs/video/renderer/glsl/glsl_particles.c \
libs/video/renderer/glsl/glsl_screen.c \
libs/video/renderer/glsl/glsl_shader.c \
libs/video/renderer/glsl/glsl_sprite.c \
libs/video/renderer/glsl/glsl_textures.c \
@ -158,7 +156,6 @@ libs_video_renderer_librender_sw_la_SOURCES = \
libs/video/renderer/sw/fpu.c \
libs/video/renderer/sw/fpua.S \
libs/video/renderer/sw/nonintel.c \
libs/video/renderer/sw/screen.c \
libs/video/renderer/sw/surf8.S \
libs/video/renderer/sw/sw_fisheye.c \
libs/video/renderer/sw/sw_graph.c \

View file

@ -1,62 +0,0 @@
/*
gl_screen.c
master for refresh, status bar, console, chat, notify, etc
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include "QF/image.h"
#include "QF/sys.h"
#include "QF/GL/defines.h"
#include "QF/GL/funcs.h"
#include "QF/GL/qf_rmain.h"
#include "r_internal.h"
#include "vid_gl.h"
/* SCREEN SHOTS */
tex_t *
gl_SCR_CaptureBGR (void)
{
int count;
tex_t *tex;
count = vid.width * vid.height;
tex = malloc (sizeof (tex_t) + count * 3);
tex->data = (byte *) (tex + 1);
SYS_CHECKMEM (tex);
tex->width = vid.width;
tex->height = vid.height;
tex->format = tex_rgb;
tex->palette = 0;
qfglReadPixels (0, 0, tex->width, tex->height, GL_BGR_EXT,
GL_UNSIGNED_BYTE, tex->data);
return tex;
}

View file

@ -1,69 +0,0 @@
/*
glsl_main.c
GLSL rendering
Copyright (C) 2011 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2011/12/23
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include "QF/image.h"
#include "QF/sys.h"
#include "QF/GLSL/defines.h"
#include "QF/GLSL/funcs.h"
#include "r_internal.h"
#include "vid_gl.h"
tex_t *
glsl_SCR_CaptureBGR (void)
{
byte *r, *b;
int count, i;
tex_t *tex;
count = vid.width * vid.height;
tex = malloc (sizeof (tex_t) + count * 3);
tex->data = (byte *) (tex + 1);
SYS_CHECKMEM (tex);
tex->width = vid.width;
tex->height = vid.height;
tex->format = tex_rgb;
tex->palette = 0;
qfeglReadPixels (0, 0, vid.width, vid.height, GL_RGB,
GL_UNSIGNED_BYTE, tex->data);
for (i = 0, r = tex->data, b = tex->data + 2; i < count;
i++, r += 3, b += 3) {
byte t = *b;
*b = *r;
*r = t;
}
return tex;
}

View file

@ -357,6 +357,28 @@ SCR_SetBottomMargin (int lines)
update_vrect ();
}
typedef struct scr_capture_s {
dstring_t *name;
QFile *file;
} scr_capture_t;
static void
scr_write_caputre (tex_t *tex, void *data)
{
scr_capture_t *cap = data;
if (tex) {
WritePNG (cap->file, tex->data, tex->width, tex->height);
free (tex);
Sys_Printf ("Wrote %s/%s\n", qfs_userpath, cap->name->str);
} else {
Sys_Printf ("Capture failed\n");
}
Qclose (cap->file);
dstring_delete (cap->name);
free (cap);
}
static void
ScreenShot_f (void)
{
@ -368,16 +390,13 @@ ScreenShot_f (void)
".png"))) {
Sys_Printf ("SCR_ScreenShot_f: Couldn't create a PNG file: %s\n",
name->str);
dstring_delete (name);
} else {
tex_t *tex;
tex = r_funcs->SCR_CaptureBGR ();
WritePNG (file, tex->data, tex->width, tex->height);
free (tex);
Qclose (file);
Sys_Printf ("Wrote %s/%s\n", qfs_userpath, name->str);
scr_capture_t *cap = malloc (sizeof (scr_capture_t));
cap->file = file;
cap->name = name;
r_funcs->capture_screen (scr_write_caputre, cap);
}
dstring_delete (name);
}
/*
@ -463,6 +482,7 @@ SCR_DrawPause (void)
/*
Find closest color in the palette for named color
*/
#if 0
static int
MipColor (int r, int g, int b)
{
@ -495,6 +515,7 @@ MipColor (int r, int g, int b)
lastbest = best;
return best;
}
#endif
// in draw.c
@ -542,6 +563,8 @@ SCR_DrawStringToSnap (const char *s, tex_t *tex, int x, int y)
tex_t *
SCR_SnapScreen (unsigned width, unsigned height)
{
return 0;
#if 0
byte *src, *dest;
float fracw, frach;
unsigned count, dex, dey, dx, dy, nx, r, g, b, x, y, w, h;
@ -599,6 +622,7 @@ SCR_SnapScreen (unsigned width, unsigned height)
free (snap);
return tex;
#endif
}
static void

View file

@ -1,71 +0,0 @@
/*
screen.c
master for refresh, status bar, console, chat, notify, etc
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include "QF/image.h"
#include "QF/sys.h"
#include "r_internal.h"
#include "vid_sw.h"
/* SCREEN SHOTS */
tex_t *
sw_SCR_CaptureBGR (void)
{
int count, x, y;
tex_t *tex;
const byte *src;
byte *dst;
framebuffer_t *fb = sw_ctx->framebuffer;
count = fb->width * fb->height;
tex = malloc (sizeof (tex_t) + count * 3);
tex->data = (byte *) (tex + 1);
SYS_CHECKMEM (tex);
tex->width = fb->width;
tex->height = fb->height;
tex->format = tex_rgb;
tex->palette = 0;
src = ((sw_framebuffer_t *) fb->buffer)->color;
int rowbytes = ((sw_framebuffer_t *) fb->buffer)->rowbytes;
for (y = 0; y < tex->height; y++) {
dst = tex->data + (tex->height - 1 - y) * tex->width * 3;
for (x = 0; x < tex->width; x++) {
byte c = src[x];
*dst++ = vid.basepal[c * 3 + 2]; // blue
*dst++ = vid.basepal[c * 3 + 1]; // green
*dst++ = vid.basepal[c * 3 + 0]; // red
}
src += rowbytes;
}
return tex;
}

View file

@ -29,6 +29,7 @@
#endif
#include "QF/cvar.h"
#include "QF/image.h"
#include "QF/plugin/general.h"
#include "QF/plugin/vid_render.h"
@ -458,6 +459,26 @@ gl_set_fov (float x, float y)
mmulf (gl_ctx->projection, depth_range, proj);
}
static void
gl_capture_screen (capfunc_t callback, void *data)
{
int count;
tex_t *tex;
count = vid.width * vid.height;
tex = malloc (sizeof (tex_t) + count * 3);
if (tex) {
tex->data = (byte *) (tex + 1);
tex->width = vid.width;
tex->height = vid.height;
tex->format = tex_rgb;
tex->palette = 0;
qfglReadPixels (0, 0, tex->width, tex->height, GL_BGR_EXT,
GL_UNSIGNED_BYTE, tex->data);
}
callback (tex, data);
}
vid_render_funcs_t gl_vid_render_funcs = {
gl_vid_render_init,
gl_Draw_Character,
@ -481,8 +502,6 @@ vid_render_funcs_t gl_vid_render_funcs = {
gl_Draw_Picf,
gl_Draw_SubPic,
gl_SCR_CaptureBGR,
gl_ParticleSystem,
gl_R_Init,
gl_R_ClearState,
@ -504,6 +523,8 @@ vid_render_funcs_t gl_vid_render_funcs = {
gl_set_viewport,
gl_set_fov,
gl_capture_screen,
&model_funcs
};

View file

@ -29,6 +29,7 @@
#endif
#include "QF/cvar.h"
#include "QF/image.h"
#include "QF/plugin/general.h"
#include "QF/plugin/vid_render.h"
@ -405,6 +406,35 @@ glsl_set_fov (float x, float y)
mmulf (glsl_ctx->projection, depth_range, proj);
}
static void
glsl_capture_screen (capfunc_t callback, void *data)
{
byte *r, *b;
int count, i;
tex_t *tex;
count = vid.width * vid.height;
tex = malloc (sizeof (tex_t) + count * 3);
if (tex) {
tex->data = (byte *) (tex + 1);
tex->width = vid.width;
tex->height = vid.height;
tex->format = tex_rgb;
tex->palette = 0;
qfeglReadPixels (0, 0, tex->width, tex->height, GL_RGB,
GL_UNSIGNED_BYTE, tex->data);
//FIXME shouldn't have to swap between rgb and bgr since WritePNG
//swaps back
for (i = 0, r = tex->data, b = tex->data + 2; i < count;
i++, r+= 3, b += 3) {
byte t = *b;
*b = *r;
*r = t;
}
}
callback (tex, data);
}
vid_render_funcs_t glsl_vid_render_funcs = {
glsl_vid_render_init,
glsl_Draw_Character,
@ -428,8 +458,6 @@ vid_render_funcs_t glsl_vid_render_funcs = {
glsl_Draw_Picf,
glsl_Draw_SubPic,
glsl_SCR_CaptureBGR,
glsl_ParticleSystem,
glsl_R_Init,
glsl_R_ClearState,
@ -451,6 +479,8 @@ vid_render_funcs_t glsl_vid_render_funcs = {
glsl_set_viewport,
glsl_set_fov,
glsl_capture_screen,
&model_funcs
};

View file

@ -31,6 +31,7 @@
#include <string.h>
#include "QF/cvar.h"
#include "QF/image.h"
#include "QF/plugin/general.h"
#include "QF/plugin/vid_render.h"
@ -411,6 +412,41 @@ sw_set_fov (float x, float y)
r_resfudge = r_aliastransadj->value * res_scale;
}
static void
sw_capture_screen (capfunc_t callback, void *data)
{
int count, x, y;
tex_t *tex;
const byte *src;
byte *dst;
framebuffer_t *fb = sw_ctx->framebuffer;
count = fb->width * fb->height;
tex = malloc (sizeof (tex_t) + count * 3);
if (tex) {
tex->data = (byte *) (tex + 1);
tex->width = fb->width;
tex->height = fb->height;
tex->format = tex_rgb;
tex->palette = 0;
src = ((sw_framebuffer_t *) fb->buffer)->color;
int rowbytes = ((sw_framebuffer_t *) fb->buffer)->rowbytes;
//FIXME shouldn't have to swap between rgb and bgr since WritePNG
//swaps back
for (y = 0; y < tex->height; y++) {
dst = tex->data + (tex->height - 1 - y) * tex->width * 3;
for (x = 0; x < tex->width; x++) {
byte c = src[x];
*dst++ = vid.basepal[c * 3 + 2]; // blue
*dst++ = vid.basepal[c * 3 + 1]; // green
*dst++ = vid.basepal[c * 3 + 0]; // red
}
src += rowbytes;
}
}
callback (tex, data);
}
vid_render_funcs_t sw_vid_render_funcs = {
sw_vid_render_init,
Draw_Character,
@ -434,8 +470,6 @@ vid_render_funcs_t sw_vid_render_funcs = {
Draw_Picf,
Draw_SubPic,
sw_SCR_CaptureBGR,
sw_ParticleSystem,
sw_R_Init,
R_ClearState,
@ -457,6 +491,8 @@ vid_render_funcs_t sw_vid_render_funcs = {
sw_set_viewport,
sw_set_fov,
sw_capture_screen,
&model_funcs
};

View file

@ -462,60 +462,69 @@ vulkan_set_fov (float x, float y)
mctx->dirty = mctx->frames.size;
}
#if 0
static int
is_bgr (VkFormat format)
{
return (format >= VK_FORMAT_B8G8R8A8_UNORM
&& format <= VK_FORMAT_B8G8R8A8_SRGB);
}
#endif
static void
capture_screenshot (const byte *data, int width, int height)
{
#if 0
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;
}
int count = width * height;
tex_t *tex = malloc (sizeof (tex_t) + count * 3);
if (tex) {
tex->data = (byte *) (tex + 1);
tex->width = width;
tex->height = height;
tex->format = tex_rgb;
tex->palette = 0;
//FIXME shouldn't have to swap between rgb and bgr since WritePNG
//swaps back (ie, it can work with either)
//can it auto-convert RGBA to RGB?
if (!vulkan_ctx->capture->canBlit ||
is_bgr (vulkan_ctx->swapchain->format)) {
const byte *src = data;
byte *dst = tex->data;
for (int count = width * height; count-- > 0; ) {
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
src++;
}
} else {
const byte *src = data;
byte *dst = tex->data;
for (int count = width * height; count-- > 0; ) {
byte r = *src++;
byte g = *src++;
byte b = *src++;
*dst++ = b;
*dst++ = g;
*dst++ = r;
src++;
}
Qclose (file);
}
}
dstring_delete (name);
#endif
capfunc_t callback = vulkan_ctx->capture_complete;
callback (tex, vulkan_ctx->capture_complete_data);;
}
static tex_t *
vulkan_SCR_CaptureBGR (void)
static void
vulkan_capture_screen (capfunc_t callback, void *data)
{
if (!vulkan_ctx->capture) {
Sys_Printf ("Capture not supported\n");
return 0;
callback (0, data);
return;
}
vulkan_ctx->capture_callback = capture_screenshot;
//FIXME async process
return 0;
vulkan_ctx->capture_complete = callback;
vulkan_ctx->capture_complete_data = data;
}
static void
@ -727,8 +736,6 @@ vid_render_funcs_t vulkan_vid_render_funcs = {
vulkan_Draw_Picf,
vulkan_Draw_SubPic,
vulkan_SCR_CaptureBGR,
vulkan_ParticleSystem,
vulkan_R_Init,
vulkan_R_ClearState,
@ -750,6 +757,8 @@ vid_render_funcs_t vulkan_vid_render_funcs = {
vulkan_set_viewport,
vulkan_set_fov,
vulkan_capture_screen,
&model_funcs
};

View file

@ -699,7 +699,7 @@ _Host_Frame (float time)
Host_ClientFrame ();
else
host_time += host_frametime; //FIXME is this needed? vcr stuff
#if 0
if (cls.demo_capture) {
tex_t *tex = r_funcs->SCR_CaptureBGR ();
QFile *file = Qopen (va (0, "%s/qfmv%06d.png",
@ -711,7 +711,7 @@ _Host_Frame (float time)
}
free (tex);
}
#endif
host_framecount++;
fps_count++;
}

View file

@ -1749,7 +1749,7 @@ Host_Frame (float time)
Sys_Printf ("%3i tot %3i server %3i gfx %3i snd\n",
pass1 + pass2 + pass3, pass1, pass2, pass3);
}
#if 0
if (cls.demo_capture) {
tex_t *tex = r_funcs->SCR_CaptureBGR ();
QFile *file = Qopen (va (0, "%s/qfmv%06d.png",
@ -1761,7 +1761,7 @@ Host_Frame (float time)
}
free (tex);
}
#endif
host_framecount++;
fps_count++;
}