vulkan: fix crash on minimize and resize. clear framebuffer on resize

many vulkan fixes related to init/term/reset
This commit is contained in:
Flyinghead 2021-01-09 18:16:39 +01:00
parent 4a103a1de3
commit 1727413247
14 changed files with 335 additions and 189 deletions

View File

@ -37,20 +37,25 @@ static inline void ImGui_impl_RenderDrawData(ImDrawData *draw_data, bool save_ba
if (!settings.pvr.IsOpenGL())
{
VulkanContext *context = VulkanContext::Instance();
bool rendering = context->IsRendering();
const std::vector<vk::UniqueCommandBuffer> *vmuCmdBuffers = nullptr;
if (!rendering)
{
context->NewFrame();
vmuCmdBuffers = context->PrepareVMUs();
context->BeginRenderPass();
context->PresentLastFrame();
context->DrawVMUs(gui_get_scaling());
if (!context->IsValid())
return;
try {
bool rendering = context->IsRendering();
const std::vector<vk::UniqueCommandBuffer> *vmuCmdBuffers = nullptr;
if (!rendering)
{
context->NewFrame();
vmuCmdBuffers = context->PrepareVMUs();
context->BeginRenderPass();
context->PresentLastFrame();
context->DrawVMUs(gui_get_scaling());
}
// Record Imgui Draw Data and draw funcs into command buffer
ImGui_ImplVulkan_RenderDrawData(draw_data, (VkCommandBuffer)context->GetCurrentCommandBuffer());
if (!rendering)
context->EndFrame(vmuCmdBuffers);
} catch (const InvalidVulkanContext& err) {
}
// Record Imgui Draw Data and draw funcs into command buffer
ImGui_ImplVulkan_RenderDrawData(draw_data, (VkCommandBuffer)context->GetCurrentCommandBuffer());
if (!rendering)
context->EndFrame(vmuCmdBuffers);
}
else
#endif

View File

@ -590,6 +590,9 @@ void ScreenDrawer::Init(SamplerManager *samplerManager, ShaderManager *shaderMan
framebuffers.clear();
colorAttachments.clear();
depthAttachment.reset();
transitionNeeded.clear();
clearNeeded.clear();
frameRendered = false;
}
this->viewport = viewport;
if (!depthAttachment)
@ -600,7 +603,7 @@ void ScreenDrawer::Init(SamplerManager *samplerManager, ShaderManager *shaderMan
vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::eTransientAttachment);
}
if (!renderPass)
if (!renderPassLoad)
{
vk::AttachmentDescription attachmentDescriptions[] = {
// Color attachment
@ -629,7 +632,12 @@ void ScreenDrawer::Init(SamplerManager *samplerManager, ShaderManager *shaderMan
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(),
renderPassLoad = GetContext()->GetDevice().createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(),
ARRAY_SIZE(attachmentDescriptions), attachmentDescriptions,
ARRAY_SIZE(subpasses), subpasses,
dependencies.size(), dependencies.data()));
attachmentDescriptions[0].loadOp = vk::AttachmentLoadOp::eClear;
renderPassClear = GetContext()->GetDevice().createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(),
ARRAY_SIZE(attachmentDescriptions), attachmentDescriptions,
ARRAY_SIZE(subpasses), subpasses,
dependencies.size(), dependencies.data()));
@ -639,6 +647,8 @@ void ScreenDrawer::Init(SamplerManager *samplerManager, ShaderManager *shaderMan
{
colorAttachments.resize(size);
framebuffers.resize(size);
transitionNeeded.resize(size);
clearNeeded.resize(size);
}
else
{
@ -653,16 +663,18 @@ void ScreenDrawer::Init(SamplerManager *samplerManager, ShaderManager *shaderMan
colorAttachments.back()->Init(viewport.width, viewport.height, GetContext()->GetColorFormat(),
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled);
attachments[0] = colorAttachments.back()->GetImageView();
vk::FramebufferCreateInfo createInfo(vk::FramebufferCreateFlags(), *renderPass,
vk::FramebufferCreateInfo createInfo(vk::FramebufferCreateFlags(), *renderPassLoad,
ARRAY_SIZE(attachments), attachments, viewport.width, viewport.height, 1);
framebuffers.push_back(GetContext()->GetDevice().createFramebufferUnique(createInfo));
transitionsNeeded++;
transitionNeeded.push_back(true);
clearNeeded.resize(true);
}
}
frameRendered = false;
if (!screenPipelineManager)
screenPipelineManager = std::unique_ptr<PipelineManager>(new PipelineManager());
screenPipelineManager->Init(shaderManager, *renderPass);
screenPipelineManager->Init(shaderManager, *renderPassLoad);
Drawer::Init(samplerManager, screenPipelineManager.get());
}
@ -674,15 +686,17 @@ vk::CommandBuffer ScreenDrawer::BeginRenderPass()
vk::CommandBuffer commandBuffer = commandPool->Allocate();
commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
if (transitionsNeeded)
if (transitionNeeded[GetCurrentImage()])
{
for (size_t i = colorAttachments.size() - transitionsNeeded; i < colorAttachments.size(); i++)
setImageLayout(commandBuffer, colorAttachments[i]->GetImage(), GetContext()->GetColorFormat(), 1, vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal);
transitionsNeeded = 0;
setImageLayout(commandBuffer, colorAttachments[GetCurrentImage()]->GetImage(), GetContext()->GetColorFormat(),
1, vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal);
transitionNeeded[GetCurrentImage()] = false;
}
vk::RenderPass renderPass = clearNeeded[GetCurrentImage()] ? *renderPassClear : *renderPassLoad;
clearNeeded[GetCurrentImage()] = false;
const vk::ClearValue clear_colors[] = { vk::ClearColorValue(std::array<float, 4> { 0.f, 0.f, 0.f, 1.f }), vk::ClearDepthStencilValue { 0.f, 0 } };
commandBuffer.beginRenderPass(vk::RenderPassBeginInfo(*renderPass, *framebuffers[GetCurrentImage()],
commandBuffer.beginRenderPass(vk::RenderPassBeginInfo(renderPass, *framebuffers[GetCurrentImage()],
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));

View File

@ -193,14 +193,13 @@ class ScreenDrawer : public Drawer
{
public:
void Init(SamplerManager *samplerManager, ShaderManager *shaderManager);
vk::RenderPass GetRenderPass() const { return *renderPass; }
virtual void EndRenderPass() override;
bool PresentFrame()
{
if (!frameRendered)
return false;
frameRendered = false;
GetContext()->PresentFrame(colorAttachments[GetCurrentImage()]->GetImageView(), vk::Offset2D(viewport.width, viewport.height));
GetContext()->PresentFrame(colorAttachments[GetCurrentImage()]->GetImageView(), viewport);
NewImage();
return true;
@ -213,14 +212,16 @@ protected:
private:
std::unique_ptr<PipelineManager> screenPipelineManager;
vk::UniqueRenderPass renderPass;
vk::UniqueRenderPass renderPassLoad;
vk::UniqueRenderPass renderPassClear;
std::vector<vk::UniqueFramebuffer> framebuffers;
std::vector<std::unique_ptr<FramebufferAttachment>> colorAttachments;
std::unique_ptr<FramebufferAttachment> depthAttachment;
vk::Extent2D viewport;
int currentScreenScaling = 0;
ShaderManager *shaderManager = nullptr;
int transitionsNeeded = 0;
std::vector<bool> transitionNeeded;
std::vector<bool> clearNeeded;
bool frameRendered = false;
};

View File

@ -354,6 +354,14 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture)
// Final subpass
cmdBuffer.nextSubpass(vk::SubpassContents::eInline);
GetCurrentDescSet().BindColorInputDescSet(cmdBuffer, (pvrrc.render_passes.used() - 1 - render_pass) % 2);
if (initialPass && clearNeeded[GetCurrentImage()] && !pvrrc.isRTT)
{
clearNeeded[GetCurrentImage()] = false;
SetScissor(cmdBuffer, viewport);
cmdBuffer.clearAttachments(vk::ClearAttachment(vk::ImageAspectFlagBits::eColor, 0, clear_colors[0]),
vk::ClearRect(viewport, 0, 1));
}
SetScissor(cmdBuffer, baseScissor);
if (!oitBuffers->isFirstFrameAfterInit())
@ -460,6 +468,8 @@ void OITScreenDrawer::MakeFramebuffers()
MakeBuffers(viewport.extent.width, viewport.extent.height);
framebuffers.clear();
finalColorAttachments.clear();
transitionNeeded.clear();
clearNeeded.clear();
while (finalColorAttachments.size() < GetContext()->GetSwapChainSize())
{
finalColorAttachments.push_back(std::unique_ptr<FramebufferAttachment>(
@ -474,7 +484,8 @@ void OITScreenDrawer::MakeFramebuffers()
vk::FramebufferCreateInfo createInfo(vk::FramebufferCreateFlags(), screenPipelineManager->GetRenderPass(true, true),
ARRAY_SIZE(attachments), attachments, viewport.extent.width, viewport.extent.height, 1);
framebuffers.push_back(GetContext()->GetDevice().createFramebufferUnique(createInfo));
transitionsNeeded++;
transitionNeeded.push_back(true);
clearNeeded.push_back(true);
}
}
@ -676,11 +687,10 @@ vk::CommandBuffer OITScreenDrawer::NewFrame()
vk::CommandBuffer commandBuffer = commandPool->Allocate();
commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
if (transitionsNeeded)
if (transitionNeeded[GetCurrentImage()])
{
for (size_t i = finalColorAttachments.size() - transitionsNeeded; i < finalColorAttachments.size(); i++)
setImageLayout(commandBuffer, finalColorAttachments[i]->GetImage(), GetColorFormat(), 1, vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal);
transitionsNeeded = 0;
setImageLayout(commandBuffer, finalColorAttachments[GetCurrentImage()]->GetImage(), GetColorFormat(), 1, vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal);
transitionNeeded[GetCurrentImage()] = false;
}
matrices.CalcMatrices(&pvrrc);

View File

@ -118,6 +118,7 @@ protected:
std::array<std::unique_ptr<FramebufferAttachment>, 2> colorAttachments;
std::unique_ptr<FramebufferAttachment> depthAttachment;
vk::CommandBuffer currentCommandBuffer;
std::vector<bool> clearNeeded;
private:
void DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sortTriangles, Pass pass,
@ -166,6 +167,7 @@ public:
currentScreenScaling = 0;
MakeFramebuffers();
GetContext()->PresentFrame(vk::ImageView(), viewport.extent);
}
void Term()
{
@ -191,7 +193,7 @@ public:
if (!frameRendered)
return false;
frameRendered = false;
GetContext()->PresentFrame(finalColorAttachments[GetCurrentImage()]->GetImageView(), vk::Offset2D(viewport.extent.width, viewport.extent.height));
GetContext()->PresentFrame(finalColorAttachments[GetCurrentImage()]->GetImageView(), viewport.extent);
NewImage();
return true;
@ -208,7 +210,7 @@ private:
std::vector<vk::UniqueFramebuffer> framebuffers;
std::unique_ptr<OITPipelineManager> screenPipelineManager;
int currentScreenScaling = 0;
int transitionsNeeded = 0;
std::vector<bool> transitionNeeded;
bool frameRendered = false;
};

View File

@ -148,12 +148,11 @@ public:
{
colorInputDescSets[index] = std::move(GetContext()->GetDevice().allocateDescriptorSetsUnique(
vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), 1, &colorInputLayout)).front());
std::vector<vk::WriteDescriptorSet> writeDescriptorSets;
vk::DescriptorImageInfo colorImageInfo(vk::Sampler(), colorImageView, vk::ImageLayout::eShaderReadOnlyOptimal);
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*colorInputDescSets[index], 0, 0, 1, vk::DescriptorType::eInputAttachment, &colorImageInfo, nullptr, nullptr));
GetContext()->GetDevice().updateDescriptorSets(writeDescriptorSets, nullptr);
}
vk::DescriptorImageInfo colorImageInfo(vk::Sampler(), colorImageView, vk::ImageLayout::eShaderReadOnlyOptimal);
vk::WriteDescriptorSet writeDescriptorSet(*colorInputDescSets[index], 0, 0, 1, vk::DescriptorType::eInputAttachment, &colorImageInfo, nullptr, nullptr);
GetContext()->GetDevice().updateDescriptorSets(1, &writeDescriptorSet, 0, nullptr);
}
void SetTexture(u64 textureId0, TSP tsp0, u64 textureId1, TSP tsp1)

