Merge branch 'master' into netplay

This commit is contained in:
Stenzek 2023-05-04 01:07:40 +10:00
commit 59bc4d3038
42 changed files with 3382 additions and 2348 deletions

1
.gitignore vendored
View File

@ -17,6 +17,7 @@ ipch/*
*.vcxproj.user
*.VC.opendb
*.VC.db
/.vscode/
# cmake stuff
CMakeCache.txt

View File

@ -44,7 +44,7 @@ if(SUPPORTS_X11)
option(USE_X11 "Support X11 window system" ON)
endif()
if(SUPPORTS_WAYLAND)
option(USE_WAYLAND "Support Wayland window system" OFF)
option(USE_WAYLAND "Support Wayland window system" ON)
endif()
if((LINUX OR FREEBSD) OR ANDROID)
option(USE_EGL "Support EGL OpenGL context creation" ON)
@ -53,6 +53,7 @@ if((LINUX OR FREEBSD) AND NOT ANDROID)
option(USE_DRMKMS "Support DRM/KMS OpenGL contexts" OFF)
option(USE_FBDEV "Support FBDev OpenGL contexts" OFF)
option(USE_EVDEV "Support EVDev controller interface" OFF)
option(USE_DBUS "Enable DBus support for screensaver inhibiting" ON)
endif()
# Force EGL when using Wayland

View File

@ -1,8 +1,6 @@
# DuckStation - PlayStation 1, aka. PSX Emulator
[Latest News](#latest-news) | [Features](#features) | [Screenshots](#screenshots) | [Downloading and Running](#downloading-and-running) | [Building](#building) | [Disclaimers](#disclaimers)
**Discord Server:** https://discord.gg/Buktv3t
**Latest Builds for Windows and Linux (AppImage)** https://github.com/stenzek/duckstation/releases/tag/latest
**Game Compatibility List:** https://docs.google.com/spreadsheets/d/1H66MxViRjjE5f8hOl5RQmF5woS1murio2dsLn14kEqo/edit
@ -144,14 +142,14 @@ Requirements:
### Linux
Requirements (Debian/Ubuntu package names):
- CMake (`cmake`)
- SDL2 (at least version 2.0.22) (`libsdl2-dev`, `libxrandr-dev`)
- SDL2 (at least version 2.0.22) (`libsdl2-dev` `libxrandr-dev`)
- pkgconfig (`pkg-config`)
- Qt 6 (at least version 6.1.0) (`qtbase6-dev`, `qtbase6-private-dev`, `qtbase6-dev-tools`, `qttools6-dev`)
- Qt 6 (at least version 6.1.0) (`qt6-base-dev` `qt6-base-private-dev` `qt6-base-dev-tools` `qt6-tools-dev` `libqt6svg6`)
- libevdev (`libevdev-dev`)
- git (`git`) (Note: needed to clone the repository and at build time)
- When Wayland is enabled (default): `libwayland-dev` `libwayland-egl-backend-dev` `extra-cmake-modules`
- When Wayland is enabled (default): (`libwayland-dev` `libwayland-egl-backend-dev` `extra-cmake-modules` `qt6-wayland`)
- Optional for RetroAchievements (on by default): libcurl (`libcurl4-gnutls-dev`)
- Optional for framebuffer output: DRM/GBM (`libgbm-dev`, `libdrm-dev`)
- Optional for framebuffer output: DRM/GBM (`libgbm-dev` `libdrm-dev`)
- Optional for faster building: Ninja (`ninja-build`)
1. Clone the repository. Submodules aren't necessary, there is only one and it is only used for Windows (`git clone https://github.com/stenzek/duckstation.git -b dev`).

View File

@ -202,14 +202,14 @@ Requisitos:
### Linux
Requisitos (Debian/Ubuntu):
- CMake (`cmake`)
- SDL2 (`libsdl2-dev`, `libxrandr-dev`)
- SDL2 (`libsdl2-dev` `libxrandr-dev`)
- pkgconfig (`pkg-config`)
- Qt 5 (`qtbase5-dev`, `qtbase5-private-dev`, `qtbase5-dev-tools`, `qttools5-dev`)
- Qt 6 (`qt6-base-dev` `qt6-base-private-dev` `qt6-base-dev-tools` `qt6-tools-dev` `libqt6svg6`)
- libevdev (`libevdev-dev`)
- git (`git`) (Nota: necessário para clonar o repositório e em tempo de construção)
- Quando o wayland está ativado (padrão): `libwayland-dev` `libwayland-egl-backend-dev` `extra-cmake-modules`
- Quando o wayland está ativado (padrão): (`libwayland-dev` `libwayland-egl-backend-dev` `extra-cmake-modules` `qt6-wayland`)
- Opcional para RetroAchievements (ativado por padrão): libcurl (`libcurl4-gnutls-dev`)
- Opcional para saída do framebuffer: DRM/GBM (`libgbm-dev`, `libdrm-dev`)
- Opcional para saída do framebuffer: DRM/GBM (`libgbm-dev` `libdrm-dev`)
- Opcional para construção rápida: Ninja (`ninja-build`)
1. Clone o repositório. Submódulos não são necessários, há apenas um e é usado apenas para Windows (`git clone https://github.com/stenzek/duckstation.git -b dev`).
@ -293,4 +293,4 @@ Atalhos:
Ícone feito icons8: https://icons8.com/icon/74847/platforms.undefined.short-title
"PlayStation" e "PSX" são marcas registradas da Sony Interactive Entertainment Europe Limited.
Este projeto não é afiliado de forma alguma com a Sony Interactive Entertainment.
Este projeto não é afiliado de forma alguma com a Sony Interactive Entertainment.

View File

@ -148,6 +148,7 @@ declare -a SYSLIBS=(
"libhx509.so.5"
"libsqlite3.so.0"
"libcrypt.so.1"
"libdbus-1.so.3"
)
declare -a DEPLIBS=(

View File

@ -125,6 +125,8 @@ endif()
if(USE_WAYLAND)
target_compile_definitions(common PRIVATE "-DUSE_WAYLAND=1")
elseif(SUPPORTS_WAYLAND)
message(WARNING "Wayland support for renderers is disabled.\nDuckStation will FAIL to start on Wayland.")
endif()
if(USE_DRMKMS)

View File

@ -4,6 +4,7 @@
#include "context.h"
#include "../log.h"
#include "loader.h"
#include <cstdio>
#include <cstdlib>
#ifdef __APPLE__
#include <stdlib.h>

View File

@ -4,6 +4,7 @@
#pragma once
#include "types.h"
#include <cinttypes>
#include <cstdarg>
#include <mutex>
enum LOGLEVEL

View File

@ -76,6 +76,8 @@ add_library(core
negcon.h
pad.cpp
pad.h
pcdrv.cpp
pcdrv.h
pgxp.cpp
pgxp.h
playstation_mouse.cpp

View File

@ -2019,6 +2019,27 @@ bool SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value)
return true;
}
bool SafeReadMemoryCString(VirtualMemoryAddress addr, std::string* value, u32 max_length /*= 1024*/)
{
value->clear();
u8 ch;
while (SafeReadMemoryByte(addr, &ch))
{
if (ch == 0)
return true;
value->push_back(ch);
if (value->size() >= max_length)
return true;
addr++;
}
value->clear();
return false;
}
bool SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value)
{
u32 temp = ZeroExtend32(value);

View File

@ -7,13 +7,14 @@
#include "digital_controller.h"
#include "fmt/format.h"
#include "guncon.h"
#include "host.h"
#include "negcon.h"
#include "playstation_mouse.h"
#include "util/state_wrapper.h"
static const Controller::ControllerInfo s_none_info = {ControllerType::None,
"None",
"Not Connected",
TRANSLATABLE("ControllerType", "Not Connected"),
nullptr,
0,
nullptr,

View File

@ -65,6 +65,7 @@
<ClCompile Include="netplay.cpp" />
<ClCompile Include="pad.cpp" />
<ClCompile Include="controller.cpp" />
<ClCompile Include="pcdrv.cpp" />
<ClCompile Include="pgxp.cpp" />
<ClCompile Include="playstation_mouse.cpp" />
<ClCompile Include="psf_loader.cpp" />
@ -141,6 +142,7 @@
<ClInclude Include="netplay.h" />
<ClInclude Include="pad.h" />
<ClInclude Include="controller.h" />
<ClInclude Include="pcdrv.h" />
<ClInclude Include="pgxp.h" />
<ClInclude Include="playstation_mouse.h" />
<ClInclude Include="psf_loader.h" />

View File

@ -59,6 +59,7 @@
<ClCompile Include="gpu_hw_d3d12.cpp" />
<ClCompile Include="host.cpp" />
<ClCompile Include="game_database.cpp" />
<ClCompile Include="pcdrv.cpp" />
<ClCompile Include="netplay.cpp" />
</ItemGroup>
<ItemGroup>
@ -126,6 +127,7 @@
<ClInclude Include="achievements.h" />
<ClInclude Include="game_database.h" />
<ClInclude Include="input_types.h" />
<ClInclude Include="pcdrv.h" />
<ClInclude Include="netplay.h" />
</ItemGroup>
</Project>
</Project>

View File

@ -11,6 +11,7 @@
#include "cpu_recompiler_thunks.h"
#include "gte.h"
#include "host.h"
#include "pcdrv.h"
#include "pgxp.h"
#include "settings.h"
#include "system.h"
@ -293,6 +294,20 @@ void RaiseException(Exception excode)
g_state.current_instruction_pc, GetExceptionVector());
}
void RaiseBreakException(u32 CAUSE_bits, u32 EPC, u32 instruction_bits)
{
if (PCDrv::HandleSyscall(instruction_bits, g_state.regs))
{
// immediately return
g_state.regs.npc = EPC + 4;
FlushPipeline();
return;
}
// normal exception
RaiseException(CAUSE_bits, EPC, GetExceptionVector());
}
void SetExternalInterrupt(u8 bit)
{
g_state.cop0_regs.cause.Ip |= static_cast<u8>(1u << bit);
@ -1109,7 +1124,10 @@ restart_instruction:
case InstructionFunct::break_:
{
RaiseException(Exception::BP);
RaiseBreakException(Cop0Registers::CAUSE::MakeValueForException(
Exception::BP, g_state.current_instruction_in_branch_delay_slot,
g_state.current_instruction_was_branch_taken, g_state.current_instruction.cop.cop_n),
g_state.current_instruction_pc, g_state.current_instruction.bits);
}
break;

View File

@ -8,6 +8,7 @@
#include "types.h"
#include <array>
#include <optional>
#include <string>
#include <vector>
class StateWrapper;
@ -147,6 +148,7 @@ ALWAYS_INLINE bool InKernelMode()
bool SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value);
bool SafeReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
bool SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value);
bool SafeReadMemoryCString(VirtualMemoryAddress addr, std::string* value, u32 max_length = 1024);
bool SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value);
bool SafeWriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
bool SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value);

