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
This commit is contained in:
Flyinghead 2020-07-10 18:50:53 +02:00
parent a1dd76818e
commit 1713124711
10 changed files with 347 additions and 505 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -19,61 +19,27 @@
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#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 <memory>
#include <vector>
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<Texture>(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<BufferData>(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<u32> pb;
int width;
int height;
ReadFramebuffer(pb, width, height);
if (framebufferTextures.size() != GetContext()->GetSwapChainSize())
framebufferTextures.resize(GetContext()->GetSwapChainSize());
std::unique_ptr<Texture>& curTexture = framebufferTextures[GetContext()->GetCurrentImageIndex()];
if (!curTexture)
{
curTexture = std::unique_ptr<Texture>(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<OSDVertex> 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<Texture>(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<Texture>(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<Texture> fogTexture;
std::unique_ptr<Texture> paletteTexture;
CommandPool texCommandPool;
SamplerManager samplerManager;
OITShaderManager shaderManager;
ShaderManager normalShaderManager;
OITShaderManager oitShaderManager;
OITScreenDrawer screenDrawer;
OITTextureDrawer textureDrawer;
std::vector<std::unique_ptr<Texture>> framebufferTextures;
OSDPipeline osdPipeline;
std::unique_ptr<Texture> vjoyTexture;
std::unique_ptr<BufferData> osdBuffer;
TextureCache textureCache;
};
Renderer* rend_OITVulkan()

View File

@ -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<BufferData>(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();
}

View File

@ -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<Texture>
{
public:
Texture *getTextureCacheData(TSP tsp, TCW tcw)
{
Texture *texture = BaseTextureCache<Texture>::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<Texture *>& 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)

View File

@ -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)

View File

@ -19,23 +19,17 @@
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#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 <memory>
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<Texture>(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<BufferData>(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<u32> pb;
int width;
int height;
ReadFramebuffer(pb, width, height);
if (framebufferTextures.size() != GetContext()->GetSwapChainSize())
framebufferTextures.resize(GetContext()->GetSwapChainSize());
std::unique_ptr<Texture>& curTexture = framebufferTextures[GetContext()->GetCurrentImageIndex()];
if (!curTexture)
{
curTexture = std::unique_ptr<Texture>(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<OSDVertex> 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<Texture>(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<Texture>(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<Texture> fogTexture;
std::unique_ptr<Texture> paletteTexture;
CommandPool texCommandPool;
SamplerManager samplerManager;
ShaderManager shaderManager;
ScreenDrawer screenDrawer;
TextureDrawer textureDrawer;
std::vector<std::unique_ptr<Texture>> framebufferTextures;
OSDPipeline osdPipeline;
std::unique_ptr<Texture> vjoyTexture;
std::unique_ptr<BufferData> osdBuffer;
TextureCache textureCache;
};
Renderer* rend_Vulkan()

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#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 <memory>
#include <vector>
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<Texture>(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<BufferData>(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<OSDVertex> 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<u32> pb;
int width;
int height;
ReadFramebuffer(pb, width, height);
if (framebufferTextures.size() != GetContext()->GetSwapChainSize())
framebufferTextures.resize(GetContext()->GetSwapChainSize());
std::unique_ptr<Texture>& curTexture = framebufferTextures[GetContext()->GetCurrentImageIndex()];
if (!curTexture)
{
curTexture = std::unique_ptr<Texture>(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<Texture>(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<Texture>(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<Texture> fogTexture;
std::unique_ptr<Texture> paletteTexture;
CommandPool texCommandPool;
std::vector<std::unique_ptr<Texture>> framebufferTextures;
OSDPipeline osdPipeline;
std::unique_ptr<Texture> vjoyTexture;
std::unique_ptr<BufferData> osdBuffer;
TextureCache textureCache;
};