diff --git a/core/rend/gui_util.h b/core/rend/gui_util.h
index 5e9c8dfa9..25bf55641 100644
--- a/core/rend/gui_util.h
+++ b/core/rend/gui_util.h
@@ -22,7 +22,7 @@
#include "imgui/imgui.h"
#include "vulkan/imgui_impl_vulkan.h"
#include "gles/imgui_impl_opengl3.h"
-#include "vulkan/vulkan.h"
+#include "vulkan/vulkan_context.h"
typedef void (*StringCallback)(bool cancelled, std::string selection);
diff --git a/core/rend/vulkan/buffer.cpp b/core/rend/vulkan/buffer.cpp
index 71e6b4021..62852b40e 100644
--- a/core/rend/vulkan/buffer.cpp
+++ b/core/rend/vulkan/buffer.cpp
@@ -20,6 +20,7 @@
*/
#include "buffer.h"
#include "utils.h"
+#include "vulkan_context.h"
BufferData::BufferData(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags propertyFlags)
: bufferSize(size)
diff --git a/core/rend/vulkan/buffer.h b/core/rend/vulkan/buffer.h
index 86218453c..00da94347 100644
--- a/core/rend/vulkan/buffer.h
+++ b/core/rend/vulkan/buffer.h
@@ -20,6 +20,7 @@
*/
#pragma once
#include "vulkan.h"
+#include "vmallocator.h"
struct BufferData
{
diff --git a/core/rend/vulkan/commandpool.h b/core/rend/vulkan/commandpool.h
index bd51f293b..42557ea0a 100644
--- a/core/rend/vulkan/commandpool.h
+++ b/core/rend/vulkan/commandpool.h
@@ -19,7 +19,7 @@
along with Flycast. If not, see .
*/
#pragma once
-#include "vulkan.h"
+#include "vulkan_context.h"
class CommandPool
{
@@ -58,10 +58,9 @@ public:
void EndFrame()
{
- vk::CommandBuffer commandBuffer = Allocate();
- commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
- commandBuffer.end();
- VulkanContext::Instance()->GetGraphicsQueue().submit(vk::SubmitInfo(0, nullptr, nullptr, 1, &commandBuffer), *fences[index]);
+ std::vector commandBuffers = GetInFlightCommandBuffers();
+ VulkanContext::Instance()->GetGraphicsQueue().submit(
+ vk::SubmitInfo(0, nullptr, nullptr, commandBuffers.size(), commandBuffers.data()), *fences[index]);
}
void BeginFrame()
@@ -95,7 +94,23 @@ public:
return *inFlightBuffers[index].back();
}
+ vk::Fence GetCurrentFence()
+ {
+ return *fences[index];
+ }
+
private:
+ std::vector GetInFlightCommandBuffers() const
+ {
+ const auto& buffers = inFlightBuffers[index];
+ std::vector v;
+ v.reserve(buffers.size());
+ std::for_each(buffers.begin(), buffers.end(),
+ [&v](const vk::UniqueCommandBuffer& cmdBuf) { v.push_back(*cmdBuf); });
+
+ return v;
+ }
+
int index = 0;
std::vector> freeBuffers;
std::vector> inFlightBuffers;
diff --git a/core/rend/vulkan/compiler.cpp b/core/rend/vulkan/compiler.cpp
index f349a35ee..439b53f0b 100644
--- a/core/rend/vulkan/compiler.cpp
+++ b/core/rend/vulkan/compiler.cpp
@@ -21,6 +21,7 @@
#include
#include "compiler.h"
#include "SPIRV/GlslangToSpv.h"
+#include "vulkan_context.h"
static const TBuiltInResource DefaultTBuiltInResource = {
/* .MaxLights = */ 32,
diff --git a/core/rend/vulkan/drawer.cpp b/core/rend/vulkan/drawer.cpp
index ccc662601..51ab3e62d 100644
--- a/core/rend/vulkan/drawer.cpp
+++ b/core/rend/vulkan/drawer.cpp
@@ -408,7 +408,7 @@ vk::CommandBuffer TextureDrawer::BeginRenderPass()
{
if (!depthAttachment)
depthAttachment = std::unique_ptr(new FramebufferAttachment(context->GetPhysicalDevice(), device));
- depthAttachment->Init(widthPow2, heightPow2, GetContext()->GetDepthFormat());
+ depthAttachment->Init(widthPow2, heightPow2, GetContext()->GetDepthFormat(), vk::ImageUsageFlagBits::eDepthStencilAttachment);
}
vk::ImageView colorImageView;
vk::ImageLayout colorImageCurrentLayout;
@@ -464,7 +464,8 @@ vk::CommandBuffer TextureDrawer::BeginRenderPass()
{
colorAttachment = std::unique_ptr(new FramebufferAttachment(context->GetPhysicalDevice(), device));
}
- colorAttachment->Init(widthPow2, heightPow2, vk::Format::eR8G8B8A8Unorm);
+ colorAttachment->Init(widthPow2, heightPow2, vk::Format::eR8G8B8A8Unorm,
+ vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc);
}
colorImage = colorAttachment->GetImage();
colorImageView = colorAttachment->GetImageView();
@@ -517,16 +518,14 @@ void TextureDrawer::EndRenderPass()
}
currentCommandBuffer.end();
- GetContext()->GetGraphicsQueue().submit(vk::SubmitInfo(0, nullptr, nullptr, 1, ¤tCommandBuffer),
- settings.rend.RenderToTextureBuffer ? *fence : nullptr);
colorImage = nullptr;
currentCommandBuffer = nullptr;
commandPool->EndFrame();
if (settings.rend.RenderToTextureBuffer)
{
- GetContext()->GetDevice().waitForFences(1, &fence.get(), true, UINT64_MAX);
- GetContext()->GetDevice().resetFences(1, &fence.get());
+ vk::Fence fence = commandPool->GetCurrentFence();
+ GetContext()->GetDevice().waitForFences(1, &fence, true, UINT64_MAX);
u16 *dst = (u16 *)&vram[textureAddr];
@@ -546,15 +545,29 @@ void TextureDrawer::EndRenderPass()
vk::CommandBuffer ScreenDrawer::BeginRenderPass()
{
- GetContext()->NewFrame();
- GetContext()->BeginRenderPass();
- vk::CommandBuffer commandBuffer = GetContext()->GetCurrentCommandBuffer();
- commandBuffer.setViewport(0, vk::Viewport(0.0f, 0.0f, (float)screen_width, (float)screen_height, 1.0f, 0.0f));
+ if (currentScreenScaling != settings.rend.ScreenScaling)
+ Init(samplerManager, shaderManager);
+ vk::CommandBuffer commandBuffer = commandPool->Allocate();
+ commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
+
+ const vk::ClearValue clear_colors[] = { vk::ClearColorValue(std::array { 0.f, 0.f, 0.f, 1.f }), vk::ClearDepthStencilValue { 0.f, 0 } };
+ commandBuffer.beginRenderPass(vk::RenderPassBeginInfo(*renderPass, *framebuffer,
+ vk::Rect2D( { 0, 0 }, viewport), 2, clear_colors), vk::SubpassContents::eInline);
+ commandBuffer.setViewport(0, vk::Viewport(0.0f, 0.0f, viewport.width, viewport.height, 1.0f, 0.0f));
matrices.CalcMatrices(&pvrrc);
SetBaseScissor();
-
commandBuffer.setScissor(0, baseScissor);
+ currentCommandBuffer = commandBuffer;
+
return commandBuffer;
}
+
+void ScreenDrawer::EndRenderPass()
+{
+ currentCommandBuffer.endRenderPass();
+ currentCommandBuffer.end();
+ commandPool->EndFrame();
+ GetContext()->PresentFrame(colorAttachment->GetImageView(), vk::Offset2D(viewport.width, viewport.height));
+}
diff --git a/core/rend/vulkan/drawer.h b/core/rend/vulkan/drawer.h
index 9c5bb0841..7d51dc790 100644
--- a/core/rend/vulkan/drawer.h
+++ b/core/rend/vulkan/drawer.h
@@ -105,6 +105,7 @@ public:
Drawer& operator=(Drawer&& other) = default;
virtual vk::CommandBuffer BeginRenderPass() = 0;
virtual void EndRenderPass() = 0;
+ void SetCommandPool(CommandPool *commandPool) { this->commandPool = commandPool; }
protected:
void Init(SamplerManager *samplerManager, PipelineManager *pipelineManager)
@@ -116,6 +117,9 @@ protected:
virtual BufferData *GetMainBuffer(u32 size) = 0;
PipelineManager *pipelineManager = nullptr;
+ vk::CommandBuffer currentCommandBuffer;
+ SamplerManager *samplerManager = nullptr;
+ CommandPool *commandPool = nullptr;
private:
void SortTriangles();
@@ -135,8 +139,6 @@ private:
std::vector> sortedPolys;
std::vector> sortedIndexes;
u32 sortedIndexCount = 0;
-
- SamplerManager *samplerManager = nullptr;
};
class ScreenDrawer : public Drawer
@@ -144,9 +146,64 @@ class ScreenDrawer : public Drawer
public:
void Init(SamplerManager *samplerManager, ShaderManager *shaderManager)
{
+ this->shaderManager = shaderManager;
+ currentScreenScaling = settings.rend.ScreenScaling;
+ viewport = GetContext()->GetViewPort();
+ viewport.width = lroundf(viewport.width * currentScreenScaling / 100.f);
+ viewport.height = lroundf(viewport.height * currentScreenScaling / 100.f);
+ depthAttachment = std::unique_ptr(
+ new FramebufferAttachment(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice()));
+ depthAttachment->Init(viewport.width, viewport.height, GetContext()->GetDepthFormat(), vk::ImageUsageFlagBits::eDepthStencilAttachment);
+ colorAttachment = std::unique_ptr(
+ new FramebufferAttachment(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice()));
+ colorAttachment->Init(viewport.width, viewport.height, GetContext()->GetColorFormat(),
+ vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled);
+
+ if (!renderPass)
+ {
+ vk::AttachmentDescription attachmentDescriptions[] = {
+ // Color attachment
+ vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetColorFormat(), vk::SampleCountFlagBits::e1,
+ vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore,
+ vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
+ vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal),
+ // Depth attachment
+ vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1,
+ vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare,
+ vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare,
+ vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal),
+ };
+ vk::AttachmentReference colorReference(0, vk::ImageLayout::eColorAttachmentOptimal);
+ vk::AttachmentReference depthReference(1, vk::ImageLayout::eDepthStencilAttachmentOptimal);
+
+ vk::SubpassDescription subpasses[] = {
+ vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics,
+ 0, nullptr,
+ 1, &colorReference,
+ nullptr,
+ &depthReference),
+ };
+
+ std::vector dependencies;
+ dependencies.emplace_back(0, VK_SUBPASS_EXTERNAL, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eFragmentShader,
+ vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eShaderRead, vk::DependencyFlagBits::eByRegion);
+
+ renderPass = GetContext()->GetDevice().createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(),
+ ARRAY_SIZE(attachmentDescriptions), attachmentDescriptions,
+ ARRAY_SIZE(subpasses), subpasses,
+ dependencies.size(), dependencies.data()));
+ }
+ vk::ImageView attachments[] = {
+ colorAttachment->GetImageView(),
+ depthAttachment->GetImageView(),
+ };
+ vk::FramebufferCreateInfo createInfo(vk::FramebufferCreateFlags(), *renderPass,
+ ARRAY_SIZE(attachments), attachments, viewport.width, viewport.height, 1);
+ framebuffer = GetContext()->GetDevice().createFramebufferUnique(createInfo);
+
if (!screenPipelineManager)
screenPipelineManager = std::unique_ptr(new PipelineManager());
- screenPipelineManager->Init(shaderManager);
+ screenPipelineManager->Init(shaderManager, *renderPass);
Drawer::Init(samplerManager, screenPipelineManager.get());
if (descriptorSets.size() > GetContext()->GetSwapChainSize())
@@ -158,16 +215,14 @@ public:
descriptorSets.back().Init(samplerManager, screenPipelineManager->GetPipelineLayout(), screenPipelineManager->GetPerFrameDSLayout(), screenPipelineManager->GetPerPolyDSLayout());
}
}
+
ScreenDrawer() = default;
ScreenDrawer(const ScreenDrawer& other) = delete;
ScreenDrawer(ScreenDrawer&& other) = default;
ScreenDrawer& operator=(const ScreenDrawer& other) = delete;
ScreenDrawer& operator=(ScreenDrawer&& other) = default;
virtual vk::CommandBuffer BeginRenderPass() override;
- virtual void EndRenderPass() override
- {
- GetContext()->EndFrame();
- }
+ virtual void EndRenderPass() override;
protected:
virtual DescriptorSets& GetCurrentDescSet() override { return descriptorSets[GetCurrentImage()]; }
@@ -197,6 +252,14 @@ private:
std::vector descriptorSets;
std::vector> mainBuffers;
std::unique_ptr screenPipelineManager;
+
+ vk::UniqueRenderPass renderPass;
+ vk::UniqueFramebuffer framebuffer;
+ std::unique_ptr colorAttachment;
+ std::unique_ptr depthAttachment;
+ vk::Extent2D viewport;
+ int currentScreenScaling = 0;
+ ShaderManager *shaderManager = nullptr;
};
class TextureDrawer : public Drawer
@@ -207,10 +270,8 @@ public:
Drawer::Init(samplerManager, pipelineManager);
descriptorSets.Init(samplerManager, pipelineManager->GetPipelineLayout(), pipelineManager->GetPerFrameDSLayout(), pipelineManager->GetPerPolyDSLayout());
- fence = GetContext()->GetDevice().createFenceUnique(vk::FenceCreateInfo());
this->textureCache = textureCache;
}
- void SetCommandPool(CommandPool *commandPool) { this->commandPool = commandPool; }
TextureDrawer() = default;
TextureDrawer(const TextureDrawer& other) = delete;
@@ -244,14 +305,11 @@ private:
Texture *texture = nullptr;
vk::Image colorImage;
- vk::CommandBuffer currentCommandBuffer;
vk::UniqueFramebuffer framebuffer;
std::unique_ptr colorAttachment;
std::unique_ptr depthAttachment;
- vk::UniqueFence fence;
DescriptorSets descriptorSets;
std::unique_ptr mainBuffer;
- CommandPool *commandPool = nullptr;
TextureCache *textureCache = nullptr;
};
diff --git a/core/rend/vulkan/imgui_impl_vulkan.cpp b/core/rend/vulkan/imgui_impl_vulkan.cpp
index 16657f241..83df6e62f 100644
--- a/core/rend/vulkan/imgui_impl_vulkan.cpp
+++ b/core/rend/vulkan/imgui_impl_vulkan.cpp
@@ -702,6 +702,7 @@ void ImGui_ImplVulkan_InvalidateDeviceObjects()
if (g_DescriptorSetLayout) { vkDestroyDescriptorSetLayout(g_Device, g_DescriptorSetLayout, g_Allocator); g_DescriptorSetLayout = VK_NULL_HANDLE; }
if (g_PipelineLayout) { vkDestroyPipelineLayout(g_Device, g_PipelineLayout, g_Allocator); g_PipelineLayout = VK_NULL_HANDLE; }
if (g_Pipeline) { vkDestroyPipeline(g_Device, g_Pipeline, g_Allocator); g_Pipeline = VK_NULL_HANDLE; }
+ g_DescriptorSet = VK_NULL_HANDLE;
}
bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass, int subpass)
@@ -739,6 +740,15 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend
void ImGui_ImplVulkan_Shutdown()
{
ImGui_ImplVulkan_InvalidateDeviceObjects();
+ g_Instance = VK_NULL_HANDLE;
+ g_PhysicalDevice = VK_NULL_HANDLE;
+ g_Device = VK_NULL_HANDLE;
+ g_Queue = VK_NULL_HANDLE;
+ g_RenderPass = VK_NULL_HANDLE;
+ g_PipelineCache = VK_NULL_HANDLE;
+ g_DescriptorPool = VK_NULL_HANDLE;
+ g_Allocator = nullptr;
+ g_CheckVkResultFn = nullptr;
}
void ImGui_ImplVulkan_NewFrame()
diff --git a/core/rend/vulkan/oit_drawer.cpp b/core/rend/vulkan/oit_drawer.cpp
index d6b46f66a..8f468286f 100644
--- a/core/rend/vulkan/oit_drawer.cpp
+++ b/core/rend/vulkan/oit_drawer.cpp
@@ -283,7 +283,7 @@ bool OITDrawer::Draw(const Texture *fogTexture)
if (!finalPass)
targetFramebuffer = *tempFramebuffers[(pvrrc.render_passes.used() - 1 - render_pass) % 2];
else
- targetFramebuffer = GetCurrentFramebuffer();
+ targetFramebuffer = *framebuffer;
cmdBuffer.beginRenderPass(
vk::RenderPassBeginInfo(pipelineManager->GetRenderPass(initialPass, finalPass),
targetFramebuffer, viewport, clear_colors.size(), clear_colors.data()),
@@ -361,13 +361,15 @@ void OITDrawer::MakeBuffers(int width, int height)
attachment.reset();
attachment = std::unique_ptr(
new FramebufferAttachment(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice()));
- attachment->Init(maxWidth, maxHeight, GetColorFormat(), vk::ImageUsageFlagBits::eInputAttachment);
+ attachment->Init(maxWidth, maxHeight, GetColorFormat(),
+ vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eInputAttachment);
}
depthAttachment.reset();
depthAttachment = std::unique_ptr(
new FramebufferAttachment(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice()));
- depthAttachment->Init(maxWidth, maxHeight, GetContext()->GetDepthFormat(), vk::ImageUsageFlagBits::eInputAttachment);
+ depthAttachment->Init(maxWidth, maxHeight, GetContext()->GetDepthFormat(),
+ vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::eInputAttachment);
vk::ImageView attachments[] = {
colorAttachments[1]->GetImageView(),
@@ -382,23 +384,32 @@ void OITDrawer::MakeBuffers(int width, int height)
tempFramebuffers[1] = GetContext()->GetDevice().createFramebufferUnique(createInfo);
}
-void OITScreenDrawer::MakeFramebuffers(int width, int height)
+void OITScreenDrawer::MakeFramebuffers()
{
- MakeBuffers(width, height);
+ if (currentScreenScaling == settings.rend.ScreenScaling)
+ return;
+ currentScreenScaling = settings.rend.ScreenScaling;
+ viewport.offset.x = 0;
+ viewport.offset.y = 0;
+ viewport.extent = GetContext()->GetViewPort();
+ viewport.extent.width = lroundf(viewport.extent.width * currentScreenScaling / 100.f);
+ viewport.extent.height = lroundf(viewport.extent.height * currentScreenScaling / 100.f);
+
+ MakeBuffers(viewport.extent.width, viewport.extent.height);
+ finalColorAttachment.reset();
+ finalColorAttachment = std::unique_ptr(
+ new FramebufferAttachment(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice()));
+ finalColorAttachment->Init(viewport.extent.width, viewport.extent.height, GetContext()->GetColorFormat(),
+ vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled);
vk::ImageView attachments[] = {
- nullptr, // swap chain image view, set later
+ finalColorAttachment->GetImageView(),
colorAttachments[0]->GetImageView(),
depthAttachment->GetImageView(),
};
- framebuffers.clear();
- framebuffers.reserve(GetContext()->GetSwapChainSize());
- for (int i = 0; i < GetContext()->GetSwapChainSize(); i++)
- {
- vk::FramebufferCreateInfo createInfo(vk::FramebufferCreateFlags(), pipelineManager->GetRenderPass(true, true),
- ARRAY_SIZE(attachments), attachments, width, height, 1);
- attachments[0] = GetContext()->GetSwapChainImageView(i);
- framebuffers.push_back(GetContext()->GetDevice().createFramebufferUnique(createInfo));
- }
+ framebuffer.reset();
+ vk::FramebufferCreateInfo createInfo(vk::FramebufferCreateFlags(), pipelineManager->GetRenderPass(true, true),
+ ARRAY_SIZE(attachments), attachments, viewport.extent.width, viewport.extent.height, 1);
+ framebuffer = GetContext()->GetDevice().createFramebufferUnique(createInfo);
}
vk::CommandBuffer OITTextureDrawer::NewFrame()
@@ -493,7 +504,8 @@ vk::CommandBuffer OITTextureDrawer::NewFrame()
colorAttachment = std::unique_ptr(
new FramebufferAttachment(context->GetPhysicalDevice(), device));
}
- colorAttachment->Init(widthPow2, heightPow2, vk::Format::eR8G8B8A8Unorm);
+ colorAttachment->Init(widthPow2, heightPow2, vk::Format::eR8G8B8A8Unorm,
+ vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc);
}
colorImage = colorAttachment->GetImage();
colorImageView = colorAttachment->GetImageView();
@@ -545,18 +557,14 @@ void OITTextureDrawer::EndFrame()
}
currentCommandBuffer.end();
- GetContext()->GetGraphicsQueue().submit(vk::SubmitInfo(0, nullptr, nullptr, 1, ¤tCommandBuffer),
- settings.rend.RenderToTextureBuffer ? *fence : nullptr);
colorImage = nullptr;
currentCommandBuffer = nullptr;
commandPool->EndFrame();
-
-
if (settings.rend.RenderToTextureBuffer)
{
- GetContext()->GetDevice().waitForFences(1, &fence.get(), true, UINT64_MAX);
- GetContext()->GetDevice().resetFences(1, &fence.get());
+ vk::Fence fence = commandPool->GetCurrentFence();
+ GetContext()->GetDevice().waitForFences(1, &fence, true, UINT64_MAX);
u16 *dst = (u16 *)&vram[textureAddr];
@@ -576,13 +584,9 @@ void OITTextureDrawer::EndFrame()
vk::CommandBuffer OITScreenDrawer::NewFrame()
{
- GetContext()->NewFrame();
- GetContext()->InitImgui(GetRenderPass(), 2);
- vk::CommandBuffer commandBuffer = GetContext()->GetCurrentCommandBuffer();
-
- viewport.offset.x = 0;
- viewport.offset.y = 0;
- viewport.extent = GetContext()->GetViewPort();
+ MakeFramebuffers();
+ vk::CommandBuffer commandBuffer = commandPool->Allocate();
+ commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
matrices.CalcMatrices(&pvrrc);
@@ -590,6 +594,7 @@ vk::CommandBuffer OITScreenDrawer::NewFrame()
commandBuffer.setScissor(0, baseScissor);
commandBuffer.setViewport(0, vk::Viewport(viewport.offset.x, viewport.offset.y, viewport.extent.width, viewport.extent.height, 1.0f, 0.0f));
+ currentCommandBuffer = commandBuffer;
return commandBuffer;
}
diff --git a/core/rend/vulkan/oit_drawer.h b/core/rend/vulkan/oit_drawer.h
index 3536b45fe..96281702c 100644
--- a/core/rend/vulkan/oit_drawer.h
+++ b/core/rend/vulkan/oit_drawer.h
@@ -45,6 +45,7 @@ public:
virtual vk::CommandBuffer NewFrame() = 0;
virtual void EndFrame() = 0;
+ void SetCommandPool(CommandPool *commandPool) { this->commandPool = commandPool; }
protected:
void Init(SamplerManager *samplerManager, OITPipelineManager *pipelineManager, OITBuffers *oitBuffers)
@@ -64,7 +65,6 @@ protected:
}
virtual OITDescriptorSets& GetCurrentDescSet() = 0;
virtual BufferData *GetMainBuffer(u32 size) = 0;
- virtual vk::Framebuffer GetCurrentFramebuffer() const = 0;
void MakeBuffers(int width, int height);
virtual vk::Format GetColorFormat() const = 0;
@@ -72,6 +72,9 @@ protected:
vk::Rect2D viewport;
std::array, 2> colorAttachments;
std::unique_ptr depthAttachment;
+ CommandPool *commandPool = nullptr;
+ vk::CommandBuffer currentCommandBuffer;
+ vk::UniqueFramebuffer framebuffer;
private:
void DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sortTriangles, int pass,
@@ -123,14 +126,13 @@ public:
screenPipelineManager->GetPerPolyDSLayout(),
screenPipelineManager->GetColorInputDSLayout());
}
- vk::Extent2D viewport = GetContext()->GetViewPort();
- MakeFramebuffers(viewport.width, viewport.height);
+ MakeFramebuffers();
}
void Term()
{
mainBuffers.clear();
screenPipelineManager.reset();
- framebuffers.clear();
+ framebuffer.reset();
descriptorSets.clear();
OITDrawer::Term();
}
@@ -143,9 +145,13 @@ public:
virtual vk::CommandBuffer NewFrame() override;
virtual void EndFrame() override
{
- GetContext()->EndFrame();
+ currentCommandBuffer.endRenderPass();
+ currentCommandBuffer.end();
+ currentCommandBuffer = nullptr;
+ commandPool->EndFrame();
+ GetContext()->PresentFrame(finalColorAttachment->GetImageView(),
+ vk::Offset2D(viewport.extent.width, viewport.extent.height));
}
- vk::RenderPass GetRenderPass() { return screenPipelineManager->GetRenderPass(true, true); }
protected:
virtual OITDescriptorSets& GetCurrentDescSet() override { return descriptorSets[GetCurrentImage()]; }
@@ -170,17 +176,17 @@ protected:
}
return mainBuffers[GetCurrentImage()].get();
};
- virtual vk::Framebuffer GetCurrentFramebuffer() const override { return *framebuffers[GetCurrentImage()]; }
virtual vk::Format GetColorFormat() const override { return GetContext()->GetColorFormat(); }
private:
int GetCurrentImage() const { return GetContext()->GetCurrentImageIndex(); }
- void MakeFramebuffers(int width, int height);
+ void MakeFramebuffers();
- std::vector framebuffers;
+ std::unique_ptr finalColorAttachment;
std::vector descriptorSets;
std::vector> mainBuffers;
std::unique_ptr screenPipelineManager;
+ int currentScreenScaling = 0;
};
class OITTextureDrawer : public OITDrawer
@@ -196,7 +202,6 @@ public:
pipelineManager->GetPerFrameDSLayout(),
pipelineManager->GetPerPolyDSLayout(),
pipelineManager->GetColorInputDSLayout());
- fence = GetContext()->GetDevice().createFenceUnique(vk::FenceCreateInfo());
this->textureCache = textureCache;
}
void Term()
@@ -204,12 +209,9 @@ public:
mainBuffer.reset();
colorAttachment.reset();
framebuffer.reset();
- fence.reset();
OITDrawer::Term();
}
- void SetCommandPool(CommandPool *commandPool) { this->commandPool = commandPool; }
-
OITTextureDrawer() = default;
OITTextureDrawer(const OITTextureDrawer& other) = delete;
OITTextureDrawer(OITTextureDrawer&& other) = default;
@@ -235,7 +237,6 @@ protected:
}
return mainBuffer.get();
}
- virtual vk::Framebuffer GetCurrentFramebuffer() const override { return *framebuffer; }
virtual vk::Format GetColorFormat() const override { return vk::Format::eR8G8B8A8Unorm; }
private:
@@ -243,13 +244,9 @@ private:
Texture *texture = nullptr;
vk::Image colorImage;
- vk::CommandBuffer currentCommandBuffer;
- vk::UniqueFramebuffer framebuffer;
std::unique_ptr colorAttachment;
- vk::UniqueFence fence;
OITDescriptorSets descriptorSets;
std::unique_ptr mainBuffer;
- CommandPool *commandPool = nullptr;
TextureCache *textureCache = nullptr;
};
diff --git a/core/rend/vulkan/oit_renderer.cpp b/core/rend/vulkan/oit_renderer.cpp
index 0771abd64..57efb19c6 100644
--- a/core/rend/vulkan/oit_renderer.cpp
+++ b/core/rend/vulkan/oit_renderer.cpp
@@ -44,7 +44,7 @@ public:
textureDrawer.SetCommandPool(&texCommandPool);
screenDrawer.Init(&samplerManager, &shaderManager, &oitBuffers);
- quadPipeline.Init(&normalShaderManager);
+ screenDrawer.SetCommandPool(&texCommandPool);
quadBuffer = std::unique_ptr(new QuadBuffer());
#ifdef __ANDROID__
@@ -67,8 +67,9 @@ public:
vjoyTexture->SetCommandBuffer(texCommandPool.Allocate());
vjoyTexture->UploadToGPU(OSD_TEX_W, OSD_TEX_H, image_data);
vjoyTexture->SetCommandBuffer(nullptr);
+ texCommandPool.EndFrame();
delete [] image_data;
- osdPipeline.Init(&normalShaderManager, vjoyTexture->GetImageView(), screenDrawer.GetRenderPass(), 2);
+ osdPipeline.Init(&normalShaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass());
}
}
if (!osdBuffer)
@@ -86,9 +87,8 @@ public:
NOTICE_LOG(RENDERER, "OIT Resize %d x %d", w, h);
texCommandPool.Init();
screenDrawer.Init(&samplerManager, &shaderManager, &oitBuffers);
- quadPipeline.Init(&normalShaderManager);
#ifdef __ANDROID__
- osdPipeline.Init(&normalShaderManager, vjoyTexture->GetImageView(), screenDrawer.GetRenderPass(), 2);
+ osdPipeline.Init(&normalShaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass());
#endif
}
@@ -134,50 +134,7 @@ public:
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;
- }
- quadBuffer->Update();
-
- GetContext()->NewFrame();
- GetContext()->BeginRenderPass();
- vk::CommandBuffer cmdBuffer = GetContext()->GetCurrentCommandBuffer();
-
- 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((u32)std::max(lroundf(min_x), 0L), (u32)std::max(lroundf(min_y), 0L)),
- vk::Extent2D((u32)std::max(lroundf(width), 0L), (u32)std::max(lroundf(height), 0L))));
- quadBuffer->Bind(cmdBuffer);
- quadBuffer->Draw(cmdBuffer);
-
- gui_display_osd();
-
- GetContext()->EndFrame();
+ GetContext()->PresentFrame(curTexture->GetImageView(), { 640, 480 });
return true;
}
@@ -208,7 +165,7 @@ public:
if (result)
CheckFogTexture();
- if (!result || !ctx->rend.isRTT)
+ if (!result)
texCommandPool.EndFrame();
return result;
@@ -267,8 +224,7 @@ public:
drawer = &screenDrawer;
drawer->Draw(fogTexture.get());
- if (!pvrrc.isRTT)
- DrawOSD(false);
+
drawer->EndFrame();
return !pvrrc.isRTT;
@@ -340,7 +296,6 @@ private:
RttOITPipelineManager rttPipelineManager;
OITTextureDrawer textureDrawer;
std::vector> framebufferTextures;
- QuadPipeline quadPipeline;
OSDPipeline osdPipeline;
std::unique_ptr vjoyTexture;
std::unique_ptr osdBuffer;
diff --git a/core/rend/vulkan/oit_renderpass.cpp b/core/rend/vulkan/oit_renderpass.cpp
index 065f994d9..1941ec86d 100644
--- a/core/rend/vulkan/oit_renderpass.cpp
+++ b/core/rend/vulkan/oit_renderpass.cpp
@@ -27,13 +27,15 @@ vk::UniqueRenderPass RenderPasses::MakeRenderPass(bool initial, bool last)
GetAttachment0Description(initial, last),
// OP+PT color attachment
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetColorFormat(), vk::SampleCountFlagBits::e1,
- initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp::eStore,
+ initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
+ last ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore,
vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
initial ? vk::ImageLayout::eUndefined : vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eShaderReadOnlyOptimal),
// OP+PT depth attachment
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1,
- initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp::eStore,
- vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore,
+ initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
+ last ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore,
+ vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare,
initial ? vk::ImageLayout::eUndefined : vk::ImageLayout::eDepthStencilReadOnlyOptimal, vk::ImageLayout::eDepthStencilReadOnlyOptimal),
};
vk::AttachmentReference swapChainReference(0, vk::ImageLayout::eColorAttachmentOptimal);
diff --git a/core/rend/vulkan/oit_renderpass.h b/core/rend/vulkan/oit_renderpass.h
index 740c00e00..10b617d1d 100644
--- a/core/rend/vulkan/oit_renderpass.h
+++ b/core/rend/vulkan/oit_renderpass.h
@@ -19,7 +19,7 @@
along with Flycast. If not, see .
*/
#pragma once
-#include "vulkan.h"
+#include "vulkan_context.h"
class RenderPasses
{
@@ -45,7 +45,7 @@ protected:
{
return vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetColorFormat(), vk::SampleCountFlagBits::e1,
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
- vk::ImageLayout::eUndefined, last ? vk::ImageLayout::ePresentSrcKHR : vk::ImageLayout::eShaderReadOnlyOptimal);
+ vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal);
}
virtual vk::Format GetColorFormat() const { return GetContext()->GetColorFormat(); }
virtual std::vector GetSubpassDependencies() const
diff --git a/core/rend/vulkan/pipeline.cpp b/core/rend/vulkan/pipeline.cpp
index 43ee7c178..32a1f7ecd 100644
--- a/core/rend/vulkan/pipeline.cpp
+++ b/core/rend/vulkan/pipeline.cpp
@@ -325,75 +325,6 @@ void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const Pol
graphicsPipelineCreateInfo);
}
-void QuadPipeline::CreatePipeline()
-{
- vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetQuadInputStateCreateInfo(true);
-
- // Input assembly state
- vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(), vk::PrimitiveTopology::eTriangleStrip);
-
- // Viewport and scissor states
- vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr);
-
- // Rasterization and multisample states
- vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo;
- pipelineRasterizationStateCreateInfo.lineWidth = 1.0;
- vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
-
- // Depth and stencil
- vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo;
-
- // Color flags and blending
- vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState(
- true, // blendEnable
- vk::BlendFactor::eConstantAlpha, // srcColorBlendFactor
- vk::BlendFactor::eOneMinusConstantAlpha, // dstColorBlendFactor
- vk::BlendOp::eAdd, // colorBlendOp
- vk::BlendFactor::eConstantAlpha, // srcAlphaBlendFactor
- vk::BlendFactor::eOneMinusConstantAlpha, // dstAlphaBlendFactor
- vk::BlendOp::eAdd, // alphaBlendOp
- vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
- | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
- );
- vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
- (
- vk::PipelineColorBlendStateCreateFlags(), // flags
- false, // logicOpEnable
- vk::LogicOp::eNoOp, // logicOp
- 1, // attachmentCount
- &pipelineColorBlendAttachmentState, // pAttachments
- { { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants
- );
-
- vk::DynamicState dynamicStates[] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor, vk::DynamicState::eBlendConstants };
- vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), ARRAY_SIZE(dynamicStates),
- dynamicStates);
-
- vk::PipelineShaderStageCreateInfo stages[] = {
- { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, shaderManager->GetQuadVertexShader(), "main" },
- { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, shaderManager->GetQuadFragmentShader(), "main" },
- };
- vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
- (
- vk::PipelineCreateFlags(), // flags
- 2, // stageCount
- stages, // pStages
- &pipelineVertexInputStateCreateInfo, // pVertexInputState
- &pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState
- nullptr, // pTessellationState
- &pipelineViewportStateCreateInfo, // pViewportState
- &pipelineRasterizationStateCreateInfo, // pRasterizationState
- &pipelineMultisampleStateCreateInfo, // pMultisampleState
- &pipelineDepthStencilStateCreateInfo, // pDepthStencilState
- &pipelineColorBlendStateCreateInfo, // pColorBlendState
- &pipelineDynamicStateCreateInfo, // pDynamicState
- *pipelineLayout, // layout
- renderPass // renderPass
- );
-
- pipeline = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo);
-}
-
void OSDPipeline::CreatePipeline()
{
// Vertex input state
@@ -474,7 +405,7 @@ void OSDPipeline::CreatePipeline()
&pipelineDynamicStateCreateInfo, // pDynamicState
*pipelineLayout, // layout
renderPass, // renderPass
- subpass
+ 0 // subpass
);
pipeline = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo);
diff --git a/core/rend/vulkan/pipeline.h b/core/rend/vulkan/pipeline.h
index ac842f2ca..a71ab7966 100644
--- a/core/rend/vulkan/pipeline.h
+++ b/core/rend/vulkan/pipeline.h
@@ -24,6 +24,7 @@
#include "texture.h"
#include "utils.h"
#include "hw/pvr/ta_ctx.h"
+#include "vulkan_context.h"
class DescriptorSets
{
@@ -126,7 +127,7 @@ class PipelineManager
public:
virtual ~PipelineManager() = default;
- virtual void Init(ShaderManager *shaderManager)
+ void Init(ShaderManager *shaderManager, vk::RenderPass renderPass)
{
this->shaderManager = shaderManager;
@@ -151,9 +152,9 @@ public:
vk::PipelineLayoutCreateInfo(vk::PipelineLayoutCreateFlags(), ARRAY_SIZE(layouts), layouts, 1, &pushConstant));
}
- if (renderPass != VulkanContext::Instance()->GetRenderPass())
+ if (this->renderPass != renderPass)
{
- renderPass = VulkanContext::Instance()->GetRenderPass();
+ this->renderPass = renderPass;
pipelines.clear();
modVolPipelines.clear();
}
@@ -244,10 +245,8 @@ protected:
class RttPipelineManager : public PipelineManager
{
public:
- void Init(ShaderManager *shaderManager) override
+ void Init(ShaderManager *shaderManager)
{
- PipelineManager::Init(shaderManager);
-
// RTT render pass
renderToTextureBuffer = settings.rend.RenderToTextureBuffer;
vk::AttachmentDescription attachmentDescriptions[] = {
@@ -276,8 +275,10 @@ public:
rttRenderPass = GetContext()->GetDevice().createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(), 2, attachmentDescriptions,
1, &subpass, renderToTextureBuffer ? ARRAY_SIZE(vramWriteDeps) : ARRAY_SIZE(dependencies), renderToTextureBuffer ? vramWriteDeps : dependencies));
- renderPass = *rttRenderPass;
+
+ PipelineManager::Init(shaderManager, *rttRenderPass);
}
+
void CheckSettingsChange()
{
if (renderToTextureBuffer != settings.rend.RenderToTextureBuffer)
@@ -289,82 +290,10 @@ private:
bool renderToTextureBuffer;
};
-class QuadPipeline
-{
-public:
- void Init(ShaderManager *shaderManager)
- {
- this->shaderManager = shaderManager;
- if (!pipelineLayout)
- {
- vk::DescriptorSetLayoutBinding bindings[] = {
- { 0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment },// texture
- };
- descSetLayout = GetContext()->GetDevice().createDescriptorSetLayoutUnique(
- vk::DescriptorSetLayoutCreateInfo(vk::DescriptorSetLayoutCreateFlags(), ARRAY_SIZE(bindings), bindings));
- pipelineLayout = GetContext()->GetDevice().createPipelineLayoutUnique(
- vk::PipelineLayoutCreateInfo(vk::PipelineLayoutCreateFlags(), 1, &descSetLayout.get()));
- }
- if (!sampler)
- {
- sampler = VulkanContext::Instance()->GetDevice().createSamplerUnique(
- vk::SamplerCreateInfo(vk::SamplerCreateFlags(), vk::Filter::eLinear, vk::Filter::eLinear,
- vk::SamplerMipmapMode::eLinear, vk::SamplerAddressMode::eClampToEdge, vk::SamplerAddressMode::eClampToEdge,
- vk::SamplerAddressMode::eClampToEdge, 0.0f, false, 16.0f, false,
- vk::CompareOp::eNever, 0.0f, 0.0f, vk::BorderColor::eFloatOpaqueBlack));
- }
- if (GetContext()->GetRenderPass() != renderPass)
- {
- renderPass = GetContext()->GetRenderPass();
- pipeline.reset();
- }
- descriptorSets.resize(GetContext()->GetSwapChainSize());
- }
-
- vk::Pipeline GetPipeline()
- {
- if (!pipeline)
- CreatePipeline();
- return *pipeline;
- }
-
- void SetTexture(Texture *texture)
- {
- vk::UniqueDescriptorSet& descriptorSet = descriptorSets[GetContext()->GetCurrentImageIndex()];
- if (!descriptorSet)
- {
- descriptorSet = std::move(GetContext()->GetDevice().allocateDescriptorSetsUnique(
- vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), 1, &descSetLayout.get())).front());
- }
- vk::DescriptorImageInfo imageInfo(*sampler, texture->GetImageView(), vk::ImageLayout::eShaderReadOnlyOptimal);
- std::vector writeDescriptorSets;
- writeDescriptorSets.push_back(vk::WriteDescriptorSet(*descriptorSet, 0, 0, 1, vk::DescriptorType::eCombinedImageSampler, &imageInfo, nullptr, nullptr));
-
- GetContext()->GetDevice().updateDescriptorSets(writeDescriptorSets, nullptr);
- }
-
- void BindDescriptorSets(vk::CommandBuffer cmdBuffer)
- {
- cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, *pipelineLayout, 0, 1, &descriptorSets[GetContext()->GetCurrentImageIndex()].get(), 0, nullptr);
- }
-
-private:
- VulkanContext *GetContext() const { return VulkanContext::Instance(); }
- void CreatePipeline();
-
- vk::RenderPass renderPass;
- vk::UniquePipeline pipeline;
- vk::UniqueSampler sampler;
- std::vector descriptorSets;
- vk::UniquePipelineLayout pipelineLayout;
- vk::UniqueDescriptorSetLayout descSetLayout;
- ShaderManager *shaderManager;
-};
-
class OSDPipeline
{
public:
- void Init(ShaderManager *shaderManager, vk::ImageView imageView, vk::RenderPass renderPass, int subpass = 0)
+ void Init(ShaderManager *shaderManager, vk::ImageView imageView, vk::RenderPass renderPass)
{
this->shaderManager = shaderManager;
if (!pipelineLayout)
@@ -379,16 +308,15 @@ public:
}
if (!sampler)
{
- sampler = VulkanContext::Instance()->GetDevice().createSamplerUnique(
+ sampler = GetContext()->GetDevice().createSamplerUnique(
vk::SamplerCreateInfo(vk::SamplerCreateFlags(), vk::Filter::eLinear, vk::Filter::eLinear,
vk::SamplerMipmapMode::eLinear, vk::SamplerAddressMode::eClampToEdge, vk::SamplerAddressMode::eClampToEdge,
vk::SamplerAddressMode::eClampToEdge, 0.0f, false, 16.0f, false,
vk::CompareOp::eNever, 0.0f, 0.0f, vk::BorderColor::eFloatOpaqueBlack));
}
- if (this->renderPass != renderPass || this->subpass != subpass)
+ if (this->renderPass != renderPass)
{
this->renderPass = renderPass;
- this->subpass = subpass;
pipeline.reset();
}
if (!descriptorSet)
@@ -419,7 +347,6 @@ private:
void CreatePipeline();
vk::RenderPass renderPass;
- int subpass = 0;
vk::UniquePipeline pipeline;
vk::UniqueSampler sampler;
vk::UniqueDescriptorSet descriptorSet;
diff --git a/core/rend/vulkan/quad.cpp b/core/rend/vulkan/quad.cpp
index 1cc0faa7b..20b70abe7 100644
--- a/core/rend/vulkan/quad.cpp
+++ b/core/rend/vulkan/quad.cpp
@@ -19,6 +19,12 @@
along with Flycast. If not, see .
*/
#include "quad.h"
+#include "vulkan_context.h"
+
+static VulkanContext *GetContext()
+{
+ return VulkanContext::Instance();
+}
vk::PipelineVertexInputStateCreateInfo GetQuadInputStateCreateInfo(bool uv)
{
@@ -39,3 +45,134 @@ vk::PipelineVertexInputStateCreateInfo GetQuadInputStateCreateInfo(bool uv)
ARRAY_SIZE(vertexInputAttributeDescriptions) - (uv ? 0 : 1),
vertexInputAttributeDescriptions);
}
+
+void QuadPipeline::CreatePipeline()
+{
+ vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetQuadInputStateCreateInfo(true);
+
+ // Input assembly state
+ vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(), vk::PrimitiveTopology::eTriangleStrip);
+
+ // Viewport and scissor states
+ vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr);
+
+ // Rasterization and multisample states
+ vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo;
+ pipelineRasterizationStateCreateInfo.lineWidth = 1.0;
+ vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
+
+ // Depth and stencil
+ vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo;
+
+ // Color flags and blending
+ vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState(
+ true, // blendEnable
+ vk::BlendFactor::eConstantAlpha, // srcColorBlendFactor
+ vk::BlendFactor::eOneMinusConstantAlpha, // dstColorBlendFactor
+ vk::BlendOp::eAdd, // colorBlendOp
+ vk::BlendFactor::eConstantAlpha, // srcAlphaBlendFactor
+ vk::BlendFactor::eOneMinusConstantAlpha, // dstAlphaBlendFactor
+ vk::BlendOp::eAdd, // alphaBlendOp
+ vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
+ | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
+ );
+ vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
+ (
+ vk::PipelineColorBlendStateCreateFlags(), // flags
+ false, // logicOpEnable
+ vk::LogicOp::eNoOp, // logicOp
+ 1, // attachmentCount
+ &pipelineColorBlendAttachmentState, // pAttachments
+ { { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants
+ );
+
+ vk::DynamicState dynamicStates[] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor, vk::DynamicState::eBlendConstants };
+ vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), ARRAY_SIZE(dynamicStates),
+ dynamicStates);
+
+ vk::PipelineShaderStageCreateInfo stages[] = {
+ { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, shaderManager->GetQuadVertexShader(), "main" },
+ { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, shaderManager->GetQuadFragmentShader(), "main" },
+ };
+ vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
+ (
+ vk::PipelineCreateFlags(), // flags
+ 2, // stageCount
+ stages, // pStages
+ &pipelineVertexInputStateCreateInfo, // pVertexInputState
+ &pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState
+ nullptr, // pTessellationState
+ &pipelineViewportStateCreateInfo, // pViewportState
+ &pipelineRasterizationStateCreateInfo, // pRasterizationState
+ &pipelineMultisampleStateCreateInfo, // pMultisampleState
+ &pipelineDepthStencilStateCreateInfo, // pDepthStencilState
+ &pipelineColorBlendStateCreateInfo, // pColorBlendState
+ &pipelineDynamicStateCreateInfo, // pDynamicState
+ *pipelineLayout, // layout
+ VulkanContext::Instance()->GetRenderPass() // renderPass
+ );
+
+ pipeline = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo);
+}
+
+void QuadPipeline::Init(ShaderManager *shaderManager) {
+ this->shaderManager = shaderManager;
+ if (!pipelineLayout) {
+ vk::DescriptorSetLayoutBinding bindings[] = { { 0,
+ vk::DescriptorType::eCombinedImageSampler, 1,
+ vk::ShaderStageFlagBits::eFragment }, // texture
+ };
+ descSetLayout =
+ GetContext()->GetDevice().createDescriptorSetLayoutUnique(
+ vk::DescriptorSetLayoutCreateInfo(
+ vk::DescriptorSetLayoutCreateFlags(),
+ ARRAY_SIZE(bindings), bindings));
+ pipelineLayout = GetContext()->GetDevice().createPipelineLayoutUnique(
+ vk::PipelineLayoutCreateInfo(vk::PipelineLayoutCreateFlags(), 1,
+ &descSetLayout.get()));
+ }
+ if (!sampler) {
+ sampler = GetContext()->GetDevice().createSamplerUnique(
+ vk::SamplerCreateInfo(vk::SamplerCreateFlags(),
+ vk::Filter::eLinear, vk::Filter::eLinear,
+ vk::SamplerMipmapMode::eLinear,
+ vk::SamplerAddressMode::eClampToBorder,
+ vk::SamplerAddressMode::eClampToBorder,
+ vk::SamplerAddressMode::eClampToBorder, 0.0f, false,
+ 16.0f, false, vk::CompareOp::eNever, 0.0f, 0.0f,
+ vk::BorderColor::eFloatOpaqueBlack));
+ }
+ if (GetContext()->GetRenderPass() != renderPass) {
+ renderPass = GetContext()->GetRenderPass();
+ pipeline.reset();
+ }
+ descriptorSets.resize(GetContext()->GetSwapChainSize());
+}
+
+void QuadPipeline::SetTexture(vk::ImageView imageView) {
+ vk::UniqueDescriptorSet &descriptorSet =
+ descriptorSets[GetContext()->GetCurrentImageIndex()];
+ if (!descriptorSet) {
+ descriptorSet = std::move(
+ GetContext()->GetDevice().allocateDescriptorSetsUnique(
+ vk::DescriptorSetAllocateInfo(
+ GetContext()->GetDescriptorPool(), 1,
+ &descSetLayout.get())).front());
+ }
+ vk::DescriptorImageInfo imageInfo(*sampler, imageView,
+ vk::ImageLayout::eShaderReadOnlyOptimal);
+ std::vector writeDescriptorSets;
+ writeDescriptorSets.push_back(
+ vk::WriteDescriptorSet(*descriptorSet, 0, 0, 1,
+ vk::DescriptorType::eCombinedImageSampler, &imageInfo,
+ nullptr, nullptr));
+ GetContext()->GetDevice().updateDescriptorSets(writeDescriptorSets,
+ nullptr);
+}
+
+void QuadPipeline::BindDescriptorSets(vk::CommandBuffer cmdBuffer) {
+ cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
+ *pipelineLayout, 0, 1,
+ &descriptorSets[GetContext()->GetCurrentImageIndex()].get(), 0,
+ nullptr);
+}
diff --git a/core/rend/vulkan/quad.h b/core/rend/vulkan/quad.h
index 10d8c7cf3..4c8b7554c 100644
--- a/core/rend/vulkan/quad.h
+++ b/core/rend/vulkan/quad.h
@@ -21,6 +21,7 @@
#pragma once
#include "vulkan.h"
#include "buffer.h"
+#include "shaders.h"
struct QuadVertex
{
@@ -65,3 +66,31 @@ public:
private:
std::unique_ptr buffer;
};
+
+class QuadPipeline
+{
+public:
+ void Init(ShaderManager *shaderManager);
+
+ vk::Pipeline GetPipeline()
+ {
+ if (!pipeline)
+ CreatePipeline();
+ return *pipeline;
+ }
+
+ void SetTexture(vk::ImageView imageView);
+
+ void BindDescriptorSets(vk::CommandBuffer cmdBuffer);
+
+private:
+ void CreatePipeline();
+
+ vk::RenderPass renderPass;
+ vk::UniquePipeline pipeline;
+ vk::UniqueSampler sampler;
+ std::vector descriptorSets;
+ vk::UniquePipelineLayout pipelineLayout;
+ vk::UniqueDescriptorSetLayout descSetLayout;
+ ShaderManager *shaderManager;
+};
diff --git a/core/rend/vulkan/texture.cpp b/core/rend/vulkan/texture.cpp
index cd173817c..0a9449dc8 100644
--- a/core/rend/vulkan/texture.cpp
+++ b/core/rend/vulkan/texture.cpp
@@ -259,8 +259,6 @@ void Texture::SetImage(u32 srcSize, void *srcData, bool isNew)
setImageLayout(commandBuffer, image.get(), format, mipmapLevels, vk::ImageLayout::ePreinitialized, vk::ImageLayout::eShaderReadOnlyOptimal);
}
commandBuffer.end();
-
- VulkanContext::Instance()->GetGraphicsQueue().submit(vk::SubmitInfo(0, nullptr, nullptr, 1, &commandBuffer), nullptr);
}
void Texture::GenerateMipmaps()
@@ -315,38 +313,19 @@ void Texture::GenerateMipmaps()
commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {}, nullptr, nullptr, barrier);
}
-void FramebufferAttachment::Init(u32 width, u32 height, vk::Format format, vk::ImageUsageFlags additionalUsageFlags)
+void FramebufferAttachment::Init(u32 width, u32 height, vk::Format format, vk::ImageUsageFlags usage)
{
this->format = format;
this->extent = vk::Extent2D { width, height };
bool depth = format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint || format == vk::Format::eD16UnormS8Uint;
- vk::ImageUsageFlags usage;
- if (depth)
+ if (usage & vk::ImageUsageFlagBits::eTransferSrc)
{
- usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
+ stagingBufferData = std::unique_ptr(new BufferData(width * height * 4,
+ vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst));
}
- else
- {
- if (!(additionalUsageFlags & vk::ImageUsageFlagBits::eStorage))
- usage = vk::ImageUsageFlagBits::eColorAttachment;
- if (!(additionalUsageFlags & (vk::ImageUsageFlagBits::eInputAttachment | vk::ImageUsageFlagBits::eStorage)))
- {
- if (settings.rend.RenderToTextureBuffer)
- {
- usage |= vk::ImageUsageFlagBits::eTransferSrc;
- stagingBufferData = std::unique_ptr(new BufferData(width * height * 4,
- vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst));
- }
- else
- {
- usage |= vk::ImageUsageFlagBits::eSampled;
- }
- }
- }
- usage |= additionalUsageFlags;
vk::ImageCreateInfo imageCreateInfo(vk::ImageCreateFlags(), vk::ImageType::e2D, format, vk::Extent3D(extent, 1), 1, 1, vk::SampleCountFlagBits::e1,
- (additionalUsageFlags & vk::ImageUsageFlagBits::eStorage) ? vk::ImageTiling::eLinear : vk::ImageTiling::eOptimal,
+ (usage & vk::ImageUsageFlagBits::eStorage) ? vk::ImageTiling::eLinear : vk::ImageTiling::eOptimal,
usage,
vk::SharingMode::eExclusive, 0, nullptr, vk::ImageLayout::eUndefined);
image = device.createImageUnique(imageCreateInfo);
@@ -358,7 +337,7 @@ void FramebufferAttachment::Init(u32 width, u32 height, vk::Format format, vk::I
format, vk::ComponentMapping(), vk::ImageSubresourceRange(depth ? vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
imageView = device.createImageViewUnique(imageViewCreateInfo);
- if (depth && (additionalUsageFlags & vk::ImageUsageFlagBits::eInputAttachment))
+ if ((usage & vk::ImageUsageFlagBits::eDepthStencilAttachment) && (usage & vk::ImageUsageFlagBits::eInputAttachment))
{
// Also create an imageView for the stencil
imageViewCreateInfo.subresourceRange = vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eStencil, 0, 1, 0, 1);
diff --git a/core/rend/vulkan/texture.h b/core/rend/vulkan/texture.h
index 0941abf19..eb93fd7b2 100644
--- a/core/rend/vulkan/texture.h
+++ b/core/rend/vulkan/texture.h
@@ -20,7 +20,7 @@
*/
#pragma once
#include
-#include "vulkan.h"
+#include "vulkan_context.h"
#include "buffer.h"
#include "rend/TexCache.h"
#include "hw/pvr/Renderer_if.h"
@@ -106,7 +106,7 @@ public:
FramebufferAttachment(vk::PhysicalDevice physicalDevice, vk::Device device)
: physicalDevice(physicalDevice), device(device), format(vk::Format::eUndefined)
{}
- void Init(u32 width, u32 height, vk::Format format, vk::ImageUsageFlags = vk::ImageUsageFlags());
+ void Init(u32 width, u32 height, vk::Format format, vk::ImageUsageFlags usage);
void Reset() { image.reset(); imageView.reset(); }
vk::ImageView GetImageView() const { return *imageView; }
diff --git a/core/rend/vulkan/vmallocator.cpp b/core/rend/vulkan/vmallocator.cpp
index 5f32bf7f5..a21dbd58a 100644
--- a/core/rend/vulkan/vmallocator.cpp
+++ b/core/rend/vulkan/vmallocator.cpp
@@ -20,6 +20,8 @@
*/
#define VMA_IMPLEMENTATION
#include "vulkan.h"
+#include "vmallocator.h"
+#include "vulkan_context.h"
#if HOST_CPU == CPU_ARM
__attribute__((pcs("aapcs-vfp")))
diff --git a/core/rend/vulkan/vulkan.h b/core/rend/vulkan/vulkan.h
index aa2dec7af..76ad7dd8f 100644
--- a/core/rend/vulkan/vulkan.h
+++ b/core/rend/vulkan/vulkan.h
@@ -24,173 +24,5 @@
#include "volk/volk.h"
#undef VK_NO_PROTOTYPES
#include "vulkan/vulkan.hpp"
-#include "rend/TexCache.h"
-#include "vmallocator.h"
-//#define VK_DEBUG
-
-extern int screen_width, screen_height;
-
-class VulkanContext
-{
-public:
- VulkanContext() { verify(contextInstance == nullptr); contextInstance = this; }
- ~VulkanContext() { verify(contextInstance == this); contextInstance = nullptr; }
-
- bool Init();
- bool InitInstance(const char** extensions, uint32_t extensions_count);
- bool InitDevice();
- void InitImgui(vk::RenderPass renderPass, int subpass = 0);
- void CreateSwapChain();
- void Term();
- void SetWindow(void *window, void *display) { this->window = window; this->display = display; }
-
- VkInstance GetInstance() const { return static_cast(instance.get()); }
- u32 GetGraphicsQueueFamilyIndex() const { return graphicsQueueIndex; }
- void SetWindowSize(u32 width, u32 height) { this->width = screen_width = width; this->height = screen_height = height; }
- void NewFrame();
- void BeginRenderPass();
- void EndFrame();
- void Present();
-
- vk::PhysicalDevice GetPhysicalDevice() const { return physicalDevice; }
- vk::Device GetDevice() const { return *device; }
- vk::PipelineCache GetPipelineCache() const { return *pipelineCache; }
- vk::RenderPass GetRenderPass() const { return *renderPass; }
- vk::CommandPool GetCurrentCommandPool() const { return *commandPools[GetCurrentImageIndex()]; }
- vk::CommandBuffer GetCurrentCommandBuffer() const { return *commandBuffers[GetCurrentImageIndex()]; }
- vk::DescriptorPool GetDescriptorPool() const { return *descriptorPool; }
- vk::Extent2D GetViewPort() const { return { width, height }; }
- int GetSwapChainSize() const { return (int)imageViews.size(); }
- int GetCurrentImageIndex() const { return currentImage; }
- void WaitIdle() const { graphicsQueue.waitIdle(); }
- bool IsRendering() const { return rendering; }
- vk::Queue GetGraphicsQueue() const { return graphicsQueue; }
- vk::DeviceSize GetUniformBufferAlignment() const { return uniformBufferAlignment; }
- vk::DeviceSize GetStorageBufferAlignment() const { return storageBufferAlignment; }
- bool IsFormatSupported(TextureType textureType)
- {
- switch (textureType)
- {
- case TextureType::_4444:
- return optimalTilingSupported4444;
- case TextureType::_565:
- return optimalTilingSupported565;
- case TextureType::_5551:
- return optimalTilingSupported1555;
- default:
- return true;
- }
- }
- std::string GetDriverName() const { vk::PhysicalDeviceProperties props; physicalDevice.getProperties(&props); return props.deviceName; }
- std::string GetDriverVersion() const { vk::PhysicalDeviceProperties props; physicalDevice.getProperties(&props); return std::to_string(props.driverVersion); }
- vk::Format GetColorFormat() const { return colorFormat; }
- vk::Format GetDepthFormat() const { return depthFormat; }
- vk::ImageView GetSwapChainImageView(int i) const { return imageViews[i].get(); }
- static VulkanContext *Instance() { return contextInstance; }
- bool SupportsFragmentShaderStoresAndAtomics() const { return fragmentStoresAndAtomics; }
- bool SupportsSamplerAnisotropy() const { return samplerAnisotropy; }
- bool SupportsDedicatedAllocation() const { return dedicatedAllocationSupported; }
- const VMAllocator& GetAllocator() const { return allocator; }
- bool IsUnifiedMemory() const { return unifiedMemory; }
-
-private:
- vk::Format InitDepthBuffer();
- void DoSwapAutomation();
- vk::SurfaceKHR GetSurface() {
-#ifdef USE_SDL
- return surface;
-#else
- return *surface;
-#endif
- }
-
- bool HasSurfaceDimensionChanged()
- {
- vk::SurfaceCapabilitiesKHR surfaceCapabilities = physicalDevice.getSurfaceCapabilitiesKHR(GetSurface());
- VkExtent2D swapchainExtent;
- if (surfaceCapabilities.currentExtent.width == std::numeric_limits::max())
- {
- // If the surface size is undefined, the size is set to the size of the images requested.
- swapchainExtent.width = std::min(std::max(width, surfaceCapabilities.minImageExtent.width), surfaceCapabilities.maxImageExtent.width);
- swapchainExtent.height = std::min(std::max(height, surfaceCapabilities.minImageExtent.height), surfaceCapabilities.maxImageExtent.height);
- }
- else
- {
- // If the surface size is defined, the swap chain size must match
- swapchainExtent = surfaceCapabilities.currentExtent;
- }
- if (width == swapchainExtent.width && height == swapchainExtent.height)
- return false;
-
- screen_width = width = swapchainExtent.width;
- screen_height = height = swapchainExtent.height;
-
- return true;
- }
-
- VMAllocator allocator;
- void *window = nullptr;
- void *display = nullptr;
- bool rendering = false;
- bool renderDone = false;
- u32 width = 0;
- u32 height = 0;
- vk::UniqueInstance instance;
- vk::PhysicalDevice physicalDevice;
-
- u32 graphicsQueueIndex = 0;
- u32 presentQueueIndex = 0;
- vk::DeviceSize uniformBufferAlignment = 0;
- vk::DeviceSize storageBufferAlignment = 0;
- bool optimalTilingSupported565 = false;
- bool optimalTilingSupported1555 = false;
- bool optimalTilingSupported4444 = false;
- bool fragmentStoresAndAtomics = false;
- bool samplerAnisotropy = false;
- bool dedicatedAllocationSupported = false;
- bool unifiedMemory = false;
- vk::UniqueDevice device;
-
-#ifdef USE_SDL
- vk::SurfaceKHR surface;
-#else
- vk::UniqueSurfaceKHR surface;
-#endif
-
- vk::UniqueSwapchainKHR swapChain;
- std::vector imageViews;
- u32 currentImage = 0;
- vk::Format colorFormat = vk::Format::eUndefined;
-
- vk::Queue graphicsQueue;
- vk::Queue presentQueue;
-
- vk::UniqueDescriptorPool descriptorPool;
- vk::UniqueRenderPass renderPass;
-
- vk::UniqueImage depthImage;
- vk::UniqueImageView depthView;
- vk::UniqueDeviceMemory depthMemory;
- vk::Format depthFormat = vk::Format::eUndefined;
-
- std::vector commandPools;
- std::vector commandBuffers;
-
- std::vector framebuffers;
-
- std::vector drawFences;
- std::vector renderCompleteSemaphores;
- std::vector imageAcquiredSemaphores;
- u32 currentSemaphore = 0;
-
- vk::UniquePipelineCache pipelineCache;
-#ifdef VK_DEBUG
-#ifndef __ANDROID__
- vk::UniqueDebugUtilsMessengerEXT debugUtilsMessenger;
-#else
- vk::UniqueDebugReportCallbackEXT debugReportCallback;
-#endif
-#endif
- static VulkanContext *contextInstance;
-};
+#define VK_DEBUG
diff --git a/core/rend/vulkan/vulkan_context.cpp b/core/rend/vulkan/vulkan_context.cpp
index 757e0790a..339b4b80f 100644
--- a/core/rend/vulkan/vulkan_context.cpp
+++ b/core/rend/vulkan/vulkan_context.cpp
@@ -18,7 +18,7 @@
You should have received a copy of the GNU General Public License
along with Flycast. If not, see .
*/
-#include "vulkan.h"
+#include "vulkan_context.h"
#include "imgui/imgui.h"
#include "imgui_impl_vulkan.h"
#include "../gui.h"
@@ -137,15 +137,14 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
vext.push_back(extensions[i]);
std::vector layer_names;
+ //layer_names.push_back("VK_LAYER_ARM_AGA");
#ifdef VK_DEBUG
#ifndef __ANDROID__
vext.push_back("VK_EXT_debug_utils");
- extensions_count += 1;
// layer_names.push_back("VK_LAYER_LUNARG_api_dump");
layer_names.push_back("VK_LAYER_LUNARG_standard_validation");
#else
vext.push_back("VK_EXT_debug_report"); // NDK <= 19?
- extensions_count += 1;
layer_names.push_back("VK_LAYER_GOOGLE_threading");
layer_names.push_back("VK_LAYER_LUNARG_parameter_validation");
layer_names.push_back("VK_LAYER_LUNARG_object_tracker");
@@ -153,8 +152,8 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
layer_names.push_back("VK_LAYER_GOOGLE_unique_objects");
#endif
#endif
- extensions = &vext[0];
- vk::InstanceCreateInfo instanceCreateInfo({}, &applicationInfo, layer_names.size(), &layer_names[0], extensions_count, extensions);
+ vk::InstanceCreateInfo instanceCreateInfo({}, &applicationInfo, layer_names.size(), layer_names.data(),
+ vext.size(), vext.data());
// create a UniqueInstance
instance = vk::createInstanceUnique(instanceCreateInfo);
@@ -217,7 +216,7 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
return false;
}
-vk::Format VulkanContext::InitDepthBuffer()
+vk::Format VulkanContext::FindDepthFormat()
{
const vk::Format depthFormats[] = { vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint, vk::Format::eD16UnormS8Uint };
vk::ImageTiling tiling;
@@ -252,36 +251,10 @@ vk::Format VulkanContext::InitDepthBuffer()
}
NOTICE_LOG(RENDERER, "Using depth format %s tiling %s", vk::to_string(depthFormat).c_str(), vk::to_string(tiling).c_str());
- vk::ImageCreateInfo imageCreateInfo(vk::ImageCreateFlags(), vk::ImageType::e2D, depthFormat, vk::Extent3D(width, height, 1), 1, 1,
- vk::SampleCountFlagBits::e1, tiling, vk::ImageUsageFlagBits::eDepthStencilAttachment);
- depthImage = device->createImageUnique(imageCreateInfo);
-
- vk::PhysicalDeviceMemoryProperties memoryProperties = physicalDevice.getMemoryProperties();
- vk::MemoryRequirements memoryRequirements = device->getImageMemoryRequirements(*depthImage);
- uint32_t typeBits = memoryRequirements.memoryTypeBits;
- uint32_t typeIndex = uint32_t(~0);
- for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++)
- {
- if ((typeBits & 1) && ((memoryProperties.memoryTypes[i].propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal) == vk::MemoryPropertyFlagBits::eDeviceLocal))
- {
- typeIndex = i;
- break;
- }
- typeBits >>= 1;
- }
- verify(typeIndex != ~0);
- depthMemory = device->allocateMemoryUnique(vk::MemoryAllocateInfo(memoryRequirements.size, typeIndex));
-
- device->bindImageMemory(*depthImage, *depthMemory, 0);
-
- vk::ComponentMapping componentMapping(vk::ComponentSwizzle::eR, vk::ComponentSwizzle::eG, vk::ComponentSwizzle::eB, vk::ComponentSwizzle::eA);
- vk::ImageSubresourceRange subResourceRange(vk::ImageAspectFlagBits::eDepth, 0, 1, 0, 1);
- depthView = device->createImageViewUnique(vk::ImageViewCreateInfo(vk::ImageViewCreateFlags(), *depthImage, vk::ImageViewType::e2D, depthFormat, componentMapping, subResourceRange));
-
return depthFormat;
}
-void VulkanContext::InitImgui(vk::RenderPass renderPass, int subpass)
+void VulkanContext::InitImgui()
{
gui_init();
ImGui_ImplVulkan_InitInfo initInfo = {};
@@ -296,7 +269,7 @@ void VulkanContext::InitImgui(vk::RenderPass renderPass, int subpass)
initInfo.CheckVkResultFn = &CheckImGuiResult;
#endif
- if (!ImGui_ImplVulkan_Init(&initInfo, (VkRenderPass)renderPass, subpass))
+ if (!ImGui_ImplVulkan_Init(&initInfo, (VkRenderPass)*renderPass, 0))
{
die("ImGui initialization failed");
}
@@ -399,8 +372,9 @@ bool VulkanContext::InitDevice()
features.fragmentStoresAndAtomics = true;
if (samplerAnisotropy)
features.samplerAnisotropy = true;
+ const char *layers[] = { "VK_LAYER_ARM_AGA" };
device = physicalDevice.createDeviceUnique(vk::DeviceCreateInfo(vk::DeviceCreateFlags(), 1, &deviceQueueCreateInfo,
- 0, nullptr, deviceExtensions.size(), &deviceExtensions[0], &features));
+ 0, layers, deviceExtensions.size(), &deviceExtensions[0], &features));
// This links entry points directly from the driver and isn't absolutely necessary
volkLoadDevice(static_cast(*device));
@@ -446,6 +420,10 @@ bool VulkanContext::InitDevice()
}
allocator.Init(physicalDevice, *device);
+ quadBuffer = std::unique_ptr(new QuadBuffer());
+ shaderManager = std::unique_ptr(new ShaderManager());
+ quadPipeline = std::unique_ptr(new QuadPipeline());
+
CreateSwapChain();
return true;
@@ -574,25 +552,21 @@ void VulkanContext::CreateSwapChain()
commandBuffers.push_back(std::move(device->allocateCommandBuffersUnique(vk::CommandBufferAllocateInfo(*commandPools.back(), vk::CommandBufferLevel::ePrimary, 1)).front()));
}
- vk::Format depthFormat = InitDepthBuffer();
+ vk::Format depthFormat = FindDepthFormat();
// Render pass
- vk::AttachmentDescription attachmentDescriptions[2];
- // FIXME we should use vk::AttachmentLoadOp::eLoad for loadOp to preserve previous framebuffer but it fails on the first render
- attachmentDescriptions[0] = vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), colorFormat, vk::SampleCountFlagBits::e1, vk::AttachmentLoadOp::eClear,
- vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, vk::ImageLayout::eUndefined, vk::ImageLayout::ePresentSrcKHR);
- attachmentDescriptions[1] = vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), depthFormat, vk::SampleCountFlagBits::e1, vk::AttachmentLoadOp::eClear,
- vk::AttachmentStoreOp::eDontCare, vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare, vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal);
+ vk::AttachmentDescription attachmentDescription = vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), colorFormat, vk::SampleCountFlagBits::e1,
+ vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
+ vk::ImageLayout::eUndefined, vk::ImageLayout::ePresentSrcKHR);
vk::AttachmentReference colorReference(0, vk::ImageLayout::eColorAttachmentOptimal);
- vk::AttachmentReference depthReference(1, vk::ImageLayout::eDepthStencilAttachmentOptimal);
- vk::SubpassDescription subpass(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 0, nullptr, 1, &colorReference, nullptr, &depthReference);
+ vk::SubpassDescription subpass(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 0, nullptr, 1, &colorReference,
+ nullptr, nullptr);
- renderPass = device->createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(), 2, attachmentDescriptions, 1, &subpass));
+ renderPass = device->createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(),
+ 1, &attachmentDescription, 1, &subpass));
// Framebuffers, fences, semaphores
- vk::ImageView attachments[2];
- attachments[1] = *depthView;
framebuffers.reserve(imageViews.size());
drawFences.reserve(imageViews.size());
@@ -600,14 +574,17 @@ void VulkanContext::CreateSwapChain()
imageAcquiredSemaphores.reserve(imageViews.size());
for (auto const& view : imageViews)
{
- attachments[0] = *view;
- framebuffers.push_back(device->createFramebufferUnique(vk::FramebufferCreateInfo(vk::FramebufferCreateFlags(), *renderPass, 2, attachments, width, height, 1)));
+ framebuffers.push_back(device->createFramebufferUnique(vk::FramebufferCreateInfo(vk::FramebufferCreateFlags(), *renderPass,
+ 1, &view.get(), width, height, 1)));
drawFences.push_back(device->createFenceUnique(vk::FenceCreateInfo(vk::FenceCreateFlagBits::eSignaled)));
renderCompleteSemaphores.push_back(device->createSemaphoreUnique(vk::SemaphoreCreateInfo()));
imageAcquiredSemaphores.push_back(device->createSemaphoreUnique(vk::SemaphoreCreateInfo()));
}
+ quadPipeline->Init(shaderManager.get());
- InitImgui(*renderPass);
+ InitImgui();
+
+ currentImage = GetSwapChainSize() - 1;
INFO_LOG(RENDERER, "Vulkan swap chain created: %d x %d, swap chain size %d", width, height, (int)imageViews.size());
}
@@ -680,7 +657,6 @@ void VulkanContext::NewFrame()
void VulkanContext::BeginRenderPass()
{
- InitImgui(*renderPass);
const vk::ClearValue clear_colors[] = { vk::ClearColorValue(std::array{0.f, 0.f, 0.f, 1.f}), vk::ClearDepthStencilValue{ 0.f, 0 } };
vk::CommandBuffer commandBuffer = *commandBuffers[currentImage];
commandBuffer.beginRenderPass(vk::RenderPassBeginInfo(*renderPass, *framebuffers[currentImage], vk::Rect2D({0, 0}, {width, height}), 2, clear_colors),
@@ -716,6 +692,41 @@ void VulkanContext::Present()
}
}
+extern Renderer *renderer;
+
+void VulkanContext::PresentFrame(vk::ImageView imageView, vk::Offset2D extent)
+{
+ NewFrame();
+ BeginRenderPass();
+
+ float marginWidth = ((float)extent.y / extent.x * width / height - 1.f) / 2.f;
+ QuadVertex vtx[] = {
+ { { -1, -1, 0 }, { 0 - marginWidth, 0 } },
+ { { 1, -1, 0 }, { 1 + marginWidth, 0 } },
+ { { -1, 1, 0 }, { 0 - marginWidth, 1 } },
+ { { 1, 1, 0 }, { 1 + marginWidth, 1 } },
+ };
+ quadBuffer->Update(vtx);
+
+ vk::CommandBuffer commandBuffer = GetCurrentCommandBuffer();
+ vk::Pipeline pipeline = quadPipeline->GetPipeline();
+ commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
+
+ quadPipeline->SetTexture(imageView);
+ quadPipeline->BindDescriptorSets(commandBuffer);
+
+ float blendConstants[4] = { 1.0, 1.0, 1.0, 1.0 };
+ commandBuffer.setBlendConstants(blendConstants);
+
+ vk::Viewport viewport(0, 0, width, height);
+ commandBuffer.setViewport(0, 1, &viewport);
+ commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(0, 0), vk::Extent2D(width, height)));
+ quadBuffer->Bind(commandBuffer);
+ quadBuffer->Draw(commandBuffer);
+ renderer->DrawOSD(false);
+ EndFrame();
+}
+
void VulkanContext::Term()
{
ImGui_ImplVulkan_Shutdown();
@@ -739,10 +750,10 @@ void VulkanContext::Term()
imageViews.clear();
framebuffers.clear();
renderPass.reset();
+ quadBuffer.reset();
+ quadPipeline.reset();
+ shaderManager.reset();
descriptorPool.reset();
- depthView.reset();
- depthMemory.reset();
- depthImage.reset();
commandBuffers.clear();
commandPools.clear();
imageAcquiredSemaphores.clear();
diff --git a/core/rend/vulkan/vulkan_context.h b/core/rend/vulkan/vulkan_context.h
new file mode 100644
index 000000000..68bc65de9
--- /dev/null
+++ b/core/rend/vulkan/vulkan_context.h
@@ -0,0 +1,192 @@
+/*
+ Created on: Nov 29, 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 .
+*/
+#pragma once
+#include "vulkan.h"
+#include "vmallocator.h"
+#include "quad.h"
+#include "rend/TexCache.h"
+
+extern int screen_width, screen_height;
+
+class VulkanContext
+{
+public:
+ VulkanContext() { verify(contextInstance == nullptr); contextInstance = this; }
+ ~VulkanContext() { verify(contextInstance == this); contextInstance = nullptr; }
+
+ bool Init();
+ bool InitInstance(const char** extensions, uint32_t extensions_count);
+ bool InitDevice();
+ void CreateSwapChain();
+ void Term();
+ void SetWindow(void *window, void *display) { this->window = window; this->display = display; }
+
+ VkInstance GetInstance() const { return static_cast(instance.get()); }
+ u32 GetGraphicsQueueFamilyIndex() const { return graphicsQueueIndex; }
+ void SetWindowSize(u32 width, u32 height) { this->width = screen_width = width; this->height = screen_height = height; }
+ void NewFrame();
+ void BeginRenderPass();
+ void EndFrame();
+ void Present();
+ void PresentFrame(vk::ImageView imageView, vk::Offset2D extent);
+
+ vk::PhysicalDevice GetPhysicalDevice() const { return physicalDevice; }
+ vk::Device GetDevice() const { return *device; }
+ vk::PipelineCache GetPipelineCache() const { return *pipelineCache; }
+ vk::RenderPass GetRenderPass() const { return *renderPass; }
+ vk::CommandBuffer GetCurrentCommandBuffer() const { return *commandBuffers[GetCurrentImageIndex()]; }
+ vk::DescriptorPool GetDescriptorPool() const { return *descriptorPool; }
+ vk::Extent2D GetViewPort() const { return { width, height }; }
+ int GetSwapChainSize() const { return (int)imageViews.size(); }
+ int GetCurrentImageIndex() const { return currentImage; }
+ void WaitIdle() const { graphicsQueue.waitIdle(); }
+ bool IsRendering() const { return rendering; }
+ vk::Queue GetGraphicsQueue() const { return graphicsQueue; }
+ vk::DeviceSize GetUniformBufferAlignment() const { return uniformBufferAlignment; }
+ vk::DeviceSize GetStorageBufferAlignment() const { return storageBufferAlignment; }
+ bool IsFormatSupported(TextureType textureType)
+ {
+ switch (textureType)
+ {
+ case TextureType::_4444:
+ return optimalTilingSupported4444;
+ case TextureType::_565:
+ return optimalTilingSupported565;
+ case TextureType::_5551:
+ return optimalTilingSupported1555;
+ default:
+ return true;
+ }
+ }
+ std::string GetDriverName() const { vk::PhysicalDeviceProperties props; physicalDevice.getProperties(&props); return props.deviceName; }
+ std::string GetDriverVersion() const { vk::PhysicalDeviceProperties props; physicalDevice.getProperties(&props); return std::to_string(props.driverVersion); }
+ vk::Format GetColorFormat() const { return colorFormat; }
+ vk::Format GetDepthFormat() const { return depthFormat; }
+ static VulkanContext *Instance() { return contextInstance; }
+ bool SupportsFragmentShaderStoresAndAtomics() const { return fragmentStoresAndAtomics; }
+ bool SupportsSamplerAnisotropy() const { return samplerAnisotropy; }
+ bool SupportsDedicatedAllocation() const { return dedicatedAllocationSupported; }
+ const VMAllocator& GetAllocator() const { return allocator; }
+ bool IsUnifiedMemory() const { return unifiedMemory; }
+
+private:
+ vk::Format FindDepthFormat();
+ void InitImgui();
+ void DoSwapAutomation();
+ vk::SurfaceKHR GetSurface() {
+#ifdef USE_SDL
+ return surface;
+#else
+ return *surface;
+#endif
+ }
+
+ bool HasSurfaceDimensionChanged()
+ {
+ vk::SurfaceCapabilitiesKHR surfaceCapabilities = physicalDevice.getSurfaceCapabilitiesKHR(GetSurface());
+ VkExtent2D swapchainExtent;
+ if (surfaceCapabilities.currentExtent.width == std::numeric_limits::max())
+ {
+ // If the surface size is undefined, the size is set to the size of the images requested.
+ swapchainExtent.width = std::min(std::max(width, surfaceCapabilities.minImageExtent.width), surfaceCapabilities.maxImageExtent.width);
+ swapchainExtent.height = std::min(std::max(height, surfaceCapabilities.minImageExtent.height), surfaceCapabilities.maxImageExtent.height);
+ }
+ else
+ {
+ // If the surface size is defined, the swap chain size must match
+ swapchainExtent = surfaceCapabilities.currentExtent;
+ }
+ if (width == swapchainExtent.width && height == swapchainExtent.height)
+ return false;
+
+ screen_width = width = swapchainExtent.width;
+ screen_height = height = swapchainExtent.height;
+
+ return true;
+ }
+
+ VMAllocator allocator;
+ void *window = nullptr;
+ void *display = nullptr;
+ bool rendering = false;
+ bool renderDone = false;
+ u32 width = 0;
+ u32 height = 0;
+ vk::UniqueInstance instance;
+ vk::PhysicalDevice physicalDevice;
+
+ u32 graphicsQueueIndex = 0;
+ u32 presentQueueIndex = 0;
+ vk::DeviceSize uniformBufferAlignment = 0;
+ vk::DeviceSize storageBufferAlignment = 0;
+ bool optimalTilingSupported565 = false;
+ bool optimalTilingSupported1555 = false;
+ bool optimalTilingSupported4444 = false;
+ bool fragmentStoresAndAtomics = false;
+ bool samplerAnisotropy = false;
+ bool dedicatedAllocationSupported = false;
+ bool unifiedMemory = false;
+ vk::UniqueDevice device;
+
+#ifdef USE_SDL
+ vk::SurfaceKHR surface;
+#else
+ vk::UniqueSurfaceKHR surface;
+#endif
+
+ vk::UniqueSwapchainKHR swapChain;
+ std::vector imageViews;
+ u32 currentImage = 0;
+ vk::Format colorFormat = vk::Format::eUndefined;
+
+ vk::Queue graphicsQueue;
+ vk::Queue presentQueue;
+
+ vk::UniqueDescriptorPool descriptorPool;
+ vk::UniqueRenderPass renderPass;
+
+ vk::Format depthFormat = vk::Format::eUndefined;
+
+ std::vector commandPools;
+ std::vector commandBuffers;
+
+ std::vector framebuffers;
+
+ std::vector drawFences;
+ std::vector renderCompleteSemaphores;
+ std::vector imageAcquiredSemaphores;
+ u32 currentSemaphore = 0;
+
+ vk::UniquePipelineCache pipelineCache;
+
+ std::unique_ptr quadBuffer;
+ std::unique_ptr quadPipeline;
+ std::unique_ptr shaderManager;
+
+#ifdef VK_DEBUG
+#ifndef __ANDROID__
+ vk::UniqueDebugUtilsMessengerEXT debugUtilsMessenger;
+#else
+ vk::UniqueDebugReportCallbackEXT debugReportCallback;
+#endif
+#endif
+ static VulkanContext *contextInstance;
+};
diff --git a/core/rend/vulkan/vulkan_renderer.cpp b/core/rend/vulkan/vulkan_renderer.cpp
index ea8b89a9b..f88e1947f 100644
--- a/core/rend/vulkan/vulkan_renderer.cpp
+++ b/core/rend/vulkan/vulkan_renderer.cpp
@@ -52,6 +52,7 @@ public:
}
screenDrawer.Init(&samplerManager, &shaderManager);
+ screenDrawer.SetCommandPool(&texCommandPool);
quadPipeline.Init(&shaderManager);
quadBuffer = std::unique_ptr(new QuadBuffer());
@@ -75,6 +76,7 @@ public:
vjoyTexture->SetCommandBuffer(texCommandPool.Allocate());
vjoyTexture->UploadToGPU(OSD_TEX_W, OSD_TEX_H, image_data);
vjoyTexture->SetCommandBuffer(nullptr);
+ texCommandPool.EndFrame();
delete [] image_data;
osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass());
}
@@ -137,48 +139,7 @@ public:
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;
- }
- quadBuffer->Update();
-
- 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((u32)std::max(lroundf(min_x), 0L), (u32)std::max(lroundf(min_y), 0L)),
- vk::Extent2D((u32)std::max(lroundf(width), 0L), (u32)std::max(lroundf(height), 0L))));
- quadBuffer->Bind(cmdBuffer);
- quadBuffer->Draw(cmdBuffer);
-
- gui_display_osd();
-
- screenDrawer.EndRenderPass();
+ GetContext()->PresentFrame(curTexture->GetImageView(), { 640, 480 });
return true;
}
@@ -209,12 +170,13 @@ public:
if (result)
CheckFogTexture();
- if (!result || !ctx->rend.isRTT)
+ if (!result)
texCommandPool.EndFrame();
return result;
}
+ // FIXME This needs to go in its own class
void DrawOSD(bool clear_screen) override
{
gui_display_osd();
@@ -268,8 +230,7 @@ public:
drawer = &screenDrawer;
drawer->Draw(fogTexture.get());
- if (!pvrrc.isRTT)
- DrawOSD(false);
+
drawer->EndRenderPass();
return !pvrrc.isRTT;
diff --git a/core/wsi/context.h b/core/wsi/context.h
index 0ba7bf743..88ce8f8c4 100644
--- a/core/wsi/context.h
+++ b/core/wsi/context.h
@@ -21,7 +21,7 @@
#pragma once
#include "gl_context.h"
#ifdef USE_VULKAN
-#include "rend/vulkan/vulkan.h"
+#include "rend/vulkan/vulkan_context.h"
extern VulkanContext theVulkanContext;
#endif
diff --git a/core/wsi/switcher.cpp b/core/wsi/switcher.cpp
index a6577f8ae..85ae2d459 100644
--- a/core/wsi/switcher.cpp
+++ b/core/wsi/switcher.cpp
@@ -33,6 +33,8 @@ void InitRenderApi()
if (theVulkanContext.Init())
return;
// Fall back to Open GL
+ WARN_LOG(RENDERER, "Vulkan init failed. Falling back to Open GL.");
+ settings.pvr.rend = 0;
}
#endif
if (!theGLContext.Init())