Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff9da17498 | ||
|
|
722bc94270 | ||
|
|
d51a5db5b1 | ||
|
|
04541ae2ab | ||
|
|
c58a67815b | ||
|
|
085f964cd9 | ||
|
|
a8b6e448eb | ||
|
|
78ab8381d9 | ||
|
|
49a5d82086 | ||
|
|
4ed129ccac | ||
|
|
ed09dca17e | ||
|
|
e64fbb2f0e | ||
|
|
e37f8a6521 | ||
|
|
b54bcc0e20 | ||
|
|
db37d51bb1 | ||
|
|
197b9fc560 | ||
|
|
fd30b00205 | ||
|
|
f182379d24 | ||
|
|
32ba08980d | ||
|
|
1e1f08abb9 | ||
|
|
cbe20c0eed | ||
|
|
ec288ffa62 | ||
|
|
7e18c02c7e | ||
|
|
4a505cc239 | ||
|
|
35c81106c6 | ||
|
|
1094222d3f | ||
|
|
065d0db4c9 | ||
|
|
bc51537080 | ||
|
|
b7dfcb282b | ||
|
|
78a9411766 | ||
|
|
4724f67596 | ||
|
|
e0851bb86f | ||
|
|
08e68d9563 | ||
|
|
0b6dccae51 | ||
|
|
60adfd5046 |
2
.github/ISSUE_TEMPLATE/app_bug_report.yaml
vendored
@@ -70,6 +70,8 @@ body:
|
||||
- Windows 11
|
||||
- Windows 10 (64bit)
|
||||
- Linux (64bit) - Specify distro below
|
||||
- macOS 26 (Tahoe)
|
||||
- macOS 15 (Sequoia)
|
||||
- macOS 14 (Sonoma)
|
||||
- macOS 13 (Ventura)
|
||||
- macOS 12 (Monterey)
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/emu_bug_report.yaml
vendored
@@ -87,6 +87,8 @@ body:
|
||||
- Windows 11
|
||||
- Windows 10 (64bit)
|
||||
- Linux (64bit) - Specify distro below
|
||||
- macOS 26 (Tahoe)
|
||||
- macOS 15 (Sequoia)
|
||||
- macOS 14 (Sonoma)
|
||||
- macOS 13 (Ventura)
|
||||
- macOS 12 (Monterey)
|
||||
@@ -104,14 +106,14 @@ body:
|
||||
id: cpu
|
||||
attributes:
|
||||
label: CPU
|
||||
placeholder: "Example: i5-7600"
|
||||
placeholder: "Example: Intel i5 12400F"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: gpu
|
||||
attributes:
|
||||
label: GPU
|
||||
placeholder: "Example: GTX 1070"
|
||||
placeholder: "Example: Nvidia RTX 4060"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
1
.github/workflows/linux_build_qt.yml
vendored
@@ -144,6 +144,7 @@ jobs:
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DENABLE_SETCAP=OFF \
|
||||
-DDISABLE_ADVANCE_SIMD=TRUE \
|
||||
-DUSE_LINKED_FFMPEG=ON \
|
||||
-DCMAKE_DISABLE_PRECOMPILE_HEADERS=ON \
|
||||
$ADDITIONAL_CMAKE_ARGS
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 38 KiB |
41
bin/resources/fullscreenui/applications-system.svg
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg
|
||||
fill="#000000"
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 32 32"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="applications-system.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="0.97875"
|
||||
inkscape:cx="399.48914"
|
||||
inkscape:cy="400"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1010"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<defs
|
||||
id="defs1" />
|
||||
<path
|
||||
d="M30.015 12.97l-2.567-0.569c-0.2-0.64-0.462-1.252-0.762-1.841l1.389-2.313c0.518-0.829 0.78-2.047 0-2.829l-1.415-1.414c-0.78-0.781-2.098-0.64-2.894-0.088l-2.251 1.434c-0.584-0.303-1.195-0.563-1.829-0.768l-0.576-2.598c-0.172-0.953-1.005-1.984-2.11-1.984h-2c-1.104 0-1.781 1.047-2 2l-0.642 2.567c-0.678 0.216-1.328 0.492-1.948 0.819l-2.308-1.47c-0.795-0.552-2.114-0.692-2.894 0.088l-1.415 1.414c-0.781 0.782-0.519 2 0 2.828l1.461 2.435c-0.274 0.552-0.517 1.123-0.705 1.72l-2.566 0.569c-0.953 0.171-1.984 1.005-1.984 2.109v2c0 1.105 1.047 1.782 2 2l2.598 0.649c0.179 0.551 0.404 1.080 0.658 1.593l-1.462 2.438c-0.518 0.828-0.78 2.047 0 2.828l1.415 1.414c0.78 0.782 2.098 0.64 2.894 0.089l2.313-1.474c0.623 0.329 1.277 0.608 1.96 0.823l0.64 2.559c0.219 0.953 0.896 2 2 2h2c1.105 0 1.938-1.032 2.11-1.985l0.577-2.604c0.628-0.203 1.23-0.459 1.808-0.758l2.256 1.438c0.796 0.552 2.114 0.692 2.895-0.089l1.415-1.414c0.78-0.782 0.518-2 0-2.828l-1.39-2.317c0.279-0.549 0.521-1.12 0.716-1.714l2.599-0.649c0.953-0.219 2-0.895 2-2v-2c0-1.104-1.031-1.938-1.985-2.11zM30.001 16.939c-0.085 0.061-0.245 0.145-0.448 0.192l-3.708 0.926-0.344 1.051c-0.155 0.474-0.356 0.954-0.597 1.428l-0.502 0.986 1.959 3.267c0.125 0.2 0.183 0.379 0.201 0.485l-1.316 1.314c-0.127-0.040-0.271-0.092-0.341-0.14l-3.292-2.099-1.023 0.529c-0.493 0.256-0.999 0.468-1.503 0.631l-1.090 0.352-0.824 3.723c-0.038 0.199-0.145 0.36-0.218 0.417h-1.8c-0.061-0.085-0.145-0.245-0.191-0.448l-0.921-3.681-1.066-0.338c-0.549-0.173-1.097-0.404-1.63-0.684l-1.028-0.543-3.293 2.099c-0.135 0.091-0.279 0.143-0.409 0.143l-1.311-1.276c0.018-0.104 0.072-0.274 0.181-0.449l2.045-3.408-0.487-0.98c-0.227-0.462-0.407-0.895-0.547-1.325l-0.343-1.052-3.671-0.918c-0.231-0.052-0.398-0.139-0.485-0.2v-1.86c0.001 0.001 0.002 0.001 0.005 0.001 0.034 0 0.198-0.117 0.335-0.142l3.772-0.835 0.346-1.103c0.141-0.449 0.333-0.917 0.588-1.43l0.487-0.98-2.024-3.373c-0.125-0.201-0.184-0.38-0.201-0.485l1.315-1.314c0.128 0.041 0.271 0.093 0.34 0.14l3.354 2.138 1.027-0.542c0.527-0.278 1.073-0.507 1.622-0.682l1.063-0.338 0.912-3.649c0.053-0.231 0.138-0.398 0.2-0.485h1.859c-0.014 0.020 0.115 0.195 0.142 0.339l0.84 3.794 1.089 0.352c0.511 0.165 1.023 0.38 1.523 0.639l1.023 0.532 3.224-2.053c0.135-0.092 0.279-0.143 0.409-0.143l1.313 1.276c-0.017 0.104-0.072 0.276-0.181 0.45l-1.98 3.296 0.505 0.988c0.273 0.533 0.48 1.033 0.635 1.529l0.346 1.104 3.697 0.82c0.224 0.041 0.398 0.171 0.434 0.241zM16.013 9.99c-3.321 0-6.023 2.697-6.023 6.010s2.702 6.010 6.023 6.010 6.023-2.697 6.023-6.009c0-3.313-2.702-6.010-6.023-6.010zM16 20c-2.205 0-4-1.794-4-4s1.794-4 4-4c2.206 0 4 1.794 4 4s-1.794 4-4 4z"
|
||||
id="path1"
|
||||
style="fill:#ffffff" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
7
bin/resources/fullscreenui/back-icon.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg fill="#ffffff" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800px" height="800px" viewBox="0 0 72 72" enable-background="new 0 0 72 72" xml:space="preserve">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 13 KiB |
7
bin/resources/fullscreenui/desktop-mode.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg fill="#ffffff" width="800px" height="800px" viewBox="0 0 16 16" id="window-16px" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 19 KiB |
7
bin/resources/fullscreenui/drive-cdrom.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg fill="#ffffff" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" xml:space="preserve">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 27 KiB |
7
bin/resources/fullscreenui/exit.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" id="SVGRoot" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" fill="#ffffff">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 48 KiB |
7
bin/resources/fullscreenui/game-list.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg height="800px" width="800px" version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" xml:space="preserve" fill="#ffffff">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 100 KiB |
7
bin/resources/fullscreenui/media-cdrom.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg fill="#ffffff" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800px" height="800px" viewBox="0 0 72 72" enable-background="new 0 0 72 72" xml:space="preserve">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 14 KiB |
9
bin/resources/fullscreenui/start-bios.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg fill="#ffffff" width="800px" height="800px" viewBox="-2 -2 24 24" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin" class="jam jam-microchip">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 22 KiB |
7
bin/resources/fullscreenui/start-file.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -329,6 +329,7 @@
|
||||
030000000d0f0000ee00000000000000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
030000000d0f0000c100000000000000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
|
||||
030000000d0f0000f600000000000000,Horipad Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
030000000d0f00000202000000000000,Horipad O Nintendo Switch 2 Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,
|
||||
030000000d0f00006700000000000000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
|
||||
030000000d0f00009601000000000000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc2:b2,paddle1:b5,paddle2:b15,paddle3:b18,paddle4:b19,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
030000000d0f0000dc00000000000000,Horipad Switch,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
|
||||
|
||||
@@ -44,7 +44,7 @@ bool DebugInterface::m_pause_on_entry = false;
|
||||
|
||||
bool DebugInterface::isAlive()
|
||||
{
|
||||
return VMManager::HasValidVM() && g_FrameCount > 0;
|
||||
return VMManager::HasValidVM();
|
||||
}
|
||||
|
||||
bool DebugInterface::isCpuPaused()
|
||||
|
||||
@@ -575,7 +575,7 @@ void GSState::GIFPackedRegHandlerSTQ(const GIFPackedReg* RESTRICT r)
|
||||
|
||||
// Vexx (character shadow)
|
||||
// q = 0 (st also 0 on the first 16 vertices), setting it to 1.0f to avoid div by zero later
|
||||
q = q.blend8(GSVector4i::cast(GSVector4::m_one), q == GSVector4i::zero());
|
||||
q = q.blend8(GSVector4i::cast(GSVector4(FLT_MIN)), q == GSVector4i::zero());
|
||||
|
||||
// Suikoden 4
|
||||
// creates some nan for Q. Let's avoid undefined behavior (See GIFRegHandlerRGBAQ)
|
||||
@@ -671,7 +671,7 @@ void GSState::GIFPackedRegHandlerSTQRGBAXYZF2(const GIFPackedReg* RESTRICT r, u3
|
||||
GSVector4i q = GSVector4i::loadl(&r[0].U64[1]);
|
||||
const GSVector4i rgba = (GSVector4i::load<false>(&r[1]) & GSVector4i::x000000ff()).ps32().pu16();
|
||||
|
||||
q = q.blend8(GSVector4i::cast(GSVector4::m_one), q == GSVector4i::zero()); // see GIFPackedRegHandlerSTQ
|
||||
q = q.blend8(GSVector4i::cast(GSVector4(FLT_MIN)), q == GSVector4i::zero()); // see GIFPackedRegHandlerSTQ
|
||||
|
||||
m_v.m[0] = st.upl64(rgba.upl32(q)); // TODO: only store the last one
|
||||
|
||||
@@ -705,7 +705,7 @@ void GSState::GIFPackedRegHandlerSTQRGBAXYZ2(const GIFPackedReg* RESTRICT r, u32
|
||||
GSVector4i q = GSVector4i::loadl(&r[0].U64[1]);
|
||||
const GSVector4i rgba = (GSVector4i::load<false>(&r[1]) & GSVector4i::x000000ff()).ps32().pu16();
|
||||
|
||||
q = q.blend8(GSVector4i::cast(GSVector4::m_one), q == GSVector4i::zero()); // see GIFPackedRegHandlerSTQ
|
||||
q = q.blend8(GSVector4i::cast(GSVector4(FLT_MIN)), q == GSVector4i::zero()); // see GIFPackedRegHandlerSTQ
|
||||
|
||||
m_v.m[0] = st.upl64(rgba.upl32(q)); // TODO: only store the last one
|
||||
|
||||
@@ -4237,7 +4237,10 @@ void GSState::CalcAlphaMinMax(const int tex_alpha_min, const int tex_alpha_max)
|
||||
|
||||
if (IsCoverageAlpha())
|
||||
{
|
||||
min = 128;
|
||||
// HW renderer doesn't currently support AA, so its min is 128.
|
||||
// If we add AA support to the HW renderer, this will need to be changed.
|
||||
// (Will probably only be supported with ROV/FBFetch so we would want to check for that.)
|
||||
min = GSIsHardwareRenderer() ? 128 : 0;
|
||||
max = 128;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1105,14 +1105,14 @@ float GSDevice11::GetAndResetAccumulatedGPUTime()
|
||||
void GSDevice11::DrawPrimitive()
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
|
||||
PSUpdateShaderState();
|
||||
PSUpdateShaderState(true, true);
|
||||
m_ctx->Draw(m_vertex.count, m_vertex.start);
|
||||
}
|
||||
|
||||
void GSDevice11::DrawIndexedPrimitive()
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
|
||||
PSUpdateShaderState();
|
||||
PSUpdateShaderState(true, true);
|
||||
m_ctx->DrawIndexed(m_index.count, m_index.start, m_vertex.start);
|
||||
}
|
||||
|
||||
@@ -1120,7 +1120,7 @@ void GSDevice11::DrawIndexedPrimitive(int offset, int count)
|
||||
{
|
||||
pxAssert(offset + count <= (int)m_index.count);
|
||||
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
|
||||
PSUpdateShaderState();
|
||||
PSUpdateShaderState(true, true);
|
||||
m_ctx->DrawIndexed(count, m_index.start + offset, m_vertex.start);
|
||||
}
|
||||
|
||||
@@ -1280,6 +1280,9 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||
GSVector2i ds;
|
||||
if (dTex)
|
||||
{
|
||||
// ps unbind conflicting srvs
|
||||
PSUnbindConflictingSRVs(dTex);
|
||||
|
||||
ds = dTex->GetSize();
|
||||
if (draw_in_depth)
|
||||
OMSetRenderTargets(nullptr, dTex);
|
||||
@@ -1292,6 +1295,7 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||
}
|
||||
|
||||
// om
|
||||
|
||||
if (draw_in_depth)
|
||||
OMSetDepthStencilState(m_convert.dss_write.get(), 0);
|
||||
else
|
||||
@@ -1329,7 +1333,7 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||
PSSetSamplerState(linear ? m_convert.ln.get() : m_convert.pt.get());
|
||||
PSSetShader(ps, ps_cb);
|
||||
|
||||
//
|
||||
// draw
|
||||
|
||||
DrawPrimitive();
|
||||
}
|
||||
@@ -1341,6 +1345,9 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||
GSVector2i ds;
|
||||
if (dTex)
|
||||
{
|
||||
// ps unbind conflicting srvs
|
||||
PSUnbindConflictingSRVs(dTex);
|
||||
|
||||
ds = dTex->GetSize();
|
||||
OMSetRenderTargets(dTex, nullptr);
|
||||
}
|
||||
@@ -1356,6 +1363,7 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||
m_ctx->UpdateSubresource(m_present.ps_cb.get(), 0, nullptr, &cb, 0, 0);
|
||||
|
||||
// om
|
||||
|
||||
OMSetDepthStencilState(m_convert.dss.get(), 0);
|
||||
OMSetBlendState(m_convert.bs[D3D11_COLOR_WRITE_ENABLE_ALL].get(), 0);
|
||||
|
||||
@@ -1388,7 +1396,7 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||
PSSetSamplerState(linear ? m_convert.ln.get() : m_convert.pt.get());
|
||||
PSSetShader(m_present.ps[static_cast<u32>(shader)].get(), m_present.ps_cb.get());
|
||||
|
||||
//
|
||||
// draw
|
||||
|
||||
DrawPrimitive();
|
||||
}
|
||||
@@ -1454,6 +1462,7 @@ void GSDevice11::DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_re
|
||||
|
||||
VSSetShader(m_convert.vs.get(), nullptr);
|
||||
PSSetShader(m_convert.ps[static_cast<int>(shader)].get(), nullptr);
|
||||
PSUnbindConflictingSRVs(dTex);
|
||||
|
||||
OMSetDepthStencilState(dTex->IsRenderTarget() ? m_convert.dss.get() : m_convert.dss_write.get(), 0);
|
||||
OMSetRenderTargets(dTex->IsRenderTarget() ? dTex : nullptr, dTex->IsDepthStencil() ? dTex : nullptr);
|
||||
@@ -2110,7 +2119,7 @@ void GSDevice11::RenderImGui()
|
||||
|
||||
// Since we don't have the GSTexture...
|
||||
m_state.ps_sr_views[0] = reinterpret_cast<ID3D11ShaderResourceView*>(pcmd->GetTexID());
|
||||
PSUpdateShaderState();
|
||||
PSUpdateShaderState(true, true);
|
||||
|
||||
m_ctx->DrawIndexed(pcmd->ElemCount, m_index.start + pcmd->IdxOffset, vertex_offset + pcmd->VtxOffset);
|
||||
}
|
||||
@@ -2130,6 +2139,10 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vert
|
||||
|
||||
m_ctx->ClearDepthStencilView(*static_cast<GSTexture11*>(ds), D3D11_CLEAR_STENCIL, 0.0f, 0);
|
||||
|
||||
// ps unbind conflicting srvs
|
||||
|
||||
PSUnbindConflictingSRVs(ds);
|
||||
|
||||
// om
|
||||
|
||||
OMSetDepthStencilState(m_date.dss.get(), 1);
|
||||
@@ -2147,11 +2160,12 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vert
|
||||
VSSetShader(m_convert.vs.get(), nullptr);
|
||||
|
||||
// ps
|
||||
|
||||
PSSetShaderResource(0, rt);
|
||||
PSSetSamplerState(m_convert.pt.get());
|
||||
PSSetShader(m_convert.ps[SetDATMShader(datm)].get(), nullptr);
|
||||
|
||||
//
|
||||
// draw
|
||||
|
||||
DrawPrimitive();
|
||||
}
|
||||
@@ -2357,10 +2371,63 @@ void GSDevice11::PSSetShader(ID3D11PixelShader* ps, ID3D11Buffer* ps_cb)
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice11::PSUpdateShaderState()
|
||||
void GSDevice11::PSUpdateShaderState(const bool sr_update, const bool ss_update)
|
||||
{
|
||||
m_ctx->PSSetShaderResources(0, m_state.ps_sr_views.size(), m_state.ps_sr_views.data());
|
||||
m_ctx->PSSetSamplers(0, m_state.ps_ss.size(), m_state.ps_ss.data());
|
||||
// Shader resource caching requires srv/rtv hazards to be resolved, ensure PSUnbindConflictingSRVs handle.
|
||||
if (sr_update)
|
||||
{
|
||||
bool sr_changed = false;
|
||||
for (size_t i = 0; i < m_state.ps_sr_views.size(); ++i)
|
||||
{
|
||||
if (m_state.ps_cached_sr_views[i] != m_state.ps_sr_views[i])
|
||||
{
|
||||
sr_changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sr_changed)
|
||||
{
|
||||
m_state.ps_cached_sr_views = m_state.ps_sr_views;
|
||||
m_ctx->PSSetShaderResources(0, m_state.ps_sr_views.size(), m_state.ps_sr_views.data());
|
||||
}
|
||||
}
|
||||
|
||||
if (ss_update)
|
||||
{
|
||||
bool ss_changed = false;
|
||||
for (size_t i = 0; i < m_state.ps_ss.size(); ++i)
|
||||
{
|
||||
if (m_state.ps_cached_ss[i] != m_state.ps_ss[i])
|
||||
{
|
||||
ss_changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ss_changed)
|
||||
{
|
||||
m_state.ps_cached_ss = m_state.ps_ss;
|
||||
m_ctx->PSSetSamplers(0, m_state.ps_ss.size(), m_state.ps_ss.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice11::PSUnbindConflictingSRVs(GSTexture* tex1, GSTexture* tex2)
|
||||
{
|
||||
// Make sure no SRVs are bound using the same texture before binding it to a RTV.
|
||||
bool changed = false;
|
||||
for (size_t i = 0; i < m_state.ps_sr_views.size(); i++)
|
||||
{
|
||||
if ((tex1 && m_state.ps_sr_views[i] == *(GSTexture11*)tex1) || (tex2 && m_state.ps_sr_views[i] == *(GSTexture11*)tex2))
|
||||
{
|
||||
m_state.ps_sr_views[i] = nullptr;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
PSUpdateShaderState(true, false);
|
||||
}
|
||||
|
||||
void GSDevice11::OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref)
|
||||
@@ -2523,18 +2590,18 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
}
|
||||
|
||||
GSTexture* primid_tex = nullptr;
|
||||
GSTexture* primid_texture = nullptr;
|
||||
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
|
||||
{
|
||||
primid_tex = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::PrimID, false);
|
||||
if (!primid_tex)
|
||||
primid_texture = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::PrimID, false);
|
||||
if (!primid_texture)
|
||||
{
|
||||
Console.WriteLn("D3D11: Failed to allocate DATE image, aborting draw.");
|
||||
Console.Warning("D3D11: Failed to allocate DATE image, aborting draw.");
|
||||
return;
|
||||
}
|
||||
|
||||
StretchRect(colclip_rt ? colclip_rt : config.rt, GSVector4(config.drawarea) / GSVector4(rtsize).xyxy(),
|
||||
primid_tex, GSVector4(config.drawarea), m_date.primid_init_ps[static_cast<u8>(config.datm)].get(), nullptr, false);
|
||||
primid_texture, GSVector4(config.drawarea), m_date.primid_init_ps[static_cast<u8>(config.datm)].get(), nullptr, false);
|
||||
}
|
||||
else if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::Stencil ||
|
||||
config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne)
|
||||
@@ -2596,6 +2663,9 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
IASetPrimitiveTopology(topology);
|
||||
|
||||
// Should be called before changing local srv state.
|
||||
PSUnbindConflictingSRVs(colclip_rt ? colclip_rt : config.rt, config.ds);
|
||||
|
||||
if (config.tex)
|
||||
{
|
||||
CommitClear(config.tex);
|
||||
@@ -2645,20 +2715,20 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
SetupVS(config.vs, &config.cb_vs);
|
||||
SetupPS(config.ps, &config.cb_ps, config.sampler);
|
||||
|
||||
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
|
||||
if (primid_texture)
|
||||
{
|
||||
OMDepthStencilSelector dss = config.depth;
|
||||
dss.zwe = 0;
|
||||
const OMBlendSelector blend(GSHWDrawConfig::ColorMaskSelector(1),
|
||||
GSHWDrawConfig::BlendState(true, CONST_ONE, CONST_ONE, 3 /* MIN */, CONST_ONE, CONST_ZERO, false, 0));
|
||||
SetupOM(dss, blend, 0);
|
||||
OMSetRenderTargets(primid_tex, config.ds, &config.scissor);
|
||||
OMSetRenderTargets(primid_texture, config.ds, &config.scissor);
|
||||
DrawIndexedPrimitive();
|
||||
|
||||
config.ps.date = 3;
|
||||
config.alpha_second_pass.ps.date = 3;
|
||||
SetupPS(config.ps, nullptr, config.sampler);
|
||||
PSSetShaderResource(3, primid_tex);
|
||||
PSSetShaderResource(3, primid_texture);
|
||||
}
|
||||
|
||||
SetupOM(config.depth, OMBlendSelector(config.colormask, config.blend), config.blend.constant);
|
||||
@@ -2697,8 +2767,8 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
if (draw_ds_clone)
|
||||
Recycle(draw_ds_clone);
|
||||
|
||||
if (primid_tex)
|
||||
Recycle(primid_tex);
|
||||
if (primid_texture)
|
||||
Recycle(primid_texture);
|
||||
|
||||
if (colclip_rt)
|
||||
{
|
||||
|
||||
@@ -145,9 +145,11 @@ private:
|
||||
ID3D11VertexShader* vs;
|
||||
ID3D11Buffer* vs_cb;
|
||||
std::array<ID3D11ShaderResourceView*, MAX_TEXTURES> ps_sr_views;
|
||||
std::array<ID3D11ShaderResourceView*, MAX_TEXTURES> ps_cached_sr_views;
|
||||
ID3D11PixelShader* ps;
|
||||
ID3D11Buffer* ps_cb;
|
||||
std::array<ID3D11SamplerState*, MAX_SAMPLERS> ps_ss;
|
||||
std::array<ID3D11SamplerState*, MAX_SAMPLERS> ps_cached_ss;
|
||||
GSVector2i viewport;
|
||||
GSVector4i scissor;
|
||||
u32 vb_stride;
|
||||
@@ -324,7 +326,8 @@ public:
|
||||
|
||||
void PSSetShaderResource(int i, GSTexture* sr);
|
||||
void PSSetShader(ID3D11PixelShader* ps, ID3D11Buffer* ps_cb);
|
||||
void PSUpdateShaderState();
|
||||
void PSUpdateShaderState(const bool sr_update, const bool ss_update);
|
||||
void PSUnbindConflictingSRVs(GSTexture* tex1 = nullptr, GSTexture* tex2 = nullptr);
|
||||
void PSSetSamplerState(ID3D11SamplerState* ss0);
|
||||
|
||||
void OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref);
|
||||
|
||||
@@ -3903,7 +3903,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
config.rt = backup_rt;
|
||||
if (!date_image)
|
||||
{
|
||||
Console.WriteLn("D3D12: Failed to allocate DATE image, aborting draw.");
|
||||
Console.Warning("D3D12: Failed to allocate DATE image, aborting draw.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,23 +822,6 @@ bool GSHwHack::GSC_MetalGearSolid3(GSRendererHW& r, int& skip)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::GSC_HitmanBloodMoney(GSRendererHW& r, int& skip)
|
||||
{
|
||||
// The game does a stupid thing where it backs up the last 2 pages of the framebuffer with shuffles, uploads a CT32 texture to it
|
||||
// then copies the RGB back (keeping the new alpha only). It's pretty gross, I have no idea why they didn't just upload a new alpha.
|
||||
// This is a real pain to emulate with the current state of things, so let's just clear the dirty area from the upload and pretend it wasn't there.
|
||||
|
||||
// Catch the first draw of the copy back.
|
||||
if (RFBP > 0 && RTPSM == PSMT8H && RFPSM == PSMCT32)
|
||||
{
|
||||
GSTextureCache::Target* target = g_texture_cache->FindOverlappingTarget(RFBP, RFBP + 1);
|
||||
if (target)
|
||||
target->m_dirty.clear();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
const u32 n_vertices = r.m_vertex.next;
|
||||
@@ -1328,7 +1311,6 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
|
||||
CRC_F(GSC_NFSUndercover),
|
||||
CRC_F(GSC_PolyphonyDigitalGames),
|
||||
CRC_F(GSC_MetalGearSolid3),
|
||||
CRC_F(GSC_HitmanBloodMoney),
|
||||
CRC_F(GSC_Battlefield2),
|
||||
|
||||
// Channel Effect
|
||||
|
||||
@@ -26,7 +26,6 @@ public:
|
||||
static bool GSC_Battlefield2(GSRendererHW& r, int& skip);
|
||||
static bool GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip);
|
||||
static bool GSC_MetalGearSolid3(GSRendererHW& r, int& skip);
|
||||
static bool GSC_HitmanBloodMoney(GSRendererHW& r, int& skip);
|
||||
|
||||
static bool OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
|
||||
@@ -4179,6 +4179,10 @@ void GSRendererHW::Draw()
|
||||
|
||||
const int new_w = std::min(2048, std::max(new_size.x, std::max(rt ? rt->m_unscaled_size.x : 0, ds ? ds_size.x : 0)));
|
||||
const int new_h = std::min(2048, std::max(new_size.y, std::max(rt ? rt->m_unscaled_size.y : 0, ds ? ds_size.y : 0)));
|
||||
|
||||
const bool full_cover_clear = is_possible_mem_clear && GSLocalMemory::IsPageAligned(m_cached_ctx.FRAME.PSM, m_r) && m_r.x == 0 && m_r.y == 0 && !preserve_rt_rgb &&
|
||||
!IsPageCopy() && m_r.width() == (m_cached_ctx.FRAME.FBW * 64);
|
||||
|
||||
if (rt)
|
||||
{
|
||||
const u32 old_end_block = rt->m_end_block;
|
||||
@@ -4225,9 +4229,9 @@ void GSRendererHW::Draw()
|
||||
g_texture_cache->AddDirtyRectTarget(rt, GSVector4i(rt->m_valid.x, rt->m_valid.w, rt->m_valid.z, new_h), rt->m_TEX0.PSM, rt->m_TEX0.TBW, mask, false);
|
||||
g_texture_cache->GetTargetSize(rt->m_TEX0.TBP0, rt->m_TEX0.TBW, rt->m_TEX0.PSM, 0, new_h);
|
||||
}
|
||||
|
||||
rt->ResizeValidity(rt->m_valid.rintersect(rt->GetUnscaledRect()));
|
||||
rt->ResizeDrawn(rt->m_drawn_since_read.rintersect(rt->GetUnscaledRect()));
|
||||
const bool rt_cover = full_cover_clear && (m_r.height() + frame_psm.pgs.y) >= rt->m_valid.height();
|
||||
rt->ResizeValidity(rt_cover ? update_rect : rt->m_valid.rintersect(rt->GetUnscaledRect()));
|
||||
rt->ResizeDrawn(rt_cover ? update_rect : rt->m_drawn_since_read.rintersect(rt->GetUnscaledRect()));
|
||||
}
|
||||
|
||||
const bool rt_update = can_update_size || (m_texture_shuffle && (src && rt && src->m_from_target != rt));
|
||||
@@ -4235,7 +4239,7 @@ void GSRendererHW::Draw()
|
||||
// If it's updating from a texture shuffle, limit the size to the source size.
|
||||
if (rt_update && !can_update_size)
|
||||
{
|
||||
if(src->m_from_target)
|
||||
if (src->m_from_target)
|
||||
update_rect = update_rect.rintersect(src->m_from_target->m_valid);
|
||||
|
||||
update_rect = update_rect.rintersect(GSVector4i::loadh(GSVector2i(new_w, new_h)));
|
||||
@@ -4305,16 +4309,18 @@ void GSRendererHW::Draw()
|
||||
DevCon.Warning("HW: Temporary depth buffer creation failed.");
|
||||
}
|
||||
}
|
||||
const bool z_masked = m_cached_ctx.ZBUF.ZMSK;
|
||||
|
||||
if (!m_texture_shuffle && !m_channel_shuffle)
|
||||
{
|
||||
ds->ResizeValidity(ds->GetUnscaledRect());
|
||||
ds->ResizeDrawn(ds->GetUnscaledRect());
|
||||
const bool z_cover = full_cover_clear && (m_r.height() + GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y) >= ds->m_valid.height();
|
||||
ds->ResizeValidity(z_cover ? m_r : ds->GetUnscaledRect());
|
||||
ds->ResizeDrawn(z_cover ? m_r : ds->GetUnscaledRect());
|
||||
}
|
||||
|
||||
// Limit to 2x the vertical height of the resolution (for double buffering)
|
||||
// Dark cloud writes to 424 when the buffer is only 416 high, but masks the Z.
|
||||
// Updating the valid causes the Z to overlap the framebuffer, which is obviously incorrect.
|
||||
const bool z_masked = m_cached_ctx.ZBUF.ZMSK;
|
||||
const bool z_update = can_update_size && !z_masked;
|
||||
|
||||
if (rt && m_using_temp_z)
|
||||
@@ -8179,7 +8185,7 @@ bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_t
|
||||
|
||||
if (start_bp < dirty_end_bp && end_bp > dirty_start_bp)
|
||||
{
|
||||
if (dirty_start_bp > start_bp || dirty_end_bp < end_bp)
|
||||
if (dirty_start_bp <= start_bp && dirty_end_bp >= end_bp)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -228,19 +228,22 @@ bool GSTextureCache::CanTranslate(u32 bp, u32 bw, u32 spsm, GSVector4i r, u32 db
|
||||
{
|
||||
const GSVector2i src_page_size = GSLocalMemory::m_psm[spsm].pgs;
|
||||
const GSVector2i dst_page_size = GSLocalMemory::m_psm[dpsm].pgs;
|
||||
const u32 src_bw = std::max(1U, bw);
|
||||
const u32 dst_bw = std::max(1U, dbw);
|
||||
const bool block_layout_match = GSLocalMemory::m_psm[spsm].bpp == GSLocalMemory::m_psm[dpsm].bpp;
|
||||
const bool bp_page_aligned_bp = ((bp & ~((1 << 5) - 1)) == bp) || bp == dbp || (block_layout_match && bw == dbw);
|
||||
const bool bp_page_aligned_bp = ((bp & ~((1 << 5) - 1)) == bp) || bp == dbp || (block_layout_match && src_bw == dst_bw);
|
||||
const GSVector4i page_mask(GSVector4i((src_page_size.x - 1), (src_page_size.y - 1)).xyxy());
|
||||
const GSVector4i masked_rect(r & ~page_mask);
|
||||
const int src_pixel_width = static_cast<int>(bw * 64);
|
||||
const int src_pixel_width = src_bw * 64;
|
||||
const int dst_pixel_width = dst_bw * 64;
|
||||
// We can do this if:
|
||||
// The page width matches.
|
||||
// The rect width is less than the width of the destination texture and the height is less than or equal to 1 page high.
|
||||
// The rect width and height is equal to the page size and it covers the width of the incoming bw, so lines are sequential.
|
||||
const bool page_aligned_rect = masked_rect.xyxy().eq(r.xyxy());
|
||||
const bool width_match = ((bw * 64) / src_page_size.x) == ((dbw * 64) / dst_page_size.x);
|
||||
const bool width_match = (src_pixel_width / src_page_size.x) == (dst_pixel_width / dst_page_size.x);
|
||||
const bool sequential_pages = page_aligned_rect && r.x == 0 && r.z == src_pixel_width;
|
||||
const bool single_row = (((bw * 64) / src_page_size.x) <= ((dbw * 64) / dst_page_size.x)) && r.width() <= src_pixel_width && r.height() <= src_page_size.y;
|
||||
const bool single_row = ((src_pixel_width / src_page_size.x) <= (dst_pixel_width / dst_page_size.x)) && r.width() <= src_pixel_width && r.height() <= src_page_size.y;
|
||||
const bool single_page_aligned = page_aligned_rect && r.z <= src_page_size.x && r.w <= src_page_size.y;
|
||||
if (block_layout_match)
|
||||
{
|
||||
@@ -300,7 +303,7 @@ GSVector4i GSTextureCache::TranslateAlignedRectByPage(u32 tbp, u32 tebp, u32 tbw
|
||||
return GSVector4i::zero();
|
||||
}
|
||||
|
||||
const bool block_matched_format = s_psm.bpp == t_psm.bpp && (sbw == tbw || sbw == 1);
|
||||
const bool block_matched_format = s_psm.bpp == t_psm.bpp && (clamped_sbw == clamped_tbw || clamped_sbw == 1);
|
||||
// If there is block offset left over, try to adjust to that.
|
||||
if (block_matched_format)
|
||||
{
|
||||
@@ -431,6 +434,8 @@ GSVector4i GSTextureCache::TranslateAlignedRectByPage(u32 tbp, u32 tebp, u32 tbw
|
||||
GSVector4i offset_rect(horizontal_dst_page_offset * t_psm.pgs.x, vertical_dst_page_offset * t_psm.pgs.y);
|
||||
new_rect = in_rect + offset_rect.xyxy();
|
||||
}
|
||||
else
|
||||
new_rect = in_rect;
|
||||
|
||||
if (new_rect.z > dst_bw)
|
||||
{
|
||||
@@ -1320,7 +1325,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||
// If the BP is offset in to a page and the format does not match, trying to match up the correct position is very difficult since we don't swizzle.
|
||||
// Tomb Raider Legends does a block level BP in PSMT8 over a C16 target, which is just a nightmare to get right.
|
||||
// Baldurs Gate used to have this too, but now we can translate HW moves inside targets when the format matches.
|
||||
if (((bp & (BLOCKS_PER_PAGE - 1)) != (t->m_TEX0.TBP0 & (BLOCKS_PER_PAGE - 1))) && (bp & (BLOCKS_PER_PAGE - 1)) && !CanTranslate(bp, bw, psm, block_boundary_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW))
|
||||
if (((bp & (BLOCKS_PER_PAGE - 1)) != (t->m_TEX0.TBP0 & (BLOCKS_PER_PAGE - 1))) && (bp & (BLOCKS_PER_PAGE - 1)))
|
||||
continue;
|
||||
|
||||
const bool width_match = (std::max(64U, bw * 64U) >> GSLocalMemory::m_psm[psm].info.pageShiftX()) ==
|
||||
@@ -2041,11 +2046,29 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||
{
|
||||
rect.y /= 2;
|
||||
rect.w /= 2;
|
||||
|
||||
if (region.HasY())
|
||||
{
|
||||
const u32 min_y = region.GetMinY() / 2;
|
||||
const u32 max_y = region.GetMaxY() / 2;
|
||||
|
||||
region.ClearY();
|
||||
region.SetY(min_y, max_y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.x /= 2;
|
||||
rect.z /= 2;
|
||||
|
||||
if (region.HasX())
|
||||
{
|
||||
const u32 min_x = region.GetMinX() / 2;
|
||||
const u32 max_x = region.GetMaxX() / 2;
|
||||
|
||||
region.ClearX();
|
||||
region.SetX(min_x, max_x);
|
||||
}
|
||||
}
|
||||
if (TEX0.TBP0 == frame.Block())
|
||||
{
|
||||
|
||||
@@ -45,6 +45,9 @@ public:
|
||||
bool HasY() const { return static_cast<u32>(bits >> 32) != 0; }
|
||||
bool HasEither() const { return (bits != 0); }
|
||||
|
||||
void ClearX() { bits &= ~0xFFFFFFFFULL; }
|
||||
void ClearY() { bits &= 0xFFFFFFFFULL; }
|
||||
|
||||
void SetX(s32 min, s32 max) { bits |= (static_cast<u64>(static_cast<u16>(min)) | (static_cast<u64>(static_cast<u16>(max) << 16))); }
|
||||
void SetY(s32 min, s32 max) { bits |= ((static_cast<u64>(static_cast<u16>(min)) << 32) | (static_cast<u64>(static_cast<u16>(max)) << 48)); }
|
||||
|
||||
|
||||
@@ -2475,7 +2475,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
primid_texture = InitPrimDateTexture(colclip_rt ? colclip_rt : config.rt, config.drawarea, config.datm);
|
||||
if (!primid_texture)
|
||||
{
|
||||
Console.WriteLn("GL: Failed to allocate DATE image, aborting draw.");
|
||||
Console.Warning("GL: Failed to allocate DATE image, aborting draw.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -2585,6 +2585,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
{
|
||||
// Ensure all depth writes are finished before sampling
|
||||
GL_INS("GL: Texture barrier to flush depth or rt before reading");
|
||||
g_perfmon.Put(GSPerfMon::Barriers, 1);
|
||||
glTextureBarrier();
|
||||
}
|
||||
// additional non-pipeline config stuff
|
||||
@@ -2607,7 +2608,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
}
|
||||
|
||||
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
|
||||
if (primid_texture)
|
||||
{
|
||||
GL_PUSH("Destination Alpha PrimID Init");
|
||||
|
||||
|
||||
@@ -5630,7 +5630,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
date_image = SetupPrimitiveTrackingDATE(config);
|
||||
if (!date_image)
|
||||
{
|
||||
Console.WriteLn("VK: Failed to allocate DATE image, aborting draw.");
|
||||
Console.Warning("VK: Failed to allocate DATE image, aborting draw.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ class Error;
|
||||
#pragma clang diagnostic ignored "-Wnullability-completeness"
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#pragma clang diagnostic ignored "-Wunused-private-field"
|
||||
#endif
|
||||
|
||||
#define VMA_STATIC_VULKAN_FUNCTIONS 1
|
||||
|
||||
@@ -139,6 +139,7 @@ using ImGuiFullscreen::GetCachedTextureAsync;
|
||||
using ImGuiFullscreen::GetPlaceholderTexture;
|
||||
using ImGuiFullscreen::GetQueuedFocusResetType;
|
||||
using ImGuiFullscreen::HorizontalMenuItem;
|
||||
using ImGuiFullscreen::HorizontalMenuSvgItem;
|
||||
using ImGuiFullscreen::IsFocusResetQueued;
|
||||
using ImGuiFullscreen::IsGamepadInputSource;
|
||||
using ImGuiFullscreen::LayoutScale;
|
||||
@@ -271,8 +272,6 @@ namespace FullscreenUI
|
||||
static std::array<std::shared_ptr<GSTexture>, static_cast<u32>(GameDatabaseSchema::Compatibility::Perfect)>
|
||||
s_game_compatibility_textures;
|
||||
static std::shared_ptr<GSTexture> s_banner_texture;
|
||||
static std::shared_ptr<GSTexture> s_fallback_disc_texture;
|
||||
static std::shared_ptr<GSTexture> s_fallback_exe_texture;
|
||||
static std::vector<std::unique_ptr<GSTexture>> s_cleanup_textures;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -476,9 +475,13 @@ namespace FullscreenUI
|
||||
static void DrawGameListSettingsWindow();
|
||||
static void SwitchToGameList();
|
||||
static void PopulateGameListEntryList();
|
||||
static GSTexture* GetTextureForGameListEntryType(GameList::EntryType type);
|
||||
static GSTexture* GetTextureForGameListEntryType(GameList::EntryType type, const ImVec2& size, SvgScaling mode = SvgScaling::Stretch);
|
||||
static GSTexture* GetGameListCover(const GameList::Entry* entry);
|
||||
static GSTexture* GetCoverForCurrentGame();
|
||||
static void DrawGameCover(const GameList::Entry* entry, const ImVec2& size);
|
||||
static void DrawGameCover(const GameList::Entry* entry, ImDrawList* draw_list, const ImVec2& min, const ImVec2& max);
|
||||
// For when we have no GameList entry
|
||||
static void DrawFallbackCover(const ImVec2& size);
|
||||
static void DrawFallbackCover(ImDrawList* draw_list, const ImVec2& min, const ImVec2& max);
|
||||
|
||||
// Lazily populated cover images.
|
||||
static std::unordered_map<std::string, std::string> s_cover_image_map;
|
||||
@@ -1107,9 +1110,6 @@ void FullscreenUI::ReturnToMainWindow()
|
||||
|
||||
bool FullscreenUI::LoadResources()
|
||||
{
|
||||
s_fallback_disc_texture = LoadTexture("fullscreenui/media-cdrom.png");
|
||||
s_fallback_exe_texture = LoadTexture("fullscreenui/applications-system.png");
|
||||
|
||||
return LoadSvgResources();
|
||||
}
|
||||
|
||||
@@ -1128,8 +1128,6 @@ bool FullscreenUI::LoadSvgResources()
|
||||
|
||||
void FullscreenUI::DestroyResources()
|
||||
{
|
||||
s_fallback_exe_texture.reset();
|
||||
s_fallback_disc_texture.reset();
|
||||
s_banner_texture.reset();
|
||||
for (auto& tex : s_game_compatibility_textures)
|
||||
tex.reset();
|
||||
@@ -1457,27 +1455,26 @@ void FullscreenUI::DrawLandingWindow()
|
||||
{
|
||||
ResetFocusHere();
|
||||
|
||||
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/game-list.png"), FSUI_CSTR("Game List"),
|
||||
if (HorizontalMenuSvgItem("fullscreenui/game-list.svg", FSUI_CSTR("Game List"),
|
||||
FSUI_CSTR("Launch a game from images scanned from your game directories.")))
|
||||
{
|
||||
SwitchToGameList();
|
||||
}
|
||||
|
||||
if (HorizontalMenuItem(
|
||||
GetCachedTexture("fullscreenui/media-cdrom.png"), FSUI_CSTR("Start Game"),
|
||||
if (HorizontalMenuSvgItem("fullscreenui/media-cdrom.svg", FSUI_CSTR("Start Game"),
|
||||
FSUI_CSTR("Launch a game from a file, disc, or starts the console without any disc inserted.")))
|
||||
{
|
||||
s_current_main_window = MainWindowType::StartGame;
|
||||
QueueResetFocus(FocusResetType::WindowChanged);
|
||||
}
|
||||
|
||||
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/applications-system.png"), FSUI_CSTR("Settings"),
|
||||
if (HorizontalMenuSvgItem("fullscreenui/applications-system.svg", FSUI_CSTR("Settings"),
|
||||
FSUI_CSTR("Changes settings for the application.")))
|
||||
{
|
||||
SwitchToSettings();
|
||||
}
|
||||
|
||||
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/exit.png"), FSUI_CSTR("Exit"),
|
||||
if (HorizontalMenuSvgItem("fullscreenui/exit.svg", FSUI_CSTR("Exit"),
|
||||
FSUI_CSTR("Return to desktop mode, or exit the application.")) ||
|
||||
(!AreAnyDialogsOpen() && WantsToCloseMenu()))
|
||||
{
|
||||
@@ -1536,26 +1533,25 @@ void FullscreenUI::DrawStartGameWindow()
|
||||
{
|
||||
ResetFocusHere();
|
||||
|
||||
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/start-file.png"), FSUI_CSTR("Start File"),
|
||||
if (HorizontalMenuSvgItem("fullscreenui/start-file.svg", FSUI_CSTR("Start File"),
|
||||
FSUI_CSTR("Launch a game by selecting a file/disc image.")))
|
||||
{
|
||||
DoStartFile();
|
||||
}
|
||||
|
||||
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/drive-cdrom.png"), FSUI_CSTR("Start Disc"),
|
||||
if (HorizontalMenuSvgItem("fullscreenui/drive-cdrom.svg", FSUI_CSTR("Start Disc"),
|
||||
FSUI_CSTR("Start a game from a disc in your PC's DVD drive.")))
|
||||
{
|
||||
DoStartDisc();
|
||||
}
|
||||
|
||||
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/start-bios.png"), FSUI_CSTR("Start BIOS"),
|
||||
if (HorizontalMenuSvgItem("fullscreenui/start-bios.svg", FSUI_CSTR("Start BIOS"),
|
||||
FSUI_CSTR("Start the console without any disc inserted.")))
|
||||
{
|
||||
DoStartBIOS();
|
||||
}
|
||||
|
||||
// https://www.iconpacks.net/free-icon/arrow-back-3783.html
|
||||
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/back-icon.png"), FSUI_CSTR("Back"),
|
||||
if (HorizontalMenuSvgItem("fullscreenui/back-icon.svg", FSUI_CSTR("Back"),
|
||||
FSUI_CSTR("Return to the previous menu.")) ||
|
||||
(!AreAnyDialogsOpen() && WantsToCloseMenu()))
|
||||
{
|
||||
@@ -1606,8 +1602,7 @@ void FullscreenUI::DrawExitWindow()
|
||||
{
|
||||
ResetFocusHere();
|
||||
|
||||
// https://www.iconpacks.net/free-icon/arrow-back-3783.html
|
||||
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/back-icon.png"), FSUI_CSTR("Back"),
|
||||
if (HorizontalMenuSvgItem("fullscreenui/back-icon.svg", FSUI_CSTR("Back"),
|
||||
FSUI_CSTR("Return to the previous menu.")) ||
|
||||
WantsToCloseMenu())
|
||||
{
|
||||
@@ -1615,7 +1610,7 @@ void FullscreenUI::DrawExitWindow()
|
||||
QueueResetFocus(FocusResetType::WindowChanged);
|
||||
}
|
||||
|
||||
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/exit.png"), FSUI_CSTR("Exit PCSX2"),
|
||||
if (HorizontalMenuSvgItem("fullscreenui/exit.svg", FSUI_CSTR("Exit PCSX2"),
|
||||
FSUI_CSTR("Completely exits the application, returning you to your desktop.")))
|
||||
{
|
||||
DoRequestExit();
|
||||
@@ -1623,8 +1618,8 @@ void FullscreenUI::DrawExitWindow()
|
||||
|
||||
if (!Host::InNoGUIMode())
|
||||
{
|
||||
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/desktop-mode.png"), FSUI_CSTR("Desktop Mode"),
|
||||
FSUI_CSTR("Exits Big Picture mode, returning to the desktop interface.")))
|
||||
if (HorizontalMenuSvgItem("fullscreenui/desktop-mode.svg", FSUI_CSTR("Desktop Mode"),
|
||||
FSUI_CSTR("Exits Big Picture mode, returning to the desktop interface.")))
|
||||
{
|
||||
DoDesktopMode();
|
||||
}
|
||||
@@ -3384,7 +3379,7 @@ void FullscreenUI::DrawSummarySettingsPage()
|
||||
|
||||
for (const std::string& name : Pad::GetInputProfileNames())
|
||||
{
|
||||
options.emplace_back(fmt::format(FSUI_FSTR(name)), (value.has_value() && !value->empty() && value == name) ? true : false);
|
||||
options.emplace_back(name, (value.has_value() && !value->empty() && value == name) ? true : false);
|
||||
names.push_back(std::move(name));
|
||||
}
|
||||
|
||||
@@ -5463,14 +5458,18 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
|
||||
}
|
||||
DrawShadowedText(dl, g_medium_font, subtitle_pos, text_color, s_current_game_subtitle.c_str());
|
||||
|
||||
|
||||
GSTexture* const cover = GetCoverForCurrentGame();
|
||||
const ImVec2 image_min(display_size.x - LayoutScale(10.0f + image_width),
|
||||
display_size.y - LayoutScale(LAYOUT_FOOTER_HEIGHT) - LayoutScale(10.0f + image_height) - rp_height);
|
||||
const ImVec2 image_max(image_min.x + LayoutScale(image_width), image_min.y + LayoutScale(image_height) + rp_height);
|
||||
const ImRect image_rect(CenterImage(
|
||||
ImRect(image_min, image_max), ImVec2(static_cast<float>(cover->GetWidth()), static_cast<float>(cover->GetHeight()))));
|
||||
dl->AddImage(reinterpret_cast<ImTextureID>(cover->GetNativeHandle()), image_rect.Min, image_rect.Max);
|
||||
{
|
||||
auto lock = GameList::GetLock();
|
||||
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(s_current_disc_path.c_str());
|
||||
if (entry)
|
||||
DrawGameCover(entry, dl, image_min, image_max);
|
||||
else
|
||||
DrawFallbackCover(dl, image_min, image_max);
|
||||
}
|
||||
}
|
||||
|
||||
// current time / play time
|
||||
@@ -6286,7 +6285,7 @@ void FullscreenUI::PopulateGameListEntryList()
|
||||
case 4: // CRC
|
||||
{
|
||||
if (lhs->crc != rhs->crc)
|
||||
return reverse ? (lhs->crc >= rhs->crc) : (lhs->crc < rhs->crc);
|
||||
return reverse ? (lhs->crc > rhs->crc) : (lhs->crc < rhs->crc);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -6458,8 +6457,6 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
||||
if (!visible)
|
||||
continue;
|
||||
|
||||
GSTexture* cover_texture = GetGameListCover(entry);
|
||||
|
||||
summary.clear();
|
||||
if (entry->serial.empty())
|
||||
fmt::format_to(std::back_inserter(summary), "{} - ", GameList::RegionToString(entry->region, true));
|
||||
@@ -6469,11 +6466,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
||||
const std::string_view filename(Path::GetFileName(entry->path));
|
||||
summary.append(filename);
|
||||
|
||||
const ImRect image_rect(CenterImage(ImRect(bb.Min, bb.Min + image_size),
|
||||
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
|
||||
|
||||
ImGui::GetWindowDrawList()->AddImage(reinterpret_cast<ImTextureID>(cover_texture->GetNativeHandle()),
|
||||
image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
DrawGameCover(entry, ImGui::GetWindowDrawList(), bb.Min, bb.Min + image_size);
|
||||
|
||||
const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f);
|
||||
const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f);
|
||||
@@ -6514,29 +6507,46 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
||||
|
||||
if (BeginFullscreenColumnWindow(-530.0f, 0.0f, "game_list_info", UIPrimaryDarkColor))
|
||||
{
|
||||
const GSTexture* cover_texture =
|
||||
selected_entry ? GetGameListCover(selected_entry) : GetTextureForGameListEntryType(GameList::EntryType::Count);
|
||||
if (cover_texture)
|
||||
{
|
||||
const ImRect image_rect(CenterImage(LayoutScale(ImVec2(275.0f, 400.0f)),
|
||||
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
|
||||
const float img_padding_y = LayoutScale(20.0f);
|
||||
// Spacing between each text item
|
||||
const float text_spacing_y = LayoutScale(8.0f);
|
||||
// Space between title/serial and details, is in addition to text_spacing_y
|
||||
const float title_padding_below_y = LayoutScale(12.0f);
|
||||
|
||||
ImGui::SetCursorPos(LayoutScale(ImVec2(128.0f, 20.0f)) + image_rect.Min);
|
||||
ImGui::Image(reinterpret_cast<ImTextureID>(selected_entry ? GetGameListCover(selected_entry)->GetNativeHandle() :
|
||||
GetTextureForGameListEntryType(GameList::EntryType::Count)->GetNativeHandle()),
|
||||
image_rect.GetSize());
|
||||
}
|
||||
// Estimate how much space is needed for text
|
||||
// Do this even when nothing is selected, to ensure cover/icon is in a consistant size/position
|
||||
const float title_detail_height =
|
||||
LayoutScale(LAYOUT_LARGE_FONT_SIZE) + text_spacing_y + // Title
|
||||
LayoutScale(LAYOUT_MEDIUM_FONT_SIZE) + text_spacing_y + // Serial
|
||||
title_padding_below_y +
|
||||
7.0f * (LayoutScale(LAYOUT_MEDIUM_FONT_SIZE) + text_spacing_y) + // File, CRC, Region, Compat, Time/Last Played, Size
|
||||
LayoutScale(12.0f); // Extra padding
|
||||
|
||||
const float work_width = ImGui::GetCurrentWindow()->WorkRect.GetWidth();
|
||||
constexpr float field_margin_y = 10.0f;
|
||||
constexpr float start_x = 50.0f;
|
||||
float text_y = 440.0f;
|
||||
// Limit cover height to avoid pushing text off the screen
|
||||
const ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
// Based on ImGui code for WorkRect, with scrolling logic removed
|
||||
const float window_height = std::trunc(window->InnerRect.GetHeight() - 2.0f * std::max(window->WindowPadding.y, window->WindowBorderSize));
|
||||
|
||||
const float free_height = window_height - title_detail_height;
|
||||
const float img_height = std::min(free_height - 2.0f * img_padding_y, LayoutScale(400.0f));
|
||||
|
||||
const ImVec2 image_size = ImVec2(LayoutScale(275.0f), img_height);
|
||||
ImGui::SetCursorPos(ImVec2(LayoutScale(128.0f), img_padding_y));
|
||||
|
||||
if (selected_entry)
|
||||
DrawGameCover(selected_entry, image_size);
|
||||
else
|
||||
DrawFallbackCover(image_size);
|
||||
|
||||
const float work_width = window->WorkRect.GetWidth();
|
||||
const float start_x = LayoutScale(50.0f);
|
||||
const float text_y = img_height + 2.0f * img_padding_y;
|
||||
float text_width;
|
||||
|
||||
PushPrimaryColor();
|
||||
ImGui::SetCursorPos(LayoutScale(start_x, text_y));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, field_margin_y));
|
||||
ImGui::PushTextWrapPos(LayoutScale(480.0f));
|
||||
ImGui::SetCursorPos(ImVec2(start_x, text_y));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, text_spacing_y));
|
||||
ImGui::PushTextWrapPos(LayoutScale(490.0f));
|
||||
ImGui::BeginGroup();
|
||||
|
||||
if (selected_entry)
|
||||
@@ -6545,6 +6555,8 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
||||
ImGui::PushFont(g_large_font);
|
||||
const std::string_view title(std::string_view(selected_entry->GetTitle(true)).substr(0, 37));
|
||||
text_width = ImGui::CalcTextSize(title.data(), title.data() + title.length(), false, work_width).x;
|
||||
if (title.length() != selected_entry->GetTitle(true).length())
|
||||
text_width += ImGui::CalcTextSize("...", nullptr, false, -1.0f).x;
|
||||
ImGui::SetCursorPosX((work_width - text_width) / 2.0f);
|
||||
ImGui::TextWrapped(
|
||||
"%.*s%s", static_cast<int>(title.size()), title.data(), (title.length() == selected_entry->GetTitle(true).length()) ? "" : "...");
|
||||
@@ -6556,7 +6568,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
||||
text_width = ImGui::CalcTextSize(selected_entry->serial.c_str(), nullptr, false, work_width).x;
|
||||
ImGui::SetCursorPosX((work_width - text_width) / 2.0f);
|
||||
ImGui::TextWrapped("%s", selected_entry->serial.c_str());
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 15.0f);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + title_padding_below_y);
|
||||
|
||||
// file tile
|
||||
ImGui::TextWrapped("%s", SmallString::from_format(FSUI_FSTR("File: {}"), Path::GetFileName(selected_entry->path)).c_str());
|
||||
@@ -6693,12 +6705,7 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
|
||||
bb.Min += style.FramePadding;
|
||||
bb.Max -= style.FramePadding;
|
||||
|
||||
const GSTexture* const cover_texture = GetGameListCover(entry);
|
||||
const ImRect image_rect(CenterImage(ImRect(bb.Min, bb.Min + image_size),
|
||||
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
|
||||
|
||||
ImGui::GetWindowDrawList()->AddImage(reinterpret_cast<ImTextureID>(cover_texture->GetNativeHandle()),
|
||||
image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
DrawGameCover(entry, ImGui::GetWindowDrawList(), bb.Min, bb.Min + image_size);
|
||||
|
||||
const ImRect title_bb(ImVec2(bb.Min.x, bb.Min.y + image_height + title_spacing), bb.Max);
|
||||
const std::string_view title(std::string_view(entry->GetTitle(true)).substr(0, 31));
|
||||
@@ -6987,33 +6994,108 @@ GSTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
|
||||
cover_it = s_cover_image_map.emplace(entry->path, std::move(cover_path)).first;
|
||||
}
|
||||
|
||||
GSTexture* tex = (!cover_it->second.empty()) ? GetCachedTextureAsync(cover_it->second.c_str()) : nullptr;
|
||||
return tex ? tex : GetTextureForGameListEntryType(entry->type);
|
||||
return (!cover_it->second.empty()) ? GetCachedTextureAsync(cover_it->second.c_str()) : nullptr;
|
||||
}
|
||||
|
||||
GSTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType type)
|
||||
GSTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType type, const ImVec2& size, SvgScaling mode)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GameList::EntryType::ELF:
|
||||
return s_fallback_exe_texture.get();
|
||||
return GetCachedSvgTexture("fullscreenui/applications-system.svg", size, mode);
|
||||
|
||||
case GameList::EntryType::PS1Disc:
|
||||
case GameList::EntryType::PS2Disc:
|
||||
default:
|
||||
return s_fallback_disc_texture.get();
|
||||
return GetCachedSvgTexture("fullscreenui/media-cdrom.svg", size, mode);
|
||||
}
|
||||
}
|
||||
|
||||
GSTexture* FullscreenUI::GetCoverForCurrentGame()
|
||||
void FullscreenUI::DrawGameCover(const GameList::Entry* entry, const ImVec2& size)
|
||||
{
|
||||
auto lock = GameList::GetLock();
|
||||
// Used in DrawGameList (selected preview)
|
||||
const GSTexture* cover_texture = GetGameListCover(entry);
|
||||
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(s_current_disc_path.c_str());
|
||||
if (!entry)
|
||||
return s_fallback_disc_texture.get();
|
||||
pxAssert(ImGui::GetCurrentContext()->Style.ImageBorderSize == 0);
|
||||
const ImVec2 origin = ImGui::GetCursorPos();
|
||||
|
||||
return GetGameListCover(entry);
|
||||
if (cover_texture)
|
||||
{
|
||||
const ImRect image_rect(CenterImage(size,
|
||||
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
|
||||
|
||||
ImGui::SetCursorPos(origin + image_rect.Min);
|
||||
ImGui::Image(reinterpret_cast<ImTextureID>(cover_texture->GetNativeHandle()), image_rect.GetSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
const float min_size = std::min(size.x, size.y);
|
||||
const ImVec2 image_square(min_size, min_size);
|
||||
GSTexture* const icon_texture = GetTextureForGameListEntryType(entry->type, image_square);
|
||||
|
||||
const ImRect image_rect(CenterImage(size, image_square));
|
||||
|
||||
ImGui::SetCursorPos(origin + image_rect.Min);
|
||||
DrawSvgTexture(icon_texture, image_square);
|
||||
}
|
||||
// Pretend the image we drew was the the size passed to us
|
||||
ImGui::SetCursorPos(origin);
|
||||
ImGui::Dummy(size);
|
||||
}
|
||||
|
||||
void FullscreenUI::DrawGameCover(const GameList::Entry* entry, ImDrawList* draw_list, const ImVec2& min, const ImVec2& max)
|
||||
{
|
||||
// Used in DrawPauseMenu, DrawGameList (list item), DrawGameGrid
|
||||
const GSTexture* cover_texture = GetGameListCover(entry);
|
||||
|
||||
if (cover_texture)
|
||||
{
|
||||
const ImRect image_rect(CenterImage(ImRect(min, max),
|
||||
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
|
||||
|
||||
draw_list->AddImage(reinterpret_cast<ImTextureID>(cover_texture->GetNativeHandle()),
|
||||
image_rect.Min, image_rect.Max);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float min_size = std::min(max.x - min.x, max.y - min.y);
|
||||
const ImVec2 image_square(min_size, min_size);
|
||||
|
||||
const ImRect image_rect(CenterImage(ImRect(min, max), image_square));
|
||||
|
||||
DrawListSvgTexture(draw_list, GetTextureForGameListEntryType(entry->type, image_square, SvgScaling::Fit),
|
||||
image_rect.Min, image_rect.Max);
|
||||
}
|
||||
}
|
||||
|
||||
void FullscreenUI::DrawFallbackCover(const ImVec2& size)
|
||||
{
|
||||
pxAssert(ImGui::GetCurrentContext()->Style.ImageBorderSize == 0);
|
||||
const ImVec2 origin = ImGui::GetCursorPos();
|
||||
|
||||
const float min_size = std::min(size.x, size.y);
|
||||
const ImVec2 image_square(min_size, min_size);
|
||||
GSTexture* const icon_texture = GetTextureForGameListEntryType(GameList::EntryType::PS2Disc, image_square);
|
||||
|
||||
const ImRect image_rect(CenterImage(size, image_square));
|
||||
|
||||
ImGui::SetCursorPos(origin + image_rect.Min);
|
||||
DrawSvgTexture(icon_texture, image_square);
|
||||
|
||||
// Pretend the image we drew was the the size passed to us
|
||||
ImGui::SetCursorPos(origin);
|
||||
ImGui::Dummy(size);
|
||||
}
|
||||
|
||||
void FullscreenUI::DrawFallbackCover(ImDrawList* draw_list, const ImVec2& min, const ImVec2& max)
|
||||
{
|
||||
const float min_size = std::min(max.x - min.x, max.y - min.y);
|
||||
const ImVec2 image_square(min_size, min_size);
|
||||
|
||||
const ImRect image_rect(CenterImage(ImRect(min, max), image_square));
|
||||
|
||||
DrawListSvgTexture(draw_list, GetTextureForGameListEntryType(GameList::EntryType::PS2Disc, image_square, SvgScaling::Fit),
|
||||
image_rect.Min, image_rect.Max);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1992,7 +1992,7 @@ void ImGuiFullscreen::EndHorizontalMenu()
|
||||
EndFullscreenWindow();
|
||||
}
|
||||
|
||||
bool ImGuiFullscreen::HorizontalMenuItem(GSTexture* icon, const char* title, const char* description)
|
||||
bool ImGuiFullscreen::HorizontalMenuItem(GSTexture* icon, const ImVec2& icon_uv0, const ImVec2& icon_uv1, const char* title, const char* description)
|
||||
{
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
@@ -2031,7 +2031,7 @@ bool ImGuiFullscreen::HorizontalMenuItem(GSTexture* icon, const char* title, con
|
||||
const ImVec2 icon_pos = bb.Min + ImVec2((avail_width - icon_size) * 0.5f, 0.0f);
|
||||
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
dl->AddImage(reinterpret_cast<ImTextureID>(icon->GetNativeHandle()), icon_pos, icon_pos + ImVec2(icon_size, icon_size));
|
||||
dl->AddImage(reinterpret_cast<ImTextureID>(icon->GetNativeHandle()), icon_pos, icon_pos + ImVec2(icon_size, icon_size), icon_uv0, icon_uv1);
|
||||
|
||||
ImFont* title_font = g_large_font;
|
||||
const ImVec2 title_size = title_font->CalcTextSizeA(title_font->FontSize, avail_width, 0.0f, title);
|
||||
@@ -2056,6 +2056,22 @@ bool ImGuiFullscreen::HorizontalMenuItem(GSTexture* icon, const char* title, con
|
||||
return pressed;
|
||||
}
|
||||
|
||||
bool ImGuiFullscreen::HorizontalMenuItem(GSTexture* icon, const char* title, const char* description)
|
||||
{
|
||||
return HorizontalMenuItem(icon, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), title, description);
|
||||
}
|
||||
|
||||
bool ImGuiFullscreen::HorizontalMenuSvgItem(const char* svg_path, const char* title, const char* description, SvgScaling mode)
|
||||
{
|
||||
const ImVec2 icon_size = LayoutScale(150.0f, 150.0f);
|
||||
GSTexture* padded_texture = GetCachedSvgTexture(svg_path, icon_size, mode);
|
||||
|
||||
const ImVec2 padded_size(padded_texture->GetWidth(), padded_texture->GetHeight());
|
||||
const ImVec2 uv1 = icon_size / padded_size;
|
||||
|
||||
return HorizontalMenuItem(padded_texture, ImVec2(0.0f, 0.0f), uv1, title, description);
|
||||
}
|
||||
|
||||
void ImGuiFullscreen::PopulateFileSelectorItems()
|
||||
{
|
||||
s_file_selector_items.clear();
|
||||
|
||||
@@ -235,7 +235,9 @@ namespace ImGuiFullscreen
|
||||
|
||||
bool BeginHorizontalMenu(const char* name, const ImVec2& position, const ImVec2& size, u32 num_items);
|
||||
void EndHorizontalMenu();
|
||||
bool HorizontalMenuItem(GSTexture* icon, const ImVec2& icon_uv0, const ImVec2& icon_uv1, const char* title, const char* description);
|
||||
bool HorizontalMenuItem(GSTexture* icon, const char* title, const char* description);
|
||||
bool HorizontalMenuSvgItem(const char* svg_path, const char* title, const char* description, SvgScaling mode = SvgScaling::Stretch);
|
||||
|
||||
using FileSelectorCallback = std::function<void(const std::string& path)>;
|
||||
using FileSelectorFilters = std::vector<std::string>;
|
||||
|
||||
@@ -416,10 +416,10 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa
|
||||
|
||||
APPEND("B={} PL={} ", static_cast<unsigned>(GSConfig.AccurateBlendingUnit), static_cast<unsigned>(GSConfig.TexturePreloading));
|
||||
if (GSConfig.GPUPaletteConversion)
|
||||
APPEND("PT ");
|
||||
APPEND("PLTX ");
|
||||
|
||||
if (GSConfig.HWDownloadMode != GSHardwareDownloadMode::Enabled)
|
||||
APPEND("DL={} ", static_cast<unsigned>(GSConfig.HWDownloadMode));
|
||||
APPEND("HWDM={} ", static_cast<unsigned>(GSConfig.HWDownloadMode));
|
||||
|
||||
if (GSConfig.HWMipmap)
|
||||
APPEND("MM ");
|
||||
@@ -466,7 +466,7 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa
|
||||
if (GSConfig.UserHacks_CPUFBConversion)
|
||||
APPEND("FBC ");
|
||||
if (GSConfig.UserHacks_ReadTCOnClose)
|
||||
APPEND("FTC ");
|
||||
APPEND("RTOC ");
|
||||
if (GSConfig.UserHacks_DisableDepthSupport)
|
||||
APPEND("DDC ");
|
||||
if (GSConfig.UserHacks_DisablePartialInvalidation)
|
||||
|
||||
@@ -947,12 +947,12 @@ namespace R3000A
|
||||
return 1;
|
||||
|
||||
// maximum allowed size for our buffer before we truncate
|
||||
const unsigned int max_len = 4096;
|
||||
constexpr unsigned int max_len = 4096;
|
||||
char tmp[max_len], tmp2[max_len];
|
||||
char* ptmp = tmp;
|
||||
unsigned int printed_bytes = 0;
|
||||
int remaining_buf = max_len - 1;
|
||||
int n = 1, i = 0, j = 0;
|
||||
unsigned int n = 1, i = 0, j = 0;
|
||||
|
||||
while (fmt[i])
|
||||
{
|
||||
|
||||