- consolidated the savegame picture code.

This commit is contained in:
Christoph Oelckers 2020-04-25 13:18:57 +02:00
parent 6177ed153d
commit 44d39ef63e
11 changed files with 158 additions and 177 deletions

View File

@ -82,6 +82,7 @@
#include "events.h" #include "events.h"
#include "c_buttons.h" #include "c_buttons.h"
#include "d_buttons.h" #include "d_buttons.h"
#include "hwrenderer/scene/hw_drawinfo.h"
static FRandom pr_dmspawn ("DMSpawn"); static FRandom pr_dmspawn ("DMSpawn");
@ -2252,56 +2253,6 @@ static void PutSaveComment (FSerializer &arc)
arc.AddString("Comment", comment); arc.AddString("Comment", comment);
} }
void DoWriteSavePic(FileWriter *file, ESSType ssformat, uint8_t *scr, int width, int height, sector_t *viewsector, bool upsidedown)
{
PalEntry palette[256];
PalEntry modulateColor;
auto blend = V_CalcBlend(viewsector, &modulateColor);
int pixelsize = 1;
// Apply the screen blend, because the renderer does not provide this.
if (ssformat == SS_RGB)
{
int numbytes = width * height * 3;
pixelsize = 3;
if (modulateColor != 0xffffffff)
{
float r = modulateColor.r / 255.f;
float g = modulateColor.g / 255.f;
float b = modulateColor.b / 255.f;
for (int i = 0; i < numbytes; i += 3)
{
scr[i] = uint8_t(scr[i] * r);
scr[i + 1] = uint8_t(scr[i + 1] * g);
scr[i + 2] = uint8_t(scr[i + 2] * b);
}
}
float iblendfac = 1.f - blend.W;
blend.X *= blend.W;
blend.Y *= blend.W;
blend.Z *= blend.W;
for (int i = 0; i < numbytes; i += 3)
{
scr[i] = uint8_t(scr[i] * iblendfac + blend.X);
scr[i + 1] = uint8_t(scr[i + 1] * iblendfac + blend.Y);
scr[i + 2] = uint8_t(scr[i + 2] * iblendfac + blend.Z);
}
}
else
{
// Apply the screen blend to the palette. The colormap related parts get skipped here because these are already part of the image.
DoBlending(GPalette.BaseColors, palette, 256, uint8_t(blend.X), uint8_t(blend.Y), uint8_t(blend.Z), uint8_t(blend.W*255));
}
int pitch = width * pixelsize;
if (upsidedown)
{
scr += ((height - 1) * width * pixelsize);
pitch *= -1;
}
M_CreatePNG(file, scr, ssformat == SS_PAL? palette : nullptr, ssformat, width, height, pitch, Gamma);
}
static void PutSavePic (FileWriter *file, int width, int height) static void PutSavePic (FileWriter *file, int width, int height)
{ {
if (width <= 0 || height <= 0 || !storesavepic) if (width <= 0 || height <= 0 || !storesavepic)
@ -2312,7 +2263,7 @@ static void PutSavePic (FileWriter *file, int width, int height)
{ {
D_Render([&]() D_Render([&]()
{ {
screen->WriteSavePic(&players[consoleplayer], file, width, height); WriteSavePic(&players[consoleplayer], file, width, height);
}, false); }, false);
} }
} }

View File

