hw2: add screenshot pass

This commit is contained in:
Eidolon 2023-02-11 17:35:33 -06:00
parent b9cf1316bd
commit e875c8e20d
11 changed files with 184 additions and 27 deletions

View file

@ -868,8 +868,10 @@ void D_SRB2Loop(void)
// Only take screenshots after drawing. // Only take screenshots after drawing.
if (moviemode) if (moviemode)
M_SaveFrame(); M_SaveFrame();
if (takescreenshot) #ifdef HWRENDER
M_DoScreenShot(); if (rendermode == render_opengl && takescreenshot)
M_DoLegacyGLScreenShot();
#endif
// consoleplayer -> displayplayers (hear sounds from viewpoint) // consoleplayer -> displayplayers (hear sounds from viewpoint)
S_UpdateSounds(); // move positional sounds S_UpdateSounds(); // move positional sounds

View file

@ -1603,8 +1603,6 @@ void G_PreLevelTitleCard(void)
if (moviemode) if (moviemode)
M_SaveFrame(); M_SaveFrame();
if (takescreenshot) // Only take screenshots after drawing.
M_DoScreenShot();
while (!((nowtime = I_GetTime()) - lasttime)) while (!((nowtime = I_GetTime()) - lasttime))
{ {
@ -4396,7 +4394,7 @@ void G_LoadGameData(void)
{ {
// Don't load, but do save. (essentially, reset) // Don't load, but do save. (essentially, reset)
gamedata->loaded = true; gamedata->loaded = true;
return; return;
} }
if (P_SaveBufferFromFile(&save, va(pandf, srb2home, gamedatafilename)) == false) if (P_SaveBufferFromFile(&save, va(pandf, srb2home, gamedatafilename)) == false)

View file

@ -9,6 +9,8 @@ target_sources(SRB2SDL2 PRIVATE
pass_postprocess.hpp pass_postprocess.hpp
pass_resource_managers.cpp pass_resource_managers.cpp
pass_resource_managers.hpp pass_resource_managers.hpp
pass_screenshot.cpp
pass_screenshot.hpp
pass_software.cpp pass_software.cpp
pass_software.hpp pass_software.hpp
pass_twodee.cpp pass_twodee.cpp

View file

@ -0,0 +1,68 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2023 by Ronald "Eidolon" Kinard
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
#include "pass_screenshot.hpp"
#include <tcb/span.hpp>
#include "../m_misc.h"
using namespace srb2;
using namespace srb2::hwr2;
using namespace srb2::rhi;
ScreenshotPass::ScreenshotPass() = default;
ScreenshotPass::~ScreenshotPass() = default;
void ScreenshotPass::prepass(Rhi& rhi)
{
if (!render_pass_)
{
render_pass_ = rhi.create_render_pass(
{
std::nullopt,
PixelFormat::kRGBA8,
AttachmentLoadOp::kLoad,
AttachmentStoreOp::kStore
}
);
}
doing_screenshot_ = takescreenshot;
}
void ScreenshotPass::transfer(Rhi& rhi, Handle<TransferContext> ctx)
{
}
void ScreenshotPass::graphics(Rhi& rhi, Handle<GraphicsContext> ctx)
{
if (!doing_screenshot_)
{
return;
}
pixel_data_.clear();
pixel_data_.resize(width_ * height_ * 3); // 3 bytes per pixel for RGB8
rhi.begin_render_pass(ctx, {render_pass_, source_, std::nullopt, {0.f, 0.f, 0.f, 0.f}});
rhi.read_pixels(ctx, {0, 0, width_, height_}, PixelFormat::kRGB8, tcb::as_writable_bytes(tcb::span(pixel_data_)));
rhi.end_render_pass(ctx);
}
void ScreenshotPass::postpass(Rhi& rhi)
{
if (!doing_screenshot_)
{
return;
}
M_DoScreenShot(width_, height_, tcb::as_bytes(tcb::span(pixel_data_)));
doing_screenshot_ = false;
}

View file

@ -0,0 +1,49 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2023 by Ronald "Eidolon" Kinard
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
#ifndef __SRB2_HWR2_PASS_SCREENSHOT_HPP__
#define __SRB2_HWR2_PASS_SCREENSHOT_HPP__
#include <cstddef>
#include <vector>
#include "pass.hpp"
namespace srb2::hwr2
{
class ScreenshotPass : public Pass
{
bool doing_screenshot_ = false;
rhi::Handle<rhi::Texture> source_;
rhi::Handle<rhi::RenderPass> render_pass_;
std::vector<uint8_t> pixel_data_;
uint32_t width_ = 0;
uint32_t height_ = 0;
public:
ScreenshotPass();
virtual ~ScreenshotPass();
virtual void prepass(rhi::Rhi& rhi) override;
virtual void transfer(rhi::Rhi& rhi, rhi::Handle<rhi::TransferContext> ctx) override;
virtual void graphics(rhi::Rhi& rhi, rhi::Handle<rhi::GraphicsContext> ctx) override;
virtual void postpass(rhi::Rhi& rhi) override;
void set_source(rhi::Handle<rhi::Texture> source, uint32_t width, uint32_t height)
{
source_ = source;
width_ = width;
height_ = height;
}
};
} // namespace srb2::hwr2
#endif // __SRB2_HWR2_PASS_SCREENSHOT_HPP__

View file

@ -22,6 +22,7 @@
#include "hwr2/pass_manager.hpp" #include "hwr2/pass_manager.hpp"
#include "hwr2/pass_postprocess.hpp" #include "hwr2/pass_postprocess.hpp"
#include "hwr2/pass_resource_managers.hpp" #include "hwr2/pass_resource_managers.hpp"
#include "hwr2/pass_screenshot.hpp"
#include "hwr2/pass_software.hpp" #include "hwr2/pass_software.hpp"
#include "hwr2/pass_twodee.hpp" #include "hwr2/pass_twodee.hpp"
#include "hwr2/twodee.hpp" #include "hwr2/twodee.hpp"
@ -118,6 +119,7 @@ static std::shared_ptr<PassManager> build_pass_manager()
twodee->ctx_ = &g_2d; twodee->ctx_ = &g_2d;
std::shared_ptr<BlitRectPass> pp_simple_blit_pass = std::make_shared<BlitRectPass>(false); std::shared_ptr<BlitRectPass> pp_simple_blit_pass = std::make_shared<BlitRectPass>(false);
std::shared_ptr<PostprocessWipePass> pp_wipe_pass = std::make_shared<PostprocessWipePass>(); std::shared_ptr<PostprocessWipePass> pp_wipe_pass = std::make_shared<PostprocessWipePass>();
std::shared_ptr<ScreenshotPass> screenshot_pass = std::make_shared<ScreenshotPass>();
std::shared_ptr<ImguiPass> imgui_pass = std::make_shared<ImguiPass>(); std::shared_ptr<ImguiPass> imgui_pass = std::make_shared<ImguiPass>();
std::shared_ptr<BlitRectPass> final_composite_pass = std::make_shared<BlitRectPass>(true); std::shared_ptr<BlitRectPass> final_composite_pass = std::make_shared<BlitRectPass>(true);
@ -186,7 +188,7 @@ static std::shared_ptr<PassManager> build_pass_manager()
} }
pp_simple_blit_pass->set_texture(color, vid.width, vid.height); pp_simple_blit_pass->set_texture(color, vid.width, vid.height);
pp_simple_blit_pass pp_simple_blit_pass
->set_output(framebuffer_manager->current_post_color(), vid.width, vid.height, false, true); ->set_output(framebuffer_manager->current_post_color(), vid.width, vid.height, false, false);
} }
); );
manager->insert("pp_final_simple_blit", pp_simple_blit_pass); manager->insert("pp_final_simple_blit", pp_simple_blit_pass);
@ -209,12 +211,21 @@ static std::shared_ptr<PassManager> build_pass_manager()
[framebuffer_manager](PassManager&, Rhi&) { framebuffer_manager->swap_post(); } [framebuffer_manager](PassManager&, Rhi&) { framebuffer_manager->swap_post(); }
); );
manager->insert(
"screenshot_prepare",
[screenshot_pass, framebuffer_manager](PassManager&, Rhi&)
{
screenshot_pass->set_source(framebuffer_manager->previous_post_color(), vid.width, vid.height);
}
);
manager->insert("screenshot", screenshot_pass);
manager->insert( manager->insert(
"final_composite_prepare", "final_composite_prepare",
[final_composite_pass, framebuffer_manager](PassManager&, Rhi&) [final_composite_pass, framebuffer_manager](PassManager&, Rhi&)
{ {
final_composite_pass->set_texture(framebuffer_manager->previous_post_color(), vid.width, vid.height); final_composite_pass->set_texture(framebuffer_manager->previous_post_color(), vid.width, vid.height);
final_composite_pass->set_output(kNullHandle, vid.realwidth, vid.realheight, true, true); final_composite_pass->set_output(kNullHandle, vid.realwidth, vid.realheight, true, false);
} }
); );
manager->insert("final_composite", final_composite_pass); manager->insert("final_composite", final_composite_pass);