View File

@ -10,6 +10,7 @@ namespace CPU {
// exceptions
void RaiseException(Exception excode);
void RaiseException(u32 CAUSE_bits, u32 EPC);
void RaiseBreakException(u32 CAUSE_bits, u32 EPC, u32 instruction_bits);
ALWAYS_INLINE bool HasPendingInterrupt()
{

View File

@ -931,8 +931,17 @@ void CodeGenerator::GenerateExceptionExit(const CodeBlockInstruction& cbi, Excep
m_register_cache.FlushAllGuestRegisters(true, true);
m_register_cache.FlushLoadDelay(true);
EmitFunctionCall(nullptr, static_cast<void (*)(u32, u32)>(&CPU::RaiseException), CAUSE_bits,
GetCurrentInstructionPC());
if (excode == Exception::BP)
{
EmitFunctionCall(nullptr, static_cast<void (*)(u32, u32, u32)>(&CPU::RaiseBreakException), CAUSE_bits,
GetCurrentInstructionPC(), Value::FromConstantU32(cbi.instruction.bits));
}
else
{
EmitFunctionCall(nullptr, static_cast<void (*)(u32, u32)>(&CPU::RaiseException), CAUSE_bits,
GetCurrentInstructionPC());
}
return;
}

View File

@ -232,18 +232,20 @@ void HostDisplay::CalculateDrawRect(s32 window_width, s32 window_height, float*
apply_aspect_ratio ?
(display_aspect_ratio / (static_cast<float>(m_display_width) / static_cast<float>(m_display_height))) :
1.0f;
const float display_width = g_settings.display_stretch_vertically ?
static_cast<float>(m_display_width) : static_cast<float>(m_display_width) * x_scale;
const float display_height = g_settings.display_stretch_vertically ?
static_cast<float>(m_display_height) / x_scale : static_cast<float>(m_display_height);
const float active_left = g_settings.display_stretch_vertically ?
static_cast<float>(m_display_active_left) : static_cast<float>(m_display_active_left) * x_scale;
const float active_top = g_settings.display_stretch_vertically ?
static_cast<float>(m_display_active_top) / x_scale : static_cast<float>(m_display_active_top);
const float display_width = g_settings.display_stretch_vertically ? static_cast<float>(m_display_width) :
static_cast<float>(m_display_width) * x_scale;
const float display_height = g_settings.display_stretch_vertically ? static_cast<float>(m_display_height) / x_scale :
static_cast<float>(m_display_height);
const float active_left = g_settings.display_stretch_vertically ? static_cast<float>(m_display_active_left) :
static_cast<float>(m_display_active_left) * x_scale;
const float active_top = g_settings.display_stretch_vertically ? static_cast<float>(m_display_active_top) / x_scale :
static_cast<float>(m_display_active_top);
const float active_width = g_settings.display_stretch_vertically ?
static_cast<float>(m_display_active_width) : static_cast<float>(m_display_active_width) * x_scale;
static_cast<float>(m_display_active_width) :
static_cast<float>(m_display_active_width) * x_scale;
const float active_height = g_settings.display_stretch_vertically ?
static_cast<float>(m_display_active_height) / x_scale : static_cast<float>(m_display_active_height);
static_cast<float>(m_display_active_height) / x_scale :
static_cast<float>(m_display_active_height);
if (out_x_scale)
*out_x_scale = x_scale;
@ -500,11 +502,13 @@ bool HostDisplay::WriteDisplayTextureToFile(std::string filename, bool full_reso
const float ss_height_scale = static_cast<float>(m_display_active_height) / static_cast<float>(m_display_height);
const float ss_aspect_ratio = m_display_aspect_ratio * ss_width_scale / ss_height_scale;
resize_width = g_settings.display_stretch_vertically ?
m_display_texture_view_width : static_cast<s32>(static_cast<float>(resize_height) * ss_aspect_ratio);
m_display_texture_view_width :
static_cast<s32>(static_cast<float>(resize_height) * ss_aspect_ratio);
resize_height = g_settings.display_stretch_vertically ?
static_cast<s32>(static_cast<float>(resize_height) /
(m_display_aspect_ratio / (static_cast<float>(m_display_width) / static_cast<float>(m_display_height)))) :
resize_height;
static_cast<s32>(static_cast<float>(resize_height) /
(m_display_aspect_ratio /
(static_cast<float>(m_display_width) / static_cast<float>(m_display_height)))) :
resize_height;
}
else
{
@ -614,17 +618,65 @@ bool HostDisplay::WriteDisplayTextureToBuffer(std::vector<u32>* buffer, u32 resi
return true;
}
bool HostDisplay::WriteScreenshotToFile(std::string filename, bool compress_on_thread /*= false*/)
bool HostDisplay::WriteScreenshotToFile(std::string filename, bool internal_resolution /* = false */,
bool compress_on_thread /* = false */)
{
const u32 width = m_window_info.surface_width;
const u32 height = m_window_info.surface_height;
u32 width = m_window_info.surface_width;
u32 height = m_window_info.surface_height;
auto [draw_left, draw_top, draw_width, draw_height] = CalculateDrawRect(width, height);
if (internal_resolution && m_display_texture_view_width != 0 && m_display_texture_view_height != 0)
{
// If internal res, scale the computed draw rectangle to the internal res.
// We re-use the draw rect because it's already been AR corrected.
const float sar =
static_cast<float>(m_display_texture_view_width) / static_cast<float>(m_display_texture_view_height);
const float dar = static_cast<float>(draw_width) / static_cast<float>(draw_height);
if (sar >= dar)
{
// stretch height, preserve width
const float scale = static_cast<float>(m_display_texture_view_width) / static_cast<float>(draw_width);
width = m_display_texture_view_width;
height = static_cast<u32>(std::round(static_cast<float>(draw_height) * scale));
}
else
{
// stretch width, preserve height
const float scale = static_cast<float>(m_display_texture_view_height) / static_cast<float>(draw_height);
width = static_cast<u32>(std::round(static_cast<float>(draw_width) * scale));
height = m_display_texture_view_height;
}
// DX11 won't go past 16K texture size.
constexpr u32 MAX_TEXTURE_SIZE = 16384;
if (width > MAX_TEXTURE_SIZE)
{
height = static_cast<u32>(static_cast<float>(height) /
(static_cast<float>(width) / static_cast<float>(MAX_TEXTURE_SIZE)));
width = MAX_TEXTURE_SIZE;
}
if (height > MAX_TEXTURE_SIZE)
{
height = MAX_TEXTURE_SIZE;
width = static_cast<u32>(static_cast<float>(width) /
(static_cast<float>(height) / static_cast<float>(MAX_TEXTURE_SIZE)));
}
// Remove padding, it's not part of the framebuffer.
draw_left = 0;
draw_top = 0;
draw_width = static_cast<s32>(width);
draw_height = static_cast<s32>(height);
}
if (width == 0 || height == 0)
return false;
std::vector<u32> pixels;
u32 pixels_stride;
GPUTexture::Format pixels_format;
if (!RenderScreenshot(width, height, &pixels, &pixels_stride, &pixels_format))
if (!RenderScreenshot(width, height,
Common::Rectangle<s32>::FromExtents(draw_top, draw_left, draw_width, draw_height), &pixels,
&pixels_stride, &pixels_format))
{
Log_ErrorPrintf("Failed to render %ux%u screenshot", width, height);
return false;

View File

@ -104,8 +104,8 @@ public:
virtual bool Render(bool skip_present) = 0;
/// Renders the display with postprocessing to the specified image.
virtual bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
GPUTexture::Format* out_format) = 0;
virtual bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect,
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format) = 0;
ALWAYS_INLINE bool IsVsyncEnabled() const { return m_vsync_enabled; }
virtual void SetVSync(bool enabled) = 0;
@ -206,7 +206,7 @@ public:
bool clear_alpha = true);
/// Helper function to save screenshot to PNG.
bool WriteScreenshotToFile(std::string filename, bool compress_on_thread = false);
bool WriteScreenshotToFile(std::string filename, bool internal_resolution = false, bool compress_on_thread = false);
protected:
ALWAYS_INLINE bool HasSoftwareCursor() const { return static_cast<bool>(m_cursor_texture); }
@ -251,7 +251,7 @@ protected:
bool m_vsync_enabled = false;
};
/// Returns a pointer to the current host display abstraction. Assumes AcquireHostDisplay() has been caled.
/// Returns a pointer to the current host display abstraction. Assumes AcquireHostDisplay() has been called.
extern std::unique_ptr<HostDisplay> g_host_display;
namespace Host {

330
src/core/pcdrv.cpp Normal file
View File

@ -0,0 +1,330 @@
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "pcdrv.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/path.h"
#include "common/string_util.h"
#include "cpu_core.h"
#include "settings.h"
Log_SetChannel(PCDrv);
static constexpr u32 MAX_FILES = 100;
static std::vector<FileSystem::ManagedCFilePtr> s_files;
enum PCDrvAttribute : u32
{
PCDRV_ATTRIBUTE_READ_ONLY = (1 << 0),
PCDRV_ATTRIBUTE_HIDDEN = (1 << 1),
PCDRV_ATTRIBUTE_SYSTEM = (1 << 2),
PCDRV_ATTRIBUTE_DIRECTORY = (1 << 4),
PCDRV_ATTRIBUTE_ARCHIVE = (1 << 5),
};
static s32 GetFreeFileHandle()
{
for (s32 i = 0; i < static_cast<s32>(s_files.size()); i++)
{
if (!s_files[i])
return i;
}
if (s_files.size() >= MAX_FILES)
{
Log_ErrorPrint("Too many open files.");
return -1;
}
const s32 index = static_cast<s32>(s_files.size());
s_files.emplace_back(nullptr, [](std::FILE*) {});
return index;
}
static void CloseAllFiles()
{
if (!s_files.empty())
Log_DevPrintf("Closing %zu open files.", s_files.size());
s_files.clear();
}
static FILE* GetFileFromHandle(u32 handle)
{
if (handle >= static_cast<u32>(s_files.size()) || !s_files[handle])
{
Log_ErrorPrintf("Invalid file handle %d", static_cast<s32>(handle));
return nullptr;
}
return s_files[handle].get();
}
static bool CloseFileHandle(u32 handle)
{
if (handle >= static_cast<u32>(s_files.size()) || !s_files[handle])
{
Log_ErrorPrintf("Invalid file handle %d", static_cast<s32>(handle));
return false;
}
s_files[handle].reset();
while (!s_files.empty() && !s_files.back())
s_files.pop_back();
return true;
}
static std::string ResolveHostPath(const std::string& path)
{
// Double-check that it falls within the directory of the elf.
// Not a real sandbox, but emulators shouldn't be treated as such. Don't run untrusted code!
const std::string& root = g_settings.pcdrv_root;
std::string canonicalized_path = Path::Canonicalize(Path::Combine(root, path));
if (canonicalized_path.length() < root.length() || // Length has to be longer (a file),
!StringUtil::StartsWith(canonicalized_path, root) || // and start with the host root,
canonicalized_path[root.length()] != FS_OSPATH_SEPARATOR_CHARACTER) // and we can't access a sibling.
{
Log_ErrorPrintf("Denying access to path outside of PCDrv directory. Requested path: '%s', "
"Resolved path: '%s', Root directory: '%s'",
path.c_str(), root.c_str(), canonicalized_path.c_str());
canonicalized_path.clear();
}
return canonicalized_path;
}
void PCDrv::Initialize()
{
if (!g_settings.pcdrv_enable)
return;
Log_WarningPrintf("%s PCDrv is enabled at '%s'", g_settings.pcdrv_enable_writes ? "Read/Write" : "Read-Only",
g_settings.pcdrv_root.c_str());
}
void PCDrv::Reset()
{
CloseAllFiles();
}
void PCDrv::Shutdown()
{
CloseAllFiles();
}
bool PCDrv::HandleSyscall(u32 instruction_bits, CPU::Registers& regs)
{
// Based on https://problemkaputt.de/psxspx-bios-pc-file-server.htm
#define RETURN_ERROR() \
regs.v0 = 0xffffffff; \
regs.v1 = 0xffffffff; // error code
if (!g_settings.pcdrv_enable)
return false;
const u32 code = (instruction_bits >> 6) & 0xfffff; // 20 bits, funct = 0
switch (code)
{
case 0x101: // PCinit
{
Log_DevPrintf("PCinit");
CloseAllFiles();
regs.v0 = 0;
regs.v1 = 0;
return true;
}
case 0x102: // PCcreat
case 0x103: // PCopen
{
const bool is_open = (code == 0x103);
const char* func = (code == 0x102) ? "PCcreat" : "PCopen";
const u32 mode = regs.a2;
std::string filename;
if (!CPU::SafeReadMemoryCString(regs.a1, &filename))
{
Log_ErrorPrintf("%s: Invalid string", func);
return false;
}
Log_DebugPrintf("%s: '%s' mode %u", func, filename.c_str(), mode);
if ((filename = ResolveHostPath(filename)).empty())
{
RETURN_ERROR();
return true;
}
if (!is_open && !g_settings.pcdrv_enable_writes)
{
Log_ErrorPrintf("%s: Writes are not enabled", func);
RETURN_ERROR();
return true;
}
// Directories are unsupported for now, ignore other attributes
if (mode & PCDRV_ATTRIBUTE_DIRECTORY)
{
Log_ErrorPrintf("%s: Directories are unsupported", func);
RETURN_ERROR();
return true;
}
// Create empty file, truncate if exists.
const s32 handle = GetFreeFileHandle();
if (handle < 0)
{
RETURN_ERROR();
return true;
}
s_files[handle] = FileSystem::OpenManagedCFile(filename.c_str(),
is_open ? (g_settings.pcdrv_enable_writes ? "r+b" : "rb") : "w+b");
if (!s_files[handle])
{
Log_ErrorPrintf("%s: Failed to open '%s'", func, filename.c_str());
RETURN_ERROR();
return true;
}
Log_DebugPrintf("PCDrv: Opened '%s' => %d", filename.c_str(), handle);
regs.v0 = 0;
regs.v1 = static_cast<u32>(handle);
return true;
}
case 0x104: // PCclose
{
Log_DebugPrintf("PCclose(%u)", regs.a1);
if (!CloseFileHandle(regs.a1))
{
RETURN_ERROR();
return true;
}
regs.v0 = 0;
regs.v1 = 0;
return true;
}
case 0x105: // PCread
{
Log_DebugPrintf("PCread(%u, %u, 0x%08x)", regs.a1, regs.a2, regs.a3);
std::FILE* fp = GetFileFromHandle(regs.a1);
if (!fp)
{
RETURN_ERROR();
return true;
}
const u32 count = regs.a2;
u32 dstaddr = regs.a3;
for (u32 i = 0; i < count; i++)
{
// Certainly less than optimal, but it's not like you're going to be reading megabytes of data here.
u8 val;
if (std::fread(&val, 1, 1, fp) != 1)
{
// Does not stop at EOF according to psx-spx.
if (std::ferror(fp) != 0)
{
RETURN_ERROR();
return true;
}
val = 0;
}
CPU::SafeWriteMemoryByte(dstaddr, val);
dstaddr++;
}
regs.v0 = 0;
regs.v1 = count;
return true;
}
case 0x106: // PCwrite
{
Log_DebugPrintf("PCwrite(%u, %u, 0x%08x)", regs.a1, regs.a2, regs.a3);
std::FILE* fp = GetFileFromHandle(regs.a1);
if (!fp)
{
RETURN_ERROR();
return true;
}
const u32 count = regs.a2;
u32 srcaddr = regs.a3;
u32 written = 0;
for (u32 i = 0; i < count; i++)
{
u8 val;
if (!CPU::SafeReadMemoryByte(srcaddr, &val))
break;
if (std::fwrite(&val, 1, 1, fp) != 1)
{
RETURN_ERROR();
return true;
}
srcaddr++;
written++;
}
regs.v0 = 0;
regs.v1 = written;
return true;
}
case 0x107: // PClseek
{
Log_DebugPrintf("PClseek(%u, %u, %u)", regs.a1, regs.a2, regs.a3);
std::FILE* fp = GetFileFromHandle(regs.a1);
if (!fp)
{
RETURN_ERROR();
return true;
}
const s32 offset = static_cast<s32>(regs.a2);
const u32 mode = regs.a3;
int hmode;
switch (mode)
{
case 0:
hmode = SEEK_SET;
break;
case 1:
hmode = SEEK_CUR;
break;
case 2:
hmode = SEEK_END;
break;
default:
RETURN_ERROR();
return true;
}
if (FileSystem::FSeek64(fp, offset, hmode) != 0)
{
Log_ErrorPrintf("FSeek for PCDrv failed: %d %u", offset, hmode);
RETURN_ERROR();
return true;
}
regs.v0 = 0;
regs.v1 = static_cast<u32>(static_cast<s32>(FileSystem::FTell64(fp)));
return true;
}
default:
return false;
}
}

18
src/core/pcdrv.h Normal file
View File

@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "cpu_types.h"
#include "types.h"
//////////////////////////////////////////////////////////////////////////
// HLE Implementation of PCDrv
//////////////////////////////////////////////////////////////////////////
namespace PCDrv {
void Initialize();
void Reset();
void Shutdown();
bool HandleSyscall(u32 instruction_bits, CPU::Registers& regs);
}

View File

@ -303,6 +303,9 @@ void Settings::Load(SettingsInterface& si)
audio_dump_on_boot = si.GetBoolValue("Audio", "DumpOnBoot", false);
use_old_mdec_routines = si.GetBoolValue("Hacks", "UseOldMDECRoutines", false);
pcdrv_enable = si.GetBoolValue("PCDrv", "Enabled", false);
pcdrv_enable_writes = si.GetBoolValue("PCDrv", "EnableWrites", false);
pcdrv_root = si.GetStringValue("PCDrv", "Root");
dma_max_slice_ticks = si.GetIntValue("Hacks", "DMAMaxSliceTicks", DEFAULT_DMA_MAX_SLICE_TICKS);
dma_halt_ticks = si.GetIntValue("Hacks", "DMAHaltTicks", DEFAULT_DMA_HALT_TICKS);
@ -522,6 +525,10 @@ void Settings::Save(SettingsInterface& si) const
si.SetIntValue("Hacks", "GPUFIFOSize", gpu_fifo_size);
si.SetIntValue("Hacks", "GPUMaxRunAhead", gpu_max_run_ahead);
si.SetBoolValue("PCDrv", "Enabled", pcdrv_enable);
si.SetBoolValue("PCDrv", "EnableWrites", pcdrv_enable_writes);
si.SetStringValue("PCDrv", "Root", pcdrv_root.c_str());
si.SetBoolValue("BIOS", "PatchTTYEnable", bios_patch_tty_enable);
si.SetBoolValue("BIOS", "PatchFastBoot", bios_patch_fast_boot);
@ -610,10 +617,17 @@ void Settings::FixIncompatibleSettings(bool display_osd_messages)
g_settings.cdrom_mute_cd_audio = false;
g_settings.texture_replacements.enable_vram_write_replacements = false;
g_settings.use_old_mdec_routines = false;
g_settings.pcdrv_enable = false;
g_settings.bios_patch_fast_boot = false;
g_settings.bios_patch_tty_enable = false;
}
if (g_settings.pcdrv_enable && g_settings.pcdrv_root.empty())
{
Log_WarningPrintf("Disabling PCDrv because no root directory is specified.");
g_settings.pcdrv_enable = false;
}
if (g_settings.display_integer_scaling && g_settings.display_linear_filtering)
{
Log_WarningPrintf("Disabling linear filter due to integer upscaling.");

View File

@ -170,6 +170,7 @@ struct Settings
bool audio_dump_on_boot = false;
bool use_old_mdec_routines = false;
bool pcdrv_enable = false;
// timing hacks section
TickCount dma_max_slice_ticks = DEFAULT_DMA_MAX_SLICE_TICKS;
@ -228,8 +229,6 @@ struct Settings
}
} texture_replacements;
// TODO: Controllers, memory cards, etc.
bool bios_patch_tty_enable = false;
bool bios_patch_fast_boot = DEFAULT_FAST_BOOT_VALUE;
bool enable_8mb_ram = false;
@ -243,6 +242,9 @@ struct Settings
MultitapMode multitap_mode = DEFAULT_MULTITAP_MODE;
std::string pcdrv_root;
bool pcdrv_enable_writes = false;
std::array<TinyString, NUM_CONTROLLER_AND_CARD_PORTS> GeneratePortLabels() const;
LOGLEVEL log_level = DEFAULT_LOG_LEVEL;

View File

@ -5,8 +5,8 @@
#include "cdrom.h"
#include "common/bitfield.h"
#include "common/fifo_queue.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/path.h"
#include "dma.h"
#include "host.h"
#include "imgui.h"
@ -1493,7 +1493,7 @@ bool SPU::StartDumpingAudio(const char* filename)
else
new_suffix.Format("voice%u.wav", i);
std::string voice_filename(FileSystem::ReplaceExtension(filename, new_suffix));
const std::string voice_filename = Path::ReplaceExtension(filename, new_suffix);
if (!s_voice_dump_writers[i]->Open(voice_filename.c_str(), SAMPLE_RATE, 2))
{
Log_ErrorPrintf("Failed to open voice dump filename '%s'", voice_filename.c_str());

View File

@ -35,6 +35,7 @@
#include "multitap.h"
#include "netplay.h"
#include "pad.h"
#include "pcdrv.h"
#include "pgxp.h"
#include "psf_loader.h"
#include "save_state_version.h"
@ -1418,6 +1419,7 @@ bool System::Initialize(bool force_software_renderer)
SPU::Initialize();
MDEC::Initialize();
SIO::Initialize();
PCDrv::Initialize();
static constexpr float WARNING_DURATION = 15.0f;
@ -1473,6 +1475,7 @@ void System::DestroySystem()
g_texture_replacements.Shutdown();
PCDrv::Shutdown();
SIO::Shutdown();
MDEC::Shutdown();
SPU::Shutdown();
@ -1831,6 +1834,7 @@ void System::InternalReset()
SPU::Reset();
MDEC::Reset();
SIO::Reset();
PCDrv::Reset();
s_frame_number = 1;
s_internal_frame_number = 0;
TimingEvents::Reset();
@ -2064,8 +2068,9 @@ bool System::InternalSaveState(ByteStream* state, u32 screenshot_size /* = 256 *
std::vector<u32> screenshot_buffer;
u32 screenshot_stride;
GPUTexture::Format screenshot_format;
if (g_host_display->RenderScreenshot(screenshot_width, screenshot_height, &screenshot_buffer, &screenshot_stride,
&screenshot_format) &&
if (g_host_display->RenderScreenshot(screenshot_width, screenshot_height,
Common::Rectangle<s32>::FromExtents(0, 0, screenshot_width, screenshot_height),
&screenshot_buffer, &screenshot_stride, &screenshot_format) &&
GPUTexture::ConvertTextureDataToRGBA8(screenshot_width, screenshot_height, screenshot_buffer, screenshot_stride,
screenshot_format))
{
@ -3912,10 +3917,8 @@ bool System::SaveScreenshot(const char* filename /* = nullptr */, bool full_reso
return false;
}
const bool screenshot_saved =
g_settings.display_internal_resolution_screenshots ?
g_host_display->WriteDisplayTextureToFile(filename, full_resolution, apply_aspect_ratio, compress_on_thread) :
g_host_display->WriteScreenshotToFile(filename, compress_on_thread);
const bool screenshot_saved = g_host_display->WriteScreenshotToFile(
filename, g_settings.display_internal_resolution_screenshots, compress_on_thread);
if (!screenshot_saved)
{

View File

@ -175,6 +175,52 @@ static void addMSAATweakOption(SettingsDialog* dialog, QTableWidget* table, cons
table->setCellWidget(row, 1, msaa);
}
static void addDirectoryOption(SettingsDialog* dialog, QTableWidget* table, const QString& name, std::string section,
std::string key)
{
const int row = table->rowCount();
table->insertRow(row);
QTableWidgetItem* name_item = new QTableWidgetItem(name);
name_item->setFlags(name_item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsSelectable));
table->setItem(row, 0, name_item);
QWidget* container = new QWidget(table);
QHBoxLayout* layout = new QHBoxLayout(container);
layout->setContentsMargins(0, 0, 0, 0);
QLineEdit* value = new QLineEdit(container);
value->setObjectName(QStringLiteral("value"));
SettingWidgetBinder::BindWidgetToStringSetting(dialog->getSettingsInterface(), value, std::move(section),
std::move(key));
layout->addWidget(value, 1);
QPushButton* browse = new QPushButton(container);
browse->setText(QStringLiteral("..."));
browse->setMaximumWidth(32);
QObject::connect(browse, &QPushButton::clicked, browse, [browse, value, name]() {
const QString path(QDir::toNativeSeparators(QFileDialog::getExistingDirectory(
QtUtils::GetRootWidget(browse), qApp->translate("AdvancedSettingsWidget", "Select folder for %1").arg(name),
value->text())));
if (!path.isEmpty())
value->setText(path);
});
layout->addWidget(browse, 0);
table->setCellWidget(row, 1, container);
}
static void setDirectoryOption(QTableWidget* table, int row, const char* value)
{
QWidget* widget = table->cellWidget(row, 1);
Assert(widget);
QLineEdit* valuew = widget->findChild<QLineEdit*>(QStringLiteral("value"));
Assert(valuew);
valuew->setText(QString::fromUtf8(value));
}
AdvancedSettingsWidget::AdvancedSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent), m_dialog(dialog)
{
@ -299,6 +345,10 @@ void AdvancedSettingsWidget::addTweakOptions()
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Create Save State Backups"), "General",
"CreateSaveStateBackups", false);
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable PCDrv"), "PCDrv", "Enabled", false);
addBooleanTweakOption(m_dialog, m_ui.tweakOptionTable, tr("Enable PCDrv Writes"), "PCDrv", "EnableWrites", false);
addDirectoryOption(m_dialog, m_ui.tweakOptionTable, tr("PCDrv Root Directory"), "PCDrv", "Root");
}
void AdvancedSettingsWidget::onResetToDefaultClicked()
@ -338,9 +388,13 @@ void AdvancedSettingsWidget::onResetToDefaultClicked()
setIntRangeTweakOption(m_ui.tweakOptionTable, i++,
static_cast<int>(Settings::DEFAULT_GPU_MAX_RUN_AHEAD)); // GPU max run-ahead
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Use debug host GPU device
setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Increase timer resolution
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Stretch Display Vertically
setBooleanTweakOption(m_ui.tweakOptionTable, i++, true); // Increase Timer Resolution
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Allow booting without SBI file
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Create save state backups
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Enable PCDRV
setBooleanTweakOption(m_ui.tweakOptionTable, i++, false); // Enable PCDRV Writes
setDirectoryOption(m_ui.tweakOptionTable, i++, ""); // PCDrv Root Directory
return;
}
@ -378,9 +432,13 @@ void AdvancedSettingsWidget::onResetToDefaultClicked()
sif->DeleteValue("Hacks", "GPUFIFOSize");
sif->DeleteValue("Hacks", "GPUMaxRunAhead");
sif->DeleteValue("GPU", "UseDebugDevice");
sif->DeleteValue("Display", "StretchVertically");
sif->DeleteValue("Main", "IncreaseTimerResolution");
sif->DeleteValue("CDROM", "AllowBootingWithoutSBIFile");
sif->DeleteValue("General", "CreateSaveStateBackups");
sif->DeleteValue("PCDrv", "Enabled");
sif->DeleteValue("PCDrv", "EnableWrites");
sif->DeleteValue("PCDrv", "Root");
sif->Save();
while (m_ui.tweakOptionTable->rowCount() > 0)
m_ui.tweakOptionTable->removeRow(m_ui.tweakOptionTable->rowCount() - 1);

