/* 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 "hw/pvr/Renderer_if.h" #include "allocator.h" #include "commandpool.h" #include "drawer.h" #include "shaders.h" #include "rend/gui.h" #include "rend/osd.h" class VulkanRenderer : public Renderer { public: bool Init() override { DEBUG_LOG(RENDERER, "VulkanRenderer::Init"); shaderManager.Init(); texCommandPool.Init(); // FIXME this might be called after initial init texAllocator.SetChunkSize(16 * 1024 * 1024); rttPipelineManager.Init(&shaderManager); if (textureDrawer.size() > GetContext()->GetSwapChainSize()) textureDrawer.resize(GetContext()->GetSwapChainSize()); else { while (textureDrawer.size() < GetContext()->GetSwapChainSize()) textureDrawer.emplace_back(); } for (auto& drawer : textureDrawer) { drawer.Init(&samplerManager, &texAllocator, &rttPipelineManager, &textureCache); drawer.SetCommandPool(&texCommandPool); } screenDrawer.Init(&samplerManager, &shaderManager); quadPipeline.Init(&shaderManager); #ifdef __ANDROID__ if (!vjoyTexture) { int w, h; u8 *image_data = loadPNGData(get_readonly_data_path(DATA_PATH "buttons.png"), w, h); if (image_data == nullptr) { WARN_LOG(RENDERER, "Cannot load buttons.png image"); } else { vjoyTexture = std::unique_ptr(new Texture()); vjoyTexture->tex_type = TextureType::_8888; vjoyTexture->tcw.full = 0; vjoyTexture->tsp.full = 0; vjoyTexture->SetAllocator(&texAllocator); vjoyTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); vjoyTexture->SetDevice(GetContext()->GetDevice()); vjoyTexture->SetCommandBuffer(texCommandPool.Allocate()); vjoyTexture->UploadToGPU(OSD_TEX_W, OSD_TEX_H, image_data); vjoyTexture->SetCommandBuffer(nullptr); delete [] image_data; osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView()); } } if (!osdBuffer) { osdBuffer = std::unique_ptr(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(), sizeof(OSDVertex) * VJOY_VISIBLE * 4, vk::BufferUsageFlagBits::eVertexBuffer, &texAllocator)); } #endif return true; } void Resize(int w, int h) override { texCommandPool.Init(); screenDrawer.Init(&samplerManager, &shaderManager); quadPipeline.Init(&shaderManager); osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView()); } void Term() override { DEBUG_LOG(RENDERER, "VulkanRenderer::Term"); GetContext()->WaitIdle(); textureCache.Clear(); fogTexture = nullptr; texCommandPool.Term(); shaderManager.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->SetAllocator(&texAllocator); curTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); curTexture->SetDevice(GetContext()->GetDevice()); } curTexture->SetCommandBuffer(texCommandPool.Allocate()); curTexture->UploadToGPU(width, height, (u8*)pb.data()); curTexture->SetCommandBuffer(nullptr); texCommandPool.EndFrame(); TransformMatrix matrices(pvrrc); glm::vec4 viewport_min = matrices.GetViewportMatrix() * glm::vec4(0, 0, 0, 1.f); glm::vec4 viewport_dim = matrices.GetViewportMatrix() * glm::vec4(640.f, 480.f, 0, 0); float min_x = viewport_min[0]; float min_y = viewport_min[1]; width = viewport_dim[0]; height = viewport_dim[1]; if (width < 0) { min_x += width; width = -width; } if (height < 0) { min_y += height; height = -height; } vk::CommandBuffer cmdBuffer = screenDrawer.BeginRenderPass(); vk::Pipeline pipeline = quadPipeline.GetPipeline(); cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); quadPipeline.SetTexture(curTexture.get()); quadPipeline.BindDescriptorSets(cmdBuffer); float blendConstants[4] = { 1.0, 1.0, 1.0, 1.0 }; cmdBuffer.setBlendConstants(blendConstants); vk::Viewport viewport(min_x, min_y, width, height); cmdBuffer.setViewport(0, 1, &viewport); cmdBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(min_x, min_y), vk::Extent2D(width, height))); cmdBuffer.draw(3, 1, 0, 0); gui_display_osd(); screenDrawer.EndRenderPass(); return true; } bool Process(TA_context* ctx) override { texCommandPool.BeginFrame(); if (ctx->rend.isRenderFramebuffer) { return RenderFramebuffer(); } ctx->rend_inuse.Lock(); if (KillTex) textureCache.Clear(); bool result = ta_parse_vdrc(ctx); textureCache.CollectCleanup(); if (ctx->rend.Overrun) WARN_LOG(PVR, "ERROR: TA context overrun"); result = result && !ctx->rend.Overrun; if (result) CheckFogTexture(); if (!result || !ctx->rend.isRTT) 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(GetContext()->GetDevice(), osdVertices.size() * sizeof(OSDVertex), osdVertices.data()); const vk::DeviceSize zero = 0; cmdBuffer.bindVertexBuffers(0, 1, &osdBuffer->buffer.get(), &zero); for (int i = 0; i < osdVertices.size(); i += 4) cmdBuffer.draw(4, 1, i, 0); if (clear_screen) GetContext()->EndFrame(); } bool Render() override { if (pvrrc.isRenderFramebuffer) return true; Drawer *drawer; if (pvrrc.isRTT) drawer = &textureDrawer[GetContext()->GetCurrentImageIndex()]; else drawer = &screenDrawer; drawer->Draw(fogTexture.get()); if (!pvrrc.isRTT) DrawOSD(false); drawer->EndRenderPass(); 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->SetAllocator(&texAllocator); tf->SetPhysicalDevice(GetContext()->GetPhysicalDevice()); tf->SetDevice(GetContext()->GetDevice()); } //update if needed if (tf->NeedsUpdate()) { tf->SetCommandBuffer(texCommandPool.Allocate()); tf->Update(); tf->SetCommandBuffer(nullptr); } else tf->CheckCustomTexture(); return tf->GetIntId(); } private: VulkanContext *GetContext() const { return VulkanContext::Instance(); } void CheckFogTexture() { if (!fogTexture) { fogTexture = std::unique_ptr(new Texture()); fogTexture->SetAllocator(&texAllocator); 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); fogTexture->SetCommandBuffer(nullptr); } std::unique_ptr fogTexture; CommandPool texCommandPool; SamplerManager samplerManager; ShaderManager shaderManager; ScreenDrawer screenDrawer; RttPipelineManager rttPipelineManager; std::vector textureDrawer; VulkanAllocator texAllocator; std::vector> framebufferTextures; QuadPipeline quadPipeline; OSDPipeline osdPipeline; std::unique_ptr vjoyTexture; std::unique_ptr osdBuffer; TextureCache textureCache; }; Renderer* rend_Vulkan() { return new VulkanRenderer(); }