citra_qt, video_core: Screenshot functionality

Allows capturing screenshot at the current internal resolution (native for software renderer), but a setting is available to capture it in other resolutions. The screenshot is saved to a single PNG in the current layout.
This commit is contained in:
zhupengfei 2018-08-31 14:16:16 +08:00 committed by zhupengfei
parent 7e90abec78
commit 071b41cb61
No known key found for this signature in database
GPG key ID: DD129E108BD09378
14 changed files with 202 additions and 14 deletions

View file

@ -24,7 +24,6 @@
#include "common/vector_math.h"
#include "core/frontend/emu_window.h"
#include "core/memory.h"
#include "core/settings.h"
#include "video_core/pica_state.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
@ -78,12 +77,6 @@ constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
return boost::make_iterator_range(map.equal_range(interval));
}
static u16 GetResolutionScaleFactor() {
return !Settings::values.resolution_factor
? VideoCore::g_renderer->GetRenderWindow().GetFramebufferLayout().GetScalingRatio()
: Settings::values.resolution_factor;
}
template <bool morton_to_gl, PixelFormat format>
static void MortonCopyTile(u32 stride, u8* tile_buffer, u8* gl_buffer) {
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8;
@ -1360,9 +1353,9 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
const auto& config = regs.framebuffer.framebuffer;
// update resolution_scale_factor and reset cache if changed
static u16 resolution_scale_factor = GetResolutionScaleFactor();
if (resolution_scale_factor != GetResolutionScaleFactor()) {
resolution_scale_factor = GetResolutionScaleFactor();
static u16 resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
if (resolution_scale_factor != VideoCore::GetResolutionScaleFactor()) {
resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
FlushAll();
while (!surface_cache.empty())
UnregisterSurface(*surface_cache.begin()->second.begin());

View file

@ -140,7 +140,39 @@ void RendererOpenGL::SwapBuffers() {
}
}
DrawScreens();
if (VideoCore::g_renderer_screenshot_requested) {
// Draw this frame to the screenshot framebuffer
screenshot_framebuffer.Create();
GLuint old_read_fb = state.draw.read_framebuffer;
GLuint old_draw_fb = state.draw.draw_framebuffer;
state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle;
state.Apply();
Layout::FramebufferLayout layout{VideoCore::g_screenshot_framebuffer_layout};
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, layout.width, layout.height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
renderbuffer);
DrawScreens(layout);
glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
VideoCore::g_screenshot_bits);
screenshot_framebuffer.Release();
state.draw.read_framebuffer = old_read_fb;
state.draw.draw_framebuffer = old_draw_fb;
state.Apply();
glDeleteRenderbuffers(1, &renderbuffer);
VideoCore::g_screenshot_complete_callback();
VideoCore::g_renderer_screenshot_requested = false;
}
DrawScreens(render_window.GetFramebufferLayout());
Core::System::GetInstance().perf_stats.EndSystemFrame();
@ -386,14 +418,13 @@ void RendererOpenGL::DrawSingleScreenRotated(const ScreenInfo& screen_info, floa
/**
* Draws the emulated screens to the emulator window.
*/
void RendererOpenGL::DrawScreens() {
void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout) {
if (VideoCore::g_renderer_bg_color_update_requested.exchange(false)) {
// Update background color before drawing
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
0.0f);
}
auto layout = render_window.GetFramebufferLayout();
const auto& top_screen = layout.top_screen;
const auto& bottom_screen = layout.bottom_screen;

View file

@ -13,6 +13,10 @@
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_state.h"
namespace Layout {
class FramebufferLayout;
}
namespace OpenGL {
/// Structure used for storing information about the textures for each 3DS screen
@ -50,7 +54,7 @@ private:
void InitOpenGLObjects();
void ConfigureFramebufferTexture(TextureInfo& texture,
const GPU::Regs::FramebufferConfig& framebuffer);
void DrawScreens();
void DrawScreens(const Layout::FramebufferLayout& layout);
void DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y, float w, float h);
void UpdateFramerate();
@ -66,6 +70,7 @@ private:
OGLVertexArray vertex_array;
OGLBuffer vertex_buffer;
OGLProgram shader;
OGLFramebuffer screenshot_framebuffer;
/// Display information for top and bottom screens respectively
std::array<ScreenInfo, 3> screen_infos;

View file

@ -4,6 +4,7 @@
#include <memory>
#include "common/logging/log.h"
#include "core/settings.h"
#include "video_core/pica.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
@ -22,6 +23,11 @@ std::atomic<bool> g_hw_shader_enabled;
std::atomic<bool> g_hw_shader_accurate_gs;
std::atomic<bool> g_hw_shader_accurate_mul;
std::atomic<bool> g_renderer_bg_color_update_requested;
// Screenshot
std::atomic<bool> g_renderer_screenshot_requested;
void* g_screenshot_bits;
std::function<void()> g_screenshot_complete_callback;
Layout::FramebufferLayout g_screenshot_framebuffer_layout;
/// Initialize the video core
Core::System::ResultStatus Init(EmuWindow& emu_window) {
@ -48,4 +54,27 @@ void Shutdown() {
LOG_DEBUG(Render, "shutdown OK");
}
void RequestScreenshot(void* data, std::function<void()> callback,
const Layout::FramebufferLayout& layout) {
if (g_renderer_screenshot_requested) {
LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
return;
}
g_screenshot_bits = data;
g_screenshot_complete_callback = std::move(callback);
g_screenshot_framebuffer_layout = layout;
g_renderer_screenshot_requested = true;
}
u16 GetResolutionScaleFactor() {
if (g_hw_renderer_enabled) {
return !Settings::values.resolution_factor
? g_renderer->GetRenderWindow().GetFramebufferLayout().GetScalingRatio()
: Settings::values.resolution_factor;
} else {
// Software renderer always render at native resolution
return 1;
}
}
} // namespace VideoCore

View file

@ -7,6 +7,7 @@
#include <atomic>
#include <memory>
#include "core/core.h"
#include "core/frontend/emu_window.h"
class EmuWindow;
class RendererBase;
@ -26,6 +27,11 @@ extern std::atomic<bool> g_hw_shader_enabled;
extern std::atomic<bool> g_hw_shader_accurate_gs;
extern std::atomic<bool> g_hw_shader_accurate_mul;
extern std::atomic<bool> g_renderer_bg_color_update_requested;
// Screenshot
extern std::atomic<bool> g_renderer_screenshot_requested;
extern void* g_screenshot_bits;
extern std::function<void()> g_screenshot_complete_callback;
extern Layout::FramebufferLayout g_screenshot_framebuffer_layout;
/// Initialize the video core
Core::System::ResultStatus Init(EmuWindow& emu_window);
@ -33,4 +39,10 @@ Core::System::ResultStatus Init(EmuWindow& emu_window);
/// Shutdown the video core
void Shutdown();
/// Request a screenshot of the next frame
void RequestScreenshot(void* data, std::function<void()> callback,
const Layout::FramebufferLayout& layout);
u16 GetResolutionScaleFactor();
} // namespace VideoCore