View File

@ -392,7 +392,7 @@ void ControllerSettingsDialog::createWidgets()
const Controller::ControllerInfo* ci =
Controller::GetControllerInfo(m_port_bindings[global_slot]->getControllerType());
const QString display_name(ci ? QString::fromUtf8(ci->display_name) : QStringLiteral("Unknown"));
const QString display_name(ci ? qApp->translate("ControllerType", ci->display_name) : QStringLiteral("Unknown"));
QListWidgetItem* item = new QListWidgetItem();
item->setText(mtap_enabled[port] ?
@ -433,7 +433,7 @@ void ControllerSettingsDialog::updateListDescription(u32 global_slot, Controller
const bool mtap_enabled = getBoolValue("Pad", (port == 0) ? "MultitapPort1" : "MultitapPort2", false);
const Controller::ControllerInfo* ci = Controller::GetControllerInfo(widget->getControllerType());
const QString display_name(ci ? QString::fromUtf8(ci->display_name) : QStringLiteral("Unknown"));
const QString display_name(ci ? qApp->translate("ControllerType", ci->display_name) : QStringLiteral("Unknown"));
item->setText(mtap_enabled ?
(tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name)) :

View File

@ -148,7 +148,7 @@ static const ImWchar* QtHost::GetGlyphRangesJapanese()
{
// clang-format off
// auto update by generate_update_glyph_ranges.py with duckstation-qt_ja.ts
static const char16_t chars[] = u"←↑→↓□△○ 、。々「」〜あいうえおかがきぎくぐけげこごさざしじすずせそただちっつづてでとどなにぬねのはばびへべほぼぽまみむめもやゆよらりるれろわをんァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソタダチッツテデトドナニネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロワンー一上下不与両並中主了予事二互交人今介他付代令以件任休伸位低体作使例供依価係保信修個倍値停側傍備像優元先光入全公共具典内再凍処出分切初別利到制削前割力加効動勧化十協単去参及反収取古可右号各合同名向含告周呼命問善回囲固国圧在地垂型埋域基報場境増壊声売変外多大央失奨妥始子字存学安完定宛実密対専射小少岐左差巻帰常幅平年度座延式引弱張強当形影役待後従得御復微心必忘応性恐情意感態成我戻所手扱投択押拡持指振挿排探接推描提換損摩撃撮操改敗数整文料断新方既日早明映昨時景更書替最有望期未本来析枚果枠栄検概構標権機欄次止正歪残毎比水永求汎決況法波注海消深混済減測源準滑演点無照版牲特犠状獲率現理生用申画界番異疑発登的目直相瞬知短破確示禁秒称移程種穴空立端符等算管範簡粋精約純索細終組結統続維緑線編縮績繰置翻者耗背能自致般良色荷行表装補製複要見規視覧観解言計記設許訳証試詳認語説読調識警護象負販費質赤起超跡転軸軽較込近返追送逆通速連進遅遊達遠適遷選部重野量録長閉開間関防降限除隅隠集離電青非面音響頂順領頭頻頼題類飛高鮮黒%?X";
static const char16_t chars[] = u"←↑→↓□△○ 、。々「」〜あいうえおかがきぎくぐけげこごさざしじすずせそただちっつづてでとどなにぬねのはばびへべほぼぽまみむめもやゆよらりるれろわをんァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソタダチッツテデトドナニネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロワンー一上下不与両並中主了予事二互交人今介他付代令以件任休伸位低体作使例供依価係保信修個倍値停側傍備像優元先光入全公共具典内再凍処出分切初別利到制削前割力加効動勧化十協単去参及反収取古可右号各合同名向含告周呼命問善回囲固国圧在地垂型埋域基報場境増壊声売変外多大央失奨妥始子字存学安完定宛実密対専射小少岐左差巻帰常幅平年度座延式引弱張強当形影役待後従得御復微心必忘応性恐情意感態成我戻所手扱投択押拡持指振挿排探接推描提換損摩撃撮操改敗数整文料断新方既日早明映昨時景更書替最有望期未本来析枚果枠栄検概構標権機欄次止正歪残毎比水永求汎決況法波注海消深混済減測源準滑演点無照版牲特犠状獲率現理生用申画界番異疑発登的目直相瞬知短破確示禁秒称移程種穴空立端符等算管範簡粋精約純索細終組結統続維緑線編縮績繰置翻者耗背能自致般良色荷行表装補製複要見規視覧観解言計記設許訳証試詳認語説読調識警護象負販費質赤起超跡転軸軽較込近返追送逆通速連進遅遊達遠適遷選部重野量録長閉開間関防降限除隅隠集離電青非面音響頂順領頭頻頼題類飛高鮮黒%?X";
const int chars_length = sizeof(chars) / sizeof(chars[0]);
// clang-format on

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -126,6 +126,14 @@ if(USE_X11)
target_include_directories(frontend-common PRIVATE "${X11_INCLUDE_DIR}")
endif()
if(USE_DBUS)
target_compile_definitions(frontend-common PRIVATE USE_DBUS)
find_package(PkgConfig REQUIRED)
pkg_check_modules(DBUS REQUIRED dbus-1)
target_include_directories(frontend-common PRIVATE ${DBUS_INCLUDE_DIRS})
target_link_libraries(frontend-common PRIVATE ${DBUS_LINK_LIBRARIES})
endif()
if(ENABLE_DISCORD_PRESENCE)
target_compile_definitions(frontend-common PUBLIC -DWITH_DISCORD_PRESENCE=1)
target_link_libraries(frontend-common PRIVATE discord-rpc)

View File

@ -705,8 +705,8 @@ bool D3D11HostDisplay::Render(bool skip_present)
return true;
}
bool D3D11HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
GPUTexture::Format* out_format)
bool D3D11HostDisplay::RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect,
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format)
{
static constexpr GPUTexture::Format hdformat = GPUTexture::Format::RGBA8;
@ -720,20 +720,18 @@ bool D3D11HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>*
if (HasDisplayTexture())
{
const auto [left, top, draw_width, draw_height] = CalculateDrawRect(width, height);
if (!m_post_processing_chain.IsEmpty())
{
ApplyPostProcessingChain(render_texture.GetD3DRTV(), left, top, draw_width, draw_height,
static_cast<D3D11::Texture*>(m_display_texture), m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
width, height);
ApplyPostProcessingChain(render_texture.GetD3DRTV(), draw_rect.left, draw_rect.top, draw_rect.GetWidth(),
draw_rect.GetHeight(), static_cast<D3D11::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, width, height);
}
else
{
RenderDisplay(left, top, draw_width, draw_height, static_cast<D3D11::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, IsUsingLinearFiltering());
RenderDisplay(draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight(),
static_cast<D3D11::Texture*>(m_display_texture), m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering());
}
}

View File

@ -67,8 +67,8 @@ public:
void SetVSync(bool enabled) override;
bool Render(bool skip_present) override;
bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
GPUTexture::Format* out_format) override;
bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, std::vector<u32>* out_pixels,
u32* out_stride, GPUTexture::Format* out_format) override;
static AdapterAndModeList StaticGetAdapterAndModeList();

