flycast/core/rend/vulkan/vulkan_renderer.h

357 lines
9.4 KiB
C++

/*
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:
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());
vk::CommandBuffer cmdBuffer = texCommandPool.Allocate();
cmdBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
vjoyTexture->SetCommandBuffer(cmdBuffer);
vjoyTexture->UploadToGPU(OSD_TEX_W, OSD_TEX_H, image_data, false);
vjoyTexture->SetCommandBuffer(nullptr);
cmdBuffer.end();
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 Term() override
{
GetContext()->PresentFrame(nullptr, vk::Extent2D());
osdBuffer.reset();
vjoyTexture.reset();
textureCache.Clear();
fogTexture = nullptr;
paletteTexture = nullptr;
texCommandPool.Term();
framebufferTextures.clear();
}
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(texCommandBuffer);
tf->Update();
}
else if (tf->IsCustomTextureAvailable())
{
textureCache.DestroyLater(tf);
tf->SetCommandBuffer(texCommandBuffer);
tf->CheckCustomTexture();
}
tf->SetCommandBuffer(nullptr);
textureCache.SetInFlight(tf);
return tf->GetIntId();
}
bool Process(TA_context* ctx) override
{
if (KillTex)
textureCache.Clear();
texCommandPool.BeginFrame();
textureCache.SetCurrentIndex(texCommandPool.GetIndex());
textureCache.Cleanup();
texCommandBuffer = texCommandPool.Allocate();
texCommandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
bool result;
if (ctx->rend.isRenderFramebuffer)
result = RenderFramebuffer(ctx);
else
result = ta_parse_vdrc(ctx);
if (result)
{
CheckFogTexture();
CheckPaletteTexture();
texCommandBuffer.end();
}
else
{
texCommandBuffer.end();
texCommandPool.EndFrame();
}
return result;
}
void Resize(int w, int h) override
{
if ((u32)w == viewport.width && (u32)h == viewport.height)
return;
viewport.width = w;
viewport.height = h;
}
void ReInitOSD()
{
texCommandPool.Init();
#ifdef __ANDROID__
osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass());
#endif
}
void DrawOSD(bool clear_screen) override
{
gui_display_osd();
if (!vjoyTexture)
return;
try {
if (clear_screen)
{
GetContext()->NewFrame();
GetContext()->BeginRenderPass();
GetContext()->PresentLastFrame();
}
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();
} catch (const InvalidVulkanContext& err) {
}
}
protected:
BaseVulkanRenderer() : viewport(640, 480) {}
VulkanContext *GetContext() const { return VulkanContext::Instance(); }
bool RenderFramebuffer(TA_context* ctx)
{
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(texCommandBuffer);
curTexture->UploadToGPU(width, height, (u8*)pb.data(), false);
curTexture->SetCommandBuffer(nullptr);
Vertex *vtx = ctx->rend.verts.Append(4);
vtx[0].x = 0.f;
vtx[0].y = 0.f;
vtx[0].z = 0.1f;
vtx[0].u = 0.f;
vtx[0].v = 0.f;
vtx[1] = vtx[0];
vtx[1].x = 640.f;
vtx[1].u = 1.f;
vtx[2] = vtx[0];
vtx[2].y = 480.f;
vtx[2].v = 1.f;
vtx[3] = vtx[0];
vtx[3].x = 640.f;
vtx[3].y = 480.f;
vtx[3].u = 1.f;
vtx[3].v = 1.f;
u32 *idx = ctx->rend.idx.Append(4);
idx[0] = ctx->rend.verts.used() - 4;
idx[1] = idx[0] + 1;
idx[2] = idx[1] + 1;
idx[3] = idx[2] + 1;
PolyParam *pp = ctx->rend.global_param_op.Append(1);
pp->first = ctx->rend.idx.used() - 4;
pp->count = 4;
pp->isp.full = 0;
pp->isp.DepthMode = 7;
pp->pcw.full = 0;
pp->pcw.Gouraud = 1;
pp->pcw.Texture = 1;
pp->tcw.full = 0;
pp->tcw.TexAddr = 0x1fffff;
pp->tcw1.full = (u32)-1;
pp->tsp.full = 0;
pp->tsp.FilterMode = 1;
pp->tsp.FogCtrl = 2;
pp->tsp.SrcInstr = 1;
pp->tsp1.full = (u32)-1;
pp->texid = (u64)reinterpret_cast<uintptr_t>(curTexture.get());
pp->texid1 = (u64)-1;
pp->tileclip = 0;
RenderPass *pass = ctx->rend.render_passes.Append(1);
pass->autosort = false;
pass->mvo_count = 0;
pass->mvo_tr_count = 0;
pass->op_count = ctx->rend.global_param_op.used();
pass->pt_count = 0;
pass->tr_count = 0;
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 || !config::Fog)
return;
fog_needs_update = false;
u8 texData[256];
MakeFogTexture(texData);
fogTexture->SetCommandBuffer(texCommandBuffer);
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(texCommandBuffer);
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;
vk::Extent2D viewport;
vk::CommandBuffer texCommandBuffer;
};