Skip to content

Commit

Permalink
Add Screenshot Feature (#2354)
Browse files Browse the repository at this point in the history
* Add internal screenshot  capabilities

* update version notice
  • Loading branch information
emmauss authored Jun 28, 2021
1 parent a79b39b commit 28618c5
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 56 deletions.
1 change: 1 addition & 0 deletions Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
public struct KeyboardHotkeys
{
public Key ToggleVsync { get; set; }
public Key Screenshot { get; set; }
}
}
4 changes: 4 additions & 0 deletions Ryujinx.Graphics.GAL/IRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ namespace Ryujinx.Graphics.GAL
{
public interface IRenderer : IDisposable
{
event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;

IPipeline Pipeline { get; }

IWindow Window { get; }
Expand Down Expand Up @@ -44,5 +46,7 @@ public interface IRenderer : IDisposable
void WaitSync(ulong id);

void Initialize(GraphicsDebugLevel logLevel);

void Screenshot();
}
}
22 changes: 22 additions & 0 deletions Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Ryujinx.Graphics.GAL
{
public struct ScreenCaptureImageInfo
{
public ScreenCaptureImageInfo(int width, int height, bool isBgra, byte[] data, bool flipX, bool flipY)
{
Width = width;
Height = height;
IsBgra = isBgra;
Data = data;
FlipX = flipX;
FlipY = flipY;
}

public int Width { get; }
public int Height { get; }
public byte[] Data { get; }
public bool IsBgra { get; }
public bool FlipX { get; }
public bool FlipY { get; }
}
}
12 changes: 12 additions & 0 deletions Ryujinx.Graphics.OpenGL/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public sealed class Renderer : IRenderer

private Sync _sync;

public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;

internal ResourcePool ResourcePool { get; }

internal int BufferCount { get; private set; }
Expand Down Expand Up @@ -196,5 +198,15 @@ public void WaitSync(ulong id)
{
_sync.Wait(id);
}

public void Screenshot()
{
_window.ScreenCaptureRequested = true;
}

public void OnScreenCaptured(ScreenCaptureImageInfo bitmap)
{
ScreenCaptured?.Invoke(this, bitmap);
}
}
}
19 changes: 19 additions & 0 deletions Ryujinx.Graphics.OpenGL/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class Window : IWindow, IDisposable

internal BackgroundContextWorker BackgroundContext { get; private set; }

internal bool ScreenCaptureRequested { get; set; }