View File

@ -51,14 +51,13 @@ public:
void Resize(int w, int h) override
{
NOTICE_LOG(RENDERER, "OIT Resize %d x %d", w, h);
BaseVulkanRenderer::Resize(w, h);
screenDrawer.Init(&samplerManager, &oitShaderManager, &oitBuffers);
}
void Term() override
{
DEBUG_LOG(RENDERER, "VulkanRenderer::Term");
DEBUG_LOG(RENDERER, "OITVulkanRenderer::Term");
GetContext()->WaitIdle();
screenDrawer.Term();
textureDrawer.Term();
@ -68,17 +67,24 @@ public:
bool Render() override
{
OITDrawer *drawer;
if (pvrrc.isRTT)
drawer = &textureDrawer;
else
drawer = &screenDrawer;
try {
OITDrawer *drawer;
if (pvrrc.isRTT)
drawer = &textureDrawer;
else
drawer = &screenDrawer;
drawer->Draw(fogTexture.get(), paletteTexture.get());
drawer->Draw(fogTexture.get(), paletteTexture.get());
drawer->EndFrame();
drawer->EndFrame();
return !pvrrc.isRTT;
return !pvrrc.isRTT;
} catch (const vk::SystemError& e) {
// Sometimes happens when resizing the window
WARN_LOG(RENDERER, "Vulkan system error %s", e.what());
return false;
}
}
bool Present() override

View File

@ -142,7 +142,7 @@ private:
std::vector<vk::UniqueDescriptorSet> perPolyDescSets;
std::map<std::pair<u64, u32>, vk::UniqueDescriptorSet> perPolyDescSetsInFlight;
SamplerManager* samplerManager;
SamplerManager* samplerManager = nullptr;
};
class PipelineManager
@ -278,7 +278,7 @@ protected:
VulkanContext *GetContext() const { return VulkanContext::Instance(); }
vk::RenderPass renderPass;
ShaderManager *shaderManager;
ShaderManager *shaderManager = nullptr;
};
class RttPipelineManager : public PipelineManager
@ -326,7 +326,7 @@ public:
private:
vk::UniqueRenderPass rttRenderPass;
bool renderToTextureBuffer;
bool renderToTextureBuffer = false;
};
class OSDPipeline
@ -391,5 +391,5 @@ private:
vk::UniqueDescriptorSet descriptorSet;
vk::UniquePipelineLayout pipelineLayout;
vk::UniqueDescriptorSetLayout descSetLayout;
ShaderManager *shaderManager;
ShaderManager *shaderManager = nullptr;
};