@ -32,6 +32,7 @@
#include "r_data/r_interpolate.h" #include "r_data/r_interpolate.h"
#include "r_utility.h" #include "r_utility.h"
#include "d_player.h" #include "d_player.h"
#include "swrenderer/r_renderer.h"
#include "hwrenderer/dynlights/hw_dynlightdata.h" #include "hwrenderer/dynlights/hw_dynlightdata.h"
#include "hwrenderer/utility/hw_clock.h" #include "hwrenderer/utility/hw_clock.h"
#include "hwrenderer/data/flatvertices.h" #include "hwrenderer/data/flatvertices.h"
@ -39,6 +40,7 @@
#include "hwrenderer/dynlights/hw_lightbuffer.h" #include "hwrenderer/dynlights/hw_lightbuffer.h"
#include "hwrenderer/utility/hw_cvars.h" #include "hwrenderer/utility/hw_cvars.h"
#include "hwrenderer/data/hw_viewpointbuffer.h" #include "hwrenderer/data/hw_viewpointbuffer.h"
#include "hwrenderer/scene/hw_fakeflat.h"
#include "hwrenderer/scene/hw_clipper.h" #include "hwrenderer/scene/hw_clipper.h"
#include "hwrenderer/scene/hw_portal.h" #include "hwrenderer/scene/hw_portal.h"
#include "hwrenderer/utility/hw_vrmodes.h" #include "hwrenderer/utility/hw_vrmodes.h"
@ -118,3 +120,107 @@ sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bou
return mainvp.sector; return mainvp.sector;
} }
void DoWriteSavePic(FileWriter* file, ESSType ssformat, uint8_t* scr, int width, int height, sector_t* viewsector, bool upsidedown)
{
PalEntry palette[256];
PalEntry modulateColor;
auto blend = V_CalcBlend(viewsector, &modulateColor);
int pixelsize = 1;
// Apply the screen blend, because the renderer does not provide this.
if (ssformat == SS_RGB)
{
int numbytes = width * height * 3;
pixelsize = 3;
if (modulateColor != 0xffffffff)
{
float r = modulateColor.r / 255.f;
float g = modulateColor.g / 255.f;
float b = modulateColor.b / 255.f;
for (int i = 0; i < numbytes; i += 3)
{
scr[i] = uint8_t(scr[i] * r);
scr[i + 1] = uint8_t(scr[i + 1] * g);
scr[i + 2] = uint8_t(scr[i + 2] * b);
}
}
float iblendfac = 1.f - blend.W;
blend.X *= blend.W;
blend.Y *= blend.W;
blend.Z *= blend.W;
for (int i = 0; i < numbytes; i += 3)
{
scr[i] = uint8_t(scr[i] * iblendfac + blend.X);
scr[i + 1] = uint8_t(scr[i + 1] * iblendfac + blend.Y);
scr[i + 2] = uint8_t(scr[i + 2] * iblendfac + blend.Z);
}
}
else
{
// Apply the screen blend to the palette. The colormap related parts get skipped here because these are already part of the image.
DoBlending(GPalette.BaseColors, palette, 256, uint8_t(blend.X), uint8_t(blend.Y), uint8_t(blend.Z), uint8_t(blend.W * 255));
}
int pitch = width * pixelsize;
if (upsidedown)
{
scr += ((height - 1) * width * pixelsize);
pitch *= -1;
}
M_CreatePNG(file, scr, ssformat == SS_PAL ? palette : nullptr, ssformat, width, height, pitch, Gamma);
}
//===========================================================================
//
// Render the view to a savegame picture
//
//===========================================================================
void WriteSavePic(player_t* player, FileWriter* file, int width, int height)
{
if (!V_IsHardwareRenderer())
{
SWRenderer->WriteSavePic(player, file, width, height);
}
else
{
IntRect bounds;
bounds.left = 0;
bounds.top = 0;
bounds.width = width;
bounds.height = height;
auto& RenderState = *screen->RenderState();
// we must be sure the GPU finished reading from the buffer before we fill it with new data.
screen->WaitForCommands(false);
// Switch to render buffers dimensioned for the savepic
screen->SetSaveBuffers(true);
screen->ImageTransitionScene(true);
hw_ClearFakeFlat();
RenderState.SetVertexBuffer(screen->mVertexData);
screen->mVertexData->Reset();
screen->mLights->Clear();
screen->mViewpoints->Clear();
// This shouldn't overwrite the global viewpoint even for a short time.
FRenderViewpoint savevp;
sector_t* viewsector = RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false);
RenderState.EnableStencil(false);
RenderState.SetNoSoftLightLevel();
int numpixels = width * height;
uint8_t* scr = (uint8_t*)M_Malloc(numpixels * 3);
screen->CopyScreenToBuffer(width, height, scr);
DoWriteSavePic(file, SS_RGB, scr, width, height, viewsector, screen->FlipSavePic());
M_Free(scr);
// Switch back the screen render buffers
screen->SetViewportRects(nullptr);
screen->SetSaveBuffers(false);
}
}

View File

