Compare commits

...

16 Commits

Author SHA1 Message Date
Ty
de03d2f672 Debugger: Properly reset the breakpoint skip first when skipped 2026-01-11 14:32:21 -05:00
YukiXXL
ae33c3d991 GameDB: SFOS cop2 fixes. (#13460)
SCES-52033:
Fixed "Cop2 problems." code that. Second address was wrong and was producing a bug.

SCUS-97264:
added fixes for Review Prototype (cop2, other languages)

SCUS-97397:
added fixes (cop2)

TCES-52033:
added fixes (cop2)
2026-01-11 10:12:31 +01:00
refractionpcsx2
61280a945d GS/HW: Tweak Native Scale Upscaled to work in more scenarios 2026-01-11 09:36:00 +01:00
PCSX2 Bot
e82712bf52 [ci skip] Qt: Update Base Translation. 2026-01-11 09:28:44 +01:00
refractionpcsx2
06307abd03 GS/HW: Only enable depth writes/floor calculation if depth is output 2026-01-11 02:36:55 +01:00
refractionpcsx2
daf735b047 GS/HW: Floor depth writes to improve Z testing 2026-01-11 02:36:55 +01:00
JordanTheToaster
f591c88aff GS/HW: Add Limit 24 Bit Depth Hack 2026-01-10 15:53:25 -05:00
refractionpcsx2
ca47a08882 GS: Fix up adding GS_to_GS transfers to the draw_transfers list 2026-01-10 15:50:59 -05:00
refractionpcsx2
92adacf99e GS: Disallow flipped GS->GS transfer when destination overwrites source 2026-01-10 15:50:59 -05:00
JordanTheToaster
43e5ec25ab microVU: Ignore MAC flags in block match if not required 2026-01-10 15:49:57 -05:00
KamFretoZ
1018b75847 DiscordRPC: Simplify Game Icon Retrieval 2026-01-10 15:49:45 -05:00
Shiva9361
3871d1bd5d Qt: Patch for distinguishing left and right variants of shift/alt/ctrl keys 2026-01-10 15:49:18 -05:00
Mrlinkwii
976d4a8dbb Revert "CI/Windows: Disable Qt's PCRE2 JIT"
This reverts commit 1ec4c248fb.
2026-01-10 15:47:32 -05:00
Ty
40b1b9b717 Qt: Add an EE SIO RX input textbox to the log window 2026-01-10 15:46:59 -05:00
Ty
a3b817cb1f Core: Use deque for EE SIO RX/TX FIFOs 2026-01-10 15:46:59 -05:00
SternXD
83e152cd21 GameDB: Add post-bloom alignment and native scaling for Urban Reign 2026-01-10 17:18:43 +01:00
42 changed files with 1261 additions and 837 deletions

View File

@@ -201,10 +201,6 @@ echo Building Qt base...
rmdir /S /Q "qtbase-everywhere-src-%QT%"
%SEVENZIP% x "qtbase-everywhere-src-%QT%.zip" || goto error
cd "qtbase-everywhere-src-%QT%" || goto error
rem Disable the PCRE2 JIT, it doesn't properly verify AVX2 support.
%PATCH% -p1 < "%SCRIPTDIR%\qtbase-disable-pcre2-jit.patch" || goto error
cmake -B build -DFEATURE_sql=OFF -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" %FORCEPDB% -DINPUT_gui=yes -DINPUT_widgets=yes -DINPUT_ssl=yes -DINPUT_openssl=no -DINPUT_schannel=yes -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON %QTBUILDSPEC% || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error

View File

@@ -1,35 +0,0 @@
--- qtbase/src/3rdparty/pcre2/CMakeLists.txt 2024-03-19 08:46:43.000000000 -0700
+++ qtbase/src/3rdparty/pcre2/CMakeLists.txt 2024-06-06 21:52:20.539619500 -0700
@@ -41,6 +41,7 @@
src/pcre2_xclass.c
DEFINES
HAVE_CONFIG_H
+ PCRE2_DISABLE_JIT
PUBLIC_DEFINES
PCRE2_CODE_UNIT_WIDTH=16
PUBLIC_INCLUDE_DIRECTORIES
@@ -52,23 +53,8 @@
## Scopes:
#####################################################################
-qt_internal_extend_target(BundledPcre2 CONDITION QNX OR UIKIT
- DEFINES
- PCRE2_DISABLE_JIT
-)
-
-qt_internal_extend_target(BundledPcre2 CONDITION (TEST_architecture_arch STREQUAL "arm") AND WIN32
- DEFINES
- PCRE2_DISABLE_JIT
-)
-
-qt_internal_extend_target(BundledPcre2 CONDITION (TEST_architecture_arch STREQUAL "arm64") AND WIN32
- DEFINES
- PCRE2_DISABLE_JIT
-)
-
if (APPLE)
- target_compile_options(BundledPcre2 PRIVATE "SHELL:-Xarch_arm64 -DPCRE2_DISABLE_JIT")
+ target_compile_options(BundledPcre2 PRIVATE "SHELL:-Xarch_arm64")
endif()
qt_internal_extend_target(BundledPcre2 CONDITION WIN32

View File

@@ -2239,6 +2239,8 @@ SCAJ-20152:
gsHWFixes:
alignSprite: 1 # Fixes vertical lines.
textureInsideRT: 1 # Fixes corruption.
halfPixelOffset: 4 # Aligns post bloom.
nativeScaling: 2 # Fixes post effects.
getSkipCount: "GSC_UrbanReign"
SCAJ-20153:
name: "コード・エイジ コマンダーズ"
@@ -5600,7 +5602,7 @@ SCES-52033:
author=refraction
// Cop2 problems.
patch=0,EE,003953F8,word,48438000
patch=0,EE,003735FC,word,4B06521B
patch=0,EE,003953FC,word,4B06521B
author=YukiXXL
// Speed Correction (25 FPS)
patch=1,EE,00175E1C,extended,00000019
@@ -6229,6 +6231,8 @@ SCES-53688:
gsHWFixes:
alignSprite: 1 # Fixes vertical lines.
textureInsideRT: 1 # Fixes corruption.
halfPixelOffset: 4 # Aligns post bloom.
nativeScaling: 2 # Fixes post effects.
getSkipCount: "GSC_UrbanReign"
SCES-53795:
name: "SingStar - '80s"
@@ -7698,6 +7702,8 @@ SCKA-20065:
gsHWFixes:
alignSprite: 1 # Fixes vertical lines.
textureInsideRT: 1 # Fixes corruption.
halfPixelOffset: 4 # Aligns post bloom.
nativeScaling: 2 # Fixes post effects.
getSkipCount: "GSC_UrbanReign"
SCKA-20066:
name: "아이토이 - 플레이 3"
@@ -11686,6 +11692,15 @@ SCUS-97264:
// Cop2 problems.
patch=0,EE,003735F8,word,48438000
patch=0,EE,003735FC,word,4B06521B
062BC79E:
content: |-
author=YukiXXL
// Cop2 problems.
patch=0,EE,00395D28,word,48438000
patch=0,EE,00395D2C,word,4B06521B
// Other languages SNDVAG fix
patch=1,EE,204A0C3C,word,482E7325
patch=1,EE,204A0C40,word,0000474F
SCUS-97265:
name: "Jak II"
region: "NTSC-U"
@@ -12073,12 +12088,21 @@ SCUS-97396:
gsHWFixes:
PCRTCOffsets: 1 # Fixes boot videos screen size.
SCUS-97397:
name: "Syphon Filter - The Omega Strain"
name: "Syphon Filter - The Omega Strain Public Beta 1.0"
region: "NTSC-U"
gsHWFixes:
autoFlush: 2 # Fixes lights going through walls.
preloadFrameData: 1 # Fixes light flicker.
halfPixelOffset: 2 # Corrects light position.
gameFixes:
- EETimingHack # Fixes random hangs.
patches:
C909A32E:
content: |-
author=YukiXXL
// Cop2 problems.
patch=0,EE,00367798,word,48438000
patch=0,EE,0036779C,word,4B06521B
SCUS-97398:
name: "Siren [Demo]"
region: "NTSC-U"
@@ -35952,6 +35976,8 @@ SLPM-60272:
gsHWFixes:
alignSprite: 1 # Fixes vertical lines.
textureInsideRT: 1 # Fixes corruption.
halfPixelOffset: 4 # Aligns post bloom.
nativeScaling: 2 # Fixes post effects.
getSkipCount: "GSC_UrbanReign"
SLPM-60273:
name: "絶体絶命都市2 -凍てついた記憶たち- [体験版 Type-B]"
@@ -60340,6 +60366,8 @@ SLPS-25557:
gsHWFixes:
alignSprite: 1 # Fixes vertical lines.
textureInsideRT: 1 # Fixes corruption.
halfPixelOffset: 4 # Aligns post bloom.
nativeScaling: 2 # Fixes post effects.
getSkipCount: "GSC_UrbanReign"
SLPS-25558:
name: "ネオジオ バトルコロシアム"
@@ -70360,6 +70388,8 @@ SLUS-21209:
gsHWFixes:
alignSprite: 1 # Fixes vertical lines.
textureInsideRT: 1 # Fixes corruption.
halfPixelOffset: 4 # Aligns post bloom.
nativeScaling: 2 # Fixes post effects.
getSkipCount: "GSC_UrbanReign"
SLUS-21212:
name: "Spartan - Total Warrior"
@@ -75768,6 +75798,9 @@ TCES-52033:
0DDA2728:
content: |-
author=YukiXXL
// Cop2 problems.
patch=0,EE,0038E8E8,word,48438000
patch=0,EE,0038E8EC,word,4B06521B
// Speed Correction (25 FPS)
patch=1,EE,00175a7c,extended,00000019
patch=1,EE,00175848,extended,00000019

View File

@@ -71,6 +71,7 @@
#define PS_DITHER 0
#define PS_DITHER_ADJUST 0
#define PS_ZCLAMP 0
#define PS_ZFLOOR 0
#define PS_SCANMSK 0
#define PS_AUTOMATIC_LOD 0
#define PS_MANUAL_LOD 0
@@ -138,7 +139,7 @@ struct PS_OUTPUT
#endif
#endif
#endif
#if PS_ZCLAMP
#if PS_ZCLAMP || PS_ZFLOOR
float depth : SV_Depth;
#endif
};
@@ -1209,8 +1210,16 @@ PS_OUTPUT ps_main(PS_INPUT input)
#endif // PS_DATE != 1/2
#if PS_ZFLOOR
float depth_value = floor(input.p.z * exp2(32.0f)) * exp2(-32.0f);
#else
float depth_value = input.p.z;
#endif
#if PS_ZCLAMP
output.depth = min(input.p.z, MaxDepthPS);
output.depth = min(depth_value, MaxDepthPS);
#elif PS_ZFLOOR
output.depth = depth_value;
#endif
return output;

View File

@@ -1143,7 +1143,16 @@ void ps_main()
#endif
#endif
#if PS_ZCLAMP
gl_FragDepth = min(gl_FragCoord.z, MaxDepthPS);
#if PS_ZFLOOR
float depth_value = floor(gl_FragCoord.z * exp2(32.0f)) * exp2(-32.0f);
#else
float depth_value = gl_FragCoord.z;
#endif
#if PS_ZCLAMP
gl_FragDepth = min(depth_value, MaxDepthPS);
#elif PS_ZFLOOR
gl_FragDepth = depth_value;
#endif
}

View File

@@ -288,6 +288,7 @@ void main()
#define PS_DITHER 0
#define PS_DITHER_ADJUST 0
#define PS_ZCLAMP 0
#define PS_ZFLOOR 0
#define PS_FEEDBACK_LOOP 0
#define PS_TEX_IS_FB 0
#endif
@@ -1400,8 +1401,16 @@ void main()
#endif
#endif
#if PS_ZFLOOR
float depth_value = floor(gl_FragCoord.z * exp2(32.0f)) * exp2(-32.0f);;
#else
float depth_value = gl_FragCoord.z;
#endif
#if PS_ZCLAMP
gl_FragDepth = min(gl_FragCoord.z, MaxDepthPS);
#elif PS_ZFLOOR
gl_FragDepth = depth_value;
#endif
#endif // PS_DATE

View File

@@ -5,10 +5,14 @@
#include "MainWindow.h"
#include "QtHost.h"
#include "SettingWidgetBinder.h"
#include "VMManager.h"
#include <QtCore/QLatin1StringView>
#include <QtCore/QUtf8StringView>
#include <QtGui/QIcon>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QScrollBar>
@@ -46,6 +50,12 @@ void LogWindow::updateSettings()
const bool new_enabled = Host::GetBaseBoolSettingValue("Logging", "EnableLogWindow", false) && !Host::InNoGUIMode();
const bool attach_to_main = Host::GetBaseBoolSettingValue("Logging", "AttachLogWindowToMainWindow", true);
const bool curr_enabled = Log::IsHostOutputEnabled();
const bool input_enabled = Host::GetBaseBoolSettingValue("Logging", "ShowEESIOInput");
if(g_log_window && g_log_window->m_line_input)
{
g_log_window->m_input_widget->setVisible(input_enabled);
}
if (new_enabled == curr_enabled)
{
@@ -171,6 +181,10 @@ void LogWindow::createUi()
action->setCheckable(true);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, action, "Logging", "EnableTimestamps", true);
action = settings_menu->addAction(tr("Show EE SIO &Input"));
action->setCheckable(true);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, action, "Logging", "ShowEESIOInput", false);
settings_menu->addSeparator();
// TODO: Log Level
@@ -181,6 +195,30 @@ void LogWindow::createUi()
m_text->setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
m_text->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
m_text->setWordWrapMode(QTextOption::WrapAnywhere);
m_text->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_line_input = new QLineEdit(this);
connect(m_line_input, &QLineEdit::returnPressed, this, &LogWindow::onInputEntered);
m_local_echo_checkbox = new QCheckBox(tr("Local Echo"), this);
m_local_echo_checkbox->setChecked(m_local_echo);
connect(m_local_echo_checkbox, &QCheckBox::checkStateChanged, this, [&](Qt::CheckState state)
{
m_local_echo = state == Qt::CheckState::Checked;
});
m_newline_on_enter_checkbox = new QCheckBox(tr("Newline on send"), this);
m_newline_on_enter_checkbox->setChecked(m_newline_on_enter);
connect(m_newline_on_enter_checkbox, &QCheckBox::checkStateChanged, this, [&](Qt::CheckState state)
{
m_newline_on_enter = state == Qt::CheckState::Checked;
});
m_input_hbox = new QHBoxLayout(this);
m_input_hbox->addWidget(m_line_input, 1);
m_input_hbox->addWidget(m_local_echo_checkbox);
m_input_hbox->addWidget(m_newline_on_enter_checkbox);
m_input_hbox->setSpacing(8);
#if defined(_WIN32)
QFont font("Consolas");
@@ -194,7 +232,19 @@ void LogWindow::createUi()
#endif
m_text->setFont(font);
setCentralWidget(m_text);
QWidget* central_widget = new QWidget(this);
m_input_widget = new QWidget(this);
m_input_widget->setVisible(Host::GetBaseBoolSettingValue("Logging", "ShowEESIOInput"));
m_input_widget->setLayout(m_input_hbox);
QVBoxLayout* vlayout = new QVBoxLayout(central_widget);
vlayout->setContentsMargins(0, 0, 0, 0);
vlayout->setSpacing(0);
vlayout->addWidget(m_text);
vlayout->addWidget(m_input_widget);
central_widget->setLayout(vlayout);
setCentralWidget(central_widget);
}
void LogWindow::onClearTriggered()
@@ -361,6 +411,31 @@ void LogWindow::appendMessage(quint32 level, quint32 color, const QString& messa
}
}
void LogWindow::onInputEntered()
{
QString text = m_line_input->text();
if (text.isEmpty() && !m_newline_on_enter)
return;
if(m_newline_on_enter)
text.append('\n');
std::string str = text.toUtf8().toStdString();
if(VMManager::WriteBytesToEESIORXFIFO({reinterpret_cast<const u8*>(str.data()), str.size()}))
{
m_line_input->clear();
if(m_local_echo)
// appendMessage expects a newline to be at the end of the string
appendMessage(0, 0, m_newline_on_enter ? text : (text + '\n') );
QTextCursor cursor(m_text->textCursor());
cursor.movePosition(QTextCursor::End);
m_text->setTextCursor(cursor);
}
}
void LogWindow::saveSize()
{
const int current_width = Host::GetBaseIntSettingValue("UI", "LogWindowWidth", DEFAULT_WIDTH);

View File

@@ -5,6 +5,9 @@
#include "common/Console.h"
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QPlainTextEdit>
@@ -36,6 +39,7 @@ private Q_SLOTS:
void onClearTriggered();
void onSaveTriggered();
void appendMessage(quint32 level, quint32 color, const QString& message);
void onInputEntered();
private:
static constexpr int DEFAULT_WIDTH = 750;
@@ -45,8 +49,15 @@ private:
void restoreSize();
QPlainTextEdit* m_text;
QLineEdit* m_line_input;
QMenu* m_level_menu;
QWidget* m_input_widget;
QHBoxLayout* m_input_hbox;
QCheckBox* m_local_echo_checkbox;
QCheckBox* m_newline_on_enter_checkbox;
bool m_local_echo = false;
bool m_newline_on_enter = true;
bool m_attached_to_main_window = true;
bool m_destroying = false;
};

View File

