/* Copyright 2021 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 . */ #pragma once #include "rend/imgui_driver.h" #include "imgui_impl_vulkan.h" #include "vulkan_context.h" #include "texture.h" #include class VulkanDriver final : public ImGuiDriver { public: ~VulkanDriver() { textures.clear(); linearSampler.reset(); ImGui_ImplVulkan_Shutdown(); } void newFrame() override { } void renderDrawData(ImDrawData *drawData) override { VulkanContext *context = getContext(); if (!context->IsValid()) return; try { bool rendering = context->IsRendering(); if (!rendering) { if (context->recreateSwapChainIfNeeded()) return; context->NewFrame(); } vk::CommandBuffer vmuCmdBuffer{}; if (!rendering || newFrameStarted) { vmuCmdBuffer = getContext()->PrepareOverlay(true, false); context->BeginRenderPass(); context->PresentLastFrame(); context->DrawOverlay(settings.display.uiScale, true, false); } if (!justStarted) // Record Imgui Draw Data and draw funcs into command buffer ImGui_ImplVulkan_RenderDrawData(drawData, (VkCommandBuffer)getCommandBuffer()); justStarted = false; if (!rendering || newFrameStarted) context->EndFrame(vmuCmdBuffer); newFrameStarted = false; } catch (const InvalidVulkanContext& err) { } } void present() override { getContext()->Present(); // may destroy this driver } ImTextureID getTexture(const std::string& name) override { auto it = textures.find(name); if (it != textures.end()) return it->second.textureId; else return ImTextureID{}; } ImTextureID updateTexture(const std::string& name, const u8 *data, int width, int height) override { VkTexture vkTex(std::unique_ptr(new Texture())); vkTex.texture->tex_type = TextureType::_8888; vkTex.texture->SetCommandBuffer(getCommandBuffer()); vkTex.texture->UploadToGPU(width, height, data, false); vkTex.texture->SetCommandBuffer(nullptr); if (!linearSampler) { linearSampler = getContext()->GetDevice().createSamplerUnique( vk::SamplerCreateInfo(vk::SamplerCreateFlags(), vk::Filter::eLinear, vk::Filter::eLinear, vk::SamplerMipmapMode::eLinear, vk::SamplerAddressMode::eClampToBorder, vk::SamplerAddressMode::eClampToBorder, vk::SamplerAddressMode::eClampToEdge, 0.0f, false, 0.f, false, vk::CompareOp::eNever, 0.0f, VK_LOD_CLAMP_NONE, vk::BorderColor::eFloatTransparentBlack)); } ImTextureID texId = vkTex.textureId = ImGui_ImplVulkan_AddTexture((VkSampler)*linearSampler, (VkImageView)vkTex.texture->GetImageView(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); // TODO update existing texture //auto it = textures.find(name); //if (it != textures.end() && it->second.texture != nullptr) // textureCache.DestroyLater(it->second.texture.get()); textures[name] = std::move(vkTex); return texId; } private: struct VkTexture { VkTexture() = default; VkTexture(std::unique_ptr&& texture, ImTextureID textureId = ImTextureID()) : texture(std::move(texture)), textureId(textureId) {} std::unique_ptr texture; ImTextureID textureId{}; }; VulkanContext *getContext() { return VulkanContext::Instance(); } vk::CommandBuffer getCommandBuffer() { VulkanContext *context = getContext(); if (!context->IsRendering()) { if (context->recreateSwapChainIfNeeded()) throw InvalidVulkanContext(); context->NewFrame(); newFrameStarted = true; } return context->GetCurrentCommandBuffer(); } std::unordered_map textures; vk::UniqueSampler linearSampler; bool newFrameStarted = false; bool justStarted = true; };