@ -57,8 +57,6 @@ EXTERN_CVAR(Bool, r_drawvoxels)
EXTERN_CVAR(Int, gl_tonemap) EXTERN_CVAR(Int, gl_tonemap)
EXTERN_CVAR(Bool, cl_capfps) EXTERN_CVAR(Bool, cl_capfps)
void DoWriteSavePic(FileWriter* file, ESSType ssformat, uint8_t* scr, int width, int height, sector_t* viewsector, bool upsidedown);
extern bool NoInterpolateView; extern bool NoInterpolateView;
void gl_LoadExtensions(); void gl_LoadExtensions();
@ -192,59 +190,18 @@ void OpenGLFrameBuffer::Update()
Super::Update(); Super::Update();
} }
//=========================================================================== void OpenGLFrameBuffer::CopyScreenToBuffer(int width, int height, uint8_t* scr)
//
// Render the view to a savegame picture
//
//===========================================================================
void OpenGLFrameBuffer::WriteSavePic(player_t *player, FileWriter *file, int width, int height)
{ {
if (!V_IsHardwareRenderer()) IntRect bounds;
{ bounds.left = 0;
Super::WriteSavePic(player, file, width, height); bounds.top = 0;
} bounds.width = width;
else if (GLRenderer != nullptr) bounds.height = height;
{ GLRenderer->CopyToBackbuffer(&bounds, false);
IntRect bounds;
bounds.left = 0;
bounds.top = 0;
bounds.width = width;
bounds.height = height;
// we must be sure the GPU finished reading from the buffer before we fill it with new data. // strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers
glFinish(); glFinish();
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, scr);
// Switch to render buffers dimensioned for the savepic
GLRenderer->mBuffers = GLRenderer->mSaveBuffers;
hw_ClearFakeFlat();
gl_RenderState.SetVertexBuffer(screen->mVertexData);
screen->mVertexData->Reset();
screen->mLights->Clear();
screen->mViewpoints->Clear();
// This shouldn't overwrite the global viewpoint even for a short time.
FRenderViewpoint savevp;
sector_t* viewsector = RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false);
glDisable(GL_STENCIL_TEST);
gl_RenderState.SetNoSoftLightLevel();
GLRenderer->CopyToBackbuffer(&bounds, false);
// strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers
glFinish();
int numpixels = width * height;
uint8_t* scr = (uint8_t*)M_Malloc(numpixels * 3);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, scr);
DoWriteSavePic(file, SS_RGB, scr, width, height, viewsector, true);
M_Free(scr);
// Switch back the screen render buffers
screen->SetViewportRects(nullptr);
GLRenderer->mBuffers = GLRenderer->mScreenBuffers;
}
} }
//=========================================================================== //===========================================================================
@ -549,6 +506,18 @@ void OpenGLFrameBuffer::UpdateShadowMap()
GLRenderer->UpdateShadowMap(); GLRenderer->UpdateShadowMap();
} }
void OpenGLFrameBuffer::WaitForCommands(bool finish)
{
glFinish();
}
void OpenGLFrameBuffer::SetSaveBuffers(bool yes)
{
if (!GLRenderer) return;
if (yes) GLRenderer->mBuffers = GLRenderer->mSaveBuffers;
else GLRenderer->mBuffers = GLRenderer->mScreenBuffers;
}
//=========================================================================== //===========================================================================
// //
// //

View File

@ -32,13 +32,16 @@ public:
void NextEye(int eyecount) override; void NextEye(int eyecount) override;
void SetSceneRenderTarget(bool useSSAO) override; void SetSceneRenderTarget(bool useSSAO) override;
void UpdateShadowMap() override; void UpdateShadowMap() override;
void WaitForCommands(bool finish) override;
void SetSaveBuffers(bool yes) override;
void CopyScreenToBuffer(int width, int height, uint8_t* buffer) override;
bool FlipSavePic() const override { return true; }
FRenderState* RenderState() override; FRenderState* RenderState() override;
void CleanForRestart() override; void CleanForRestart() override;
void UpdatePalette() override; void UpdatePalette() override;
uint32_t GetCaps() override; uint32_t GetCaps() override;
const char* DeviceName() const override; const char* DeviceName() const override;
void WriteSavePic(player_t *player, FileWriter *file, int width, int height) override;
sector_t *RenderView(player_t *player) override; sector_t *RenderView(player_t *player) override;
void SetTextureFilterMode() override; void SetTextureFilterMode() override;
IHardwareTexture *CreateHardwareTexture() override; IHardwareTexture *CreateHardwareTexture() override;

View File

@ -330,3 +330,4 @@ public:
}; };
sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen);
void WriteSavePic(player_t* player, FileWriter* file, int width, int height);

View File

