mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									7e90abec78
								
							
						
					
					
						commit
						071b41cb61
					
				
					 14 changed files with 202 additions and 14 deletions
				
			
		|  | @ -15,6 +15,7 @@ | |||
| #include "input_common/main.h" | ||||
| #include "input_common/motion_emu.h" | ||||
| #include "network/network.h" | ||||
| #include "video_core/video_core.h" | ||||
| 
 | ||||
| EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} | ||||
| 
 | ||||
|  | @ -335,6 +336,19 @@ void GRenderWindow::InitRenderTarget() { | |||
|     BackupGeometry(); | ||||
| } | ||||
| 
 | ||||
| void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) { | ||||
|     if (!res_scale) | ||||
|         res_scale = VideoCore::GetResolutionScaleFactor(); | ||||
|     const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)}; | ||||
|     screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); | ||||
|     VideoCore::RequestScreenshot(screenshot_image.bits(), | ||||
|                                  [=] { | ||||
|                                      screenshot_image.mirrored(false, true).save(screenshot_path); | ||||
|                                      LOG_INFO(Frontend, "The screenshot is saved."); | ||||
|                                  }, | ||||
|                                  layout); | ||||
| } | ||||
| 
 | ||||
| void GRenderWindow::OnMinimalClientAreaChangeRequest( | ||||
|     const std::pair<unsigned, unsigned>& minimal_size) { | ||||
|     setMinimumSize(minimal_size.first, minimal_size.second); | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| #include <condition_variable> | ||||
| #include <mutex> | ||||
| #include <QGLWidget> | ||||
| #include <QImage> | ||||
| #include <QThread> | ||||
| #include "common/thread.h" | ||||
| #include "core/core.h" | ||||
|  | @ -139,6 +140,8 @@ public: | |||
| 
 | ||||
|     void InitRenderTarget(); | ||||
| 
 | ||||
|     void CaptureScreenshot(u16 res_scale, const QString& screenshot_path); | ||||
| 
 | ||||
| public slots: | ||||
|     void moveContext(); // overridden
 | ||||
| 
 | ||||
|  | @ -165,6 +168,9 @@ private: | |||
| 
 | ||||
|     EmuThread* emu_thread; | ||||
| 
 | ||||
|     /// Temporary storage of the screenshot taken
 | ||||
|     QImage screenshot_image; | ||||
| 
 | ||||
| protected: | ||||
|     void showEvent(QShowEvent* event) override; | ||||
| }; | ||||
|  |  | |||
|  | @ -206,6 +206,8 @@ void Config::ReadValues() { | |||
|     UISettings::values.theme = ReadSetting("theme", UISettings::themes[0].second).toString(); | ||||
|     UISettings::values.enable_discord_presence = | ||||
|         ReadSetting("enable_discord_presence", true).toBool(); | ||||
|     UISettings::values.screenshot_resolution_factor = | ||||
|         static_cast<u16>(ReadSetting("screenshot_resolution_factor", 0).toUInt()); | ||||
| 
 | ||||
|     qt_config->beginGroup("Updater"); | ||||
|     UISettings::values.check_for_update_on_start = | ||||
|  | @ -251,6 +253,7 @@ void Config::ReadValues() { | |||
|     UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); | ||||
|     UISettings::values.movie_record_path = ReadSetting("movieRecordPath").toString(); | ||||
|     UISettings::values.movie_playback_path = ReadSetting("moviePlaybackPath").toString(); | ||||
|     UISettings::values.screenshot_path = ReadSetting("screenshotPath").toString(); | ||||
|     UISettings::values.game_dir_deprecated = ReadSetting("gameListRootDir", ".").toString(); | ||||
|     UISettings::values.game_dir_deprecated_deepscan = | ||||
|         ReadSetting("gameListDeepScan", false).toBool(); | ||||
|  | @ -460,6 +463,8 @@ void Config::SaveValues() { | |||
|     qt_config->beginGroup("UI"); | ||||
|     WriteSetting("theme", UISettings::values.theme, UISettings::themes[0].second); | ||||
|     WriteSetting("enable_discord_presence", UISettings::values.enable_discord_presence, true); | ||||
|     WriteSetting("screenshot_resolution_factor", UISettings::values.screenshot_resolution_factor, | ||||
|                  0); | ||||
| 
 | ||||
|     qt_config->beginGroup("Updater"); | ||||
|     WriteSetting("check_for_update_on_start", UISettings::values.check_for_update_on_start, true); | ||||
|  | @ -487,6 +492,7 @@ void Config::SaveValues() { | |||
|     WriteSetting("symbolsPath", UISettings::values.symbols_path); | ||||
|     WriteSetting("movieRecordPath", UISettings::values.movie_record_path); | ||||
|     WriteSetting("moviePlaybackPath", UISettings::values.movie_playback_path); | ||||
|     WriteSetting("screenshotPath", UISettings::values.screenshot_path); | ||||
|     qt_config->beginWriteArray("gamedirs"); | ||||
|     for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { | ||||
|         qt_config->setArrayIndex(i); | ||||
|  |  | |||
|  | @ -365,6 +365,7 @@ void GMainWindow::InitializeHotkeys() { | |||
|                                    Qt::ApplicationShortcut); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Remove Amiibo", QKeySequence(Qt::Key_F3), | ||||
|                                    Qt::ApplicationShortcut); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot", QKeySequence(tr("CTRL+P"))); | ||||
| 
 | ||||
|     hotkey_registry.LoadHotkeys(); | ||||
| 
 | ||||
|  | @ -439,6 +440,12 @@ void GMainWindow::InitializeHotkeys() { | |||
|                     OnRemoveAmiibo(); | ||||
|                 } | ||||
|             }); | ||||
|     connect(hotkey_registry.GetHotkey("Main Window", "Capture Screenshot", this), | ||||
|             &QShortcut::activated, this, [&] { | ||||
|                 if (emu_thread->IsRunning()) { | ||||
|                     OnCaptureScreenshot(); | ||||
|                 } | ||||
|             }); | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::ShowUpdaterWidgets() { | ||||
|  | @ -588,6 +595,8 @@ void GMainWindow::ConnectMenuEvents() { | |||
|             Core::System::GetInstance().frame_limiter.AdvanceFrame(); | ||||
|         } | ||||
|     }); | ||||
|     connect(ui.action_Capture_Screenshot, &QAction::triggered, this, | ||||
|             &GMainWindow::OnCaptureScreenshot); | ||||
| 
 | ||||