View File

@ -164,6 +164,8 @@ void QuadDrawer::Init(QuadPipeline *pipeline)
this->pipeline = pipeline;
buffer = std::unique_ptr<QuadBuffer>(new QuadBuffer());
descriptorSets.resize(VulkanContext::Instance()->GetSwapChainSize());
for (auto& descSet : descriptorSets)
descSet.reset();
}
void QuadDrawer::Draw(vk::CommandBuffer commandBuffer, vk::ImageView imageView, QuadVertex vertices[], bool nearestFilter)

View File

@ -94,7 +94,7 @@ VKAPI_ATTR static VkBool32 VKAPI_CALL debugUtilsMessengerCallback(VkDebugUtilsMe
ERROR_LOG(RENDERER, "%s", msg.c_str());
break;
}
return VK_TRUE;
return VK_FALSE;
}
#else
#if HOST_CPU == CPU_ARM
@ -137,7 +137,8 @@ bool VulkanContext::InitInstance(const char** extensions, uint32_t extensions_co
{
u32 apiVersion;
if (vk::enumerateInstanceVersion(&apiVersion) == vk::Result::eSuccess)
vulkan11 = VK_VERSION_MINOR(apiVersion) == 1;
vulkan11 = VK_VERSION_MAJOR(apiVersion) > 1
|| (VK_VERSION_MAJOR(apiVersion) == 1 && VK_VERSION_MINOR(apiVersion) >= 1);
}
vk::ApplicationInfo applicationInfo("Flycast", 1, "Flycast", 1, vulkan11 ? VK_API_VERSION_1_1 : VK_API_VERSION_1_0);
std::vector<const char *> vext;
@ -410,6 +411,15 @@ bool VulkanContext::InitDevice()
deviceExtensions.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
dedicatedAllocationSupported = true;
}
#ifdef VK_DEBUG
else if (!strcmp(property.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)
|| !strcmp(property.extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME)
|| !strcmp(property.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
{
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName);
deviceExtensions.push_back(property.extensionName);
}
#endif
}
dedicatedAllocationSupported &= getMemReq2Supported;
@ -482,6 +492,9 @@ bool VulkanContext::InitDevice()
{
ERROR_LOG(RENDERER, "Vulkan error: %s", err.what());
}
catch (const InvalidVulkanContext& err)
{
}
catch (...)
{
ERROR_LOG(RENDERER, "Unknown error");
@ -501,7 +514,8 @@ void VulkanContext::CreateSwapChain()
renderCompleteSemaphores.clear();
commandBuffers.clear();
commandPools.clear();
imageViews.clear();
for (auto& img : imageViews)
img.reset();
// get the supported VkFormats
std::vector<vk::SurfaceFormatKHR> formats = physicalDevice.getSurfaceFormatsKHR(GetSurface());
@ -539,6 +553,9 @@ void VulkanContext::CreateSwapChain()
swapchainExtent = surfaceCapabilities.currentExtent;
}
SetWindowSize(swapchainExtent.width, swapchainExtent.height);
resized = false;
if (swapchainExtent.width == 0 || swapchainExtent.height == 0)
throw InvalidVulkanContext();
// The FIFO present mode is guaranteed by the spec to be supported
vk::PresentModeKHR swapchainPresentMode = vk::PresentModeKHR::eFifo;
@ -598,22 +615,23 @@ void VulkanContext::CreateSwapChain()
{
DEBUG_LOG(RENDERER, "createSwapchainKHRUnique failed: %s", err.what());
if (++tries > 10)
throw err;
throw InvalidVulkanContext();
}
}
while (!swapChain);
std::vector<vk::Image> swapChainImages = device->getSwapchainImagesKHR(*swapChain);
imageViews.reserve(swapChainImages.size());
imageViews.resize(swapChainImages.size());
commandPools.reserve(swapChainImages.size());
commandBuffers.reserve(swapChainImages.size());
vk::ComponentMapping componentMapping(vk::ComponentSwizzle::eR, vk::ComponentSwizzle::eG, vk::ComponentSwizzle::eB, vk::ComponentSwizzle::eA);
vk::ImageSubresourceRange subResourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1);
u32 imageIdx = 0;
for (auto image : swapChainImages)
{
vk::ImageViewCreateInfo imageViewCreateInfo(vk::ImageViewCreateFlags(), image, vk::ImageViewType::e2D, colorFormat, componentMapping, subResourceRange);
imageViews.push_back(device->createImageViewUnique(imageViewCreateInfo));
imageViews[imageIdx++] = device->createImageViewUnique(imageViewCreateInfo);
// create a UniqueCommandPool to allocate a CommandBuffer from
commandPools.push_back(device->createCommandPoolUnique(vk::CommandPoolCreateInfo(vk::CommandPoolCreateFlagBits::eTransient, graphicsQueueIndex)));
@ -663,10 +681,8 @@ void VulkanContext::CreateSwapChain()
catch (const vk::SystemError& err)
{
ERROR_LOG(RENDERER, "Vulkan error: %s", err.what());
}
catch (...)
{
ERROR_LOG(RENDERER, "Unknown error");
SetWindowSize(0, 0);
throw InvalidVulkanContext();
}
}
@ -696,8 +712,9 @@ bool VulkanContext::Init()
#if defined(USE_SDL)
VkSurfaceKHR surface;
if (SDL_Vulkan_CreateSurface((SDL_Window *)window, (VkInstance)*instance, (VkSurfaceKHR *)&this->surface) == 0)
if (SDL_Vulkan_CreateSurface((SDL_Window *)window, (VkInstance)*instance, &surface) == 0)
return false;
this->surface.reset(surface);
#elif defined(_WIN32)
vk::Win32SurfaceCreateInfoKHR createInfo(vk::Win32SurfaceCreateFlagsKHR(), GetModuleHandle(NULL), (HWND)window);
surface = instance->createWin32SurfaceKHRUnique(createInfo);
@ -715,11 +732,14 @@ bool VulkanContext::Init()
void VulkanContext::NewFrame()
{
if (HasSurfaceDimensionChanged())
if (resized || HasSurfaceDimensionChanged())
{
CreateSwapChain();
rend_resize(width, height);
rend_resize(screen_width, screen_height);
lastFrameView = vk::ImageView();
}
if (!IsValid())
throw InvalidVulkanContext();
device->acquireNextImageKHR(*swapChain, UINT64_MAX, *imageAcquiredSemaphores[currentSemaphore], nullptr, &currentImage);
device->waitForFences(1, &(*drawFences[currentImage]), true, UINT64_MAX);
device->resetFences(1, &(*drawFences[currentImage]));
@ -732,6 +752,8 @@ void VulkanContext::NewFrame()
void VulkanContext::BeginRenderPass()
{
if (!IsValid())
return;
const vk::ClearValue clear_colors[] = { vk::ClearColorValue(std::array<float, 4>{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),
@ -740,6 +762,8 @@ void VulkanContext::BeginRenderPass()
void VulkanContext::EndFrame(const std::vector<vk::UniqueCommandBuffer> *cmdBuffers)
{
if (!IsValid())
return;
vk::CommandBuffer commandBuffer = *commandBuffers[currentImage];
commandBuffer.endRenderPass();
commandBuffer.end();
@ -756,7 +780,7 @@ void VulkanContext::EndFrame(const std::vector<vk::UniqueCommandBuffer> *cmdBuff
renderDone = true;
}
void VulkanContext::Present()
void VulkanContext::Present() noexcept
{
if (renderDone)
{
@ -764,18 +788,23 @@ void VulkanContext::Present()
DoSwapAutomation();
presentQueue.presentKHR(vk::PresentInfoKHR(1, &(*renderCompleteSemaphores[currentSemaphore]), 1, &(*swapChain), &currentImage));
currentSemaphore = (currentSemaphore + 1) % imageViews.size();
} catch (const vk::OutOfDateKHRError& e) {
// Sometimes happens when resizing the window
INFO_LOG(RENDERER, "vk::OutOfDateKHRError");
CreateSwapChain();
} catch (const vk::SystemError& e) {
// Happens when resizing the window
INFO_LOG(RENDERER, "vk::SystemError %s", e.what());
resized = true;
}
renderDone = false;
}
if (resized)
try {
CreateSwapChain();
} catch (const InvalidVulkanContext& err) {
}
}
void VulkanContext::DrawFrame(vk::ImageView imageView, vk::Offset2D extent)
void VulkanContext::DrawFrame(vk::ImageView imageView, vk::Extent2D extent)
{
float marginWidth = ((float)extent.y / extent.x * width / height - 1.f) / 2.f;
float marginWidth = ((float)extent.height / extent.width * width / height - 1.f) / 2.f;
QuadVertex vtx[] = {
{ { -1, -1, 0 }, { 0 - marginWidth, 0 } },
{ { 1, -1, 0 }, { 1 + marginWidth, 0 } },
@ -795,6 +824,31 @@ void VulkanContext::DrawFrame(vk::ImageView imageView, vk::Offset2D extent)
quadDrawer->Draw(commandBuffer, imageView, vtx);
}
void VulkanContext::WaitIdle() const
{
try {
graphicsQueue.waitIdle();
} catch (const vk::Error &err) {
WARN_LOG(RENDERER, "WaitIdle: %s", err.what());
}
}
std::string VulkanContext::GetDriverName() const
{
vk::PhysicalDeviceProperties props;
physicalDevice.getProperties(&props);
return std::string(props.deviceName);
}
std::string VulkanContext::GetDriverVersion() const
{
vk::PhysicalDeviceProperties props;
physicalDevice.getProperties(&props);
return std::to_string(VK_VERSION_MAJOR(props.driverVersion)) + "."
+ std::to_string(VK_VERSION_MINOR(props.driverVersion)) + "."
+ std::to_string(VK_VERSION_PATCH(props.driverVersion));
}
const std::vector<vk::UniqueCommandBuffer> *VulkanContext::PrepareVMUs()
{
return vmus->PrepareVMUs(*commandPools[GetCurrentImageIndex()]);
@ -802,36 +856,42 @@ const std::vector<vk::UniqueCommandBuffer> *VulkanContext::PrepareVMUs()
void VulkanContext::DrawVMUs(float scaling)
{
vmus->DrawVMUs(vk::Extent2D(width, height), scaling);
if (IsValid())
vmus->DrawVMUs(vk::Extent2D(width, height), scaling);
}
extern Renderer *renderer;
void VulkanContext::PresentFrame(vk::ImageView imageView, vk::Offset2D extent)
void VulkanContext::PresentFrame(vk::ImageView imageView, vk::Extent2D extent) noexcept
{
if (imageView)
{
NewFrame();
const std::vector<vk::UniqueCommandBuffer> *vmuCmdBuffers = nullptr;
if (settings.rend.FloatVMUs)
vmuCmdBuffers = PrepareVMUs();
BeginRenderPass();
DrawFrame(imageView, extent);
if (settings.rend.FloatVMUs)
DrawVMUs(gui_get_scaling());
renderer->DrawOSD(false);
EndFrame(vmuCmdBuffers);
}
lastFrameView = imageView;
lastFrameExtent = extent;
if (imageView && IsValid())
{
try {
NewFrame();
const std::vector<vk::UniqueCommandBuffer> *vmuCmdBuffers = nullptr;
if (settings.rend.FloatVMUs)
vmuCmdBuffers = PrepareVMUs();
BeginRenderPass();
if (lastFrameView) // Might have been nullified if swap chain recreated
DrawFrame(imageView, extent);
if (settings.rend.FloatVMUs)
DrawVMUs(gui_get_scaling());
renderer->DrawOSD(false);
EndFrame(vmuCmdBuffers);
} catch (const InvalidVulkanContext& err) {
}
}
}
void VulkanContext::PresentLastFrame()
{
if (lastFrameView)
if (lastFrameView && IsValid())
DrawFrame(lastFrameView, lastFrameExtent);
}
@ -840,7 +900,7 @@ void VulkanContext::Term()
lastFrameView = nullptr;
if (!device)
return;
device->waitIdle();
WaitIdle();
ImGui_ImplVulkan_Shutdown();
gui_term();
if (device && pipelineCache)
@ -875,6 +935,8 @@ void VulkanContext::Term()
allocator.Term();
#ifndef USE_SDL
surface.reset();
#else
::vkDestroySurfaceKHR(*instance, surface.release(), nullptr);
#endif
pipelineCache.reset();
device.reset();
@ -1011,3 +1073,46 @@ void VulkanContext::DoSwapAutomation()
}
#endif
}
bool VulkanContext::HasSurfaceDimensionChanged() const
{
vk::SurfaceCapabilitiesKHR surfaceCapabilities =
physicalDevice.getSurfaceCapabilitiesKHR(GetSurface());
VkExtent2D swapchainExtent;
if (surfaceCapabilities.currentExtent.width == std::numeric_limits < uint32_t > ::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;
}
return width != swapchainExtent.width || height != swapchainExtent.height;
}
void VulkanContext::SetWindowSize(u32 width, u32 height)
{
if (width != this->width || height != this->height)
{
this->width = width;
this->height = height;
// When the window is minimized, it can happen that the max surface dimension is 0,0
// In this case, the context becomes invalid but we keep the previous
// dimensions to not confuse the renderer and imgui
if (width != 0)
screen_width = width;
if (height != 0)
screen_height = height;
SetResized();
}
}

View File

@ -20,7 +20,7 @@
*/
#pragma once
#ifdef USE_VULKAN
#include <stdexcept>
#include "vulkan.h"
#include "vmallocator.h"
#include "quad.h"
@ -42,20 +42,18 @@ public:
~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<VkInstance>(instance.get()); }
u32 GetGraphicsQueueFamilyIndex() const { return graphicsQueueIndex; }
void SetWindowSize(u32 width, u32 height) { this->width = screen_width = width; this->height = screen_height = height; }
void SetResized() { resized = true; }
bool IsValid() { return width != 0 && height != 0; }
void NewFrame();
void BeginRenderPass();
void EndFrame(const std::vector<vk::UniqueCommandBuffer> *cmdBuffers = nullptr);
void Present();
void PresentFrame(vk::ImageView imageView, vk::Offset2D extent);
void Present() noexcept;
void PresentFrame(vk::ImageView imageView, vk::Extent2D extent) noexcept;
void PresentLastFrame();
vk::PhysicalDevice GetPhysicalDevice() const { return physicalDevice; }
@ -64,10 +62,10 @@ public:
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 }; }
vk::Extent2D GetViewPort() const { return { (u32)screen_width, (u32)screen_height }; }
size_t GetSwapChainSize() const { return imageViews.size(); }
int GetCurrentImageIndex() const { return currentImage; }
void WaitIdle() const { graphicsQueue.waitIdle(); }
void WaitIdle() const;
bool IsRendering() const { return rendering; }
vk::Queue GetGraphicsQueue() const { return graphicsQueue; }
vk::DeviceSize GetUniformBufferAlignment() const { return uniformBufferAlignment; }
@ -86,15 +84,8 @@ public:
return true;
}
}
std::string GetDriverName() const { vk::PhysicalDeviceProperties props; physicalDevice.getProperties(&props); return std::string(props.deviceName); }
std::string GetDriverVersion() const {
vk::PhysicalDeviceProperties props;
physicalDevice.getProperties(&props);
return std::to_string(VK_VERSION_MAJOR(props.driverVersion))
+ "." + std::to_string(VK_VERSION_MINOR(props.driverVersion))
+ "." + std::to_string(VK_VERSION_PATCH(props.driverVersion));
}
std::string GetDriverName() const;
std::string GetDriverVersion() const;
vk::Format GetColorFormat() const { return colorFormat; }
vk::Format GetDepthFormat() const { return depthFormat; }
static VulkanContext *Instance() { return contextInstance; }
@ -109,42 +100,30 @@ public:
const std::vector<vk::UniqueCommandBuffer> *PrepareVMUs();
void DrawVMUs(float scaling);
#ifdef VK_DEBUG
void setObjectName(u64 object, VkDebugReportObjectTypeEXT objectType, const std::string& name)
{
VkDebugMarkerObjectNameInfoEXT nameInfo = {};
nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT;
nameInfo.objectType = objectType;
nameInfo.object = object;
nameInfo.pObjectName = name.c_str();
vkDebugMarkerSetObjectNameEXT((VkDevice)*device, &nameInfo);
}
#endif
private:
void CreateSwapChain();
bool InitDevice();
bool InitInstance(const char** extensions, uint32_t extensions_count);
vk::Format FindDepthFormat();
void InitImgui();
void DoSwapAutomation();
void DrawFrame(vk::ImageView imageView, vk::Offset2D extent);
vk::SurfaceKHR GetSurface() {
#ifdef USE_SDL
return surface;
#else
return *surface;
#endif
}
void DrawFrame(vk::ImageView imageView, vk::Extent2D extent);
vk::SurfaceKHR GetSurface() const { return *surface; }
bool HasSurfaceDimensionChanged()
{
vk::SurfaceCapabilitiesKHR surfaceCapabilities = physicalDevice.getSurfaceCapabilitiesKHR(GetSurface());
VkExtent2D swapchainExtent;
if (surfaceCapabilities.currentExtent.width == std::numeric_limits<uint32_t>::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;
}
bool HasSurfaceDimensionChanged() const;
void SetWindowSize(u32 width, u32 height);
VMAllocator allocator;
void *window = nullptr;
@ -153,6 +132,7 @@ private:
bool renderDone = false;
u32 width = 0;
u32 height = 0;
bool resized = false;
vk::UniqueInstance instance;
vk::PhysicalDevice physicalDevice;
@ -172,11 +152,7 @@ private:
u32 vendorID = 0;
vk::UniqueDevice device;
#ifdef USE_SDL
vk::SurfaceKHR surface;
#else
vk::UniqueSurfaceKHR surface;
#endif
vk::UniqueSwapchainKHR swapChain;
std::vector<vk::UniqueImageView> imageViews;
@ -208,7 +184,7 @@ private:
std::unique_ptr<ShaderManager> shaderManager;
vk::ImageView lastFrameView;
vk::Offset2D lastFrameExtent;
vk::Extent2D lastFrameExtent;
std::unique_ptr<VulkanVMUs> vmus;
@ -222,4 +198,9 @@ private:
static VulkanContext *contextInstance;
};
class InvalidVulkanContext : public std::runtime_error {
public:
InvalidVulkanContext() : std::runtime_error("Invalid Vulkan context") {}
};
#endif // USE_VULKAN

