#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 . */ #include #include #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()); textureDrawer.ShareDepth(&screenDrawer); 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); textureDrawer.ShareDepth(&screenDrawer); 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(); }