|     // Help
 | ||||
|     connect(ui.action_Open_Citra_Folder, &QAction::triggered, this, | ||||
|  | @ -882,6 +891,7 @@ void GMainWindow::ShutdownGame() { | |||
|     ui.action_Enable_Frame_Advancing->setEnabled(false); | ||||
|     ui.action_Enable_Frame_Advancing->setChecked(false); | ||||
|     ui.action_Advance_Frame->setEnabled(false); | ||||
|     ui.action_Capture_Screenshot->setEnabled(false); | ||||
|     render_window->hide(); | ||||
|     if (game_list->isEmpty()) | ||||
|         game_list_placeholder->show(); | ||||
|  | @ -1169,6 +1179,7 @@ void GMainWindow::OnStartGame() { | |||
|     ui.action_Load_Amiibo->setEnabled(true); | ||||
|     ui.action_Report_Compatibility->setEnabled(true); | ||||
|     ui.action_Enable_Frame_Advancing->setEnabled(true); | ||||
|     ui.action_Capture_Screenshot->setEnabled(true); | ||||
| 
 | ||||
|     discord_rpc->Update(); | ||||
| } | ||||
|  | @ -1179,6 +1190,7 @@ void GMainWindow::OnPauseGame() { | |||
|     ui.action_Start->setEnabled(true); | ||||
|     ui.action_Pause->setEnabled(false); | ||||
|     ui.action_Stop->setEnabled(true); | ||||
|     ui.action_Capture_Screenshot->setEnabled(false); | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::OnStopGame() { | ||||
|  | @ -1539,6 +1551,18 @@ void GMainWindow::OnStopRecordingPlayback() { | |||
|     ui.action_Stop_Recording_Playback->setEnabled(false); | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::OnCaptureScreenshot() { | ||||
|     OnPauseGame(); | ||||
|     const QString path = | ||||
|         QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), | ||||
|                                      UISettings::values.screenshot_path, tr("PNG Image (*.png)")); | ||||
|     if (!path.isEmpty()) { | ||||
|         UISettings::values.screenshot_path = QFileInfo(path).path(); | ||||
|         render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path); | ||||
|     } | ||||
|     OnStartGame(); | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::UpdateStatusBar() { | ||||
|     if (emu_thread == nullptr) { | ||||
|         status_bar_update_timer.stop(); | ||||
|  |  | |||
|  | @ -183,6 +183,7 @@ private slots: | |||
|     void OnRecordMovie(); | ||||
|     void OnPlayMovie(); | ||||
|     void OnStopRecordingPlayback(); | ||||
|     void OnCaptureScreenshot(); | ||||
|     void OnCoreError(Core::System::ResultStatus, std::string); | ||||
|     /// Called whenever a user selects Help->About Citra
 | ||||
|     void OnMenuAboutCitra(); | ||||
|  |  | |||
|  | @ -131,6 +131,8 @@ | |||
|     <addaction name="separator"/> | ||||
|     <addaction name="action_Enable_Frame_Advancing"/> | ||||
|     <addaction name="action_Advance_Frame"/> | ||||
|     <addaction name="separator"/> | ||||
|     <addaction name="action_Capture_Screenshot"/> | ||||
|    </widget> | ||||
|    <widget class="QMenu" name="menu_Multiplayer"> | ||||
|     <property name="enabled"> | ||||
|  | @ -315,6 +317,14 @@ | |||
|     <string>Advance Frame</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="action_Capture_Screenshot"> | ||||
|    <property name="enabled"> | ||||
|     <bool>false</bool> | ||||
|    </property> | ||||
|    <property name="text"> | ||||
|     <string>Capture Screenshot</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="action_View_Lobby"> | ||||
|    <property name="enabled"> | ||||
|     <bool>true</bool> | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| #include <QMetaType> | ||||
| #include <QString> | ||||
| #include <QStringList> | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace UISettings { | ||||
| 
 | ||||
|  | @ -78,10 +79,13 @@ struct Values { | |||
|     GameListText game_list_row_2; | ||||
|     bool game_list_hide_no_icon; | ||||
| 
 | ||||
|     u16 screenshot_resolution_factor; | ||||
| 
 | ||||
|     QString roms_path; | ||||
|     QString symbols_path; | ||||
|     QString movie_record_path; | ||||
|     QString movie_playback_path; | ||||
|     QString screenshot_path; | ||||
|     QString game_dir_deprecated; | ||||
|     bool game_dir_deprecated_deepscan; | ||||
|     QList<UISettings::GameDir> game_dirs; | ||||
|  |  | |||
|  | @ -191,4 +191,50 @@ FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) { | |||
|     res.bottom_screen = bot_screen; | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale) { | ||||
|     FramebufferLayout layout; | ||||
|     if (Settings::values.custom_layout == true) { | ||||
|         layout = CustomFrameLayout( | ||||
|             std::max(Settings::values.custom_top_right, Settings::values.custom_bottom_right), | ||||
|             std::max(Settings::values.custom_top_bottom, Settings::values.custom_bottom_bottom)); | ||||
|     } else { | ||||
|         int width, height; | ||||
|         switch (Settings::values.layout_option) { | ||||
|         case Settings::LayoutOption::SingleScreen: | ||||
|             if (Settings::values.swap_screen) { | ||||
|                 width = Core::kScreenBottomWidth * res_scale; | ||||
|                 height = Core::kScreenBottomHeight * res_scale; | ||||
|             } else { | ||||
|                 width = Core::kScreenTopWidth * res_scale; | ||||
|                 height = Core::kScreenTopHeight * res_scale; | ||||
|             } | ||||
|             layout = SingleFrameLayout(width, height, Settings::values.swap_screen); | ||||
|             break; | ||||
|         case Settings::LayoutOption::LargeScreen: | ||||
|             if (Settings::values.swap_screen) { | ||||
|                 width = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 4) * res_scale; | ||||
|                 height = Core::kScreenBottomHeight * res_scale; | ||||
|             } else { | ||||
|                 width = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale; | ||||
|                 height = Core::kScreenTopHeight * res_scale; | ||||
|             } | ||||
|             layout = LargeFrameLayout(width, height, Settings::values.swap_screen); | ||||
|             break; | ||||
|         case Settings::LayoutOption::SideScreen: | ||||
|             width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale; | ||||
|             height = Core::kScreenTopHeight * res_scale; | ||||
|             layout = SideFrameLayout(width, height, Settings::values.swap_screen); | ||||
|             break; | ||||
|         case Settings::LayoutOption::Default: | ||||
|         default: | ||||
|             width = Core::kScreenTopWidth * res_scale; | ||||
|             height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; | ||||
|             layout = DefaultFrameLayout(width, height, Settings::values.swap_screen); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     return layout; | ||||
| } | ||||
| 
 | ||||
| } // namespace Layout
 | ||||
|  |  | |||
|  | @ -72,4 +72,11 @@ FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool is_swapp | |||
|  */ | ||||
| FramebufferLayout CustomFrameLayout(unsigned width, unsigned height); | ||||
| 
 | ||||
| /**
 | ||||
|  * Convenience method to get frame layout by resolution scale | ||||
|  * Read from the current settings to determine which layout to use. | ||||
|  * @param res_scale resolution scale factor | ||||
|  */ | ||||
| FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale); | ||||
| 
 | ||||
| } // namespace Layout
 | ||||
|  |  | |||
|  | @ -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()); | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue