1
1
Fork 0
forked from suyu/suyu

Merge pull request #11656 from liamwhite/recreate-surface-automatically

vk_present_manager: recreate surface on any surface loss
This commit is contained in:
liamwhite 2023-10-07 12:49:54 -04:00 committed by GitHub
commit bd42bba71c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 33 additions and 49 deletions

View file

@ -218,7 +218,6 @@ public:
return;
}
m_window->OnSurfaceChanged(m_native_window);
m_system.Renderer().NotifySurfaceChanged();
}
void ConfigureFilesystemProvider(const std::string& filepath) {

View file

@ -89,9 +89,6 @@ public:
void RequestScreenshot(void* data, std::function<void(bool)> callback,
const Layout::FramebufferLayout& layout);
/// This is called to notify the rendering backend of a surface change
virtual void NotifySurfaceChanged() {}
protected:
Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
std::unique_ptr<Core::Frontend::GraphicsContext> context;

View file

@ -56,10 +56,6 @@ public:
return device.GetDriverName();
}
void NotifySurfaceChanged() override {
present_manager.NotifySurfaceChanged();
}
private:
void Report() const;

View file

@ -103,8 +103,7 @@ PresentManager::PresentManager(const vk::Instance& instance_,
surface{surface_}, blit_supported{CanBlitToSwapchain(device.GetPhysical(),
swapchain.GetImageViewFormat())},
use_present_thread{Settings::values.async_presentation.GetValue()},
image_count{swapchain.GetImageCount()}, last_render_surface{
render_window_.GetWindowInfo().render_surface} {
image_count{swapchain.GetImageCount()} {
auto& dld = device.GetLogical();
cmdpool = dld.CreateCommandPool({
@ -289,44 +288,36 @@ void PresentManager::PresentThread(std::stop_token token) {
}
}
void PresentManager::NotifySurfaceChanged() {
#ifdef ANDROID
std::scoped_lock lock{recreate_surface_mutex};
recreate_surface_cv.notify_one();
#endif
void PresentManager::RecreateSwapchain(Frame* frame) {
swapchain.Create(*surface, frame->width, frame->height, frame->is_srgb);
image_count = swapchain.GetImageCount();
}
void PresentManager::CopyToSwapchain(Frame* frame) {
MICROPROFILE_SCOPE(Vulkan_CopyToSwapchain);
bool requires_recreation = false;
const auto recreate_swapchain = [&] {
swapchain.Create(*surface, frame->width, frame->height, frame->is_srgb);
image_count = swapchain.GetImageCount();
};
#ifdef ANDROID
std::unique_lock lock{recreate_surface_mutex};
const auto needs_recreation = [&] {
if (last_render_surface != render_window.GetWindowInfo().render_surface) {
return true;
}
if (swapchain.NeedsRecreation(frame->is_srgb)) {
return true;
}
return false;
};
recreate_surface_cv.wait_for(lock, std::chrono::milliseconds(400),
[&]() { return !needs_recreation(); });
// If the frontend recreated the surface, recreate the renderer surface and swapchain.
if (last_render_surface != render_window.GetWindowInfo().render_surface) {
last_render_surface = render_window.GetWindowInfo().render_surface;
while (true) {
try {
// Recreate surface and swapchain if needed.
if (requires_recreation) {
surface = CreateSurface(instance, render_window.GetWindowInfo());
recreate_swapchain();
RecreateSwapchain(frame);
}
#endif
// Draw to swapchain.
return CopyToSwapchainImpl(frame);
} catch (const vk::Exception& except) {
if (except.GetResult() != VK_ERROR_SURFACE_LOST_KHR) {
throw;
}
requires_recreation = true;
}
}
}
void PresentManager::CopyToSwapchainImpl(Frame* frame) {
MICROPROFILE_SCOPE(Vulkan_CopyToSwapchain);
// If the size or colorspace of the incoming frames has changed, recreate the swapchain
// to account for that.
@ -334,11 +325,11 @@ void PresentManager::CopyToSwapchain(Frame* frame) {
const bool size_changed =
swapchain.GetWidth() != frame->width || swapchain.GetHeight() != frame->height;
if (srgb_changed || size_changed) {
recreate_swapchain();
RecreateSwapchain(frame);
}
while (swapchain.AcquireNextImage()) {
recreate_swapchain();
RecreateSwapchain(frame);
}
const vk::CommandBuffer cmdbuf{frame->cmdbuf};

View file

@ -54,14 +54,15 @@ public:
/// Waits for the present thread to finish presenting all queued frames.
void WaitPresent();
/// This is called to notify the rendering backend of a surface change
void NotifySurfaceChanged();
private:
void PresentThread(std::stop_token token);
void CopyToSwapchain(Frame* frame);
void CopyToSwapchainImpl(Frame* frame);
void RecreateSwapchain(Frame* frame);
private:
const vk::Instance& instance;
Core::Frontend::EmuWindow& render_window;
@ -76,16 +77,13 @@ private:
std::queue<Frame*> free_queue;
std::condition_variable_any frame_cv;
std::condition_variable free_cv;
std::condition_variable recreate_surface_cv;
std::mutex swapchain_mutex;
std::mutex recreate_surface_mutex;
std::mutex queue_mutex;
std::mutex free_mutex;
std::jthread present_thread;
bool blit_supported;
bool use_present_thread;
std::size_t image_count{};
void* last_render_surface{};
};
} // namespace Vulkan

View file

@ -117,6 +117,9 @@ public:
virtual ~Exception() = default;
const char* what() const noexcept override;
VkResult GetResult() const noexcept {
return result;
}
private:
VkResult result;