@ -53,7 +53,6 @@
#include "engineerrors.h" #include "engineerrors.h"
void Draw2D(F2DDrawer *drawer, FRenderState &state); void Draw2D(F2DDrawer *drawer, FRenderState &state);
void DoWriteSavePic(FileWriter *file, ESSType ssformat, uint8_t *scr, int width, int height, sector_t *viewsector, bool upsidedown);
EXTERN_CVAR(Bool, r_drawvoxels) EXTERN_CVAR(Bool, r_drawvoxels)
EXTERN_CVAR(Int, gl_tonemap) EXTERN_CVAR(Int, gl_tonemap)
@ -218,17 +217,6 @@ void PolyFrameBuffer::Update()
} }
void PolyFrameBuffer::WriteSavePic(player_t *player, FileWriter *file, int width, int height)
{
if (!V_IsHardwareRenderer())
{
Super::WriteSavePic(player, file, width, height);
}
else
{
}
}
sector_t *PolyFrameBuffer::RenderView(player_t *player) sector_t *PolyFrameBuffer::RenderView(player_t *player)
{ {
// To do: this is virtually identical to FGLRenderer::RenderView and should be merged. // To do: this is virtually identical to FGLRenderer::RenderView and should be merged.

View File

@ -40,7 +40,6 @@ public:
void PrecacheMaterial(FMaterial *mat, int translation) override; void PrecacheMaterial(FMaterial *mat, int translation) override;
void UpdatePalette() override; void UpdatePalette() override;
uint32_t GetCaps() override; uint32_t GetCaps() override;
void WriteSavePic(player_t *player, FileWriter *file, int width, int height) override;
sector_t *RenderView(player_t *player) override; sector_t *RenderView(player_t *player) override;
void SetTextureFilterMode() override; void SetTextureFilterMode() override;
void TextureFilterChanged() override; void TextureFilterChanged() override;

View File

@ -312,12 +312,6 @@ uint32_t DFrameBuffer::GetCaps()
return (uint32_t)FlagSet; return (uint32_t)FlagSet;
} }
void DFrameBuffer::WriteSavePic(player_t *player, FileWriter *file, int width, int height)
{
SWRenderer->WriteSavePic(player, file, width, height);
}
//========================================================================== //==========================================================================
// //
// Calculates the viewport values needed for 2D and 3D operations // Calculates the viewport values needed for 2D and 3D operations

View File

@ -309,13 +309,18 @@ public:
virtual uint32_t GetCaps(); virtual uint32_t GetCaps();
virtual int Backend() { return 0; } virtual int Backend() { return 0; }
virtual const char* DeviceName() const { return "Unknown"; } virtual const char* DeviceName() const { return "Unknown"; }
virtual void WriteSavePic(player_t *player, FileWriter *file, int width, int height);
virtual sector_t *RenderView(player_t *player) { return nullptr; } virtual sector_t *RenderView(player_t *player) { return nullptr; }
virtual void AmbientOccludeScene(float m5) {} virtual void AmbientOccludeScene(float m5) {}
virtual void FirstEye() {} virtual void FirstEye() {}
virtual void NextEye(int eyecount) {} virtual void NextEye(int eyecount) {}
virtual void SetSceneRenderTarget(bool useSSAO) {} virtual void SetSceneRenderTarget(bool useSSAO) {}
virtual void UpdateShadowMap() {} virtual void UpdateShadowMap() {}
virtual void WaitForCommands(bool finish) {}
virtual void SetSaveBuffers(bool yes) {}
virtual void ImageTransitionScene(bool unknown) {}
virtual void CopyScreenToBuffer(int width, int height, uint8_t* buffer) { memset(buffer, 0, width* height); }
virtual bool FlipSavePic() const { return false; }
// Screen wiping // Screen wiping
virtual FTexture *WipeStartScreen(); virtual FTexture *WipeStartScreen();

View File