@@ -67,6 +67,17 @@ struct KeyCodeName
const char* icon_name;
};
// Extended keys for left/right versions, Non conflicting with Qt::Key enum
enum ExtendedKeys
{
Key_RightShift = 0x01100022,
Key_LeftShift = 0x01100023,
Key_RightControl = 0x01100024,
Key_LeftControl = 0x01100025,
Key_RightAlt = 0x01100026,
Key_LeftAlt = 0x01100027,
};
static constexpr const KeyCodeName s_qt_key_names[] = {
{Qt::Key_Escape, "Escape", ICON_PF_ESC},
{Qt::Key_Tab, "Tab", ICON_PF_TAB},
@@ -92,6 +103,13 @@ static constexpr const KeyCodeName s_qt_key_names[] = {
{Qt::Key_Control, "Control", ICON_PF_CTRL},
{Qt::Key_Meta, "Meta", ICON_PF_SUPER},
{Qt::Key_Alt, "Alt", ICON_PF_ALT},
// patch for left and right versions of the keys
{ExtendedKeys::Key_LeftShift, "LShift", ICON_PF_SHIFT},
{ExtendedKeys::Key_RightShift, "RShift", ICON_PF_SHIFT},
{ExtendedKeys::Key_LeftControl, "LCtrl", ICON_PF_CTRL},
{ExtendedKeys::Key_RightControl, "RCtrl", ICON_PF_CTRL},
{ExtendedKeys::Key_LeftAlt, "LAlt", ICON_PF_ALT},
{ExtendedKeys::Key_RightAlt, "RAlt", ICON_PF_ALT},
{Qt::Key_CapsLock, "CapsLock", ICON_PF_CAPS},
{Qt::Key_NumLock, "NumLock", ICON_PF_NUMLOCK},
{Qt::Key_ScrollLock, "ScrollLock", ICON_PF_SCRLK},
@@ -575,6 +593,41 @@ u32 QtUtils::KeyEventToCode(const QKeyEvent* ev)
const u8 keycode = set_keycode ? map_text_to_keycode(text) : 0;
int key = ev->key();
if (key == Qt::Key_Shift || key == Qt::Key_Alt || key == Qt::Key_Control)
{
#if defined(Q_OS_WIN) or defined(Q_OS_LINUX)
// Scan codes (Tested it)
// 0x2A : Left shift
// 0x36 : Right shift
// 0x1D : Left ctrl
// 0xE01D : Right ctrl
// 0x38 : Left alt
// right alt can become ctrl + right alt in some keyboard layouts (windows)
// but thats fine for our use case
switch (ev->nativeScanCode())
{
case 0x2A:
key = ExtendedKeys::Key_LeftShift;
break;
case 0x36:
key = ExtendedKeys::Key_RightShift;
break;
case 0x1D:
key = ExtendedKeys::Key_LeftControl;
break;
case 0xE01D:
key = ExtendedKeys::Key_RightControl;
break;
case 0x38:
key = ExtendedKeys::Key_LeftAlt;
break;
default:
break;
}
#endif
}
if (keycode != 0)
key = keycode; // Override key if mapped

View File

@@ -14,8 +14,31 @@
<string/>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
<item row="3" column="1">
<widget class="QComboBox" name="hwAutoFlush">
<item row="8" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="6" column="0">
<widget class="QLabel" name="skipDrawLabel">
<property name="text">
<string>Skip Draw Range:</string>
</property>
<property name="buddy">
<cstring>skipDrawStart</cstring>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="textureInsideRt">
<item>
<property name="text">
<string>Disabled (Default)</string>
@@ -23,12 +46,12 @@
</item>
<item>
<property name="text">
<string>Enabled (Sprites Only)</string>
<string>Inside Target</string>
</property>
</item>
<item>
<property name="text">
<string>Enabled (All Primitives)</string>
<string>Merge Targets</string>
</property>
</item>
</widget>
@@ -43,41 +66,6 @@
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="skipDrawLabel">
<property name="text">
<string>Skip Draw Range:</string>
</property>
<property name="buddy">
<cstring>skipDrawStart</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cpuCLUTRender">
<property name="currentText">
<string extracomment="0 (Disabled)">0 (Disabled)</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>0 (Disabled)</string>
</property>
</item>
<item>
<property name="text">
<string>1 (Normal)</string>
</property>
</item>
<item>
<property name="text">
<string>2 (Aggressive)</string>
</property>
</item>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="cpuCLUTRenderLayout" stretch="0,0">
<item>
@@ -160,8 +148,36 @@
</item>
</layout>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="textureInsideRt">
<item row="3" column="0">
<widget class="QLabel" name="hwAutoFlushLabel">
<property name="text">
<string>Auto Flush:</string>
</property>
<property name="buddy">
<cstring>hwAutoFlush</cstring>
</property>
</widget>
</item>
<item row="6" column="1">
<layout class="QHBoxLayout" name="skipDrawLayout">
<item>
<widget class="QSpinBox" name="skipDrawStart">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="skipDrawEnd">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="gpuTargetCLUTMode">
<item>
<property name="text">
<string>Disabled (Default)</string>
@@ -169,12 +185,57 @@
</item>
<item>
<property name="text">
<string>Inside Target</string>
<string>Enabled (Exact Match)</string>
</property>
</item>
<item>
<property name="text">
<string>Merge Targets</string>
<string>Enabled (Check Inside Target)</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="cpuCLUTRenderLabel">
<property name="text">
<string>CPU Sprite Render Size:</string>
</property>
<property name="buddy">
<cstring>cpuSpriteRenderBW</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="gpuTargetCLUTLabel">
<property name="text">
<string extracomment="CLUT: Color Look Up Table, often referred to as a palette in non-PS2 things. GPU Target CLUT: GPU handling of when a game uses data from a render target as a CLUT.">GPU Target CLUT:</string>
</property>
<property name="buddy">
<cstring>gpuTargetCLUTMode</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cpuCLUTRender">
<property name="currentText">
<string extracomment="0 (Disabled)">0 (Disabled)</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>0 (Disabled)</string>
</property>
</item>
<item>
<property name="text">
<string>1 (Normal)</string>
</property>
</item>
<item>
<property name="text">
<string>2 (Aggressive)</string>
</property>
</item>
</widget>
@@ -189,7 +250,26 @@
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<item row="3" column="1">
<widget class="QComboBox" name="hwAutoFlush">
<item>
<property name="text">
<string>Disabled (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Enabled (Sprites Only)</string>
</property>
</item>
<item>
<property name="text">
<string>Enabled (All Primitives)</string>
</property>
</item>
</widget>
</item>
<item row="7" column="0" colspan="2">
<layout class="QGridLayout" name="hwFixesLayout">
<item row="4" column="0">
<widget class="QCheckBox" name="estimateTextureRegion">
@@ -256,28 +336,8 @@
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="hwAutoFlushLabel">
<property name="text">
<string>Auto Flush:</string>
</property>
<property name="buddy">
<cstring>hwAutoFlush</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="gpuTargetCLUTLabel">
<property name="text">
<string extracomment="CLUT: Color Look Up Table, often referred to as a palette in non-PS2 things. GPU Target CLUT: GPU handling of when a game uses data from a render target as a CLUT.">GPU Target CLUT:</string>
</property>
<property name="buddy">
<cstring>gpuTargetCLUTMode</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="gpuTargetCLUTMode">
<item row="5" column="1">
<widget class="QComboBox" name="limit24BitDepth">
<item>
<property name="text">
<string>Disabled (Default)</string>
@@ -285,57 +345,23 @@
</item>
<item>
<property name="text">
<string>Enabled (Exact Match)</string>
<string>Prioritize Upper</string>
</property>
</item>
<item>
<property name="text">
<string>Enabled (Check Inside Target)</string>
<string>Prioritize Lower</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="cpuCLUTRenderLabel">
<item row="5" column="0">
<widget class="QLabel" name="limitDepthLabel">
<property name="text">
<string>CPU Sprite Render Size:</string>
</property>
<property name="buddy">
<cstring>cpuSpriteRenderBW</cstring>
<string>Limit 24 Bit Depth</string>
</property>
</widget>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="skipDrawLayout">
<item>
<widget class="QSpinBox" name="skipDrawStart">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="skipDrawEnd">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>

View File

@@ -147,6 +147,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
sif, m_fixes.disablePartialInvalidation, "EmuCore/GS", "UserHacks_DisablePartialInvalidation", false);
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_fixes.textureInsideRt, "EmuCore/GS", "UserHacks_TextureInsideRt", static_cast<int>(GSTextureInRtMode::Disabled));
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_fixes.limit24BitDepth, "EmuCore/GS", "UserHacks_Limit24BitDepth", static_cast<int>(GSLimit24BitDepth::Disabled));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_fixes.readTCOnClose, "EmuCore/GS", "UserHacks_ReadTCOnClose", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_fixes.estimateTextureRegion, "EmuCore/GS", "UserHacks_EstimateTextureRegion", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_fixes.gpuPaletteConversion, "EmuCore/GS", "paltex", false);
@@ -624,6 +626,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(m_fixes.textureInsideRt, tr("Texture Inside RT"), tr("Disabled"),
tr("Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer."));
dialog()->registerWidgetHelp(m_fixes.limit24BitDepth, tr("Limit 24 Bit Depth"), tr("Disabled"),
tr("Eats your cheese."));
dialog()->registerWidgetHelp(m_fixes.readTCOnClose, tr("Read Targets When Closing"), tr("Unchecked"),
tr("Flushes all targets in the texture cache back to local memory when shutting down. Can prevent lost visuals when saving "
"state or switching graphics APIs, but can also cause graphical corruption."));

