mirror of
https://github.com/libretro/parallel-dc.git
synced 2026-11-10 03:11:26 -10:00
A render to a texture draws with the screen's attachments - the depth with the per-triangle and per-strip renderers, the colour and depth the per-pixel renderer keeps between passes - made at least as large as a render of a screen, 640x480 times Render To Texture Upscaling (TextureDrawer::ShareExtent()). It draws its own pixels, not the whole power-of-two texture they are in; the rest is cleared black once, when the image is new or was written since (Texture::ClearOutside()). Nothing is made for a render during play, and the video memory is the screen's: no more than before renders to textures had attachments made up front, and up to 16 MB less than with them, at any resolution where the screen is at least as large as the renders. A render larger than the screen has attachments of its own made when it comes, logged. With SSAA or the upscaler the texture drawers keep their own. tools/vulkan/vk_vram.sh: the test disc built with -DLIVE_ODD, a render of no power of two drawn as its whole texture, each raster renderer at 1x, 2x and with 2x renders: every render drew with the screen's attachments, black past it (tools/rend/odd_check.py), validation clean, the image memory at the most printed (vk_hdr VKH_IMAGE_BYTES). In CI.
264 lines
10 KiB
C++
264 lines
10 KiB
C++
#define PVR_REGS_FOR_RENDERER // see hw/pvr/pvr_regs.h
|
|
/*
|
|
Created on: Nov 7, 2019
|
|
|
|
Copyright 2019 flyinghead
|
|
|
|
This file is part of Flycast.
|
|
|
|
Flycast 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.
|
|
|
|
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "../vulkan.h"
|
|
#include "../vulkan_renderer.h"
|
|
#include "oit_drawer.h"
|
|
#include "rend/upscaler.h"
|
|
#include "oit_shaders.h"
|
|
#include "oit_buffer.h"
|
|
#include "rend/ssaa_pattern.h"
|
|
#include "../compute_drawer.h"
|
|
#include "rend/rend_method.h"
|
|
|
|
class OITVulkanRenderer final : public BaseVulkanRenderer
|
|
{
|
|
public:
|
|
bool Init() override
|
|
{
|
|
DEBUG_LOG(RENDERER, "OITVulkanRenderer::Init");
|
|
|
|
oitBuffers.SetCompact(rend_compute_wanted && ComputeDrawer::DeviceLacks() == nullptr);
|
|
samples = SsaaSamples();
|
|
samplerManager.Init();
|
|
/* (with SSAA and the HDR output, the last pass's channel sums, for
|
|
* the encode: OITBuffers::SetSums()) */
|
|
oitBuffers.SetSums(samples > 1 && GetContext()->HdrSumsReady());
|
|
oitBuffers.Init(0, 0, &texCommandPool);
|
|
oitShaderManager.SetSamples(samples);
|
|
// (the compute renderer's slots are the pool's: made now, BaseInit() finds them made)
|
|
texCommandPool.Init();
|
|
MakeCompute();
|
|
NOTICE_LOG(RENDERER, "Vulkan per-pixel renderer: with SSAA, a translucent fragment takes the samples %s",
|
|
GetContext()->postDepthCoverage ? "the depth test passed (VK_EXT_post_depth_coverage)" : "its own depth test passes, at each sample");
|
|
textureDrawer.SetCommandPool(&texCommandPool);
|
|
textureDrawer.Init(&samplerManager, &rttShaderManager, &textureCache, &oitBuffers);
|
|
/* (renders to a texture drawn by the compute renderer go to the
|
|
* pool's images, which it then writes as storage images) */
|
|
texPool.SetTargetStorage(rend_compute_wanted && ComputeDrawer::DeviceLacks() == nullptr);
|
|
textureDrawer.SetGpuDraw(taDecoder.Draw());
|
|
|
|
screenDrawer.SetStoragePicture(rend_compute_wanted);
|
|
screenDrawer.Init(&samplerManager, &oitShaderManager, &oitBuffers, samples);
|
|
screenDrawer.SetCommandPool(&texCommandPool);
|
|
textureDrawer.ShareAttachments(&screenDrawer);
|
|
screenDrawer.SetGpuDraw(taDecoder.Draw());
|
|
computeDrawer.SetGpuDraw(taDecoder.Draw());
|
|
BaseInit(screenDrawer.GetRenderPass(), screenDrawer.GetOsdSubpass());
|
|
InitOverlay(screenDrawer.OverlayPass());
|
|
textureDrawer.SetStorageTarget(texPool.TargetsStorable());
|
|
// (a quad pipeline is for one subpass: the one above is the last subpass's, this the colour one's)
|
|
lastPicturePipeline.Init(&shaderManager, screenDrawer.GetRenderPass(), 1, samples);
|
|
screenDrawer.SetLastPicturePipeline(&lastPicturePipeline);
|
|
InitComputeMotion();
|
|
|
|
return true;
|
|
}
|
|
|
|
void Resize(int w, int h) override
|
|
{
|
|
NOTICE_LOG(RENDERER, "OIT Resize %d x %d", w, h);
|
|
// what is replaced below may be in use by frames still on the GPU
|
|
texCommandPool.WaitAll();
|
|
oitBuffers.SetCompact(rend_compute_wanted && ComputeDrawer::DeviceLacks() == nullptr);
|
|
const u32 was = samples;
|
|
samples = SsaaSamples();
|
|
oitBuffers.SetSums(samples > 1 && GetContext()->HdrSumsReady());
|
|
if (samples != was)
|
|
{
|
|
// (a change of SSAA: the shaders, render passes and pipelines of the other count go)
|
|
oitShaderManager.SetSamples(samples);
|
|
quadPipeline.Forget();
|
|
lastPicturePipeline.Forget();
|
|
}
|
|
screenDrawer.Init(&samplerManager, &oitShaderManager, &oitBuffers, samples);
|
|
textureDrawer.ShareAttachments(&screenDrawer);
|
|
BaseInit(screenDrawer.GetRenderPass(), screenDrawer.GetOsdSubpass());
|
|
InitOverlay(screenDrawer.OverlayPass());
|
|
// (a quad pipeline is for one subpass: the one above is the last subpass's, this the colour one's)
|
|
lastPicturePipeline.Init(&shaderManager, screenDrawer.GetRenderPass(), 1, samples);
|
|
screenDrawer.SetLastPicturePipeline(&lastPicturePipeline);
|
|
MakeCompute();
|
|
InitComputeMotion();
|
|
// what this size no longer needs is given back once the frames that used it are done
|
|
GetContext()->GetHeap().TrimSoon();
|
|
}
|
|
|
|
void KeepPicture() override { screenDrawer.KeepPicture(); }
|
|
void SetRestoredPicture(vk::ImageView view) override { screenDrawer.SetRestoredPicture(view); }
|
|
|
|
protected:
|
|
/* The per-pixel lists sort the translucent polygons, by depth and then
|
|
* by polygon number: a frame decoded on the GPU numbers its strips by
|
|
* their draws' firstInstance, and without that it has no sorted pass */
|
|
u32 TaFlags() const override
|
|
{
|
|
return TA_GPU_DRAW | TA_GPU_PROVOKE | (taDecoder.HasInstance() ? TA_GPU_INSTANCE : TA_GPU_NO_SORT);
|
|
}
|
|
|
|
public:
|
|
void Term() override
|
|
{
|
|
DEBUG_LOG(RENDERER, "VulkanRenderer::Term");
|
|
GetContext()->LogVramUsage();
|
|
// (the frontend lets go of the pictures before they go)
|
|
GetContext()->ReleasePicture();
|
|
texCommandPool.WaitAll();
|
|
DropQuads();
|
|
computeDrawer.Term();
|
|
samplerManager.Term();
|
|
screenDrawer.Term();
|
|
lastPicturePipeline.Term();
|
|
textureDrawer.Term();
|
|
/* (after the drawers: the pipeline thread may be making pipelines
|
|
* of these shaders for them until their pipelines go) */
|
|
oitShaderManager.Term();
|
|
rttShaderManager.Term();
|
|
oitBuffers.Term();
|
|
BaseVulkanRenderer::Term();
|
|
}
|
|
|
|
bool RenderFrame() override
|
|
{
|
|
if (pvrrc.isRenderFramebuffer)
|
|
return true;
|
|
if (pvrrc.isRTT && (pvrrc.fb_X_CLIP.max < pvrrc.fb_X_CLIP.min || pvrrc.fb_Y_CLIP.max < pvrrc.fb_Y_CLIP.min))
|
|
{
|
|
/* A pixel clip that ends before it starts holds no pixel: the
|
|
* chip writes nothing. (Its size would be negative here.) */
|
|
WARN_LOG(RENDERER, "Render to texture with an empty pixel clip: %d-%d, %d-%d",
|
|
pvrrc.fb_X_CLIP.min, pvrrc.fb_X_CLIP.max, pvrrc.fb_Y_CLIP.min, pvrrc.fb_Y_CLIP.max);
|
|
return false;
|
|
}
|
|
|
|
/* Rendering Method: Compute draws the frames it can
|
|
* (vulkan/compute_drawer.h) into the same pictures, and the renders
|
|
* to a texture into the same textures; the others are drawn below */
|
|
if (computeDrawer.Ready() && pvrrc.isRTT)
|
|
{
|
|
if (!texPool.TargetsStorable())
|
|
computeDrawer.Fallback(ComputeFrame::Why_Rtt);
|
|
else if (!computeDrawer.CanDraw())
|
|
computeDrawer.Fallback(ComputeFrame::Why_Decode);
|
|
else if (computeDrawer.Prepare(false, false, paletteTexture.get(), OITTextureDrawer::ComputeSize()))
|
|
{
|
|
const OITTextureDrawer::ComputeTarget target = textureDrawer.ComputeBegin();
|
|
computeDrawer.Record(target.image, target.view, vk::ImageView());
|
|
textureDrawer.ComputeEnd();
|
|
return false;
|
|
}
|
|
}
|
|
else if (computeDrawer.Ready())
|
|
{
|
|
bool last, scaled;
|
|
if (!computeDrawer.CanDraw())
|
|
computeDrawer.Fallback(ComputeFrame::Why_Decode);
|
|
else if (vmus->Drawing())
|
|
computeDrawer.Fallback(ComputeFrame::Why_Osd);
|
|
else if (!screenDrawer.ComputeCanStart(&last, &scaled))
|
|
computeDrawer.Fallback(ComputeFrame::Why_Picture);
|
|
else if (computeDrawer.Prepare(last, scaled, paletteTexture.get()))
|
|
{
|
|
// (the upscaler's strips matched once the frame is this renderer's: the raster drawer matches its own)
|
|
screenDrawer.ComputeMotionBegin(computeDrawer.Slot());
|
|
const OITScreenDrawer::ComputeTarget target = screenDrawer.ComputeBegin(last, scaled);
|
|
computeDrawer.Record(target.image, target.view, target.last);
|
|
screenDrawer.ComputeEnd(computeDrawer.Sums());
|
|
screenDrawer.ComputeMotionEnd(computeDrawer.Slot(), computeDrawer.DepthImage(), computeDrawer.DepthView());
|
|
return true;
|
|
}
|
|
}
|
|
|
|
OITDrawer *drawer;
|
|
if (pvrrc.isRTT)
|
|
drawer = &textureDrawer;
|
|
else
|
|
drawer = &screenDrawer;
|
|
|
|
SetParsed(*drawer);
|
|
drawer->Draw(fogTexture.get(), paletteTexture.get());
|
|
|
|
if (!pvrrc.isRTT)
|
|
{
|
|
/* (the chip's write-out before what goes over the picture, which
|
|
* then goes in a render pass of its own: writeout.h) */
|
|
const bool after = screenDrawer.HasWriteOut() && vmus->Drawing() && screenDrawer.WriteOutBeforeOsd();
|
|
vmus->DrawOSD(screenDrawer.GetCurrentCommandBuffer(), vk::Extent2D(screen_width, screen_height), &texCommandPool,
|
|
after ? &overlayQuadPipeline : nullptr);
|
|
}
|
|
|
|
drawer->EndFrame();
|
|
|
|
return !pvrrc.isRTT;
|
|
}
|
|
|
|
private:
|
|
/* The compute renderer, when it is the rendering method; then the
|
|
* screen's drawer's motion vector pass over it. (Made before the
|
|
* drawers when the renderer starts: its kernels are the first the
|
|
* pipeline thread is given, as every frame draws with them.) */
|
|
void MakeCompute()
|
|
{
|
|
if (rend_compute_wanted)
|
|
// (for the temporal upscaler, with the depth stored)
|
|
computeDrawer.Init(GetContext()->GetViewPort(), samples, &texCommandPool, &samplerManager, upscaler_active() && samples == 1);
|
|
else
|
|
computeDrawer.Term();
|
|
}
|
|
void InitComputeMotion()
|
|
{
|
|
if (rend_compute_wanted)
|
|
computeDrawer.SetMotionPass(screenDrawer.ComputeMotionPass(computeDrawer.DepthViews()));
|
|
else
|
|
screenDrawer.ComputeMotionPass(std::vector<vk::ImageView>());
|
|
}
|
|
|
|
/* SSAA's samples a pixel, for the screen: as far as the device and
|
|
* the video memory budget go for its attachments, after the lists */
|
|
u32 SsaaSamples() const
|
|
{
|
|
// (the upscaler is the anti-aliasing then, and reads the depth, which SSAA's samples are not)
|
|
if (upscaler_active())
|
|
return 1;
|
|
const vk::Extent2D size = GetContext()->GetViewPort();
|
|
return GetContext()->SsaaSamples(settings.rend.Ssaa, ssaa_per_sample_kib(size.width, size.height, 4 + GetContext()->GetDepthBytes()),
|
|
(u32)(oitBuffers.PlannedSize(size.width, size.height) >> 10), false, "Vulkan per-pixel renderer");
|
|
}
|
|
|
|
OITBuffers oitBuffers;
|
|
SamplerManager samplerManager;
|
|
OITShaderManager oitShaderManager;
|
|
/* Textures are rendered to with one sample a pixel, SSAA or not: their
|
|
* sample attachments would be made as large as such a texture can be,
|
|
* more video memory than SSAA's screen attachments, and these renders'
|
|
* attachments are stored between render passes */
|
|
OITShaderManager rttShaderManager;
|
|
OITScreenDrawer screenDrawer;
|
|
QuadPipeline lastPicturePipeline;
|
|
OITTextureDrawer textureDrawer;
|
|
ComputeDrawer computeDrawer;
|
|
};
|
|
|
|
Renderer* rend_OITVulkan()
|
|
{
|
|
return new OITVulkanRenderer();
|
|
}
|