View File

@ -615,8 +615,8 @@ bool D3D12HostDisplay::Render(bool skip_present)
return true;
}
bool D3D12HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
GPUTexture::Format* out_format)
bool D3D12HostDisplay::RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect,
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format)
{
static constexpr DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM;
static constexpr GPUTexture::Format hdformat = GPUTexture::Format::RGBA8;
@ -630,14 +630,13 @@ bool D3D12HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>*
}
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
const auto [left, top, draw_width, draw_height] = CalculateDrawRect(width, height);
if (HasDisplayTexture() && !m_post_processing_chain.IsEmpty())
{
ApplyPostProcessingChain(cmdlist, &render_texture, left, top, width, height,
static_cast<D3D12::Texture*>(m_display_texture), m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
width, height);
ApplyPostProcessingChain(cmdlist, &render_texture, draw_rect.left, draw_rect.top, draw_rect.GetWidth(),
draw_rect.GetHeight(), static_cast<D3D12::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, width, height);
}
else
{
@ -647,9 +646,9 @@ bool D3D12HostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>*
if (HasDisplayTexture())
{
RenderDisplay(cmdlist, left, top, draw_width, draw_height, static_cast<D3D12::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, IsUsingLinearFiltering());
RenderDisplay(cmdlist, draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight(),
static_cast<D3D12::Texture*>(m_display_texture), m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering());
}
}

