From 1713124711259502263bb86aa5ccb4c3f05d65c1 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 10 Jul 2020 18:50:53 +0200 Subject: [PATCH] vulkan: linear tiling for small texs. don't track in-flight texs Linear tiling is faster for small, frequently updated textures When lots of textures are updated each frame, a skipped frame will destroy/recreate all textures, causing another skipped frame, etc. So in-flight texture tracking is disabled, except for RTT textures Limit swap chain to 2 images Refactor base and oit renderers to avoid code dup --- core/rend/TexCache.cpp | 3 +- core/rend/TexCache.h | 3 - core/rend/vulkan/drawer.cpp | 4 +- core/rend/vulkan/oit/oit_drawer.cpp | 5 +- core/rend/vulkan/oit/oit_renderer.cpp | 250 +---------------------- core/rend/vulkan/texture.cpp | 40 +++- core/rend/vulkan/texture.h | 20 +- core/rend/vulkan/vulkan_context.cpp | 6 +- core/rend/vulkan/vulkan_renderer.cpp | 242 +--------------------- core/rend/vulkan/vulkan_renderer.h | 279 ++++++++++++++++++++++++++ 10 files changed, 347 insertions(+), 505 deletions(-) create mode 100644 core/rend/vulkan/vulkan_renderer.h diff --git a/core/rend/TexCache.cpp b/core/rend/TexCache.cpp index 0b25543c1..d05579328 100644 --- a/core/rend/TexCache.cpp +++ b/core/rend/TexCache.cpp @@ -388,7 +388,7 @@ void BaseTextureCacheData::PrintTextureName() //true if : dirty or paletted texture and hashes don't match bool BaseTextureCacheData::NeedsUpdate() { - bool rc = dirty; + bool rc = dirty != 0; if (tex_type != TextureType::_8) { if (tcw.PixelFmt == PixelPal4 && palette_hash != pal_hash_16[tcw.PalSelect]) @@ -421,7 +421,6 @@ bool BaseTextureCacheData::Delete() void BaseTextureCacheData::Create() { //Reset state info .. - Lookups = 0; Updates = 0; dirty = FrameCount; lock_block = nullptr; diff --git a/core/rend/TexCache.h b/core/rend/TexCache.h index 4575242cd..c74bf06cd 100644 --- a/core/rend/TexCache.h +++ b/core/rend/TexCache.h @@ -651,8 +651,6 @@ public: // Decoded/filtered texture format TextureType tex_type; - u32 Lookups; - u32 sa; //pixel data start address in vram (might be offset for mipmaps/etc) u32 sa_tex; //texture data start address in vram u32 w,h; //width & height of the texture @@ -770,7 +768,6 @@ public: texture->tsp = tsp; texture->tcw = tcw; } - texture->Lookups++; return texture; } diff --git a/core/rend/vulkan/drawer.cpp b/core/rend/vulkan/drawer.cpp index a8b69d9ae..a3531e917 100644 --- a/core/rend/vulkan/drawer.cpp +++ b/core/rend/vulkan/drawer.cpp @@ -471,13 +471,15 @@ vk::CommandBuffer TextureDrawer::BeginRenderPass() texture->readOnlyImageView = *texture->imageView; textureCache->DestroyLater(texture); } + textureCache->SetInFlight(texture); if (texture->format != vk::Format::eR8G8B8A8Unorm || texture->extent.width != widthPow2 || texture->extent.height != heightPow2) { texture->extent = vk::Extent2D(widthPow2, heightPow2); texture->format = vk::Format::eR8G8B8A8Unorm; + texture->needsStaging = true; texture->CreateImage(vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled, - vk::ImageLayout::eUndefined, vk::MemoryPropertyFlags(), vk::ImageAspectFlagBits::eColor); + vk::ImageLayout::eUndefined, vk::ImageAspectFlagBits::eColor); colorImageCurrentLayout = vk::ImageLayout::eUndefined; } else diff --git a/core/rend/vulkan/oit/oit_drawer.cpp b/core/rend/vulkan/oit/oit_drawer.cpp index ecece3674..4b648870a 100644 --- a/core/rend/vulkan/oit/oit_drawer.cpp +++ b/core/rend/vulkan/oit/oit_drawer.cpp @@ -540,12 +540,15 @@ vk::CommandBuffer OITTextureDrawer::NewFrame() texture->readOnlyImageView = *texture->imageView; textureCache->DestroyLater(texture); } + textureCache->SetInFlight(texture); + if (texture->format != vk::Format::eR8G8B8A8Unorm || texture->extent.width != widthPow2 || texture->extent.height != heightPow2) { texture->extent = vk::Extent2D(widthPow2, heightPow2); texture->format = vk::Format::eR8G8B8A8Unorm; + texture->needsStaging = true; texture->CreateImage(vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled, - vk::ImageLayout::eUndefined, vk::MemoryPropertyFlags(), vk::ImageAspectFlagBits::eColor); + vk::ImageLayout::eUndefined, vk::ImageAspectFlagBits::eColor); colorImageCurrentLayout = vk::ImageLayout::eUndefined; } else diff --git a/core/rend/vulkan/oit/oit_renderer.cpp b/core/rend/vulkan/oit/oit_renderer.cpp index d875f24ca..fb2745f52 100644 --- a/core/rend/vulkan/oit/oit_renderer.cpp +++ b/core/rend/vulkan/oit/oit_renderer.cpp @@ -19,61 +19,27 @@ along with Flycast. If not, see . */ #include "../vulkan.h" -#include "hw/pvr/ta.h" -#include "hw/pvr/Renderer_if.h" -#include "../commandpool.h" +#include "../vulkan_renderer.h" #include "oit_drawer.h" #include "oit_shaders.h" -#include "rend/gui.h" -#include "rend/osd.h" -#include "../pipeline.h" #include "oit_buffer.h" -#include -#include - -class OITVulkanRenderer : public Renderer +class OITVulkanRenderer final : public BaseVulkanRenderer { public: bool Init() override { DEBUG_LOG(RENDERER, "OITVulkanRenderer::Init"); try { - texCommandPool.Init(); + BaseVulkanRenderer::Init(); oitBuffers.Init(0, 0); - textureDrawer.Init(&samplerManager, &shaderManager, &textureCache, &oitBuffers); + textureDrawer.Init(&samplerManager, &oitShaderManager, &textureCache, &oitBuffers); textureDrawer.SetCommandPool(&texCommandPool); - screenDrawer.Init(&samplerManager, &shaderManager, &oitBuffers); + screenDrawer.Init(&samplerManager, &oitShaderManager, &oitBuffers); screenDrawer.SetCommandPool(&texCommandPool); -#ifdef __ANDROID__ - if (!vjoyTexture) - { - int w, h; - u8 *image_data = loadOSDButtons(w, h); - texCommandPool.BeginFrame(); - vjoyTexture = std::unique_ptr(new Texture()); - vjoyTexture->tex_type = TextureType::_8888; - vjoyTexture->tcw.full = 0; - vjoyTexture->tsp.full = 0; - vjoyTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); - vjoyTexture->SetDevice(GetContext()->GetDevice()); - vjoyTexture->SetCommandBuffer(texCommandPool.Allocate()); - vjoyTexture->UploadToGPU(OSD_TEX_W, OSD_TEX_H, image_data, false); - vjoyTexture->SetCommandBuffer(nullptr); - texCommandPool.EndFrame(); - delete [] image_data; - osdPipeline.Init(&normalShaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass()); - } - if (!osdBuffer) - { - osdBuffer = std::unique_ptr(new BufferData(sizeof(OSDVertex) * VJOY_VISIBLE * 4, - vk::BufferUsageFlagBits::eVertexBuffer)); - } -#endif - return true; } catch (const vk::SystemError& err) @@ -86,11 +52,8 @@ public: void Resize(int w, int h) override { NOTICE_LOG(RENDERER, "OIT Resize %d x %d", w, h); - texCommandPool.Init(); - screenDrawer.Init(&samplerManager, &shaderManager, &oitBuffers); -#ifdef __ANDROID__ - osdPipeline.Init(&normalShaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass()); -#endif + BaseVulkanRenderer::Resize(w, h); + screenDrawer.Init(&samplerManager, &oitShaderManager, &oitBuffers); } void Term() override @@ -100,114 +63,7 @@ public: screenDrawer.Term(); textureDrawer.Term(); oitBuffers.Term(); - osdBuffer.reset(); - textureCache.Clear(); - fogTexture = nullptr; - texCommandPool.Term(); - framebufferTextures.clear(); - } - - bool RenderFramebuffer() - { - if (FB_R_SIZE.fb_x_size == 0 || FB_R_SIZE.fb_y_size == 0) - return false; - - PixelBuffer pb; - int width; - int height; - ReadFramebuffer(pb, width, height); - - if (framebufferTextures.size() != GetContext()->GetSwapChainSize()) - framebufferTextures.resize(GetContext()->GetSwapChainSize()); - std::unique_ptr& curTexture = framebufferTextures[GetContext()->GetCurrentImageIndex()]; - if (!curTexture) - { - curTexture = std::unique_ptr(new Texture()); - curTexture->tex_type = TextureType::_8888; - curTexture->tcw.full = 0; - curTexture->tsp.full = 0; - curTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); - curTexture->SetDevice(GetContext()->GetDevice()); - } - curTexture->SetCommandBuffer(texCommandPool.Allocate()); - curTexture->UploadToGPU(width, height, (u8*)pb.data(), false); - curTexture->SetCommandBuffer(nullptr); - texCommandPool.EndFrame(); - - GetContext()->PresentFrame(curTexture->GetImageView(), { 640, 480 }); - - return true; - } - - bool Process(TA_context* ctx) override - { - texCommandPool.BeginFrame(); - textureCache.SetCurrentIndex(texCommandPool.GetIndex()); - - if (ctx->rend.isRenderFramebuffer) - { - return RenderFramebuffer(); - } - - ctx->rend_inuse.lock(); - - if (KillTex) - textureCache.Clear(); - - bool result = ta_parse_vdrc(ctx); - - textureCache.CollectCleanup(); - - if (result) - { - CheckFogTexture(); - CheckPaletteTexture(); - } - else - texCommandPool.EndFrame(); - - return result; - } - - void DrawOSD(bool clear_screen) override - { - gui_display_osd(); - if (!vjoyTexture) - return; - if (clear_screen) - { - GetContext()->NewFrame(); - GetContext()->BeginRenderPass(); - } - const float dc2s_scale_h = screen_height / 480.0f; - const float sidebarWidth = (screen_width - dc2s_scale_h * 640.0f) / 2; - - std::vector osdVertices = GetOSDVertices(); - const float x1 = 2.0f / (screen_width / dc2s_scale_h); - const float y1 = 2.0f / 480; - const float x2 = 1 - 2 * sidebarWidth / screen_width; - const float y2 = 1; - for (OSDVertex& vtx : osdVertices) - { - vtx.x = vtx.x * x1 - x2; - vtx.y = vtx.y * y1 - y2; - } - - const vk::CommandBuffer cmdBuffer = GetContext()->GetCurrentCommandBuffer(); - cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, osdPipeline.GetPipeline()); - - osdPipeline.BindDescriptorSets(cmdBuffer); - const vk::Viewport viewport(0, 0, (float)screen_width, (float)screen_height, 0, 1.f); - cmdBuffer.setViewport(0, 1, &viewport); - const vk::Rect2D scissor({ 0, 0 }, { (u32)screen_width, (u32)screen_height }); - cmdBuffer.setScissor(0, 1, &scissor); - osdBuffer->upload(osdVertices.size() * sizeof(OSDVertex), osdVertices.data()); - const vk::DeviceSize zero = 0; - cmdBuffer.bindVertexBuffers(0, 1, &osdBuffer->buffer.get(), &zero); - for (size_t i = 0; i < osdVertices.size(); i += 4) - cmdBuffer.draw(4, 1, i, 0); - if (clear_screen) - GetContext()->EndFrame(); + BaseVulkanRenderer::Term(); } bool Render() override @@ -228,100 +84,12 @@ public: return !pvrrc.isRTT; } - void Present() override - { - GetContext()->Present(); - } - - virtual u64 GetTexture(TSP tsp, TCW tcw) override - { - Texture* tf = textureCache.getTextureCacheData(tsp, tcw); - - if (tf->IsNew()) - { - tf->Create(); - tf->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); - tf->SetDevice(GetContext()->GetDevice()); - } - - //update if needed - if (tf->NeedsUpdate()) - { - textureCache.DestroyLater(tf); - tf->SetCommandBuffer(texCommandPool.Allocate()); - tf->Update(); - } - else if (tf->IsCustomTextureAvailable()) - { - textureCache.DestroyLater(tf); - tf->SetCommandBuffer(texCommandPool.Allocate()); - tf->CheckCustomTexture(); - } - tf->SetCommandBuffer(nullptr); - - return tf->GetIntId(); - } - private: - VulkanContext *GetContext() const { return VulkanContext::Instance(); } - - void CheckFogTexture() - { - if (!fogTexture) - { - fogTexture = std::unique_ptr(new Texture()); - fogTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); - fogTexture->SetDevice(GetContext()->GetDevice()); - fogTexture->tex_type = TextureType::_8; - fog_needs_update = true; - } - if (!fog_needs_update || !settings.rend.Fog) - return; - fog_needs_update = false; - u8 texData[256]; - MakeFogTexture(texData); - fogTexture->SetCommandBuffer(texCommandPool.Allocate()); - - fogTexture->UploadToGPU(128, 2, texData, false); - - fogTexture->SetCommandBuffer(nullptr); - } - void CheckPaletteTexture() - { - if (!paletteTexture) - { - paletteTexture = std::unique_ptr(new Texture()); - paletteTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); - paletteTexture->SetDevice(GetContext()->GetDevice()); - paletteTexture->tex_type = TextureType::_8888; - palette_updated = true; - } - if (!palette_updated) - return; - palette_updated = false; - - paletteTexture->SetCommandBuffer(texCommandPool.Allocate()); - - paletteTexture->UploadToGPU(1024, 1, (u8 *)palette32_ram, false); - - paletteTexture->SetCommandBuffer(nullptr); - } - OITBuffers oitBuffers; - std::unique_ptr fogTexture; - std::unique_ptr paletteTexture; - CommandPool texCommandPool; - SamplerManager samplerManager; - OITShaderManager shaderManager; - ShaderManager normalShaderManager; + OITShaderManager oitShaderManager; OITScreenDrawer screenDrawer; OITTextureDrawer textureDrawer; - std::vector> framebufferTextures; - OSDPipeline osdPipeline; - std::unique_ptr vjoyTexture; - std::unique_ptr osdBuffer; - TextureCache textureCache; }; Renderer* rend_OITVulkan() diff --git a/core/rend/vulkan/texture.cpp b/core/rend/vulkan/texture.cpp index 432c90f53..d6d60876f 100644 --- a/core/rend/vulkan/texture.cpp +++ b/core/rend/vulkan/texture.cpp @@ -202,37 +202,39 @@ void Texture::Init(u32 width, u32 height, vk::Format format, u32 dataSize, bool == vk::FormatFeatureFlagBits::eSampledImage ? vk::ImageTiling::eOptimal : vk::ImageTiling::eLinear; + if (height <= 32 + && dataSize / height <= 64 + && !mipmapped + && (formatProperties.linearTilingFeatures & vk::FormatFeatureFlagBits::eSampledImage) == vk::FormatFeatureFlagBits::eSampledImage) + imageTiling = vk::ImageTiling::eLinear; needsStaging = imageTiling != vk::ImageTiling::eLinear; vk::ImageLayout initialLayout; - vk::MemoryPropertyFlags requirements; vk::ImageUsageFlags usageFlags = vk::ImageUsageFlagBits::eSampled; if (needsStaging) { stagingBufferData = std::unique_ptr(new BufferData(dataSize, vk::BufferUsageFlagBits::eTransferSrc)); usageFlags |= vk::ImageUsageFlagBits::eTransferDst; initialLayout = vk::ImageLayout::eUndefined; - requirements = vk::MemoryPropertyFlagBits::eDeviceLocal; } else { verify((formatProperties.linearTilingFeatures & vk::FormatFeatureFlagBits::eSampledImage) == vk::FormatFeatureFlagBits::eSampledImage); initialLayout = vk::ImageLayout::ePreinitialized; - requirements = vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostVisible; } if (mipmapped && !mipmapsIncluded) usageFlags |= vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst; - CreateImage(imageTiling, usageFlags, initialLayout, requirements, vk::ImageAspectFlagBits::eColor); + CreateImage(imageTiling, usageFlags, initialLayout, vk::ImageAspectFlagBits::eColor); } void Texture::CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::ImageLayout initialLayout, - vk::MemoryPropertyFlags memoryProperties, vk::ImageAspectFlags aspectMask) + vk::ImageAspectFlags aspectMask) { vk::ImageCreateInfo imageCreateInfo(vk::ImageCreateFlags(), vk::ImageType::e2D, format, vk::Extent3D(extent, 1), mipmapLevels, 1, vk::SampleCountFlagBits::e1, tiling, usage, vk::SharingMode::eExclusive, 0, nullptr, initialLayout); image = device.createImageUnique(imageCreateInfo); - VmaAllocationCreateInfo allocCreateInfo = { VmaAllocationCreateFlags(), VmaMemoryUsage::VMA_MEMORY_USAGE_GPU_ONLY }; + VmaAllocationCreateInfo allocCreateInfo = { VmaAllocationCreateFlags(), needsStaging ? VmaMemoryUsage::VMA_MEMORY_USAGE_GPU_ONLY : VmaMemoryUsage::VMA_MEMORY_USAGE_CPU_TO_GPU }; if (!needsStaging) allocCreateInfo.flags = VmaAllocationCreateFlagBits::VMA_ALLOCATION_CREATE_MAPPED_BIT; allocation = VulkanContext::Instance()->GetAllocator().AllocateForImage(*image, allocCreateInfo); @@ -247,8 +249,8 @@ void Texture::SetImage(u32 srcSize, void *srcData, bool isNew, bool genMipmaps) verify((bool)commandBuffer); commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit)); -// if (!isNew && !needsStaging) -// setImageLayout(commandBuffer, image.get(), format, mipmapLevels, vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eUndefined); + if (!isNew && !needsStaging) + setImageLayout(commandBuffer, image.get(), format, mipmapLevels, vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eGeneral); void* data; if (needsStaging) @@ -270,6 +272,25 @@ void Texture::SetImage(u32 srcSize, void *srcData, bool isNew, bool genMipmaps) src += size; } } + else if (!needsStaging) + { + vk::SubresourceLayout layout = device.getImageSubresourceLayout(*image, vk::ImageSubresource(vk::ImageAspectFlagBits::eColor, 0, 0)); + if (layout.size != srcSize) + { + u8 *src = (u8 *)srcData; + u8 *dst = (u8 *)data; + u32 srcSz = extent.width * 2; + if (tex_type == TextureType::_8888) + srcSz *= 2; + else if (tex_type == TextureType::_8) + srcSz /= 2; + u8 * const srcEnd = src + srcSz * extent.height; + for (; src < srcEnd; src += srcSz, dst += layout.rowPitch) + memcpy(dst, src, srcSz); + } + else + memcpy(data, srcData, srcSize); + } else memcpy(data, srcData, srcSize); @@ -309,7 +330,8 @@ void Texture::SetImage(u32 srcSize, void *srcData, bool isNew, bool genMipmaps) GenerateMipmaps(); else // If we can use the linear tiled image as a texture, just do it - setImageLayout(commandBuffer, image.get(), format, mipmapLevels, vk::ImageLayout::ePreinitialized, vk::ImageLayout::eShaderReadOnlyOptimal); + setImageLayout(commandBuffer, image.get(), format, mipmapLevels, isNew ? vk::ImageLayout::ePreinitialized : vk::ImageLayout::eGeneral, + vk::ImageLayout::eShaderReadOnlyOptimal); } commandBuffer.end(); } diff --git a/core/rend/vulkan/texture.h b/core/rend/vulkan/texture.h index 8927857d4..692023661 100644 --- a/core/rend/vulkan/texture.h +++ b/core/rend/vulkan/texture.h @@ -52,7 +52,7 @@ private: void Init(u32 width, u32 height, vk::Format format ,u32 dataSize, bool mipmapped, bool mipmapsIncluded); void SetImage(u32 size, void *data, bool isNew, bool genMipmaps); void CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::ImageLayout initialLayout, - vk::MemoryPropertyFlags memoryProperties, vk::ImageAspectFlags aspectMask); + vk::ImageAspectFlags aspectMask); void GenerateMipmaps(); vk::Format format = vk::Format::eUndefined; @@ -137,13 +137,6 @@ private: class TextureCache : public BaseTextureCache { public: - Texture *getTextureCacheData(TSP tsp, TCW tcw) - { - Texture *texture = BaseTextureCache::getTextureCacheData(tsp, tcw); - inFlightTextures[currentIndex].insert(texture); - return texture; - } - void SetCurrentIndex(int index) { if (currentIndex < inFlightTextures.size()) std::for_each(inFlightTextures[currentIndex].begin(), inFlightTextures[currentIndex].end(), @@ -158,8 +151,15 @@ public: bool IsInFlight(Texture *texture) { - return std::any_of(inFlightTextures.begin(), inFlightTextures.end(), - [texture](const std::unordered_set& set) { return set.find(texture) != set.end(); }); + for (u32 i = 0; i < inFlightTextures.size(); i++) + if (i != currentIndex && inFlightTextures[i].find(texture) != inFlightTextures[i].end()) + return true; + return false; + } + + void SetInFlight(Texture *texture) + { + inFlightTextures[currentIndex].insert(texture); } void DestroyLater(Texture *texture) diff --git a/core/rend/vulkan/vulkan_context.cpp b/core/rend/vulkan/vulkan_context.cpp index 15eaacef7..312df48c9 100644 --- a/core/rend/vulkan/vulkan_context.cpp +++ b/core/rend/vulkan/vulkan_context.cpp @@ -97,6 +97,9 @@ VKAPI_ATTR static VkBool32 VKAPI_CALL debugUtilsMessengerCallback(VkDebugUtilsMe return VK_TRUE; } #else +#if HOST_CPU == CPU_ARM +__attribute__((pcs("aapcs-vfp"))) +#endif static VkBool32 debugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* /*pUserData*/) { @@ -563,7 +566,7 @@ void VulkanContext::CreateSwapChain() (surfaceCapabilities.supportedCompositeAlpha & vk::CompositeAlphaFlagBitsKHR::ePreMultiplied) ? vk::CompositeAlphaFlagBitsKHR::ePreMultiplied : (surfaceCapabilities.supportedCompositeAlpha & vk::CompositeAlphaFlagBitsKHR::ePostMultiplied) ? vk::CompositeAlphaFlagBitsKHR::ePostMultiplied : (surfaceCapabilities.supportedCompositeAlpha & vk::CompositeAlphaFlagBitsKHR::eInherit) ? vk::CompositeAlphaFlagBitsKHR::eInherit : vk::CompositeAlphaFlagBitsKHR::eOpaque; - u32 imageCount = std::max(3u, surfaceCapabilities.minImageCount); + u32 imageCount = std::max(2u, surfaceCapabilities.minImageCount); if (surfaceCapabilities.maxImageCount != 0) imageCount = std::min(imageCount, surfaceCapabilities.maxImageCount); vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eColorAttachment; @@ -818,6 +821,7 @@ void VulkanContext::PresentLastFrame() void VulkanContext::Term() { lastFrameView = nullptr; + device->waitIdle(); ImGui_ImplVulkan_Shutdown(); gui_term(); if (device && pipelineCache) diff --git a/core/rend/vulkan/vulkan_renderer.cpp b/core/rend/vulkan/vulkan_renderer.cpp index d022f49d4..8cb10662b 100644 --- a/core/rend/vulkan/vulkan_renderer.cpp +++ b/core/rend/vulkan/vulkan_renderer.cpp @@ -19,23 +19,17 @@ along with Flycast. If not, see . */ #include "vulkan.h" -#include "hw/pvr/Renderer_if.h" -#include "hw/pvr/ta.h" -#include "commandpool.h" +#include "vulkan_renderer.h" #include "drawer.h" #include "shaders.h" -#include "rend/gui.h" -#include "rend/osd.h" -#include - -class VulkanRenderer : public Renderer +class VulkanRenderer final : public BaseVulkanRenderer { public: bool Init() override { DEBUG_LOG(RENDERER, "VulkanRenderer::Init"); - texCommandPool.Init(); + BaseVulkanRenderer::Init(); textureDrawer.Init(&samplerManager, &shaderManager, &textureCache); textureDrawer.SetCommandPool(&texCommandPool); @@ -43,158 +37,20 @@ public: screenDrawer.Init(&samplerManager, &shaderManager); screenDrawer.SetCommandPool(&texCommandPool); -#ifdef __ANDROID__ - if (!vjoyTexture) - { - int w, h; - u8 *image_data = loadOSDButtons(w, h); - texCommandPool.BeginFrame(); - vjoyTexture = std::unique_ptr(new Texture()); - vjoyTexture->tex_type = TextureType::_8888; - vjoyTexture->tcw.full = 0; - vjoyTexture->tsp.full = 0; - vjoyTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); - vjoyTexture->SetDevice(GetContext()->GetDevice()); - vjoyTexture->SetCommandBuffer(texCommandPool.Allocate()); - vjoyTexture->UploadToGPU(OSD_TEX_W, OSD_TEX_H, image_data, false); - vjoyTexture->SetCommandBuffer(nullptr); - texCommandPool.EndFrame(); - delete [] image_data; - osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass()); - } - if (!osdBuffer) - { - osdBuffer = std::unique_ptr(new BufferData(sizeof(OSDVertex) * VJOY_VISIBLE * 4, - vk::BufferUsageFlagBits::eVertexBuffer)); - } -#endif - return true; } void Resize(int w, int h) override { - texCommandPool.Init(); + BaseVulkanRenderer::Resize(w, h); screenDrawer.Init(&samplerManager, &shaderManager); -#ifdef __ANDROID__ - osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass()); -#endif } void Term() override { DEBUG_LOG(RENDERER, "VulkanRenderer::Term"); GetContext()->WaitIdle(); - osdBuffer.reset(); - vjoyTexture.reset(); - textureCache.Clear(); - fogTexture = nullptr; - texCommandPool.Term(); - framebufferTextures.clear(); - } - - bool RenderFramebuffer() - { - if (FB_R_SIZE.fb_x_size == 0 || FB_R_SIZE.fb_y_size == 0) - return false; - - PixelBuffer pb; - int width; - int height; - ReadFramebuffer(pb, width, height); - - if (framebufferTextures.size() != GetContext()->GetSwapChainSize()) - framebufferTextures.resize(GetContext()->GetSwapChainSize()); - std::unique_ptr& curTexture = framebufferTextures[GetContext()->GetCurrentImageIndex()]; - if (!curTexture) - { - curTexture = std::unique_ptr(new Texture()); - curTexture->tex_type = TextureType::_8888; - curTexture->tcw.full = 0; - curTexture->tsp.full = 0; - curTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); - curTexture->SetDevice(GetContext()->GetDevice()); - } - curTexture->SetCommandBuffer(texCommandPool.Allocate()); - curTexture->UploadToGPU(width, height, (u8*)pb.data(), false); - curTexture->SetCommandBuffer(nullptr); - texCommandPool.EndFrame(); - - GetContext()->PresentFrame(curTexture->GetImageView(), { 640, 480 }); - - return true; - } - - bool Process(TA_context* ctx) override - { - texCommandPool.BeginFrame(); - textureCache.SetCurrentIndex(texCommandPool.GetIndex()); - - if (ctx->rend.isRenderFramebuffer) - { - return RenderFramebuffer(); - } - - ctx->rend_inuse.lock(); - - if (KillTex) - textureCache.Clear(); - - bool result = ta_parse_vdrc(ctx); - - textureCache.CollectCleanup(); - - if (result) - { - CheckFogTexture(); - CheckPaletteTexture(); - } - else - texCommandPool.EndFrame(); - - return result; - } - - // FIXME This needs to go in its own class - void DrawOSD(bool clear_screen) override - { - gui_display_osd(); - if (!vjoyTexture) - return; - if (clear_screen) - { - GetContext()->NewFrame(); - GetContext()->BeginRenderPass(); - } - const float dc2s_scale_h = screen_height / 480.0f; - const float sidebarWidth = (screen_width - dc2s_scale_h * 640.0f) / 2; - - std::vector osdVertices = GetOSDVertices(); - const float x1 = 2.0f / (screen_width / dc2s_scale_h); - const float y1 = 2.0f / 480; - const float x2 = 1 - 2 * sidebarWidth / screen_width; - const float y2 = 1; - for (OSDVertex& vtx : osdVertices) - { - vtx.x = vtx.x * x1 - x2; - vtx.y = vtx.y * y1 - y2; - } - - const vk::CommandBuffer cmdBuffer = GetContext()->GetCurrentCommandBuffer(); - cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, osdPipeline.GetPipeline()); - - osdPipeline.BindDescriptorSets(cmdBuffer); - const vk::Viewport viewport(0, 0, (float)screen_width, (float)screen_height, 0, 1.f); - cmdBuffer.setViewport(0, 1, &viewport); - const vk::Rect2D scissor({ 0, 0 }, { (u32)screen_width, (u32)screen_height }); - cmdBuffer.setScissor(0, 1, &scissor); - osdBuffer->upload(osdVertices.size() * sizeof(OSDVertex), osdVertices.data()); - const vk::DeviceSize zero = 0; - cmdBuffer.bindVertexBuffers(0, 1, &osdBuffer->buffer.get(), &zero); - for (size_t i = 0; i < osdVertices.size(); i += 4) - cmdBuffer.draw(4, 1, i, 0); - if (clear_screen) - GetContext()->EndFrame(); + BaseVulkanRenderer::Term(); } bool Render() override @@ -215,98 +71,10 @@ public: return !pvrrc.isRTT; } - void Present() override - { - GetContext()->Present(); - } - - virtual u64 GetTexture(TSP tsp, TCW tcw) override - { - Texture* tf = textureCache.getTextureCacheData(tsp, tcw); - - if (tf->IsNew()) - { - tf->Create(); - tf->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); - tf->SetDevice(GetContext()->GetDevice()); - } - - //update if needed - if (tf->NeedsUpdate()) - { - textureCache.DestroyLater(tf); - tf->SetCommandBuffer(texCommandPool.Allocate()); - tf->Update(); - } - else if (tf->IsCustomTextureAvailable()) - { - textureCache.DestroyLater(tf); - tf->SetCommandBuffer(texCommandPool.Allocate()); - tf->CheckCustomTexture(); - } - tf->SetCommandBuffer(nullptr); - - return tf->GetIntId(); - } - private: - VulkanContext *GetContext() const { return VulkanContext::Instance(); } - - void CheckFogTexture() - { - if (!fogTexture) - { - fogTexture = std::unique_ptr(new Texture()); - fogTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); - fogTexture->SetDevice(GetContext()->GetDevice()); - fogTexture->tex_type = TextureType::_8; - fog_needs_update = true; - } - if (!fog_needs_update || !settings.rend.Fog) - return; - fog_needs_update = false; - u8 texData[256]; - MakeFogTexture(texData); - fogTexture->SetCommandBuffer(texCommandPool.Allocate()); - - fogTexture->UploadToGPU(128, 2, texData, false); - - fogTexture->SetCommandBuffer(nullptr); - } - void CheckPaletteTexture() - { - if (!paletteTexture) - { - paletteTexture = std::unique_ptr(new Texture()); - paletteTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); - paletteTexture->SetDevice(GetContext()->GetDevice()); - paletteTexture->tex_type = TextureType::_8888; - palette_updated = true; - } - if (!palette_updated) - return; - palette_updated = false; - - paletteTexture->SetCommandBuffer(texCommandPool.Allocate()); - - paletteTexture->UploadToGPU(1024, 1, (u8 *)palette32_ram, false); - - paletteTexture->SetCommandBuffer(nullptr); - } - - std::unique_ptr fogTexture; - std::unique_ptr paletteTexture; - CommandPool texCommandPool; - SamplerManager samplerManager; - ShaderManager shaderManager; ScreenDrawer screenDrawer; TextureDrawer textureDrawer; - std::vector> framebufferTextures; - OSDPipeline osdPipeline; - std::unique_ptr vjoyTexture; - std::unique_ptr osdBuffer; - TextureCache textureCache; }; Renderer* rend_Vulkan() diff --git a/core/rend/vulkan/vulkan_renderer.h b/core/rend/vulkan/vulkan_renderer.h new file mode 100644 index 000000000..413573835 --- /dev/null +++ b/core/rend/vulkan/vulkan_renderer.h @@ -0,0 +1,279 @@ +/* + Copyright 2020 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 "vulkan.h" +#include "hw/pvr/Renderer_if.h" +#include "hw/pvr/ta.h" +#include "commandpool.h" +#include "pipeline.h" +#include "rend/gui.h" +#include "rend/osd.h" + +#include +#include + +class BaseVulkanRenderer : public Renderer +{ +public: + virtual bool Init() override + { + texCommandPool.Init(); + +#ifdef __ANDROID__ + if (!vjoyTexture) + { + int w, h; + u8 *image_data = loadOSDButtons(w, h); + texCommandPool.BeginFrame(); + vjoyTexture = std::unique_ptr(new Texture()); + vjoyTexture->tex_type = TextureType::_8888; + vjoyTexture->tcw.full = 0; + vjoyTexture->tsp.full = 0; + vjoyTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); + vjoyTexture->SetDevice(GetContext()->GetDevice()); + vjoyTexture->SetCommandBuffer(texCommandPool.Allocate()); + vjoyTexture->UploadToGPU(OSD_TEX_W, OSD_TEX_H, image_data, false); + vjoyTexture->SetCommandBuffer(nullptr); + texCommandPool.EndFrame(); + delete [] image_data; + osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass()); + } + if (!osdBuffer) + { + osdBuffer = std::unique_ptr(new BufferData(sizeof(OSDVertex) * VJOY_VISIBLE * 4, + vk::BufferUsageFlagBits::eVertexBuffer)); + } +#endif + + return true; + } + + virtual void Term() override + { + osdBuffer.reset(); + vjoyTexture.reset(); + textureCache.Clear(); + fogTexture = nullptr; + paletteTexture = nullptr; + texCommandPool.Term(); + framebufferTextures.clear(); + } + + virtual u64 GetTexture(TSP tsp, TCW tcw) override + { + Texture* tf = textureCache.getTextureCacheData(tsp, tcw); + + if (tf->IsNew()) + { + tf->Create(); + tf->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); + tf->SetDevice(GetContext()->GetDevice()); + } + + //update if needed + if (tf->NeedsUpdate()) + { + // This kills performance when a frame is skipped and lots of texture updated each frame + //if (textureCache.IsInFlight(tf)) + // textureCache.DestroyLater(tf); + tf->SetCommandBuffer(texCommandPool.Allocate()); + tf->Update(); + } + else if (tf->IsCustomTextureAvailable()) + { + textureCache.DestroyLater(tf); + tf->SetCommandBuffer(texCommandPool.Allocate()); + tf->CheckCustomTexture(); + } + tf->SetCommandBuffer(nullptr); + textureCache.SetInFlight(tf); + + return tf->GetIntId(); + } + + virtual bool Process(TA_context* ctx) override + { + texCommandPool.BeginFrame(); + textureCache.SetCurrentIndex(texCommandPool.GetIndex()); + + if (ctx->rend.isRenderFramebuffer) + return RenderFramebuffer(); + + ctx->rend_inuse.lock(); + + if (KillTex) + textureCache.Clear(); + + bool result = ta_parse_vdrc(ctx); + + textureCache.CollectCleanup(); + + if (result) + { + CheckFogTexture(); + CheckPaletteTexture(); + } + else + texCommandPool.EndFrame(); + + return result; + } + + void Present() override + { + GetContext()->Present(); + } + + void Resize(int w, int h) override + { + texCommandPool.Init(); +#ifdef __ANDROID__ + osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass()); +#endif + } + + void DrawOSD(bool clear_screen) override + { + gui_display_osd(); + if (!vjoyTexture) + return; + if (clear_screen) + { + GetContext()->NewFrame(); + GetContext()->BeginRenderPass(); + } + const float dc2s_scale_h = screen_height / 480.0f; + const float sidebarWidth = (screen_width - dc2s_scale_h * 640.0f) / 2; + + std::vector osdVertices = GetOSDVertices(); + const float x1 = 2.0f / (screen_width / dc2s_scale_h); + const float y1 = 2.0f / 480; + const float x2 = 1 - 2 * sidebarWidth / screen_width; + const float y2 = 1; + for (OSDVertex& vtx : osdVertices) + { + vtx.x = vtx.x * x1 - x2; + vtx.y = vtx.y * y1 - y2; + } + + const vk::CommandBuffer cmdBuffer = GetContext()->GetCurrentCommandBuffer(); + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, osdPipeline.GetPipeline()); + + osdPipeline.BindDescriptorSets(cmdBuffer); + const vk::Viewport viewport(0, 0, (float)screen_width, (float)screen_height, 0, 1.f); + cmdBuffer.setViewport(0, 1, &viewport); + const vk::Rect2D scissor({ 0, 0 }, { (u32)screen_width, (u32)screen_height }); + cmdBuffer.setScissor(0, 1, &scissor); + osdBuffer->upload(osdVertices.size() * sizeof(OSDVertex), osdVertices.data()); + const vk::DeviceSize zero = 0; + cmdBuffer.bindVertexBuffers(0, 1, &osdBuffer->buffer.get(), &zero); + for (size_t i = 0; i < osdVertices.size(); i += 4) + cmdBuffer.draw(4, 1, i, 0); + if (clear_screen) + GetContext()->EndFrame(); + } + +protected: + VulkanContext *GetContext() const { return VulkanContext::Instance(); } + + bool RenderFramebuffer() + { + if (FB_R_SIZE.fb_x_size == 0 || FB_R_SIZE.fb_y_size == 0) + return false; + + PixelBuffer pb; + int width; + int height; + ReadFramebuffer(pb, width, height); + + if (framebufferTextures.size() != GetContext()->GetSwapChainSize()) + framebufferTextures.resize(GetContext()->GetSwapChainSize()); + std::unique_ptr& curTexture = framebufferTextures[GetContext()->GetCurrentImageIndex()]; + if (!curTexture) + { + curTexture = std::unique_ptr(new Texture()); + curTexture->tex_type = TextureType::_8888; + curTexture->tcw.full = 0; + curTexture->tsp.full = 0; + curTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); + curTexture->SetDevice(GetContext()->GetDevice()); + } + curTexture->SetCommandBuffer(texCommandPool.Allocate()); + curTexture->UploadToGPU(width, height, (u8*)pb.data(), false); + curTexture->SetCommandBuffer(nullptr); + texCommandPool.EndFrame(); + + GetContext()->PresentFrame(curTexture->GetImageView(), { 640, 480 }); + + return true; + } + + void CheckFogTexture() + { + if (!fogTexture) + { + fogTexture = std::unique_ptr(new Texture()); + fogTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); + fogTexture->SetDevice(GetContext()->GetDevice()); + fogTexture->tex_type = TextureType::_8; + fog_needs_update = true; + } + if (!fog_needs_update || !settings.rend.Fog) + return; + fog_needs_update = false; + u8 texData[256]; + MakeFogTexture(texData); + fogTexture->SetCommandBuffer(texCommandPool.Allocate()); + + fogTexture->UploadToGPU(128, 2, texData, false); + + fogTexture->SetCommandBuffer(nullptr); + } + + void CheckPaletteTexture() + { + if (!paletteTexture) + { + paletteTexture = std::unique_ptr(new Texture()); + paletteTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); + paletteTexture->SetDevice(GetContext()->GetDevice()); + paletteTexture->tex_type = TextureType::_8888; + palette_updated = true; + } + if (!palette_updated) + return; + palette_updated = false; + + paletteTexture->SetCommandBuffer(texCommandPool.Allocate()); + + paletteTexture->UploadToGPU(1024, 1, (u8 *)palette32_ram, false); + + paletteTexture->SetCommandBuffer(nullptr); + } + + ShaderManager shaderManager; + std::unique_ptr fogTexture; + std::unique_ptr paletteTexture; + CommandPool texCommandPool; + std::vector> framebufferTextures; + OSDPipeline osdPipeline; + std::unique_ptr vjoyTexture; + std::unique_ptr osdBuffer; + TextureCache textureCache; +}; +