2023-08-01 02:35:41 +02:00
|
|
|
// Copyright 2023 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2023-09-13 00:28:50 +02:00
|
|
|
#include "common/logging/log.h"
|
2023-08-01 02:35:41 +02:00
|
|
|
#include "video_core/renderer_opengl/gl_state.h"
|
|
|
|
#include "video_core/renderer_opengl/gl_texture_mailbox.h"
|
|
|
|
|
|
|
|
namespace OpenGL {
|
|
|
|
|
|
|
|
OGLTextureMailbox::OGLTextureMailbox(bool has_debug_tool_) : has_debug_tool{has_debug_tool_} {
|
|
|
|
for (auto& frame : swap_chain) {
|
|
|
|
free_queue.push(&frame);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
OGLTextureMailbox::~OGLTextureMailbox() {
|
|
|
|
// Lock the mutex and clear out the present and free_queues and notify any people who are
|
|
|
|
// blocked to prevent deadlock on shutdown
|
|
|
|
std::scoped_lock lock(swap_chain_lock);
|
|
|
|
free_queue = {};
|
|
|
|
present_queue.clear();
|
|
|
|
present_cv.notify_all();
|
|
|
|
free_cv.notify_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
void OGLTextureMailbox::ReloadPresentFrame(Frontend::Frame* frame, u32 height, u32 width) {
|
|
|
|
frame->present.Release();
|
|
|
|
frame->present.Create();
|
|
|
|
GLint previous_draw_fbo{};
|
|
|
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
|
|
|
frame->color.handle);
|
|
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
|
|
|
LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
|
|
|
|
}
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
|
|
|
|
frame->color_reloaded = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OGLTextureMailbox::ReloadRenderFrame(Frontend::Frame* frame, u32 width, u32 height) {
|
|
|
|
OpenGLState prev_state = OpenGLState::GetCurState();
|
|
|
|
OpenGLState state = OpenGLState::GetCurState();
|
|
|
|
|
|
|
|
// Recreate the color texture attachment
|
|
|
|
frame->color.Release();
|
|
|
|
frame->color.Create();
|
|
|
|
state.renderbuffer = frame->color.handle;
|
|
|
|
state.Apply();
|
|
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
|
|
|
|
|
|
|
|
// Recreate the FBO for the render target
|
|
|
|
frame->render.Release();
|
|
|
|
frame->render.Create();
|
|
|
|
state.draw.read_framebuffer = frame->render.handle;
|
|
|
|
state.draw.draw_framebuffer = frame->render.handle;
|
|
|
|
state.Apply();
|
|
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
|
|
|
frame->color.handle);
|
|
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
|
|
|
LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
|
|
|
|
}
|
|
|
|
prev_state.Apply();
|
|
|
|
frame->width = width;
|
|
|
|
frame->height = height;
|
|
|
|
frame->color_reloaded = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Frontend::Frame* OGLTextureMailbox::GetRenderFrame() {
|
|
|
|
std::unique_lock lock{swap_chain_lock};
|
|
|
|
|
|
|
|
// If theres no free frames, we will reuse the oldest render frame
|
|
|
|
if (free_queue.empty()) {
|
|
|
|
auto frame = present_queue.back();
|
|
|
|
present_queue.pop_back();
|
|
|
|
return frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
Frontend::Frame* frame = free_queue.front();
|
|
|
|
free_queue.pop();
|
|
|
|
return frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OGLTextureMailbox::ReleaseRenderFrame(Frontend::Frame* frame) {
|
|
|
|
std::unique_lock lock{swap_chain_lock};
|
|
|
|
present_queue.push_front(frame);
|
|
|
|
present_cv.notify_one();
|
|
|
|
|
|
|
|
DebugNotifyNextFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
void OGLTextureMailbox::LoadPresentFrame() {
|
|
|
|
// Free the previous frame and add it back to the free queue
|
|
|
|
if (previous_frame) {
|
|
|
|
free_queue.push(previous_frame);
|
|
|
|
free_cv.notify_one();
|
|
|
|
}
|
|
|
|
|
|
|
|
// The newest entries are pushed to the front of the queue
|
|
|
|
Frontend::Frame* frame = present_queue.front();
|
|
|
|
present_queue.pop_front();
|
|
|
|
// Remove all old entries from the present queue and move them back to the free_queue
|
|
|
|
for (auto f : present_queue) {
|
|
|
|
free_queue.push(f);
|
|
|
|
}
|
|
|
|
present_queue.clear();
|
|
|
|
previous_frame = frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
Frontend::Frame* OGLTextureMailbox::TryGetPresentFrame(int timeout_ms) {
|
|
|
|
DebugWaitForNextFrame();
|
|
|
|
|
|
|
|
std::unique_lock lock{swap_chain_lock};
|
|
|
|
// Wait for new entries in the present_queue
|
|
|
|
present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
|
|
|
|
[&] { return !present_queue.empty(); });
|
|
|
|
if (present_queue.empty()) {
|
|
|
|
// Timed out waiting for a frame to draw so return the previous frame
|
|
|
|
return previous_frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
LoadPresentFrame();
|
|
|
|
return previous_frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OGLTextureMailbox::DebugNotifyNextFrame() {
|
|
|
|
if (!has_debug_tool) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
frame_for_debug++;
|
|
|
|
std::scoped_lock lock{debug_synch_mutex};
|
|
|
|
debug_synch_condition.notify_one();
|
|
|
|
}
|
|
|
|
|
|
|
|
void OGLTextureMailbox::DebugWaitForNextFrame() {
|
|
|
|
if (!has_debug_tool) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const int last_frame = frame_for_debug;
|
|
|
|
std::unique_lock lock{debug_synch_mutex};
|
|
|
|
debug_synch_condition.wait(lock, [this, last_frame] { return frame_for_debug > last_frame; });
|
|
|
|
}
|
|
|
|
|
|
|
|
Frontend::Frame* OGLVideoDumpingMailbox::GetRenderFrame() {
|
|
|
|
std::unique_lock lock{swap_chain_lock};
|
|
|
|
|
|
|
|
// If theres no free frames, we will wait until one shows up
|
|
|
|
if (free_queue.empty()) {
|
|
|
|
free_cv.wait(lock, [&] { return (!free_queue.empty() || quit); });
|
|
|
|
if (quit) {
|
|
|
|
throw OGLTextureMailboxException("VideoDumpingMailbox quitting");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (free_queue.empty()) {
|
|
|
|
LOG_CRITICAL(Render_OpenGL, "Could not get free frame");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Frontend::Frame* frame = free_queue.front();
|
|
|
|
free_queue.pop();
|
|
|
|
return frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OGLVideoDumpingMailbox::LoadPresentFrame() {
|
|
|
|
// Free the previous frame and add it back to the free queue
|
|
|
|
if (previous_frame) {
|
|
|
|
free_queue.push(previous_frame);
|
|
|
|
free_cv.notify_one();
|
|
|
|
}
|
|
|
|
|
|
|
|
Frontend::Frame* frame = present_queue.back();
|
|
|
|
present_queue.pop_back();
|
|
|
|
previous_frame = frame;
|
|
|
|
|
|
|
|
// Do not remove entries from the present_queue, as video dumping would require
|
|
|
|
// that we preserve all frames
|
|
|
|
}
|
|
|
|
|
|
|
|
Frontend::Frame* OGLVideoDumpingMailbox::TryGetPresentFrame(int timeout_ms) {
|
|
|
|
std::unique_lock lock{swap_chain_lock};
|
|
|
|
// Wait for new entries in the present_queue
|
|
|
|
present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
|
|
|
|
[&] { return !present_queue.empty(); });
|
|
|
|
if (present_queue.empty()) {
|
|
|
|
// Timed out waiting for a frame
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
LoadPresentFrame();
|
|
|
|
return previous_frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace OpenGL
|