View File

@ -66,8 +66,8 @@ public:
void SetVSync(bool enabled) override;
bool Render(bool skip_present) override;
bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
GPUTexture::Format* out_format) override;
bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, std::vector<u32>* out_pixels,
u32* out_stride, GPUTexture::Format* out_format) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;

View File

@ -680,8 +680,8 @@ bool OpenGLHostDisplay::Render(bool skip_present)
return true;
}
bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
GPUTexture::Format* out_format)
bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect,
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format)
{
GL::Texture texture;
if (!texture.Create(width, height, 1, 1, 1, GPUTexture::Format::RGBA8, nullptr, 0) || !texture.CreateFramebuffer())
@ -692,14 +692,13 @@ bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>
glDisable(GL_SCISSOR_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
const auto [left, top, draw_width, draw_height] = CalculateDrawRect(width, height);
if (HasDisplayTexture() && !m_post_processing_chain.IsEmpty())
{
ApplyPostProcessingChain(texture.GetGLFramebufferID(), left, height - top - draw_height, draw_width, draw_height,
static_cast<GL::Texture*>(m_display_texture), m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
width, height);
ApplyPostProcessingChain(texture.GetGLFramebufferID(), draw_rect.left,
height - draw_rect.top - draw_rect.GetHeight(), draw_rect.GetWidth(),
draw_rect.GetHeight(), static_cast<GL::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, width, height);
}
else
{
@ -708,9 +707,10 @@ bool OpenGLHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>
if (HasDisplayTexture())
{
RenderDisplay(left, height - top - draw_height, draw_width, draw_height,
static_cast<GL::Texture*>(m_display_texture), m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering());
RenderDisplay(draw_rect.left, height - draw_rect.top - draw_rect.GetHeight(), draw_rect.GetWidth(),
draw_rect.GetHeight(), static_cast<GL::Texture*>(m_display_texture), m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
IsUsingLinearFiltering());
}
}

View File

@ -55,8 +55,8 @@ public:
void SetVSync(bool enabled) override;
bool Render(bool skip_present) override;
bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
GPUTexture::Format* out_format) override;
bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, std::vector<u32>* out_pixels,
u32* out_stride, GPUTexture::Format* out_format) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;

