mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-05 00:42:48 +00:00
presenter: render the previous frame to keep the render rendering
This commit is contained in:
parent
f423d151cf
commit
421acfcf4e
@ -131,7 +131,7 @@ class DebugStateImpl {
|
|||||||
friend class Core::Devtools::Widget::FrameGraph;
|
friend class Core::Devtools::Widget::FrameGraph;
|
||||||
friend class Core::Devtools::Widget::ShaderList;
|
friend class Core::Devtools::Widget::ShaderList;
|
||||||
|
|
||||||
static bool showing_debug_menu_bar;
|
bool showing_debug_menu_bar = false;
|
||||||
|
|
||||||
std::queue<std::string> debug_message_popup;
|
std::queue<std::string> debug_message_popup;
|
||||||
|
|
||||||
@ -155,6 +155,9 @@ class DebugStateImpl {
|
|||||||
std::vector<ShaderDump> shader_dump_list{};
|
std::vector<ShaderDump> shader_dump_list{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
float Framerate = 1.0f / 60.0f;
|
||||||
|
float FrameDeltaTime;
|
||||||
|
|
||||||
void ShowDebugMessage(std::string message) {
|
void ShowDebugMessage(std::string message) {
|
||||||
if (message.empty()) {
|
if (message.empty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -24,8 +24,6 @@ using namespace ImGui;
|
|||||||
using namespace Core::Devtools;
|
using namespace Core::Devtools;
|
||||||
using L = Core::Devtools::Layer;
|
using L = Core::Devtools::Layer;
|
||||||
|
|
||||||
bool DebugStateType::DebugStateImpl::showing_debug_menu_bar = false;
|
|
||||||
|
|
||||||
static bool show_simple_fps = false;
|
static bool show_simple_fps = false;
|
||||||
static bool visibility_toggled = false;
|
static bool visibility_toggled = false;
|
||||||
|
|
||||||
@ -254,8 +252,8 @@ void L::DrawAdvanced() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void L::DrawSimple() {
|
void L::DrawSimple() {
|
||||||
const auto io = GetIO();
|
const float frameRate = DebugState.Framerate;
|
||||||
Text("%.1f FPS (%.2f ms)", io.Framerate, 1000.0f / io.Framerate);
|
Text("%d FPS (%.1f ms)", static_cast<int>(std::round(1.0f / frameRate)), frameRate * 1000.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LoadSettings(const char* line) {
|
static void LoadSettings(const char* line) {
|
||||||
@ -337,7 +335,7 @@ void L::Draw() {
|
|||||||
|
|
||||||
if (!DebugState.IsGuestThreadsPaused()) {
|
if (!DebugState.IsGuestThreadsPaused()) {
|
||||||
const auto fn = DebugState.flip_frame_count.load();
|
const auto fn = DebugState.flip_frame_count.load();
|
||||||
frame_graph.AddFrame(fn, io.DeltaTime);
|
frame_graph.AddFrame(fn, DebugState.FrameDeltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsKeyPressed(ImGuiKey_F10, false)) {
|
if (IsKeyPressed(ImGuiKey_F10, false)) {
|
||||||
|
@ -83,9 +83,6 @@ void FrameGraph::Draw() {
|
|||||||
|
|
||||||
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
||||||
|
|
||||||
static float deltaTime;
|
|
||||||
static float frameRate;
|
|
||||||
|
|
||||||
if (!isSystemPaused) {
|
if (!isSystemPaused) {
|
||||||
deltaTime = io.DeltaTime * 1000.0f;
|
deltaTime = io.DeltaTime * 1000.0f;
|
||||||
frameRate = 1000.0f / deltaTime;
|
frameRate = 1000.0f / deltaTime;
|
||||||
|
@ -16,6 +16,9 @@ class FrameGraph {
|
|||||||
|
|
||||||
std::array<FrameInfo, FRAME_BUFFER_SIZE> frame_list{};
|
std::array<FrameInfo, FRAME_BUFFER_SIZE> frame_list{};
|
||||||
|
|
||||||
|
float deltaTime{};
|
||||||
|
float frameRate{};
|
||||||
|
|
||||||
void DrawFrameGraph();
|
void DrawFrameGraph();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
@ -207,6 +205,13 @@ void VideoOutDriver::DrawBlankFrame() {
|
|||||||
presenter->Present(empty_frame);
|
presenter->Present(empty_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VideoOutDriver::DrawLastFrame() {
|
||||||
|
const auto frame = presenter->PrepareLastFrame();
|
||||||
|
if (frame != nullptr) {
|
||||||
|
presenter->Present(frame, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
|
bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
|
||||||
bool is_eop /*= false*/) {
|
bool is_eop /*= false*/) {
|
||||||
{
|
{
|
||||||
@ -278,17 +283,24 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
|
|||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
auto delay = std::chrono::microseconds{0};
|
|
||||||
while (!token.stop_requested()) {
|
while (!token.stop_requested()) {
|
||||||
timer.Start();
|
timer.Start();
|
||||||
|
|
||||||
|
if (DebugState.IsGuestThreadsPaused()) {
|
||||||
|
DrawLastFrame();
|
||||||
|
timer.End();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if it's time to take a request.
|
// Check if it's time to take a request.
|
||||||
auto& vblank_status = main_port.vblank_status;
|
auto& vblank_status = main_port.vblank_status;
|
||||||
if (vblank_status.count % (main_port.flip_rate + 1) == 0) {
|
if (vblank_status.count % (main_port.flip_rate + 1) == 0) {
|
||||||
const auto request = receive_request();
|
const auto request = receive_request();
|
||||||
if (!request) {
|
if (!request) {
|
||||||
if (!main_port.is_open || DebugState.IsGuestThreadsPaused()) {
|
if (!main_port.is_open) {
|
||||||
DrawBlankFrame();
|
DrawBlankFrame();
|
||||||
|
} else {
|
||||||
|
DrawLastFrame();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Flip(request);
|
Flip(request);
|
||||||
|
@ -102,7 +102,8 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
void Flip(const Request& req);
|
void Flip(const Request& req);
|
||||||
void DrawBlankFrame(); // Used when there is no flip request to keep ImGui up to date
|
void DrawBlankFrame(); // Video port out not open
|
||||||
|
void DrawLastFrame(); // Used when there is no flip request
|
||||||
void SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false);
|
void SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false);
|
||||||
void PresentThread(std::stop_token token);
|
void PresentThread(std::stop_token token);
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ bool ProcessEvent(SDL_Event* event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiID NewFrame() {
|
ImGuiID NewFrame(bool is_reusing_frame) {
|
||||||
{
|
{
|
||||||
std::scoped_lock lock{change_layers_mutex};
|
std::scoped_lock lock{change_layers_mutex};
|
||||||
while (!change_layers.empty()) {
|
while (!change_layers.empty()) {
|
||||||
@ -183,7 +183,7 @@ ImGuiID NewFrame() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Sdl::NewFrame();
|
Sdl::NewFrame(is_reusing_frame);
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
ImGuiWindowFlags flags = ImGuiDockNodeFlags_PassthruCentralNode;
|
ImGuiWindowFlags flags = ImGuiDockNodeFlags_PassthruCentralNode;
|
||||||
|
@ -26,7 +26,7 @@ void Shutdown(const vk::Device& device);
|
|||||||
|
|
||||||
bool ProcessEvent(SDL_Event* event);
|
bool ProcessEvent(SDL_Event* event);
|
||||||
|
|
||||||
ImGuiID NewFrame();
|
ImGuiID NewFrame(bool is_reusing_frame = false);
|
||||||
|
|
||||||
void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view,
|
void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view,
|
||||||
const vk::Extent2D& extent);
|
const vk::Extent2D& extent);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
|
#include "core/debug_state.h"
|
||||||
#include "imgui_impl_sdl3.h"
|
#include "imgui_impl_sdl3.h"
|
||||||
|
|
||||||
// SDL
|
// SDL
|
||||||
@ -26,6 +27,7 @@ struct SdlData {
|
|||||||
SDL_Window* window{};
|
SDL_Window* window{};
|
||||||
SDL_WindowID window_id{};
|
SDL_WindowID window_id{};
|
||||||
Uint64 time{};
|
Uint64 time{};
|
||||||
|
Uint64 nonReusedtime{};
|
||||||
const char* clipboard_text_data{};
|
const char* clipboard_text_data{};
|
||||||
|
|
||||||
// IME handling
|
// IME handling
|
||||||
@ -785,7 +787,7 @@ static void UpdateGamepads() {
|
|||||||
+thumb_dead_zone, +32767);
|
+thumb_dead_zone, +32767);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewFrame() {
|
void NewFrame(bool is_reusing_frame) {
|
||||||
SdlData* bd = GetBackendData();
|
SdlData* bd = GetBackendData();
|
||||||
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
@ -798,9 +800,26 @@ void NewFrame() {
|
|||||||
if (current_time <= bd->time)
|
if (current_time <= bd->time)
|
||||||
current_time = bd->time + 1;
|
current_time = bd->time + 1;
|
||||||
io.DeltaTime = bd->time > 0 ? (float)((double)(current_time - bd->time) / (double)frequency)
|
io.DeltaTime = bd->time > 0 ? (float)((double)(current_time - bd->time) / (double)frequency)
|
||||||
: (float)(1.0f / 60.0f);
|
: 1.0f / 60.0f;
|
||||||
bd->time = current_time;
|
bd->time = current_time;
|
||||||
|
|
||||||
|
if (!is_reusing_frame) {
|
||||||
|
if (current_time <= bd->nonReusedtime)
|
||||||
|
current_time = bd->nonReusedtime + 1;
|
||||||
|
float deltaTime =
|
||||||
|
bd->nonReusedtime > 0
|
||||||
|
? (float)((double)(current_time - bd->nonReusedtime) / (double)frequency)
|
||||||
|
: 1.0f / 60.0f;
|
||||||
|
bd->nonReusedtime = current_time;
|
||||||
|
DebugState.FrameDeltaTime = deltaTime;
|
||||||
|
float distribution = 0.016f / deltaTime / 10.0f;
|
||||||
|
if (distribution > 1.0f) {
|
||||||
|
distribution = 1.0f;
|
||||||
|
}
|
||||||
|
DebugState.Framerate =
|
||||||
|
deltaTime * distribution + DebugState.Framerate * (1.0f - distribution);
|
||||||
|
}
|
||||||
|
|
||||||
if (bd->mouse_pending_leave_frame && bd->mouse_pending_leave_frame >= ImGui::GetFrameCount() &&
|
if (bd->mouse_pending_leave_frame && bd->mouse_pending_leave_frame >= ImGui::GetFrameCount() &&
|
||||||
bd->mouse_buttons_down == 0) {
|
bd->mouse_buttons_down == 0) {
|
||||||
bd->mouse_window_id = 0;
|
bd->mouse_window_id = 0;
|
||||||
|
@ -14,7 +14,7 @@ namespace ImGui::Sdl {
|
|||||||
|
|
||||||
bool Init(SDL_Window* window);
|
bool Init(SDL_Window* window);
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
void NewFrame();
|
void NewFrame(bool is_reusing);
|
||||||
bool ProcessEvent(const SDL_Event* event);
|
bool ProcessEvent(const SDL_Event* event);
|
||||||
void OnResize();
|
void OnResize();
|
||||||
|
|
||||||
|
@ -383,8 +383,8 @@ bool Instance::CreateDevice() {
|
|||||||
.extendedDynamicState = true,
|
.extendedDynamicState = true,
|
||||||
},
|
},
|
||||||
vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{
|
vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{
|
||||||
.extendedDynamicState3ColorWriteMask = true,
|
|
||||||
.extendedDynamicState3ColorBlendEnable = true,
|
.extendedDynamicState3ColorBlendEnable = true,
|
||||||
|
.extendedDynamicState3ColorWriteMask = true,
|
||||||
},
|
},
|
||||||
vk::PhysicalDeviceDepthClipControlFeaturesEXT{
|
vk::PhysicalDeviceDepthClipControlFeaturesEXT{
|
||||||
.depthClipControl = true,
|
.depthClipControl = true,
|
||||||
|
@ -411,6 +411,61 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) {
|
|||||||
frame->imgui_texture->disable_blend = true;
|
frame->imgui_texture->disable_blend = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Frame* Presenter::PrepareLastFrame() {
|
||||||
|
if (last_submit_frame == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame* frame = last_submit_frame;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
vk::Result result = instance.GetDevice().waitForFences(frame->present_done, false,
|
||||||
|
std::numeric_limits<u64>::max());
|
||||||
|
if (result == vk::Result::eSuccess) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (result == vk::Result::eTimeout) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ASSERT_MSG(result != vk::Result::eErrorDeviceLost,
|
||||||
|
"Device lost during waiting for a frame");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& scheduler = flip_scheduler;
|
||||||
|
scheduler.EndRendering();
|
||||||
|
const auto cmdbuf = scheduler.CommandBuffer();
|
||||||
|
|
||||||
|
const auto frame_subresources = vk::ImageSubresourceRange{
|
||||||
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto pre_barrier =
|
||||||
|
vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
|
||||||
|
.srcAccessMask = vk::AccessFlagBits2::eColorAttachmentRead,
|
||||||
|
.dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
|
||||||
|
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
|
||||||
|
.oldLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
|
.newLayout = vk::ImageLayout::eGeneral,
|
||||||
|
.image = frame->image,
|
||||||
|
.subresourceRange{frame_subresources}};
|
||||||
|
|
||||||
|
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
|
||||||
|
.imageMemoryBarrierCount = 1,
|
||||||
|
.pImageMemoryBarriers = &pre_barrier,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Flush frame creation commands.
|
||||||
|
frame->ready_semaphore = scheduler.GetMasterSemaphore()->Handle();
|
||||||
|
frame->ready_tick = scheduler.CurrentTick();
|
||||||
|
SubmitInfo info{};
|
||||||
|
scheduler.Flush(info);
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) {
|
bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) {
|
||||||
const auto* splash = Common::Singleton<Splash>::Instance();
|
const auto* splash = Common::Singleton<Splash>::Instance();
|
||||||
if (splash->GetImageData().empty()) {
|
if (splash->GetImageData().empty()) {
|
||||||
@ -529,8 +584,8 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop)
|
|||||||
};
|
};
|
||||||
|
|
||||||
const auto pre_barrier =
|
const auto pre_barrier =
|
||||||
vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
|
||||||
.srcAccessMask = vk::AccessFlagBits2::eTransferRead,
|
.srcAccessMask = vk::AccessFlagBits2::eColorAttachmentRead,
|
||||||
.dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
|
.dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
|
||||||
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
|
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
|
||||||
.oldLayout = vk::ImageLayout::eUndefined,
|
.oldLayout = vk::ImageLayout::eUndefined,
|
||||||
@ -641,12 +696,15 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop)
|
|||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Presenter::Present(Frame* frame) {
|
void Presenter::Present(Frame* frame, bool is_reusing_frame) {
|
||||||
// Free the frame for reuse
|
// Free the frame for reuse
|
||||||
const auto free_frame = [&] {
|
const auto free_frame = [&] {
|
||||||
std::scoped_lock fl{free_mutex};
|
if (!is_reusing_frame) {
|
||||||
free_queue.push(frame);
|
last_submit_frame = frame;
|
||||||
free_cv.notify_one();
|
std::scoped_lock fl{free_mutex};
|
||||||
|
free_queue.push(frame);
|
||||||
|
free_cv.notify_one();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Recreate the swapchain if the window was resized.
|
// Recreate the swapchain if the window was resized.
|
||||||
@ -669,7 +727,7 @@ void Presenter::Present(Frame* frame) {
|
|||||||
// the frame's present fence and future GetRenderFrame() call will hang waiting for this frame.
|
// the frame's present fence and future GetRenderFrame() call will hang waiting for this frame.
|
||||||
instance.GetDevice().resetFences(frame->present_done);
|
instance.GetDevice().resetFences(frame->present_done);
|
||||||
|
|
||||||
ImGuiID dockId = ImGui::Core::NewFrame();
|
ImGuiID dockId = ImGui::Core::NewFrame(is_reusing_frame);
|
||||||
|
|
||||||
const vk::Image swapchain_image = swapchain.Image();
|
const vk::Image swapchain_image = swapchain.Image();
|
||||||
const vk::ImageView swapchain_image_view = swapchain.ImageView();
|
const vk::ImageView swapchain_image_view = swapchain.ImageView();
|
||||||
@ -744,13 +802,13 @@ void Presenter::Present(Frame* frame) {
|
|||||||
ImGui::SetNextWindowDockID(dockId, ImGuiCond_Once);
|
ImGui::SetNextWindowDockID(dockId, ImGuiCond_Once);
|
||||||
ImGui::Begin("Display##game_display", nullptr, ImGuiWindowFlags_NoNav);
|
ImGui::Begin("Display##game_display", nullptr, ImGuiWindowFlags_NoNav);
|
||||||
|
|
||||||
ImVec2 contentArea = ImGui::GetContentRegionMax();
|
ImVec2 contentArea = ImGui::GetContentRegionAvail();
|
||||||
const vk::Rect2D imgRect =
|
const vk::Rect2D imgRect =
|
||||||
FitImage(frame->width, frame->height, (s32)contentArea.x, (s32)contentArea.y);
|
FitImage(frame->width, frame->height, (s32)contentArea.x, (s32)contentArea.y);
|
||||||
ImGui::SetCursorPos({
|
ImGui::SetCursorPos(ImGui::GetCursorStartPos() + ImVec2{
|
||||||
(float)imgRect.offset.x,
|
(float)imgRect.offset.x,
|
||||||
(float)imgRect.offset.y,
|
(float)imgRect.offset.y,
|
||||||
});
|
});
|
||||||
ImGui::Image(frame->imgui_texture, {
|
ImGui::Image(frame->imgui_texture, {
|
||||||
static_cast<float>(imgRect.extent.width),
|
static_cast<float>(imgRect.extent.width),
|
||||||
static_cast<float>(imgRect.extent.height),
|
static_cast<float>(imgRect.extent.height),
|
||||||
@ -784,7 +842,9 @@ void Presenter::Present(Frame* frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
free_frame();
|
free_frame();
|
||||||
DebugState.IncFlipFrameNum();
|
if (!is_reusing_frame) {
|
||||||
|
DebugState.IncFlipFrameNum();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Frame* Presenter::GetRenderFrame() {
|
Frame* Presenter::GetRenderFrame() {
|
||||||
|
@ -89,8 +89,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ShowSplash(Frame* frame = nullptr);
|
bool ShowSplash(Frame* frame = nullptr);
|
||||||
void Present(Frame* frame);
|
void Present(Frame* frame, bool is_reusing_frame = false);
|
||||||
void RecreateFrame(Frame* frame, u32 width, u32 height);
|
void RecreateFrame(Frame* frame, u32 width, u32 height);
|
||||||
|
Frame* PrepareLastFrame();
|
||||||
|
|
||||||
void FlushDraw() {
|
void FlushDraw() {
|
||||||
SubmitInfo info{};
|
SubmitInfo info{};
|
||||||
@ -124,6 +125,7 @@ private:
|
|||||||
vk::UniqueCommandPool command_pool;
|
vk::UniqueCommandPool command_pool;
|
||||||
std::vector<Frame> present_frames;
|
std::vector<Frame> present_frames;
|
||||||
std::queue<Frame*> free_queue;
|
std::queue<Frame*> free_queue;
|
||||||
|
Frame* last_submit_frame;
|
||||||
std::mutex free_mutex;
|
std::mutex free_mutex;
|
||||||
std::condition_variable free_cv;
|
std::condition_variable free_cv;
|
||||||
std::condition_variable_any frame_cv;
|
std::condition_variable_any frame_cv;
|
||||||
|
Loading…
Reference in New Issue
Block a user