File diff suppressed because it is too large Load Diff

View File

@@ -1043,21 +1043,11 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
s_has_leaderboards = has_leaderboards;
s_has_rich_presence = rc_client_has_rich_presence(client);
s_game_icon = {};
s_game_icon_url = {};
s_game_icon_url = info->badge_url;
// ensure fullscreen UI is ready for notifications
MTGS::RunOnGSThread(&ImGuiManager::InitializeFullscreenUI);
char url_buffer[URL_BUFFER_SIZE];
if (int err = rc_client_game_get_image_url(info, url_buffer, std::size(url_buffer)); err == RC_OK)
{
s_game_icon_url = url_buffer;
}
else
{
ReportRCError(err, "rc_client_game_get_image_url() failed: ");
}
if (const std::string_view badge_name = info->badge_name; !badge_name.empty())
{
s_game_icon = Path::Combine(s_image_directory, fmt::format("game_{}.png", badge_name));

View File

@@ -432,6 +432,13 @@ enum class GSTextureInRtMode : u8
MergeTargets,
};
enum class GSLimit24BitDepth : u8
{
Disabled,
PrioritizeUpper,
PrioritizeLower,
};
enum class GSBilinearDirtyMode : u8
{
Automatic,
@@ -844,6 +851,7 @@ struct Pcsx2Config
u8 UserHacks_CPUCLUTRender = 0;
GSGPUTargetCLUTMode UserHacks_GPUTargetCLUTMode = GSGPUTargetCLUTMode::Disabled;
GSTextureInRtMode UserHacks_TextureInsideRt = GSTextureInRtMode::Disabled;
GSLimit24BitDepth UserHacks_Limit24BitDepth = GSLimit24BitDepth::Disabled;
GSBilinearDirtyMode UserHacks_BilinearHack = GSBilinearDirtyMode::Automatic;
TriFiltering TriFilter = TriFiltering::Automatic;
s8 OverrideTextureBarriers = -1;

View File

@@ -11,6 +11,8 @@
std::vector<BreakPoint> CBreakPoints::breakPoints_;
u32 CBreakPoints::breakSkipFirstAtEE_ = 0;
u32 CBreakPoints::breakSkipFirstAtIop_ = 0;
bool CBreakPoints::pendingClearSkipFirstAtEE_ = false;
bool CBreakPoints::pendingClearSkipFirstAtIop_ = false;
std::vector<MemCheck> CBreakPoints::memChecks_;
std::vector<MemCheck*> CBreakPoints::cleanupMemChecks_;
bool CBreakPoints::breakpointTriggered_ = false;
@@ -391,10 +393,12 @@ void CBreakPoints::SetSkipFirst(BreakPointCpu cpu, u32 pc)
if (cpu == BREAKPOINT_EE)
{
breakSkipFirstAtEE_ = standardizeBreakpointAddress(pc);
pendingClearSkipFirstAtEE_ = false;
}
else if (cpu == BREAKPOINT_IOP)
{
breakSkipFirstAtIop_ = pc;
pendingClearSkipFirstAtIop_ = false;
}
}
@@ -407,10 +411,29 @@ u32 CBreakPoints::CheckSkipFirst(BreakPointCpu cpu, u32 cmpPc)
return 0;
}
void CBreakPoints::ClearSkipFirst()
void CBreakPoints::ClearSkipFirst(BreakPointCpu cpu)
{
breakSkipFirstAtEE_ = 0;
breakSkipFirstAtIop_ = 0;
if((cpu & BREAKPOINT_EE) != 0)
pendingClearSkipFirstAtEE_ = true;
else if ((cpu & BREAKPOINT_IOP) != 0)
pendingClearSkipFirstAtIop_ = true;
if(cpu == BREAKPOINT_IOP_AND_EE)
CommitClearSkipFirst(BREAKPOINT_IOP_AND_EE);
}
void CBreakPoints::CommitClearSkipFirst(BreakPointCpu cpu)
{
if((cpu & BREAKPOINT_EE) != 0 && pendingClearSkipFirstAtEE_)
{
pendingClearSkipFirstAtEE_ = false;
breakSkipFirstAtEE_ = 0;
}
else if ((cpu & BREAKPOINT_IOP) != 0 && pendingClearSkipFirstAtIop_)
{
pendingClearSkipFirstAtIop_ = true;
breakSkipFirstAtIop_ = 0;
}
}
const std::vector<MemCheck> CBreakPoints::GetMemCheckRanges()

View File

@@ -135,7 +135,8 @@ public:
static void SetSkipFirst(BreakPointCpu cpu, u32 pc);
static u32 CheckSkipFirst(BreakPointCpu cpu, u32 pc);
static void ClearSkipFirst();
static void ClearSkipFirst(BreakPointCpu cpu = BREAKPOINT_IOP_AND_EE);
static void CommitClearSkipFirst(BreakPointCpu cpu);
// Includes uncached addresses.
static const std::vector<MemCheck> GetMemCheckRanges();
@@ -169,9 +170,9 @@ private:
static std::vector<BreakPoint> breakPoints_;
static u32 breakSkipFirstAtEE_;
static u64 breakSkipFirstTicksEE_;
static bool pendingClearSkipFirstAtEE_;
static u32 breakSkipFirstAtIop_;
static u64 breakSkipFirstTicksIop_;
static bool pendingClearSkipFirstAtIop_;
static bool breakpointTriggered_;
static BreakPointCpu breakpointTriggeredCpu_;

View File