View File

@ -3,17 +3,18 @@
#include "common/log.h"
#include "common/string.h"
#include "platform_misc.h"
#include "input_manager.h"
#include "platform_misc.h"
#include <cinttypes>
Log_SetChannel(FrontendCommon);
#ifdef USE_X11
#include <cstdio>
#include <spawn.h>
#include <sys/wait.h>
#include <unistd.h>
#if !defined(USE_DBUS) && defined(USE_X11)
#include <cstdio>
#include <sys/wait.h>
static bool SetScreensaverInhibitX11(bool inhibit, const WindowInfo& wi)
{
TinyString command;
@ -38,10 +39,80 @@ static bool SetScreensaverInhibitX11(bool inhibit, const WindowInfo& wi)
return true;
}
#endif // USE_X11
#elif defined(USE_DBUS)
#include <dbus/dbus.h>
bool ChangeScreenSaverStateDBus(const bool inhibit_requested, const char* program_name, const char* reason)
{
static dbus_uint32_t s_cookie;
// "error_dbus" doesn't need to be cleared in the end with "dbus_message_unref" at least if there is
// no error set, since calling "dbus_error_free" reinitializes it like "dbus_error_init" after freeing.
DBusError error_dbus;
dbus_error_init(&error_dbus);
DBusConnection* connection = nullptr;
DBusMessage* message = nullptr;
DBusMessage* response = nullptr;
// Initialized here because initializations should be before "goto" statements.
const char* bus_method = (inhibit_requested) ? "Inhibit" : "UnInhibit";
// "dbus_bus_get" gets a pointer to the same connection in libdbus, if exists, without creating a new connection.
// this doesn't need to be deleted, except if there's an error then calling "dbus_connection_unref", to free it,
// might be better so a new connection is established on the next try.
if (!(connection = dbus_bus_get(DBUS_BUS_SESSION, &error_dbus)) || (dbus_error_is_set(&error_dbus)))
goto cleanup;
if (!(message = dbus_message_new_method_call("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver", bus_method)))
goto cleanup;
// Initialize an append iterator for the message, gets freed with the message.
DBusMessageIter message_itr;
dbus_message_iter_init_append(message, &message_itr);
if (inhibit_requested)
{
// Append process/window name.
if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &program_name))
goto cleanup;
// Append reason for inhibiting the screensaver.
if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &reason))
goto cleanup;
}
else
{
// Only Append the cookie.
if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_UINT32, &s_cookie))
goto cleanup;
}
// Send message and get response.
if (!(response =
dbus_connection_send_with_reply_and_block(connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error_dbus)) ||
dbus_error_is_set(&error_dbus))
goto cleanup;
if (inhibit_requested)
{
// Get the cookie from the response message.
if (!dbus_message_get_args(response, &error_dbus, DBUS_TYPE_UINT32, &s_cookie, DBUS_TYPE_INVALID))
goto cleanup;
}
dbus_message_unref(message);
dbus_message_unref(response);
return true;
cleanup:
if (dbus_error_is_set(&error_dbus))
dbus_error_free(&error_dbus);
if (connection)
dbus_connection_unref(connection);
if (message)
dbus_message_unref(message);
if (response)
dbus_message_unref(response);
return false;
}
#endif
static bool SetScreensaverInhibit(bool inhibit)
{
#ifdef USE_DBUS
return ChangeScreenSaverStateDBus(inhibit, "DuckStation", "DuckStation VM is running.");
#else
std::optional<WindowInfo> wi(Host::GetTopLevelWindowInfo());
if (!wi.has_value())
{
@ -60,6 +131,7 @@ static bool SetScreensaverInhibit(bool inhibit)
Log_ErrorPrintf("Unknown type: %u", static_cast<unsigned>(wi->type));
return false;
}
#endif
}
static bool s_screensaver_suspended;