public Window(Renderer renderer)
{
_renderer = renderer;
Expand Down Expand Up @@ -106,6 +108,13 @@ private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffe
int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY;
int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY;

if (ScreenCaptureRequested)
{
CaptureFrame(srcX0, srcY0, srcX1, srcY1, view.Format.IsBgra8(), crop.FlipX, crop.FlipY);

ScreenCaptureRequested = false;
}

GL.BlitFramebuffer(
srcX0,
srcY0,
Expand Down Expand Up @@ -159,6 +168,16 @@ public void InitializeBackgroundContext(IOpenGLContext baseContext)
BackgroundContext = new BackgroundContextWorker(baseContext);
}

public void CaptureFrame(int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
{
long size = Math.Abs(4 * width * height);
byte[] bitmap = new byte[size];

GL.ReadPixels(x, y, width, height, isBgra ? PixelFormat.Bgra : PixelFormat.Rgba, PixelType.UnsignedByte, bitmap);

_renderer.OnScreenCaptured(new ScreenCaptureImageInfo(width, height, isBgra, bitmap, flipX, flipY));
}

public void Dispose()
{
BackgroundContext.Dispose();
Expand Down
5 changes: 3 additions & 2 deletions Ryujinx/Config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": 27,
"version": 28,
"enable_file_log": true,
"res_scale": 1,
"res_scale_custom": 1,
Expand Down Expand Up @@ -57,7 +57,8 @@
"enable_keyboard": false,
"enable_mouse": false,
"hotkeys": {
"toggle_vsync": "Tab"
"toggle_vsync": "Tab",
"screenshot": "F8"
},
"keyboard_config": [],
"controller_config": [],
Expand Down
2 changes: 1 addition & 1 deletion Ryujinx/Configuration/ConfigurationFileFormat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class ConfigurationFileFormat
/// <summary>
/// The current version of the file format
/// </summary>
public const int CurrentVersion = 27;
public const int CurrentVersion = 28;

public int Version { get; set; }

Expand Down
16 changes: 15 additions & 1 deletion Ryujinx/Configuration/ConfigurationState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,8 @@ public void LoadDefault()
Hid.EnableMouse.Value = false;
Hid.Hotkeys.Value = new KeyboardHotkeys
{
ToggleVsync = Key.Tab
ToggleVsync = Key.Tab,
Screenshot = Key.F8
};
Hid.InputConfig.Value = new List<InputConfig>
{
Expand Down Expand Up @@ -845,6 +846,19 @@ public void Load(ConfigurationFileFormat configurationFileFormat, string configu
configurationFileUpdated = true;
}

if (configurationFileFormat.Version < 28)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 28.");

configurationFileFormat.Hotkeys = new KeyboardHotkeys
{
ToggleVsync = Key.Tab,
Screenshot = Key.F8
};

configurationFileUpdated = true;
}

Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.ResScale.Value = configurationFileFormat.ResScale;
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
Expand Down
30 changes: 22 additions & 8 deletions Ryujinx/Ui/MainWindow.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

using ARMeilleure.Translation;
using ARMeilleure.Translation.PTC;

using Gtk;

using LibHac.Common;
using LibHac.FsSystem;
using LibHac.FsSystem.NcaUtils;
using LibHac.Ns;

using Ryujinx.Audio.Backends.Dummy;
using Ryujinx.Audio.Backends.OpenAL;
using Ryujinx.Audio.Backends.SDL2;
Expand All @@ -31,13 +42,6 @@
using Ryujinx.Ui.Helper;
using Ryujinx.Ui.Widgets;
using Ryujinx.Ui.Windows;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

using GUI = Gtk.Builder.ObjectAttribute;

Expand Down Expand Up @@ -96,6 +100,7 @@ public class MainWindow : Window
[GUI] MenuItem _stopEmulation;
[GUI] MenuItem _simulateWakeUpMessage;
[GUI] MenuItem _scanAmiibo;
[GUI] MenuItem _takeScreenshot;
[GUI] MenuItem _fullScreen;
[GUI] CheckMenuItem _startFullScreen;
[GUI] CheckMenuItem _favToggle;
Expand Down Expand Up @@ -1377,7 +1382,8 @@ private void Simulate_WakeUp_Message_Pressed(object sender, EventArgs args)

private void ActionMenu_StateChanged(object o, StateChangedArgs args)
{
_scanAmiibo.Sensitive = _emulationContext != null && _emulationContext.System.SearchingForAmiibo(out int _);
_scanAmiibo.Sensitive = _emulationContext != null && _emulationContext.System.SearchingForAmiibo(out int _);
_takeScreenshot.Sensitive = _emulationContext != null;
}

private void Scan_Amiibo(object sender, EventArgs args)
Expand All @@ -1402,6 +1408,14 @@ private void Scan_Amiibo(object sender, EventArgs args)
}
}

private void Take_Screenshot(object sender, EventArgs args)
{
if (_emulationContext != null && RendererWidget != null)
{
RendererWidget.ScreenshotRequested = true;
}
}

private void AmiiboWindow_DeleteEvent(object sender, DeleteEventArgs args)
{
if (((AmiiboWindow)sender).AmiiboId != "" && ((AmiiboWindow)sender).Response == ResponseType.Ok)
Expand Down
83 changes: 42 additions & 41 deletions Ryujinx/Ui/MainWindow.glade
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkApplicationWindow" id="_mainWin">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Ryujinx</property>
<property name="window_position">center</property>
<child type="titlebar">
<placeholder/>
</child>
<child>
<object class="GtkBox" id="_box">
<property name="visible">True</property>
Expand Down Expand Up @@ -332,6 +329,15 @@
<signal name="activate" handler="Scan_Amiibo" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_takeScreenshot">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Take a screenshot</property>
<property name="label" translatable="yes">Take Screenshot</property>
<signal name="activate" handler="Take_Screenshot" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
Expand Down Expand Up @@ -450,7 +456,7 @@
<property name="can_focus">True</property>
<property name="reorderable">True</property>
<property name="hover_selection">True</property>
<signal name="row-activated" handler="Row_Activated" swapped="no"/>
<signal name="row_activated" handler="Row_Activated" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="_gameTableSelection"/>
</child>
Expand Down Expand Up @@ -484,7 +490,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<signal name="button-release-event" handler="RefreshList_Pressed" swapped="no"/>
<signal name="button_release_event" handler="RefreshList_Pressed" swapped="no"/>
<child>
<object class="GtkImage">
<property name="name">RefreshList</property>
Expand Down Expand Up @@ -547,8 +553,7 @@
<object class="GtkEventBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">0</property>
<signal name="button-release-event" handler="VSyncStatus_Clicked" swapped="no"/>
<signal name="button_release_event" handler="VSyncStatus_Clicked" swapped="no"/>
<child>
<object class="GtkLabel" id="_vSyncStatus">
<property name="visible">True</property>
Expand Down Expand Up @@ -581,8 +586,7 @@
<object class="GtkEventBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">0</property>
<signal name="button-release-event" handler="DockedMode_Clicked" swapped="no"/>
<signal name="button_release_event" handler="DockedMode_Clicked" swapped="no"/>
<child>
<object class="GtkLabel" id="_dockedMode">
<property name="visible">True</property>
Expand Down Expand Up @@ -614,8 +618,7 @@
<object class="GtkEventBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">0</property>
<signal name="button-release-event" handler="AspectRatio_Clicked" swapped="no"/>
<signal name="button_release_event" handler="AspectRatio_Clicked" swapped="no"/>
<child>
<object class="GtkLabel" id="_aspectRatio">
<property name="visible">True</property>
Expand Down Expand Up @@ -713,35 +716,6 @@
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="_loadingStatusLabel">
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="label" translatable="yes">0/0 </property>
<property name="visible">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">11</property>
</packing>
</child>
<child>
<object class="GtkProgressBar" id="_loadingStatusBar">
<property name="width_request">200</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_bottom">6</property>
<property name="visible">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">12</property>
</packing>
</child>
<child>
<object class="GtkBox">
Expand Down Expand Up @@ -783,6 +757,33 @@
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="_loadingStatusLabel">
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="label" translatable="yes">0/0 </property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">11</property>
</packing>
</child>
<child>
<object class="GtkProgressBar" id="_loadingStatusBar">
<property name="width_request">200</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_bottom">6</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">12</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
Expand Down
Loading

0 comments on commit 28618c5

Please sign in to comment.