@@ -806,12 +806,12 @@ void GSState::DumpTransferList(const std::string& filename)
(*file) << std::endl;
// clear, EE->GS, or GS->GS
(*file) << LIST_ITEM << "type: " << (transfer.zero_clear ? "clear" : (transfer.ee_to_gs ? "EE_to_GS" : "GS_to_GS")) << std::endl;
(*file) << LIST_ITEM << "type: " << (transfer.zero_clear ? "clear" : ((transfer.transfer_type == EEGS_TransferType::EE_to_GS) ? "EE_to_GS" : "GS_to_GS")) << std::endl;
// Dump BITBLTBUF
(*file) << INDENT << "BITBLTBUF: " << OPEN_MAP;
const bool gs_to_gs = !transfer.ee_to_gs && !transfer.zero_clear;
const bool gs_to_gs = (transfer.transfer_type == EEGS_TransferType::GS_to_GS) && !transfer.zero_clear;
if (gs_to_gs)
{
@@ -857,7 +857,7 @@ void GSState::DumpTransferImages()
const GSUploadQueue& transfer = m_draw_transfers[i];
std::string filename;
if (transfer.ee_to_gs || transfer.zero_clear)
if ((transfer.transfer_type == EEGS_TransferType::EE_to_GS) || transfer.zero_clear)
{
// clear or EE->GS: only the destination info is relevant.
filename = GetDrawDumpPath("%05d_transfer%02d_%s_%04x_%d_%s_%d_%d_%d_%d.png",
@@ -2365,7 +2365,7 @@ void GSState::Write(const u8* mem, int len)
s_last_transfer_draw_n = s_n;
// Store the transfer for preloading new RT's.
if ((m_draw_transfers.size() > 0 && blit.DBP == m_draw_transfers.back().blit.DBP))
if ((m_draw_transfers.size() > 0 && blit.DBP == m_draw_transfers.back().blit.DBP && m_draw_transfers.back().transfer_type == EEGS_TransferType::EE_to_GS))
{
// Same BP, let's update the rect.
GSUploadQueue transfer = m_draw_transfers.back();
@@ -2377,7 +2377,7 @@ void GSState::Write(const u8* mem, int len)
}
else
{
const GSUploadQueue new_transfer = { blit, r, s_n, false, true };
const GSUploadQueue new_transfer = {blit, r, s_n, false, EEGS_TransferType::EE_to_GS};
m_draw_transfers.push_back(new_transfer);
}
@@ -2519,21 +2519,31 @@ void GSState::Move()
InvalidateLocalMem(m_env.BITBLTBUF, GSVector4i(sx, sy, sx + w, sy + h));
InvalidateVideoMem(m_env.BITBLTBUF, GSVector4i(dx, dy, dx + w, dy + h));
const bool overlaps = m_env.BITBLTBUF.SBP == m_env.BITBLTBUF.DBP;
const bool intersect = overlaps && !(GSVector4i(sx, sy, sx + w, sy + h).rintersect(GSVector4i(dx, dy, dx + w, dy + h)).rempty());
int xinc = 1;
int yinc = 1;
if (m_env.TRXPOS.DIRX)
{
sx += w - 1;
dx += w - 1;
xinc = -1;
// Only allow it to reverse if the destination is behind the source.
if (!intersect || (sx <= dx && (sx == dx || ((!m_env.TRXPOS.DIRY && sy >= dy) || (m_env.TRXPOS.DIRY && sy < dy)))))
{
sx += w - 1;
dx += w - 1;
xinc = -1;
}
}
if (m_env.TRXPOS.DIRY)
{
sy += h - 1;
dy += h - 1;
yinc = -1;
// Only allow it to reverse if the destination is behind the source.
if (!intersect || (sy <= dy && (sy == dy || ((!m_env.TRXPOS.DIRX && sx >= dx) || (m_env.TRXPOS.DIRX && sx < dx)))))
{
sy += h - 1;
dy += h - 1;
yinc = -1;
}
}
const GSLocalMemory::psm_t& spsm = GSLocalMemory::m_psm[m_env.BITBLTBUF.SPSM];
@@ -2556,7 +2566,7 @@ void GSState::Move()
s_last_transfer_draw_n = s_n;
// Store the transfer for preloading new RT's.
if ((m_draw_transfers.size() > 0 && m_env.BITBLTBUF.DBP == m_draw_transfers.back().blit.DBP))
if ((m_draw_transfers.size() > 0 && m_env.BITBLTBUF.DBP == m_draw_transfers.back().blit.DBP && m_draw_transfers.back().transfer_type == EEGS_TransferType::GS_to_GS))
{
// Same BP, let's update the rect.
GSUploadQueue transfer = m_draw_transfers.back();
@@ -2568,11 +2578,11 @@ void GSState::Move()
}
else
{
const GSUploadQueue new_transfer = { m_env.BITBLTBUF, r, s_n, false, false };
const GSUploadQueue new_transfer = {m_env.BITBLTBUF, r, s_n, false, EEGS_TransferType::GS_to_GS};
m_draw_transfers.push_back(new_transfer);
}
auto copy = [this, sbp, dbp, sx, sy, dx, dy, w, h, yinc, xinc](const GSOffset& dpo, const GSOffset& spo, auto&& pxCopyFn)
auto copy = [this, sbp, dbp, sx, sy, dx, dy, w, h, yinc, xinc, intersect](const GSOffset& dpo, const GSOffset& spo, auto&& pxCopyFn)
{
int _sy = sy, _dy = dy; // Faster with local copied variables, compiler optimizations are dumb
if (xinc > 0)
@@ -2584,8 +2594,6 @@ void GSState::Move()
// Copying from itself to itself (rotating textures) used in Gitaroo Man stage 8
// What probably happens is because the copy is buffered, the source stays just ahead of the destination.
// No need to do all this if the copy source/destination don't intersect, however.
const bool intersect = !(GSVector4i(sx, sy, sx + w, sy + h).rintersect(GSVector4i(dx, dy, dx + w, dy + h)).rempty());
if (intersect && sbp == dbp && (((_sy < _dy) && ((ypage + page_height) > _dy)) || ((sx < dx) && ((xpage + page_width) > dx))))
{
int starty = (yinc > 0) ? 0 : h-1;
@@ -4657,6 +4665,14 @@ __forceinline void GSState::VertexKick(u32 skip)
u32 next = m_vertex.next;
u32 xy_tail = m_vertex.xy_tail;
if (GSIsHardwareRenderer() && GSLocalMemory::m_psm[m_context->ZBUF.PSM].bpp == 32)
{
if (GSConfig.UserHacks_Limit24BitDepth == GSLimit24BitDepth::PrioritizeUpper)
m_v.XYZ.Z = ((m_v.XYZ.Z >> 8) & ~0xFF) | (m_v.XYZ.Z & 0xFF);
else if (GSConfig.UserHacks_Limit24BitDepth == GSLimit24BitDepth::PrioritizeLower)
m_v.XYZ.Z &= 0x00FFFFFF;
}
// callers should write XYZUVF to m_v.m[1] in one piece to have this load store-forwarded, either by the cpu or the compiler when this function is inlined
const GSVector4i new_v0(m_v.m[0]);

View File

@@ -211,13 +211,20 @@ protected:
void CorrectATEAlphaMinMax(const u32 atst, const int aref);
public:
enum EEGS_TransferType
{
EE_to_GS,
GS_to_GS,
GS_to_EE
};
struct GSUploadQueue
{
GIFRegBITBLTBUF blit;
GSVector4i rect;
int draw;
bool zero_clear;
bool ee_to_gs;
EEGS_TransferType transfer_type;
};
enum NoGapsType

View File

@@ -401,6 +401,7 @@ struct alignas(16) GSHWDrawConfig
// Depth clamp
u32 zclamp : 1;
u32 zfloor : 1;
// Hack
u32 tcoffsethack : 1;

View File

@@ -1760,6 +1760,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
sm.AddMacro("PS_DITHER", sel.dither);
sm.AddMacro("PS_DITHER_ADJUST", sel.dither_adjust);
sm.AddMacro("PS_ZCLAMP", sel.zclamp);
sm.AddMacro("PS_ZFLOOR", sel.zfloor);
sm.AddMacro("PS_SCANMSK", sel.scanmsk);
sm.AddMacro("PS_AUTOMATIC_LOD", sel.automatic_lod);
sm.AddMacro("PS_MANUAL_LOD", sel.manual_lod);

View File

@@ -2922,6 +2922,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
sm.AddMacro("PS_DITHER", sel.dither);
sm.AddMacro("PS_DITHER_ADJUST", sel.dither_adjust);
sm.AddMacro("PS_ZCLAMP", sel.zclamp);
sm.AddMacro("PS_ZFLOOR", sel.zfloor);
sm.AddMacro("PS_SCANMSK", sel.scanmsk);
sm.AddMacro("PS_AUTOMATIC_LOD", sel.automatic_lod);
sm.AddMacro("PS_MANUAL_LOD", sel.manual_lod);

View File

@@ -3194,6 +3194,7 @@ void GSRendererHW::Draw()
float target_scale = GetTextureScaleFactor();
bool scaled_copy = false;
int scale_draw = IsScalingDraw(src, m_primitive_covers_without_gaps != NoGapsType::GapsFound);
if (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off)
{
if (target_scale > 1.0f && scale_draw > 0)
@@ -3202,9 +3203,15 @@ void GSRendererHW::Draw()
// 2 == Upscale, so likely putting it over the top of the render target.
if (scale_draw == 1)
{
if (!PRIM->ABE || GSConfig.UserHacks_NativeScaling < GSNativeScaling::NormalUpscaled)
target_scale = 1.0f;
m_downscale_source = src->m_from_target ? src->m_from_target->GetScale() > 1.0f : false;
const bool highlights_only = m_cached_ctx.TEST.ATE || (PRIM->ABE && m_context->ALPHA.C == 2 && m_context->ALPHA.FIX == 255);
// If it's alpha tested/stenciling for the bloom, we don't want to using the Upscaled versions. Also if the source is already native scale, may as well keep it so.
// Overlap check is for games such as Tomb Raider, where it recursively downsamples.
// Also make sure this isn't a blend, just draw (possibly with modulation).
if (GSConfig.UserHacks_NativeScaling < GSNativeScaling::NormalUpscaled || highlights_only || !PRIM->ABE || (src->m_from_target && src->m_from_target->Overlaps(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r)))
{
target_scale = 1.0f;
}
}
else
m_downscale_source = ((GSConfig.UserHacks_NativeScaling != GSNativeScaling::Aggressive && GSConfig.UserHacks_NativeScaling != GSNativeScaling::AggressiveUpscaled) || !src->m_from_target) ? false : src->m_from_target->GetScale() > 1.0f; // Bad for GTA + Full Spectrum Warrior, good for Sacred Blaze + Parappa.
@@ -3232,7 +3239,7 @@ void GSRendererHW::Draw()
// This upscaling hack is for games which construct P8 textures by drawing a bunch of small sprites in C32,
// then reinterpreting it as P8. We need to keep the off-screen intermediate textures at native resolution,
// but not propagate that through to the normal render targets. Test Case: Crash Wrath of Cortex.
if (no_ds && src && !m_channel_shuffle && src->m_from_target && (GSConfig.UserHacks_NativePaletteDraw || (src->m_target_direct && src->m_from_target->m_downscaled && scale_draw <= 1)) &&
if (no_ds && src && !m_channel_shuffle && src->m_from_target && (GSConfig.UserHacks_NativePaletteDraw || (src->m_target_direct && src->m_from_target->m_downscaled && scale_draw <= 1)) &&
src->m_scale == 1.0f && (src->m_TEX0.PSM == PSMT8 || src->m_TEX0.TBP0 == m_cached_ctx.FRAME.Block()))
{
GL_CACHE("HW: Using native resolution for target based on texture source");
@@ -3525,7 +3532,7 @@ void GSRendererHW::Draw()
// Preserve downscaled target when copying directly from a downscaled target, or it's a normal draw using a downscaled target. Clears that are drawing to the target can also preserve size.
// Of course if this size is different (in width) or this is a shuffle happening, this will be bypassed.
const bool preserve_downscale_draw = (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && (std::abs(scale_draw) == 1 || (scale_draw == 0 && src && src->m_from_target && src->m_from_target->m_downscaled))) || is_possible_mem_clear == ClearType::ClearWithDraw;
const bool preserve_downscale_draw = (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && ((std::abs(scale_draw) == 1 && !scaled_copy) || (scale_draw == 0 && src && src->m_from_target && src->m_from_target->m_downscaled))) || is_possible_mem_clear == ClearType::ClearWithDraw;
rt = g_texture_cache->LookupTarget(FRAME_TEX0, t_size, ((src && src->m_scale != 1) && (GSConfig.UserHacks_NativeScaling == GSNativeScaling::Normal || GSConfig.UserHacks_NativeScaling == GSNativeScaling::NormalUpscaled) && !possible_shuffle) ? GetTextureScaleFactor() : target_scale, GSTextureCache::RenderTarget, true,
fm, false, force_preload, preserve_rt_rgb, preserve_rt_alpha, lookup_rect, possible_shuffle, is_possible_mem_clear && FRAME_TEX0.TBP0 != m_cached_ctx.ZBUF.Block(),
@@ -5138,6 +5145,7 @@ void GSRendererHW::EmulateZbuffer(const GSTextureCache::Target* ds)
m_conf.cb_vs.max_depth = GSVector2i(0xFFFFFFFF);
//ps_cb.MaxDepth = GSVector4(0.0f, 0.0f, 0.0f, 1.0f);
m_conf.ps.zclamp = 0;
m_conf.ps.zfloor = !m_cached_ctx.ZBUF.ZMSK;
if (clamp_z)
{
@@ -7024,7 +7032,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
// When using native HPO, the top-left column/row of pixels are often not drawn. Clamp these away to avoid sampling black,
// causing bleeding into the edges of the downsampled texture.
const u32 downsample_factor = static_cast<u32>(src_target->GetScale());
const GSVector2i clamp_min = (GSConfig.UserHacks_HalfPixelOffset < GSHalfPixelOffset::Native) ?
const GSVector2i clamp_min = (GSConfig.UserHacks_HalfPixelOffset != GSHalfPixelOffset::Native) ?
GSVector2i(0, 0) :
GSVector2i(downsample_factor, downsample_factor);
GSVector4i copy_rect = tmm.coverage;
@@ -9222,7 +9230,7 @@ int GSRendererHW::IsScalingDraw(GSTextureCache::Source* src, bool no_gaps)
const bool no_resize = (std::abs(draw_size.x - tex_size.x) <= 1 && std::abs(draw_size.y - tex_size.y) <= 1);
const bool can_maintain = no_resize || (!is_target_src && m_index.tail == 2);
if (!src || ((!is_target_src || src->m_from_target->m_downscaled) && can_maintain))
if (!src || ((!is_target_src || (src->m_from_target->m_downscaled || GSConfig.UserHacks_NativeScaling > GSNativeScaling::Aggressive)) && can_maintain))
return -1;
const GSDrawingContext& next_ctx = m_env.CTXT[m_env.PRIM.CTXT];
@@ -9237,7 +9245,7 @@ int GSRendererHW::IsScalingDraw(GSTextureCache::Source* src, bool no_gaps)
// Only allow non-bilineared downscales if it's most of the target (misdetections of shadows in Naruto, Transformers etc), otherwise it's fine.
const GSVector4i src_valid = src->m_from_target ? src->m_from_target->m_valid : src->m_valid_rect;
const GSVector2i tex_size_half = GSVector2i((src->GetRegion().HasX() ? src->GetRegionSize().x : src_valid.width()) / 2, (src->GetRegion().HasY() ? src->GetRegionSize().y : src_valid.height()) / 2);
const bool possible_downscale = m_context->TEX1.MMIN == 1 || !src->m_from_target || src->m_from_target->m_downscaled || tex_size.x >= tex_size_half.x || tex_size.y >= tex_size_half.y;
const bool possible_downscale = m_context->TEX1.MMIN == 1 || !src->m_from_target || src->m_from_target->m_downscaled || GSConfig.UserHacks_NativeScaling > GSNativeScaling::Aggressive || tex_size.x >= tex_size_half.x || tex_size.y >= tex_size_half.y;
if (is_downscale && (draw_size.x >= PCRTCDisplays.GetResolution().x || !possible_downscale))
return 0;

View File

@@ -2756,7 +2756,22 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
if (!tex)
return nullptr;
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, dst->m_scale < scale);
if (scale == 1.0f)
{
// When using native HPO, the top-left column/row of pixels are often not drawn. Clamp these away to avoid sampling black,
// causing bleeding into the edges of the downsampled texture.
const u32 downsample_factor = static_cast<u32>(dst->GetScale());
const GSVector2i clamp_min = (GSConfig.UserHacks_HalfPixelOffset != GSHalfPixelOffset::Native) ?
GSVector2i(0, 0) :
GSVector2i(downsample_factor, downsample_factor);
const GSVector4 dRect = GSVector4(dst->GetUnscaledRect());
g_gs_device->FilteredDownsampleTexture(dst->m_texture, tex, downsample_factor, clamp_min, dRect);
}
else
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, dst->m_scale < scale);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
m_target_memory_usage = (m_target_memory_usage - dst->m_texture->GetMemUsage()) + tex->GetMemUsage();

View File

@@ -1878,6 +1878,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
setFnConstantI(m_fn_constants, pssel.dither, GSMTLConstantIndex_PS_DITHER);
setFnConstantI(m_fn_constants, pssel.dither_adjust, GSMTLConstantIndex_PS_DITHER_ADJUST);
setFnConstantB(m_fn_constants, pssel.zclamp, GSMTLConstantIndex_PS_ZCLAMP);
setFnConstantB(m_fn_constants, pssel.zfloor, GSMTLConstantIndex_PS_ZFLOOR);
setFnConstantB(m_fn_constants, pssel.tcoffsethack, GSMTLConstantIndex_PS_TCOFFSETHACK);
setFnConstantB(m_fn_constants, pssel.urban_chaos_hle, GSMTLConstantIndex_PS_URBAN_CHAOS_HLE);
setFnConstantB(m_fn_constants, pssel.tales_of_abyss_hle, GSMTLConstantIndex_PS_TALES_OF_ABYSS_HLE);

View File

@@ -208,6 +208,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_PS_DITHER,
GSMTLConstantIndex_PS_DITHER_ADJUST,
GSMTLConstantIndex_PS_ZCLAMP,
GSMTLConstantIndex_PS_ZFLOOR,
GSMTLConstantIndex_PS_TCOFFSETHACK,
GSMTLConstantIndex_PS_URBAN_CHAOS_HLE,
GSMTLConstantIndex_PS_TALES_OF_ABYSS_HLE,

View File

@@ -61,6 +61,7 @@ constant uint PS_CHANNEL [[function_constant(GSMTLConstantIndex_PS_CH
constant uint PS_DITHER [[function_constant(GSMTLConstantIndex_PS_DITHER)]];
constant uint PS_DITHER_ADJUST [[function_constant(GSMTLConstantIndex_PS_DITHER_ADJUST)]];
constant bool PS_ZCLAMP [[function_constant(GSMTLConstantIndex_PS_ZCLAMP)]];
constant bool PS_ZFLOOR [[function_constant(GSMTLConstantIndex_PS_ZFLOOR)]];
constant bool PS_TCOFFSETHACK [[function_constant(GSMTLConstantIndex_PS_TCOFFSETHACK)]];
constant bool PS_URBAN_CHAOS_HLE [[function_constant(GSMTLConstantIndex_PS_URBAN_CHAOS_HLE)]];
constant bool PS_TALES_OF_ABYSS_HLE [[function_constant(GSMTLConstantIndex_PS_TALES_OF_ABYSS_HLE)]];
@@ -102,6 +103,7 @@ constant bool NEEDS_RT = NEEDS_RT_FOR_AFAIL || NEEDS_RT_EARLY || (!PS_PRIM_CHECK
constant bool PS_COLOR0 = !PS_NO_COLOR;
constant bool PS_COLOR1 = !PS_NO_COLOR1;
constant bool PS_ZOUTPUT = PS_ZCLAMP || PS_ZFLOOR;
struct MainVSIn
{
@@ -137,7 +139,7 @@ struct MainPSOut
{
float4 c0 [[color(0), index(0), function_constant(PS_COLOR0)]];
float4 c1 [[color(0), index(1), function_constant(PS_COLOR1)]];
float depth [[depth(less), function_constant(PS_ZCLAMP)]];
float depth [[depth(less), function_constant(PS_ZOUTPUT)]];
};
// MARK: - Vertex functions
@@ -1209,8 +1211,13 @@ struct PSMain
}
if (PS_COLOR1)
out.c1 = alpha_blend;
float depth_value = PS_ZFLOOR ? (floor(in.p.z * exp2(32.0f)) * exp2(-32.0f)) : in.p.z;
if (PS_ZCLAMP)
out.depth = min(in.p.z, cb.max_depth);
out.depth = min(depth_value, cb.max_depth);
else if (PS_ZFLOOR)
out.depth = depth_value;
return out;
}

View File

@@ -1389,7 +1389,9 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
+ fmt::format("#define PS_DITHER {}\n", sel.dither)
+ fmt::format("#define PS_DITHER_ADJUST {}\n", sel.dither_adjust)
+ fmt::format("#define PS_ZCLAMP {}\n", sel.zclamp)
+ fmt::format("#define PS_ZFLOOR {}\n", sel.zfloor)
+ fmt::format("#define PS_BLEND_MIX {}\n", sel.blend_mix)
+ fmt::format("#define PS_ZCLAMP {}\n", sel.zclamp)
+ fmt::format("#define PS_ROUND_INV {}\n", sel.round_inv)
+ fmt::format("#define PS_FIXED_ONE_A {}\n", sel.fixed_one_a)
+ fmt::format("#define PS_PABE {}\n", sel.pabe)

View File

@@ -4777,6 +4777,7 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector
AddMacro(ss, "PS_DITHER", sel.dither);
AddMacro(ss, "PS_DITHER_ADJUST", sel.dither_adjust);
AddMacro(ss, "PS_ZCLAMP", sel.zclamp);
AddMacro(ss, "PS_ZFLOOR", sel.zfloor);
AddMacro(ss, "PS_PABE", sel.pabe);
AddMacro(ss, "PS_SCANMSK", sel.scanmsk);
AddMacro(ss, "PS_TEX_IS_FB", sel.tex_is_fb);

View File

@@ -11,15 +11,27 @@
#include "fmt/format.h"
#include <deque>
using namespace R5900;
const int rdram_devices = 2; // put 8 for TOOL and 2 for PS2 and PSX
int rdram_sdevid = 0;
std::deque<u8> ee_sio_rx_fifo;
std::deque<u8> ee_sio_tx_fifo;
void hwReset()
{
std::memset(eeHw, 0, sizeof(eeHw));
{
std::deque<u8> empty_rx;
std::swap(ee_sio_rx_fifo, empty_rx);
std::deque<u8> empty_tx;
std::swap(ee_sio_tx_fifo, empty_tx);
}
psHu32(SBUS_F260) = 0x1D000060;
// i guess this is kinda a version, it's used by some bioses

View File

@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include <deque>
#pragma once
namespace EEMemoryMap
@@ -355,3 +357,6 @@ extern void hwReset();
extern const int rdram_devices;
extern int rdram_sdevid;
extern std::deque<u8> ee_sio_rx_fifo;
extern std::deque<u8> ee_sio_tx_fifo;

View File

@@ -125,7 +125,23 @@ mem32_t _hwRead32(u32 mem)
switch( mem )
{
case SIO_ISR:
// Not (yet) hardware tested
// The PS2SDK behaviour is as follows:
// TX: Don't write to TX FIFO until until bit 15 is 0
// RX: RX FIFO has data when bits 8-11 are not 0 (maybe a byte count?)
// RX: When reading from the RX FIFO, set bits 0-2 to 1 (why??)
// For TX, we don't do any LLE buffering, so we can keep bit 15 to 0
// For RX, just hack it so when the ee_rx_fifo.size() != 0, ISR = 0xf00
if(!ee_sio_rx_fifo.empty())
return 0xf00;
return 0x0;
break;
case 0x1000f410:
case MCH_RICM:
return 0;
@@ -211,6 +227,16 @@ mem32_t hwRead32_page_0F_INTC_HACK(u32 mem)
template< uint page >
mem8_t _hwRead8(u32 mem)
{
if(mem == SIO_RXFIFO)
{
if(ee_sio_rx_fifo.empty())
return 0; // needs hardware test, what does it return with the FIFO is empty
const char c = ee_sio_rx_fifo.front();
ee_sio_rx_fifo.pop_front();
return c;
}
u32 ret32 = _hwRead32<page, false>(mem & ~0x03);
return ((u8*)&ret32)[mem & 0x03];
}

View File

@@ -285,26 +285,35 @@ void _hwWrite8(u32 mem, u8 value)
#endif
if (mem == SIO_TXFIFO)
{
static bool included_newline = false;
static char sio_buffer[1024];
static int sio_count;
if (value == '\r')
static bool last_char_was_cr = false;
// skip the \n in \r\n
if(last_char_was_cr && (value == '\n'))
{
included_newline = true;
sio_buffer[sio_count++] = '\n';
last_char_was_cr = false;
return;
}
else if (!included_newline || (value != '\n'))
last_char_was_cr = value == '\r';
bool should_flush_cause_newline = false;
if (last_char_was_cr)
{
included_newline = false;
sio_buffer[sio_count++] = value;
should_flush_cause_newline = true;
ee_sio_tx_fifo.push_back('\n');
}
else
{
should_flush_cause_newline = value == '\n';
ee_sio_tx_fifo.push_back(value);
}
if ((sio_count == std::size(sio_buffer)-1) || (sio_count != 0 && sio_buffer[sio_count-1] == '\n'))
// Check if the only thing in the buffer
if (ee_sio_tx_fifo.size() == 1024 || should_flush_cause_newline)
{
sio_buffer[sio_count] = 0;
eeConLog( ShiftJIS_ConvertString(sio_buffer) );
sio_count = 0;
std::string output_string(ee_sio_tx_fifo.begin(), ee_sio_tx_fifo.end());
eeConLog(ShiftJIS_ConvertString(output_string.c_str()));
ee_sio_tx_fifo.clear();
}
return;
}

View File

@@ -619,6 +619,8 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa
APPEND("SD={}/{} ", GSConfig.SkipDrawStart, GSConfig.SkipDrawEnd);
if (GSConfig.UserHacks_TextureInsideRt != GSTextureInRtMode::Disabled)
APPEND("TexRT={} ", static_cast<unsigned>(GSConfig.UserHacks_TextureInsideRt));
if (GSConfig.UserHacks_Limit24BitDepth != GSLimit24BitDepth::Disabled)
APPEND("TDR={} ", static_cast<unsigned>(GSConfig.UserHacks_Limit24BitDepth));
if (GSConfig.UserHacks_BilinearHack != GSBilinearDirtyMode::Automatic)
APPEND("BLU={} ", static_cast<unsigned>(GSConfig.UserHacks_BilinearHack));
if (GSConfig.UserHacks_ForceEvenSpritePosition)

View File

@@ -67,7 +67,10 @@ void intBreakpoint(bool memcheck)
{
const u32 pc = cpuRegs.pc;
if (CBreakPoints::CheckSkipFirst(BREAKPOINT_EE, pc) != 0)
{
CBreakPoints::ClearSkipFirst(BREAKPOINT_EE);
return;
}
if (!memcheck)
{
@@ -161,6 +164,8 @@ static void execI()
intBreakpoint(false);
intCheckMemcheck();
CBreakPoints::CommitClearSkipFirst(BREAKPOINT_EE);
#endif
const u32 pc = cpuRegs.pc;

View File

@@ -755,6 +755,7 @@ Pcsx2Config::GSOptions::GSOptions()
ManualUserHacks = false;
UserHacks_AlignSpriteX = false;
UserHacks_AutoFlush = GSHWAutoFlushLevel::Disabled;
UserHacks_Limit24BitDepth = GSLimit24BitDepth::Disabled;
UserHacks_CPUFBConversion = false;
UserHacks_ReadTCOnClose = false;
UserHacks_DisableDepthSupport = false;
@@ -847,6 +848,7 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
OpEqu(UserHacks_CPUCLUTRender) &&
OpEqu(UserHacks_GPUTargetCLUTMode) &&
OpEqu(UserHacks_TextureInsideRt) &&
OpEqu(UserHacks_Limit24BitDepth) &&
OpEqu(UserHacks_BilinearHack) &&
OpEqu(OverrideTextureBarriers) &&
@@ -983,6 +985,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
SettingsWrapIntEnumEx(UserHacks_BilinearHack, "UserHacks_BilinearHack");
SettingsWrapBitBoolEx(UserHacks_NativePaletteDraw, "UserHacks_NativePaletteDraw");
SettingsWrapIntEnumEx(UserHacks_TextureInsideRt, "UserHacks_TextureInsideRt");
SettingsWrapIntEnumEx(UserHacks_Limit24BitDepth, "UserHacks_Limit24BitDepth");
SettingsWrapBitBoolEx(UserHacks_EstimateTextureRegion, "UserHacks_EstimateTextureRegion");
SettingsWrapBitBoolEx(FXAA, "fxaa");
SettingsWrapBitBool(ShadeBoost);
@@ -1111,6 +1114,7 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
UserHacks_CPUFBConversion = false;
UserHacks_ReadTCOnClose = false;
UserHacks_TextureInsideRt = GSTextureInRtMode::Disabled;
UserHacks_Limit24BitDepth = GSLimit24BitDepth::Disabled;
UserHacks_EstimateTextureRegion = false;
UserHacks_TCOffsetX = 0;
UserHacks_TCOffsetY = 0;

View File

@@ -120,7 +120,10 @@ void psxBreakpoint(bool memcheck)
{
u32 pc = psxRegs.pc;
if (CBreakPoints::CheckSkipFirst(BREAKPOINT_IOP, pc) != 0)
{
CBreakPoints::ClearSkipFirst(BREAKPOINT_IOP);
return;
}
if (!memcheck)
{
@@ -208,6 +211,8 @@ static __fi void execI()
psxBreakpoint(false);
psxCheckMemcheck();
CBreakPoints::CommitClearSkipFirst(BREAKPOINT_IOP);
#endif
// Inject IRX hack

View File

@@ -3,4 +3,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 77;
static constexpr u32 SHADER_CACHE_VERSION = 78;

View File

@@ -526,6 +526,7 @@ void VMManager::SetDefaultLoggingSettings(SettingsInterface& si)
si.SetBoolValue("Logging", "EnableSystemConsole", false);
si.SetBoolValue("Logging", "EnableFileLogging", true);
si.SetBoolValue("Logging", "EnableTimestamps", true);
si.SetBoolValue("Logging", "EnableEESIOInput", false);
si.SetBoolValue("Logging", "EnableVerbose", false);
si.SetBoolValue("Logging", "EnableEEConsole", false);
si.SetBoolValue("Logging", "EnableIOPConsole", false);
@@ -3725,15 +3726,16 @@ void VMManager::UpdateDiscordPresence(bool update_session_time)
auto lock = Achievements::GetLock();
if (Achievements::HasRichPresence())
if (Achievements::HasActiveGame() && Achievements::HasAchievementsOrLeaderboards())
{
rp.state = (state_string = StringUtil::Ellipsise(Achievements::GetRichPresenceString(), 128)).c_str();
if (const std::string& icon_url = Achievements::GetGameIconURL(); !icon_url.empty())
{
rp.largeImageKey = icon_url.c_str();
rp.largeImageText = s_title.c_str();
}
if (Achievements::HasRichPresence())
rp.state = (state_string = StringUtil::Ellipsise(Achievements::GetRichPresenceString(), 128)).c_str();
}
Discord_UpdatePresence(&rp);
@@ -3747,3 +3749,15 @@ void VMManager::PollDiscordPresence()
Discord_RunCallbacks();
}
bool VMManager::WriteBytesToEESIORXFIFO(const std::span<const u8> data)
{
if(ee_sio_rx_fifo.size() + data.size() > 1024)
{
Console.Warning("EE RX FIFO is full, not appending more bytes.");
return false;
}
ee_sio_rx_fifo.insert(ee_sio_rx_fifo.end(), data.begin(), data.end());
return true;
}

View File

@@ -6,6 +6,7 @@
#include <functional>
#include <mutex>
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <vector>
@@ -264,6 +265,9 @@ namespace VMManager
/// Called when the rich presence string, provided by RetroAchievements, changes.
void UpdateDiscordPresence(bool update_session_time);
/// Append bytes to the EE SIO RX FIFO. If it returns false, the FIFO is full and data is not inserted.
bool WriteBytesToEESIORXFIFO(const std::span<const u8> data);
/// Internal callbacks, implemented in the emu core.
namespace Internal
{

View File

@@ -1259,7 +1259,10 @@ static bool psxDynarecCheckBreakpoint()
{
u32 pc = psxRegs.pc;
if (CBreakPoints::CheckSkipFirst(BREAKPOINT_IOP, pc) == pc)
{
CBreakPoints::ClearSkipFirst(BREAKPOINT_IOP);
return false;
}
int bpFlags = psxIsBreakpointNeeded(pc);
bool hit = false;
@@ -1299,8 +1302,10 @@ static bool psxDynarecMemcheck(size_t i)
auto mc = CBreakPoints::GetMemChecks(BREAKPOINT_IOP)[i];
if (CBreakPoints::CheckSkipFirst(BREAKPOINT_IOP, pc) == pc)
{
CBreakPoints::ClearSkipFirst(BREAKPOINT_IOP);
return false;
}
if (mc.hasCond)
{
if (!mc.cond.Evaluate())
@@ -1373,7 +1378,7 @@ static void psxRecMemcheck(u32 op, u32 bits, bool store)
}
}
static void psxEncodeBreakpoint()
static bool psxEncodeBreakpoint()
{
if (psxIsBreakpointNeeded(psxpc) != 0)
{
@@ -1381,14 +1386,17 @@ static void psxEncodeBreakpoint()
xFastCall((void*)psxDynarecCheckBreakpoint);
xTEST(al, al);
xJNZ(iopExitRecompiledCode);
return true;
}
return false;
}
static void psxEncodeMemcheck()
static bool psxEncodeMemcheck()
{
int needed = psxIsMemcheckNeeded(psxpc);
if (needed == 0)
return;
return false;
u32 op = iopMemRead32(needed == 2 ? psxpc + 4 : psxpc);
const R5900::OPCODE& opcode = R5900::GetInstruction(op);
@@ -1409,6 +1417,7 @@ static void psxEncodeMemcheck()
psxRecMemcheck(op, 64, store);
break;
}
return true;
}
void psxRecompileNextInstruction(bool delayslot, bool swapped_delayslot)
@@ -1437,12 +1446,10 @@ void psxRecompileNextInstruction(bool delayslot, bool swapped_delayslot)
EEINST* old_inst_info = g_pCurInstInfo;
s_recompilingDelaySlot = delayslot;
// add breakpoint
if (!delayslot)
{
// Broken on x64
psxEncodeBreakpoint();
psxEncodeMemcheck();
if(psxEncodeBreakpoint() || psxEncodeMemcheck())
xFastCall((void*)CBreakPoints::CommitClearSkipFirst, BREAKPOINT_IOP);
}
else
{

View File

@@ -1498,7 +1498,10 @@ void dynarecCheckBreakpoint()
{
u32 pc = cpuRegs.pc;
if (CBreakPoints::CheckSkipFirst(BREAKPOINT_EE, pc) != 0)
{
CBreakPoints::ClearSkipFirst(BREAKPOINT_EE);
return;
}
const int bpFlags = isBreakpointNeeded(pc);
bool hit = false;
@@ -1532,7 +1535,10 @@ void dynarecMemcheck(size_t i)
const u32 op = memRead32(cpuRegs.pc);
const OPCODE& opcode = GetInstruction(op);
if (CBreakPoints::CheckSkipFirst(BREAKPOINT_EE, pc) != 0)
{
CBreakPoints::ClearSkipFirst(BREAKPOINT_EE);
return;
}
auto mc = CBreakPoints::GetMemChecks(BREAKPOINT_EE)[i];
@@ -1606,20 +1612,22 @@ void recMemcheck(u32 op, u32 bits, bool store)
}
}
void encodeBreakpoint()
bool encodeBreakpoint()
{
if (isBreakpointNeeded(pc) != 0)
{
iFlushCall(FLUSH_EVERYTHING | FLUSH_PC);
xFastCall((void*)dynarecCheckBreakpoint);
return true;
}
return false;
}
void encodeMemcheck()
bool encodeMemcheck()
{
const int needed = isMemcheckNeeded(pc);
if (needed == 0)
return;
return false;
const u32 op = memRead32(needed == 2 ? pc + 4 : pc);
const OPCODE& opcode = GetInstruction(op);
@@ -1643,6 +1651,7 @@ void encodeMemcheck()
recMemcheck(op, 128, store);
break;
}
return true;
}
void recompileNextInstruction(bool delayslot, bool swapped_delay_slot)
@@ -1653,8 +1662,8 @@ void recompileNextInstruction(bool delayslot, bool swapped_delay_slot)
// add breakpoint
if (!delayslot)
{
encodeBreakpoint();
encodeMemcheck();
if(encodeBreakpoint() || encodeMemcheck())
xFastCall((void*)CBreakPoints::CommitClearSkipFirst, BREAKPOINT_EE);
}
else
{

View File

@@ -233,7 +233,13 @@ public:
const u64 quick64 = pState->quick64[0];
for (const microBlockLinkRef& ref : quickLookup)
{
if (ref.quick != quick64) continue;
// if we're using the flag hack, ignore the mac flags going in to the new block too if an exact match wasn't requested.
if (mVUsFlagHack)
{
if ((ref.quick & ~0x0C04) != (quick64 & ~0x0C04)) continue;
}
else if (ref.quick != quick64) continue;
if (doConstProp && (ref.pBlock->pState.vi15 != pState->vi15)) continue;
if (doConstProp && (ref.pBlock->pState.vi15v != pState->vi15v)) continue;
return ref.pBlock;