@ -61,7 +61,6 @@
#include "engineerrors.h" #include "engineerrors.h"
void Draw2D(F2DDrawer *drawer, FRenderState &state); void Draw2D(F2DDrawer *drawer, FRenderState &state);
void DoWriteSavePic(FileWriter *file, ESSType ssformat, uint8_t *scr, int width, int height, sector_t *viewsector, bool upsidedown);
EXTERN_CVAR(Bool, r_drawvoxels) EXTERN_CVAR(Bool, r_drawvoxels)
EXTERN_CVAR(Int, gl_tonemap) EXTERN_CVAR(Int, gl_tonemap)
@ -318,53 +317,6 @@ void VulkanFrameBuffer::WaitForCommands(bool finish)
} }
} }
void VulkanFrameBuffer::WriteSavePic(player_t *player, FileWriter *file, int width, int height)
{
if (!V_IsHardwareRenderer())
{
Super::WriteSavePic(player, file, width, height);
}
else
{
IntRect bounds;
bounds.left = 0;
bounds.top = 0;
bounds.width = width;
bounds.height = height;
// we must be sure the GPU finished reading from the buffer before we fill it with new data.
WaitForCommands(false);
// Switch to render buffers dimensioned for the savepic
mActiveRenderBuffers = mSaveBuffers.get();
mPostprocess->ImageTransitionScene(true);
hw_ClearFakeFlat();
GetRenderState()->SetVertexBuffer(screen->mVertexData);
screen->mVertexData->Reset();
screen->mLights->Clear();
screen->mViewpoints->Clear();
// This shouldn't overwrite the global viewpoint even for a short time.
FRenderViewpoint savevp;
sector_t *viewsector = RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false);
GetRenderState()->EnableStencil(false);
GetRenderState()->SetNoSoftLightLevel();
int numpixels = width * height;
uint8_t * scr = (uint8_t *)M_Malloc(numpixels * 3);
CopyScreenToBuffer(width, height, scr);
DoWriteSavePic(file, SS_RGB, scr, width, height, viewsector, false);
M_Free(scr);
// Switch back the screen render buffers
screen->SetViewportRects(nullptr);
mActiveRenderBuffers = mScreenBuffers.get();
}
}
sector_t *VulkanFrameBuffer::RenderView(player_t *player) sector_t *VulkanFrameBuffer::RenderView(player_t *player)
{ {
// To do: this is virtually identical to FGLRenderer::RenderView and should be merged. // To do: this is virtually identical to FGLRenderer::RenderView and should be merged.
@ -634,7 +586,7 @@ FTexture *VulkanFrameBuffer::WipeEndScreen()
return tex; return tex;
} }
void VulkanFrameBuffer::CopyScreenToBuffer(int w, int h, void *data) void VulkanFrameBuffer::CopyScreenToBuffer(int w, int h, uint8_t *data)
{ {
VkTextureImage image; VkTextureImage image;
@ -866,6 +818,18 @@ void VulkanFrameBuffer::UpdateShadowMap()
mPostprocess->UpdateShadowMap(); mPostprocess->UpdateShadowMap();
} }
void VulkanFrameBuffer::SetSaveBuffers(bool yes)
{
if (yes) mActiveRenderBuffers = mSaveBuffers.get();
else mActiveRenderBuffers = mScreenBuffers.get();
}
void VulkanFrameBuffer::ImageTransitionScene(bool unknown)
{
mPostprocess->ImageTransitionScene(unknown);
}
FRenderState* VulkanFrameBuffer::RenderState() FRenderState* VulkanFrameBuffer::RenderState()
{ {
return mRenderState.get(); return mRenderState.get();

View File

@ -80,7 +80,6 @@ public:
uint32_t GetCaps() override; uint32_t GetCaps() override;
const char* DeviceName() const override; const char* DeviceName() const override;
int Backend() override { return 1; } int Backend() override { return 1; }
void WriteSavePic(player_t *player, FileWriter *file, int width, int height) override;
sector_t *RenderView(player_t *player) override; sector_t *RenderView(player_t *player) override;
void SetTextureFilterMode() override; void SetTextureFilterMode() override;
void TextureFilterChanged() override; void TextureFilterChanged() override;
@ -91,6 +90,8 @@ public:
void AmbientOccludeScene(float m5) override; void AmbientOccludeScene(float m5) override;
void SetSceneRenderTarget(bool useSSAO) override; void SetSceneRenderTarget(bool useSSAO) override;
void UpdateShadowMap() override; void UpdateShadowMap() override;
void SetSaveBuffers(bool yes) override;
void ImageTransitionScene(bool unknown) override;
IHardwareTexture *CreateHardwareTexture() override; IHardwareTexture *CreateHardwareTexture() override;
FMaterial* CreateMaterial(FGameTexture* tex, int scaleflags) override; FMaterial* CreateMaterial(FGameTexture* tex, int scaleflags) override;
@ -108,7 +109,7 @@ public:
void Draw2D() override; void Draw2D() override;
void WaitForCommands(bool finish); void WaitForCommands(bool finish) override;
void PushGroup(const FString &name); void PushGroup(const FString &name);
void PopGroup(); void PopGroup();
@ -118,7 +119,7 @@ private:
void RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, double FOV); void RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, double FOV);
void PrintStartupLog(); void PrintStartupLog();
void CreateFanToTrisIndexBuffer(); void CreateFanToTrisIndexBuffer();
void CopyScreenToBuffer(int w, int h, void *data); void CopyScreenToBuffer(int w, int h, uint8_t *data) override;
void DeleteFrameObjects(); void DeleteFrameObjects();
void FlushCommands(VulkanCommandBuffer **commands, size_t count, bool finish, bool lastsubmit); void FlushCommands(VulkanCommandBuffer **commands, size_t count, bool finish, bool lastsubmit);