View File

@ -677,8 +677,8 @@ bool VulkanHostDisplay::Render(bool skip_present)
return true;
}
bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
GPUTexture::Format* out_format)
bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect,
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format)
{
// in theory we could do this without a swap chain, but postprocessing assumes it for now...
if (!m_swap_chain)
@ -746,20 +746,19 @@ bool VulkanHostDisplay::RenderScreenshot(u32 width, u32 height, std::vector<u32>
"VulkanHostDisplay::RenderScreenshot: %ux%u", width, height);
tex.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
const auto [left, top, draw_width, draw_height] = CalculateDrawRect(width, height);
if (!m_post_processing_chain.IsEmpty())
{
ApplyPostProcessingChain(fb, left, top, draw_width, draw_height, static_cast<Vulkan::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, width, height);
ApplyPostProcessingChain(fb, draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight(),
static_cast<Vulkan::Texture*>(m_display_texture), m_display_texture_view_x,
m_display_texture_view_y, m_display_texture_view_width, m_display_texture_view_height,
width, height);
}
else
{
BeginSwapChainRenderPass(fb, width, height);
RenderDisplay(left, top, draw_width, draw_height, static_cast<Vulkan::Texture*>(m_display_texture),
m_display_texture_view_x, m_display_texture_view_y, m_display_texture_view_width,
m_display_texture_view_height, IsUsingLinearFiltering());
RenderDisplay(draw_rect.left, draw_rect.top, draw_rect.GetWidth(), draw_rect.GetHeight(),
static_cast<Vulkan::Texture*>(m_display_texture), m_display_texture_view_x, m_display_texture_view_y,
m_display_texture_view_width, m_display_texture_view_height, IsUsingLinearFiltering());
}
vkCmdEndRenderPass(g_vulkan_context->GetCurrentCommandBuffer());

View File

@ -59,8 +59,8 @@ public:
void SetVSync(bool enabled) override;
bool Render(bool skip_present) override;
bool RenderScreenshot(u32 width, u32 height, std::vector<u32>* out_pixels, u32* out_stride,
GPUTexture::Format* out_format) override;
bool RenderScreenshot(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, std::vector<u32>* out_pixels,
u32* out_stride, GPUTexture::Format* out_format) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;