View file

@ -1509,7 +1509,7 @@ void M_StopMovie(void)
* \param palette Palette of image data. * \param palette Palette of image data.
* \note if palette is NULL, BGR888 format * \note if palette is NULL, BGR888 format
*/ */
boolean M_SavePNG(const char *filename, void *data, int width, int height, const UINT8 *palette) boolean M_SavePNG(const char *filename, const void *data, int width, int height, const UINT8 *palette)
{ {
png_structp png_ptr; png_structp png_ptr;
png_infop png_info_ptr; png_infop png_info_ptr;
@ -1580,7 +1580,7 @@ boolean M_SavePNG(const char *filename, void *data, int width, int height, const
png_write_info(png_ptr, png_info_ptr); png_write_info(png_ptr, png_info_ptr);
M_PNGImage(png_ptr, png_info_ptr, height, static_cast<png_bytep>(data)); M_PNGImage(png_ptr, png_info_ptr, height, (png_bytep)data);
png_write_end(png_ptr, png_info_ptr); png_write_end(png_ptr, png_info_ptr);
png_destroy_write_struct(&png_ptr, &png_info_ptr); png_destroy_write_struct(&png_ptr, &png_info_ptr);
@ -1688,19 +1688,24 @@ void M_ScreenShot(void)
takescreenshot = true; takescreenshot = true;
} }
void M_DoLegacyGLScreenShot(void)
{
const std::byte* fake_data = nullptr;
M_DoScreenShot(vid.width, vid.height, tcb::span(fake_data, vid.width * vid.height));
}
/** Takes a screenshot. /** Takes a screenshot.
* The screenshot is saved as "srb2xxxx.png" where xxxx is the lowest * The screenshot is saved as "srb2xxxx.png" where xxxx is the lowest
* four-digit number for which a file does not already exist. * four-digit number for which a file does not already exist.
* *
* \sa HWR_ScreenShot * \sa HWR_ScreenShot
*/ */
void M_DoScreenShot(void) void M_DoScreenShot(UINT32 width, UINT32 height, tcb::span<const std::byte> data)
{ {
#if NUMSCREENS > 2 #if NUMSCREENS > 2
const char *freename = NULL; const char *freename = NULL;
char pathname[MAX_WADPATH]; char pathname[MAX_WADPATH];
boolean ret = false; boolean ret = false;
UINT8 *linear = NULL;
// Don't take multiple screenshots, obviously // Don't take multiple screenshots, obviously
takescreenshot = false; takescreenshot = false;
@ -1733,13 +1738,6 @@ void M_DoScreenShot(void)
freename = Newsnapshotfile(pathname,"tga"); freename = Newsnapshotfile(pathname,"tga");
#endif #endif
if (rendermode == render_soft)
{
// munge planar buffer to linear
linear = screens[2];
I_ReadScreen(linear);
}
if (!freename) if (!freename)
goto failure; goto failure;
@ -1750,9 +1748,9 @@ void M_DoScreenShot(void)
else else
#endif #endif
{ {
M_CreateScreenShotPalette(); const void* pixel_data = static_cast<const void*>(data.data());
#ifdef USE_PNG #ifdef USE_PNG
ret = M_SavePNG(va(pandf,pathname,freename), linear, vid.width, vid.height, screenshot_palette); ret = M_SavePNG(va(pandf,pathname,freename), pixel_data, width, height, NULL);
#else #else
ret = WritePCXfile(va(pandf,pathname,freename), linear, vid.width, vid.height, screenshot_palette); ret = WritePCXfile(va(pandf,pathname,freename), linear, vid.width, vid.height, screenshot_palette);
#endif #endif

View file

@ -22,6 +22,13 @@
#include "command.h" #include "command.h"
#ifdef __cplusplus #ifdef __cplusplus
#include <cstddef>
#include <tcb/span.hpp>
void M_DoScreenShot(uint32_t width, uint32_t height, tcb::span<const std::byte> data);
extern "C" { extern "C" {
#endif #endif
@ -82,12 +89,14 @@ void FIL_ForceExtension(char *path, const char *extension);
boolean FIL_CheckExtension(const char *in); boolean FIL_CheckExtension(const char *in);
#ifdef HAVE_PNG #ifdef HAVE_PNG
boolean M_SavePNG(const char *filename, void *data, int width, int height, const UINT8 *palette); boolean M_SavePNG(const char *filename, const void *data, int width, int height, const UINT8 *palette);
#endif #endif
extern boolean takescreenshot; extern boolean takescreenshot;
void M_ScreenShot(void); void M_ScreenShot(void);
void M_DoScreenShot(void); #ifdef HWRENDER
void M_DoLegacyGLScreenShot(void);
#endif
boolean M_ScreenshotResponder(event_t *ev); boolean M_ScreenshotResponder(event_t *ev);
void M_MinimapGenerate(void); void M_MinimapGenerate(void);

View file

@ -42,6 +42,12 @@ constexpr GLenum map_pixel_format(rhi::PixelFormat format)
{ {
switch (format) switch (format)
{ {
case rhi::PixelFormat::kR8:
return GL_R8;
case rhi::PixelFormat::kRG8:
return GL_RG8;
case rhi::PixelFormat::kRGB8:
return GL_RGB8;
case rhi::PixelFormat::kRGBA8: case rhi::PixelFormat::kRGBA8:
return GL_RGBA8; return GL_RGBA8;
case rhi::PixelFormat::kDepth16: case rhi::PixelFormat::kDepth16:
@ -70,6 +76,11 @@ constexpr std::tuple<GLenum, GLenum, GLuint> map_pixel_data_format(rhi::PixelFor
type = GL_UNSIGNED_BYTE; type = GL_UNSIGNED_BYTE;
size = 2; size = 2;
break; break;
case rhi::PixelFormat::kRGB8:
layout = GL_RGB;
type = GL_UNSIGNED_BYTE;
size = 3;
break;
case rhi::PixelFormat::kRGBA8: case rhi::PixelFormat::kRGBA8:
layout = GL_RGBA; layout = GL_RGBA;
type = GL_UNSIGNED_BYTE; type = GL_UNSIGNED_BYTE;
@ -1581,13 +1592,19 @@ void GlCoreRhi::draw_indexed(Handle<GraphicsContext> ctx, uint32_t index_count,
GL_ASSERT GL_ASSERT
} }
void GlCoreRhi::read_pixels(Handle<GraphicsContext> ctx, const Rect& rect, tcb::span<std::byte> out) void GlCoreRhi::read_pixels(Handle<GraphicsContext> ctx, const Rect& rect, PixelFormat format, tcb::span<std::byte> out)
{ {
SRB2_ASSERT(graphics_context_active_ == true && graphics_context_generation_ == ctx.generation()); SRB2_ASSERT(graphics_context_active_ == true && graphics_context_generation_ == ctx.generation());
SRB2_ASSERT(current_render_pass_.has_value()); SRB2_ASSERT(current_render_pass_.has_value());
SRB2_ASSERT(out.size_bytes() == rect.w * rect.h); std::tuple<GLenum, GLenum, GLuint> gl_format = map_pixel_data_format(format);
gl_->ReadPixels(rect.x, rect.y, rect.w, rect.h, GL_RGBA, GL_UNSIGNED_BYTE, out.data()); GLenum layout = std::get<0>(gl_format);
GLenum type = std::get<1>(gl_format);
GLint size = std::get<2>(gl_format);
SRB2_ASSERT(out.size_bytes() == rect.w * rect.h * size);
gl_->ReadPixels(rect.x, rect.y, rect.w, rect.h, layout, type, out.data());
} }
void GlCoreRhi::finish() void GlCoreRhi::finish()

View file

@ -226,7 +226,8 @@ public:
virtual void set_viewport(Handle<GraphicsContext> ctx, const Rect& rect) override; virtual void set_viewport(Handle<GraphicsContext> ctx, const Rect& rect) override;
virtual void draw(Handle<GraphicsContext> ctx, uint32_t vertex_count, uint32_t first_vertex) override; virtual void draw(Handle<GraphicsContext> ctx, uint32_t vertex_count, uint32_t first_vertex) override;
virtual void draw_indexed(Handle<GraphicsContext> ctx, uint32_t index_count, uint32_t first_index) override; virtual void draw_indexed(Handle<GraphicsContext> ctx, uint32_t index_count, uint32_t first_index) override;
virtual void read_pixels(Handle<GraphicsContext> ctx, const Rect& rect, tcb::span<std::byte> out) override; virtual void
read_pixels(Handle<GraphicsContext> ctx, const Rect& rect, PixelFormat format, tcb::span<std::byte> out) override;
virtual void present() override; virtual void present() override;

View file

@ -73,6 +73,7 @@ enum class PixelFormat
{ {
kR8, kR8,
kRG8, kRG8,
kRGB8,
kRGBA8, kRGBA8,
kDepth16, kDepth16,
kStencil8 kStencil8
@ -562,7 +563,8 @@ struct Rhi
virtual void set_viewport(Handle<GraphicsContext> ctx, const Rect& rect) = 0; virtual void set_viewport(Handle<GraphicsContext> ctx, const Rect& rect) = 0;
virtual void draw(Handle<GraphicsContext> ctx, uint32_t vertex_count, uint32_t first_vertex) = 0; virtual void draw(Handle<GraphicsContext> ctx, uint32_t vertex_count, uint32_t first_vertex) = 0;
virtual void draw_indexed(Handle<GraphicsContext> ctx, uint32_t index_count, uint32_t first_index) = 0; virtual void draw_indexed(Handle<GraphicsContext> ctx, uint32_t index_count, uint32_t first_index) = 0;
virtual void read_pixels(Handle<GraphicsContext> ctx, const Rect& rect, tcb::span<std::byte> out) = 0; virtual void
read_pixels(Handle<GraphicsContext> ctx, const Rect& rect, PixelFormat format, tcb::span<std::byte> out) = 0;
virtual void present() = 0; virtual void present() = 0;