mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-08-03 20:24:14 +02:00
Merge pull request #14689 from JosJuice/android-internal-resolution-display
VideoCommon: Use XFB for internal resolution stats
This commit is contained in:
+12
@@ -700,6 +700,18 @@ enum class BooleanSetting(
|
||||
false
|
||||
),
|
||||
GFX_OVERLAY_STATS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "OverlayStats", false),
|
||||
GFX_OVERLAY_PROJ_STATS(
|
||||
Settings.FILE_GFX,
|
||||
Settings.SECTION_GFX_SETTINGS,
|
||||
"OverlayProjStats",
|
||||
false
|
||||
),
|
||||
GFX_SHOW_INTERNAL_RESOLUTION(
|
||||
Settings.FILE_GFX,
|
||||
Settings.SECTION_GFX_SETTINGS,
|
||||
"ShowInternalResolution",
|
||||
false
|
||||
),
|
||||
GFX_DUMP_TEXTURES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "DumpTextures", false),
|
||||
GFX_DUMP_MIP_TEXTURES(
|
||||
Settings.FILE_GFX,
|
||||
|
||||
+16
@@ -2169,6 +2169,22 @@ class SettingsFragmentPresenter(
|
||||
R.string.leave_this_unchecked
|
||||
)
|
||||
)
|
||||
sl.add(
|
||||
SwitchSetting(
|
||||
context,
|
||||
BooleanSetting.GFX_OVERLAY_PROJ_STATS,
|
||||
R.string.show_proj_stats,
|
||||
R.string.leave_this_unchecked
|
||||
)
|
||||
)
|
||||
sl.add(
|
||||
SwitchSetting(
|
||||
context,
|
||||
BooleanSetting.GFX_SHOW_INTERNAL_RESOLUTION,
|
||||
R.string.show_internal_resolution,
|
||||
R.string.leave_this_unchecked
|
||||
)
|
||||
)
|
||||
sl.add(
|
||||
SwitchSetting(
|
||||
context,
|
||||
|
||||
@@ -374,6 +374,8 @@
|
||||
<string name="debugging">Debugging</string>
|
||||
<string name="wireframe">Enable Wireframe</string>
|
||||
<string name="show_stats">Show Statistics</string>
|
||||
<string name="show_proj_stats">Show Projection Statistics</string>
|
||||
<string name="show_internal_resolution">Show XFB Resolution</string>
|
||||
<string name="texture_format">Texture Format Overlay</string>
|
||||
<string name="validation_layer">Enable API Validation Layers</string>
|
||||
<string name="dump_efb">Dump EFB Target</string>
|
||||
|
||||
@@ -43,8 +43,6 @@ const Info<int> GFX_CROP_CUSTOM_BOTTOM{{System::GFX, "Settings", "CropCustomBott
|
||||
const Info<int> GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES{
|
||||
{System::GFX, "Settings", "SafeTextureCacheColorSamples"}, 128};
|
||||
const Info<bool> GFX_SHOW_FPS{{System::GFX, "Settings", "ShowFPS"}, false};
|
||||
const Info<bool> GFX_SHOW_INTERNAL_RESOLUTION{{System::GFX, "Settings", "ShowInternalResolution"},
|
||||
false};
|
||||
const Info<bool> GFX_SHOW_FTIMES{{System::GFX, "Settings", "ShowFTimes"}, false};
|
||||
const Info<bool> GFX_SHOW_VPS{{System::GFX, "Settings", "ShowVPS"}, false};
|
||||
const Info<bool> GFX_SHOW_VTIMES{{System::GFX, "Settings", "ShowVTimes"}, false};
|
||||
@@ -61,6 +59,8 @@ const Info<bool> GFX_LOG_RENDER_TIME_TO_FILE{{System::GFX, "Settings", "LogRende
|
||||
const Info<bool> GFX_OVERLAY_STATS{{System::GFX, "Settings", "OverlayStats"}, false};
|
||||
const Info<bool> GFX_OVERLAY_PROJ_STATS{{System::GFX, "Settings", "OverlayProjStats"}, false};
|
||||
const Info<bool> GFX_OVERLAY_SCISSOR_STATS{{System::GFX, "Settings", "OverlayScissorStats"}, false};
|
||||
const Info<bool> GFX_SHOW_INTERNAL_RESOLUTION{{System::GFX, "Settings", "ShowInternalResolution"},
|
||||
false};
|
||||
const Info<bool> GFX_DUMP_TEXTURES{{System::GFX, "Settings", "DumpTextures"}, false};
|
||||
const Info<bool> GFX_DUMP_MIP_TEXTURES{{System::GFX, "Settings", "DumpMipTextures"}, true};
|
||||
const Info<bool> GFX_DUMP_BASE_TEXTURES{{System::GFX, "Settings", "DumpBaseTextures"}, true};
|
||||
|
||||
@@ -47,7 +47,6 @@ extern const Info<int> GFX_CROP_CUSTOM_RIGHT;
|
||||
extern const Info<int> GFX_CROP_CUSTOM_BOTTOM;
|
||||
extern const Info<int> GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES;
|
||||
extern const Info<bool> GFX_SHOW_FPS;
|
||||
extern const Info<bool> GFX_SHOW_INTERNAL_RESOLUTION;
|
||||
extern const Info<bool> GFX_SHOW_FTIMES;
|
||||
extern const Info<bool> GFX_SHOW_VPS;
|
||||
extern const Info<bool> GFX_SHOW_VTIMES;
|
||||
@@ -62,6 +61,7 @@ extern const Info<bool> GFX_LOG_RENDER_TIME_TO_FILE;
|
||||
extern const Info<bool> GFX_OVERLAY_STATS;
|
||||
extern const Info<bool> GFX_OVERLAY_PROJ_STATS;
|
||||
extern const Info<bool> GFX_OVERLAY_SCISSOR_STATS;
|
||||
extern const Info<bool> GFX_SHOW_INTERNAL_RESOLUTION;
|
||||
extern const Info<bool> GFX_DUMP_TEXTURES;
|
||||
extern const Info<bool> GFX_DUMP_MIP_TEXTURES;
|
||||
extern const Info<bool> GFX_DUMP_BASE_TEXTURES;
|
||||
|
||||
@@ -863,6 +863,9 @@ void Callback_FramePresented(const PresentInfo& present_info)
|
||||
present_info.actual_present_time - present_info.intended_present_time;
|
||||
perf_metrics.SetLatestFramePresentationOffset(presentation_offset);
|
||||
|
||||
perf_metrics.SetLatestFrameBufferSize(present_info.frame_buffer_width,
|
||||
present_info.frame_buffer_height);
|
||||
|
||||
if (present_info.reason == PresentInfo::PresentReason::VideoInterfaceDuplicate)
|
||||
return;
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@ void OnScreenDisplayPane::CreateLayout()
|
||||
performance_box->setLayout(performance_layout);
|
||||
|
||||
m_show_fps = new ConfigBool(tr("Show FPS"), Config::GFX_SHOW_FPS);
|
||||
m_show_internal_resolution =
|
||||
new ConfigBool(tr("Show Internal Resolution"), Config::GFX_SHOW_INTERNAL_RESOLUTION);
|
||||
m_show_ftimes = new ConfigBool(tr("Show Frame Times"), Config::GFX_SHOW_FTIMES);
|
||||
m_show_vps = new ConfigBool(tr("Show VPS"), Config::GFX_SHOW_VPS);
|
||||
m_show_vtimes = new ConfigBool(tr("Show VBlank Times"), Config::GFX_SHOW_VTIMES);
|
||||
@@ -103,6 +101,8 @@ void OnScreenDisplayPane::CreateLayout()
|
||||
m_show_statistics = new ConfigBool(tr("Show Statistics"), Config::GFX_OVERLAY_STATS);
|
||||
m_show_proj_statistics =
|
||||
new ConfigBool(tr("Show Projection Statistics"), Config::GFX_OVERLAY_PROJ_STATS);
|
||||
m_show_internal_resolution =
|
||||
new ConfigBool(tr("Show XFB Resolution"), Config::GFX_SHOW_INTERNAL_RESOLUTION);
|
||||
|
||||
debug_layout->addWidget(m_show_statistics, 0, 0);
|
||||
debug_layout->addWidget(m_show_proj_statistics, 0, 1);
|
||||
@@ -153,11 +153,6 @@ void OnScreenDisplayPane::AddDescriptions()
|
||||
QT_TR_NOOP("Shows the number of distinct frames rendered per second as a measure of "
|
||||
"visual smoothness.<br><br><dolphin_emphasis>If unsure, leave this "
|
||||
"unchecked.</dolphin_emphasis>");
|
||||
|
||||
static const char TR_SHOW_INTERNAL_RESOLUTION_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows the internal resolution in pixels, as a product of "
|
||||
"width and height. <br><br><dolphin_emphasis>If unsure, leave this "
|
||||
"unchecked.</dolphin_emphasis>");
|
||||
static const char TR_SHOW_FTIMES_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows the average time in ms between each distinct rendered frame alongside "
|
||||
"the standard deviation.<br><br><dolphin_emphasis>If unsure, leave this "
|
||||
@@ -225,12 +220,15 @@ void OnScreenDisplayPane::AddDescriptions()
|
||||
static const char TR_SHOW_PROJ_STATS_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows various projection statistics.<br><br><dolphin_emphasis>If unsure, "
|
||||
"leave this unchecked.</dolphin_emphasis>");
|
||||
static const char TR_SHOW_INTERNAL_RESOLUTION_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Shows the size of the emulated external frame buffer (XFB) in pixels, as a "
|
||||
"product of width and height.<br><br><dolphin_emphasis>If unsure, leave this "
|
||||
"unchecked.</dolphin_emphasis>");
|
||||
|
||||
m_enable_osd->SetDescription(tr(TR_ENABLE_OSD_DESCRIPTION));
|
||||
m_font_size->SetDescription(tr(TR_OSD_FONT_SIZE_DESCRIPTION));
|
||||
|
||||
m_show_fps->SetDescription(tr(TR_SHOW_FPS_DESCRIPTION));
|
||||
m_show_internal_resolution->SetDescription(tr(TR_SHOW_INTERNAL_RESOLUTION_DESCRIPTION));
|
||||
m_show_ftimes->SetDescription(tr(TR_SHOW_FTIMES_DESCRIPTION));
|
||||
m_show_vps->SetDescription(tr(TR_SHOW_VPS_DESCRIPTION));
|
||||
m_show_vtimes->SetDescription(tr(TR_SHOW_VTIMES_DESCRIPTION));
|
||||
@@ -251,4 +249,5 @@ void OnScreenDisplayPane::AddDescriptions()
|
||||
|
||||
m_show_statistics->SetDescription(tr(TR_SHOW_STATS_DESCRIPTION));
|
||||
m_show_proj_statistics->SetDescription(tr(TR_SHOW_PROJ_STATS_DESCRIPTION));
|
||||
m_show_internal_resolution->SetDescription(tr(TR_SHOW_INTERNAL_RESOLUTION_DESCRIPTION));
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ private:
|
||||
|
||||
// Performance
|
||||
ConfigBool* m_show_fps;
|
||||
ConfigBool* m_show_internal_resolution;
|
||||
ConfigBool* m_show_ftimes;
|
||||
ConfigBool* m_show_vps;
|
||||
ConfigBool* m_show_vtimes;
|
||||
@@ -49,4 +48,5 @@ private:
|
||||
// Debug
|
||||
ConfigBool* m_show_statistics;
|
||||
ConfigBool* m_show_proj_statistics;
|
||||
ConfigBool* m_show_internal_resolution;
|
||||
};
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "Common/HookableEvent.h"
|
||||
#include "Core/Config/GraphicsSettings.h"
|
||||
#include "Core/Core.h"
|
||||
#include "VideoCommon/FramebufferManager.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
PerformanceMetrics::PerformanceMetrics()
|
||||
@@ -89,20 +88,6 @@ double PerformanceMetrics::GetFPS() const
|
||||
return m_fps_counter.GetHzAvg();
|
||||
}
|
||||
|
||||
u32 PerformanceMetrics::GetEFBWidth() const
|
||||
{
|
||||
if (g_framebuffer_manager)
|
||||
return g_framebuffer_manager->GetEFBWidth();
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 PerformanceMetrics::GetEFBHeight() const
|
||||
{
|
||||
if (g_framebuffer_manager)
|
||||
return g_framebuffer_manager->GetEFBHeight();
|
||||
return 0;
|
||||
}
|
||||
|
||||
double PerformanceMetrics::GetVPS() const
|
||||
{
|
||||
return m_vps_counter.GetHzAvg();
|
||||
@@ -123,6 +108,11 @@ void PerformanceMetrics::SetLatestFramePresentationOffset(DT offset)
|
||||
m_frame_presentation_offset.store(offset, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void PerformanceMetrics::SetLatestFrameBufferSize(u32 width, u32 height)
|
||||
{
|
||||
m_frame_buffer_size.store(FrameBufferSize{width, height}, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void PerformanceMetrics::DrawImGuiStats(const float backbuffer_scale)
|
||||
{
|
||||
m_vps_counter.UpdateStats();
|
||||
@@ -139,8 +129,6 @@ void PerformanceMetrics::DrawImGuiStats(const float backbuffer_scale)
|
||||
const double fps = GetFPS();
|
||||
const double vps = GetVPS();
|
||||
const double speed = GetSpeed();
|
||||
const u32 width = GetEFBWidth();
|
||||
const u32 height = GetEFBHeight();
|
||||
|
||||
static ImVec2 last_display_size(-1.0f, -1.0f);
|
||||
|
||||
@@ -335,26 +323,6 @@ void PerformanceMetrics::DrawImGuiStats(const float backbuffer_scale)
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bShowInternalResolution)
|
||||
{
|
||||
ImGui::SetNextWindowPos(ImVec2(window_x, window_y), set_next_position_condition,
|
||||
ImVec2(1.0f, 0.0f));
|
||||
ImGui::SetNextWindowBgAlpha(bg_alpha);
|
||||
|
||||
if (ImGui::Begin("ResolutionStats", nullptr, imgui_flags))
|
||||
{
|
||||
if (stack_vertically)
|
||||
window_y += ImGui::GetWindowHeight() + window_padding;
|
||||
else
|
||||
window_x -= ImGui::GetWindowWidth() + window_padding;
|
||||
|
||||
clamp_window_position();
|
||||
|
||||
ImGui::TextColored(ImVec4(r, g, b, 1.0f), "Res: %ux%u", width, height);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bShowVPS || g_ActiveConfig.bShowVTimes)
|
||||
{
|
||||
// Position in the top-right corner of the screen.
|
||||
@@ -382,5 +350,26 @@ void PerformanceMetrics::DrawImGuiStats(const float backbuffer_scale)
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bShowInternalResolution)
|
||||
{
|
||||
ImGui::SetNextWindowPos(ImVec2(window_x, window_y), set_next_position_condition,
|
||||
ImVec2(1.0f, 0.0f));
|
||||
ImGui::SetNextWindowBgAlpha(bg_alpha);
|
||||
|
||||
if (ImGui::Begin("ResolutionStats", nullptr, imgui_flags))
|
||||
{
|
||||
if (stack_vertically)
|
||||
window_y += ImGui::GetWindowHeight() + window_padding;
|
||||
else
|
||||
window_x -= ImGui::GetWindowWidth() + window_padding;
|
||||
|
||||
clamp_window_position();
|
||||
|
||||
const FrameBufferSize size = m_frame_buffer_size.load(std::memory_order_relaxed);
|
||||
ImGui::TextColored(ImVec4(r, g, b, 1.0f), "XFB res: %ux%u", size.width, size.height);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
}
|
||||
|
||||
@@ -41,15 +41,20 @@ public:
|
||||
double GetVPS() const;
|
||||
double GetSpeed() const;
|
||||
double GetMaxSpeed() const;
|
||||
u32 GetEFBWidth() const;
|
||||
u32 GetEFBHeight() const;
|
||||
// Call from any thread.
|
||||
void SetLatestFramePresentationOffset(DT offset);
|
||||
void SetLatestFrameBufferSize(u32 width, u32 height);
|
||||
|
||||
// ImGui Functions
|
||||
void DrawImGuiStats(const float backbuffer_scale);
|
||||
|
||||
private:
|
||||
struct FrameBufferSize
|
||||
{
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
};
|
||||
|
||||
PerformanceTracker m_fps_counter{"render_times.txt"};
|
||||
PerformanceTracker m_vps_counter{"vblank_times.txt"};
|
||||
|
||||
@@ -59,6 +64,7 @@ private:
|
||||
std::atomic<double> m_max_speed{};
|
||||
|
||||
std::atomic<DT> m_frame_presentation_offset{};
|
||||
std::atomic<FrameBufferSize> m_frame_buffer_size{};
|
||||
|
||||
struct PerfSample
|
||||
{
|
||||
|
||||
@@ -174,6 +174,8 @@ void Presenter::ViSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height,
|
||||
.present_count = m_present_count++,
|
||||
.emulated_timestamp = ticks,
|
||||
.intended_present_time = presentation_time,
|
||||
.frame_buffer_width = fb_width,
|
||||
.frame_buffer_height = fb_height,
|
||||
};
|
||||
|
||||
if (is_duplicate)
|
||||
@@ -234,9 +236,11 @@ void Presenter::ImmediateSwap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_
|
||||
PresentInfo present_info{
|
||||
.frame_count = m_frame_count++,
|
||||
.present_count = m_present_count++,
|
||||
.reason = PresentInfo::PresentReason::Immediate,
|
||||
.emulated_timestamp = ticks,
|
||||
.intended_present_time = m_next_swap_estimated_time,
|
||||
.reason = PresentInfo::PresentReason::Immediate,
|
||||
.frame_buffer_width = fb_width,
|
||||
.frame_buffer_height = fb_height,
|
||||
};
|
||||
|
||||
auto& video_events = GetVideoEvents();
|
||||
|
||||
@@ -106,7 +106,6 @@ void VideoConfig::Refresh()
|
||||
iCropCustomBottom = Config::Get(Config::GFX_CROP_CUSTOM_BOTTOM);
|
||||
iSafeTextureCache_ColorSamples = Config::Get(Config::GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES);
|
||||
bShowFPS = Config::Get(Config::GFX_SHOW_FPS);
|
||||
bShowInternalResolution = Config::Get(Config::GFX_SHOW_INTERNAL_RESOLUTION);
|
||||
bShowFTimes = Config::Get(Config::GFX_SHOW_FTIMES);
|
||||
bShowVPS = Config::Get(Config::GFX_SHOW_VPS);
|
||||
bShowVTimes = Config::Get(Config::GFX_SHOW_VTIMES);
|
||||
@@ -118,6 +117,7 @@ void VideoConfig::Refresh()
|
||||
bOverlayStats = Config::Get(Config::GFX_OVERLAY_STATS);
|
||||
bOverlayProjStats = Config::Get(Config::GFX_OVERLAY_PROJ_STATS);
|
||||
bOverlayScissorStats = Config::Get(Config::GFX_OVERLAY_SCISSOR_STATS);
|
||||
bShowInternalResolution = Config::Get(Config::GFX_SHOW_INTERNAL_RESOLUTION);
|
||||
bDumpTextures = Config::Get(Config::GFX_DUMP_TEXTURES);
|
||||
bDumpMipmapTextures = Config::Get(Config::GFX_DUMP_MIP_TEXTURES);
|
||||
bDumpBaseTextures = Config::Get(Config::GFX_DUMP_BASE_TEXTURES);
|
||||
|
||||
@@ -250,7 +250,6 @@ struct VideoConfig final
|
||||
|
||||
// Information
|
||||
bool bShowFPS = false;
|
||||
bool bShowInternalResolution = false;
|
||||
bool bShowFTimes = false;
|
||||
bool bShowVPS = false;
|
||||
bool bShowVTimes = false;
|
||||
@@ -261,6 +260,7 @@ struct VideoConfig final
|
||||
bool bOverlayStats = false;
|
||||
bool bOverlayProjStats = false;
|
||||
bool bOverlayScissorStats = false;
|
||||
bool bShowInternalResolution = false;
|
||||
bool bTexFmtOverlayEnable = false;
|
||||
bool bTexFmtOverlayCenter = false;
|
||||
bool bLogRenderTimeToFile = false;
|
||||
|
||||
@@ -16,31 +16,6 @@ class System;
|
||||
|
||||
struct PresentInfo
|
||||
{
|
||||
enum class PresentReason
|
||||
{
|
||||
Immediate, // FIFO is Presenting the XFB immediately, straight after the XFB copy
|
||||
VideoInterface, // VideoInterface has triggered a present with a new frame
|
||||
VideoInterfaceDuplicate, // VideoInterface has triggered a present with a duplicate frame
|
||||
};
|
||||
|
||||
// The number of (unique) frames since the emulated console booted
|
||||
u64 frame_count = 0;
|
||||
|
||||
// The number of presents since the video backend was initialized.
|
||||
// never goes backwards.
|
||||
u64 present_count = 0;
|
||||
|
||||
// The frame is identical to the previous frame
|
||||
PresentReason reason = PresentReason::Immediate;
|
||||
|
||||
// The exact emulated time of the when real hardware would have presented this frame
|
||||
u64 emulated_timestamp = 0;
|
||||
|
||||
TimePoint intended_present_time{};
|
||||
|
||||
// AfterPresent only: The actual time the frame was presented
|
||||
TimePoint actual_present_time{};
|
||||
|
||||
enum class PresentTimeAccuracy
|
||||
{
|
||||
// The Driver/OS has given us an exact timestamp of when the first line of the frame started
|
||||
@@ -58,9 +33,37 @@ struct PresentInfo
|
||||
Unimplemented,
|
||||
};
|
||||
|
||||
enum class PresentReason
|
||||
{
|
||||
Immediate, // FIFO is Presenting the XFB immediately, straight after the XFB copy
|
||||
VideoInterface, // VideoInterface has triggered a present with a new frame
|
||||
VideoInterfaceDuplicate, // VideoInterface has triggered a present with a duplicate frame
|
||||
};
|
||||
|
||||
// The number of (unique) frames since the emulated console booted
|
||||
u64 frame_count = 0;
|
||||
|
||||
// The number of presents since the video backend was initialized.
|
||||
// never goes backwards.
|
||||
u64 present_count = 0;
|
||||
|
||||
// The exact emulated time of the when real hardware would have presented this frame
|
||||
u64 emulated_timestamp = 0;
|
||||
|
||||
TimePoint intended_present_time{};
|
||||
|
||||
// AfterPresent only: The actual time the frame was presented
|
||||
TimePoint actual_present_time{};
|
||||
|
||||
// Accuracy of actual_present_time
|
||||
PresentTimeAccuracy present_time_accuracy = PresentTimeAccuracy::Unimplemented;
|
||||
|
||||
// Where the presentation of the frame was triggered from
|
||||
PresentReason reason = PresentReason::Immediate;
|
||||
|
||||
u32 frame_buffer_width;
|
||||
u32 frame_buffer_height;
|
||||
|
||||
std::vector<std::string_view> xfb_copy_hashes;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user