Files
libretroadmin b01ee57541 Samplers, texture sets and the sort's room made before the first frame
Every sampler a TSP can ask for - filtered or not, each axis clamped,
mirrored or repeated, the 16 mipmap biases, 288 - is made at the
Vulkan renderer's start into a plain table (SamplerManager::Init()), in
place of a std::map filled as polygons first asked. A drawer's texture
descriptor cache has its tables and a batch of sets with the drawer. The
translucent sort's room is reserved before any renderer's first frame,
the translucent list's polygons and 64K triangles (SortReserve()), and
each renderer's lists of the passes' draws (SortPassesReserve()): a
frame up to them allocates nothing.

Six sites come off the list of allocations during play, 26 left.
2026-11-10 03:25:22 +01:00

167 lines
5.5 KiB
C++

#define PVR_REGS_FOR_RENDERER // see hw/pvr/pvr_regs.h
/*
Created on: Oct 2, 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 <memory>
#include <math.h>
#include "vulkan.h"
#include "vulkan_renderer.h"
#include "drawer.h"
#include "shaders.h"
#include "rend/ssaa_pattern.h"
class VulkanRenderer final : public BaseVulkanRenderer
{
public:
bool Init() override
{
DEBUG_LOG(RENDERER, "VulkanRenderer::Init");
samples = SsaaSamples();
samplerManager.Init();
textureDrawer.SetCommandPool(&texCommandPool);
textureDrawer.Init(&samplerManager, &shaderManager, &textureCache, RttSamples());
textureDrawer.SetGpuDraw(taDecoder.Draw());
screenDrawer.Init(&samplerManager, &shaderManager, samples);
screenDrawer.SetCommandPool(&texCommandPool);
screenDrawer.SetGpuDraw(taDecoder.Draw());
taDecoder.WantSort(true);
BaseInit(screenDrawer.GetRenderPass(), screenDrawer.GetOsdSubpass());
InitOverlay(screenDrawer.OverlayPass());
screenDrawer.SetQuadPipeline(&quadPipeline);
return true;
}
void Resize(int w, int h) override
{
// what is replaced below may be in use by frames still on the GPU
texCommandPool.WaitAll();
const u32 was = samples;
samples = SsaaSamples();
if (samples != was)
{
textureDrawer.Init(&samplerManager, &shaderManager, &textureCache, RttSamples());
quadPipeline.Forget();
}
screenDrawer.Init(&samplerManager, &shaderManager, samples);
BaseInit(screenDrawer.GetRenderPass(), screenDrawer.GetOsdSubpass());
InitOverlay(screenDrawer.OverlayPass());
screenDrawer.SetQuadPipeline(&quadPipeline);
// 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:
/* A sorted pass's translucent triangles are sorted from the frame's
* depths (TA_GPU_SORT); sorted by strip, the polygons are sorted from
* the CPU's vertices, and a frame that may have a sorted pass is
* parsed. The polygons drawn again for shadows are those the GPU says
* reach into the volumes. */
u32 TaFlags() const override
{
return TA_GPU_DRAW | TA_GPU_PROVOKE | TA_GPU_SHADOWS
| (!settings.pvr.Emulation.AlphaSortMode && taDecoder.CanSort() ? TA_GPU_SORT : 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();
screenDrawer.Term();
textureDrawer.Term();
samplerManager.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;
}
Drawer *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->EndRenderPass();
return !pvrrc.isRTT;
}
private:
/* SSAA's samples a pixel, for the screen: what the option asks, as far
* as the device and the video memory budget go for its attachments */
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()),
0, true, "Vulkan per-triangle renderer");
}
/* ...and for the textures rendered to: the same where their sample
* attachments cost nothing (transient, never stored, in memory that
* is only backed when used); one elsewhere, since they are made as
* large as such a texture can be, which is more video memory than the
* screen's */
u32 RttSamples() const { return GetContext()->HasLazyMemory() ? samples : 1; }
SamplerManager samplerManager;
ScreenDrawer screenDrawer;
TextureDrawer textureDrawer;
};
Renderer* rend_Vulkan()
{
return new VulkanRenderer();
}