diff --git a/src/g_game.cpp b/src/g_game.cpp index 0bf781260..9cb948b88 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -82,6 +82,7 @@ #include "events.h" #include "c_buttons.h" #include "d_buttons.h" +#include "hwrenderer/scene/hw_drawinfo.h" static FRandom pr_dmspawn ("DMSpawn"); @@ -2252,56 +2253,6 @@ static void PutSaveComment (FSerializer &arc) 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) { if (width <= 0 || height <= 0 || !storesavepic) @@ -2312,7 +2263,7 @@ static void PutSavePic (FileWriter *file, int width, int height) { D_Render([&]() { - screen->WriteSavePic(&players[consoleplayer], file, width, height); + WriteSavePic(&players[consoleplayer], file, width, height); }, false); } } diff --git a/src/rendering/gl/renderer/gl_scene.cpp b/src/rendering/gl/renderer/gl_scene.cpp index 55f373099..324703a45 100644 --- a/src/rendering/gl/renderer/gl_scene.cpp +++ b/src/rendering/gl/renderer/gl_scene.cpp @@ -32,6 +32,7 @@ #include "r_data/r_interpolate.h" #include "r_utility.h" #include "d_player.h" +#include "swrenderer/r_renderer.h" #include "hwrenderer/dynlights/hw_dynlightdata.h" #include "hwrenderer/utility/hw_clock.h" #include "hwrenderer/data/flatvertices.h" @@ -39,6 +40,7 @@ #include "hwrenderer/dynlights/hw_lightbuffer.h" #include "hwrenderer/utility/hw_cvars.h" #include "hwrenderer/data/hw_viewpointbuffer.h" +#include "hwrenderer/scene/hw_fakeflat.h" #include "hwrenderer/scene/hw_clipper.h" #include "hwrenderer/scene/hw_portal.h" #include "hwrenderer/utility/hw_vrmodes.h" @@ -118,3 +120,107 @@ sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bou 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); + } +} + diff --git a/src/rendering/gl/system/gl_framebuffer.cpp b/src/rendering/gl/system/gl_framebuffer.cpp index 8eed56869..278cefdf8 100644 --- a/src/rendering/gl/system/gl_framebuffer.cpp +++ b/src/rendering/gl/system/gl_framebuffer.cpp @@ -57,8 +57,6 @@ EXTERN_CVAR(Bool, r_drawvoxels) EXTERN_CVAR(Int, gl_tonemap) 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; void gl_LoadExtensions(); @@ -192,59 +190,18 @@ void OpenGLFrameBuffer::Update() Super::Update(); } -//=========================================================================== -// -// Render the view to a savegame picture -// -//=========================================================================== - -void OpenGLFrameBuffer::WriteSavePic(player_t *player, FileWriter *file, int width, int height) +void OpenGLFrameBuffer::CopyScreenToBuffer(int width, int height, uint8_t* scr) { - if (!V_IsHardwareRenderer()) - { - Super::WriteSavePic(player, file, width, height); - } - else if (GLRenderer != nullptr) - { - IntRect bounds; - bounds.left = 0; - bounds.top = 0; - bounds.width = width; - bounds.height = height; + IntRect bounds; + bounds.left = 0; + bounds.top = 0; + bounds.width = width; + bounds.height = height; + GLRenderer->CopyToBackbuffer(&bounds, false); - // we must be sure the GPU finished reading from the buffer before we fill it with new data. - glFinish(); - - // 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; - } + // strictly speaking not needed as the glReadPixels should block until the scene is rendered, but this is to safeguard against shitty drivers + glFinish(); + glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, scr); } //=========================================================================== @@ -549,6 +506,18 @@ void OpenGLFrameBuffer::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; +} + //=========================================================================== // // diff --git a/src/rendering/gl/system/gl_framebuffer.h b/src/rendering/gl/system/gl_framebuffer.h index a6286fcb3..5412293ae 100644 --- a/src/rendering/gl/system/gl_framebuffer.h +++ b/src/rendering/gl/system/gl_framebuffer.h @@ -32,13 +32,16 @@ public: void NextEye(int eyecount) override; void SetSceneRenderTarget(bool useSSAO) 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; void CleanForRestart() override; void UpdatePalette() override; uint32_t GetCaps() 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; void SetTextureFilterMode() override; IHardwareTexture *CreateHardwareTexture() override; diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.h b/src/rendering/hwrenderer/scene/hw_drawinfo.h index 27db44e82..a3eab8ccc 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.h +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.h @@ -330,3 +330,4 @@ public: }; 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); diff --git a/src/rendering/polyrenderer/backend/poly_framebuffer.cpp b/src/rendering/polyrenderer/backend/poly_framebuffer.cpp index 427e24ffe..9a02a6b22 100644 --- a/src/rendering/polyrenderer/backend/poly_framebuffer.cpp +++ b/src/rendering/polyrenderer/backend/poly_framebuffer.cpp @@ -53,7 +53,6 @@ #include "engineerrors.h" 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(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) { // To do: this is virtually identical to FGLRenderer::RenderView and should be merged. diff --git a/src/rendering/polyrenderer/backend/poly_framebuffer.h b/src/rendering/polyrenderer/backend/poly_framebuffer.h index a7e6ee14e..ad7bc56ba 100644 --- a/src/rendering/polyrenderer/backend/poly_framebuffer.h +++ b/src/rendering/polyrenderer/backend/poly_framebuffer.h @@ -40,7 +40,6 @@ public: void PrecacheMaterial(FMaterial *mat, int translation) override; void UpdatePalette() override; uint32_t GetCaps() override; - void WriteSavePic(player_t *player, FileWriter *file, int width, int height) override; sector_t *RenderView(player_t *player) override; void SetTextureFilterMode() override; void TextureFilterChanged() override; diff --git a/src/rendering/v_framebuffer.cpp b/src/rendering/v_framebuffer.cpp index aa297fe9a..143189a3f 100644 --- a/src/rendering/v_framebuffer.cpp +++ b/src/rendering/v_framebuffer.cpp @@ -312,12 +312,6 @@ uint32_t DFrameBuffer::GetCaps() 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 diff --git a/src/rendering/v_video.h b/src/rendering/v_video.h index 4b7aedd1a..a504c3700 100644 --- a/src/rendering/v_video.h +++ b/src/rendering/v_video.h @@ -309,13 +309,18 @@ public: virtual uint32_t GetCaps(); virtual int Backend() { return 0; } 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 void AmbientOccludeScene(float m5) {} virtual void FirstEye() {} virtual void NextEye(int eyecount) {} virtual void SetSceneRenderTarget(bool useSSAO) {} 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 virtual FTexture *WipeStartScreen(); diff --git a/src/rendering/vulkan/system/vk_framebuffer.cpp b/src/rendering/vulkan/system/vk_framebuffer.cpp index f3404ef80..0fabe75ae 100644 --- a/src/rendering/vulkan/system/vk_framebuffer.cpp +++ b/src/rendering/vulkan/system/vk_framebuffer.cpp @@ -61,7 +61,6 @@ #include "engineerrors.h" 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(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) { // To do: this is virtually identical to FGLRenderer::RenderView and should be merged. @@ -634,7 +586,7 @@ FTexture *VulkanFrameBuffer::WipeEndScreen() return tex; } -void VulkanFrameBuffer::CopyScreenToBuffer(int w, int h, void *data) +void VulkanFrameBuffer::CopyScreenToBuffer(int w, int h, uint8_t *data) { VkTextureImage image; @@ -866,6 +818,18 @@ void VulkanFrameBuffer::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() { return mRenderState.get(); diff --git a/src/rendering/vulkan/system/vk_framebuffer.h b/src/rendering/vulkan/system/vk_framebuffer.h index 6a1f1697d..85a5d363f 100644 --- a/src/rendering/vulkan/system/vk_framebuffer.h +++ b/src/rendering/vulkan/system/vk_framebuffer.h @@ -80,7 +80,6 @@ public: uint32_t GetCaps() override; const char* DeviceName() const override; int Backend() override { return 1; } - void WriteSavePic(player_t *player, FileWriter *file, int width, int height) override; sector_t *RenderView(player_t *player) override; void SetTextureFilterMode() override; void TextureFilterChanged() override; @@ -91,6 +90,8 @@ public: void AmbientOccludeScene(float m5) override; void SetSceneRenderTarget(bool useSSAO) override; void UpdateShadowMap() override; + void SetSaveBuffers(bool yes) override; + void ImageTransitionScene(bool unknown) override; IHardwareTexture *CreateHardwareTexture() override; FMaterial* CreateMaterial(FGameTexture* tex, int scaleflags) override; @@ -108,7 +109,7 @@ public: void Draw2D() override; - void WaitForCommands(bool finish); + void WaitForCommands(bool finish) override; void PushGroup(const FString &name); void PopGroup(); @@ -118,7 +119,7 @@ private: void RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, double FOV); void PrintStartupLog(); void CreateFanToTrisIndexBuffer(); - void CopyScreenToBuffer(int w, int h, void *data); + void CopyScreenToBuffer(int w, int h, uint8_t *data) override; void DeleteFrameObjects(); void FlushCommands(VulkanCommandBuffer **commands, size_t count, bool finish, bool lastsubmit);