View File

@ -55,17 +55,24 @@ public:
bool Render() override
{
Drawer *drawer;
if (pvrrc.isRTT)
drawer = &textureDrawer;
else
drawer = &screenDrawer;
try {
Drawer *drawer;
if (pvrrc.isRTT)
drawer = &textureDrawer;
else
drawer = &screenDrawer;
drawer->Draw(fogTexture.get(), paletteTexture.get());
drawer->Draw(fogTexture.get(), paletteTexture.get());
drawer->EndRenderPass();
drawer->EndRenderPass();
return !pvrrc.isRTT;
return !pvrrc.isRTT;
} catch (const vk::SystemError& e) {
// Sometimes happens when resizing the window
WARN_LOG(RENDERER, "Vulkan system error %s", e.what());
return false;
}
}
bool Present() override

View File

@ -65,7 +65,7 @@ public:
virtual void Term() override
{
GetContext()->PresentFrame(nullptr, vk::Offset2D());
GetContext()->PresentFrame(nullptr, vk::Extent2D());
osdBuffer.reset();
vjoyTexture.reset();
textureCache.Clear();
@ -147,40 +147,43 @@ public:
gui_display_osd();
if (!vjoyTexture)
return;
if (clear_screen)
{
GetContext()->NewFrame();
GetContext()->BeginRenderPass();
try {
if (clear_screen)
{
GetContext()->NewFrame();
GetContext()->BeginRenderPass();
}
const float dc2s_scale_h = screen_height / 480.0f;
const float sidebarWidth = (screen_width - dc2s_scale_h * 640.0f) / 2;
std::vector<OSDVertex> osdVertices = GetOSDVertices();
const float x1 = 2.0f / (screen_width / dc2s_scale_h);
const float y1 = 2.0f / 480;
const float x2 = 1 - 2 * sidebarWidth / screen_width;
const float y2 = 1;
for (OSDVertex& vtx : osdVertices)
{
vtx.x = vtx.x * x1 - x2;
vtx.y = vtx.y * y1 - y2;
}
const vk::CommandBuffer cmdBuffer = GetContext()->GetCurrentCommandBuffer();
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, osdPipeline.GetPipeline());
osdPipeline.BindDescriptorSets(cmdBuffer);
const vk::Viewport viewport(0, 0, (float)screen_width, (float)screen_height, 0, 1.f);
cmdBuffer.setViewport(0, 1, &viewport);
const vk::Rect2D scissor({ 0, 0 }, { (u32)screen_width, (u32)screen_height });
cmdBuffer.setScissor(0, 1, &scissor);
osdBuffer->upload(osdVertices.size() * sizeof(OSDVertex), osdVertices.data());
const vk::DeviceSize zero = 0;
cmdBuffer.bindVertexBuffers(0, 1, &osdBuffer->buffer.get(), &zero);
for (size_t i = 0; i < osdVertices.size(); i += 4)
cmdBuffer.draw(4, 1, i, 0);
if (clear_screen)
GetContext()->EndFrame();
} catch (const InvalidVulkanContext& err) {
}
const float dc2s_scale_h = screen_height / 480.0f;
const float sidebarWidth = (screen_width - dc2s_scale_h * 640.0f) / 2;
std::vector<OSDVertex> osdVertices = GetOSDVertices();
const float x1 = 2.0f / (screen_width / dc2s_scale_h);
const float y1 = 2.0f / 480;
const float x2 = 1 - 2 * sidebarWidth / screen_width;
const float y2 = 1;
for (OSDVertex& vtx : osdVertices)
{
vtx.x = vtx.x * x1 - x2;
vtx.y = vtx.y * y1 - y2;
}
const vk::CommandBuffer cmdBuffer = GetContext()->GetCurrentCommandBuffer();
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, osdPipeline.GetPipeline());
osdPipeline.BindDescriptorSets(cmdBuffer);
const vk::Viewport viewport(0, 0, (float)screen_width, (float)screen_height, 0, 1.f);
cmdBuffer.setViewport(0, 1, &viewport);
const vk::Rect2D scissor({ 0, 0 }, { (u32)screen_width, (u32)screen_height });
cmdBuffer.setScissor(0, 1, &scissor);
osdBuffer->upload(osdVertices.size() * sizeof(OSDVertex), osdVertices.data());
const vk::DeviceSize zero = 0;
cmdBuffer.bindVertexBuffers(0, 1, &osdBuffer->buffer.get(), &zero);
for (size_t i = 0; i < osdVertices.size(); i += 4)
cmdBuffer.draw(4, 1, i, 0);
if (clear_screen)
GetContext()->EndFrame();
}
protected:

View File

@ -145,6 +145,17 @@ void input_sdl_handle()
for (int i = 0; event.text.text[i] != '\0'; i++)
sdl_keyboard->keyboard_character(event.text.text[i]);
break;
#ifdef USE_VULKAN
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED
|| event.window.event == SDL_WINDOWEVENT_RESTORED
|| event.window.event == SDL_WINDOWEVENT_MINIMIZED
|| event.window.event == SDL_WINDOWEVENT_MAXIMIZED)
{
theVulkanContext.SetResized();
}
break;
#endif
#endif
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP: