Compare commits
209 Commits
script/and
...
test-donot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18704cc65f | ||
|
|
b40f91974b | ||
|
|
5f6ee64dc3 | ||
|
|
722a8e4ede | ||
|
|
9caa1d5b1f | ||
|
|
c0ea48e440 | ||
|
|
03e872f69c | ||
|
|
61ff781e0a | ||
|
|
ee2f1a62de | ||
|
|
aee49e4877 | ||
|
|
c5f30776b6 | ||
|
|
ae1c974c2e | ||
|
|
1aec0c0c50 | ||
|
|
c15cd06711 | ||
|
|
1067a69daf | ||
|
|
d6a8c0b013 | ||
|
|
59a2fdba90 | ||
|
|
7e875e9072 | ||
|
|
fc6081d3b9 | ||
|
|
ff36a072e3 | ||
|
|
7fccd7573a | ||
|
|
3e5ba87a4a | ||
|
|
731965ac8f | ||
|
|
094b2a3274 | ||
|
|
2ce3e09d62 | ||
|
|
cc47aa473a | ||
|
|
b0cd2e8cbe | ||
|
|
2ccf9811b7 | ||
|
|
583a9fd320 | ||
|
|
6df9d06e65 | ||
|
|
d7a120fe74 | ||
|
|
6f6ce56528 | ||
|
|
dc8c6ce6d5 | ||
|
|
22e8b24361 | ||
|
|
f63226ff10 | ||
|
|
9f3803c986 | ||
|
|
5899816f7c | ||
|
|
280d05452a | ||
|
|
ccc31edfbb | ||
|
|
7ce570f420 | ||
|
|
fbf023fa88 | ||
|
|
98b4bfb461 | ||
|
|
4e6c978574 | ||
|
|
09b897dbf2 | ||
|
|
3fc9f0efe3 | ||
|
|
b17b70e09d | ||
|
|
65083b10d8 | ||
|
|
f2227af36c | ||
|
|
f1a5f9137b | ||
|
|
f933828749 | ||
|
|
05e8e1d494 | ||
|
|
5937d19750 | ||
|
|
313e885f7a | ||
|
|
6b0b72e034 | ||
|
|
c6d85b7589 | ||
|
|
005b1dd3be | ||
|
|
2c4748cc3d | ||
|
|
a2dbff0bc3 | ||
|
|
1574e7e804 | ||
|
|
ef0061f72b | ||
|
|
b76b74dcb7 | ||
|
|
f6ecb812ca | ||
|
|
54d6a2015d | ||
|
|
9a2e2f922f | ||
|
|
1f74ed2272 | ||
|
|
1307614ad0 | ||
|
|
989a6b7870 | ||
|
|
1cf65cba2a | ||
|
|
3fd7821fc8 | ||
|
|
59521d7e96 | ||
|
|
1fccea8db1 | ||
|
|
94ac08fcd7 | ||
|
|
992ea70ce7 | ||
|
|
9de6c125a7 | ||
|
|
5447ed0d60 | ||
|
|
8b9792855f | ||
|
|
419be467ee | ||
|
|
6df35d859a | ||
|
|
bd28d4269b | ||
|
|
0baf482214 | ||
|
|
690dfc66f5 | ||
|
|
ef7e43bc30 | ||
|
|
d4d704cc26 | ||
|
|
1c9f603947 | ||
|
|
3873b3fe6c | ||
|
|
fb7d5086b7 | ||
|
|
c5cbd67dbc | ||
|
|
7bd0d42bab | ||
|
|
08dbacdf53 | ||
|
|
da8809c240 | ||
|
|
eb7159a859 | ||
|
|
65bef7ec08 | ||
|
|
78a44a4ef6 | ||
|
|
b07356fcbf | ||
|
|
500802cb72 | ||
|
|
e31411170a | ||
|
|
8c350dfd37 | ||
|
|
de3ac7ad4e | ||
|
|
66903b4b98 | ||
|
|
df088c6442 | ||
|
|
3da798ba05 | ||
|
|
58c76ece13 | ||
|
|
e1108010a6 | ||
|
|
3553c372dd | ||
|
|
261dcc30c1 | ||
|
|
095f8e92b1 | ||
|
|
0e6a271e40 | ||
|
|
f22407de1e | ||
|
|
8fa7b068b8 | ||
|
|
29c629737b | ||
|
|
f03b4d77c2 | ||
|
|
41148132f0 | ||
|
|
bf1efc6a4c | ||
|
|
7d535a06c3 | ||
|
|
3a7091d193 | ||
|
|
bdc7acce27 | ||
|
|
68593f9ddc | ||
|
|
979d203a77 | ||
|
|
e086078e41 | ||
|
|
535b33bc6b | ||
|
|
e6b3f98b13 | ||
|
|
b03eb6bd78 | ||
|
|
e4e71a36de | ||
|
|
a83157f0a9 | ||
|
|
8c1e47bbda | ||
|
|
c18662d039 | ||
|
|
4fb7aea7b4 | ||
|
|
a8af150df4 | ||
|
|
5b41abc1b1 | ||
|
|
91f3a4e4bb | ||
|
|
aff095523d | ||
|
|
49ba71a594 | ||
|
|
97cfd9786f | ||
|
|
bc1873c944 | ||
|
|
e3b10eba10 | ||
|
|
ce26d82d09 | ||
|
|
a353f07b8f | ||
|
|
1b160c6091 | ||
|
|
95cb6b6b3d | ||
|
|
7b4b0bb6ca | ||
|
|
6e93db52a5 | ||
|
|
e33c814879 | ||
|
|
5f3ad571cb | ||
|
|
20b171ded3 | ||
|
|
68edde944a | ||
|
|
510c795bd6 | ||
|
|
eb7b62fefc | ||
|
|
b974a8f7a8 | ||
|
|
40fd7d5558 | ||
|
|
a99c35d0ff | ||
|
|
83323d7993 | ||
|
|
6b8d5c1963 | ||
|
|
7b94b7ebc1 | ||
|
|
5d645ba905 | ||
|
|
5f70c3fc57 | ||
|
|
b87d0215a0 | ||
|
|
e6e6785583 | ||
|
|
60bffd9992 | ||
|
|
130632b8ef | ||
|
|
e2264170f6 | ||
|
|
737a409088 | ||
|
|
e7130a28d6 | ||
|
|
d9db45913c | ||
|
|
6d2c8e6dbb | ||
|
|
19600b7e0e | ||
|
|
dd74bb459b | ||
|
|
648bf1e813 | ||
|
|
cbef1f3451 | ||
|
|
b4472764eb | ||
|
|
5fe7a51522 | ||
|
|
2e0374c458 | ||
|
|
61a0f38a2a | ||
|
|
72d61c38be | ||
|
|
d45094c246 | ||
|
|
1607f62149 | ||
|
|
17ddbb2240 | ||
|
|
2eb425d7f8 | ||
|
|
114c946971 | ||
|
|
0e4382d09b | ||
|
|
e3a9e85099 | ||
|
|
eb120ac1ac | ||
|
|
956c28b41a | ||
|
|
79f20ece71 | ||
|
|
1373ee90b0 | ||
|
|
74a4816530 | ||
|
|
30706cde41 | ||
|
|
e436030383 | ||
|
|
1bcc998040 | ||
|
|
fbd357d82c | ||
|
|
3fc3b5fdad | ||
|
|
b5f8ff7035 | ||
|
|
002078af36 | ||
|
|
e4b4a75cab | ||
|
|
7103abccdb | ||
|
|
319dfeaad8 | ||
|
|
49e4f3b29a | ||
|
|
bafc6f34fb | ||
|
|
f9d612329a | ||
|
|
6ca3d3a1b1 | ||
|
|
7b40a8df80 | ||
|
|
53f10ab19e | ||
|
|
33c72ef7e1 | ||
|
|
acae4b089d | ||
|
|
8055a64b5f | ||
|
|
e4ae8a72dd | ||
|
|
d68d0bd65e | ||
|
|
a35c9d5576 | ||
|
|
eade2c19c8 | ||
|
|
1ff788bed5 |
43
dist/dev.eden_emu.eden.metainfo.xml
vendored
@@ -1,22 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
|
||||
<component type="desktop-application">
|
||||
<id>org.eden_emu.eden</id>
|
||||
<id>org.yuzu_emu.yuzu</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<name>eden</name>
|
||||
<name>yuzu</name>
|
||||
<summary>Nintendo Switch emulator</summary>
|
||||
<description>
|
||||
<p>Nintendo Switch video game console emulator</p>
|
||||
<p>yuzu is the world's most popular, open-source, Nintendo Switch emulator — started by the creators of Citra.</p>
|
||||
<p>The emulator is capable of running most commercial games at full speed, provided you meet the necessary hardware requirements.</p>
|
||||
<p>For a full list of games yuzu support, please visit our Compatibility page.</p>
|
||||
<p>Check out our website for the latest news on exciting features, monthly progress reports, and more!</p>
|
||||
</description>
|
||||
<categories>
|
||||
<category>Game</category>
|
||||
@@ -26,16 +24,16 @@ SPDX-License-Identifier: CC0-1.0
|
||||
<keyword>switch</keyword>
|
||||
<keyword>emulator</keyword>
|
||||
</keywords>
|
||||
<url type="homepage">https://eden-emu.dev/</url>
|
||||
<url type="bugtracker">https://git.eden-emu.dev/eden-emu/eden/issues</url>
|
||||
<url type="faq">https://eden-emu.dev/docs</url>
|
||||
<url type="help">https://eden-emu.dev/docs</url>
|
||||
<url type="donation">https://eden-emu.dev/donations</url>
|
||||
<url type="translate">https://explore.transifex.com/edenemu/eden-emulator</url>
|
||||
<url type="contact">https://discord.gg/edenemu</url>
|
||||
<url type="vcs-browser">https://git.eden-emu.dev</url>
|
||||
<url type="contribute">https://git.eden-emu.dev/eden-emu/eden</url>
|
||||
<launchable type="desktop-id">org.eden_emu.eden.desktop</launchable>
|
||||
<url type="homepage">https://yuzu-emu.org/</url>
|
||||
<url type="bugtracker">https://github.com/yuzu-emu/yuzu/issues</url>
|
||||
<url type="faq">https://yuzu-emu.org/wiki/faq/</url>
|
||||
<url type="help">https://yuzu-emu.org/wiki/home/</url>
|
||||
<url type="donation">https://yuzu-emu.org/donate/</url>
|
||||
<url type="translate">https://www.transifex.com/projects/p/yuzu</url>
|
||||
<url type="contact">https://community.citra-emu.org/</url>
|
||||
<url type="vcs-browser">https://github.com/yuzu-emu/yuzu</url>
|
||||
<url type="contribute">https://yuzu-emu.org/wiki/contributing/</url>
|
||||
<launchable type="desktop-id">org.yuzu_emu.yuzu.desktop</launchable>
|
||||
<provides>
|
||||
<binary>yuzu</binary>
|
||||
<binary>yuzu-cmd</binary>
|
||||
@@ -52,6 +50,13 @@ SPDX-License-Identifier: CC0-1.0
|
||||
<memory>16384</memory>
|
||||
</recommends>
|
||||
<project_license>GPL-3.0-or-later</project_license>
|
||||
<developer_name>Eden Emulator Team</developer_name>
|
||||
<developer_name>yuzu Emulator Team</developer_name>
|
||||
<content_rating type="oars-1.0"/>
|
||||
<screenshots>
|
||||
<screenshot type="default"><image>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/001-Super%20Mario%20Odyssey%20.png</image></screenshot>
|
||||
<screenshot><image>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/004-The%20Legend%20of%20Zelda%20Skyward%20Sword%20HD.png</image></screenshot>
|
||||
<screenshot><image>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/007-Pokemon%20Sword.png</image></screenshot>
|
||||
<screenshot><image>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/010-Hyrule%20Warriors%20Age%20of%20Calamity.png</image></screenshot>
|
||||
<screenshot><image>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/039-Pok%C3%A9mon%20Mystery%20Dungeon%20Rescue%20Team%20DX.png.png.png</image></screenshot>
|
||||
</screenshots>
|
||||
</component>
|
||||
|
||||
673
dist/dev.eden_emu.eden.svg
vendored
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 9.2 KiB |
BIN
dist/eden.icns
vendored
BIN
dist/eden.ico
vendored
|
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 346 KiB |
BIN
dist/qt_themes/default/icons/256x256/eden.png
vendored
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
BIN
dist/qt_themes/default/icons/256x256/eden_named.png
vendored
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
BIN
dist/eden.bmp → dist/yuzu.bmp
vendored
|
Before Width: | Height: | Size: 256 KiB After Width: | Height: | Size: 256 KiB |
BIN
dist/yuzu.icns
vendored
Normal file
@@ -18,7 +18,6 @@ plugins {
|
||||
id("androidx.navigation.safeargs.kotlin")
|
||||
id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
|
||||
id("com.github.triplet.play") version "3.8.6"
|
||||
id("idea")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,8 +27,6 @@ plugins {
|
||||
*/
|
||||
val autoVersion = (((System.currentTimeMillis() / 1000) - 1451606400) / 10).toInt()
|
||||
|
||||
val edenDir = project(":Eden").projectDir
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
android {
|
||||
namespace = "org.yuzu.yuzu_emu"
|
||||
@@ -244,17 +241,11 @@ android {
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version = "3.22.1"
|
||||
path = file("${edenDir}/CMakeLists.txt")
|
||||
path = file("../../../CMakeLists.txt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
// Inclusion to exclude build/ dir from non-Android
|
||||
excludeDirs.add(file("${edenDir}/build"))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register<Delete>("ktlintReset", fun Delete.() {
|
||||
delete(File(layout.buildDirectory.toString() + File.separator + "intermediates/ktLint"))
|
||||
@@ -355,7 +346,7 @@ fun getGitVersion(): String {
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
val artifactsDir = layout.projectDirectory.dir("${edenDir}/artifacts")
|
||||
val artifactsDir = layout.projectDirectory.dir("../../../artifacts")
|
||||
val outputsDir = layout.buildDirectory.dir("outputs").get()
|
||||
|
||||
android.applicationVariants.forEach { variant ->
|
||||
|
||||
|
Before Width: | Height: | Size: 672 KiB After Width: | Height: | Size: 631 KiB |
|
Before Width: | Height: | Size: 102 KiB |
39
src/android/app/src/main/res/drawable/ic_yuzu.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:pathData="M317.52,255.37C318.06,255.37 318.6,255.38 319.65,255.41C322.37,255.43 324.58,255.42 326.79,255.41C349.49,255.43 372.2,255.43 394.91,255.46C400.46,255.47 406.02,255.65 411.58,255.6C413.68,255.59 414.71,256.12 414.54,258.43C414.35,261.26 414.33,264.1 414.37,266.94C414.4,269.64 413.52,270.71 410.51,270.7C377.29,270.59 344.07,270.65 310.85,270.66C295.82,270.66 280.79,270.66 265.75,270.66C264.61,270.66 263.47,270.66 262.06,270.66C262.06,272.3 262.06,273.7 262.06,275.5C312.31,275.5 362.6,275.5 413.71,275.5C412.65,281.11 411.74,286.21 410.66,291.28C410.55,291.83 409.53,292.38 408.84,292.56C407.95,292.78 406.96,292.62 406.01,292.62C359.34,292.62 312.66,292.62 265.99,292.62C264.75,292.62 263.51,292.62 262.03,292.62C262.03,294.2 262.03,295.49 262.03,297.18C310.9,297.18 359.72,297.18 409.36,297.18C408.27,300.87 407.4,304.03 406.39,307.15C404.52,312.95 404.48,312.94 398.53,312.94C354.38,312.94 310.22,312.95 266.07,312.92C263.76,312.91 261.86,313.09 262.84,316.08C263.07,316.81 264.11,317.46 264.92,317.77C265.65,318.06 266.58,317.84 267.42,317.84C310.84,317.84 354.26,317.84 397.67,317.84C399.01,317.84 400.34,317.84 402.55,317.84C400.14,322.79 398.2,327.17 395.86,331.32C395.42,332.11 393.44,332.31 392.17,332.31C373.14,332.37 354.11,332.35 335.08,332.35C313.54,332.35 291.98,332.36 270.43,332.35C268.66,332.35 266.89,332.26 264.63,332.33C262.52,332.45 260.88,332.45 259.24,332.45C258.67,327.67 257.69,322.9 257.58,318.11C257.21,300.69 257.06,283.26 257.01,265.84C257,262.52 257.85,259.2 258.72,255.69C260.68,255.49 262.21,255.48 264.04,255.51C279.33,255.53 294.33,255.5 309.62,255.47C312.44,255.44 314.98,255.4 317.52,255.37Z"
|
||||
android:fillColor="#BF42F6"/>
|
||||
<path
|
||||
android:pathData="M196.08,256.6C196.08,254.63 196.08,252.67 196.08,250.27C194.45,250.27 192.91,250.27 191.37,250.27C161.1,250.29 130.82,250.28 100.54,250.37C97.92,250.38 96.9,249.67 97.32,246.97C97.78,243.97 98.17,240.93 98.27,237.9C98.34,235.6 99.36,235.17 101.4,235.17C132.61,235.1 163.82,234.94 195.04,234.86C197.66,234.86 199.69,234.63 199.14,230.9C165.95,230.9 132.78,230.9 99.13,230.9C100.08,226.41 100.73,222.27 101.94,218.31C102.23,217.39 104.41,216.53 105.72,216.53C137.99,216.47 170.27,216.52 202.54,216.6C204.76,216.61 206.52,216.34 206.71,213.17C172.35,213.17 138.07,213.17 102.94,213.17C105.27,206.84 107.26,201.13 109.56,195.55C109.89,194.76 111.78,194.23 112.96,194.22C129.67,194.14 146.39,194.08 163.1,194.23C166.69,194.27 168.59,192.48 170.67,189.3C151.01,189.3 131.87,189.3 111.88,189.3C114.84,183.98 117.32,179.27 120.09,174.74C120.55,174 122.27,173.74 123.4,173.74C144.22,173.68 165.04,173.54 185.85,173.82C190.74,173.88 194.5,172.04 198.31,169.21C173.73,169.21 149.15,169.21 123.5,169.21C125.26,166.62 126.46,164.76 127.76,162.97C133.8,154.66 133.81,154.67 144.13,154.67C171.78,154.66 199.43,154.67 227.08,154.67C228.19,154.67 229.29,154.67 231.08,154.67C228.38,151.86 226.32,149.93 222.7,149.94C196.21,150.06 169.72,150.01 143.22,150.01C142.01,150.01 140.79,150.01 139.58,150.01C139.41,149.71 139.24,149.41 139.08,149.12C141.68,146.49 144.22,143.8 146.91,141.27C154.7,133.92 154.72,133.79 165.43,133.98C181.21,134.26 196.1,138.61 210.47,144.78C214.32,146.44 217.65,147.31 221.76,145.43C224.98,143.95 228.71,143.61 232.67,143.03C229.67,148.57 233.27,151.22 236.42,154.19C240.32,157.87 244.05,161.72 247.81,165.54C248.6,166.35 249.19,167.35 250.32,168.84C248.78,168.93 247.82,169.03 246.87,169.03C240.24,169.04 233.61,169.23 227,168.97C207.93,168.24 191.92,175.26 178.31,188.15C173.45,192.76 169.11,197.93 164.57,202.87C164.1,203.38 163.83,204.06 163.06,205.34C186.14,197.23 207.86,186.73 232.14,185.87C232.34,186.18 232.53,186.5 232.73,186.82C229.85,190.18 227.16,193.72 224.07,196.87C211.77,209.42 204.42,224.53 201.03,241.6C200.07,246.39 200.14,251.4 199.32,256.52C197.97,256.68 197.03,256.64 196.08,256.6ZM209.68,194.22C212.31,194.23 214.96,194.01 217.56,194.29C221.34,194.69 223.62,192.8 226.08,189.17C219.87,190.63 214.42,191.92 208.98,193.24C208.98,193.24 209.12,193.87 209.68,194.22Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M195.81,256.61C197.03,256.64 197.97,256.68 199.2,256.74C199.58,258.84 199.68,260.93 199.79,263.02C200.15,263.15 200.52,263.28 200.89,263.41C202.45,261.11 204.02,258.8 205.97,256.3C207.64,256.04 208.92,255.97 210.44,255.9C211.39,255.81 212.11,255.66 212.84,255.65C221.05,255.54 229.27,255.45 237.79,255.4C240.14,255.48 242.19,255.51 244.24,255.54C243.48,260.08 242.67,264.61 241.96,269.16C238.76,289.76 238.29,310.44 239.8,331.49C238.12,331.99 236.72,332.2 235.33,332.4C197.17,332.39 159.01,332.31 120.85,332.45C117.22,332.46 115.21,331.39 113.93,328.08C112.65,324.8 111,321.67 109.23,317.84C113.61,317.84 117.25,317.84 120.89,317.84C157.58,317.84 194.27,317.85 230.96,317.81C232.33,317.8 234.26,318.64 234.58,316.04C234.9,313.41 233.71,312.89 231.33,312.9C204.94,312.97 178.55,312.94 152.17,312.95C138.29,312.95 124.42,312.9 110.54,313.03C108.13,313.05 106.9,312.32 106.24,310.03C105.12,306.12 103.81,302.26 102.24,297.34C146.69,297.16 190.42,297.32 234.45,297.24C234.54,295.65 234.62,294.35 234.7,292.8C233.41,292.73 232.39,292.63 231.38,292.63C191.74,292.61 152.11,292.59 112.48,292.59C109.43,292.59 106.38,292.78 103.34,292.7C102.57,292.68 101.25,292.09 101.13,291.56C99.99,286.28 99.05,280.95 97.99,275.27C103.72,275.27 108.74,275.24 113.75,275.28C115.11,275.29 116.47,275.55 117.83,275.55C155.88,275.57 193.94,275.57 231.99,275.56C236.12,275.56 236.28,275.39 236.25,270.67C190.27,270.67 144.28,270.67 97.69,270.67C97.5,266.15 97.2,262.01 97.29,257.87C97.3,257.33 99.29,256.41 100.37,256.4C110.67,256.26 120.97,256.28 131.28,256.27C143.26,256.26 155.24,256.22 167.23,256.28C176.66,256.33 186.1,256.51 195.81,256.61Z"
|
||||
android:fillColor="#BF43F5"/>
|
||||
<path
|
||||
android:pathData="M235.35,332.69C236.72,332.2 238.12,331.99 239.8,331.78C241.62,343.91 242.64,356.16 244.79,368.19C247.54,383.59 251.45,398.75 257.46,413.28C257.66,413.75 257.8,414.25 257.96,414.73C240.7,421.84 173.22,399.36 158.05,381.11C172.31,381.11 186.11,381.11 199.9,381.11C213.75,381.11 227.59,381.11 241.47,381.11C241.67,377.83 240.89,376.4 237.49,376.42C210.37,376.55 183.25,376.46 156.13,376.54C153.11,376.55 150.66,375.97 148.51,373.71C143.83,368.81 138.99,364.07 134.22,359.27C134.38,358.97 134.53,358.67 134.69,358.38C168.96,358.38 203.24,358.38 237.91,358.38C237.59,356.49 237.35,355.09 237.06,353.42C235.66,353.42 234.41,353.42 233.17,353.42C204.05,353.42 174.93,353.43 145.82,353.41C141.61,353.41 137.4,353.12 133.21,353.29C130.69,353.4 129.04,352.57 127.7,350.52C124.9,346.24 122.01,342.03 118.68,337.09C158.03,337.09 196.51,337.09 235.37,337.09C235.37,335.43 235.37,334.21 235.35,332.69Z"
|
||||
android:fillColor="#985DED"/>
|
||||
<path
|
||||
android:pathData="M259.22,332.74C260.88,332.45 262.52,332.45 264.42,332.44C264.74,333.79 264.81,335.15 264.9,336.86C307.33,336.86 349.65,336.86 392.94,336.86C390.82,340.23 389.17,342.95 387.42,345.59C382.22,353.42 382.21,353.42 372.98,353.42C339.55,353.42 306.12,353.42 272.68,353.42C271.45,353.42 270.21,353.42 269.01,353.42C268.6,357.97 268.9,358.32 272.97,358.32C306.4,358.33 339.83,358.33 373.27,358.33C374.49,358.33 375.71,358.33 377.95,358.33C375.2,361.41 372.88,363.64 370.99,366.18C364.84,374.47 357,377.15 346.54,376.75C324.07,375.9 301.55,376.49 279.05,376.49C277.81,376.49 276.57,376.49 275.34,376.49C275.16,380.42 275.74,381.1 279.15,381.1C302.59,381.11 326.04,381.1 349.48,381.11C350.69,381.11 351.89,381.11 353.09,381.11C353.19,381.39 353.29,381.68 353.39,381.97C351.01,383.75 348.69,385.61 346.25,387.3C330.27,398.36 312.8,406.17 293.96,410.9C291.74,411.45 289.36,411.83 287.12,411.64C285.84,411.52 284.3,410.33 283.54,409.19C277.07,399.49 273.01,388.7 269.22,377.75C264.17,363.21 261.34,348.2 259.22,332.74Z"
|
||||
android:fillColor="#985DED"/>
|
||||
<path
|
||||
android:pathData="M326.78,255.15C324.58,255.42 322.37,255.43 319.85,255.39C321.63,245.65 321.41,236 319.06,226.36C315.84,213.14 308.31,203.06 296.96,195.73C296.52,195.45 296.09,195.16 295.7,194.28C307.61,194.28 319.52,194.28 331.43,194.28C331.48,193.93 331.53,193.59 331.57,193.25C326.8,191.44 322.07,189.34 316.8,189.29C306.29,189.2 295.77,189.33 285.27,189.21C283.16,189.19 281.07,188.5 279.01,187.57C291.5,182.55 304.29,183.1 317.13,185.13C330.14,187.18 341.45,193.94 354.27,199.15C353.23,197.19 352.63,196.05 351.78,194.42C353.03,194.32 353.99,194.18 354.94,194.18C369.55,194.16 384.16,194.21 398.77,194.12C400.99,194.11 402.21,194.7 402.93,196.91C404.51,201.76 406.34,206.52 408.3,211.96C378.96,211.96 350.45,211.96 321.79,211.96C321.89,215.04 323.22,215.9 325.8,215.89C345.56,215.82 365.32,215.82 385.08,215.87C392.43,215.89 399.77,216.08 407.11,216.34C408.02,216.37 409.48,217.14 409.67,217.85C410.73,221.77 411.47,225.78 412.41,230.2C383.7,230.2 355.5,230.2 327.33,230.2C326.95,234.48 327.15,234.7 330.87,234.7C357.35,234.74 383.84,234.81 410.32,234.8C412.27,234.8 413.19,235.26 413.31,237.37C413.52,240.82 413.96,244.26 414.41,247.69C414.69,249.87 413.98,250.71 411.68,250.63C407.17,250.48 402.65,250.56 398.13,250.56C375.85,250.56 353.57,250.56 331.29,250.56C327.03,250.56 327.03,250.57 326.78,255.15Z"
|
||||
android:fillColor="#FF43C4"/>
|
||||
<path
|
||||
android:pathData="M227.85,100.36C256.3,95.58 282.98,98.91 297.89,104.06C295.79,108.97 293.7,113.87 291.61,118.76C290.5,121.37 289.2,123.91 288.3,126.59C287.56,128.8 286.27,129.35 284.07,129.34C269.05,129.26 254.03,129.29 238.21,129.29C240.88,132.05 242.88,134.26 246.57,134.22C257.92,134.12 269.27,134.18 280.62,134.18C281.77,134.18 282.91,134.18 285.21,134.18C282.72,138.33 281.09,142.24 278.43,145.21C274.25,149.87 269.29,153.82 264.85,158.26C263.14,159.97 262.34,159.9 260.7,158.1C253.98,150.75 247.23,143.42 240.16,136.4C232.38,128.67 222.49,125.46 211.78,124.23C202.25,123.13 193.21,124.88 184.27,127.97C182.14,128.71 179.81,129.15 177.55,129.24C172.62,129.42 167.68,129.29 162.59,128.89C181.97,114.22 203.7,104.88 227.85,100.36ZM264.4,152.93C265.08,152.12 265.76,151.3 266.85,150.01C264.04,150.01 261.99,150.01 259.33,150.01C260.37,151.48 260.92,152.63 261.81,153.37C262.25,153.73 263.32,153.32 264.4,152.93Z"
|
||||
android:fillColor="#FF42C3"/>
|
||||
<path
|
||||
android:pathData="M338.22,180.32C330,173.61 320.35,171.22 310.4,169.77C298.87,168.1 287.56,168.74 276.35,173.62C276.99,172.43 277.52,171.16 278.31,170.08C288.95,155.53 302.06,143.72 317.99,135.27C320.4,133.99 323.68,134.06 326.56,134.01C335.71,133.86 344.86,133.9 354,134.01C355.37,134.02 357.07,134.45 358.04,135.32C362.81,139.6 367.39,144.09 372.01,148.53C372.28,148.78 372.38,149.19 372.81,150.01C371.31,150.01 370.2,150.01 369.09,150.01C348.07,150.01 327.04,150.08 306.02,149.93C302.48,149.9 300.52,151.7 297.87,154.67C299.78,154.67 300.97,154.67 302.15,154.67C326.02,154.67 349.88,154.71 373.74,154.6C376.46,154.59 378.32,155.41 379.86,157.57C382.39,161.15 385.05,164.63 388.18,168.84C367.16,168.84 347,168.84 326.84,168.84C326.8,169.1 326.77,169.35 326.73,169.61C331.21,171.27 335.15,173.92 340.45,173.81C356.21,173.5 371.98,173.75 387.75,173.65C390.3,173.63 391.87,174.45 393.01,176.69C395.03,180.7 397.23,184.61 399.61,189.04C398.04,189.14 397.06,189.26 396.07,189.26C380.83,189.27 365.59,189.23 350.34,189.31C348.17,189.32 346.58,188.76 345.1,187.13C343,184.79 340.64,182.68 338.22,180.32Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M263.75,255.47C262.21,255.48 260.68,255.49 258.86,255.49C260.26,237.4 261.8,219.3 267.17,201.8C267.96,199.21 268.84,198.07 271.41,199.91C273.97,201.74 276.92,203.14 279.13,205.31C294.14,220.1 308.29,235.58 317.44,255.16C314.98,255.4 312.44,255.44 309.6,255.22C308.77,252.11 307.31,250.9 304.14,250.95C292.11,251.14 280.07,251.03 268.03,251.04C266.7,251.04 265.38,251.04 263.75,251.04C263.75,252.81 263.75,254.14 263.75,255.47ZM265.23,234.42C275.39,234.42 285.55,234.42 295.71,234.42C295.83,234.04 295.95,233.66 296.07,233.28C294.82,232.66 293.59,231.54 292.34,231.52C283.86,231.36 275.38,231.45 266.9,231.42C264.98,231.41 264.11,232.09 265.23,234.42ZM272.28,213.13C270.05,212.98 268.06,213.13 268.44,216.24C272.26,216.24 276,216.24 279.86,216.24C278.35,212.79 275.57,213.04 272.28,213.13Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M244.37,255.35C242.19,255.51 240.14,255.48 237.87,255.14C237.72,253.74 237.8,252.65 237.92,251.1C230.12,251.1 222.74,251.09 215.35,251.1C211.33,251.1 210.79,251.62 210.2,255.9C208.92,255.97 207.64,256.04 206.08,256.11C209.18,249.49 212.07,242.57 216.01,236.32C223.02,225.18 230.96,214.72 241.59,206.63C246.5,202.9 251.86,200.11 257.81,198.49C258.58,198.28 259.39,198.25 261.09,198.02C254,217.03 248.24,235.81 244.37,255.35ZM226.88,230.34C223.86,230.04 223.17,232.3 221.91,234.84C228.63,234.84 234.89,234.75 241.15,234.88C244,234.94 244.78,233.68 244.4,230.4C238.69,230.4 233.05,230.4 226.88,230.34ZM249.9,214.76C249.15,214.31 248.42,213.55 247.63,213.47C245.66,213.28 243.65,213.49 241.66,213.37C239.1,213.21 237.69,214.56 236.27,217.12C240.49,217.12 244.21,217.2 247.93,217.03C248.58,217 249.17,215.9 249.9,214.76Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M315.52,109.68C327.71,114.67 338.96,120.8 349.32,129.08C328.46,127.7 308.6,131.4 289.08,139.83C289.67,136.65 290.71,134.68 293.44,133.84C297.43,132.61 301.38,131.27 305.32,129.43C301.9,129.43 298.47,129.43 294.12,129.43C298.11,121.6 301.71,114.44 305.48,107.37C305.75,106.86 307.39,106.71 308.22,106.96C310.62,107.69 312.94,108.7 315.52,109.68Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
</vector>
|
||||
39
src/android/app/src/main/res/drawable/ic_yuzu_full.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:pathData="M346.71,255.31C347.5,255.31 348.29,255.32 349.83,255.37C353.84,255.39 357.09,255.38 360.33,255.37C393.73,255.39 427.12,255.4 460.51,255.45C468.68,255.46 476.86,255.73 485.03,255.65C488.12,255.63 489.63,256.4 489.39,259.81C489.1,263.97 489.08,268.15 489.13,272.32C489.18,276.29 487.88,277.86 483.45,277.85C434.6,277.69 385.75,277.78 336.9,277.79C314.79,277.8 292.68,277.79 270.58,277.79C268.9,277.79 267.22,277.79 265.15,277.79C265.15,280.2 265.15,282.26 265.15,284.91C339.05,284.91 412.99,284.91 488.16,284.91C486.61,293.16 485.26,300.67 483.68,308.12C483.51,308.93 482.02,309.74 481,309.99C479.69,310.32 478.23,310.09 476.84,310.09C408.2,310.09 339.56,310.09 270.92,310.09C269.1,310.09 267.27,310.09 265.1,310.09C265.1,312.41 265.1,314.32 265.1,316.79C336.97,316.79 408.77,316.79 481.77,316.79C480.16,322.22 478.88,326.87 477.4,331.45C474.64,339.99 474.59,339.98 465.83,339.98C400.9,339.98 335.98,339.99 271.05,339.94C267.64,339.93 264.86,340.19 266.29,344.59C266.64,345.66 268.16,346.61 269.36,347.08C270.43,347.5 271.8,347.17 273.04,347.17C336.88,347.17 400.73,347.17 464.58,347.17C466.54,347.17 468.5,347.17 471.75,347.17C468.21,354.46 465.35,360.89 461.91,367C461.26,368.16 458.35,368.45 456.48,368.46C428.5,368.54 400.52,368.52 372.54,368.52C340.85,368.52 309.15,368.52 277.46,368.51C274.85,368.51 272.24,368.39 268.93,368.49C265.82,368.66 263.41,368.67 261.01,368.67C260.16,361.64 258.72,354.62 258.57,347.57C258.02,321.95 257.79,296.33 257.72,270.71C257.71,265.83 258.95,260.94 260.24,255.78C263.12,255.48 265.37,255.47 268.05,255.52C290.54,255.54 312.61,255.5 335.09,255.46C339.23,255.41 342.97,255.36 346.71,255.31Z"
|
||||
android:fillColor="#BF42F6"/>
|
||||
<path
|
||||
android:pathData="M168.12,257.11C168.12,254.22 168.12,251.34 168.12,247.81C165.72,247.81 163.45,247.81 161.19,247.81C116.67,247.84 72.15,247.82 27.63,247.96C23.77,247.97 22.27,246.93 22.88,242.96C23.56,238.54 24.13,234.07 24.27,229.61C24.38,226.23 25.88,225.61 28.88,225.6C74.78,225.49 120.68,225.26 166.58,225.15C170.45,225.14 173.43,224.81 172.62,219.32C123.81,219.32 75.02,219.32 25.55,219.32C26.94,212.72 27.9,206.64 29.68,200.81C30.1,199.46 33.31,198.19 35.24,198.19C82.7,198.1 130.16,198.18 177.61,198.3C180.89,198.3 183.47,197.92 183.75,193.25C133.22,193.25 82.8,193.25 31.15,193.25C34.58,183.94 37.49,175.54 40.89,167.34C41.37,166.17 44.15,165.4 45.87,165.39C70.46,165.26 95.04,165.18 119.62,165.4C124.9,165.45 127.69,162.82 130.75,158.15C101.83,158.15 73.69,158.15 44.3,158.15C48.65,150.32 52.3,143.4 56.37,136.74C57.04,135.65 59.57,135.27 61.24,135.26C91.85,135.18 122.47,134.97 153.07,135.38C160.26,135.47 165.79,132.76 171.4,128.6C135.25,128.6 99.1,128.6 61.39,128.6C63.98,124.8 65.74,122.06 67.65,119.43C76.53,107.21 76.55,107.22 91.72,107.22C132.38,107.21 173.04,107.21 213.7,107.21C215.33,107.21 216.96,107.21 219.59,107.21C215.62,103.09 212.58,100.25 207.27,100.27C168.31,100.44 129.35,100.36 90.39,100.36C88.6,100.36 86.82,100.36 85.03,100.36C84.78,99.92 84.53,99.49 84.29,99.05C88.12,95.2 91.85,91.24 95.8,87.51C107.26,76.71 107.3,76.52 123.05,76.8C146.26,77.21 168.15,83.6 189.28,92.68C194.94,95.11 199.84,96.4 205.89,93.63C210.61,91.46 216.1,90.96 221.92,90.11C217.51,98.24 222.81,102.15 227.44,106.52C233.17,111.93 238.66,117.59 244.19,123.21C245.36,124.39 246.23,125.87 247.88,128.06C245.62,128.19 244.21,128.34 242.8,128.34C233.06,128.35 223.31,128.63 213.59,128.25C185.54,127.18 161.99,137.5 141.99,156.46C134.83,163.24 128.46,170.84 121.77,178.11C121.09,178.85 120.69,179.85 119.56,181.74C153.5,169.81 185.44,154.36 221.15,153.1C221.44,153.57 221.73,154.03 222.01,154.5C217.78,159.44 213.82,164.64 209.28,169.28C191.19,187.74 180.38,209.95 175.39,235.05C173.99,242.11 174.08,249.46 172.89,256.99C170.89,257.24 169.51,257.18 168.12,257.11ZM188.12,165.39C191.98,165.39 195.88,165.07 199.7,165.48C205.27,166.07 208.62,163.3 212.23,157.96C203.1,160.11 195.09,162 187.09,163.95C187.08,163.95 187.3,164.87 188.12,165.39Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M167.72,257.14C169.51,257.18 170.89,257.24 172.7,257.32C173.27,260.41 173.42,263.49 173.57,266.56C174.11,266.75 174.65,266.95 175.19,267.14C177.49,263.75 179.79,260.35 182.66,256.68C185.11,256.3 187,256.2 189.23,256.08C190.64,255.95 191.7,255.74 192.76,255.73C204.84,255.56 216.92,255.43 229.45,255.36C232.92,255.47 235.93,255.51 238.94,255.56C237.82,262.23 236.63,268.9 235.59,275.59C230.88,305.88 230.19,336.29 232.4,367.24C229.94,367.99 227.89,368.29 225.84,368.59C169.72,368.57 113.6,368.46 57.49,368.65C52.15,368.67 49.19,367.1 47.3,362.24C45.43,357.42 43.01,352.8 40.4,347.17C46.84,347.17 52.19,347.17 57.54,347.17C111.5,347.17 165.45,347.18 219.4,347.13C221.43,347.13 224.27,348.35 224.73,344.53C225.2,340.67 223.46,339.9 219.95,339.91C181.15,340.02 142.34,339.97 103.54,339.98C83.14,339.98 62.73,339.92 42.33,340.11C38.78,340.14 36.96,339.06 36,335.69C34.36,329.94 32.42,324.27 30.12,317.03C95.49,316.77 159.8,317 224.55,316.88C224.68,314.54 224.79,312.64 224.92,310.35C223.02,310.25 221.52,310.11 220.02,310.11C161.74,310.07 103.46,310.05 45.17,310.05C40.69,310.05 36.21,310.32 31.74,310.21C30.6,310.18 28.66,309.31 28.49,308.53C26.81,300.76 25.43,292.93 23.86,284.57C32.3,284.57 39.67,284.54 47.05,284.59C49.04,284.6 51.04,284.99 53.04,284.99C109,285.01 164.97,285.01 220.93,285C227.01,285 227.24,284.75 227.2,277.81C159.57,277.81 91.94,277.81 23.43,277.81C23.15,271.16 22.71,265.07 22.84,258.98C22.86,258.2 25.78,256.84 27.37,256.82C42.52,256.62 57.67,256.64 72.82,256.63C90.44,256.61 108.07,256.55 125.69,256.64C139.57,256.71 153.44,256.98 167.72,257.14Z"
|
||||
android:fillColor="#BF43F5"/>
|
||||
<path
|
||||
android:pathData="M225.87,369.02C227.89,368.29 229.94,367.99 232.42,367.67C235.09,385.52 236.59,403.52 239.75,421.23C243.8,443.87 249.54,466.17 258.38,487.52C258.67,488.22 258.88,488.96 259.13,489.67C233.74,500.12 134.5,467.06 112.2,440.21C133.17,440.21 153.45,440.21 173.74,440.21C194.1,440.21 214.46,440.21 234.87,440.21C235.17,435.4 234.01,433.3 229.01,433.32C189.13,433.52 149.25,433.38 109.36,433.5C104.92,433.52 101.33,432.66 98.16,429.33C91.29,422.13 84.17,415.17 77.15,408.11C77.38,407.67 77.61,407.23 77.84,406.79C128.24,406.79 178.65,406.79 229.63,406.79C229.16,404.01 228.81,401.96 228.39,399.5C226.32,399.5 224.49,399.5 222.66,399.5C179.84,399.5 137.02,399.51 94.2,399.48C88.02,399.48 81.83,399.06 75.66,399.31C71.96,399.47 69.52,398.25 67.56,395.24C63.45,388.95 59.19,382.75 54.3,375.48C112.17,375.48 168.75,375.48 225.9,375.48C225.9,373.05 225.9,371.25 225.87,369.02Z"
|
||||
android:fillColor="#985DED"/>
|
||||
<path
|
||||
android:pathData="M260.97,369.08C263.41,368.67 265.82,368.66 268.61,368.65C269.1,370.64 269.19,372.63 269.32,375.15C331.73,375.15 393.96,375.15 457.62,375.15C454.49,380.1 452.08,384.1 449.5,387.99C441.86,399.51 441.83,399.49 428.27,399.49C379.1,399.5 329.94,399.5 280.77,399.5C278.95,399.5 277.13,399.5 275.36,399.5C274.76,406.19 275.21,406.71 281.19,406.71C330.36,406.72 379.52,406.71 428.69,406.72C430.48,406.72 432.28,406.72 435.58,406.72C431.53,411.25 428.13,414.53 425.35,418.27C416.3,430.46 404.77,434.4 389.38,433.81C356.34,432.56 323.22,433.43 290.13,433.43C288.31,433.43 286.48,433.43 284.68,433.43C284.41,439.2 285.27,440.2 290.28,440.21C324.76,440.22 359.23,440.21 393.71,440.21C395.48,440.21 397.25,440.21 399.01,440.21C399.16,440.64 399.31,441.06 399.46,441.49C395.96,444.11 392.55,446.85 388.96,449.33C365.45,465.59 339.77,477.07 312.06,484.02C308.8,484.84 305.3,485.4 301.99,485.11C300.12,484.95 297.85,483.2 296.73,481.51C287.23,467.26 281.26,451.38 275.67,435.29C268.26,413.89 264.08,391.82 260.97,369.08Z"
|
||||
android:fillColor="#985DED"/>
|
||||
<path
|
||||
android:pathData="M360.32,254.99C357.09,255.38 353.84,255.39 350.13,255.34C352.74,241.02 352.42,226.82 348.97,212.64C344.23,193.2 333.16,178.39 316.47,167.6C315.83,167.19 315.2,166.76 314.61,165.46C332.13,165.46 349.64,165.46 367.16,165.46C367.23,164.96 367.3,164.46 367.37,163.96C360.35,161.29 353.4,158.21 345.64,158.14C330.19,158 314.73,158.19 299.27,158.02C296.18,157.99 293.1,156.98 290.07,155.61C308.44,148.22 327.24,149.03 346.13,152.01C365.26,155.03 381.9,164.97 400.74,172.63C399.23,169.75 398.34,168.07 397.08,165.68C398.93,165.53 400.33,165.32 401.74,165.32C423.23,165.3 444.71,165.37 466.2,165.24C469.46,165.22 471.26,166.09 472.32,169.34C474.64,176.47 477.33,183.47 480.21,191.47C437.06,191.47 395.13,191.47 352.99,191.47C353.13,196.01 355.09,197.26 358.88,197.25C387.94,197.15 417,197.14 446.06,197.22C456.86,197.26 467.67,197.53 478.46,197.91C479.79,197.96 481.95,199.1 482.22,200.13C483.77,205.9 484.86,211.8 486.24,218.3C444.03,218.3 402.55,218.3 361.13,218.3C360.58,224.59 360.87,224.91 366.33,224.91C405.28,224.98 444.23,225.07 483.18,225.06C486.04,225.06 487.39,225.73 487.58,228.83C487.88,233.91 488.53,238.97 489.19,244.02C489.6,247.21 488.55,248.46 485.18,248.34C478.55,248.12 471.9,248.24 465.26,248.24C432.49,248.23 399.72,248.24 366.95,248.24C360.69,248.24 360.69,248.25 360.32,254.99Z"
|
||||
android:fillColor="#FF43C4"/>
|
||||
<path
|
||||
android:pathData="M214.84,27.35C256.67,20.33 295.91,25.23 317.84,32.8C314.75,40.02 311.68,47.22 308.61,54.41C306.97,58.24 305.06,61.99 303.74,65.93C302.65,69.18 300.74,69.98 297.52,69.97C275.43,69.85 253.34,69.9 230.07,69.9C234.01,73.95 236.94,77.2 242.37,77.15C259.06,76.99 275.76,77.09 292.45,77.1C294.13,77.1 295.81,77.1 299.19,77.1C295.53,83.19 293.14,88.94 289.22,93.31C283.08,100.17 275.78,105.97 269.24,112.5C266.73,115.01 265.56,114.91 263.14,112.27C253.27,101.45 243.33,90.67 232.95,80.36C221.49,68.98 206.96,64.27 191.21,62.45C177.19,60.83 163.89,63.42 150.75,67.96C147.61,69.04 144.19,69.69 140.87,69.82C133.62,70.09 126.36,69.9 118.87,69.31C147.37,47.73 179.32,34 214.84,27.35ZM268.58,104.66C269.59,103.46 270.59,102.27 272.19,100.38C268.06,100.38 265.05,100.38 261.13,100.38C262.66,102.53 263.47,104.22 264.79,105.31C265.43,105.84 267,105.24 268.58,104.66Z"
|
||||
android:fillColor="#FF42C3"/>
|
||||
<path
|
||||
android:pathData="M377.15,144.94C365.06,135.07 350.86,131.56 336.23,129.43C319.28,126.96 302.65,127.92 286.16,135.08C287.11,133.34 287.88,131.47 289.04,129.88C304.69,108.48 323.97,91.12 347.4,78.68C350.95,76.8 355.77,76.91 360.01,76.84C373.45,76.62 386.9,76.67 400.35,76.83C402.37,76.86 404.87,77.49 406.3,78.77C413.31,85.06 420.04,91.67 426.84,98.19C427.23,98.56 427.39,99.17 428.02,100.36C425.81,100.36 424.18,100.36 422.55,100.36C391.63,100.36 360.71,100.48 329.8,100.25C324.58,100.21 321.7,102.85 317.82,107.22C320.62,107.22 322.36,107.22 324.11,107.22C359.2,107.21 394.29,107.28 429.39,107.13C433.39,107.11 436.12,108.31 438.38,111.49C442.11,116.74 446.02,121.86 450.61,128.06C419.7,128.06 390.05,128.06 360.41,128.06C360.36,128.43 360.3,128.81 360.25,129.19C366.83,131.63 372.64,135.53 380.43,135.37C403.61,134.91 426.8,135.29 449.99,135.13C453.74,135.1 456.04,136.3 457.71,139.61C460.69,145.49 463.92,151.25 467.42,157.76C465.12,157.91 463.67,158.09 462.22,158.09C439.81,158.1 417.39,158.05 394.98,158.16C391.79,158.18 389.44,157.36 387.27,154.95C384.18,151.51 380.71,148.41 377.15,144.94Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M267.63,255.46C265.37,255.47 263.12,255.48 260.45,255.48C262.5,228.88 264.77,202.27 272.66,176.53C273.83,172.72 275.12,171.05 278.9,173.76C282.66,176.45 287.01,178.5 290.25,181.69C312.32,203.43 333.13,226.21 346.6,255C342.97,255.36 339.23,255.41 335.05,255.09C333.83,250.51 331.69,248.73 327.03,248.81C309.33,249.09 291.63,248.93 273.93,248.93C271.98,248.93 270.02,248.93 267.63,248.93C267.63,251.54 267.63,253.5 267.63,255.46ZM269.8,224.5C284.75,224.5 299.69,224.5 314.64,224.5C314.81,223.95 314.98,223.38 315.15,222.83C313.33,221.92 311.52,220.26 309.68,220.23C297.21,219.99 284.74,220.13 272.27,220.09C269.44,220.08 268.16,221.08 269.8,224.5ZM280.18,193.19C276.89,192.97 273.97,193.18 274.53,197.77C280.15,197.77 285.65,197.77 291.32,197.77C289.11,192.69 285.02,193.05 280.18,193.19Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M239.14,255.27C235.93,255.51 232.92,255.47 229.57,254.97C229.36,252.92 229.48,251.32 229.65,249.02C218.18,249.02 207.32,249.02 196.46,249.03C190.55,249.03 189.75,249.79 188.89,256.1C187,256.2 185.11,256.3 182.83,256.4C187.38,246.66 191.64,236.49 197.43,227.29C207.73,210.92 219.41,195.53 235.05,183.63C242.26,178.14 250.14,174.04 258.89,171.65C260.02,171.35 261.23,171.31 263.73,170.96C253.3,198.93 244.82,226.55 239.14,255.27ZM213.42,218.5C208.98,218.06 207.96,221.38 206.1,225.12C215.98,225.12 225.19,224.99 234.4,225.18C238.59,225.27 239.74,223.41 239.18,218.58C230.78,218.58 222.49,218.58 213.42,218.5ZM247.27,195.58C246.16,194.92 245.09,193.8 243.93,193.69C241.03,193.41 238.07,193.72 235.15,193.54C231.39,193.32 229.31,195.29 227.22,199.05C233.42,199.05 238.9,199.18 244.37,198.93C245.32,198.89 246.2,197.27 247.27,195.58Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M343.76,41.06C361.68,48.39 378.24,57.41 393.47,69.59C362.8,67.56 333.58,72.99 304.89,85.39C305.74,80.72 307.29,77.83 311.3,76.58C317.16,74.77 322.97,72.82 328.77,70.1C323.73,70.1 318.69,70.1 312.29,70.1C318.16,58.59 323.45,48.06 329,37.66C329.4,36.92 331.81,36.69 333.03,37.06C336.57,38.13 339.97,39.62 343.76,41.06Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
</vector>
|
||||
39
src/android/app/src/main/res/drawable/ic_yuzu_icon.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:pathData="M304.46,255.4C304.89,255.4 305.31,255.4 306.14,255.43C308.29,255.44 310.04,255.44 311.78,255.43C329.71,255.44 347.63,255.44 365.56,255.47C369.95,255.48 374.33,255.62 378.72,255.58C380.38,255.57 381.19,255.99 381.06,257.81C380.9,260.05 380.89,262.29 380.92,264.53C380.95,266.66 380.25,267.51 377.87,267.5C351.65,267.41 325.42,267.46 299.2,267.47C287.33,267.47 275.46,267.47 263.59,267.47C262.69,267.47 261.79,267.47 260.68,267.47C260.68,268.76 260.68,269.87 260.68,271.29C300.35,271.29 340.05,271.29 380.4,271.29C379.57,275.72 378.85,279.75 378,283.75C377.91,284.18 377.1,284.62 376.56,284.76C375.85,284.93 375.07,284.81 374.33,284.81C337.48,284.81 300.63,284.81 263.78,284.81C262.8,284.81 261.82,284.81 260.65,284.81C260.65,286.05 260.65,287.08 260.65,288.4C299.24,288.4 337.78,288.4 376.97,288.4C376.11,291.32 375.42,293.81 374.62,296.27C373.14,300.86 373.12,300.85 368.42,300.85C333.56,300.85 298.7,300.86 263.85,300.83C262.02,300.83 260.52,300.96 261.29,303.33C261.48,303.9 262.3,304.41 262.94,304.66C263.52,304.89 264.25,304.71 264.91,304.71C299.19,304.71 333.46,304.71 367.74,304.71C368.79,304.71 369.85,304.71 371.59,304.71C369.69,308.63 368.15,312.08 366.31,315.36C365.96,315.98 364.4,316.14 363.39,316.14C348.37,316.19 333.35,316.17 318.33,316.17C301.32,316.17 284.3,316.17 267.29,316.17C265.89,316.17 264.49,316.1 262.71,316.16C261.04,316.25 259.75,316.25 258.46,316.25C258,312.48 257.23,308.71 257.15,304.93C256.85,291.18 256.73,277.42 256.69,263.66C256.69,261.04 257.35,258.42 258.05,255.65C259.59,255.49 260.8,255.48 262.24,255.51C274.31,255.52 286.16,255.5 298.23,255.48C300.45,255.45 302.46,255.42 304.46,255.4Z"
|
||||
android:fillColor="#BF42F6"/>
|
||||
<path
|
||||
android:pathData="M208.59,256.37C208.59,254.82 208.59,253.26 208.59,251.37C207.3,251.37 206.09,251.37 204.87,251.37C180.97,251.39 157.07,251.38 133.17,251.45C131.1,251.46 130.29,250.9 130.62,248.77C130.99,246.4 131.29,244 131.37,241.6C131.43,239.79 132.23,239.45 133.84,239.45C158.48,239.39 183.12,239.26 207.77,239.21C209.84,239.2 211.44,239.02 211,236.08C184.8,236.08 158.61,236.08 132.05,236.08C132.8,232.53 133.32,229.27 134.27,226.14C134.49,225.41 136.22,224.73 137.26,224.73C162.73,224.69 188.21,224.73 213.69,224.79C215.45,224.79 216.83,224.59 216.98,222.08C189.85,222.08 162.79,222.08 135.06,222.08C136.9,217.08 138.46,212.57 140.29,208.17C140.55,207.54 142.04,207.13 142.96,207.12C156.16,207.06 169.36,207.01 182.55,207.13C185.39,207.16 186.89,205.74 188.53,203.24C173.01,203.24 157.9,203.24 142.12,203.24C144.45,199.04 146.41,195.32 148.6,191.75C148.96,191.16 150.32,190.96 151.21,190.95C167.65,190.91 184.08,190.8 200.51,191.01C204.37,191.06 207.34,189.61 210.35,187.38C190.94,187.38 171.54,187.38 151.29,187.38C152.68,185.33 153.63,183.86 154.66,182.45C159.42,175.89 159.43,175.9 167.58,175.9C189.4,175.89 211.23,175.89 233.06,175.89C233.94,175.89 234.81,175.89 236.22,175.89C234.09,173.68 232.46,172.15 229.61,172.17C208.69,172.26 187.78,172.21 166.86,172.21C165.9,172.21 164.94,172.21 163.98,172.21C163.85,171.98 163.72,171.75 163.59,171.51C165.64,169.44 167.65,167.32 169.77,165.32C175.92,159.52 175.94,159.41 184.39,159.57C196.85,159.79 208.61,163.22 219.95,168.09C222.99,169.4 225.62,170.09 228.87,168.6C231.4,167.44 234.35,167.17 237.47,166.71C235.1,171.08 237.95,173.17 240.44,175.52C243.51,178.42 246.46,181.46 249.43,184.48C250.05,185.12 250.52,185.91 251.41,187.09C250.2,187.15 249.44,187.23 248.68,187.24C243.45,187.24 238.22,187.39 233,187.19C217.94,186.61 205.3,192.15 194.56,202.33C190.72,205.97 187.3,210.05 183.71,213.95C183.34,214.35 183.13,214.89 182.52,215.9C200.74,209.5 217.89,201.2 237.06,200.53C237.21,200.78 237.37,201.03 237.52,201.28C235.25,203.93 233.13,206.73 230.69,209.21C220.98,219.12 215.17,231.05 212.49,244.52C211.74,248.31 211.79,252.26 211.15,256.3C210.08,256.43 209.34,256.4 208.59,256.37ZM219.33,207.12C221.4,207.13 223.49,206.96 225.55,207.17C228.53,207.49 230.34,206 232.27,203.13C227.37,204.29 223.07,205.3 218.77,206.35C218.77,206.35 218.89,206.85 219.33,207.12Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M208.38,256.38C209.34,256.4 210.08,256.43 211.05,256.48C211.35,258.14 211.43,259.79 211.51,261.44C211.81,261.54 212.1,261.64 212.39,261.75C213.62,259.93 214.86,258.11 216.4,256.13C217.71,255.93 218.73,255.87 219.92,255.81C220.68,255.74 221.25,255.63 221.82,255.62C228.3,255.53 234.79,255.46 241.52,255.42C243.38,255.48 244.99,255.51 246.61,255.53C246.01,259.12 245.37,262.7 244.81,266.29C242.28,282.55 241.91,298.87 243.1,315.49C241.78,315.89 240.68,316.05 239.58,316.21C209.45,316.2 179.32,316.14 149.2,316.25C146.34,316.26 144.74,315.41 143.73,312.8C142.73,310.21 141.43,307.74 140.02,304.71C143.48,304.71 146.35,304.71 149.23,304.71C178.19,304.71 207.16,304.72 236.12,304.69C237.21,304.69 238.73,305.34 238.98,303.29C239.23,301.22 238.3,300.81 236.42,300.82C215.59,300.87 194.75,300.85 173.92,300.85C162.97,300.85 152.01,300.82 141.06,300.92C139.16,300.94 138.18,300.36 137.66,298.55C136.78,295.46 135.74,292.42 134.51,288.53C169.6,288.39 204.12,288.52 238.88,288.45C238.95,287.2 239.01,286.17 239.08,284.95C238.06,284.89 237.26,284.82 236.45,284.81C205.16,284.8 173.88,284.78 142.59,284.78C140.18,284.78 137.78,284.93 135.37,284.87C134.76,284.85 133.72,284.39 133.63,283.97C132.73,279.8 131.99,275.6 131.15,271.1C135.68,271.1 139.63,271.09 143.59,271.11C144.67,271.12 145.74,271.33 146.81,271.33C176.85,271.34 206.9,271.34 236.94,271.34C240.2,271.34 240.33,271.2 240.3,267.48C204,267.48 167.69,267.48 130.92,267.48C130.76,263.91 130.53,260.64 130.6,257.37C130.61,256.95 132.18,256.22 133.03,256.21C141.16,256.1 149.3,256.11 157.43,256.11C166.89,256.1 176.35,256.07 185.81,256.11C193.26,256.15 200.71,256.3 208.38,256.38Z"
|
||||
android:fillColor="#BF43F5"/>
|
||||
<path
|
||||
android:pathData="M239.59,316.44C240.68,316.05 241.78,315.89 243.11,315.72C244.54,325.3 245.35,334.96 247.05,344.47C249.22,356.62 252.3,368.59 257.05,380.06C257.2,380.44 257.32,380.83 257.45,381.21C243.82,386.82 190.54,369.07 178.57,354.66C189.83,354.66 200.72,354.66 211.61,354.66C222.54,354.66 233.47,354.66 244.42,354.66C244.59,352.08 243.97,350.95 241.28,350.96C219.87,351.07 198.46,350.99 177.05,351.06C174.66,351.07 172.73,350.61 171.03,348.82C167.34,344.95 163.52,341.22 159.75,337.42C159.88,337.19 160,336.95 160.12,336.72C187.18,336.72 214.24,336.72 241.61,336.72C241.36,335.23 241.17,334.13 240.95,332.8C239.83,332.8 238.85,332.8 237.87,332.8C214.88,332.8 191.9,332.81 168.91,332.8C165.59,332.8 162.26,332.57 158.96,332.7C156.97,332.79 155.66,332.13 154.6,330.52C152.4,327.14 150.11,323.81 147.49,319.91C178.55,319.91 208.93,319.91 239.61,319.91C239.61,318.61 239.61,317.64 239.59,316.44Z"
|
||||
android:fillColor="#985DED"/>
|
||||
<path
|
||||
android:pathData="M258.44,316.48C259.75,316.25 261.04,316.25 262.54,316.24C262.8,317.31 262.85,318.38 262.92,319.73C296.42,319.73 329.83,319.73 364.01,319.73C362.33,322.39 361.03,324.54 359.64,326.62C355.54,332.81 355.53,332.8 348.25,332.8C321.86,332.8 295.46,332.8 269.07,332.8C268.09,332.8 267.11,332.8 266.16,332.8C265.84,336.39 266.08,336.68 269.29,336.68C295.68,336.68 322.08,336.68 348.47,336.68C349.44,336.68 350.4,336.68 352.17,336.68C350,339.11 348.17,340.87 346.68,342.88C341.82,349.43 335.64,351.54 327.37,351.23C309.64,350.55 291.86,351.02 274.09,351.02C273.11,351.02 272.13,351.02 271.17,351.02C271.02,354.12 271.48,354.66 274.17,354.66C292.68,354.66 311.19,354.66 329.7,354.66C330.65,354.66 331.6,354.66 332.54,354.66C332.62,354.89 332.7,355.12 332.78,355.35C330.9,356.75 329.07,358.22 327.14,359.56C314.53,368.29 300.74,374.45 285.87,378.18C284.11,378.62 282.23,378.92 280.46,378.76C279.45,378.68 278.23,377.74 277.63,376.83C272.53,369.18 269.33,360.66 266.33,352.02C262.35,340.53 260.11,328.68 258.44,316.48Z"
|
||||
android:fillColor="#985DED"/>
|
||||
<path
|
||||
android:pathData="M311.77,255.22C310.04,255.44 308.29,255.44 306.3,255.42C307.7,247.72 307.53,240.1 305.68,232.49C303.13,222.06 297.19,214.1 288.23,208.31C287.89,208.09 287.55,207.86 287.23,207.16C296.64,207.16 306.04,207.16 315.44,207.16C315.48,206.9 315.52,206.63 315.56,206.36C311.79,204.93 308.05,203.27 303.89,203.23C295.6,203.16 287.3,203.26 279,203.17C277.34,203.15 275.68,202.61 274.06,201.88C283.92,197.91 294.01,198.34 304.15,199.94C314.42,201.56 323.36,206.9 333.47,211.01C332.66,209.46 332.18,208.57 331.51,207.28C332.5,207.2 333.25,207.09 334.01,207.09C345.54,207.08 357.08,207.11 368.61,207.04C370.36,207.03 371.33,207.5 371.9,209.25C373.14,213.07 374.59,216.83 376.13,221.13C352.97,221.13 330.46,221.13 307.83,221.13C307.91,223.56 308.96,224.24 311,224.23C326.6,224.18 342.2,224.17 357.8,224.21C363.6,224.23 369.4,224.38 375.2,224.59C375.91,224.61 377.07,225.22 377.21,225.77C378.05,228.87 378.63,232.04 379.37,235.53C356.71,235.53 334.45,235.53 312.2,235.53C311.91,238.9 312.07,239.08 315,239.08C335.91,239.11 356.82,239.16 377.73,239.16C379.27,239.16 379.99,239.52 380.09,241.18C380.25,243.91 380.6,246.63 380.95,249.34C381.17,251.05 380.61,251.72 378.8,251.66C375.24,251.54 371.67,251.6 368.11,251.6C350.51,251.6 332.92,251.6 315.33,251.6C311.97,251.6 311.97,251.61 311.77,255.22Z"
|
||||
android:fillColor="#FF43C4"/>
|
||||
<path
|
||||
android:pathData="M233.67,133.02C256.13,129.25 277.19,131.88 288.96,135.95C287.31,139.82 285.66,143.69 284.01,147.55C283.13,149.6 282.1,151.61 281.39,153.73C280.81,155.48 279.79,155.91 278.06,155.9C266.2,155.83 254.34,155.87 241.85,155.87C243.96,158.04 245.54,159.78 248.45,159.75C257.41,159.67 266.37,159.72 275.33,159.73C276.24,159.73 277.14,159.73 278.95,159.73C276.99,163 275.7,166.08 273.6,168.43C270.3,172.11 266.39,175.23 262.88,178.73C261.53,180.08 260.9,180.03 259.6,178.61C254.3,172.8 248.97,167.01 243.39,161.48C237.24,155.37 229.44,152.84 220.98,151.86C213.46,150.99 206.32,152.38 199.27,154.82C197.58,155.4 195.74,155.75 193.96,155.82C190.07,155.97 186.17,155.86 182.15,155.55C197.45,143.96 214.61,136.59 233.67,133.02ZM262.52,174.52C263.06,173.88 263.6,173.24 264.46,172.22C262.24,172.22 260.62,172.22 258.52,172.22C259.34,173.38 259.78,174.29 260.48,174.87C260.83,175.15 261.67,174.84 262.52,174.52Z"
|
||||
android:fillColor="#FF42C3"/>
|
||||
<path
|
||||
android:pathData="M320.81,196.15C314.32,190.85 306.7,188.96 298.84,187.82C289.74,186.5 280.81,187.01 271.96,190.85C272.47,189.92 272.88,188.91 273.51,188.06C281.91,176.57 292.26,167.25 304.84,160.58C306.74,159.57 309.33,159.62 311.6,159.59C318.82,159.47 326.04,159.5 333.26,159.59C334.35,159.6 335.69,159.93 336.45,160.63C340.22,164 343.83,167.55 347.48,171.05C347.69,171.25 347.78,171.57 348.11,172.21C346.93,172.21 346.05,172.21 345.18,172.21C328.58,172.21 311.98,172.28 295.39,172.15C292.59,172.13 291.04,173.55 288.95,175.9C290.46,175.9 291.4,175.9 292.33,175.9C311.17,175.9 330.01,175.93 348.85,175.85C351,175.84 352.47,176.48 353.68,178.19C355.68,181.01 357.78,183.76 360.24,187.08C343.65,187.08 327.73,187.08 311.82,187.08C311.79,187.29 311.76,187.49 311.74,187.69C315.27,189 318.39,191.09 322.57,191.01C335.01,190.76 347.46,190.96 359.91,190.88C361.92,190.87 363.16,191.51 364.06,193.28C365.66,196.45 367.39,199.54 369.27,203.03C368.03,203.11 367.26,203.21 366.48,203.21C354.44,203.21 342.41,203.18 330.38,203.24C328.66,203.25 327.4,202.81 326.24,201.52C324.58,199.67 322.72,198.01 320.81,196.15Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M262.01,255.48C260.8,255.48 259.59,255.49 258.15,255.49C259.26,241.21 260.47,226.92 264.71,213.11C265.34,211.06 266.03,210.16 268.06,211.62C270.08,213.06 272.41,214.16 274.15,215.88C286.01,227.55 297.17,239.78 304.4,255.23C302.46,255.42 300.45,255.45 298.21,255.28C297.55,252.82 296.4,251.87 293.9,251.91C284.4,252.06 274.9,251.97 265.39,251.98C264.34,251.98 263.3,251.98 262.01,251.98C262.01,253.37 262.01,254.43 262.01,255.48ZM263.18,238.86C271.2,238.86 279.22,238.86 287.25,238.86C287.34,238.56 287.43,238.26 287.52,237.96C286.55,237.47 285.57,236.58 284.58,236.57C277.89,236.44 271.2,236.51 264.5,236.49C262.98,236.48 262.3,237.02 263.18,238.86ZM268.75,222.05C266.99,221.93 265.42,222.05 265.71,224.51C268.73,224.51 271.68,224.51 274.73,224.51C273.54,221.78 271.35,221.98 268.75,222.05Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M246.72,255.38C244.99,255.51 243.38,255.48 241.58,255.22C241.46,254.11 241.53,253.25 241.62,252.02C235.47,252.02 229.64,252.02 223.81,252.02C220.63,252.03 220.2,252.43 219.74,255.82C218.73,255.87 217.71,255.93 216.49,255.98C218.93,250.76 221.21,245.3 224.33,240.35C229.86,231.57 236.13,223.3 244.52,216.92C248.39,213.97 252.62,211.77 257.32,210.49C257.93,210.32 258.57,210.3 259.92,210.12C254.32,225.13 249.77,239.96 246.72,255.38ZM232.91,235.63C230.52,235.4 229.98,237.18 228.98,239.19C234.28,239.19 239.23,239.12 244.17,239.22C246.42,239.27 247.04,238.27 246.74,235.68C242.23,235.68 237.78,235.68 232.91,235.63ZM251.08,223.33C250.49,222.98 249.91,222.38 249.29,222.32C247.73,222.17 246.14,222.34 244.57,222.24C242.56,222.12 241.44,223.18 240.32,225.2C243.65,225.2 246.59,225.26 249.52,225.13C250.03,225.11 250.51,224.24 251.08,223.33Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M302.88,140.38C312.51,144.32 321.39,149.16 329.57,155.7C313.1,154.61 297.42,157.52 282.02,164.18C282.47,161.67 283.3,160.12 285.45,159.45C288.6,158.48 291.72,157.43 294.83,155.97C292.13,155.97 289.42,155.97 285.99,155.97C289.14,149.79 291.98,144.13 294.96,138.55C295.17,138.15 296.47,138.03 297.12,138.23C299.02,138.8 300.85,139.61 302.88,140.38Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
</vector>
|
||||
|
Before Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 256 KiB |
39
src/android/app/src/main/res/drawable/ic_yuzu_title.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:pathData="M317.52,168.37C318.06,168.37 318.6,168.38 319.65,168.41C322.37,168.43 324.58,168.42 326.79,168.41C349.49,168.43 372.2,168.43 394.91,168.46C400.46,168.47 406.02,168.65 411.58,168.6C413.68,168.59 414.71,169.12 414.54,171.43C414.35,174.26 414.33,177.1 414.37,179.94C414.4,182.64 413.52,183.71 410.51,183.7C377.29,183.59 344.07,183.65 310.85,183.66C295.82,183.66 280.79,183.66 265.75,183.66C264.61,183.66 263.47,183.66 262.06,183.66C262.06,185.29 262.06,186.7 262.06,188.5C312.31,188.5 362.6,188.5 413.71,188.5C412.65,194.11 411.74,199.21 410.66,204.28C410.55,204.83 409.53,205.38 408.84,205.56C407.95,205.78 406.96,205.62 406.01,205.62C359.34,205.62 312.66,205.62 265.99,205.62C264.75,205.62 263.51,205.62 262.03,205.62C262.03,207.2 262.03,208.49 262.03,210.18C310.9,210.18 359.72,210.18 409.36,210.18C408.27,213.87 407.4,217.03 406.39,220.15C404.52,225.95 404.48,225.94 398.53,225.94C354.38,225.94 310.22,225.95 266.07,225.92C263.76,225.91 261.86,226.09 262.84,229.08C263.07,229.81 264.11,230.46 264.92,230.77C265.65,231.06 266.58,230.84 267.42,230.84C310.84,230.84 354.26,230.84 397.67,230.84C399.01,230.84 400.34,230.84 402.55,230.84C400.14,235.79 398.2,240.17 395.86,244.32C395.42,245.11 393.44,245.31 392.17,245.31C373.14,245.37 354.11,245.35 335.08,245.35C313.54,245.35 291.98,245.35 270.43,245.35C268.66,245.35 266.89,245.26 264.63,245.33C262.52,245.45 260.88,245.45 259.24,245.46C258.67,240.67 257.69,235.9 257.58,231.11C257.21,213.69 257.06,196.26 257.01,178.84C257,175.52 257.85,172.2 258.72,168.69C260.68,168.49 262.21,168.48 264.04,168.51C279.33,168.53 294.33,168.5 309.62,168.47C312.44,168.44 314.98,168.4 317.52,168.37Z"
|
||||
android:fillColor="#BF42F6"/>
|
||||
<path
|
||||
android:pathData="M196.08,169.6C196.08,167.63 196.08,165.67 196.08,163.27C194.45,163.27 192.91,163.27 191.37,163.27C161.1,163.29 130.82,163.28 100.54,163.37C97.92,163.38 96.9,162.67 97.32,159.97C97.78,156.97 98.17,153.93 98.27,150.9C98.34,148.6 99.36,148.17 101.4,148.17C132.61,148.1 163.82,147.94 195.04,147.86C197.66,147.86 199.69,147.63 199.14,143.9C165.95,143.9 132.78,143.9 99.13,143.9C100.08,139.41 100.73,135.27 101.94,131.31C102.23,130.39 104.41,129.53 105.72,129.53C137.99,129.47 170.27,129.52 202.54,129.6C204.76,129.61 206.52,129.34 206.71,126.17C172.35,126.17 138.07,126.17 102.94,126.17C105.27,119.84 107.26,114.13 109.56,108.55C109.89,107.76 111.78,107.23 112.96,107.22C129.67,107.14 146.39,107.08 163.1,107.23C166.69,107.27 168.59,105.48 170.67,102.31C151.01,102.31 131.87,102.31 111.88,102.31C114.84,96.98 117.32,92.27 120.09,87.75C120.55,87 122.27,86.74 123.4,86.74C144.22,86.68 165.04,86.54 185.85,86.82C190.74,86.88 194.5,85.04 198.31,82.21C173.73,82.21 149.15,82.21 123.5,82.21C125.26,79.62 126.46,77.76 127.76,75.97C133.8,67.66 133.81,67.67 144.13,67.67C171.78,67.67 199.43,67.67 227.08,67.67C228.19,67.67 229.29,67.67 231.08,67.67C228.38,64.86 226.32,62.93 222.7,62.94C196.21,63.06 169.72,63.01 143.22,63.01C142.01,63.01 140.79,63.01 139.58,63.01C139.41,62.71 139.24,62.41 139.08,62.12C141.68,59.5 144.22,56.8 146.91,54.27C154.7,46.92 154.72,46.79 165.43,46.98C181.21,47.26 196.1,51.61 210.47,57.78C214.32,59.44 217.65,60.31 221.76,58.43C224.98,56.95 228.71,56.61 232.67,56.03C229.67,61.57 233.27,64.22 236.42,67.19C240.32,70.87 244.05,74.72 247.81,78.54C248.6,79.35 249.19,80.35 250.32,81.84C248.78,81.93 247.82,82.03 246.87,82.03C240.24,82.04 233.61,82.23 227,81.97C207.93,81.24 191.92,88.26 178.31,101.15C173.45,105.76 169.11,110.93 164.57,115.87C164.1,116.38 163.83,117.06 163.06,118.34C186.14,110.23 207.86,99.73 232.14,98.87C232.34,99.18 232.53,99.5 232.73,99.82C229.85,103.18 227.16,106.72 224.07,109.87C211.77,122.42 204.42,137.53 201.03,154.6C200.07,159.39 200.14,164.4 199.32,169.51C197.97,169.68 197.03,169.64 196.08,169.6ZM209.68,107.22C212.31,107.23 214.96,107.01 217.56,107.29C221.34,107.69 223.62,105.8 226.08,102.17C219.87,103.64 214.42,104.92 208.98,106.24C208.98,106.24 209.12,106.87 209.68,107.22Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M195.81,169.61C197.03,169.64 197.97,169.68 199.2,169.74C199.58,171.84 199.68,173.93 199.79,176.02C200.15,176.15 200.52,176.28 200.89,176.41C202.45,174.11 204.02,171.8 205.97,169.3C207.64,169.04 208.92,168.97 210.44,168.9C211.39,168.81 212.11,168.66 212.84,168.65C221.05,168.54 229.27,168.45 237.79,168.4C240.14,168.48 242.19,168.51 244.24,168.54C243.48,173.08 242.67,177.61 241.96,182.16C238.76,202.76 238.29,223.44 239.8,244.49C238.12,244.99 236.72,245.2 235.33,245.4C197.17,245.39 159.01,245.31 120.85,245.45C117.22,245.46 115.21,244.38 113.93,241.08C112.65,237.8 111,234.67 109.23,230.84C113.61,230.84 117.25,230.84 120.89,230.84C157.58,230.84 194.27,230.85 230.96,230.81C232.33,230.8 234.26,231.63 234.58,229.04C234.9,226.41 233.71,225.89 231.33,225.9C204.94,225.97 178.55,225.94 152.17,225.95C138.29,225.95 124.42,225.9 110.54,226.03C108.13,226.05 106.9,225.32 106.24,223.03C105.12,219.12 103.81,215.26 102.24,210.34C146.69,210.16 190.42,210.32 234.45,210.24C234.54,208.65 234.62,207.35 234.7,205.8C233.41,205.73 232.39,205.63 231.38,205.63C191.74,205.61 152.11,205.59 112.48,205.59C109.43,205.59 106.38,205.78 103.34,205.7C102.57,205.68 101.25,205.09 101.13,204.56C99.99,199.28 99.05,193.95 97.99,188.26C103.72,188.26 108.74,188.24 113.75,188.28C115.11,188.29 116.47,188.55 117.83,188.55C155.88,188.57 193.94,188.57 231.99,188.56C236.12,188.56 236.28,188.39 236.25,183.67C190.27,183.67 144.28,183.67 97.69,183.67C97.5,179.15 97.2,175.01 97.29,170.87C97.3,170.33 99.29,169.41 100.37,169.4C110.67,169.26 120.97,169.28 131.28,169.27C143.26,169.26 155.24,169.22 167.23,169.28C176.66,169.33 186.1,169.51 195.81,169.61Z"
|
||||
android:fillColor="#BF43F5"/>
|
||||
<path
|
||||
android:pathData="M235.35,245.69C236.72,245.2 238.12,244.99 239.8,244.78C241.62,256.91 242.64,269.16 244.79,281.19C247.54,296.59 251.45,311.75 257.46,326.28C257.66,326.75 257.8,327.25 257.96,327.73C240.7,334.84 173.22,312.36 158.05,294.11C172.31,294.11 186.11,294.11 199.9,294.11C213.75,294.11 227.59,294.11 241.47,294.11C241.67,290.83 240.89,289.4 237.49,289.42C210.37,289.55 183.25,289.46 156.13,289.54C153.11,289.55 150.66,288.97 148.51,286.71C143.83,281.81 138.99,277.07 134.22,272.27C134.38,271.97 134.53,271.67 134.69,271.38C168.96,271.38 203.24,271.38 237.91,271.38C237.59,269.49 237.35,268.09 237.06,266.42C235.66,266.42 234.41,266.42 233.17,266.42C204.05,266.42 174.93,266.43 145.82,266.41C141.61,266.41 137.4,266.12 133.21,266.29C130.69,266.4 129.04,265.57 127.7,263.52C124.9,259.24 122.01,255.03 118.68,250.09C158.03,250.09 196.51,250.09 235.37,250.09C235.37,248.43 235.37,247.21 235.35,245.69Z"
|
||||
android:fillColor="#985DED"/>
|
||||
<path
|
||||
android:pathData="M259.22,245.74C260.88,245.45 262.52,245.45 264.42,245.44C264.74,246.79 264.81,248.15 264.9,249.86C307.33,249.86 349.65,249.86 392.94,249.86C390.82,253.23 389.17,255.95 387.42,258.59C382.22,266.42 382.21,266.42 372.98,266.42C339.55,266.42 306.12,266.42 272.68,266.42C271.45,266.42 270.21,266.42 269.01,266.42C268.6,270.97 268.9,271.32 272.97,271.32C306.4,271.33 339.83,271.33 373.27,271.33C374.49,271.33 375.71,271.33 377.95,271.33C375.2,274.41 372.88,276.64 370.99,279.18C364.84,287.47 357,290.15 346.54,289.75C324.07,288.9 301.55,289.49 279.05,289.49C277.81,289.49 276.57,289.49 275.34,289.49C275.16,293.42 275.74,294.1 279.15,294.1C302.59,294.11 326.04,294.1 349.48,294.11C350.69,294.11 351.89,294.11 353.09,294.11C353.19,294.39 353.29,294.68 353.39,294.97C351.01,296.75 348.69,298.61 346.25,300.3C330.27,311.36 312.8,319.17 293.96,323.9C291.74,324.45 289.36,324.83 287.12,324.64C285.84,324.52 284.3,323.33 283.54,322.19C277.07,312.49 273.01,301.7 269.22,290.75C264.17,276.21 261.34,261.2 259.22,245.74Z"
|
||||
android:fillColor="#985DED"/>
|
||||
<path
|
||||
android:pathData="M326.78,168.15C324.58,168.42 322.37,168.43 319.85,168.39C321.63,158.65 321.41,149 319.06,139.36C315.84,126.14 308.31,116.06 296.96,108.73C296.52,108.45 296.09,108.16 295.7,107.28C307.61,107.28 319.52,107.28 331.43,107.28C331.48,106.93 331.53,106.59 331.57,106.25C326.8,104.44 322.07,102.34 316.8,102.29C306.29,102.2 295.77,102.33 285.27,102.21C283.16,102.19 281.07,101.5 279.01,100.57C291.5,95.55 304.29,96.1 317.13,98.13C330.14,100.18 341.45,106.94 354.27,112.15C353.23,110.19 352.63,109.05 351.78,107.42C353.03,107.32 353.99,107.18 354.94,107.18C369.55,107.16 384.16,107.21 398.77,107.12C400.99,107.11 402.21,107.7 402.93,109.91C404.51,114.76 406.34,119.52 408.3,124.96C378.96,124.96 350.45,124.96 321.79,124.96C321.89,128.04 323.22,128.9 325.8,128.89C345.56,128.82 365.32,128.82 385.08,128.87C392.43,128.89 399.77,129.08 407.11,129.34C408.02,129.37 409.48,130.14 409.67,130.85C410.73,134.77 411.47,138.78 412.41,143.2C383.7,143.2 355.5,143.2 327.33,143.2C326.95,147.48 327.15,147.7 330.87,147.7C357.35,147.74 383.84,147.81 410.32,147.8C412.27,147.8 413.19,148.26 413.31,150.37C413.52,153.82 413.96,157.26 414.41,160.69C414.69,162.87 413.98,163.71 411.68,163.63C407.17,163.48 402.65,163.56 398.13,163.56C375.85,163.56 353.57,163.56 331.29,163.56C327.03,163.56 327.03,163.57 326.78,168.15Z"
|
||||
android:fillColor="#FF43C4"/>
|
||||
<path
|
||||
android:pathData="M227.85,13.36C256.3,8.58 282.98,11.91 297.89,17.06C295.79,21.98 293.7,26.87 291.61,31.76C290.5,34.37 289.2,36.91 288.3,39.59C287.56,41.8 286.27,42.35 284.07,42.34C269.05,42.26 254.03,42.3 238.21,42.3C240.88,45.05 242.88,47.26 246.57,47.22C257.92,47.11 269.27,47.18 280.62,47.19C281.77,47.19 282.91,47.19 285.21,47.19C282.72,51.33 281.09,55.24 278.43,58.21C274.25,62.87 269.29,66.82 264.85,71.26C263.14,72.97 262.34,72.9 260.7,71.1C253.98,63.75 247.23,56.42 240.16,49.4C232.38,41.67 222.49,38.46 211.78,37.23C202.25,36.13 193.21,37.88 184.27,40.97C182.14,41.71 179.81,42.15 177.55,42.24C172.62,42.42 167.68,42.29 162.59,41.89C181.97,27.22 203.7,17.88 227.85,13.36ZM264.4,65.93C265.08,65.12 265.76,64.31 266.85,63.01C264.04,63.01 261.99,63.01 259.33,63.01C260.37,64.48 260.92,65.63 261.81,66.37C262.25,66.73 263.32,66.32 264.4,65.93Z"
|
||||
android:fillColor="#FF42C3"/>
|
||||
<path
|
||||
android:pathData="M338.22,93.32C330,86.61 320.35,84.22 310.4,82.77C298.87,81.1 287.56,81.75 276.35,86.62C276.99,85.43 277.52,84.16 278.31,83.08C288.95,68.53 302.06,56.72 317.99,48.27C320.4,46.99 323.68,47.06 326.56,47.01C335.71,46.86 344.86,46.9 354,47.01C355.37,47.02 357.07,47.45 358.04,48.33C362.81,52.6 367.39,57.09 372.01,61.53C372.28,61.78 372.38,62.19 372.81,63.01C371.31,63.01 370.2,63.01 369.09,63.01C348.07,63.01 327.04,63.08 306.02,62.93C302.48,62.9 300.52,64.7 297.87,67.67C299.78,67.67 300.97,67.67 302.15,67.67C326.02,67.67 349.88,67.71 373.74,67.6C376.46,67.59 378.32,68.41 379.86,70.57C382.39,74.14 385.05,77.63 388.18,81.84C367.16,81.84 347,81.84 326.84,81.84C326.8,82.1 326.77,82.35 326.73,82.61C331.21,84.27 335.15,86.92 340.45,86.81C356.21,86.5 371.98,86.75 387.75,86.65C390.3,86.63 391.87,87.45 393.01,89.69C395.03,93.7 397.23,97.61 399.61,102.04C398.04,102.14 397.06,102.26 396.07,102.26C380.83,102.27 365.59,102.23 350.34,102.31C348.17,102.32 346.58,101.76 345.1,100.13C343,97.79 340.64,95.68 338.22,93.32Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M263.75,168.47C262.21,168.48 260.68,168.49 258.86,168.49C260.26,150.4 261.8,132.3 267.17,114.8C267.96,112.21 268.84,111.07 271.41,112.92C273.97,114.75 276.92,116.14 279.13,118.31C294.14,133.1 308.29,148.58 317.44,168.16C314.98,168.4 312.44,168.44 309.6,168.22C308.77,165.11 307.31,163.9 304.14,163.95C292.11,164.14 280.07,164.03 268.03,164.04C266.7,164.04 265.38,164.04 263.75,164.04C263.75,165.81 263.75,167.14 263.75,168.47ZM265.23,147.42C275.39,147.42 285.55,147.42 295.71,147.42C295.83,147.04 295.95,146.66 296.07,146.28C294.82,145.66 293.59,144.54 292.34,144.52C283.86,144.36 275.38,144.45 266.9,144.42C264.98,144.41 264.11,145.09 265.23,147.42ZM272.28,126.13C270.05,125.98 268.06,126.13 268.44,129.24C272.26,129.24 276,129.24 279.86,129.24C278.35,125.79 275.57,126.04 272.28,126.13Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M244.37,168.35C242.19,168.51 240.14,168.48 237.87,168.14C237.72,166.74 237.8,165.65 237.92,164.1C230.12,164.1 222.74,164.09 215.35,164.1C211.33,164.1 210.79,164.62 210.2,168.9C208.92,168.97 207.64,169.04 206.08,169.11C209.18,162.49 212.07,155.57 216.01,149.32C223.02,138.18 230.96,127.72 241.59,119.63C246.5,115.89 251.86,113.11 257.81,111.49C258.58,111.28 259.39,111.25 261.09,111.02C254,130.03 248.24,148.81 244.37,168.35ZM226.88,143.34C223.86,143.04 223.17,145.3 221.91,147.84C228.63,147.84 234.89,147.75 241.15,147.88C244,147.94 244.78,146.68 244.4,143.4C238.69,143.4 233.05,143.4 226.88,143.34ZM249.9,127.76C249.15,127.31 248.42,126.55 247.63,126.47C245.66,126.28 243.65,126.49 241.66,126.37C239.1,126.21 237.69,127.56 236.27,130.12C240.49,130.12 244.21,130.2 247.93,130.03C248.58,130 249.17,128.9 249.9,127.76Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
<path
|
||||
android:pathData="M315.52,22.68C327.71,27.67 338.96,33.8 349.32,42.08C328.46,40.7 308.6,44.4 289.08,52.83C289.67,49.65 290.71,47.68 293.44,46.84C297.43,45.61 301.38,44.27 305.32,42.43C301.9,42.43 298.47,42.43 294.12,42.43C298.11,34.6 301.71,27.44 305.48,20.37C305.75,19.86 307.39,19.71 308.22,19.96C310.62,20.69 312.94,21.7 315.52,22.68Z"
|
||||
android:fillColor="#FF44C4"/>
|
||||
</vector>
|
||||
@@ -48,7 +48,7 @@
|
||||
android:layout_height="200dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:src="@drawable/ic_yuzu_named" />
|
||||
android:src="@drawable/ic_yuzu_title" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
|
||||
@@ -44,11 +44,12 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_logo"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_marginVertical="24dp"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginBottom="28dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:src="@drawable/ic_yuzu_named" />
|
||||
android:src="@drawable/ic_yuzu_title" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/EdenCard"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/linear_layout_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="16dp">
|
||||
|
||||
@@ -21,17 +21,15 @@
|
||||
android:id="@+id/logo_image"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:layout_marginVertical="48dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:src="@drawable/ic_yuzu" />
|
||||
android:src="@drawable/ic_yuzu_full" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/home_settings_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false" />
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 122 KiB |
@@ -3,7 +3,7 @@
|
||||
|
||||
<style name="Theme.Yuzu.Splash.Main" parent="Theme.SplashScreen">
|
||||
<item name="windowSplashScreenBackground">@color/eden_background</item>
|
||||
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_yuzu_splash</item>
|
||||
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_yuzu</item>
|
||||
<item name="postSplashScreenTheme">@style/Theme.Yuzu.Main</item>
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -22,6 +19,3 @@ dependencyResolutionManagement {
|
||||
}
|
||||
|
||||
include(":app")
|
||||
|
||||
include("Eden")
|
||||
project(":Eden").projectDir = file("../..")
|
||||
|
||||
@@ -578,7 +578,10 @@ public:
|
||||
#endif
|
||||
int flags = (fd > 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
||||
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
|
||||
ASSERT_MSG(ret != MAP_FAILED,
|
||||
"mmap(virt_off=0x{:X}, host_off=0x{:X}, len=0x{:X}, virt_size=0x{:X}, backing_size=0x{:X}, perms=0x{:X}) failed: {}",
|
||||
virtual_offset, host_offset, length, virtual_size, backing_size,
|
||||
static_cast<u32>(perms), strerror(errno));
|
||||
}
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length) {
|
||||
|
||||
@@ -466,7 +466,7 @@ struct Values {
|
||||
true};
|
||||
SwitchableSetting<bool> async_presentation{linkage,
|
||||
#ifdef ANDROID
|
||||
true,
|
||||
false,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
|
||||
@@ -130,6 +130,17 @@ public:
|
||||
ResetStorageBit(id.index);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Contains(SlotId id) const noexcept {
|
||||
if (!id) {
|
||||
return false;
|
||||
}
|
||||
const size_t word = id.index / 64;
|
||||
if (word >= stored_bitset.size()) {
|
||||
return false;
|
||||
}
|
||||
return ((stored_bitset[word] >> (id.index % 64)) & 1) != 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] Iterator begin() noexcept {
|
||||
const auto it = std::ranges::find_if(stored_bitset, [](u64 value) { return value != 0; });
|
||||
if (it == stored_bitset.end()) {
|
||||
|
||||
@@ -391,15 +391,28 @@ const std::size_t CACHE_PAGE_SIZE = 4096;
|
||||
|
||||
void ArmNce::ClearInstructionCache() {
|
||||
#ifdef __aarch64__
|
||||
// Ensure all previous memory operations complete
|
||||
// Use IC IALLU to actually invalidate L1 instruction cache
|
||||
asm volatile("dsb ish\n"
|
||||
"ic iallu\n"
|
||||
"dsb ish\n"
|
||||
"isb" ::: "memory");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ArmNce::InvalidateCacheRange(u64 addr, std::size_t size) {
|
||||
this->ClearInstructionCache();
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
// Invalidate instruction cache for specific range instead of full flush
|
||||
constexpr u64 cache_line_size = 64;
|
||||
const u64 aligned_addr = addr & ~(cache_line_size - 1);
|
||||
const u64 end_addr = (addr + size + cache_line_size - 1) & ~(cache_line_size - 1);
|
||||
|
||||
asm volatile("dsb ish" ::: "memory");
|
||||
for (u64 i = aligned_addr; i < end_addr; i += cache_line_size) {
|
||||
asm volatile("ic ivau, %0" :: "r"(i) : "memory");
|
||||
}
|
||||
asm volatile("dsb ish\n"
|
||||
"isb" ::: "memory");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
@@ -187,7 +191,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
buffer_w_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorABW>());
|
||||
}
|
||||
|
||||
const auto buffer_c_offset = rp.GetCurrentOffset() + command_header->data_size;
|
||||
buffer_c_offset = rp.GetCurrentOffset() + command_header->data_size;
|
||||
|
||||
if (!command_header->IsTipc()) {
|
||||
// Padding to align to 16 bytes
|
||||
@@ -294,7 +298,15 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer() {
|
||||
}
|
||||
|
||||
// Write the domain objects to the command buffer, these go after the raw untranslated data.
|
||||
// TODO(Subv): This completely ignores C buffers.
|
||||
|
||||
if (buffer_c_offset != 0 && !buffer_c_descriptors.empty()) {
|
||||
constexpr u32 WORDS_PER_DESCRIPTOR = sizeof(IPC::BufferDescriptorC) / sizeof(u32);
|
||||
u32 descriptor_offset = buffer_c_offset;
|
||||
for (const auto& descriptor : buffer_c_descriptors) {
|
||||
std::memcpy(&cmd_buf[descriptor_offset], &descriptor, sizeof(descriptor));
|
||||
descriptor_offset += WORDS_PER_DESCRIPTOR;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetManager()->IsDomain()) {
|
||||
current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
|
||||
@@ -393,10 +405,14 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
|
||||
const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
|
||||
BufferDescriptorB()[buffer_index].Size()};
|
||||
const std::size_t buffer_size{GetWriteBufferSize(buffer_index)};
|
||||
if (buffer_size == 0) {
|
||||
LOG_WARNING(Core, "WriteBuffer target index {} has zero capacity", buffer_index);
|
||||
return 0;
|
||||
}
|
||||
if (size > buffer_size) {
|
||||
LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
|
||||
buffer_size);
|
||||
size = buffer_size; // TODO(bunnei): This needs to be HW tested
|
||||
LOG_WARNING(Core, "size ({:016X}) is greater than buffer_size ({:016X}); clamping",
|
||||
size, buffer_size);
|
||||
size = buffer_size;
|
||||
}
|
||||
|
||||
if (is_buffer_b) {
|
||||
@@ -418,15 +434,25 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
|
||||
|
||||
std::size_t HLERequestContext::WriteBufferB(const void* buffer, std::size_t size,
|
||||
std::size_t buffer_index) const {
|
||||
if (buffer_index >= BufferDescriptorB().size() || size == 0) {
|
||||
if (buffer_index >= BufferDescriptorB().size()) {
|
||||
LOG_WARNING(Core, "WriteBufferB invalid buffer index {}", buffer_index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
LOG_WARNING(Core, "skip empty buffer write (B)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto buffer_size{BufferDescriptorB()[buffer_index].Size()};
|
||||
if (buffer_size == 0) {
|
||||
LOG_WARNING(Core, "WriteBufferB target index {} has zero capacity", buffer_index);
|
||||
return 0;
|
||||
}
|
||||
if (size > buffer_size) {
|
||||
LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
|
||||
buffer_size);
|
||||
size = buffer_size; // TODO(bunnei): This needs to be HW tested
|
||||
LOG_WARNING(Core, "size ({:016X}) is greater than buffer_size ({:016X}); clamping",
|
||||
size, buffer_size);
|
||||
size = buffer_size;
|
||||
}
|
||||
|
||||
memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
|
||||
@@ -435,15 +461,25 @@ std::size_t HLERequestContext::WriteBufferB(const void* buffer, std::size_t size
|
||||
|
||||
std::size_t HLERequestContext::WriteBufferC(const void* buffer, std::size_t size,
|
||||
std::size_t buffer_index) const {
|
||||
if (buffer_index >= BufferDescriptorC().size() || size == 0) {
|
||||
if (buffer_index >= BufferDescriptorC().size()) {
|
||||
LOG_WARNING(Core, "WriteBufferC invalid buffer index {}", buffer_index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
LOG_WARNING(Core, "skip empty buffer write (C)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto buffer_size{BufferDescriptorC()[buffer_index].Size()};
|
||||
if (buffer_size == 0) {
|
||||
LOG_WARNING(Core, "WriteBufferC target index {} has zero capacity", buffer_index);
|
||||
return 0;
|
||||
}
|
||||
if (size > buffer_size) {
|
||||
LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
|
||||
buffer_size);
|
||||
size = buffer_size; // TODO(bunnei): This needs to be HW tested
|
||||
LOG_WARNING(Core, "size ({:016X}) is greater than buffer_size ({:016X}); clamping",
|
||||
size, buffer_size);
|
||||
size = buffer_size;
|
||||
}
|
||||
|
||||
memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
|
||||
@@ -473,12 +509,20 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorB().size() > buffer_index, { return 0; },
|
||||
"BufferDescriptorB invalid buffer_index {}", buffer_index);
|
||||
return BufferDescriptorB()[buffer_index].Size();
|
||||
const auto size = BufferDescriptorB()[buffer_index].Size();
|
||||
if (size == 0) {
|
||||
LOG_WARNING(Core, "BufferDescriptorB index {} has zero size", buffer_index);
|
||||
}
|
||||
return size;
|
||||
} else {
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorC().size() > buffer_index, { return 0; },
|
||||
"BufferDescriptorC invalid buffer_index {}", buffer_index);
|
||||
return BufferDescriptorC()[buffer_index].Size();
|
||||
const auto size = BufferDescriptorC()[buffer_index].Size();
|
||||
if (size == 0) {
|
||||
LOG_WARNING(Core, "BufferDescriptorC index {} has zero size", buffer_index);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -422,6 +425,7 @@ private:
|
||||
u32 data_payload_offset{};
|
||||
u32 handles_offset{};
|
||||
u32 domain_offset{};
|
||||
u32 buffer_c_offset{};
|
||||
|
||||
std::weak_ptr<SessionRequestManager> manager{};
|
||||
bool is_deferred{false};
|
||||
|
||||
@@ -134,4 +134,4 @@ target_include_directories(dynarmic_tests PRIVATE . ../src)
|
||||
target_compile_options(dynarmic_tests PRIVATE ${DYNARMIC_CXX_FLAGS})
|
||||
target_compile_definitions(dynarmic_tests PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
|
||||
|
||||
add_test(NAME dynarmic_tests COMMAND dynarmic_tests --durations yes)
|
||||
add_test(dynarmic_tests dynarmic_tests --durations yes)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
#include <spirv-tools/optimizer.hpp>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||
@@ -439,15 +440,23 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
|
||||
ctx.AddExtension("SPV_KHR_shader_draw_parameters");
|
||||
ctx.AddCapability(spv::Capability::DrawParameters);
|
||||
}
|
||||
if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
|
||||
info.uses_subgroup_shuffles) &&
|
||||
profile.support_vote) {
|
||||
const bool stage_supports_warp = profile.SupportsWarpIntrinsics(ctx.stage);
|
||||
const bool needs_warp_intrinsics = info.uses_subgroup_vote ||
|
||||
info.uses_subgroup_invocation_id ||
|
||||
info.uses_subgroup_shuffles;
|
||||
|
||||
if (needs_warp_intrinsics && profile.support_vote && stage_supports_warp) {
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformBallot);
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
|
||||
if (!profile.warp_size_potentially_larger_than_guest) {
|
||||
// vote ops are only used when not taking the long path
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformVote);
|
||||
}
|
||||
} else if (needs_warp_intrinsics && !stage_supports_warp) {
|
||||
LOG_WARNING(Shader,
|
||||
"Warp intrinsics requested in stage {} but the device does not report subgroup "
|
||||
"support; falling back to scalar approximations",
|
||||
static_cast<u32>(ctx.stage));
|
||||
}
|
||||
if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
|
||||
ctx.AddCapability(spv::Capability::Int64Atomics);
|
||||
|
||||
@@ -491,9 +491,24 @@ void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) {
|
||||
}
|
||||
|
||||
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) {
|
||||
const AttributeType output_type{ctx.runtime_info.color_output_types[index]};
|
||||
Id pointer_type{ctx.output_f32};
|
||||
Id store_value{value};
|
||||
switch (output_type) {
|
||||
case AttributeType::SignedInt:
|
||||
pointer_type = ctx.output_s32;
|
||||
store_value = ctx.OpBitcast(ctx.S32[1], value);
|
||||
break;
|
||||
case AttributeType::UnsignedInt:
|
||||
pointer_type = ctx.output_u32;
|
||||
store_value = ctx.OpBitcast(ctx.U32[1], value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const Id component_id{ctx.Const(component)};
|
||||
const Id pointer{ctx.OpAccessChain(ctx.output_f32, ctx.frag_color.at(index), component_id)};
|
||||
ctx.OpStore(pointer, value);
|
||||
const Id pointer{ctx.OpAccessChain(pointer_type, ctx.frag_color.at(index), component_id)};
|
||||
ctx.OpStore(pointer, store_value);
|
||||
}
|
||||
|
||||
void EmitSetSampleMask(EmitContext& ctx, Id value) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -195,6 +198,41 @@ Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR
|
||||
}
|
||||
}
|
||||
|
||||
Id TextureColorResultType(EmitContext& ctx, const TextureDefinition& def) {
|
||||
switch (def.component_type) {
|
||||
case SamplerComponentType::Float:
|
||||
case SamplerComponentType::Depth:
|
||||
return ctx.F32[4];
|
||||
case SamplerComponentType::Sint:
|
||||
return ctx.S32[4];
|
||||
case SamplerComponentType::Stencil:
|
||||
return ctx.U32[4];
|
||||
case SamplerComponentType::Uint:
|
||||
return ctx.U32[4];
|
||||
}
|
||||
throw InvalidArgument("Invalid sampler component type {}", def.component_type);
|
||||
}
|
||||
|
||||
Id TextureSampleResultToFloat(EmitContext& ctx, const TextureDefinition& def, Id color) {
|
||||
switch (def.component_type) {
|
||||
case SamplerComponentType::Float:
|
||||
case SamplerComponentType::Depth:
|
||||
return color;
|
||||
case SamplerComponentType::Sint:
|
||||
return ctx.OpConvertSToF(ctx.F32[4], color);
|
||||
case SamplerComponentType::Stencil:
|
||||
{
|
||||
const Id converted{ctx.OpConvertUToF(ctx.F32[4], color)};
|
||||
const Id inv255{ctx.Const(1.0f / 255.0f)};
|
||||
const Id scale{ctx.ConstantComposite(ctx.F32[4], inv255, inv255, inv255, inv255)};
|
||||
return ctx.OpFMul(ctx.F32[4], converted, scale);
|
||||
}
|
||||
case SamplerComponentType::Uint:
|
||||
return ctx.OpConvertUToF(ctx.F32[4], color);
|
||||
}
|
||||
throw InvalidArgument("Invalid sampler component type {}", def.component_type);
|
||||
}
|
||||
|
||||
Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& index) {
|
||||
if (!index.IsImmediate() || index.U32() != 0) {
|
||||
throw NotImplementedException("Indirect image indexing");
|
||||
@@ -449,31 +487,39 @@ Id EmitBoundImageWrite(EmitContext&) {
|
||||
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id bias_lc, const IR::Value& offset) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||
const Id color_type{TextureColorResultType(ctx, def)};
|
||||
const Id texture{Texture(ctx, info, index)};
|
||||
Id color{};
|
||||
if (ctx.stage == Stage::Fragment) {
|
||||
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
|
||||
bias_lc, offset);
|
||||
return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
|
||||
&EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4],
|
||||
Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
||||
color = Emit(&EmitContext::OpImageSparseSampleImplicitLod,
|
||||
&EmitContext::OpImageSampleImplicitLod, ctx, inst, color_type, texture,
|
||||
coords, operands.MaskOptional(), operands.Span());
|
||||
} else {
|
||||
// We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
|
||||
// if the lod was explicitly zero. This may change on Turing with implicit compute
|
||||
// derivatives
|
||||
const Id lod{ctx.Const(0.0f)};
|
||||
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
|
||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||
color = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type, texture,
|
||||
coords, operands.Mask(), operands.Span());
|
||||
}
|
||||
return TextureSampleResultToFloat(ctx, def, color);
|
||||
}
|
||||
|
||||
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id lod, const IR::Value& offset) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||
const Id color_type{TextureColorResultType(ctx, def)};
|
||||
const ImageOperands operands(ctx, false, true, false, lod, offset);
|
||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||
const Id color{Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type,
|
||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span())};
|
||||
return TextureSampleResultToFloat(ctx, def, color);
|
||||
}
|
||||
|
||||
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
||||
@@ -509,13 +555,18 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
|
||||
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
const IR::Value& offset, const IR::Value& offset2) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||
const Id color_type{TextureColorResultType(ctx, def)};
|
||||
const ImageOperands operands(ctx, offset, offset2);
|
||||
const Id texture{Texture(ctx, info, index)};
|
||||
if (ctx.profile.need_gather_subpixel_offset) {
|
||||
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
|
||||
}
|
||||
return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
|
||||
ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
|
||||
operands.MaskOptional(), operands.Span());
|
||||
const Id color{
|
||||
Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, color_type,
|
||||
texture, coords, ctx.Const(info.gather_component), operands.MaskOptional(),
|
||||
operands.Span())};
|
||||
return TextureSampleResultToFloat(ctx, def, color);
|
||||
}
|
||||
|
||||
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
@@ -533,6 +584,9 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
||||
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
|
||||
Id lod, Id ms) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const TextureDefinition* def =
|
||||
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
|
||||
const Id result_type{def ? TextureColorResultType(ctx, *def) : ctx.F32[4]};
|
||||
AddOffsetToCoordinates(ctx, info, coords, offset);
|
||||
if (info.type == TextureType::Buffer) {
|
||||
lod = Id{};
|
||||
@@ -542,8 +596,13 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
|
||||
lod = Id{};
|
||||
}
|
||||
const ImageOperands operands(lod, ms);
|
||||
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
|
||||
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
||||
Id color{Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst,
|
||||
result_type, TextureImage(ctx, info, index), coords, operands.MaskOptional(),
|
||||
operands.Span())};
|
||||
if (def) {
|
||||
color = TextureSampleResultToFloat(ctx, *def, color);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
|
||||
@@ -588,14 +647,17 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
|
||||
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||
Id derivatives, const IR::Value& offset, Id lod_clamp) {
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||
const Id color_type{TextureColorResultType(ctx, def)};
|
||||
const auto operands = info.num_derivatives == 3
|
||||
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
||||
ctx.Def(offset), {}, lod_clamp)
|
||||
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
||||
info.num_derivatives, offset, lod_clamp);
|
||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||
const Id color{Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type,
|
||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span())};
|
||||
return TextureSampleResultToFloat(ctx, def, color);
|
||||
}
|
||||
|
||||
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -78,9 +81,25 @@ Id AddPartitionBase(EmitContext& ctx, Id thread_id) {
|
||||
const Id partition_base{ctx.OpShiftLeftLogical(ctx.U32[1], partition_idx, ctx.Const(5u))};
|
||||
return ctx.OpIAdd(ctx.U32[1], thread_id, partition_base);
|
||||
}
|
||||
|
||||
bool SupportsWarpIntrinsics(const EmitContext& ctx) {
|
||||
return ctx.profile.SupportsWarpIntrinsics(ctx.stage);
|
||||
}
|
||||
|
||||
void SetAlwaysInBounds(EmitContext& ctx, IR::Inst* inst) {
|
||||
SetInBoundsFlag(inst, ctx.true_value);
|
||||
}
|
||||
|
||||
Id FallbackBallotMask(EmitContext& ctx, Id pred) {
|
||||
const Id full_mask{ctx.Const(0xFFFFFFFFu)};
|
||||
return ctx.OpSelect(ctx.U32[1], pred, full_mask, ctx.u32_zero_value);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitLaneId(EmitContext& ctx) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
return ctx.u32_zero_value;
|
||||
}
|
||||
const Id id{GetThreadId(ctx)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return id;
|
||||
@@ -89,6 +108,9 @@ Id EmitLaneId(EmitContext& ctx) {
|
||||
}
|
||||
|
||||
Id EmitVoteAll(EmitContext& ctx, Id pred) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
return pred;
|
||||
}
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred);
|
||||
}
|
||||
@@ -102,6 +124,9 @@ Id EmitVoteAll(EmitContext& ctx, Id pred) {
|
||||
}
|
||||
|
||||
Id EmitVoteAny(EmitContext& ctx, Id pred) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
return pred;
|
||||
}
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred);
|
||||
}
|
||||
@@ -115,6 +140,9 @@ Id EmitVoteAny(EmitContext& ctx, Id pred) {
|
||||
}
|
||||
|
||||
Id EmitVoteEqual(EmitContext& ctx, Id pred) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
return pred;
|
||||
}
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred);
|
||||
}
|
||||
@@ -129,6 +157,9 @@ Id EmitVoteEqual(EmitContext& ctx, Id pred) {
|
||||
}
|
||||
|
||||
Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
return FallbackBallotMask(ctx, pred);
|
||||
}
|
||||
const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
|
||||
@@ -137,27 +168,46 @@ Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
|
||||
}
|
||||
|
||||
Id EmitSubgroupEqMask(EmitContext& ctx) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
return ctx.u32_zero_value;
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_eq);
|
||||
}
|
||||
|
||||
Id EmitSubgroupLtMask(EmitContext& ctx) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
return ctx.u32_zero_value;
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_lt);
|
||||
}
|
||||
|
||||
Id EmitSubgroupLeMask(EmitContext& ctx) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
return ctx.u32_zero_value;
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_le);
|
||||
}
|
||||
|
||||
Id EmitSubgroupGtMask(EmitContext& ctx) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
return ctx.u32_zero_value;
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_gt);
|
||||
}
|
||||
|
||||
Id EmitSubgroupGeMask(EmitContext& ctx) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
return ctx.u32_zero_value;
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_ge);
|
||||
}
|
||||
|
||||
Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
SetAlwaysInBounds(ctx, inst);
|
||||
return value;
|
||||
}
|
||||
const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)};
|
||||
const Id thread_id{EmitLaneId(ctx)};
|
||||
const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)};
|
||||
@@ -177,6 +227,10 @@ Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id cla
|
||||
|
||||
Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
SetAlwaysInBounds(ctx, inst);
|
||||
return value;
|
||||
}
|
||||
const Id thread_id{EmitLaneId(ctx)};
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)};
|
||||
@@ -192,6 +246,10 @@ Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
|
||||
Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
SetAlwaysInBounds(ctx, inst);
|
||||
return value;
|
||||
}
|
||||
const Id thread_id{EmitLaneId(ctx)};
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)};
|
||||
@@ -207,6 +265,10 @@ Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clam
|
||||
|
||||
Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask) {
|
||||
if (!SupportsWarpIntrinsics(ctx)) {
|
||||
SetAlwaysInBounds(ctx, inst);
|
||||
return value;
|
||||
}
|
||||
const Id thread_id{EmitLaneId(ctx)};
|
||||
const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)};
|
||||
Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)};
|
||||
|
||||
@@ -28,27 +28,41 @@ enum class Operation {
|
||||
FPMax,
|
||||
};
|
||||
|
||||
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
|
||||
Id ComponentScalarType(EmitContext& ctx, SamplerComponentType component_type) {
|
||||
switch (component_type) {
|
||||
case SamplerComponentType::Float:
|
||||
case SamplerComponentType::Depth:
|
||||
return ctx.F32[1];
|
||||
case SamplerComponentType::Sint:
|
||||
return ctx.S32[1];
|
||||
case SamplerComponentType::Stencil:
|
||||
return ctx.U32[1];
|
||||
case SamplerComponentType::Uint:
|
||||
return ctx.U32[1];
|
||||
}
|
||||
throw InvalidArgument("Invalid sampler component type {}", component_type);
|
||||
}
|
||||
|
||||
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc, Id sampled_type) {
|
||||
const spv::ImageFormat format{spv::ImageFormat::Unknown};
|
||||
const Id type{ctx.F32[1]};
|
||||
const bool depth{desc.is_depth};
|
||||
const bool ms{desc.is_multisample};
|
||||
switch (desc.type) {
|
||||
case TextureType::Color1D:
|
||||
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, depth, false, false, 1, format);
|
||||
case TextureType::ColorArray1D:
|
||||
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, depth, true, false, 1, format);
|
||||
case TextureType::Color2D:
|
||||
case TextureType::Color2DRect:
|
||||
return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format);
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, depth, false, ms, 1, format);
|
||||
case TextureType::ColorArray2D:
|
||||
return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, ms, 1, format);
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, depth, true, ms, 1, format);
|
||||
case TextureType::Color3D:
|
||||
return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format);
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, depth, false, false, 1, format);
|
||||
case TextureType::ColorCube:
|
||||
return ctx.TypeImage(type, spv::Dim::Cube, depth, false, false, 1, format);
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Cube, depth, false, false, 1, format);
|
||||
case TextureType::ColorArrayCube:
|
||||
return ctx.TypeImage(type, spv::Dim::Cube, depth, true, false, 1, format);
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Cube, depth, true, false, 1, format);
|
||||
case TextureType::Buffer:
|
||||
break;
|
||||
}
|
||||
@@ -315,6 +329,9 @@ void DefineSsbos(EmitContext& ctx, StorageTypeDefinition& type_def,
|
||||
ctx.Decorate(id, spv::Decoration::Binding, binding);
|
||||
ctx.Decorate(id, spv::Decoration::DescriptorSet, 0U);
|
||||
ctx.Name(id, fmt::format("ssbo{}", index));
|
||||
if (!desc.is_written) {
|
||||
ctx.Decorate(id, spv::Decoration::NonWritable);
|
||||
}
|
||||
if (ctx.profile.supported_spirv >= 0x00010400) {
|
||||
ctx.interfaces.push_back(id);
|
||||
}
|
||||
@@ -546,6 +563,7 @@ void EmitContext::DefineCommonTypes(const Info& info) {
|
||||
|
||||
output_f32 = Name(TypePointer(spv::StorageClass::Output, F32[1]), "output_f32");
|
||||
output_u32 = Name(TypePointer(spv::StorageClass::Output, U32[1]), "output_u32");
|
||||
output_s32 = Name(TypePointer(spv::StorageClass::Output, S32[1]), "output_s32");
|
||||
|
||||
if (info.uses_int8 && profile.support_int8) {
|
||||
AddCapability(spv::Capability::Int8);
|
||||
@@ -1359,7 +1377,8 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
|
||||
void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_index) {
|
||||
textures.reserve(info.texture_descriptors.size());
|
||||
for (const TextureDescriptor& desc : info.texture_descriptors) {
|
||||
const Id image_type{ImageType(*this, desc)};
|
||||
const Id result_type{ComponentScalarType(*this, desc.component_type)};
|
||||
const Id image_type{ImageType(*this, desc, result_type)};
|
||||
const Id sampled_type{TypeSampledImage(image_type)};
|
||||
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, sampled_type)};
|
||||
const Id desc_type{DescType(*this, sampled_type, pointer_type, desc.count)};
|
||||
@@ -1372,8 +1391,10 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
|
||||
.sampled_type = sampled_type,
|
||||
.pointer_type = pointer_type,
|
||||
.image_type = image_type,
|
||||
.result_type = result_type,
|
||||
.count = desc.count,
|
||||
.is_multisample = desc.is_multisample,
|
||||
.component_type = desc.component_type,
|
||||
});
|
||||
if (profile.supported_spirv >= 0x00010400) {
|
||||
interfaces.push_back(id);
|
||||
@@ -1416,6 +1437,7 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
|
||||
void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
const Info& info{program.info};
|
||||
const VaryingState loads{info.loads.mask | info.passthrough.mask};
|
||||
const bool stage_supports_warp = profile.SupportsWarpIntrinsics(stage);
|
||||
|
||||
if (info.uses_workgroup_id) {
|
||||
workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId);
|
||||
@@ -1432,24 +1454,37 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
}
|
||||
if (info.uses_sample_id) {
|
||||
sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId);
|
||||
if (stage == Stage::Fragment) {
|
||||
Decorate(sample_id, spv::Decoration::Flat);
|
||||
}
|
||||
}
|
||||
if (info.uses_is_helper_invocation) {
|
||||
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
|
||||
}
|
||||
if (info.uses_subgroup_mask) {
|
||||
if (info.uses_subgroup_mask && stage_supports_warp) {
|
||||
subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
|
||||
subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
|
||||
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
|
||||
subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
|
||||
subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
|
||||
if (stage == Stage::Fragment) {
|
||||
Decorate(subgroup_mask_eq, spv::Decoration::Flat);
|
||||
Decorate(subgroup_mask_lt, spv::Decoration::Flat);
|
||||
Decorate(subgroup_mask_le, spv::Decoration::Flat);
|
||||
Decorate(subgroup_mask_gt, spv::Decoration::Flat);
|
||||
Decorate(subgroup_mask_ge, spv::Decoration::Flat);
|
||||
}
|
||||
}
|
||||
if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
|
||||
(profile.warp_size_potentially_larger_than_guest &&
|
||||
(info.uses_subgroup_vote || info.uses_subgroup_mask))) {
|
||||
if (stage_supports_warp &&
|
||||
(info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
|
||||
(profile.warp_size_potentially_larger_than_guest &&
|
||||
(info.uses_subgroup_vote || info.uses_subgroup_mask)))) {
|
||||
AddCapability(spv::Capability::GroupNonUniform);
|
||||
subgroup_local_invocation_id =
|
||||
DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);
|
||||
Decorate(subgroup_local_invocation_id, spv::Decoration::Flat);
|
||||
if (stage == Stage::Fragment) {
|
||||
Decorate(subgroup_local_invocation_id, spv::Decoration::Flat);
|
||||
}
|
||||
}
|
||||
if (info.uses_fswzadd) {
|
||||
const Id f32_one{Const(1.0f)};
|
||||
@@ -1461,6 +1496,9 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
}
|
||||
if (loads[IR::Attribute::PrimitiveId]) {
|
||||
primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId);
|
||||
if (stage == Stage::Fragment) {
|
||||
Decorate(primitive_id, spv::Decoration::Flat);
|
||||
}
|
||||
}
|
||||
if (loads[IR::Attribute::Layer]) {
|
||||
AddCapability(spv::Capability::Geometry);
|
||||
@@ -1552,17 +1590,21 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
if (stage != Stage::Fragment) {
|
||||
continue;
|
||||
}
|
||||
switch (info.interpolation[index]) {
|
||||
case Interpolation::Smooth:
|
||||
// Default
|
||||
// Decorate(id, spv::Decoration::Smooth);
|
||||
break;
|
||||
case Interpolation::NoPerspective:
|
||||
Decorate(id, spv::Decoration::NoPerspective);
|
||||
break;
|
||||
case Interpolation::Flat:
|
||||
const bool is_integer = input_type == AttributeType::SignedInt ||
|
||||
input_type == AttributeType::UnsignedInt;
|
||||
if (is_integer) {
|
||||
Decorate(id, spv::Decoration::Flat);
|
||||
break;
|
||||
} else {
|
||||
switch (info.interpolation[index]) {
|
||||
case Interpolation::Smooth:
|
||||
break;
|
||||
case Interpolation::NoPerspective:
|
||||
Decorate(id, spv::Decoration::NoPerspective);
|
||||
break;
|
||||
case Interpolation::Flat:
|
||||
Decorate(id, spv::Decoration::Flat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stage == Stage::TessellationEval) {
|
||||
@@ -1658,7 +1700,18 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
|
||||
if (!info.stores_frag_color[index] && !profile.need_declared_frag_colors) {
|
||||
continue;
|
||||
}
|
||||
frag_color[index] = DefineOutput(*this, F32[4], std::nullopt);
|
||||
const AttributeType output_type{runtime_info.color_output_types[index]};
|
||||
const Id vec_type = [&, output_type]() -> Id {
|
||||
switch (output_type) {
|
||||
case AttributeType::SignedInt:
|
||||
return S32[4];
|
||||
case AttributeType::UnsignedInt:
|
||||
return U32[4];
|
||||
default:
|
||||
return F32[4];
|
||||
}
|
||||
}();
|
||||
frag_color[index] = DefineOutput(*this, vec_type, std::nullopt);
|
||||
Decorate(frag_color[index], spv::Decoration::Location, index);
|
||||
Name(frag_color[index], fmt::format("frag_color{}", index));
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -36,8 +39,10 @@ struct TextureDefinition {
|
||||
Id sampled_type;
|
||||
Id pointer_type;
|
||||
Id image_type;
|
||||
Id result_type;
|
||||
u32 count;
|
||||
bool is_multisample;
|
||||
SamplerComponentType component_type;
|
||||
};
|
||||
|
||||
struct TextureBufferDefinition {
|
||||
@@ -244,6 +249,7 @@ public:
|
||||
|
||||
Id output_f32{};
|
||||
Id output_u32{};
|
||||
Id output_s32{};
|
||||
|
||||
Id image_buffer_type{};
|
||||
Id image_u32{};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -22,6 +25,8 @@ public:
|
||||
|
||||
[[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0;
|
||||
|
||||
[[nodiscard]] virtual SamplerComponentType ReadTextureComponentType(u32 raw_handle) = 0;
|
||||
|
||||
[[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0;
|
||||
|
||||
[[nodiscard]] virtual bool IsTexturePixelFormatInteger(u32 raw_handle) = 0;
|
||||
|
||||
@@ -396,6 +396,10 @@ bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf)
|
||||
return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
SamplerComponentType ReadTextureComponentType(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return env.ReadTextureComponentType(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
class Descriptors {
|
||||
public:
|
||||
explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_,
|
||||
@@ -433,7 +437,9 @@ public:
|
||||
|
||||
u32 Add(const TextureDescriptor& desc) {
|
||||
const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) {
|
||||
return desc.type == existing.type && desc.is_depth == existing.is_depth &&
|
||||
return desc.type == existing.type &&
|
||||
desc.component_type == existing.component_type &&
|
||||
desc.is_depth == existing.is_depth &&
|
||||
desc.has_secondary == existing.has_secondary &&
|
||||
desc.cbuf_index == existing.cbuf_index &&
|
||||
desc.cbuf_offset == existing.cbuf_offset &&
|
||||
@@ -666,10 +672,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
.secondary_shift_left = cbuf.secondary_shift_left,
|
||||
.count = cbuf.count,
|
||||
.size_shift = DESCRIPTOR_SIZE_SHIFT,
|
||||
.component_type = ReadTextureComponentType(env, cbuf),
|
||||
});
|
||||
} else {
|
||||
index = descriptors.Add(TextureDescriptor{
|
||||
.type = flags.type,
|
||||
.component_type = ReadTextureComponentType(env, cbuf),
|
||||
.is_depth = flags.is_depth != 0,
|
||||
.is_multisample = is_multisample,
|
||||
.has_secondary = cbuf.has_secondary,
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/stage.h"
|
||||
|
||||
namespace Shader {
|
||||
|
||||
@@ -46,6 +52,8 @@ struct Profile {
|
||||
bool support_multi_viewport{};
|
||||
bool support_geometry_streams{};
|
||||
|
||||
u32 warp_stage_support_mask{std::numeric_limits<u32>::max()};
|
||||
|
||||
bool warp_size_potentially_larger_than_guest{};
|
||||
|
||||
bool lower_left_origin_mode{};
|
||||
@@ -90,6 +98,11 @@ struct Profile {
|
||||
u64 min_ssbo_alignment{};
|
||||
|
||||
u32 max_user_clip_distances{};
|
||||
|
||||
bool SupportsWarpIntrinsics(Stage stage) const {
|
||||
const u32 bit = 1u << static_cast<u32>(stage);
|
||||
return (warp_stage_support_mask & bit) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -80,6 +83,7 @@ struct TransformFeedbackVarying {
|
||||
|
||||
struct RuntimeInfo {
|
||||
std::array<AttributeType, 32> generic_input_types{};
|
||||
std::array<AttributeType, 8> color_output_types{};
|
||||
VaryingState previous_stage_stores;
|
||||
std::map<IR::Attribute, IR::Attribute> previous_stage_legacy_stores_mapping;
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -151,6 +154,14 @@ enum class ImageFormat : u32 {
|
||||
R32G32B32A32_UINT,
|
||||
};
|
||||
|
||||
enum class SamplerComponentType : u8 {
|
||||
Float,
|
||||
Sint,
|
||||
Uint,
|
||||
Depth,
|
||||
Stencil,
|
||||
};
|
||||
|
||||
enum class Interpolation {
|
||||
Smooth,
|
||||
Flat,
|
||||
@@ -183,6 +194,7 @@ struct TextureBufferDescriptor {
|
||||
u32 secondary_shift_left;
|
||||
u32 count;
|
||||
u32 size_shift;
|
||||
SamplerComponentType component_type;
|
||||
|
||||
auto operator<=>(const TextureBufferDescriptor&) const = default;
|
||||
};
|
||||
@@ -204,6 +216,7 @@ using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescrip
|
||||
|
||||
struct TextureDescriptor {
|
||||
TextureType type;
|
||||
SamplerComponentType component_type;
|
||||
bool is_depth;
|
||||
bool is_multisample;
|
||||
bool has_secondary;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
|
||||
@@ -16,8 +15,6 @@
|
||||
#include "video_core/guest_memory.h"
|
||||
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||
#include "video_core/texture_cache/util.h"
|
||||
#include "video_core/polygon_mode_utils.h"
|
||||
#include "video_core/renderer_vulkan/line_loop_utils.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
@@ -356,37 +353,14 @@ void BufferCache<P>::UpdateComputeBuffers() {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
|
||||
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
|
||||
if (is_indexed) {
|
||||
BindHostIndexBuffer();
|
||||
} else {
|
||||
if constexpr (!P::IS_OPENGL) {
|
||||
const auto polygon_mode = VideoCore::EffectivePolygonMode(maxwell3d->regs);
|
||||
if (draw_state.topology == Maxwell::PrimitiveTopology::Polygon &&
|
||||
polygon_mode == Maxwell::PolygonMode::Line && draw_state.vertex_buffer.count > 1) {
|
||||
const u32 vertex_count = draw_state.vertex_buffer.count;
|
||||
const u32 generated_count = vertex_count + 1;
|
||||
const bool use_u16 = vertex_count <= 0x10000;
|
||||
const u32 element_size = use_u16 ? sizeof(u16) : sizeof(u32);
|
||||
auto staging = runtime.UploadStagingBuffer(
|
||||
static_cast<size_t>(generated_count) * element_size);
|
||||
std::span<u8> dst_span{staging.mapped_span.data(),
|
||||
generated_count * static_cast<size_t>(element_size)};
|
||||
Vulkan::LineLoop::GenerateSequentialWithClosureRaw(dst_span, element_size);
|
||||
const auto synthetic_format = use_u16 ? Maxwell::IndexFormat::UnsignedShort
|
||||
: Maxwell::IndexFormat::UnsignedInt;
|
||||
runtime.BindIndexBuffer(draw_state.topology, synthetic_format,
|
||||
draw_state.vertex_buffer.first, generated_count,
|
||||
staging.buffer, static_cast<u32>(staging.offset),
|
||||
generated_count * element_size);
|
||||
}
|
||||
}
|
||||
if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
|
||||
if (draw_state.topology == Maxwell::PrimitiveTopology::Quads ||
|
||||
draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) {
|
||||
runtime.BindQuadIndexBuffer(draw_state.topology, draw_state.vertex_buffer.first,
|
||||
draw_state.vertex_buffer.count);
|
||||
}
|
||||
} else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
|
||||
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
|
||||
if (draw_state.topology == Maxwell::PrimitiveTopology::Quads ||
|
||||
draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) {
|
||||
runtime.BindQuadIndexBuffer(draw_state.topology, draw_state.vertex_buffer.first,
|
||||
draw_state.vertex_buffer.count);
|
||||
}
|
||||
}
|
||||
BindHostVertexBuffers();
|
||||
@@ -433,6 +407,12 @@ void BufferCache<P>::SetComputeUniformBufferState(u32 mask,
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::UnbindGraphicsStorageBuffers(size_t stage) {
|
||||
if constexpr (requires { runtime.ShouldLimitDynamicStorageBuffers(); }) {
|
||||
if (runtime.ShouldLimitDynamicStorageBuffers()) {
|
||||
channel_state->total_graphics_storage_buffers -=
|
||||
static_cast<u32>(std::popcount(channel_state->enabled_storage_buffers[stage]));
|
||||
}
|
||||
}
|
||||
channel_state->enabled_storage_buffers[stage] = 0;
|
||||
channel_state->written_storage_buffers[stage] = 0;
|
||||
}
|
||||
@@ -440,8 +420,26 @@ void BufferCache<P>::UnbindGraphicsStorageBuffers(size_t stage) {
|
||||
template <class P>
|
||||
bool BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index,
|
||||
u32 cbuf_offset, bool is_written) {
|
||||
const bool already_enabled =
|
||||
((channel_state->enabled_storage_buffers[stage] >> ssbo_index) & 1U) != 0;
|
||||
if constexpr (requires { runtime.ShouldLimitDynamicStorageBuffers(); }) {
|
||||
if (runtime.ShouldLimitDynamicStorageBuffers() && !already_enabled) {
|
||||
const u32 max_bindings = runtime.GetMaxDynamicStorageBuffers();
|
||||
if (channel_state->total_graphics_storage_buffers >= max_bindings) {
|
||||
LOG_WARNING(HW_GPU,
|
||||
"Skipping graphics storage buffer {} due to driver limit {}",
|
||||
ssbo_index, max_bindings);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
channel_state->enabled_storage_buffers[stage] |= 1U << ssbo_index;
|
||||
channel_state->written_storage_buffers[stage] |= (is_written ? 1U : 0U) << ssbo_index;
|
||||
if constexpr (requires { runtime.ShouldLimitDynamicStorageBuffers(); }) {
|
||||
if (runtime.ShouldLimitDynamicStorageBuffers() && !already_enabled) {
|
||||
++channel_state->total_graphics_storage_buffers;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& cbufs = maxwell3d->state.shader_stages[stage];
|
||||
const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset;
|
||||
@@ -472,6 +470,12 @@ void BufferCache<P>::BindGraphicsTextureBuffer(size_t stage, size_t tbo_index, G
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::UnbindComputeStorageBuffers() {
|
||||
if constexpr (requires { runtime.ShouldLimitDynamicStorageBuffers(); }) {
|
||||
if (runtime.ShouldLimitDynamicStorageBuffers()) {
|
||||
channel_state->total_compute_storage_buffers -=
|
||||
static_cast<u32>(std::popcount(channel_state->enabled_compute_storage_buffers));
|
||||
}
|
||||
}
|
||||
channel_state->enabled_compute_storage_buffers = 0;
|
||||
channel_state->written_compute_storage_buffers = 0;
|
||||
channel_state->image_compute_texture_buffers = 0;
|
||||
@@ -485,8 +489,26 @@ void BufferCache<P>::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index,
|
||||
ssbo_index);
|
||||
return;
|
||||
}
|
||||
const bool already_enabled =
|
||||
((channel_state->enabled_compute_storage_buffers >> ssbo_index) & 1U) != 0;
|
||||
if constexpr (requires { runtime.ShouldLimitDynamicStorageBuffers(); }) {
|
||||
if (runtime.ShouldLimitDynamicStorageBuffers() && !already_enabled) {
|
||||
const u32 max_bindings = runtime.GetMaxDynamicStorageBuffers();
|
||||
if (channel_state->total_compute_storage_buffers >= max_bindings) {
|
||||
LOG_WARNING(HW_GPU,
|
||||
"Skipping compute storage buffer {} due to driver limit {}",
|
||||
ssbo_index, max_bindings);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
channel_state->enabled_compute_storage_buffers |= 1U << ssbo_index;
|
||||
channel_state->written_compute_storage_buffers |= (is_written ? 1U : 0U) << ssbo_index;
|
||||
if constexpr (requires { runtime.ShouldLimitDynamicStorageBuffers(); }) {
|
||||
if (runtime.ShouldLimitDynamicStorageBuffers() && !already_enabled) {
|
||||
++channel_state->total_compute_storage_buffers;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& launch_desc = kepler_compute->launch_description;
|
||||
if (((launch_desc.const_buffer_enable_mask >> cbuf_index) & 1) == 0) {
|
||||
@@ -715,44 +737,6 @@ void BufferCache<P>::BindHostIndexBuffer() {
|
||||
const u32 offset = buffer.Offset(channel_state->index_buffer.device_addr);
|
||||
const u32 size = channel_state->index_buffer.size;
|
||||
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
|
||||
if constexpr (!P::IS_OPENGL) {
|
||||
const auto polygon_mode = VideoCore::EffectivePolygonMode(maxwell3d->regs);
|
||||
const bool polygon_line =
|
||||
draw_state.topology == Maxwell::PrimitiveTopology::Polygon &&
|
||||
polygon_mode == Maxwell::PolygonMode::Line;
|
||||
if (polygon_line && draw_state.index_buffer.count > 1) {
|
||||
const u32 element_size = draw_state.index_buffer.FormatSizeInBytes();
|
||||
const size_t src_bytes = static_cast<size_t>(draw_state.index_buffer.count) * element_size;
|
||||
const size_t total_bytes = src_bytes + element_size;
|
||||
auto staging = runtime.UploadStagingBuffer(total_bytes);
|
||||
std::span<u8> dst_span{staging.mapped_span.data(), total_bytes};
|
||||
std::span<const u8> src_span;
|
||||
if (!draw_state.inline_index_draw_indexes.empty()) {
|
||||
const u8* const src =
|
||||
draw_state.inline_index_draw_indexes.data() +
|
||||
static_cast<size_t>(draw_state.index_buffer.first) * element_size;
|
||||
src_span = {src, src_bytes};
|
||||
} else if (const u8* const cpu_base =
|
||||
device_memory.GetPointer<u8>(channel_state->index_buffer.device_addr)) {
|
||||
const u8* const src = cpu_base +
|
||||
static_cast<size_t>(draw_state.index_buffer.first) * element_size;
|
||||
src_span = {src, src_bytes};
|
||||
} else {
|
||||
const DAddr src_addr =
|
||||
channel_state->index_buffer.device_addr +
|
||||
static_cast<DAddr>(draw_state.index_buffer.first) * element_size;
|
||||
device_memory.ReadBlockUnsafe(src_addr, dst_span.data(), src_bytes);
|
||||
src_span = {dst_span.data(), src_bytes};
|
||||
}
|
||||
Vulkan::LineLoop::CopyWithClosureRaw(dst_span, src_span, element_size);
|
||||
buffer.MarkUsage(offset, size);
|
||||
runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format,
|
||||
draw_state.index_buffer.first, draw_state.index_buffer.count + 1,
|
||||
staging.buffer, static_cast<u32>(staging.offset),
|
||||
static_cast<u32>(total_bytes));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!draw_state.inline_index_draw_indexes.empty()) [[unlikely]] {
|
||||
if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
|
||||
auto upload_staging = runtime.UploadStagingBuffer(size);
|
||||
@@ -857,9 +841,23 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||
const u32 size = (std::min)(binding.size, (*channel_state->uniform_buffer_sizes)[stage][index]);
|
||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID &&
|
||||
size <= channel_state->uniform_buffer_skip_cache_size &&
|
||||
!memory_tracker.IsRegionGpuModified(device_addr, size);
|
||||
const bool has_host_buffer = binding.buffer_id != NULL_BUFFER_ID;
|
||||
const u32 offset = has_host_buffer ? buffer.Offset(device_addr) : 0;
|
||||
const bool needs_alignment_stream = [&]() {
|
||||
if constexpr (IS_OPENGL) {
|
||||
return false;
|
||||
} else {
|
||||
if (!has_host_buffer) {
|
||||
return false;
|
||||
}
|
||||
const u32 alignment = runtime.GetUniformBufferAlignment();
|
||||
return alignment > 1 && (offset % alignment) != 0;
|
||||
}
|
||||
}();
|
||||
const bool use_fast_buffer = needs_alignment_stream ||
|
||||
(has_host_buffer &&
|
||||
size <= channel_state->uniform_buffer_skip_cache_size &&
|
||||
!memory_tracker.IsRegionGpuModified(device_addr, size));
|
||||
if (use_fast_buffer) {
|
||||
if constexpr (IS_OPENGL) {
|
||||
if (runtime.HasFastBufferSubData()) {
|
||||
@@ -898,7 +896,6 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||
if (!needs_bind) {
|
||||
return;
|
||||
}
|
||||
const u32 offset = buffer.Offset(device_addr);
|
||||
if constexpr (IS_OPENGL) {
|
||||
// Mark the index as dirty if offset doesn't match
|
||||
const bool is_copy_bind = offset != 0 && !runtime.SupportsNonZeroUniformOffset();
|
||||
@@ -1015,9 +1012,30 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
|
||||
TouchBuffer(buffer, binding.buffer_id);
|
||||
const u32 size =
|
||||
(std::min)(binding.size, (*channel_state->compute_uniform_buffer_sizes)[index]);
|
||||
const bool has_host_buffer = binding.buffer_id != NULL_BUFFER_ID;
|
||||
const u32 offset = has_host_buffer ? buffer.Offset(binding.device_addr) : 0;
|
||||
const bool needs_alignment_stream = [&]() {
|
||||
if constexpr (IS_OPENGL) {
|
||||
return false;
|
||||
} else {
|
||||
if (!has_host_buffer) {
|
||||
return false;
|
||||
}
|
||||
const u32 alignment = runtime.GetUniformBufferAlignment();
|
||||
return alignment > 1 && (offset % alignment) != 0;
|
||||
}
|
||||
}();
|
||||
if constexpr (!IS_OPENGL) {
|
||||
if (needs_alignment_stream) {
|
||||
const std::span<u8> span =
|
||||
runtime.BindMappedUniformBuffer(0, binding_index, size);
|
||||
device_memory.ReadBlockUnsafe(binding.device_addr, span.data(), size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SynchronizeBuffer(buffer, binding.device_addr, size);
|
||||
|
||||
const u32 offset = buffer.Offset(binding.device_addr);
|
||||
buffer.MarkUsage(offset, size);
|
||||
if constexpr (NEEDS_BIND_UNIFORM_INDEX) {
|
||||
runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@@ -132,6 +133,9 @@ public:
|
||||
u32 enabled_compute_storage_buffers = 0;
|
||||
u32 written_compute_storage_buffers = 0;
|
||||
|
||||
u32 total_graphics_storage_buffers = 0;
|
||||
u32 total_compute_storage_buffers = 0;
|
||||
|
||||
std::array<u32, NUM_STAGES> enabled_texture_buffers{};
|
||||
std::array<u32, NUM_STAGES> written_texture_buffers{};
|
||||
std::array<u32, NUM_STAGES> image_texture_buffers{};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -235,6 +238,9 @@ constexpr Table MakeViewTable() {
|
||||
EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA);
|
||||
EnableRange(view, VIEW_CLASS_ASTC_12x10_RGBA);
|
||||
EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA);
|
||||
Enable(view, PixelFormat::D24_UNORM_S8_UINT, PixelFormat::S8_UINT);
|
||||
Enable(view, PixelFormat::S8_UINT_D24_UNORM, PixelFormat::S8_UINT);
|
||||
Enable(view, PixelFormat::D32_FLOAT_S8_UINT, PixelFormat::S8_UINT);
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,90 +43,66 @@ void DmaPusher::DispatchCalls() {
|
||||
|
||||
bool DmaPusher::Step() {
|
||||
if (!ib_enable || dma_pushbuffer.empty()) {
|
||||
// pushbuffer empty and IB empty or nonexistent - nothing to do
|
||||
return false;
|
||||
}
|
||||
|
||||
CommandList& command_list{dma_pushbuffer.front()};
|
||||
CommandList& command_list = dma_pushbuffer.front();
|
||||
|
||||
ASSERT_OR_EXECUTE(
|
||||
command_list.command_lists.size() || command_list.prefetch_command_list.size(), {
|
||||
// Somehow the command_list is empty, in order to avoid a crash
|
||||
// We ignore it and assume its size is 0.
|
||||
dma_pushbuffer.pop();
|
||||
dma_pushbuffer_subindex = 0;
|
||||
return true;
|
||||
});
|
||||
const size_t prefetch_size = command_list.prefetch_command_list.size();
|
||||
const size_t command_list_size = command_list.command_lists.size();
|
||||
|
||||
if (command_list.prefetch_command_list.size()) {
|
||||
// Prefetched command list from nvdrv, used for things like synchronization
|
||||
ProcessCommands(VideoCommon::FixSmallVectorADL(command_list.prefetch_command_list));
|
||||
if (prefetch_size == 0 && command_list_size == 0) {
|
||||
dma_pushbuffer.pop();
|
||||
} else {
|
||||
const CommandListHeader command_list_header{
|
||||
command_list.command_lists[dma_pushbuffer_subindex++]};
|
||||
dma_pushbuffer_subindex = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (signal_sync) {
|
||||
std::unique_lock lk(sync_mutex);
|
||||
sync_cv.wait(lk, [this]() { return synced; });
|
||||
signal_sync = false;
|
||||
synced = false;
|
||||
}
|
||||
if (prefetch_size > 0) {
|
||||
ProcessCommands(command_list.prefetch_command_list);
|
||||
dma_pushbuffer.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
dma_state.dma_get = command_list_header.addr;
|
||||
auto& current_command = command_list.command_lists[dma_pushbuffer_subindex];
|
||||
const CommandListHeader& header = current_command;
|
||||
dma_state.dma_get = header.addr;
|
||||
|
||||
if (command_list_header.size == 0) {
|
||||
return true;
|
||||
}
|
||||
if (signal_sync && !synced) {
|
||||
std::unique_lock lk(sync_mutex);
|
||||
sync_cv.wait(lk, [this]() { return synced; });
|
||||
signal_sync = false;
|
||||
synced = false;
|
||||
}
|
||||
|
||||
// Push buffer non-empty, read a word
|
||||
if (dma_state.method >= MacroRegistersStart) {
|
||||
if (subchannels[dma_state.subchannel]) {
|
||||
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(
|
||||
dma_state.dma_get, command_list_header.size * sizeof(u32));
|
||||
}
|
||||
}
|
||||
if (header.size > 0 && dma_state.method >= MacroRegistersStart && subchannels[dma_state.subchannel]) {
|
||||
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, header.size * sizeof(u32));
|
||||
}
|
||||
|
||||
const auto safe_process = [&] {
|
||||
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader,
|
||||
Tegra::Memory::GuestMemoryFlags::SafeRead>
|
||||
headers(memory_manager, dma_state.dma_get, command_list_header.size,
|
||||
&command_headers);
|
||||
if (header.size > 0) {
|
||||
if (Settings::IsDMALevelDefault() ? (Settings::IsGPULevelMedium() || Settings::IsGPULevelHigh()) : Settings::IsDMALevelSafe()) {
|
||||
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::SafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers);
|
||||
ProcessCommands(headers);
|
||||
};
|
||||
|
||||
const auto unsafe_process = [&] {
|
||||
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader,
|
||||
Tegra::Memory::GuestMemoryFlags::UnsafeRead>
|
||||
headers(memory_manager, dma_state.dma_get, command_list_header.size,
|
||||
&command_headers);
|
||||
ProcessCommands(headers);
|
||||
};
|
||||
|
||||
const bool use_safe = Settings::IsDMALevelDefault() ? (Settings::IsGPULevelMedium() || Settings::IsGPULevelHigh()) : Settings::IsDMALevelSafe();
|
||||
|
||||
if (use_safe) {
|
||||
safe_process();
|
||||
} else {
|
||||
unsafe_process();
|
||||
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::UnsafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers);
|
||||
ProcessCommands(headers);
|
||||
}
|
||||
}
|
||||
|
||||
if (dma_pushbuffer_subindex >= command_list.command_lists.size()) {
|
||||
// We've gone through the current list, remove it from the queue
|
||||
dma_pushbuffer.pop();
|
||||
dma_pushbuffer_subindex = 0;
|
||||
} else if (command_list.command_lists[dma_pushbuffer_subindex].sync && Settings::values.sync_memory_operations.GetValue()) {
|
||||
signal_sync = true;
|
||||
}
|
||||
if (++dma_pushbuffer_subindex >= command_list_size) {
|
||||
dma_pushbuffer.pop();
|
||||
dma_pushbuffer_subindex = 0;
|
||||
} else {
|
||||
signal_sync = command_list.command_lists[dma_pushbuffer_subindex].sync && Settings::values.sync_memory_operations.GetValue();
|
||||
}
|
||||
|
||||
if (signal_sync) {
|
||||
rasterizer->SignalFence([this]() {
|
||||
if (signal_sync) {
|
||||
rasterizer->SignalFence([this]() {
|
||||
std::scoped_lock lk(sync_mutex);
|
||||
synced = true;
|
||||
sync_cv.notify_all();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,10 @@ public:
|
||||
uncommitted_operations.clear();
|
||||
}
|
||||
QueueFence(new_fence);
|
||||
//if (!new_fence->IsStubbed()) {
|
||||
// std::scoped_lock lock{texture_cache.mutex};
|
||||
// texture_cache.CommitPendingGpuAccesses(new_fence->WaitTick());
|
||||
//}
|
||||
fences.push(std::move(new_fence));
|
||||
if (should_flush) {
|
||||
rasterizer.FlushCommands();
|
||||
@@ -179,7 +183,7 @@ private:
|
||||
return;
|
||||
}
|
||||
}
|
||||
PopAsyncFlushes();
|
||||
PopAsyncFlushes(current_fence->WaitTick());
|
||||
auto operations = std::move(pending_operations.front());
|
||||
pending_operations.pop_front();
|
||||
for (auto& operation : operations) {
|
||||
@@ -214,7 +218,7 @@ private:
|
||||
if (!current_fence->IsStubbed()) {
|
||||
WaitFence(current_fence);
|
||||
}
|
||||
PopAsyncFlushes();
|
||||
PopAsyncFlushes(current_fence->WaitTick());
|
||||
for (auto& operation : current_operations) {
|
||||
operation();
|
||||
}
|
||||
@@ -237,10 +241,11 @@ private:
|
||||
query_cache.HasUncommittedFlushes();
|
||||
}
|
||||
|
||||
void PopAsyncFlushes() {
|
||||
void PopAsyncFlushes(u64 completed_tick) {
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
texture_cache.PopAsyncFlushes();
|
||||
texture_cache.CompleteGpuAccesses(completed_tick);
|
||||
buffer_cache.PopAsyncFlushes();
|
||||
}
|
||||
query_cache.PopAsyncFlushes();
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
inline Tegra::Engines::Maxwell3D::Regs::PolygonMode EffectivePolygonMode(
|
||||
const Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
const bool cull_enabled = regs.gl_cull_test_enabled != 0;
|
||||
const auto cull_face = regs.gl_cull_face;
|
||||
const bool cull_front = cull_enabled && (cull_face == Maxwell::CullFace::Front ||
|
||||
cull_face == Maxwell::CullFace::FrontAndBack);
|
||||
const bool cull_back = cull_enabled && (cull_face == Maxwell::CullFace::Back ||
|
||||
cull_face == Maxwell::CullFace::FrontAndBack);
|
||||
|
||||
const bool render_front = !cull_front;
|
||||
const bool render_back = !cull_back;
|
||||
|
||||
const auto front_mode = regs.polygon_mode_front;
|
||||
const auto back_mode = regs.polygon_mode_back;
|
||||
|
||||
if (render_front && render_back && front_mode != back_mode) {
|
||||
if (front_mode == Maxwell::PolygonMode::Line || back_mode == Maxwell::PolygonMode::Line) {
|
||||
return Maxwell::PolygonMode::Line;
|
||||
}
|
||||
if (front_mode == Maxwell::PolygonMode::Point || back_mode == Maxwell::PolygonMode::Point) {
|
||||
return Maxwell::PolygonMode::Point;
|
||||
}
|
||||
}
|
||||
|
||||
if (render_front) {
|
||||
return front_mode;
|
||||
}
|
||||
if (render_back) {
|
||||
return back_mode;
|
||||
}
|
||||
return front_mode;
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -211,6 +211,12 @@ void QueryCacheBase<Traits>::CounterClose(QueryType counter_type) {
|
||||
streamer->CloseCounter();
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
bool QueryCacheBase<Traits>::HasStreamer(QueryType counter_type) const {
|
||||
const size_t index = static_cast<size_t>(counter_type);
|
||||
return impl->streamers[index] != nullptr;
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void QueryCacheBase<Traits>::CounterReset(QueryType counter_type) {
|
||||
size_t index = static_cast<size_t>(counter_type);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -92,6 +95,8 @@ public:
|
||||
|
||||
void CounterReset(QueryType counter_type);
|
||||
|
||||
[[nodiscard]] bool HasStreamer(QueryType counter_type) const;
|
||||
|
||||
void CounterClose(QueryType counter_type);
|
||||
|
||||
void CounterReport(GPUVAddr addr, QueryType counter_type, QueryPropertiesFlags flags,
|
||||
|
||||
@@ -198,6 +198,10 @@ public:
|
||||
return device.CanReportMemoryUsage();
|
||||
}
|
||||
|
||||
u32 GetUniformBufferAlignment() const {
|
||||
return static_cast<u32>(device.GetUniformBufferAlignment());
|
||||
}
|
||||
|
||||
u32 GetStorageBufferAlignment() const {
|
||||
return static_cast<u32>(device.GetShaderStorageBufferAlignment());
|
||||
}
|
||||
|
||||
@@ -7,13 +7,50 @@
|
||||
#include <cstring>
|
||||
#include <bit>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include "common/cityhash.h"
|
||||
#include "common/settings.h" // for enum class Settings::ShaderBackend
|
||||
#include "video_core/renderer_opengl/gl_compute_pipeline.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_util.h"
|
||||
#include "video_core/surface.h"
|
||||
|
||||
namespace OpenGL {
|
||||
namespace {
|
||||
|
||||
std::optional<VideoCore::Surface::PixelFormatNumeric>
|
||||
NumericFromComponentType(Shader::SamplerComponentType component_type) {
|
||||
using VideoCore::Surface::PixelFormatNumeric;
|
||||
switch (component_type) {
|
||||
case Shader::SamplerComponentType::Float:
|
||||
return PixelFormatNumeric::Float;
|
||||
case Shader::SamplerComponentType::Sint:
|
||||
return PixelFormatNumeric::Sint;
|
||||
case Shader::SamplerComponentType::Uint:
|
||||
return PixelFormatNumeric::Uint;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
VideoCore::Surface::PixelFormat ResolveTexelBufferFormat(
|
||||
VideoCore::Surface::PixelFormat format, Shader::SamplerComponentType component_type) {
|
||||
const auto desired_numeric = NumericFromComponentType(component_type);
|
||||
if (!desired_numeric) {
|
||||
return format;
|
||||
}
|
||||
const auto current_numeric = VideoCore::Surface::GetPixelFormatNumericType(format);
|
||||
if (*desired_numeric == current_numeric) {
|
||||
return format;
|
||||
}
|
||||
if (const auto variant =
|
||||
VideoCore::Surface::FindPixelFormatVariant(format, *desired_numeric)) {
|
||||
return *variant;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
using Shader::ImageBufferDescriptor;
|
||||
using Tegra::Texture::TexturePair;
|
||||
@@ -174,8 +211,12 @@ void ComputePipeline::Configure() {
|
||||
is_written = desc.is_written;
|
||||
}
|
||||
ImageView& image_view{texture_cache.GetImageView(views[texbuf_index].id)};
|
||||
auto buffer_format = image_view.format;
|
||||
if constexpr (!is_image) {
|
||||
buffer_format = ResolveTexelBufferFormat(buffer_format, desc.component_type);
|
||||
}
|
||||
buffer_cache.BindComputeTextureBuffer(texbuf_index, image_view.GpuAddr(),
|
||||
image_view.BufferSize(), image_view.format,
|
||||
image_view.BufferSize(), buffer_format,
|
||||
is_written, is_image);
|
||||
++texbuf_index;
|
||||
}
|
||||
@@ -205,7 +246,8 @@ void ComputePipeline::Configure() {
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
|
||||
textures[texture_binding] = image_view.Handle(desc.type);
|
||||
textures[texture_binding] =
|
||||
image_view.SampledView(desc.type, desc.component_type);
|
||||
if (texture_cache.IsRescaling(image_view)) {
|
||||
texture_scaling_mask |= 1u << texture_binding;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -25,6 +28,10 @@ public:
|
||||
|
||||
void Wait();
|
||||
|
||||
[[nodiscard]] u64 WaitTick() const noexcept {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
OGLSync sync_object;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <bit>
|
||||
@@ -18,6 +19,7 @@
|
||||
#include "video_core/renderer_opengl/gl_shader_util.h"
|
||||
#include "video_core/renderer_opengl/gl_state_tracker.h"
|
||||
#include "video_core/shader_notify.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/texture_cache/texture_cache.h"
|
||||
|
||||
#if defined(_MSC_VER) && defined(NDEBUG)
|
||||
@@ -39,6 +41,38 @@ using VideoCommon::ImageId;
|
||||
constexpr u32 MAX_TEXTURES = 64;
|
||||
constexpr u32 MAX_IMAGES = 8;
|
||||
|
||||
std::optional<VideoCore::Surface::PixelFormatNumeric>
|
||||
NumericFromComponentType(Shader::SamplerComponentType component_type) {
|
||||
using VideoCore::Surface::PixelFormatNumeric;
|
||||
switch (component_type) {
|
||||
case Shader::SamplerComponentType::Float:
|
||||
return PixelFormatNumeric::Float;
|
||||
case Shader::SamplerComponentType::Sint:
|
||||
return PixelFormatNumeric::Sint;
|
||||
case Shader::SamplerComponentType::Uint:
|
||||
return PixelFormatNumeric::Uint;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
VideoCore::Surface::PixelFormat ResolveTexelBufferFormat(
|
||||
VideoCore::Surface::PixelFormat format, Shader::SamplerComponentType component_type) {
|
||||
const auto desired_numeric = NumericFromComponentType(component_type);
|
||||
if (!desired_numeric) {
|
||||
return format;
|
||||
}
|
||||
const auto current_numeric = VideoCore::Surface::GetPixelFormatNumericType(format);
|
||||
if (*desired_numeric == current_numeric) {
|
||||
return format;
|
||||
}
|
||||
if (const auto variant =
|
||||
VideoCore::Surface::FindPixelFormatVariant(format, *desired_numeric)) {
|
||||
return *variant;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
GLenum Stage(size_t stage_index) {
|
||||
switch (stage_index) {
|
||||
case 0:
|
||||
@@ -397,8 +431,12 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
is_written = desc.is_written;
|
||||
}
|
||||
ImageView& image_view{texture_cache.GetImageView(texture_buffer_it->id)};
|
||||
auto buffer_format = image_view.format;
|
||||
if constexpr (!is_image) {
|
||||
buffer_format = ResolveTexelBufferFormat(buffer_format, desc.component_type);
|
||||
}
|
||||
buffer_cache.BindGraphicsTextureBuffer(stage, index, image_view.GpuAddr(),
|
||||
image_view.BufferSize(), image_view.format,
|
||||
image_view.BufferSize(), buffer_format,
|
||||
is_written, is_image);
|
||||
++index;
|
||||
++texture_buffer_it;
|
||||
@@ -483,7 +521,8 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
|
||||
textures[texture_binding] = image_view.Handle(desc.type);
|
||||
textures[texture_binding] =
|
||||
image_view.SampledView(desc.type, desc.component_type);
|
||||
if (texture_cache.IsRescaling(image_view)) {
|
||||
texture_scaling_mask |= 1u << stage_texture_binding;
|
||||
}
|
||||
|
||||
@@ -220,6 +220,7 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
.support_gl_sparse_textures = device.HasSparseTexture2(),
|
||||
.support_gl_derivative_control = device.HasDerivativeControl(),
|
||||
.support_geometry_streams = true,
|
||||
.warp_stage_support_mask = 0xFFFFFFFFu,
|
||||
|
||||
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyLargerThanGuest(),
|
||||
|
||||
|
||||
@@ -692,6 +692,15 @@ bool TextureCacheRuntime::HasNativeASTC() const noexcept {
|
||||
return device.HasASTC();
|
||||
}
|
||||
|
||||
bool TextureCacheRuntime::SupportsLinearFilter(VideoCore::Surface::PixelFormat format) const noexcept {
|
||||
using VideoCore::Surface::GetFormatType;
|
||||
using VideoCore::Surface::IsPixelFormatInteger;
|
||||
if (IsPixelFormatInteger(format)) {
|
||||
return false;
|
||||
}
|
||||
return GetFormatType(format) == VideoCore::Surface::SurfaceType::ColorTexture;
|
||||
}
|
||||
|
||||
Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_, GPUVAddr gpu_addr_,
|
||||
VAddr cpu_addr_)
|
||||
: VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), runtime{&runtime_} {
|
||||
@@ -1229,6 +1238,13 @@ GLuint ImageView::StorageView(Shader::TextureType texture_type, Shader::ImageFor
|
||||
return view;
|
||||
}
|
||||
|
||||
GLuint ImageView::SampledView(Shader::TextureType view_type,
|
||||
Shader::SamplerComponentType /*component_type*/) {
|
||||
// OpenGL swizzles already configure depth/stencil selection per TIC entry,
|
||||
// so fall back to the default view handle.
|
||||
return Handle(view_type);
|
||||
}
|
||||
|
||||
void ImageView::SetupView(Shader::TextureType view_type) {
|
||||
views[static_cast<size_t>(view_type)] = MakeView(view_type, internal_format);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -13,6 +16,7 @@
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_staging_buffer_pool.h"
|
||||
#include "video_core/renderer_opengl/util_shaders.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/texture_cache/image_view_base.h"
|
||||
#include "video_core/texture_cache/texture_cache_base.h"
|
||||
|
||||
@@ -129,6 +133,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SupportsLinearFilter(VideoCore::Surface::PixelFormat format) const noexcept;
|
||||
|
||||
bool HasBrokenTextureViewFormats() const noexcept {
|
||||
return has_broken_texture_view_formats;
|
||||
}
|
||||
@@ -137,6 +143,8 @@ public:
|
||||
|
||||
void TickFrame() {}
|
||||
|
||||
void WaitForGpuTick(u64) {}
|
||||
|
||||
StateTracker& GetStateTracker() {
|
||||
return state_tracker;
|
||||
}
|
||||
@@ -264,6 +272,9 @@ public:
|
||||
[[nodiscard]] GLuint StorageView(Shader::TextureType texture_type,
|
||||
Shader::ImageFormat image_format);
|
||||
|
||||
[[nodiscard]] GLuint SampledView(Shader::TextureType view_type,
|
||||
Shader::SamplerComponentType component_type);
|
||||
|
||||
[[nodiscard]] GLuint Handle(Shader::TextureType handle_type) const noexcept {
|
||||
return views[static_cast<size_t>(handle_type)];
|
||||
}
|
||||
|
||||
@@ -525,18 +525,24 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
|
||||
nullptr, PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(float) * 4>))),
|
||||
full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
|
||||
blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
|
||||
blit_depth_stencil_frag(BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)),
|
||||
blit_depth_stencil_frag(device.IsExtShaderStencilExportSupported()
|
||||
? BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)
|
||||
: vk::ShaderModule{}),
|
||||
clear_color_vert(BuildShader(device, VULKAN_COLOR_CLEAR_VERT_SPV)),
|
||||
clear_color_frag(BuildShader(device, VULKAN_COLOR_CLEAR_FRAG_SPV)),
|
||||
clear_stencil_frag(BuildShader(device, VULKAN_DEPTHSTENCIL_CLEAR_FRAG_SPV)),
|
||||
convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
|
||||
convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
|
||||
convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
|
||||
convert_abgr8_to_d24s8_frag(device.IsExtShaderStencilExportSupported()
|
||||
? BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)
|
||||
: vk::ShaderModule{}),
|
||||
convert_abgr8_to_d32f_frag(BuildShader(device, CONVERT_ABGR8_TO_D32F_FRAG_SPV)),
|
||||
convert_d32f_to_abgr8_frag(BuildShader(device, CONVERT_D32F_TO_ABGR8_FRAG_SPV)),
|
||||
convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
|
||||
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
|
||||
convert_abgr8_srgb_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_SRGB_TO_D24S8_FRAG_SPV)),
|
||||
convert_abgr8_srgb_to_d24s8_frag(device.IsExtShaderStencilExportSupported()
|
||||
? BuildShader(device, CONVERT_ABGR8_SRGB_TO_D24S8_FRAG_SPV)
|
||||
: vk::ShaderModule{}),
|
||||
convert_rgba_to_bgra_frag(BuildShader(device, CONVERT_RGBA8_TO_BGRA8_FRAG_SPV)),
|
||||
convert_yuv420_to_rgb_comp(BuildShader(device, CONVERT_YUV420_TO_RGB_COMP_SPV)),
|
||||
convert_rgb_to_yuv420_comp(BuildShader(device, CONVERT_RGB_TO_YUV420_COMP_SPV)),
|
||||
@@ -667,6 +673,11 @@ void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer,
|
||||
|
||||
void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view) {
|
||||
if (!device.IsExtShaderStencilExportSupported()) {
|
||||
// Shader requires VK_EXT_shader_stencil_export which is not available
|
||||
LOG_WARNING(Render_Vulkan, "ConvertABGR8ToD24S8 requires shader_stencil_export, skipping");
|
||||
return;
|
||||
}
|
||||
ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(),
|
||||
convert_abgr8_to_d24s8_frag);
|
||||
Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view);
|
||||
@@ -702,6 +713,11 @@ void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer,
|
||||
|
||||
void BlitImageHelper::ConvertABGR8SRGBToD24S8(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view) {
|
||||
if (!device.IsExtShaderStencilExportSupported()) {
|
||||
// Shader requires VK_EXT_shader_stencil_export which is not available
|
||||
LOG_WARNING(Render_Vulkan, "ConvertABGR8SRGBToD24S8 requires shader_stencil_export, skipping");
|
||||
return;
|
||||
}
|
||||
ConvertPipelineDepthTargetEx(convert_abgr8_srgb_to_d24s8_pipeline,
|
||||
dst_framebuffer->RenderPass(),
|
||||
convert_abgr8_srgb_to_d24s8_frag);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "video_core/engines/draw_manager.h"
|
||||
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
|
||||
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
||||
#include "video_core/polygon_mode_utils.h"
|
||||
|
||||
namespace Vulkan {
|
||||
namespace {
|
||||
@@ -60,13 +59,13 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
|
||||
raw1 = 0;
|
||||
extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0);
|
||||
extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0);
|
||||
extended_dynamic_state_2_extra.Assign(features.has_extended_dynamic_state_2_extra ? 1 : 0);
|
||||
extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 1 : 0);
|
||||
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0);
|
||||
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0);
|
||||
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
|
||||
xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
|
||||
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
|
||||
polygon_mode.Assign(PackPolygonMode(VideoCore::EffectivePolygonMode(regs)));
|
||||
polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
|
||||
tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value()));
|
||||
tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value()));
|
||||
tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() ==
|
||||
@@ -158,7 +157,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
|
||||
return static_cast<u16>(array.stride.Value());
|
||||
});
|
||||
}
|
||||
if (!extended_dynamic_state_2_extra) {
|
||||
if (!extended_dynamic_state_2_logic_op) {
|
||||
dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2);
|
||||
}
|
||||
if (!extended_dynamic_state_3_blend) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -20,9 +23,11 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
struct DynamicFeatures {
|
||||
bool has_extended_dynamic_state;
|
||||
bool has_extended_dynamic_state_2;
|
||||
bool has_extended_dynamic_state_2_extra;
|
||||
bool has_extended_dynamic_state_2_logic_op;
|
||||
bool has_extended_dynamic_state_2_patch_control_points;
|
||||
bool has_extended_dynamic_state_3_blend;
|
||||
bool has_extended_dynamic_state_3_enables;
|
||||
bool has_dual_source_blend;
|
||||
bool has_dynamic_vertex_input;
|
||||
};
|
||||
|
||||
@@ -186,7 +191,7 @@ struct FixedPipelineState {
|
||||
u32 raw1;
|
||||
BitField<0, 1, u32> extended_dynamic_state;
|
||||
BitField<1, 1, u32> extended_dynamic_state_2;
|
||||
BitField<2, 1, u32> extended_dynamic_state_2_extra;
|
||||
BitField<2, 1, u32> extended_dynamic_state_2_logic_op;
|
||||
BitField<3, 1, u32> extended_dynamic_state_3_blend;
|
||||
BitField<4, 1, u32> extended_dynamic_state_3_enables;
|
||||
BitField<5, 1, u32> dynamic_vertex_input;
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <span>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Vulkan::LineLoop {
|
||||
|
||||
inline void CopyWithClosureRaw(std::span<u8> dst, std::span<const u8> src, size_t element_size) {
|
||||
ASSERT_MSG(dst.size() == src.size() + element_size, "Invalid line loop copy sizes");
|
||||
if (src.empty()) {
|
||||
if (!dst.empty()) {
|
||||
std::fill(dst.begin(), dst.end(), u8{0});
|
||||
}
|
||||
return;
|
||||
}
|
||||
std::memcpy(dst.data(), src.data(), src.size());
|
||||
std::memcpy(dst.data() + src.size(), src.data(), element_size);
|
||||
}
|
||||
|
||||
inline void GenerateSequentialWithClosureRaw(std::span<u8> dst, size_t element_size,
|
||||
u64 start_value = 0) {
|
||||
if (dst.empty()) {
|
||||
return;
|
||||
}
|
||||
const size_t last = dst.size() - element_size;
|
||||
size_t offset = 0;
|
||||
u64 value = start_value;
|
||||
while (offset < last) {
|
||||
std::memcpy(dst.data() + offset, &value, element_size);
|
||||
offset += element_size;
|
||||
++value;
|
||||
}
|
||||
std::memcpy(dst.data() + offset, &start_value, element_size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void CopyWithClosure(std::span<T> dst, std::span<const T> src) {
|
||||
ASSERT_MSG(dst.size() == src.size() + 1, "Invalid destination size for line loop copy");
|
||||
if (src.empty()) {
|
||||
if (!dst.empty()) {
|
||||
dst.front() = {};
|
||||
}
|
||||
return;
|
||||
}
|
||||
std::copy(src.begin(), src.end(), dst.begin());
|
||||
dst.back() = src.front();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void GenerateSequentialWithClosure(std::span<T> dst, T start_value = {}) {
|
||||
if (dst.empty()) {
|
||||
return;
|
||||
}
|
||||
const size_t last = dst.size() - 1;
|
||||
for (size_t i = 0; i < last; ++i) {
|
||||
dst[i] = static_cast<T>(start_value + static_cast<T>(i));
|
||||
}
|
||||
dst.back() = start_value;
|
||||
}
|
||||
|
||||
} // namespace Vulkan::LineLoop
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -168,7 +165,7 @@ struct FormatTuple {
|
||||
{VK_FORMAT_R16G16_SINT, Attachable | Storage}, // R16G16_SINT
|
||||
{VK_FORMAT_R16G16_SNORM, Attachable | Storage}, // R16G16_SNORM
|
||||
{VK_FORMAT_R32G32B32_SFLOAT}, // R32G32B32_FLOAT
|
||||
{VK_FORMAT_A8B8G8R8_SRGB_PACK32, Attachable}, // A8B8G8R8_SRGB
|
||||
{VK_FORMAT_A8B8G8R8_SRGB_PACK32, Attachable | Storage}, // A8B8G8R8_SRGB
|
||||
{VK_FORMAT_R8G8_UNORM, Attachable | Storage}, // R8G8_UNORM
|
||||
{VK_FORMAT_R8G8_SNORM, Attachable | Storage}, // R8G8_SNORM
|
||||
{VK_FORMAT_R8G8_SINT, Attachable | Storage}, // R8G8_SINT
|
||||
@@ -180,7 +177,7 @@ struct FormatTuple {
|
||||
{VK_FORMAT_ASTC_8x8_UNORM_BLOCK}, // ASTC_2D_8X8_UNORM
|
||||
{VK_FORMAT_ASTC_8x5_UNORM_BLOCK}, // ASTC_2D_8X5_UNORM
|
||||
{VK_FORMAT_ASTC_5x4_UNORM_BLOCK}, // ASTC_2D_5X4_UNORM
|
||||
{VK_FORMAT_B8G8R8A8_SRGB, Attachable}, // B8G8R8A8_SRGB
|
||||
{VK_FORMAT_B8G8R8A8_SRGB, Attachable | Storage}, // B8G8R8A8_SRGB
|
||||
{VK_FORMAT_BC1_RGBA_SRGB_BLOCK}, // BC1_RGBA_SRGB
|
||||
{VK_FORMAT_BC2_SRGB_BLOCK}, // BC2_SRGB
|
||||
{VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB
|
||||
@@ -326,9 +323,44 @@ VkShaderStageFlagBits ShaderStage(Shader::Stage stage) {
|
||||
}
|
||||
|
||||
VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device,
|
||||
Maxwell::PrimitiveTopology topology,
|
||||
Maxwell::PolygonMode polygon_mode) {
|
||||
return detail::PrimitiveTopologyNoDevice(topology, polygon_mode);
|
||||
Maxwell::PrimitiveTopology topology) {
|
||||
switch (topology) {
|
||||
case Maxwell::PrimitiveTopology::Points:
|
||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
case Maxwell::PrimitiveTopology::Lines:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
case Maxwell::PrimitiveTopology::LineLoop:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
case Maxwell::PrimitiveTopology::LineStrip:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
||||
case Maxwell::PrimitiveTopology::Triangles:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
case Maxwell::PrimitiveTopology::TriangleStrip:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
case Maxwell::PrimitiveTopology::TriangleFan:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
|
||||
case Maxwell::PrimitiveTopology::LinesAdjacency:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
|
||||
case Maxwell::PrimitiveTopology::LineStripAdjacency:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;
|
||||
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;
|
||||
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY;
|
||||
case Maxwell::PrimitiveTopology::Quads:
|
||||
case Maxwell::PrimitiveTopology::QuadStrip:
|
||||
// TODO: Use VK_PRIMITIVE_TOPOLOGY_QUAD_LIST_EXT/VK_PRIMITIVE_TOPOLOGY_QUAD_STRIP_EXT
|
||||
// whenever it releases
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
case Maxwell::PrimitiveTopology::Patches:
|
||||
return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
|
||||
case Maxwell::PrimitiveTopology::Polygon:
|
||||
LOG_WARNING(Render_Vulkan, "Draw mode is Polygon with a polygon mode of lines should be a "
|
||||
"single body and not a bunch of triangles.");
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented topology={}", topology);
|
||||
return {};
|
||||
}
|
||||
|
||||
VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,52 +15,6 @@ namespace Vulkan::MaxwellToVK {
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
using PixelFormat = VideoCore::Surface::PixelFormat;
|
||||
|
||||
namespace detail {
|
||||
constexpr VkPrimitiveTopology PrimitiveTopologyNoDevice(Maxwell::PrimitiveTopology topology,
|
||||
Maxwell::PolygonMode polygon_mode) {
|
||||
switch (topology) {
|
||||
case Maxwell::PrimitiveTopology::Points:
|
||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
case Maxwell::PrimitiveTopology::Lines:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
case Maxwell::PrimitiveTopology::LineLoop:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
||||
case Maxwell::PrimitiveTopology::LineStrip:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
||||
case Maxwell::PrimitiveTopology::Triangles:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
case Maxwell::PrimitiveTopology::TriangleStrip:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
case Maxwell::PrimitiveTopology::TriangleFan:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
|
||||
case Maxwell::PrimitiveTopology::LinesAdjacency:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
|
||||
case Maxwell::PrimitiveTopology::LineStripAdjacency:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;
|
||||
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;
|
||||
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY;
|
||||
case Maxwell::PrimitiveTopology::Quads:
|
||||
case Maxwell::PrimitiveTopology::QuadStrip:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
case Maxwell::PrimitiveTopology::Patches:
|
||||
return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
|
||||
case Maxwell::PrimitiveTopology::Polygon:
|
||||
switch (polygon_mode) {
|
||||
case Maxwell::PolygonMode::Fill:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
|
||||
case Maxwell::PolygonMode::Line:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
||||
case Maxwell::PolygonMode::Point:
|
||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
namespace Sampler {
|
||||
|
||||
VkFilter Filter(Tegra::Texture::TextureFilter filter);
|
||||
@@ -95,8 +46,7 @@ struct FormatInfo {
|
||||
|
||||
VkShaderStageFlagBits ShaderStage(Shader::Stage stage);
|
||||
|
||||
VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology,
|
||||
Maxwell::PolygonMode polygon_mode);
|
||||
VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology);
|
||||
|
||||
VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
|
||||
Maxwell::VertexAttribute::Size size);
|
||||
|
||||
@@ -189,12 +189,16 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
|
||||
const VideoCommon::ImageViewId image_view_id{(views++)->id};
|
||||
const VideoCommon::SamplerId sampler_id{*(samplers++)};
|
||||
ImageView& image_view{texture_cache.GetImageView(image_view_id)};
|
||||
const VkImageView vk_image_view{image_view.Handle(desc.type)};
|
||||
const VkImageView vk_image_view{
|
||||
image_view.SampledView(desc.type, desc.component_type)};
|
||||
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
||||
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
|
||||
!image_view.SupportsAnisotropy()};
|
||||
const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
|
||||
: sampler.Handle()};
|
||||
const bool supports_linear_filter{
|
||||
texture_cache.SupportsLinearFilter(image_view.format)};
|
||||
const bool supports_depth_compare_sampling{
|
||||
image_view.SupportsDepthCompareSampling()};
|
||||
const VkSampler vk_sampler{
|
||||
sampler.SelectHandle(supports_linear_filter, image_view.SupportsAnisotropy(),
|
||||
supports_depth_compare_sampling)};
|
||||
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
|
||||
rescaling.PushTexture(texture_cache.IsRescaling(image_view));
|
||||
}
|
||||
|
||||
@@ -280,7 +280,6 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i
|
||||
Tegra::Texture::UnswizzleTexture(
|
||||
mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
|
||||
bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
|
||||
buffer.Flush(); // Ensure host writes are visible before the GPU copy.
|
||||
}
|
||||
|
||||
const VkBufferImageCopy copy{
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
@@ -333,6 +334,13 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
|
||||
staging_pool{staging_pool_}, guest_descriptor_queue{guest_descriptor_queue_},
|
||||
quad_index_pass(device, scheduler, descriptor_pool, staging_pool,
|
||||
compute_pass_descriptor_queue) {
|
||||
const VkDriverIdKHR driver_id = device.GetDriverID();
|
||||
limit_dynamic_storage_buffers = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
|
||||
driver_id == VK_DRIVER_ID_MESA_TURNIP ||
|
||||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
|
||||
if (limit_dynamic_storage_buffers) {
|
||||
max_dynamic_storage_buffers = device.GetMaxDescriptorSetStorageBuffersDynamic();
|
||||
}
|
||||
if (device.GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY) {
|
||||
// TODO: FixMe: Uint8Pass compute shader does not build on some Qualcomm drivers.
|
||||
uint8_pass = std::make_unique<Uint8Pass>(device, scheduler, descriptor_pool, staging_pool,
|
||||
@@ -408,6 +416,10 @@ bool BufferCacheRuntime::CanReportMemoryUsage() const {
|
||||
return device.CanReportMemoryUsage();
|
||||
}
|
||||
|
||||
u32 BufferCacheRuntime::GetUniformBufferAlignment() const {
|
||||
return static_cast<u32>(device.GetUniformBufferAlignment());
|
||||
}
|
||||
|
||||
u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
|
||||
return static_cast<u32>(device.GetStorageBufferAlignment());
|
||||
}
|
||||
@@ -583,7 +595,15 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
|
||||
if (index >= device.GetMaxVertexInputBindings()) {
|
||||
return;
|
||||
}
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) {
|
||||
ReserveNullBuffer();
|
||||
buffer = *null_buffer;
|
||||
offset = 0;
|
||||
size = std::numeric_limits<u32>::max();
|
||||
}
|
||||
// Use BindVertexBuffers2EXT only if EDS1 is supported AND VIDS is not active
|
||||
// When VIDS is active, the pipeline doesn't declare VERTEX_INPUT_BINDING_STRIDE as dynamic
|
||||
if (device.IsExtExtendedDynamicStateSupported() && !device.IsExtVertexInputDynamicStateSupported()) {
|
||||
scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
|
||||
const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
|
||||
const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE;
|
||||
@@ -623,7 +643,8 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
|
||||
if (binding_count == 0) {
|
||||
return;
|
||||
}
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
// Use BindVertexBuffers2EXT only if EDS1 is supported AND VIDS is not active
|
||||
if (device.IsExtExtendedDynamicStateSupported() && !device.IsExtVertexInputDynamicStateSupported()) {
|
||||
scheduler.Record([bindings_ = std::move(bindings),
|
||||
buffer_handles_ = std::move(buffer_handles),
|
||||
binding_count](vk::CommandBuffer cmdbuf) {
|
||||
@@ -680,27 +701,50 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::ReserveNullBuffer() {
|
||||
const VkBufferUsageFlags expected_usage = NullBufferUsageFlags();
|
||||
if (null_buffer && null_buffer_usage_flags != expected_usage) {
|
||||
RefreshNullBuffer();
|
||||
}
|
||||
if (!null_buffer) {
|
||||
null_buffer = CreateNullBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
VkBufferUsageFlags BufferCacheRuntime::NullBufferUsageFlags() const {
|
||||
VkBufferUsageFlags usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
||||
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
|
||||
if (device.IsExtTransformFeedbackSupported()) {
|
||||
usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
void BufferCacheRuntime::RefreshNullBuffer() {
|
||||
if (!null_buffer) {
|
||||
return;
|
||||
}
|
||||
scheduler.Finish();
|
||||
null_buffer.reset();
|
||||
null_buffer = CreateNullBuffer();
|
||||
}
|
||||
|
||||
vk::Buffer BufferCacheRuntime::CreateNullBuffer() {
|
||||
VkBufferCreateInfo create_info{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.size = 4,
|
||||
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
|
||||
.usage = NullBufferUsageFlags(),
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
};
|
||||
if (device.IsExtTransformFeedbackSupported()) {
|
||||
create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
|
||||
}
|
||||
vk::Buffer ret = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
|
||||
null_buffer_usage_flags = create_info.usage;
|
||||
if (device.HasDebuggingToolAttached()) {
|
||||
ret.SetObjectNameEXT("Null buffer");
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "video_core/buffer_cache/buffer_cache_base.h"
|
||||
#include "video_core/buffer_cache/memory_tracker_base.h"
|
||||
#include "video_core/buffer_cache/usage_tracker.h"
|
||||
@@ -94,6 +96,8 @@ public:
|
||||
|
||||
bool CanReportMemoryUsage() const;
|
||||
|
||||
u32 GetUniformBufferAlignment() const;
|
||||
|
||||
u32 GetStorageBufferAlignment() const;
|
||||
|
||||
[[nodiscard]] StagingBufferRef UploadStagingBuffer(size_t size);
|
||||
@@ -127,6 +131,9 @@ public:
|
||||
|
||||
void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
||||
|
||||
/// Forces destruction and recreation of the shared null buffer so new usage flags take effect.
|
||||
void RefreshNullBuffer();
|
||||
|
||||
std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t /*stage*/,
|
||||
[[maybe_unused]] u32 /*binding_index*/,
|
||||
u32 size) {
|
||||
@@ -155,6 +162,14 @@ public:
|
||||
guest_descriptor_queue.AddTexelBuffer(buffer.View(offset, size, format));
|
||||
}
|
||||
|
||||
bool ShouldLimitDynamicStorageBuffers() const {
|
||||
return limit_dynamic_storage_buffers;
|
||||
}
|
||||
|
||||
u32 GetMaxDynamicStorageBuffers() const {
|
||||
return max_dynamic_storage_buffers;
|
||||
}
|
||||
|
||||
private:
|
||||
void BindBuffer(VkBuffer buffer, u32 offset, u32 size) {
|
||||
guest_descriptor_queue.AddBuffer(buffer, offset, size);
|
||||
@@ -162,6 +177,7 @@ private:
|
||||
|
||||
void ReserveNullBuffer();
|
||||
vk::Buffer CreateNullBuffer();
|
||||
VkBufferUsageFlags NullBufferUsageFlags() const;
|
||||
|
||||
struct UniformRing {
|
||||
static constexpr size_t NUM_FRAMES = 3;
|
||||
@@ -191,9 +207,13 @@ private:
|
||||
std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer;
|
||||
|
||||
vk::Buffer null_buffer;
|
||||
VkBufferUsageFlags null_buffer_usage_flags = 0;
|
||||
|
||||
std::unique_ptr<Uint8Pass> uint8_pass;
|
||||
QuadIndexedPass quad_index_pass;
|
||||
|
||||
bool limit_dynamic_storage_buffers = false;
|
||||
u32 max_dynamic_storage_buffers = std::numeric_limits<u32>::max();
|
||||
};
|
||||
|
||||
struct BufferCacheParams {
|
||||
|
||||
@@ -418,6 +418,9 @@ ConditionalRenderingResolvePass::ConditionalRenderingResolvePass(
|
||||
|
||||
void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_buffer,
|
||||
u32 src_offset, bool compare_to_zero) {
|
||||
if (!device.IsExtConditionalRendering()) {
|
||||
return;
|
||||
}
|
||||
const size_t compare_size = compare_to_zero ? 8 : 24;
|
||||
|
||||
compute_pass_descriptor_queue.Acquire();
|
||||
@@ -448,7 +451,7 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||
cmdbuf.Dispatch(1, 1, 1);
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT, 0, write_barrier);
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, write_barrier);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -459,10 +462,14 @@ QueriesPrefixScanPass::QueriesPrefixScanPass(
|
||||
device_, descriptor_pool_, QUERIES_SCAN_DESCRIPTOR_SET_BINDINGS,
|
||||
QUERIES_SCAN_DESCRIPTOR_UPDATE_TEMPLATE, QUERIES_SCAN_BANK_INFO,
|
||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(QueriesPrefixScanPushConstants)>,
|
||||
device_.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_BASIC_BIT) &&
|
||||
device_.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) &&
|
||||
device_.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_SHUFFLE_BIT) &&
|
||||
device_.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT)
|
||||
device_.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_BASIC_BIT,
|
||||
VK_SHADER_STAGE_COMPUTE_BIT) &&
|
||||
device_.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_ARITHMETIC_BIT,
|
||||
VK_SHADER_STAGE_COMPUTE_BIT) &&
|
||||
device_.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_SHUFFLE_BIT,
|
||||
VK_SHADER_STAGE_COMPUTE_BIT) &&
|
||||
device_.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT,
|
||||
VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
? std::span<const u32>(QUERIES_PREFIX_SCAN_SUM_COMP_SPV)
|
||||
: std::span<const u32>(QUERIES_PREFIX_SCAN_SUM_NOSUBGROUPS_COMP_SPV)),
|
||||
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||
@@ -470,6 +477,14 @@ QueriesPrefixScanPass::QueriesPrefixScanPass(
|
||||
void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffer,
|
||||
VkBuffer src_buffer, size_t number_of_sums,
|
||||
size_t min_accumulation_limit, size_t max_accumulation_limit) {
|
||||
constexpr VkAccessFlags BASE_DST_ACCESS = VK_ACCESS_SHADER_READ_BIT |
|
||||
VK_ACCESS_TRANSFER_READ_BIT |
|
||||
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
|
||||
VK_ACCESS_INDIRECT_COMMAND_READ_BIT |
|
||||
VK_ACCESS_INDEX_READ_BIT |
|
||||
VK_ACCESS_UNIFORM_READ_BIT;
|
||||
const VkAccessFlags conditional_access =
|
||||
device.IsExtConditionalRendering() ? VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT : 0;
|
||||
size_t current_runs = number_of_sums;
|
||||
size_t offset = 0;
|
||||
while (current_runs != 0) {
|
||||
@@ -486,22 +501,18 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([this, descriptor_data, min_accumulation_limit, max_accumulation_limit,
|
||||
runs_to_do, used_offset](vk::CommandBuffer cmdbuf) {
|
||||
runs_to_do, used_offset, conditional_access](vk::CommandBuffer cmdbuf) {
|
||||
static constexpr VkMemoryBarrier read_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
|
||||
};
|
||||
static constexpr VkMemoryBarrier write_barrier{
|
||||
const VkMemoryBarrier write_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
|
||||
VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
|
||||
VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_INDEX_READ_BIT |
|
||||
VK_ACCESS_UNIFORM_READ_BIT |
|
||||
VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT,
|
||||
.dstAccessMask = BASE_DST_ACCESS | conditional_access,
|
||||
};
|
||||
const QueriesPrefixScanPushConstants uniforms{
|
||||
.min_accumulation_base = static_cast<u32>(min_accumulation_limit),
|
||||
@@ -519,8 +530,7 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe
|
||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms);
|
||||
cmdbuf.Dispatch(1, 1, 1);
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT, 0,
|
||||
write_barrier);
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, write_barrier);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,49 @@
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/shader_notify.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/texture_cache/texture_cache.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
#include <optional>
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
using Shader::ImageBufferDescriptor;
|
||||
using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET;
|
||||
using Tegra::Texture::TexturePair;
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
using VideoCore::Surface::PixelFormatNumeric;
|
||||
|
||||
static std::optional<PixelFormatNumeric> NumericFromComponentType(
|
||||
Shader::SamplerComponentType component_type) {
|
||||
switch (component_type) {
|
||||
case Shader::SamplerComponentType::Float:
|
||||
return PixelFormatNumeric::Float;
|
||||
case Shader::SamplerComponentType::Sint:
|
||||
return PixelFormatNumeric::Sint;
|
||||
case Shader::SamplerComponentType::Uint:
|
||||
return PixelFormatNumeric::Uint;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
static PixelFormat ResolveTexelBufferFormat(PixelFormat format,
|
||||
Shader::SamplerComponentType component_type) {
|
||||
const auto desired_numeric = NumericFromComponentType(component_type);
|
||||
if (!desired_numeric) {
|
||||
return format;
|
||||
}
|
||||
const auto current_numeric = VideoCore::Surface::GetPixelFormatNumericType(format);
|
||||
if (*desired_numeric == current_numeric) {
|
||||
return format;
|
||||
}
|
||||
if (const auto variant = VideoCore::Surface::FindPixelFormatVariant(format, *desired_numeric)) {
|
||||
return *variant;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
ComputePipeline::ComputePipeline(const Device& device_, vk::PipelineCache& pipeline_cache_,
|
||||
DescriptorPool& descriptor_pool,
|
||||
@@ -182,8 +217,12 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
|
||||
is_written = desc.is_written;
|
||||
}
|
||||
ImageView& image_view = texture_cache.GetImageView(views[index].id);
|
||||
VideoCore::Surface::PixelFormat buffer_format = image_view.format;
|
||||
if constexpr (!is_image) {
|
||||
buffer_format = ResolveTexelBufferFormat(buffer_format, desc.component_type);
|
||||
}
|
||||
buffer_cache.BindComputeTextureBuffer(index, image_view.GpuAddr(),
|
||||
image_view.BufferSize(), image_view.format,
|
||||
image_view.BufferSize(), buffer_format,
|
||||
is_written, is_image);
|
||||
++index;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,10 @@ public:
|
||||
|
||||
void Wait();
|
||||
|
||||
[[nodiscard]] u64 WaitTick() const noexcept {
|
||||
return wait_tick;
|
||||
}
|
||||
|
||||
private:
|
||||
Scheduler& scheduler;
|
||||
u64 wait_tick = 0;
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <iostream>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
@@ -23,9 +24,10 @@
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/polygon_mode_utils.h"
|
||||
#include "video_core/shader_notify.h"
|
||||
#include "video_core/texture_cache/samples_helper.h"
|
||||
#include "video_core/texture_cache/texture_cache.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
|
||||
#if defined(_MSC_VER) && defined(NDEBUG)
|
||||
@@ -46,10 +48,83 @@ using Tegra::Texture::TexturePair;
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
using VideoCore::Surface::PixelFormatFromDepthFormat;
|
||||
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
|
||||
using VideoCore::Surface::PixelFormatNumeric;
|
||||
|
||||
constexpr size_t NUM_STAGES = Maxwell::MaxShaderStage;
|
||||
constexpr size_t MAX_IMAGE_ELEMENTS = 64;
|
||||
|
||||
std::optional<PixelFormatNumeric> NumericFromComponentType(
|
||||
Shader::SamplerComponentType component_type) {
|
||||
switch (component_type) {
|
||||
case Shader::SamplerComponentType::Float:
|
||||
return PixelFormatNumeric::Float;
|
||||
case Shader::SamplerComponentType::Sint:
|
||||
return PixelFormatNumeric::Sint;
|
||||
case Shader::SamplerComponentType::Uint:
|
||||
return PixelFormatNumeric::Uint;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
PixelFormat ResolveTexelBufferFormat(PixelFormat format,
|
||||
Shader::SamplerComponentType component_type) {
|
||||
const auto desired_numeric = NumericFromComponentType(component_type);
|
||||
if (!desired_numeric) {
|
||||
return format;
|
||||
}
|
||||
const auto current_numeric = VideoCore::Surface::GetPixelFormatNumericType(format);
|
||||
if (*desired_numeric == current_numeric) {
|
||||
return format;
|
||||
}
|
||||
if (const auto variant = VideoCore::Surface::FindPixelFormatVariant(format, *desired_numeric)) {
|
||||
return *variant;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
bool UsesDualSourceFactor(Maxwell::Blend::Factor factor) {
|
||||
switch (factor) {
|
||||
case Maxwell::Blend::Factor::Source1Color_D3D:
|
||||
case Maxwell::Blend::Factor::Source1Color_GL:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color_GL:
|
||||
case Maxwell::Blend::Factor::Source1Alpha_D3D:
|
||||
case Maxwell::Blend::Factor::Source1Alpha_GL:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Maxwell::Blend::Factor FallbackDualSourceFactor(Maxwell::Blend::Factor factor) {
|
||||
switch (factor) {
|
||||
case Maxwell::Blend::Factor::Source1Color_D3D:
|
||||
case Maxwell::Blend::Factor::Source1Color_GL:
|
||||
return Maxwell::Blend::Factor::SourceColor_D3D;
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color_GL:
|
||||
return Maxwell::Blend::Factor::OneMinusSourceColor_D3D;
|
||||
case Maxwell::Blend::Factor::Source1Alpha_D3D:
|
||||
case Maxwell::Blend::Factor::Source1Alpha_GL:
|
||||
return Maxwell::Blend::Factor::SourceAlpha_D3D;
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha_D3D:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha_GL:
|
||||
return Maxwell::Blend::Factor::OneMinusSourceAlpha_D3D;
|
||||
default:
|
||||
return factor;
|
||||
}
|
||||
}
|
||||
|
||||
bool AttachmentUsesDualSource(const FixedPipelineState::BlendingAttachment& blend) {
|
||||
return UsesDualSourceFactor(blend.SourceRGBFactor()) ||
|
||||
UsesDualSourceFactor(blend.DestRGBFactor()) ||
|
||||
UsesDualSourceFactor(blend.SourceAlphaFactor()) ||
|
||||
UsesDualSourceFactor(blend.DestAlphaFactor());
|
||||
}
|
||||
|
||||
DescriptorLayoutBuilder MakeBuilder(const Device& device, std::span<const Shader::Info> infos) {
|
||||
DescriptorLayoutBuilder builder{device};
|
||||
for (size_t index = 0; index < infos.size(); ++index) {
|
||||
@@ -265,6 +340,7 @@ GraphicsPipeline::GraphicsPipeline(
|
||||
std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin());
|
||||
num_textures += Shader::NumDescriptors(info->texture_descriptors);
|
||||
}
|
||||
fragment_has_color0_output = stage_infos[NUM_STAGES - 1].stores_frag_color[0];
|
||||
auto func{[this, shader_notify, &render_pass_cache, &descriptor_pool, pipeline_statistics] {
|
||||
DescriptorLayoutBuilder builder{MakeBuilder(device, stage_infos)};
|
||||
uses_push_descriptor = builder.CanUsePushDescriptor();
|
||||
@@ -418,8 +494,12 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
||||
is_written = desc.is_written;
|
||||
}
|
||||
ImageView& image_view{texture_cache.GetImageView(texture_buffer_it->id)};
|
||||
VideoCore::Surface::PixelFormat buffer_format = image_view.format;
|
||||
if constexpr (!is_image) {
|
||||
buffer_format = ResolveTexelBufferFormat(buffer_format, desc.component_type);
|
||||
}
|
||||
buffer_cache.BindGraphicsTextureBuffer(stage, index, image_view.GpuAddr(),
|
||||
image_view.BufferSize(), image_view.format,
|
||||
image_view.BufferSize(), buffer_format,
|
||||
is_written, is_image);
|
||||
++index;
|
||||
++texture_buffer_it;
|
||||
@@ -616,10 +696,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
vertex_input_ci.pNext = &input_divisor_ci;
|
||||
}
|
||||
const bool has_tess_stages = spv_modules[1] || spv_modules[2];
|
||||
const auto polygon_mode =
|
||||
FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode.Value());
|
||||
auto input_assembly_topology =
|
||||
MaxwellToVK::PrimitiveTopology(device, key.state.topology, polygon_mode);
|
||||
auto input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, key.state.topology);
|
||||
if (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) {
|
||||
if (!has_tess_stages) {
|
||||
LOG_WARNING(Render_Vulkan, "Patch topology used without tessellation, using points");
|
||||
@@ -634,33 +711,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
input_assembly_topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
|
||||
}
|
||||
}
|
||||
if (key.state.topology == Maxwell::PrimitiveTopology::Polygon) {
|
||||
const auto polygon_mode_name = [polygon_mode]() -> std::string_view {
|
||||
switch (polygon_mode) {
|
||||
case Maxwell::PolygonMode::Fill:
|
||||
return "Fill";
|
||||
case Maxwell::PolygonMode::Line:
|
||||
return "Line";
|
||||
case Maxwell::PolygonMode::Point:
|
||||
return "Point";
|
||||
}
|
||||
return "Unknown";
|
||||
}();
|
||||
const auto vk_topology_name = [input_assembly_topology]() -> std::string_view {
|
||||
switch (input_assembly_topology) {
|
||||
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
|
||||
return "TriangleFan";
|
||||
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
|
||||
return "LineStrip";
|
||||
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
|
||||
return "PointList";
|
||||
default:
|
||||
return "Unexpected";
|
||||
}
|
||||
}();
|
||||
LOG_DEBUG(Render_Vulkan, "Polygon primitive in {} mode mapped to {}", polygon_mode_name,
|
||||
vk_topology_name);
|
||||
}
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -734,13 +784,18 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
.lineWidth = 1.0f,
|
||||
// TODO(alekpop): Transfer from regs
|
||||
};
|
||||
const bool smooth_lines_supported =
|
||||
device.IsExtLineRasterizationSupported() && device.SupportsSmoothLines();
|
||||
const bool stippled_lines_supported =
|
||||
device.IsExtLineRasterizationSupported() && device.SupportsStippledRectangularLines();
|
||||
VkPipelineRasterizationLineStateCreateInfoEXT line_state{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.lineRasterizationMode = key.state.smooth_lines != 0
|
||||
.lineRasterizationMode = key.state.smooth_lines != 0 && smooth_lines_supported
|
||||
? VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT
|
||||
: VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT,
|
||||
.stippledLineEnable = dynamic.line_stipple_enable ? VK_TRUE : VK_FALSE,
|
||||
.stippledLineEnable =
|
||||
(dynamic.line_stipple_enable && stippled_lines_supported) ? VK_TRUE : VK_FALSE,
|
||||
.lineStippleFactor = key.state.line_stipple_factor,
|
||||
.lineStipplePattern = static_cast<uint16_t>(key.state.line_stipple_pattern),
|
||||
};
|
||||
@@ -771,17 +826,25 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex);
|
||||
}
|
||||
|
||||
const VkPipelineMultisampleStateCreateInfo multisample_ci{
|
||||
const bool supports_alpha_output = fragment_has_color0_output;
|
||||
const bool alpha_to_one_supported = device.SupportsAlphaToOne();
|
||||
const auto msaa_mode = key.state.msaa_mode.Value();
|
||||
const VkSampleCountFlagBits vk_samples = MaxwellToVK::MsaaMode(msaa_mode);
|
||||
VkPipelineMultisampleStateCreateInfo multisample_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.rasterizationSamples = MaxwellToVK::MsaaMode(key.state.msaa_mode),
|
||||
.rasterizationSamples = vk_samples,
|
||||
.sampleShadingEnable = Settings::values.sample_shading.GetValue() ? VK_TRUE : VK_FALSE,
|
||||
.minSampleShading = static_cast<float>(Settings::values.sample_shading_fraction.GetValue()) / 100.0f,
|
||||
.pSampleMask = nullptr,
|
||||
.alphaToCoverageEnable = key.state.alpha_to_coverage_enabled != 0 ? VK_TRUE : VK_FALSE,
|
||||
.alphaToOneEnable = key.state.alpha_to_one_enabled != 0 ? VK_TRUE : VK_FALSE,
|
||||
.alphaToCoverageEnable =
|
||||
supports_alpha_output && key.state.alpha_to_coverage_enabled != 0 ? VK_TRUE : VK_FALSE,
|
||||
.alphaToOneEnable = supports_alpha_output && alpha_to_one_supported &&
|
||||
key.state.alpha_to_one_enabled != 0 ? VK_TRUE : VK_FALSE,
|
||||
};
|
||||
|
||||
|
||||
const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -803,6 +866,12 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
}
|
||||
static_vector<VkPipelineColorBlendAttachmentState, Maxwell::NumRenderTargets> cb_attachments;
|
||||
const size_t num_attachments{NumAttachments(key.state)};
|
||||
const bool supports_dual_source_blend = device.SupportsDualSourceBlend();
|
||||
const u32 max_dual_source_attachments = supports_dual_source_blend
|
||||
? device.MaxFragmentDualSrcAttachments()
|
||||
: 0;
|
||||
u32 granted_dual_source_attachments = 0;
|
||||
bool logged_dual_source_warning = false;
|
||||
for (size_t index = 0; index < num_attachments; ++index) {
|
||||
static constexpr std::array mask_table{
|
||||
VK_COLOR_COMPONENT_R_BIT,
|
||||
@@ -816,13 +885,30 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
for (size_t i = 0; i < mask_table.size(); ++i) {
|
||||
write_mask |= mask[i] ? mask_table[i] : 0;
|
||||
}
|
||||
const bool attachment_uses_dual_source = AttachmentUsesDualSource(blend);
|
||||
const bool allow_dual_source = attachment_uses_dual_source && supports_dual_source_blend &&
|
||||
granted_dual_source_attachments < max_dual_source_attachments;
|
||||
if (allow_dual_source) {
|
||||
++granted_dual_source_attachments;
|
||||
} else if (attachment_uses_dual_source && !logged_dual_source_warning) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Dual-source blend factors exceed device limit (maxFragmentDualSrcAttachments={}), falling back to single-source factors",
|
||||
max_dual_source_attachments);
|
||||
logged_dual_source_warning = true;
|
||||
}
|
||||
const auto sanitize_factor = [&](Maxwell::Blend::Factor factor) {
|
||||
if (allow_dual_source || !UsesDualSourceFactor(factor)) {
|
||||
return factor;
|
||||
}
|
||||
return FallbackDualSourceFactor(factor);
|
||||
};
|
||||
cb_attachments.push_back({
|
||||
.blendEnable = blend.enable != 0,
|
||||
.srcColorBlendFactor = MaxwellToVK::BlendFactor(blend.SourceRGBFactor()),
|
||||
.dstColorBlendFactor = MaxwellToVK::BlendFactor(blend.DestRGBFactor()),
|
||||
.srcColorBlendFactor = MaxwellToVK::BlendFactor(sanitize_factor(blend.SourceRGBFactor())),
|
||||
.dstColorBlendFactor = MaxwellToVK::BlendFactor(sanitize_factor(blend.DestRGBFactor())),
|
||||
.colorBlendOp = MaxwellToVK::BlendEquation(blend.EquationRGB()),
|
||||
.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.SourceAlphaFactor()),
|
||||
.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.DestAlphaFactor()),
|
||||
.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(sanitize_factor(blend.SourceAlphaFactor())),
|
||||
.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(sanitize_factor(blend.DestAlphaFactor())),
|
||||
.alphaBlendOp = MaxwellToVK::BlendEquation(blend.EquationAlpha()),
|
||||
.colorWriteMask = write_mask,
|
||||
});
|
||||
@@ -838,14 +924,25 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
.blendConstants = {}
|
||||
};
|
||||
static_vector<VkDynamicState, 34> dynamic_states{
|
||||
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
|
||||
VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
|
||||
VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
|
||||
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR,
|
||||
VK_DYNAMIC_STATE_DEPTH_BIAS,
|
||||
VK_DYNAMIC_STATE_LINE_WIDTH,
|
||||
};
|
||||
|
||||
if (device.UsesAdvancedCoreDynamicState()) {
|
||||
static constexpr std::array core_dynamic_states{
|
||||
VK_DYNAMIC_STATE_BLEND_CONSTANTS,
|
||||
VK_DYNAMIC_STATE_DEPTH_BOUNDS,
|
||||
VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
|
||||
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK,
|
||||
VK_DYNAMIC_STATE_STENCIL_REFERENCE,
|
||||
};
|
||||
dynamic_states.insert(dynamic_states.end(), core_dynamic_states.begin(),
|
||||
core_dynamic_states.end());
|
||||
}
|
||||
if (key.state.extended_dynamic_state) {
|
||||
std::vector<VkDynamicState> extended{
|
||||
static constexpr std::array extended{
|
||||
VK_DYNAMIC_STATE_CULL_MODE_EXT,
|
||||
VK_DYNAMIC_STATE_FRONT_FACE_EXT,
|
||||
VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT,
|
||||
@@ -855,51 +952,68 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
||||
VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_STENCIL_OP_EXT,
|
||||
};
|
||||
if (!device.IsExtVertexInputDynamicStateSupported()) {
|
||||
extended.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT);
|
||||
}
|
||||
if (key.state.dynamic_vertex_input) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
|
||||
}
|
||||
dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
|
||||
if (key.state.extended_dynamic_state_2) {
|
||||
static constexpr std::array extended2{
|
||||
VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT,
|
||||
};
|
||||
dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end());
|
||||
|
||||
// VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT is part of EDS1
|
||||
// Only use it if VIDS is not active (VIDS replaces it with full vertex input control)
|
||||
if (!key.state.dynamic_vertex_input) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT);
|
||||
}
|
||||
if (key.state.extended_dynamic_state_2_extra) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
|
||||
}
|
||||
|
||||
// VK_DYNAMIC_STATE_VERTEX_INPUT_EXT (VIDS) - Independent from EDS
|
||||
// Provides full dynamic vertex input control, replaces VERTEX_INPUT_BINDING_STRIDE
|
||||
if (key.state.dynamic_vertex_input) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
|
||||
}
|
||||
|
||||
// EDS2 - Core (3 states)
|
||||
if (key.state.extended_dynamic_state_2) {
|
||||
static constexpr std::array extended2{
|
||||
VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT,
|
||||
};
|
||||
dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end());
|
||||
}
|
||||
|
||||
// EDS2 - LogicOp (granular)
|
||||
if (key.state.extended_dynamic_state_2_logic_op) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
|
||||
}
|
||||
|
||||
// EDS3 - Blending (composite: 3 states)
|
||||
if (key.state.extended_dynamic_state_3_blend) {
|
||||
static constexpr std::array extended3{
|
||||
VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT,
|
||||
VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT,
|
||||
};
|
||||
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
|
||||
}
|
||||
|
||||
// EDS3 - Enables (composite: per-feature)
|
||||
if (key.state.extended_dynamic_state_3_enables) {
|
||||
if (device.SupportsDynamicState3DepthClampEnable()) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT);
|
||||
}
|
||||
if (key.state.extended_dynamic_state_3_blend) {
|
||||
static constexpr std::array extended3{
|
||||
VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT,
|
||||
VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT,
|
||||
|
||||
// VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT,
|
||||
};
|
||||
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
|
||||
if (device.SupportsDynamicState3LogicOpEnable()) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT);
|
||||
}
|
||||
if (key.state.extended_dynamic_state_3_enables) {
|
||||
static constexpr std::array extended3{
|
||||
VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT,
|
||||
|
||||
// additional state3 extensions
|
||||
VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT,
|
||||
|
||||
VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT,
|
||||
|
||||
VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT,
|
||||
};
|
||||
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
|
||||
if (device.SupportsDynamicState3LineRasterizationMode()) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT);
|
||||
}
|
||||
if (device.SupportsDynamicState3ConservativeRasterizationMode()) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT);
|
||||
}
|
||||
if (device.SupportsDynamicState3LineStippleEnable()) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT);
|
||||
}
|
||||
if (device.SupportsDynamicState3AlphaToCoverageEnable()) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT);
|
||||
}
|
||||
if (device.SupportsDynamicState3AlphaToOneEnable()) {
|
||||
dynamic_states.push_back(VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +82,17 @@ public:
|
||||
const std::array<const Shader::Info*, NUM_STAGES>& infos);
|
||||
|
||||
bool HasDynamicVertexInput() const noexcept { return key.state.dynamic_vertex_input; }
|
||||
bool SupportsAlphaToCoverage() const noexcept {
|
||||
return fragment_has_color0_output;
|
||||
}
|
||||
|
||||
bool SupportsAlphaToOne() const noexcept {
|
||||
return fragment_has_color0_output;
|
||||
}
|
||||
|
||||
bool UsesExtendedDynamicState() const noexcept {
|
||||
return key.state.extended_dynamic_state != 0;
|
||||
}
|
||||
GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete;
|
||||
GraphicsPipeline(GraphicsPipeline&&) noexcept = delete;
|
||||
|
||||
@@ -149,6 +160,7 @@ private:
|
||||
std::array<u32, 5> enabled_uniform_buffer_masks{};
|
||||
VideoCommon::UniformBufferSizes uniform_buffer_sizes{};
|
||||
u32 num_textures{};
|
||||
bool fragment_has_color0_output{};
|
||||
|
||||
vk::DescriptorSetLayout descriptor_set_layout;
|
||||
DescriptorAllocator descriptor_allocator;
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/shader_cache.h"
|
||||
#include "video_core/shader_environment.h"
|
||||
#include "video_core/shader_notify.h"
|
||||
@@ -105,6 +106,41 @@ Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp compariso
|
||||
return {};
|
||||
}
|
||||
|
||||
Shader::AttributeType RenderTargetAttributeType(Tegra::RenderTargetFormat format) {
|
||||
if (format == Tegra::RenderTargetFormat::NONE) {
|
||||
return Shader::AttributeType::Float;
|
||||
}
|
||||
const auto pixel_format{
|
||||
VideoCore::Surface::PixelFormatFromRenderTargetFormat(format)};
|
||||
if (!VideoCore::Surface::IsPixelFormatInteger(pixel_format)) {
|
||||
return Shader::AttributeType::Float;
|
||||
}
|
||||
if (VideoCore::Surface::IsPixelFormatSignedInteger(pixel_format)) {
|
||||
return Shader::AttributeType::SignedInt;
|
||||
}
|
||||
return Shader::AttributeType::UnsignedInt;
|
||||
}
|
||||
|
||||
VkShaderStageFlagBits StageToVkStage(Shader::Stage stage) {
|
||||
switch (stage) {
|
||||
case Shader::Stage::VertexA:
|
||||
case Shader::Stage::VertexB:
|
||||
return VK_SHADER_STAGE_VERTEX_BIT;
|
||||
case Shader::Stage::TessellationControl:
|
||||
return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
|
||||
case Shader::Stage::TessellationEval:
|
||||
return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
|
||||
case Shader::Stage::Geometry:
|
||||
return VK_SHADER_STAGE_GEOMETRY_BIT;
|
||||
case Shader::Stage::Fragment:
|
||||
return VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
case Shader::Stage::Compute:
|
||||
return VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
default:
|
||||
return VK_SHADER_STAGE_VERTEX_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribute& attr) {
|
||||
if (attr.enabled == 0) {
|
||||
return Shader::AttributeType::Disabled;
|
||||
@@ -229,6 +265,10 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
|
||||
info.alpha_test_func = MaxwellToCompareFunction(
|
||||
key.state.UnpackComparisonOp(key.state.alpha_test_func.Value()));
|
||||
info.alpha_test_reference = std::bit_cast<float>(key.state.alpha_test_ref);
|
||||
for (size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
|
||||
const auto format = static_cast<Tegra::RenderTargetFormat>(key.state.color_formats[index]);
|
||||
info.color_output_types[index] = RenderTargetAttributeType(format);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -269,8 +309,8 @@ size_t GetTotalPipelineWorkers() {
|
||||
const size_t max_core_threads =
|
||||
std::max<size_t>(static_cast<size_t>(std::thread::hardware_concurrency()), 2ULL) - 1ULL;
|
||||
#ifdef ANDROID
|
||||
// Leave at least a few cores free in android
|
||||
constexpr size_t free_cores = 3ULL;
|
||||
// Leave at least one core free on Android to reduce thermal pressure.
|
||||
constexpr size_t free_cores = 1ULL;
|
||||
if (max_core_threads <= free_cores) {
|
||||
return 1ULL;
|
||||
}
|
||||
@@ -317,6 +357,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
"VkPipelineBuilder"),
|
||||
serialization_thread(1, "VkPipelineSerialization") {
|
||||
const auto& float_control{device.FloatControlProperties()};
|
||||
const bool float_controls_supported{device.IsKhrShaderFloatControlsSupported()};
|
||||
const VkDriverId driver_id{device.GetDriverID()};
|
||||
profile = Shader::Profile{
|
||||
.supported_spirv = device.SupportedSpirvVersion(),
|
||||
@@ -326,20 +367,24 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
.support_int16 = device.IsShaderInt16Supported(),
|
||||
.support_int64 = device.IsShaderInt64Supported(),
|
||||
.support_vertex_instance_id = false,
|
||||
.support_float_controls = device.IsKhrShaderFloatControlsSupported(),
|
||||
.support_separate_denorm_behavior =
|
||||
.support_float_controls = float_controls_supported,
|
||||
.support_separate_denorm_behavior = float_controls_supported &&
|
||||
float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
|
||||
.support_separate_rounding_mode =
|
||||
.support_separate_rounding_mode = float_controls_supported &&
|
||||
float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
|
||||
.support_fp16_denorm_preserve = float_control.shaderDenormPreserveFloat16 != VK_FALSE,
|
||||
.support_fp32_denorm_preserve = float_control.shaderDenormPreserveFloat32 != VK_FALSE,
|
||||
.support_fp16_denorm_flush = float_control.shaderDenormFlushToZeroFloat16 != VK_FALSE,
|
||||
.support_fp32_denorm_flush = float_control.shaderDenormFlushToZeroFloat32 != VK_FALSE,
|
||||
.support_fp16_signed_zero_nan_preserve =
|
||||
.support_fp16_denorm_preserve = float_controls_supported &&
|
||||
float_control.shaderDenormPreserveFloat16 != VK_FALSE,
|
||||
.support_fp32_denorm_preserve = float_controls_supported &&
|
||||
float_control.shaderDenormPreserveFloat32 != VK_FALSE,
|
||||
.support_fp16_denorm_flush = float_controls_supported &&
|
||||
float_control.shaderDenormFlushToZeroFloat16 != VK_FALSE,
|
||||
.support_fp32_denorm_flush = float_controls_supported &&
|
||||
float_control.shaderDenormFlushToZeroFloat32 != VK_FALSE,
|
||||
.support_fp16_signed_zero_nan_preserve = float_controls_supported &&
|
||||
float_control.shaderSignedZeroInfNanPreserveFloat16 != VK_FALSE,
|
||||
.support_fp32_signed_zero_nan_preserve =
|
||||
.support_fp32_signed_zero_nan_preserve = float_controls_supported &&
|
||||
float_control.shaderSignedZeroInfNanPreserveFloat32 != VK_FALSE,
|
||||
.support_fp64_signed_zero_nan_preserve =
|
||||
.support_fp64_signed_zero_nan_preserve = float_controls_supported &&
|
||||
float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE,
|
||||
.support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(),
|
||||
.support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
|
||||
@@ -395,6 +440,27 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
.support_conditional_barrier = device.SupportsConditionalBarriers(),
|
||||
};
|
||||
|
||||
profile.warp_stage_support_mask = 0;
|
||||
static constexpr std::array kAllStages{
|
||||
Shader::Stage::VertexA, Shader::Stage::VertexB,
|
||||
Shader::Stage::TessellationControl, Shader::Stage::TessellationEval,
|
||||
Shader::Stage::Geometry, Shader::Stage::Fragment,
|
||||
Shader::Stage::Compute,
|
||||
};
|
||||
for (const auto stage : kAllStages) {
|
||||
const auto vk_stage = StageToVkStage(stage);
|
||||
if (device.SupportsWarpIntrinsics(vk_stage)) {
|
||||
profile.warp_stage_support_mask |= 1u << static_cast<u32>(stage);
|
||||
}
|
||||
}
|
||||
profile.support_vote = profile.warp_stage_support_mask != 0;
|
||||
|
||||
if (!profile.SupportsWarpIntrinsics(Shader::Stage::Fragment)) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Fragment shaders lack subgroup support on this driver; warp intrinsics will be "
|
||||
"approximated and visual artifacts may remain");
|
||||
}
|
||||
|
||||
if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) {
|
||||
LOG_WARNING(Render_Vulkan, "maxVertexInputAttributes is too low: {} < {}",
|
||||
device.GetMaxVertexInputAttributes(), Maxwell::NumVertexAttributes);
|
||||
@@ -404,14 +470,40 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays);
|
||||
}
|
||||
|
||||
dynamic_features = DynamicFeatures{
|
||||
.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(),
|
||||
.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported(),
|
||||
.has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported(),
|
||||
.has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported(),
|
||||
.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(),
|
||||
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
|
||||
};
|
||||
LOG_INFO(Render_Vulkan, "DynamicState setting value: {}", Settings::values.dyna_state.GetValue());
|
||||
|
||||
dynamic_features = {};
|
||||
|
||||
// User granularity enforced in vulkan_device.cpp switch statement:
|
||||
// Level 0: Core Dynamic States only
|
||||
// Level 1: Core + EDS1
|
||||
// Level 2: Core + EDS1 + EDS2 (accumulative)
|
||||
// Level 3: Core + EDS1 + EDS2 + EDS3 (accumulative)
|
||||
// Here we only verify if extensions were successfully loaded by the device
|
||||
|
||||
dynamic_features.has_extended_dynamic_state =
|
||||
device.IsExtExtendedDynamicStateSupported();
|
||||
|
||||
dynamic_features.has_extended_dynamic_state_2 =
|
||||
device.IsExtExtendedDynamicState2Supported();
|
||||
dynamic_features.has_extended_dynamic_state_2_logic_op =
|
||||
device.IsExtExtendedDynamicState2ExtrasSupported();
|
||||
dynamic_features.has_extended_dynamic_state_2_patch_control_points = false;
|
||||
|
||||
dynamic_features.has_extended_dynamic_state_3_blend =
|
||||
device.IsExtExtendedDynamicState3BlendingSupported();
|
||||
dynamic_features.has_dual_source_blend = device.SupportsDualSourceBlend();
|
||||
if (!dynamic_features.has_dual_source_blend) {
|
||||
LOG_WARNING(Render_Vulkan, "Dual-source blending unsupported, disabling dynamic blend");
|
||||
dynamic_features.has_extended_dynamic_state_3_blend = false;
|
||||
}
|
||||
dynamic_features.has_extended_dynamic_state_3_enables =
|
||||
device.IsExtExtendedDynamicState3EnablesSupported();
|
||||
|
||||
// VIDS: Independent toggle (not affected by dyna_state levels)
|
||||
dynamic_features.has_dynamic_vertex_input =
|
||||
device.IsExtVertexInputDynamicStateSupported() &&
|
||||
Settings::values.vertex_input_dynamic_state.GetValue();
|
||||
}
|
||||
|
||||
PipelineCache::~PipelineCache() {
|
||||
@@ -421,6 +513,13 @@ PipelineCache::~PipelineCache() {
|
||||
}
|
||||
}
|
||||
|
||||
void PipelineCache::DrainPendingBuilds() {
|
||||
if (!device.HasBrokenParallelShaderCompiling()) {
|
||||
return;
|
||||
}
|
||||
workers.WaitForRequests();
|
||||
}
|
||||
|
||||
GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
|
||||
|
||||
if (!RefreshStages(graphics_key.unique_hashes)) {
|
||||
@@ -451,12 +550,17 @@ ComputePipeline* PipelineCache::CurrentComputePipeline() {
|
||||
.shared_memory_size = qmd.shared_alloc,
|
||||
.workgroup_size{qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z},
|
||||
};
|
||||
const auto [pair, is_new]{compute_cache.try_emplace(key)};
|
||||
const auto [pair, inserted]{compute_cache.try_emplace(key)};
|
||||
auto& pipeline{pair->second};
|
||||
if (!is_new) {
|
||||
return pipeline.get();
|
||||
if (!pipeline) {
|
||||
auto [slot, should_build] = AcquireComputeBuildSlot(key);
|
||||
if (!should_build) {
|
||||
WaitForBuildCompletion(slot);
|
||||
} else {
|
||||
pipeline = CreateComputePipeline(key, shader);
|
||||
ReleaseComputeBuildSlot(key, slot);
|
||||
}
|
||||
}
|
||||
pipeline = CreateComputePipeline(key, shader);
|
||||
return pipeline.get();
|
||||
}
|
||||
|
||||
@@ -516,8 +620,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
dynamic_features.has_extended_dynamic_state ||
|
||||
(key.state.extended_dynamic_state_2 != 0) !=
|
||||
dynamic_features.has_extended_dynamic_state_2 ||
|
||||
(key.state.extended_dynamic_state_2_extra != 0) !=
|
||||
dynamic_features.has_extended_dynamic_state_2_extra ||
|
||||
(key.state.extended_dynamic_state_2_logic_op != 0) !=
|
||||
dynamic_features.has_extended_dynamic_state_2_logic_op ||
|
||||
(key.state.extended_dynamic_state_3_blend != 0) !=
|
||||
dynamic_features.has_extended_dynamic_state_3_blend ||
|
||||
(key.state.extended_dynamic_state_3_enables != 0) !=
|
||||
@@ -572,13 +676,20 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
}
|
||||
|
||||
GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
|
||||
const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)};
|
||||
const auto [pair, inserted]{graphics_cache.try_emplace(graphics_key)};
|
||||
auto& pipeline{pair->second};
|
||||
if (is_new) {
|
||||
pipeline = CreateGraphicsPipeline();
|
||||
}
|
||||
if (!pipeline) {
|
||||
return nullptr;
|
||||
const auto key = pair->first;
|
||||
auto [slot, should_build] = AcquireGraphicsBuildSlot(key);
|
||||
if (!should_build) {
|
||||
WaitForBuildCompletion(slot);
|
||||
} else {
|
||||
pipeline = CreateGraphicsPipeline();
|
||||
ReleaseGraphicsBuildSlot(key, slot);
|
||||
}
|
||||
if (!pipeline) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (current_pipeline) {
|
||||
current_pipeline->AddTransition(pipeline.get());
|
||||
@@ -601,6 +712,7 @@ GraphicsPipeline* PipelineCache::BuiltPipeline(GraphicsPipeline* pipeline) const
|
||||
if (draw_state.index_buffer.count <= 6 || draw_state.vertex_buffer.count <= 6) {
|
||||
return pipeline;
|
||||
}
|
||||
scheduler.KeepAliveTick();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -704,6 +816,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
}
|
||||
LOG_ERROR(Render_Vulkan, "{}", exception.what());
|
||||
return nullptr;
|
||||
} catch (const vk::Exception& exception) {
|
||||
LOG_ERROR(Render_Vulkan, "Failed to create graphics pipeline 0x{:016x}: {}", key.Hash(),
|
||||
exception.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
|
||||
@@ -767,6 +883,19 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
}
|
||||
|
||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||
const VkDriverIdKHR driver_id = device.GetDriverID();
|
||||
const bool needs_shared_mem_clamp =
|
||||
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
|
||||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
|
||||
const u32 max_shared_memory = device.GetMaxComputeSharedMemorySize();
|
||||
if (needs_shared_mem_clamp && program.shared_memory_size > max_shared_memory) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Compute shader 0x{:016x} requests {}KB shared memory but device max is {}KB - clamping",
|
||||
key.unique_hash,
|
||||
program.shared_memory_size / 1024,
|
||||
max_shared_memory / 1024);
|
||||
program.shared_memory_size = max_shared_memory;
|
||||
}
|
||||
const std::vector<u32> code{EmitSPIRV(profile, program, this->optimize_spirv_output)};
|
||||
device.SaveShader(code);
|
||||
vk::ShaderModule spv_module{BuildShader(device, code)};
|
||||
@@ -782,6 +911,10 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||
} catch (const Shader::Exception& exception) {
|
||||
LOG_ERROR(Render_Vulkan, "{}", exception.what());
|
||||
return nullptr;
|
||||
} catch (const vk::Exception& exception) {
|
||||
LOG_ERROR(Render_Vulkan, "Failed to create compute pipeline 0x{:016x}: {}", key.Hash(),
|
||||
exception.what());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PipelineCache::SerializeVulkanPipelineCache(const std::filesystem::path& filename,
|
||||
@@ -879,4 +1012,68 @@ vk::PipelineCache PipelineCache::LoadVulkanPipelineCache(const std::filesystem::
|
||||
}
|
||||
}
|
||||
|
||||
auto PipelineCache::AcquireGraphicsBuildSlot(const GraphicsPipelineCacheKey& key)
|
||||
-> std::pair<InFlightPipelinePtr, bool> {
|
||||
std::scoped_lock lock(graphics_inflight_mutex);
|
||||
auto [it, inserted] = graphics_inflight_builds.try_emplace(key);
|
||||
if (inserted || !it->second) {
|
||||
it->second = std::make_shared<InFlightPipelineBuild>();
|
||||
return {it->second, true};
|
||||
}
|
||||
return {it->second, false};
|
||||
}
|
||||
|
||||
auto PipelineCache::AcquireComputeBuildSlot(const ComputePipelineCacheKey& key)
|
||||
-> std::pair<InFlightPipelinePtr, bool> {
|
||||
std::scoped_lock lock(compute_inflight_mutex);
|
||||
auto [it, inserted] = compute_inflight_builds.try_emplace(key);
|
||||
if (inserted || !it->second) {
|
||||
it->second = std::make_shared<InFlightPipelineBuild>();
|
||||
return {it->second, true};
|
||||
}
|
||||
return {it->second, false};
|
||||
}
|
||||
|
||||
void PipelineCache::ReleaseGraphicsBuildSlot(const GraphicsPipelineCacheKey& key,
|
||||
const InFlightPipelinePtr& slot) {
|
||||
if (!slot) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::scoped_lock slot_lock(slot->mutex);
|
||||
slot->building = false;
|
||||
}
|
||||
slot->cv.notify_all();
|
||||
std::scoped_lock map_lock(graphics_inflight_mutex);
|
||||
auto it = graphics_inflight_builds.find(key);
|
||||
if (it != graphics_inflight_builds.end() && it->second == slot) {
|
||||
graphics_inflight_builds.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void PipelineCache::ReleaseComputeBuildSlot(const ComputePipelineCacheKey& key,
|
||||
const InFlightPipelinePtr& slot) {
|
||||
if (!slot) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::scoped_lock slot_lock(slot->mutex);
|
||||
slot->building = false;
|
||||
}
|
||||
slot->cv.notify_all();
|
||||
std::scoped_lock map_lock(compute_inflight_mutex);
|
||||
auto it = compute_inflight_builds.find(key);
|
||||
if (it != compute_inflight_builds.end() && it->second == slot) {
|
||||
compute_inflight_builds.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void PipelineCache::WaitForBuildCompletion(const InFlightPipelinePtr& slot) const {
|
||||
if (!slot) {
|
||||
return;
|
||||
}
|
||||
std::unique_lock lock(slot->mutex);
|
||||
slot->cv.wait(lock, [&] { return !slot->building; });
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <condition_variable>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -113,7 +115,17 @@ public:
|
||||
void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback);
|
||||
|
||||
void DrainPendingBuilds();
|
||||
|
||||
private:
|
||||
struct InFlightPipelineBuild {
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
bool building{true};
|
||||
};
|
||||
|
||||
using InFlightPipelinePtr = std::shared_ptr<InFlightPipelineBuild>;
|
||||
|
||||
[[nodiscard]] GraphicsPipeline* CurrentGraphicsPipelineSlowPath();
|
||||
|
||||
[[nodiscard]] GraphicsPipeline* BuiltPipeline(GraphicsPipeline* pipeline) const noexcept;
|
||||
@@ -140,6 +152,14 @@ private:
|
||||
vk::PipelineCache LoadVulkanPipelineCache(const std::filesystem::path& filename,
|
||||
u32 expected_cache_version);
|
||||
|
||||
std::pair<InFlightPipelinePtr, bool> AcquireGraphicsBuildSlot(
|
||||
const GraphicsPipelineCacheKey& key);
|
||||
std::pair<InFlightPipelinePtr, bool> AcquireComputeBuildSlot(
|
||||
const ComputePipelineCacheKey& key);
|
||||
void ReleaseGraphicsBuildSlot(const GraphicsPipelineCacheKey& key, const InFlightPipelinePtr& slot);
|
||||
void ReleaseComputeBuildSlot(const ComputePipelineCacheKey& key, const InFlightPipelinePtr& slot);
|
||||
void WaitForBuildCompletion(const InFlightPipelinePtr& slot) const;
|
||||
|
||||
const Device& device;
|
||||
Scheduler& scheduler;
|
||||
DescriptorPool& descriptor_pool;
|
||||
@@ -158,6 +178,11 @@ private:
|
||||
std::unordered_map<ComputePipelineCacheKey, std::unique_ptr<ComputePipeline>> compute_cache;
|
||||
std::unordered_map<GraphicsPipelineCacheKey, std::unique_ptr<GraphicsPipeline>> graphics_cache;
|
||||
|
||||
std::mutex graphics_inflight_mutex;
|
||||
std::unordered_map<GraphicsPipelineCacheKey, InFlightPipelinePtr> graphics_inflight_builds;
|
||||
std::mutex compute_inflight_mutex;
|
||||
std::unordered_map<ComputePipelineCacheKey, InFlightPipelinePtr> compute_inflight_builds;
|
||||
|
||||
ShaderPools main_pools;
|
||||
|
||||
Shader::Profile profile;
|
||||
|
||||
@@ -42,7 +42,8 @@ public:
|
||||
static constexpr size_t BANK_SIZE = 256;
|
||||
static constexpr size_t QUERY_SIZE = 8;
|
||||
explicit SamplesQueryBank(const Device& device_, size_t index_)
|
||||
: BankBase(BANK_SIZE), device{device_}, index{index_} {
|
||||
: BankBase(BANK_SIZE), device{device_}, index{index_},
|
||||
supports_host_query_reset{device_.SupportsHostQueryReset()} {
|
||||
const auto& dev = device.GetLogical();
|
||||
query_pool = dev.CreateQueryPool({
|
||||
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
|
||||
@@ -60,8 +61,10 @@ public:
|
||||
void Reset() override {
|
||||
ASSERT(references == 0);
|
||||
VideoCommon::BankBase::Reset();
|
||||
const auto& dev = device.GetLogical();
|
||||
dev.ResetQueryPool(*query_pool, 0, BANK_SIZE);
|
||||
if (supports_host_query_reset) {
|
||||
const auto& dev = device.GetLogical();
|
||||
dev.ResetQueryPool(*query_pool, 0, BANK_SIZE);
|
||||
}
|
||||
host_results.fill(0ULL);
|
||||
next_bank = 0;
|
||||
}
|
||||
@@ -99,6 +102,7 @@ public:
|
||||
private:
|
||||
const Device& device;
|
||||
const size_t index;
|
||||
const bool supports_host_query_reset;
|
||||
vk::QueryPool query_pool;
|
||||
std::array<u64, BANK_SIZE> host_results;
|
||||
};
|
||||
@@ -1200,21 +1204,24 @@ struct QueryCacheRuntimeImpl {
|
||||
hcr_setup.pNext = nullptr;
|
||||
hcr_setup.flags = 0;
|
||||
|
||||
conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>(
|
||||
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
||||
if (device.IsExtConditionalRendering()) {
|
||||
conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>(
|
||||
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
||||
|
||||
const VkBufferCreateInfo buffer_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.size = sizeof(u32),
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
};
|
||||
hcr_resolve_buffer = memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
|
||||
const VkBufferCreateInfo buffer_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.size = sizeof(u32),
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
};
|
||||
hcr_resolve_buffer =
|
||||
memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
|
||||
}
|
||||
}
|
||||
|
||||
VideoCore::RasterizerInterface* rasterizer;
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/shader_cache.h"
|
||||
#include "video_core/texture_cache/texture_cache_base.h"
|
||||
#include "video_core/polygon_mode_utils.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
@@ -149,8 +148,7 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3
|
||||
return scissor;
|
||||
}
|
||||
|
||||
DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, bool is_indexed,
|
||||
Maxwell::PolygonMode polygon_mode) {
|
||||
DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, bool is_indexed) {
|
||||
DrawParams params{
|
||||
.base_instance = draw_state.base_instance,
|
||||
.num_instances = num_instances,
|
||||
@@ -170,21 +168,6 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances,
|
||||
params.base_vertex = 0;
|
||||
params.is_indexed = true;
|
||||
}
|
||||
const bool polygon_line =
|
||||
draw_state.topology == Maxwell::PrimitiveTopology::Polygon &&
|
||||
polygon_mode == Maxwell::PolygonMode::Line;
|
||||
if (polygon_line) {
|
||||
if (params.is_indexed) {
|
||||
if (draw_state.index_buffer.count > 1) {
|
||||
params.num_vertices = draw_state.index_buffer.count + 1;
|
||||
}
|
||||
} else if (draw_state.vertex_buffer.count > 1) {
|
||||
params.num_vertices = draw_state.vertex_buffer.count + 1;
|
||||
params.is_indexed = true;
|
||||
params.first_index = 0;
|
||||
params.base_vertex = draw_state.vertex_buffer.first;
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
@@ -214,6 +197,11 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
|
||||
fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler),
|
||||
wfi_event(device.GetLogical().CreateEvent()) {
|
||||
scheduler.SetQueryCache(query_cache);
|
||||
|
||||
// Log multi-draw support
|
||||
if (device.IsExtMultiDrawSupported()) {
|
||||
LOG_INFO(Render_Vulkan, "VK_EXT_multi_draw is enabled for optimized draw calls");
|
||||
}
|
||||
}
|
||||
|
||||
RasterizerVulkan::~RasterizerVulkan() = default;
|
||||
@@ -227,6 +215,10 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
||||
FlushWork();
|
||||
gpu_memory->FlushCaching();
|
||||
|
||||
if (device.HasBrokenParallelShaderCompiling()) {
|
||||
pipeline_cache.DrainPendingBuilds();
|
||||
}
|
||||
|
||||
GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()};
|
||||
if (!pipeline) {
|
||||
return;
|
||||
@@ -250,18 +242,45 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
||||
PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
|
||||
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
|
||||
const u32 num_instances{instance_count};
|
||||
const auto polygon_mode = VideoCore::EffectivePolygonMode(maxwell3d->regs);
|
||||
const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed, polygon_mode)};
|
||||
scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
|
||||
if (draw_params.is_indexed) {
|
||||
cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
|
||||
draw_params.first_index, draw_params.base_vertex,
|
||||
draw_params.base_instance);
|
||||
} else {
|
||||
cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances,
|
||||
draw_params.base_vertex, draw_params.base_instance);
|
||||
}
|
||||
});
|
||||
const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
|
||||
|
||||
// Use VK_EXT_multi_draw if available (single draw becomes multi-draw with count=1)
|
||||
if (device.IsExtMultiDrawSupported()) {
|
||||
scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
|
||||
if (draw_params.is_indexed) {
|
||||
// Use multi-draw indexed with single draw
|
||||
const VkMultiDrawIndexedInfoEXT multi_draw_info{
|
||||
.firstIndex = draw_params.first_index,
|
||||
.indexCount = draw_params.num_vertices,
|
||||
};
|
||||
const int32_t vertex_offset = static_cast<int32_t>(draw_params.base_vertex);
|
||||
cmdbuf.DrawMultiIndexedEXT(1, &multi_draw_info, draw_params.num_instances,
|
||||
draw_params.base_instance,
|
||||
sizeof(VkMultiDrawIndexedInfoEXT), &vertex_offset);
|
||||
} else {
|
||||
// Use multi-draw with single draw
|
||||
const VkMultiDrawInfoEXT multi_draw_info{
|
||||
.firstVertex = draw_params.base_vertex,
|
||||
.vertexCount = draw_params.num_vertices,
|
||||
};
|
||||
cmdbuf.DrawMultiEXT(1, &multi_draw_info, draw_params.num_instances,
|
||||
draw_params.base_instance,
|
||||
sizeof(VkMultiDrawInfoEXT));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Fallback to standard draw calls
|
||||
scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
|
||||
if (draw_params.is_indexed) {
|
||||
cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
|
||||
draw_params.first_index, draw_params.base_vertex,
|
||||
draw_params.base_instance);
|
||||
} else {
|
||||
cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances,
|
||||
draw_params.base_vertex, draw_params.base_instance);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -404,13 +423,48 @@ void RasterizerVulkan::Clear(u32 layer_count) {
|
||||
.baseArrayLayer = regs.clear_surface.layer,
|
||||
.layerCount = layer_count,
|
||||
};
|
||||
if (clear_rect.rect.extent.width == 0 || clear_rect.rect.extent.height == 0) {
|
||||
const auto clamp_rect_to_render_area = [render_area](VkRect2D& rect) -> bool {
|
||||
const auto clamp_axis = [](s32& offset, u32& extent, u32 limit) {
|
||||
auto clamp_offset = [&offset, limit]() {
|
||||
if (limit == 0) {
|
||||
offset = 0;
|
||||
return;
|
||||
}
|
||||
offset = std::clamp(offset, 0, static_cast<s32>(limit));
|
||||
};
|
||||
|
||||
if (extent == 0) {
|
||||
clamp_offset();
|
||||
return;
|
||||
}
|
||||
if (offset < 0) {
|
||||
const u32 shrink = (std::min)(extent, static_cast<u32>(-offset));
|
||||
extent -= shrink;
|
||||
offset = 0;
|
||||
}
|
||||
if (limit == 0) {
|
||||
extent = 0;
|
||||
offset = 0;
|
||||
return;
|
||||
}
|
||||
if (offset >= static_cast<s32>(limit)) {
|
||||
offset = static_cast<s32>(limit);
|
||||
extent = 0;
|
||||
return;
|
||||
}
|
||||
const u64 end_coord = static_cast<u64>(offset) + extent;
|
||||
if (end_coord > limit) {
|
||||
extent = limit - static_cast<u32>(offset);
|
||||
}
|
||||
};
|
||||
|
||||
clamp_axis(rect.offset.x, rect.extent.width, render_area.width);
|
||||
clamp_axis(rect.offset.y, rect.extent.height, render_area.height);
|
||||
return rect.extent.width != 0 && rect.extent.height != 0;
|
||||
};
|
||||
if (!clamp_rect_to_render_area(clear_rect.rect)) {
|
||||
return;
|
||||
}
|
||||
clear_rect.rect.extent = VkExtent2D{
|
||||
.width = (std::min)(clear_rect.rect.extent.width, render_area.width),
|
||||
.height = (std::min)(clear_rect.rect.extent.height, render_area.height),
|
||||
};
|
||||
|
||||
const u32 color_attachment = regs.clear_surface.RT;
|
||||
if (use_color && framebuffer->HasAspectColorBit(color_attachment)) {
|
||||
@@ -857,23 +911,21 @@ void RasterizerVulkan::LoadDiskResources(u64 title_id, std::stop_token stop_load
|
||||
|
||||
void RasterizerVulkan::FlushWork() {
|
||||
#ifdef ANDROID
|
||||
static constexpr u32 DRAWS_TO_DISPATCH = 1024;
|
||||
static constexpr u32 DRAWS_TO_DISPATCH = 512;
|
||||
static constexpr u32 CHECK_MASK = 3;
|
||||
#else
|
||||
static constexpr u32 DRAWS_TO_DISPATCH = 4096;
|
||||
static constexpr u32 CHECK_MASK = 7;
|
||||
#endif // ANDROID
|
||||
|
||||
// Only check multiples of 8 draws
|
||||
static_assert(DRAWS_TO_DISPATCH % 8 == 0);
|
||||
if ((++draw_counter & 7) != 7) {
|
||||
static_assert(DRAWS_TO_DISPATCH % (CHECK_MASK + 1) == 0);
|
||||
if ((++draw_counter & CHECK_MASK) != CHECK_MASK) {
|
||||
return;
|
||||
}
|
||||
if (draw_counter < DRAWS_TO_DISPATCH) {
|
||||
// Send recorded tasks to the worker thread
|
||||
scheduler.DispatchWork();
|
||||
return;
|
||||
}
|
||||
// Otherwise (every certain number of draws) flush execution.
|
||||
// This submits commands to the Vulkan driver.
|
||||
scheduler.Flush();
|
||||
draw_counter = 0;
|
||||
}
|
||||
@@ -939,6 +991,8 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info,
|
||||
|
||||
void RasterizerVulkan::UpdateDynamicStates() {
|
||||
auto& regs = maxwell3d->regs;
|
||||
|
||||
// Core Dynamic States (Vulkan 1.0) - Always active regardless of dyna_state setting
|
||||
UpdateViewportsState(regs);
|
||||
UpdateScissorsState(regs);
|
||||
UpdateDepthBias(regs);
|
||||
@@ -946,6 +1000,7 @@ void RasterizerVulkan::UpdateDynamicStates() {
|
||||
UpdateDepthBounds(regs);
|
||||
UpdateStencilFaces(regs);
|
||||
UpdateLineWidth(regs);
|
||||
// EDS1: CullMode, DepthCompare, FrontFace, StencilOp, DepthBoundsTest, DepthTest, DepthWrite, StencilTest
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
UpdateCullMode(regs);
|
||||
UpdateDepthCompareOp(regs);
|
||||
@@ -956,40 +1011,52 @@ void RasterizerVulkan::UpdateDynamicStates() {
|
||||
UpdateDepthTestEnable(regs);
|
||||
UpdateDepthWriteEnable(regs);
|
||||
UpdateStencilTestEnable(regs);
|
||||
if (device.IsExtExtendedDynamicState2Supported()) {
|
||||
UpdatePrimitiveRestartEnable(regs);
|
||||
UpdateRasterizerDiscardEnable(regs);
|
||||
UpdateDepthBiasEnable(regs);
|
||||
}
|
||||
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
|
||||
using namespace Tegra::Engines;
|
||||
if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE || device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
|
||||
const auto has_float = std::any_of(
|
||||
regs.vertex_attrib_format.begin(),
|
||||
regs.vertex_attrib_format.end(),
|
||||
[](const auto& attrib) {
|
||||
return attrib.type == Maxwell3D::Regs::VertexAttribute::Type::Float;
|
||||
}
|
||||
);
|
||||
if (regs.logic_op.enable) {
|
||||
regs.logic_op.enable = static_cast<u32>(!has_float);
|
||||
}
|
||||
}
|
||||
UpdateLogicOpEnable(regs);
|
||||
UpdateDepthClampEnable(regs);
|
||||
}
|
||||
}
|
||||
if (device.IsExtExtendedDynamicState2ExtrasSupported()) {
|
||||
UpdateLogicOp(regs);
|
||||
}
|
||||
if (device.IsExtExtendedDynamicState3BlendingSupported()) {
|
||||
UpdateBlending(regs);
|
||||
}
|
||||
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
|
||||
UpdateLineStippleEnable(regs);
|
||||
UpdateConservativeRasterizationMode(regs);
|
||||
}
|
||||
}
|
||||
|
||||
// EDS2: PrimitiveRestart, RasterizerDiscard, DepthBias enable/disable
|
||||
if (device.IsExtExtendedDynamicState2Supported()) {
|
||||
UpdatePrimitiveRestartEnable(regs);
|
||||
UpdateRasterizerDiscardEnable(regs);
|
||||
UpdateDepthBiasEnable(regs);
|
||||
}
|
||||
|
||||
// EDS2 Extras: LogicOp operation selection
|
||||
if (device.IsExtExtendedDynamicState2ExtrasSupported()) {
|
||||
UpdateLogicOp(regs);
|
||||
}
|
||||
|
||||
// EDS3 Enables: LogicOpEnable, DepthClamp, LineStipple, ConservativeRaster
|
||||
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
|
||||
using namespace Tegra::Engines;
|
||||
// AMD Workaround: LogicOp incompatible with float render targets
|
||||
if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE ||
|
||||
device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
|
||||
const auto has_float = std::any_of(
|
||||
regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(),
|
||||
[](const auto& attrib) {
|
||||
return attrib.type == Maxwell3D::Regs::VertexAttribute::Type::Float;
|
||||
}
|
||||
);
|
||||
if (regs.logic_op.enable) {
|
||||
regs.logic_op.enable = static_cast<u32>(!has_float);
|
||||
}
|
||||
}
|
||||
UpdateLogicOpEnable(regs);
|
||||
UpdateDepthClampEnable(regs);
|
||||
UpdateLineRasterizationMode(regs);
|
||||
UpdateLineStippleEnable(regs);
|
||||
UpdateConservativeRasterizationMode(regs);
|
||||
UpdateAlphaToCoverageEnable(regs);
|
||||
UpdateAlphaToOneEnable(regs);
|
||||
}
|
||||
|
||||
// EDS3 Blending: ColorBlendEnable, ColorBlendEquation, ColorWriteMask
|
||||
if (device.IsExtExtendedDynamicState3BlendingSupported()) {
|
||||
UpdateBlending(regs);
|
||||
}
|
||||
|
||||
// Vertex Input Dynamic State: Independent from EDS levels
|
||||
if (device.IsExtVertexInputDynamicStateSupported()) {
|
||||
if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) {
|
||||
UpdateVertexInput(regs);
|
||||
@@ -1162,109 +1229,141 @@ void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& reg
|
||||
if (!state_tracker.TouchBlendConstants()) {
|
||||
return;
|
||||
}
|
||||
const std::array blend_color = {regs.blend_color.r, regs.blend_color.g, regs.blend_color.b,
|
||||
regs.blend_color.a};
|
||||
scheduler.Record(
|
||||
[blend_color](vk::CommandBuffer cmdbuf) { cmdbuf.SetBlendConstants(blend_color.data()); });
|
||||
if (!device.UsesAdvancedCoreDynamicState()) {
|
||||
return;
|
||||
}
|
||||
const std::array<float, 4> blend_constants{
|
||||
regs.blend_color.r,
|
||||
regs.blend_color.g,
|
||||
regs.blend_color.b,
|
||||
regs.blend_color.a,
|
||||
};
|
||||
scheduler.Record([blend_constants](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetBlendConstants(blend_constants.data());
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthBounds()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]](
|
||||
vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBounds(min, max); });
|
||||
if (!device.IsDepthBoundsSupported()) {
|
||||
return;
|
||||
}
|
||||
if (!device.UsesAdvancedCoreDynamicState()) {
|
||||
return;
|
||||
}
|
||||
const bool unrestricted = device.IsExtDepthRangeUnrestrictedSupported();
|
||||
const float min_depth = unrestricted ? regs.depth_bounds[0]
|
||||
: std::clamp(regs.depth_bounds[0], 0.0f, 1.0f);
|
||||
const float max_depth = unrestricted ? regs.depth_bounds[1]
|
||||
: std::clamp(regs.depth_bounds[1], 0.0f, 1.0f);
|
||||
scheduler.Record([min_depth, max_depth](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthBounds(min_depth, max_depth);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchStencilProperties()) {
|
||||
const bool two_sided = regs.stencil_two_side_enable != 0;
|
||||
const bool update_properties = state_tracker.TouchStencilProperties();
|
||||
const bool update_side = state_tracker.TouchStencilSide(two_sided);
|
||||
const bool refs_dirty = state_tracker.TouchStencilReference();
|
||||
const bool write_dirty = state_tracker.TouchStencilWriteMask();
|
||||
const bool compare_dirty = state_tracker.TouchStencilCompare();
|
||||
|
||||
const bool update_references = update_properties || update_side || refs_dirty;
|
||||
const bool update_write_masks = update_properties || update_side || write_dirty;
|
||||
const bool update_compare_masks = update_properties || update_side || compare_dirty;
|
||||
|
||||
if (!update_references && !update_write_masks && !update_compare_masks) {
|
||||
state_tracker.ClearStencilReset();
|
||||
return;
|
||||
}
|
||||
bool update_references = state_tracker.TouchStencilReference();
|
||||
bool update_write_mask = state_tracker.TouchStencilWriteMask();
|
||||
bool update_compare_masks = state_tracker.TouchStencilCompare();
|
||||
if (state_tracker.TouchStencilSide(regs.stencil_two_side_enable != 0)) {
|
||||
update_references = true;
|
||||
update_write_mask = true;
|
||||
update_compare_masks = true;
|
||||
|
||||
if (!device.UsesAdvancedCoreDynamicState()) {
|
||||
state_tracker.ClearStencilReset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (update_references) {
|
||||
[&]() {
|
||||
if (regs.stencil_two_side_enable) {
|
||||
if (!state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref) &&
|
||||
!state_tracker.CheckStencilReferenceBack(regs.stencil_back_ref)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref)) {
|
||||
return;
|
||||
}
|
||||
if (two_sided) {
|
||||
const bool front_dirty =
|
||||
state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref);
|
||||
const bool back_dirty =
|
||||
state_tracker.CheckStencilReferenceBack(regs.stencil_back_ref);
|
||||
if (front_dirty || back_dirty) {
|
||||
scheduler.Record([front_ref = regs.stencil_front_ref,
|
||||
back_ref = regs.stencil_back_ref,
|
||||
is_two_sided = two_sided](vk::CommandBuffer cmdbuf) {
|
||||
const bool set_back = is_two_sided && front_ref != back_ref;
|
||||
cmdbuf.SetStencilReference(set_back ? VK_STENCIL_FACE_FRONT_BIT
|
||||
: VK_STENCIL_FACE_FRONT_AND_BACK,
|
||||
front_ref);
|
||||
if (set_back) {
|
||||
cmdbuf.SetStencilReference(VK_STENCIL_FACE_BACK_BIT, back_ref);
|
||||
}
|
||||
});
|
||||
}
|
||||
scheduler.Record([front_ref = regs.stencil_front_ref, back_ref = regs.stencil_back_ref,
|
||||
two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
|
||||
const bool set_back = two_sided && front_ref != back_ref;
|
||||
// Front face
|
||||
cmdbuf.SetStencilReference(set_back ? VK_STENCIL_FACE_FRONT_BIT
|
||||
: VK_STENCIL_FACE_FRONT_AND_BACK,
|
||||
front_ref);
|
||||
if (set_back) {
|
||||
cmdbuf.SetStencilReference(VK_STENCIL_FACE_BACK_BIT, back_ref);
|
||||
}
|
||||
} else if (state_tracker.CheckStencilReferenceFront(regs.stencil_front_ref)) {
|
||||
const u32 reference = regs.stencil_front_ref;
|
||||
scheduler.Record([reference](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, reference);
|
||||
});
|
||||
}();
|
||||
}
|
||||
}
|
||||
if (update_write_mask) {
|
||||
[&]() {
|
||||
if (regs.stencil_two_side_enable) {
|
||||
if (!state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask) &&
|
||||
!state_tracker.CheckStencilWriteMaskBack(regs.stencil_back_mask)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (update_write_masks) {
|
||||
if (two_sided) {
|
||||
if (state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask) ||
|
||||
state_tracker.CheckStencilWriteMaskBack(regs.stencil_back_mask)) {
|
||||
scheduler.Record([
|
||||
front_write_mask = regs.stencil_front_mask,
|
||||
back_write_mask = regs.stencil_back_mask,
|
||||
is_two_sided = regs.stencil_two_side_enable
|
||||
](vk::CommandBuffer cmdbuf) {
|
||||
const bool set_back = is_two_sided && front_write_mask != back_write_mask;
|
||||
cmdbuf.SetStencilWriteMask(set_back ? VK_STENCIL_FACE_FRONT_BIT
|
||||
: VK_STENCIL_FACE_FRONT_AND_BACK,
|
||||
front_write_mask);
|
||||
if (set_back) {
|
||||
cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, back_write_mask);
|
||||
}
|
||||
});
|
||||
}
|
||||
scheduler.Record([front_write_mask = regs.stencil_front_mask,
|
||||
back_write_mask = regs.stencil_back_mask,
|
||||
two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
|
||||
const bool set_back = two_sided && front_write_mask != back_write_mask;
|
||||
// Front face
|
||||
cmdbuf.SetStencilWriteMask(set_back ? VK_STENCIL_FACE_FRONT_BIT
|
||||
: VK_STENCIL_FACE_FRONT_AND_BACK,
|
||||
front_write_mask);
|
||||
if (set_back) {
|
||||
cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_BACK_BIT, back_write_mask);
|
||||
}
|
||||
} else if (state_tracker.CheckStencilWriteMaskFront(regs.stencil_front_mask)) {
|
||||
const u32 front_write_mask = regs.stencil_front_mask;
|
||||
scheduler.Record([front_write_mask](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, front_write_mask);
|
||||
});
|
||||
}();
|
||||
}
|
||||
}
|
||||
|
||||
if (update_compare_masks) {
|
||||
[&]() {
|
||||
if (regs.stencil_two_side_enable) {
|
||||
if (!state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask) &&
|
||||
!state_tracker.CheckStencilCompareMaskBack(regs.stencil_back_func_mask)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask)) {
|
||||
return;
|
||||
}
|
||||
if (two_sided) {
|
||||
if (state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask) ||
|
||||
state_tracker.CheckStencilCompareMaskBack(regs.stencil_back_func_mask)) {
|
||||
scheduler.Record([
|
||||
front_test_mask = regs.stencil_front_func_mask,
|
||||
back_test_mask = regs.stencil_back_func_mask,
|
||||
is_two_sided = regs.stencil_two_side_enable
|
||||
](vk::CommandBuffer cmdbuf) {
|
||||
const bool set_back = is_two_sided && front_test_mask != back_test_mask;
|
||||
cmdbuf.SetStencilCompareMask(set_back ? VK_STENCIL_FACE_FRONT_BIT
|
||||
: VK_STENCIL_FACE_FRONT_AND_BACK,
|
||||
front_test_mask);
|
||||
if (set_back) {
|
||||
cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, back_test_mask);
|
||||
}
|
||||
});
|
||||
}
|
||||
scheduler.Record([front_test_mask = regs.stencil_front_func_mask,
|
||||
back_test_mask = regs.stencil_back_func_mask,
|
||||
two_sided = regs.stencil_two_side_enable](vk::CommandBuffer cmdbuf) {
|
||||
const bool set_back = two_sided && front_test_mask != back_test_mask;
|
||||
// Front face
|
||||
cmdbuf.SetStencilCompareMask(set_back ? VK_STENCIL_FACE_FRONT_BIT
|
||||
: VK_STENCIL_FACE_FRONT_AND_BACK,
|
||||
front_test_mask);
|
||||
if (set_back) {
|
||||
cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_BACK_BIT, back_test_mask);
|
||||
}
|
||||
} else if (state_tracker.CheckStencilCompareMaskFront(regs.stencil_front_func_mask)) {
|
||||
const u32 front_test_mask = regs.stencil_front_func_mask;
|
||||
scheduler.Record([front_test_mask](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, front_test_mask);
|
||||
});
|
||||
}();
|
||||
}
|
||||
}
|
||||
|
||||
state_tracker.ClearStencilReset();
|
||||
}
|
||||
|
||||
@@ -1287,61 +1386,15 @@ void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthBoundsTestEnable()) {
|
||||
return;
|
||||
}
|
||||
bool enabled = regs.depth_bounds_enable;
|
||||
if (enabled && !device.IsDepthBoundsSupported()) {
|
||||
LOG_WARNING(Render_Vulkan, "Depth bounds is enabled but not supported");
|
||||
enabled = false;
|
||||
}
|
||||
scheduler.Record([enable = enabled](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthBoundsTestEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthTestEnable()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthTestEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthWriteEnable()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthWriteEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchPrimitiveRestartEnable()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetPrimitiveRestartEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchRasterizerDiscardEnable()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([disable = regs.rasterize_enable](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetRasterizerDiscardEnableEXT(disable == 0);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
void RasterizerVulkan::UpdateConservativeRasterizationMode(
|
||||
Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchConservativeRasterizationMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!device.SupportsDynamicState3ConservativeRasterizationMode() ||
|
||||
!device.IsExtConservativeRasterizationSupported()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.conservative_raster_enable](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetConservativeRasterizationModeEXT(
|
||||
enable ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT
|
||||
@@ -1354,23 +1407,69 @@ void RasterizerVulkan::UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs&
|
||||
return;
|
||||
}
|
||||
|
||||
if (!device.SupportsDynamicState3LineStippleEnable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler.Record([enable = regs.line_stipple_enable](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetLineStippleEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
// if (!state_tracker.TouchLi()) {
|
||||
// return;
|
||||
// }
|
||||
if (!device.IsExtLineRasterizationSupported()) {
|
||||
return;
|
||||
}
|
||||
if (!state_tracker.TouchLineRasterizationMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: The maxwell emulator does not capture line rasters
|
||||
if (!device.SupportsDynamicState3LineRasterizationMode()) {
|
||||
static std::once_flag warn_missing_rect;
|
||||
std::call_once(warn_missing_rect, [] {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Driver lacks rectangular line rasterization support; skipping dynamic "
|
||||
"line state updates");
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// scheduler.Record([enable = regs.line](vk::CommandBuffer cmdbuf) {
|
||||
// cmdbuf.SetConservativeRasterizationModeEXT(
|
||||
// enable ? VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT
|
||||
// : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT);
|
||||
// });
|
||||
const bool wants_smooth = regs.line_anti_alias_enable != 0;
|
||||
VkLineRasterizationModeEXT mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT;
|
||||
if (wants_smooth) {
|
||||
if (device.SupportsSmoothLines()) {
|
||||
mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT;
|
||||
} else {
|
||||
static std::once_flag warn_missing_smooth;
|
||||
std::call_once(warn_missing_smooth, [] {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Line anti-aliasing requested but smoothLines feature unavailable; "
|
||||
"using rectangular rasterization");
|
||||
});
|
||||
}
|
||||
}
|
||||
scheduler.Record([mode](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetLineRasterizationModeEXT(mode);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchPrimitiveRestartEnable()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.primitive_restart.enabled != 0](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetPrimitiveRestartEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchRasterizerDiscardEnable()) {
|
||||
return;
|
||||
}
|
||||
const bool discard = regs.rasterize_enable == 0;
|
||||
scheduler.Record([discard](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetRasterizerDiscardEnableEXT(discard);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
@@ -1412,6 +1511,9 @@ void RasterizerVulkan::UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs
|
||||
if (!state_tracker.TouchLogicOpEnable()) {
|
||||
return;
|
||||
}
|
||||
if (!device.SupportsDynamicState3LogicOpEnable()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.logic_op.enable](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetLogicOpEnableEXT(enable != 0);
|
||||
});
|
||||
@@ -1421,6 +1523,9 @@ void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& r
|
||||
if (!state_tracker.TouchDepthClampEnable()) {
|
||||
return;
|
||||
}
|
||||
if (!device.SupportsDynamicState3DepthClampEnable()) {
|
||||
return;
|
||||
}
|
||||
bool is_enabled = !(regs.viewport_clip_control.geometry_clip ==
|
||||
Maxwell::ViewportClipControl::GeometryClip::Passthrough ||
|
||||
regs.viewport_clip_control.geometry_clip ==
|
||||
@@ -1431,6 +1536,41 @@ void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& r
|
||||
[is_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthClampEnableEXT(is_enabled); });
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchAlphaToCoverageEnable()) {
|
||||
return;
|
||||
}
|
||||
if (!device.SupportsDynamicState3AlphaToCoverageEnable()) {
|
||||
return;
|
||||
}
|
||||
GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline();
|
||||
const bool enable = pipeline != nullptr && pipeline->SupportsAlphaToCoverage() &&
|
||||
regs.anti_alias_alpha_control.alpha_to_coverage != 0;
|
||||
scheduler.Record([enable](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetAlphaToCoverageEnableEXT(enable ? VK_TRUE : VK_FALSE);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchAlphaToOneEnable()) {
|
||||
return;
|
||||
}
|
||||
if (!device.SupportsDynamicState3AlphaToOneEnable()) {
|
||||
static std::once_flag warn_alpha_to_one;
|
||||
std::call_once(warn_alpha_to_one, [] {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Alpha-to-one is not supported on this device; forcing it disabled");
|
||||
});
|
||||
return;
|
||||
}
|
||||
GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline();
|
||||
const bool enable = pipeline != nullptr && pipeline->SupportsAlphaToOne() &&
|
||||
regs.anti_alias_alpha_control.alpha_to_one != 0;
|
||||
scheduler.Record([enable](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetAlphaToOneEnableEXT(enable ? VK_TRUE : VK_FALSE);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthCompareOp()) {
|
||||
return;
|
||||
@@ -1440,6 +1580,36 @@ void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& reg
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthBoundsTestEnable()) {
|
||||
return;
|
||||
}
|
||||
if (!device.IsDepthBoundsSupported()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.depth_bounds_enable != 0](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthBoundsTestEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthTestEnable()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.depth_test_enable != 0](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthTestEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthWriteEnable()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.depth_write_enabled != 0](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthWriteEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchFrontFace()) {
|
||||
return;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/texture_cache/samples_helper.h"
|
||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
@@ -168,7 +169,6 @@ private:
|
||||
void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
|
||||
void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
@@ -183,6 +183,8 @@ private:
|
||||
void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
@@ -13,6 +14,7 @@
|
||||
|
||||
#include "common/thread.h"
|
||||
#include "video_core/renderer_vulkan/vk_command_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
||||
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
||||
@@ -77,6 +79,14 @@ void Scheduler::WaitWorker() {
|
||||
std::scoped_lock el{execution_mutex};
|
||||
}
|
||||
|
||||
void Scheduler::KeepAliveTick() {
|
||||
const auto now = Clock::now();
|
||||
if (now - last_submission_time < KEEPALIVE_INTERVAL) {
|
||||
return;
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
void Scheduler::DispatchWork() {
|
||||
if (chunk->Empty()) {
|
||||
return;
|
||||
@@ -130,9 +140,27 @@ void Scheduler::RequestOutsideRenderPassOperationContext() {
|
||||
|
||||
bool Scheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
|
||||
if (state.graphics_pipeline == pipeline) {
|
||||
if (pipeline && pipeline->UsesExtendedDynamicState() &&
|
||||
state.needs_state_enable_refresh) {
|
||||
state_tracker.InvalidateStateEnableFlag();
|
||||
state.needs_state_enable_refresh = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
state.graphics_pipeline = pipeline;
|
||||
|
||||
if (!pipeline) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!pipeline->UsesExtendedDynamicState()) {
|
||||
state.needs_state_enable_refresh = true;
|
||||
} else if (state.needs_state_enable_refresh) {
|
||||
state_tracker.InvalidateStateEnableFlag();
|
||||
state.needs_state_enable_refresh = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -252,6 +280,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
|
||||
});
|
||||
chunk->MarkSubmit();
|
||||
DispatchWork();
|
||||
last_submission_time = Clock::now();
|
||||
return signal_value;
|
||||
}
|
||||
|
||||
@@ -276,8 +305,13 @@ void Scheduler::EndRenderPass()
|
||||
return;
|
||||
}
|
||||
|
||||
query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false);
|
||||
query_cache->NotifySegment(false);
|
||||
if (query_cache) {
|
||||
query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false);
|
||||
if (query_cache->HasStreamer(VideoCommon::QueryType::StreamingByteCount)) {
|
||||
query_cache->CounterEnable(VideoCommon::QueryType::StreamingByteCount, false);
|
||||
}
|
||||
query_cache->NotifySegment(false);
|
||||
}
|
||||
|
||||
Record([num_images = num_renderpass_images,
|
||||
images = renderpass_images,
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
@@ -49,6 +53,9 @@ public:
|
||||
/// safe to touch worker resources.
|
||||
void WaitWorker();
|
||||
|
||||
/// Submits a tiny chunk of work if recent GPU submissions are stale.
|
||||
void KeepAliveTick();
|
||||
|
||||
/// Sends currently recorded work to the worker thread.
|
||||
void DispatchWork();
|
||||
|
||||
@@ -125,6 +132,8 @@ public:
|
||||
std::mutex submit_mutex;
|
||||
|
||||
private:
|
||||
using Clock = std::chrono::steady_clock;
|
||||
|
||||
class Command {
|
||||
public:
|
||||
virtual ~Command() = default;
|
||||
@@ -214,6 +223,7 @@ private:
|
||||
GraphicsPipeline* graphics_pipeline = nullptr;
|
||||
bool is_rescaling = false;
|
||||
bool rescaling_defined = false;
|
||||
bool needs_state_enable_refresh = false;
|
||||
};
|
||||
|
||||
void WorkerThread(std::stop_token stop_token);
|
||||
@@ -246,6 +256,9 @@ private:
|
||||
|
||||
State state;
|
||||
|
||||
Clock::time_point last_submission_time{Clock::time_point::min()};
|
||||
static constexpr std::chrono::milliseconds KEEPALIVE_INTERVAL{4};
|
||||
|
||||
u32 num_renderpass_images = 0;
|
||||
std::array<VkImage, 9> renderpass_images{};
|
||||
std::array<VkImageSubresourceRange, 9> renderpass_image_ranges{};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -49,6 +50,7 @@ size_t GetStreamBufferSize(const Device& device) {
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
|
||||
StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_,
|
||||
Scheduler& scheduler_)
|
||||
: device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
|
||||
@@ -74,13 +76,16 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
|
||||
}
|
||||
stream_pointer = stream_buffer.Mapped();
|
||||
ASSERT_MSG(!stream_pointer.empty(), "Stream buffer must be host visible!");
|
||||
|
||||
}
|
||||
|
||||
StagingBufferPool::~StagingBufferPool() = default;
|
||||
|
||||
StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage, bool deferred) {
|
||||
if (!deferred && usage == MemoryUsage::Upload && size <= region_size) {
|
||||
return GetStreamBuffer(size);
|
||||
if (!deferred && usage == MemoryUsage::Upload) {
|
||||
if (size <= region_size) {
|
||||
return GetStreamBuffer(size);
|
||||
}
|
||||
}
|
||||
return GetStagingBuffer(size, usage, deferred);
|
||||
}
|
||||
@@ -142,6 +147,7 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
|
||||
}
|
||||
|
||||
bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end) const {
|
||||
scheduler.GetMasterSemaphore().Refresh();
|
||||
const u64 gpu_tick = scheduler.GetMasterSemaphore().KnownGpuTick();
|
||||
return std::any_of(sync_ticks.begin() + region_begin, sync_ticks.begin() + region_end,
|
||||
[gpu_tick](u64 sync_tick) { return gpu_tick < sync_tick; });
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -48,6 +48,7 @@ Flags MakeInvalidationFlags() {
|
||||
FrontFace,
|
||||
StencilOp,
|
||||
StencilTestEnable,
|
||||
RasterizerDiscardEnable,
|
||||
VertexBuffers,
|
||||
VertexInput,
|
||||
StateEnable,
|
||||
@@ -55,6 +56,9 @@ Flags MakeInvalidationFlags() {
|
||||
DepthBiasEnable,
|
||||
LogicOpEnable,
|
||||
DepthClampEnable,
|
||||
AlphaToCoverageEnable,
|
||||
AlphaToOneEnable,
|
||||
LineRasterizationMode,
|
||||
LogicOp,
|
||||
Blending,
|
||||
ColorMask,
|
||||
@@ -148,6 +152,8 @@ void SetupDirtyStateEnable(Tables& tables) {
|
||||
setup(OFF(logic_op.enable), LogicOpEnable);
|
||||
setup(OFF(viewport_clip_control.geometry_clip), DepthClampEnable);
|
||||
setup(OFF(line_stipple_enable), LineStippleEnable);
|
||||
setup(OFF(anti_alias_alpha_control.alpha_to_coverage), AlphaToCoverageEnable);
|
||||
setup(OFF(anti_alias_alpha_control.alpha_to_one), AlphaToOneEnable);
|
||||
}
|
||||
|
||||
void SetupDirtyDepthCompareOp(Tables& tables) {
|
||||
@@ -226,6 +232,7 @@ void SetupRasterModes(Tables &tables) {
|
||||
|
||||
table[OFF(line_stipple_params)] = LineStippleParams;
|
||||
table[OFF(conservative_raster_enable)] = ConservativeRasterizationMode;
|
||||
table[OFF(line_anti_alias_enable)] = LineRasterizationMode;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ enum : u8 {
|
||||
PrimitiveRestartEnable,
|
||||
RasterizerDiscardEnable,
|
||||
ConservativeRasterizationMode,
|
||||
LineRasterizationMode,
|
||||
LineStippleEnable,
|
||||
LineStippleParams,
|
||||
DepthBiasEnable,
|
||||
@@ -61,6 +62,8 @@ enum : u8 {
|
||||
LogicOp,
|
||||
LogicOpEnable,
|
||||
DepthClampEnable,
|
||||
AlphaToCoverageEnable,
|
||||
AlphaToOneEnable,
|
||||
|
||||
Blending,
|
||||
BlendEnable,
|
||||
@@ -94,6 +97,10 @@ public:
|
||||
(*flags)[Dirty::Scissors] = true;
|
||||
}
|
||||
|
||||
void InvalidateStateEnableFlag() {
|
||||
(*flags)[Dirty::StateEnable] = true;
|
||||
}
|
||||
|
||||
bool TouchViewports() {
|
||||
const bool dirty_viewports = Exchange(Dirty::Viewports, false);
|
||||
const bool rescale_viewports = Exchange(VideoCommon::Dirty::RescaleViewports, false);
|
||||
@@ -225,6 +232,14 @@ public:
|
||||
return Exchange(Dirty::DepthClampEnable, false);
|
||||
}
|
||||
|
||||
bool TouchAlphaToCoverageEnable() {
|
||||
return Exchange(Dirty::AlphaToCoverageEnable, false);
|
||||
}
|
||||
|
||||
bool TouchAlphaToOneEnable() {
|
||||
return Exchange(Dirty::AlphaToOneEnable, false);
|
||||
}
|
||||
|
||||
bool TouchDepthCompareOp() {
|
||||
return Exchange(Dirty::DepthCompareOp, false);
|
||||
}
|
||||
@@ -261,6 +276,10 @@ public:
|
||||
return Exchange(Dirty::LogicOp, false);
|
||||
}
|
||||
|
||||
bool TouchLineRasterizationMode() {
|
||||
return Exchange(Dirty::LineRasterizationMode, false);
|
||||
}
|
||||
|
||||
bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
|
||||
const bool has_changed = current_topology != new_topology;
|
||||
current_topology = new_topology;
|
||||
|
||||
@@ -306,7 +306,17 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) {
|
||||
swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size());
|
||||
swapchain_ci.pQueueFamilyIndices = queue_indices.data();
|
||||
}
|
||||
static constexpr std::array view_formats{VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB};
|
||||
// According to Vulkan spec, when using VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR,
|
||||
// the base format (imageFormat) MUST be included in pViewFormats
|
||||
const std::array view_formats{
|
||||
swapchain_ci.imageFormat, // Base format MUST be first
|
||||
VK_FORMAT_B8G8R8A8_UNORM,
|
||||
VK_FORMAT_B8G8R8A8_SRGB,
|
||||
#ifdef ANDROID
|
||||
VK_FORMAT_R8G8B8A8_UNORM, // Android may use RGBA
|
||||
VK_FORMAT_R8G8B8A8_SRGB,
|
||||
#endif
|
||||
};
|
||||
VkImageFormatListCreateInfo format_list{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -101,7 +104,7 @@ public:
|
||||
}
|
||||
|
||||
VkSemaphore CurrentRenderSemaphore() const {
|
||||
return *render_semaphores[frame_index];
|
||||
return *render_semaphores[image_index];
|
||||
}
|
||||
|
||||
u32 GetWidth() const {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
#include "video_core/texture_cache/texture_cache_base.h"
|
||||
@@ -56,12 +60,16 @@ public:
|
||||
|
||||
void TickFrame();
|
||||
|
||||
void WaitForGpuTick(u64 tick);
|
||||
|
||||
u64 GetDeviceLocalMemory() const;
|
||||
|
||||
u64 GetDeviceMemoryUsage() const;
|
||||
|
||||
bool CanReportMemoryUsage() const;
|
||||
|
||||
std::optional<size_t> GetSamplerHeapBudget() const;
|
||||
|
||||
void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
|
||||
const Region2D& dst_region, const Region2D& src_region,
|
||||
Tegra::Engines::Fermi2D::Filter filter,
|
||||
@@ -108,10 +116,15 @@ public:
|
||||
return view_formats[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
bool RequiresBlockCompatibleViewFormats(PixelFormat format) const noexcept {
|
||||
return requires_block_view_formats[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
void BarrierFeedbackLoop();
|
||||
|
||||
bool IsFormatDitherable(VideoCore::Surface::PixelFormat format);
|
||||
bool IsFormatScalable(VideoCore::Surface::PixelFormat format);
|
||||
bool SupportsLinearFilter(VideoCore::Surface::PixelFormat format) const;
|
||||
|
||||
VkFormat GetSupportedFormat(VkFormat requested_format, VkFormatFeatureFlags required_features) const;
|
||||
|
||||
@@ -125,6 +138,7 @@ public:
|
||||
std::unique_ptr<MSAACopyPass> msaa_copy_pass;
|
||||
const Settings::ResolutionScalingInfo& resolution;
|
||||
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
||||
std::array<bool, VideoCore::Surface::MaxPixelFormat> requires_block_view_formats{};
|
||||
|
||||
static constexpr size_t indexing_slots = 8 * sizeof(size_t);
|
||||
std::array<vk::Buffer, indexing_slots> buffers{};
|
||||
@@ -171,6 +185,30 @@ public:
|
||||
return (this->*current_image).UsageFlags();
|
||||
}
|
||||
|
||||
void TrackGpuReadTick(u64 tick) noexcept {
|
||||
TrackPendingReadTick(tick);
|
||||
}
|
||||
|
||||
void TrackGpuWriteTick(u64 tick) noexcept {
|
||||
TrackPendingWriteTick(tick);
|
||||
}
|
||||
|
||||
void CompleteGpuReadTick(u64 completed_tick) noexcept {
|
||||
ClearPendingReadTick(completed_tick);
|
||||
}
|
||||
|
||||
void CompleteGpuWriteTick(u64 completed_tick) noexcept {
|
||||
ClearPendingWriteTick(completed_tick);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<u64> PendingGpuReadTick() const noexcept {
|
||||
return PendingReadTick();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<u64> PendingGpuWriteTick() const noexcept {
|
||||
return PendingWriteTick();
|
||||
}
|
||||
|
||||
/// Returns true when the image is already initialized and mark it as initialized
|
||||
[[nodiscard]] bool ExchangeInitialization() noexcept {
|
||||
return std::exchange(initialized, true);
|
||||
@@ -233,15 +271,23 @@ public:
|
||||
|
||||
[[nodiscard]] VkImageView ColorView();
|
||||
|
||||
[[nodiscard]] VkImageView SampledView(Shader::TextureType texture_type,
|
||||
Shader::SamplerComponentType component_type);
|
||||
|
||||
[[nodiscard]] VkImageView StorageView(Shader::TextureType texture_type,
|
||||
Shader::ImageFormat image_format);
|
||||
|
||||
[[nodiscard]] bool IsRescaled() const noexcept;
|
||||
|
||||
[[nodiscard]] bool SupportsDepthCompareSampling() const noexcept;
|
||||
|
||||
[[nodiscard]] bool Is3DImage() const noexcept {
|
||||
return is_3d_image;
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept {
|
||||
return *image_views[static_cast<size_t>(texture_type)];
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImage ImageHandle() const noexcept {
|
||||
return image_handle;
|
||||
}
|
||||
@@ -266,23 +312,36 @@ private:
|
||||
struct StorageViews {
|
||||
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> signeds;
|
||||
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> unsigneds;
|
||||
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> typeless;
|
||||
};
|
||||
|
||||
static constexpr size_t NUMERIC_VIEW_TYPES = 3;
|
||||
|
||||
[[nodiscard]] Shader::TextureType BaseTextureType() const noexcept;
|
||||
[[nodiscard]] std::optional<u32> LayerCountOverride(Shader::TextureType texture_type) const noexcept;
|
||||
[[nodiscard]] VkImageView DepthView(Shader::TextureType texture_type);
|
||||
[[nodiscard]] VkImageView StencilView(Shader::TextureType texture_type);
|
||||
|
||||
[[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask);
|
||||
[[nodiscard]] vk::ImageView MakeView(VkFormat vk_format, VkImageAspectFlags aspect_mask,
|
||||
Shader::TextureType texture_type);
|
||||
|
||||
const Device* device = nullptr;
|
||||
const SlotVector<Image>* slot_images = nullptr;
|
||||
|
||||
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> image_views;
|
||||
std::unique_ptr<StorageViews> storage_views;
|
||||
vk::ImageView depth_view;
|
||||
vk::ImageView stencil_view;
|
||||
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> depth_views;
|
||||
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> stencil_views;
|
||||
std::array<std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES>, NUMERIC_VIEW_TYPES>
|
||||
sampled_component_views;
|
||||
vk::ImageView color_view;
|
||||
vk::Image null_image;
|
||||
VkImage image_handle = VK_NULL_HANDLE;
|
||||
VkImageView render_target = VK_NULL_HANDLE;
|
||||
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
u32 buffer_size = 0;
|
||||
bool is_3d_image = false;
|
||||
};
|
||||
|
||||
class ImageAlloc : public VideoCommon::ImageAllocBase {};
|
||||
@@ -303,9 +362,19 @@ public:
|
||||
return static_cast<bool>(sampler_default_anisotropy);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkSampler SelectHandle(bool supports_linear_filter,
|
||||
bool supports_anisotropy,
|
||||
bool allow_depth_compare) const noexcept;
|
||||
|
||||
private:
|
||||
vk::Sampler sampler;
|
||||
vk::Sampler sampler_default_anisotropy;
|
||||
vk::Sampler sampler_force_point;
|
||||
vk::Sampler sampler_compare_disabled;
|
||||
vk::Sampler sampler_default_anisotropy_compare_disabled;
|
||||
vk::Sampler sampler_force_point_compare_disabled;
|
||||
bool uses_linear_filter = false;
|
||||
bool depth_compare_enabled = false;
|
||||
};
|
||||
|
||||
class Framebuffer {
|
||||
|
||||
@@ -70,6 +70,102 @@ static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture
|
||||
entry.a_type, entry.srgb_conversion));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] bool UsesSwizzleSource(const Tegra::Texture::TICEntry& entry,
|
||||
Tegra::Texture::SwizzleSource source) {
|
||||
const std::array swizzles{entry.x_source.Value(), entry.y_source.Value(),
|
||||
entry.z_source.Value(), entry.w_source.Value()};
|
||||
return std::ranges::any_of(swizzles, [source](Tegra::Texture::SwizzleSource current) {
|
||||
return current == source;
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<Shader::SamplerComponentType> DepthStencilComponentFromSwizzle(
|
||||
const Tegra::Texture::TICEntry& entry, VideoCore::Surface::PixelFormat pixel_format) {
|
||||
using Tegra::Texture::SwizzleSource;
|
||||
const bool uses_r = UsesSwizzleSource(entry, SwizzleSource::R);
|
||||
const bool uses_g = UsesSwizzleSource(entry, SwizzleSource::G);
|
||||
|
||||
switch (pixel_format) {
|
||||
case VideoCore::Surface::PixelFormat::D24_UNORM_S8_UINT:
|
||||
case VideoCore::Surface::PixelFormat::D32_FLOAT_S8_UINT:
|
||||
if (uses_r != uses_g) {
|
||||
return uses_r ? Shader::SamplerComponentType::Depth
|
||||
: Shader::SamplerComponentType::Stencil;
|
||||
}
|
||||
break;
|
||||
case VideoCore::Surface::PixelFormat::S8_UINT_D24_UNORM:
|
||||
if (uses_r != uses_g) {
|
||||
return uses_r ? Shader::SamplerComponentType::Stencil
|
||||
: Shader::SamplerComponentType::Depth;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
static Shader::SamplerComponentType ConvertSamplerComponentType(
|
||||
const Tegra::Texture::TICEntry& entry) {
|
||||
const auto pixel_format = PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type,
|
||||
entry.b_type, entry.a_type,
|
||||
entry.srgb_conversion);
|
||||
const auto surface_type = VideoCore::Surface::GetFormatType(pixel_format);
|
||||
if (entry.depth_texture != 0 || surface_type == VideoCore::Surface::SurfaceType::Depth) {
|
||||
return Shader::SamplerComponentType::Depth;
|
||||
}
|
||||
if (surface_type == VideoCore::Surface::SurfaceType::Stencil) {
|
||||
return Shader::SamplerComponentType::Stencil;
|
||||
}
|
||||
if (surface_type == VideoCore::Surface::SurfaceType::DepthStencil) {
|
||||
if (const auto inferred = DepthStencilComponentFromSwizzle(entry, pixel_format)) {
|
||||
return *inferred;
|
||||
}
|
||||
return entry.depth_texture != 0 ? Shader::SamplerComponentType::Depth
|
||||
: Shader::SamplerComponentType::Stencil;
|
||||
}
|
||||
|
||||
const auto accumulate = [](const Tegra::Texture::ComponentType component,
|
||||
bool& has_signed, bool& has_unsigned) {
|
||||
switch (component) {
|
||||
case Tegra::Texture::ComponentType::SINT:
|
||||
has_signed = true;
|
||||
break;
|
||||
case Tegra::Texture::ComponentType::UINT:
|
||||
has_unsigned = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
bool has_signed{};
|
||||
bool has_unsigned{};
|
||||
accumulate(entry.r_type, has_signed, has_unsigned);
|
||||
accumulate(entry.g_type, has_signed, has_unsigned);
|
||||
accumulate(entry.b_type, has_signed, has_unsigned);
|
||||
accumulate(entry.a_type, has_signed, has_unsigned);
|
||||
|
||||
if (has_signed && !has_unsigned) {
|
||||
return Shader::SamplerComponentType::Sint;
|
||||
}
|
||||
if (has_unsigned && !has_signed) {
|
||||
return Shader::SamplerComponentType::Uint;
|
||||
}
|
||||
if (has_signed) {
|
||||
return Shader::SamplerComponentType::Sint;
|
||||
}
|
||||
if (has_unsigned) {
|
||||
return Shader::SamplerComponentType::Uint;
|
||||
}
|
||||
return Shader::SamplerComponentType::Float;
|
||||
}
|
||||
|
||||
static std::string_view StageToPrefix(Shader::Stage stage) {
|
||||
switch (stage) {
|
||||
case Shader::Stage::VertexB:
|
||||
@@ -200,6 +296,7 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
|
||||
const u64 code_size{static_cast<u64>(CachedSizeBytes())};
|
||||
const u64 num_texture_types{static_cast<u64>(texture_types.size())};
|
||||
const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())};
|
||||
const u64 num_texture_component_types{static_cast<u64>(texture_component_types.size())};
|
||||
const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())};
|
||||
const u64 num_cbuf_replacement_values{static_cast<u64>(cbuf_replacements.size())};
|
||||
|
||||
@@ -207,6 +304,8 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
|
||||
.write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types))
|
||||
.write(reinterpret_cast<const char*>(&num_texture_pixel_formats),
|
||||
sizeof(num_texture_pixel_formats))
|
||||
.write(reinterpret_cast<const char*>(&num_texture_component_types),
|
||||
sizeof(num_texture_component_types))
|
||||
.write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values))
|
||||
.write(reinterpret_cast<const char*>(&num_cbuf_replacement_values),
|
||||
sizeof(num_cbuf_replacement_values))
|
||||
@@ -223,6 +322,10 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
|
||||
file.write(reinterpret_cast<const char*>(&key), sizeof(key))
|
||||
.write(reinterpret_cast<const char*>(&type), sizeof(type));
|
||||
}
|
||||
for (const auto& [key, component] : texture_component_types) {
|
||||
file.write(reinterpret_cast<const char*>(&key), sizeof(key))
|
||||
.write(reinterpret_cast<const char*>(&component), sizeof(component));
|
||||
}
|
||||
for (const auto& [key, format] : texture_pixel_formats) {
|
||||
file.write(reinterpret_cast<const char*>(&key), sizeof(key))
|
||||
.write(reinterpret_cast<const char*>(&format), sizeof(format));
|
||||
@@ -277,7 +380,17 @@ std::optional<u64> GenericEnvironment::TryFindSize() {
|
||||
Tegra::Texture::TICEntry GenericEnvironment::ReadTextureInfo(GPUVAddr tic_addr, u32 tic_limit,
|
||||
bool via_header_index, u32 raw) {
|
||||
const auto handle{Tegra::Texture::TexturePair(raw, via_header_index)};
|
||||
ASSERT(handle.first <= tic_limit);
|
||||
if (handle.first > tic_limit) {
|
||||
static std::atomic<size_t> oob_count{0};
|
||||
const size_t n = ++oob_count;
|
||||
if (n <= 4 || (n & 63) == 0) {
|
||||
LOG_WARNING(Shader,
|
||||
"TIC handle {} exceeds limit {} (via_header_index={}) — returning empty",
|
||||
handle.first, tic_limit, via_header_index);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
const GPUVAddr descriptor_addr{tic_addr + handle.first * sizeof(Tegra::Texture::TICEntry)};
|
||||
Tegra::Texture::TICEntry entry;
|
||||
gpu_memory->ReadBlock(descriptor_addr, &entry, sizeof(entry));
|
||||
@@ -374,6 +487,21 @@ Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) {
|
||||
ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle);
|
||||
const Shader::TextureType result{ConvertTextureType(entry)};
|
||||
texture_types.emplace(handle, result);
|
||||
texture_component_types.emplace(handle, ConvertSamplerComponentType(entry));
|
||||
return result;
|
||||
}
|
||||
|
||||
Shader::SamplerComponentType GraphicsEnvironment::ReadTextureComponentType(u32 handle) {
|
||||
const auto it{texture_component_types.find(handle)};
|
||||
if (it != texture_component_types.end()) {
|
||||
return it->second;
|
||||
}
|
||||
const auto& regs{maxwell3d->regs};
|
||||
const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
|
||||
auto entry =
|
||||
ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle);
|
||||
const Shader::SamplerComponentType result{ConvertSamplerComponentType(entry)};
|
||||
texture_component_types.emplace(handle, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -430,6 +558,20 @@ Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) {
|
||||
auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle);
|
||||
const Shader::TextureType result{ConvertTextureType(entry)};
|
||||
texture_types.emplace(handle, result);
|
||||
texture_component_types.emplace(handle, ConvertSamplerComponentType(entry));
|
||||
return result;
|
||||
}
|
||||
|
||||
Shader::SamplerComponentType ComputeEnvironment::ReadTextureComponentType(u32 handle) {
|
||||
const auto it{texture_component_types.find(handle)};
|
||||
if (it != texture_component_types.end()) {
|
||||
return it->second;
|
||||
}
|
||||
const auto& regs{kepler_compute->regs};
|
||||
const auto& qmd{kepler_compute->launch_description};
|
||||
auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle);
|
||||
const Shader::SamplerComponentType result{ConvertSamplerComponentType(entry)};
|
||||
texture_component_types.emplace(handle, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -455,12 +597,15 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
|
||||
u64 code_size{};
|
||||
u64 num_texture_types{};
|
||||
u64 num_texture_pixel_formats{};
|
||||
u64 num_texture_component_types{};
|
||||
u64 num_cbuf_values{};
|
||||
u64 num_cbuf_replacement_values{};
|
||||
file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size))
|
||||
.read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types))
|
||||
.read(reinterpret_cast<char*>(&num_texture_pixel_formats),
|
||||
.read(reinterpret_cast<char*>(&num_texture_pixel_formats),
|
||||
sizeof(num_texture_pixel_formats))
|
||||
.read(reinterpret_cast<char*>(&num_texture_component_types),
|
||||
sizeof(num_texture_component_types))
|
||||
.read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values))
|
||||
.read(reinterpret_cast<char*>(&num_cbuf_replacement_values),
|
||||
sizeof(num_cbuf_replacement_values))
|
||||
@@ -480,6 +625,13 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
|
||||
.read(reinterpret_cast<char*>(&type), sizeof(type));
|
||||
texture_types.emplace(key, type);
|
||||
}
|
||||
for (size_t i = 0; i < num_texture_component_types; ++i) {
|
||||
u32 key;
|
||||
Shader::SamplerComponentType component;
|
||||
file.read(reinterpret_cast<char*>(&key), sizeof(key))
|
||||
.read(reinterpret_cast<char*>(&component), sizeof(component));
|
||||
texture_component_types.emplace(key, component);
|
||||
}
|
||||
for (size_t i = 0; i < num_texture_pixel_formats; ++i) {
|
||||
u32 key;
|
||||
Shader::TexturePixelFormat format;
|
||||
@@ -534,6 +686,15 @@ u32 FileEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Shader::SamplerComponentType FileEnvironment::ReadTextureComponentType(u32 handle) {
|
||||
const auto it{texture_component_types.find(handle)};
|
||||
if (it == texture_component_types.end()) {
|
||||
LOG_WARNING(Render_Vulkan, "Texture component descriptor {:08x} not found", handle);
|
||||
return Shader::SamplerComponentType::Float;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Shader::TextureType FileEnvironment::ReadTextureType(u32 handle) {
|
||||
const auto it{texture_types.find(handle)};
|
||||
if (it == texture_types.end()) {
|
||||
|
||||
@@ -80,6 +80,7 @@ protected:
|
||||
|
||||
std::vector<u64> code;
|
||||
std::unordered_map<u32, Shader::TextureType> texture_types;
|
||||
std::unordered_map<u32, Shader::SamplerComponentType> texture_component_types;
|
||||
std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
|
||||
std::unordered_map<u64, u32> cbuf_values;
|
||||
std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements;
|
||||
@@ -116,6 +117,8 @@ public:
|
||||
|
||||
Shader::TextureType ReadTextureType(u32 handle) override;
|
||||
|
||||
Shader::SamplerComponentType ReadTextureComponentType(u32 handle) override;
|
||||
|
||||
Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
|
||||
|
||||
bool IsTexturePixelFormatInteger(u32 handle) override;
|
||||
@@ -142,6 +145,8 @@ public:
|
||||
|
||||
Shader::TextureType ReadTextureType(u32 handle) override;
|
||||
|
||||
Shader::SamplerComponentType ReadTextureComponentType(u32 handle) override;
|
||||
|
||||
Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
|
||||
|
||||
bool IsTexturePixelFormatInteger(u32 handle) override;
|
||||
@@ -176,6 +181,8 @@ public:
|
||||
|
||||
[[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override;
|
||||
|
||||
[[nodiscard]] Shader::SamplerComponentType ReadTextureComponentType(u32 handle) override;
|
||||
|
||||
[[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
|
||||
|
||||
[[nodiscard]] bool IsTexturePixelFormatInteger(u32 handle) override;
|
||||
@@ -202,6 +209,7 @@ public:
|
||||
private:
|
||||
std::vector<u64> code;
|
||||
std::unordered_map<u32, Shader::TextureType> texture_types;
|
||||
std::unordered_map<u32, Shader::SamplerComponentType> texture_component_types;
|
||||
std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
|
||||
std::unordered_map<u64, u32> cbuf_values;
|
||||
std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/settings.h"
|
||||
@@ -408,6 +410,126 @@ bool IsPixelFormatSignedInteger(PixelFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct NumericVariantSet {
|
||||
PixelFormat float_format = PixelFormat::Invalid;
|
||||
PixelFormat uint_format = PixelFormat::Invalid;
|
||||
PixelFormat sint_format = PixelFormat::Invalid;
|
||||
|
||||
[[nodiscard]] std::optional<PixelFormat> Select(PixelFormatNumeric numeric) const {
|
||||
PixelFormat candidate = PixelFormat::Invalid;
|
||||
switch (numeric) {
|
||||
case PixelFormatNumeric::Float:
|
||||
candidate = float_format;
|
||||
break;
|
||||
case PixelFormatNumeric::Uint:
|
||||
candidate = uint_format;
|
||||
break;
|
||||
case PixelFormatNumeric::Sint:
|
||||
candidate = sint_format;
|
||||
break;
|
||||
}
|
||||
if (candidate == PixelFormat::Invalid) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr NumericVariantSet MakeVariant(PixelFormat float_format, PixelFormat uint_format,
|
||||
PixelFormat sint_format) {
|
||||
return NumericVariantSet{
|
||||
.float_format = float_format,
|
||||
.uint_format = uint_format,
|
||||
.sint_format = sint_format,
|
||||
};
|
||||
}
|
||||
|
||||
std::optional<NumericVariantSet> LookupNumericVariantSet(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::R8_UNORM:
|
||||
case PixelFormat::R8_SNORM:
|
||||
case PixelFormat::R8_UINT:
|
||||
case PixelFormat::R8_SINT:
|
||||
return MakeVariant(PixelFormat::R8_UNORM, PixelFormat::R8_UINT, PixelFormat::R8_SINT);
|
||||
case PixelFormat::R16_FLOAT:
|
||||
case PixelFormat::R16_UNORM:
|
||||
case PixelFormat::R16_SNORM:
|
||||
case PixelFormat::R16_UINT:
|
||||
case PixelFormat::R16_SINT:
|
||||
return MakeVariant(PixelFormat::R16_FLOAT, PixelFormat::R16_UINT, PixelFormat::R16_SINT);
|
||||
case PixelFormat::R32_FLOAT:
|
||||
case PixelFormat::R32_UINT:
|
||||
case PixelFormat::R32_SINT:
|
||||
return MakeVariant(PixelFormat::R32_FLOAT, PixelFormat::R32_UINT, PixelFormat::R32_SINT);
|
||||
case PixelFormat::R8G8_UNORM:
|
||||
case PixelFormat::R8G8_SNORM:
|
||||
case PixelFormat::R8G8_UINT:
|
||||
case PixelFormat::R8G8_SINT:
|
||||
return MakeVariant(PixelFormat::R8G8_UNORM, PixelFormat::R8G8_UINT, PixelFormat::R8G8_SINT);
|
||||
case PixelFormat::R16G16_FLOAT:
|
||||
case PixelFormat::R16G16_UNORM:
|
||||
case PixelFormat::R16G16_SNORM:
|
||||
case PixelFormat::R16G16_UINT:
|
||||
case PixelFormat::R16G16_SINT:
|
||||
return MakeVariant(PixelFormat::R16G16_FLOAT, PixelFormat::R16G16_UINT,
|
||||
PixelFormat::R16G16_SINT);
|
||||
case PixelFormat::R32G32_FLOAT:
|
||||
case PixelFormat::R32G32_UINT:
|
||||
case PixelFormat::R32G32_SINT:
|
||||
return MakeVariant(PixelFormat::R32G32_FLOAT, PixelFormat::R32G32_UINT,
|
||||
PixelFormat::R32G32_SINT);
|
||||
case PixelFormat::R16G16B16A16_FLOAT:
|
||||
case PixelFormat::R16G16B16A16_UNORM:
|
||||
case PixelFormat::R16G16B16A16_SNORM:
|
||||
case PixelFormat::R16G16B16A16_UINT:
|
||||
case PixelFormat::R16G16B16A16_SINT:
|
||||
return MakeVariant(PixelFormat::R16G16B16A16_FLOAT, PixelFormat::R16G16B16A16_UINT,
|
||||
PixelFormat::R16G16B16A16_SINT);
|
||||
case PixelFormat::R32G32B32A32_FLOAT:
|
||||
case PixelFormat::R32G32B32A32_UINT:
|
||||
case PixelFormat::R32G32B32A32_SINT:
|
||||
return MakeVariant(PixelFormat::R32G32B32A32_FLOAT, PixelFormat::R32G32B32A32_UINT,
|
||||
PixelFormat::R32G32B32A32_SINT);
|
||||
case PixelFormat::A8B8G8R8_UNORM:
|
||||
case PixelFormat::A8B8G8R8_SNORM:
|
||||
case PixelFormat::A8B8G8R8_SRGB:
|
||||
case PixelFormat::A8B8G8R8_UINT:
|
||||
case PixelFormat::A8B8G8R8_SINT:
|
||||
return MakeVariant(PixelFormat::A8B8G8R8_UNORM, PixelFormat::A8B8G8R8_UINT,
|
||||
PixelFormat::A8B8G8R8_SINT);
|
||||
case PixelFormat::A2B10G10R10_UNORM:
|
||||
case PixelFormat::A2B10G10R10_UINT:
|
||||
return MakeVariant(PixelFormat::A2B10G10R10_UNORM, PixelFormat::A2B10G10R10_UINT,
|
||||
PixelFormat::Invalid);
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
PixelFormatNumeric GetPixelFormatNumericType(PixelFormat format) {
|
||||
if (IsPixelFormatInteger(format)) {
|
||||
return IsPixelFormatSignedInteger(format) ? PixelFormatNumeric::Sint
|
||||
: PixelFormatNumeric::Uint;
|
||||
}
|
||||
return PixelFormatNumeric::Float;
|
||||
}
|
||||
|
||||
std::optional<PixelFormat> FindPixelFormatVariant(PixelFormat format,
|
||||
PixelFormatNumeric target_numeric) {
|
||||
const auto variants = LookupNumericVariantSet(format);
|
||||
if (!variants) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (const auto candidate = variants->Select(target_numeric)) {
|
||||
return candidate;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
size_t PixelComponentSizeBitsInteger(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::A8B8G8R8_SINT:
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -517,6 +521,16 @@ bool IsPixelFormatSignedInteger(PixelFormat format);
|
||||
|
||||
size_t PixelComponentSizeBitsInteger(PixelFormat format);
|
||||
|
||||
enum class PixelFormatNumeric {
|
||||
Float,
|
||||
Uint,
|
||||
Sint,
|
||||
};
|
||||
|
||||
PixelFormatNumeric GetPixelFormatNumericType(PixelFormat format);
|
||||
std::optional<PixelFormat> FindPixelFormatVariant(PixelFormat format,
|
||||
PixelFormatNumeric target_numeric);
|
||||
|
||||
std::pair<u32, u32> GetASTCBlockSize(PixelFormat format);
|
||||
|
||||
u64 TranscodedAstcSize(u64 base_size, PixelFormat format);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -21,6 +24,27 @@ constexpr auto FLOAT = ComponentType::FLOAT;
|
||||
constexpr bool LINEAR = false;
|
||||
constexpr bool SRGB = true;
|
||||
|
||||
constexpr TextureFormat SanitizeFormat(TextureFormat format) {
|
||||
if (format == static_cast<TextureFormat>(0)) {
|
||||
return TextureFormat::A8B8G8R8;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
constexpr ComponentType SanitizeComponent(ComponentType component) {
|
||||
if (component == static_cast<ComponentType>(0)) {
|
||||
return ComponentType::UNORM;
|
||||
}
|
||||
switch (component) {
|
||||
case ComponentType::SNORM_FORCE_FP16:
|
||||
return ComponentType::SNORM;
|
||||
case ComponentType::UNORM_FORCE_FP16:
|
||||
return ComponentType::UNORM;
|
||||
default:
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 Hash(TextureFormat format, ComponentType red_component, ComponentType green_component,
|
||||
ComponentType blue_component, ComponentType alpha_component, bool is_srgb) {
|
||||
u32 hash = is_srgb ? 1 : 0;
|
||||
@@ -41,6 +65,11 @@ constexpr u32 Hash(TextureFormat format, ComponentType component, bool is_srgb =
|
||||
PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, ComponentType green,
|
||||
ComponentType blue, ComponentType alpha,
|
||||
bool is_srgb) noexcept {
|
||||
format = SanitizeFormat(format);
|
||||
red = SanitizeComponent(red);
|
||||
green = SanitizeComponent(green);
|
||||
blue = SanitizeComponent(blue);
|
||||
alpha = SanitizeComponent(alpha);
|
||||
switch (Hash(format, red, green, blue, alpha, is_srgb)) {
|
||||
case Hash(TextureFormat::A8B8G8R8, UNORM):
|
||||
return PixelFormat::A8B8G8R8_UNORM;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
@@ -58,6 +62,50 @@ struct ImageBase {
|
||||
explicit ImageBase(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
|
||||
explicit ImageBase(const NullImageParams&);
|
||||
|
||||
void TrackPendingReadTick(u64 tick) noexcept {
|
||||
if (pending_read_tick) {
|
||||
*pending_read_tick = std::max(*pending_read_tick, tick);
|
||||
} else {
|
||||
pending_read_tick = tick;
|
||||
}
|
||||
}
|
||||
|
||||
void TrackPendingWriteTick(u64 tick) noexcept {
|
||||
if (pending_write_tick) {
|
||||
*pending_write_tick = std::max(*pending_write_tick, tick);
|
||||
} else {
|
||||
pending_write_tick = tick;
|
||||
}
|
||||
}
|
||||
|
||||
void ClearPendingReadTick(u64 completed_tick) noexcept {
|
||||
if (pending_read_tick && completed_tick >= *pending_read_tick) {
|
||||
pending_read_tick.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void ClearPendingWriteTick(u64 completed_tick) noexcept {
|
||||
if (pending_write_tick && completed_tick >= *pending_write_tick) {
|
||||
pending_write_tick.reset();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasPendingReadTick() const noexcept {
|
||||
return pending_read_tick.has_value();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasPendingWriteTick() const noexcept {
|
||||
return pending_write_tick.has_value();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<u64> PendingReadTick() const noexcept {
|
||||
return pending_read_tick;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<u64> PendingWriteTick() const noexcept {
|
||||
return pending_write_tick;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<SubresourceBase> TryFindBase(GPUVAddr other_addr) const noexcept;
|
||||
|
||||
[[nodiscard]] ImageViewId FindView(const ImageViewInfo& view_info) const noexcept;
|
||||
@@ -115,6 +163,9 @@ struct ImageBase {
|
||||
std::vector<AliasedImage> aliased_images;
|
||||
std::vector<ImageId> overlapping_images;
|
||||
ImageMapId map_view_id{};
|
||||
|
||||
std::optional<u64> pending_read_tick;
|
||||
std::optional<u64> pending_write_tick;
|
||||
};
|
||||
|
||||
struct ImageMapView {
|
||||
|
||||
@@ -33,6 +33,7 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
|
||||
dma_downloaded = forced_flushed;
|
||||
format = PixelFormatFromTextureInfo(config.format, config.r_type, config.g_type, config.b_type,
|
||||
config.a_type, config.srgb_conversion);
|
||||
|
||||
num_samples = NumSamples(config.msaa_mode);
|
||||
resources.levels = config.max_mip_level + 1;
|
||||
if (config.IsPitchLinear()) {
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
constexpr inline std::size_t MaxSampleLocationSlots = 16;
|
||||
|
||||
[[nodiscard]] inline std::pair<int, int> SamplesLog2(int num_samples) {
|
||||
switch (num_samples) {
|
||||
case 1:
|
||||
@@ -95,4 +102,24 @@ namespace VideoCommon {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// NVN guarantees up to sixteen programmable sample slots shared across a repeating pixel grid
|
||||
// (per the 0.7.0 addon release notes), so the grid dimensions shrink as MSAA increases.
|
||||
[[nodiscard]] inline std::pair<u32, u32> SampleLocationGridSize(Tegra::Texture::MsaaMode msaa_mode) {
|
||||
const int samples = NumSamples(msaa_mode);
|
||||
switch (samples) {
|
||||
case 1:
|
||||
return {4u, 4u};
|
||||
case 2:
|
||||
return {4u, 2u};
|
||||
case 4:
|
||||
return {2u, 2u};
|
||||
case 8:
|
||||
return {2u, 1u};
|
||||
case 16:
|
||||
return {1u, 1u};
|
||||
}
|
||||
ASSERT_MSG(false, "Unsupported sample count for grid size={}", samples);
|
||||
return {1u, 1u};
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
@@ -538,6 +540,7 @@ void TextureCache<P>::WriteMemory(DAddr cpu_addr, size_t size) {
|
||||
if (True(image.flags & ImageFlagBits::CpuModified)) {
|
||||
return;
|
||||
}
|
||||
EnsureImageReady(image, ImageAccessType::Write);
|
||||
image.flags |= ImageFlagBits::CpuModified;
|
||||
if (True(image.flags & ImageFlagBits::Tracked)) {
|
||||
UntrackImage(image, image_id);
|
||||
@@ -548,11 +551,12 @@ void TextureCache<P>::WriteMemory(DAddr cpu_addr, size_t size) {
|
||||
template <class P>
|
||||
void TextureCache<P>::DownloadMemory(DAddr cpu_addr, size_t size) {
|
||||
boost::container::small_vector<ImageId, 16> images;
|
||||
ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) {
|
||||
ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) {
|
||||
if (!image.IsSafeDownload()) {
|
||||
return;
|
||||
}
|
||||
image.flags &= ~ImageFlagBits::GpuModified;
|
||||
EnsureImageReady(this->slot_images[image_id], ImageAccessType::Read);
|
||||
images.push_back(image_id);
|
||||
});
|
||||
if (images.empty()) {
|
||||
@@ -604,6 +608,7 @@ void TextureCache<P>::UnmapMemory(DAddr cpu_addr, size_t size) {
|
||||
ForEachImageInRegion(cpu_addr, size, [&](ImageId id, Image&) { deleted_images.push_back(id); });
|
||||
for (const ImageId id : deleted_images) {
|
||||
Image& image = slot_images[id];
|
||||
EnsureImageReady(image, ImageAccessType::Write);
|
||||
if (True(image.flags & ImageFlagBits::Tracked)) {
|
||||
UntrackImage(image, id);
|
||||
}
|
||||
@@ -619,6 +624,7 @@ void TextureCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t siz
|
||||
[&](ImageId id, Image&) { deleted_images.push_back(id); });
|
||||
for (const ImageId id : deleted_images) {
|
||||
Image& image = slot_images[id];
|
||||
EnsureImageReady(image, ImageAccessType::Write);
|
||||
if (False(image.flags & ImageFlagBits::CpuModified)) {
|
||||
image.flags |= ImageFlagBits::CpuModified;
|
||||
if (True(image.flags & ImageFlagBits::Tracked)) {
|
||||
@@ -1736,11 +1742,89 @@ SamplerId TextureCache<P>::FindSampler(const TSCEntry& config) {
|
||||
}
|
||||
const auto [pair, is_new] = channel_state->samplers.try_emplace(config);
|
||||
if (is_new) {
|
||||
EnforceSamplerBudget();
|
||||
pair->second = slot_samplers.insert(runtime, config);
|
||||
}
|
||||
return pair->second;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
std::optional<size_t> TextureCache<P>::QuerySamplerBudget() const {
|
||||
if constexpr (requires { runtime.GetSamplerHeapBudget(); }) {
|
||||
return runtime.GetSamplerHeapBudget();
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::EnforceSamplerBudget() {
|
||||
const auto budget = QuerySamplerBudget();
|
||||
if (!budget) {
|
||||
return;
|
||||
}
|
||||
if (slot_samplers.size() < *budget) {
|
||||
return;
|
||||
}
|
||||
if (!channel_state) {
|
||||
return;
|
||||
}
|
||||
if (last_sampler_gc_frame == frame_tick) {
|
||||
return;
|
||||
}
|
||||
last_sampler_gc_frame = frame_tick;
|
||||
TrimInactiveSamplers(*budget);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::TrimInactiveSamplers(size_t budget) {
|
||||
if (channel_state->samplers.empty()) {
|
||||
return;
|
||||
}
|
||||
static constexpr size_t SAMPLER_GC_SLACK = 1024;
|
||||
auto mark_active = [](auto& set, SamplerId id) {
|
||||
if (!id || id == CORRUPT_ID || id == NULL_SAMPLER_ID) {
|
||||
return;
|
||||
}
|
||||
set.insert(id);
|
||||
};
|
||||
std::unordered_set<SamplerId> active;
|
||||
active.reserve(channel_state->graphics_sampler_ids.size() +
|
||||
channel_state->compute_sampler_ids.size());
|
||||
for (const SamplerId id : channel_state->graphics_sampler_ids) {
|
||||
mark_active(active, id);
|
||||
}
|
||||
for (const SamplerId id : channel_state->compute_sampler_ids) {
|
||||
mark_active(active, id);
|
||||
}
|
||||
|
||||
size_t removed = 0;
|
||||
auto& sampler_map = channel_state->samplers;
|
||||
for (auto it = sampler_map.begin(); it != sampler_map.end();) {
|
||||
const SamplerId sampler_id = it->second;
|
||||
if (!sampler_id || sampler_id == CORRUPT_ID) {
|
||||
it = sampler_map.erase(it);
|
||||
continue;
|
||||
}
|
||||
if (active.find(sampler_id) != active.end()) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
slot_samplers.erase(sampler_id);
|
||||
it = sampler_map.erase(it);
|
||||
++removed;
|
||||
if (slot_samplers.size() + SAMPLER_GC_SLACK <= budget) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (removed != 0) {
|
||||
LOG_WARNING(HW_GPU,
|
||||
"Sampler cache exceeded {} entries on this driver; reclaimed {} inactive samplers",
|
||||
budget, removed);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
ImageViewId TextureCache<P>::FindColorBuffer(size_t index) {
|
||||
const auto& regs = maxwell3d->regs;
|
||||
@@ -2343,6 +2427,9 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
|
||||
template <class P>
|
||||
void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool invalidate) {
|
||||
Image& image = slot_images[image_id];
|
||||
EnsureImageReady(image, is_modification ? ImageAccessType::Write : ImageAccessType::Read);
|
||||
TrackGpuImageAccess(image_id, is_modification ? ImageAccessType::Write : ImageAccessType::Read);
|
||||
runtime.TransitionImageLayout(image);
|
||||
if (invalidate) {
|
||||
image.flags &= ~(ImageFlagBits::CpuModified | ImageFlagBits::GpuModified);
|
||||
if (False(image.flags & ImageFlagBits::Tracked)) {
|
||||
@@ -2358,6 +2445,85 @@ void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool
|
||||
lru_cache.Touch(image.lru_index, frame_tick);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::TrackGpuImageAccess(ImageId image_id, ImageAccessType access) {
|
||||
staged_gpu_accesses.push_back({image_id, access});
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::CommitPendingGpuAccesses(u64 fence_value) {
|
||||
if (staged_gpu_accesses.empty()) {
|
||||
return;
|
||||
}
|
||||
if (fence_value == 0) {
|
||||
return;
|
||||
}
|
||||
auto& batch = committed_gpu_accesses.emplace_back(std::move(staged_gpu_accesses));
|
||||
staged_gpu_accesses.clear();
|
||||
committed_gpu_ticks.push_back(fence_value);
|
||||
for (const PendingImageAccess& access : batch) {
|
||||
ImageBase& image = slot_images[access.image_id];
|
||||
if (access.access == ImageAccessType::Read) {
|
||||
image.TrackPendingReadTick(fence_value);
|
||||
} else {
|
||||
image.TrackPendingWriteTick(fence_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::CompleteGpuAccesses(u64 completed_fence) {
|
||||
if (completed_fence == 0) {
|
||||
return;
|
||||
}
|
||||
while (!committed_gpu_ticks.empty() && committed_gpu_ticks.front() <= completed_fence) {
|
||||
auto accesses = std::move(committed_gpu_accesses.front());
|
||||
committed_gpu_accesses.pop_front();
|
||||
committed_gpu_ticks.pop_front();
|
||||
for (const PendingImageAccess& access : accesses) {
|
||||
if (!slot_images.Contains(access.image_id)) {
|
||||
continue;
|
||||
}
|
||||
ImageBase& image = slot_images[access.image_id];
|
||||
if (access.access == ImageAccessType::Read) {
|
||||
image.ClearPendingReadTick(completed_fence);
|
||||
} else {
|
||||
image.ClearPendingWriteTick(completed_fence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::EnsureImageReady(ImageBase& image, ImageAccessType access) {
|
||||
auto wait_tick = [this](std::optional<u64> tick) -> std::optional<u64> {
|
||||
if (!tick) {
|
||||
return std::nullopt;
|
||||
}
|
||||
runtime.WaitForGpuTick(*tick);
|
||||
return tick;
|
||||
};
|
||||
|
||||
if (access == ImageAccessType::Write) {
|
||||
if (const auto tick = image.PendingReadTick()) {
|
||||
if (const auto waited = wait_tick(tick)) {
|
||||
image.ClearPendingReadTick(*waited);
|
||||
}
|
||||
}
|
||||
if (const auto tick = image.PendingWriteTick()) {
|
||||
if (const auto waited = wait_tick(tick)) {
|
||||
image.ClearPendingWriteTick(*waited);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (const auto tick = image.PendingWriteTick()) {
|
||||
if (const auto waited = wait_tick(tick)) {
|
||||
image.ClearPendingWriteTick(*waited);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::PrepareImageView(ImageViewId image_view_id, bool is_modification,
|
||||
bool invalidate) {
|
||||
@@ -2375,6 +2541,8 @@ template <class P>
|
||||
void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<ImageCopy> copies) {
|
||||
Image& dst = slot_images[dst_id];
|
||||
Image& src = slot_images[src_id];
|
||||
EnsureImageReady(dst, ImageAccessType::Write);
|
||||
EnsureImageReady(src, ImageAccessType::Read);
|
||||
const bool is_rescaled = True(src.flags & ImageFlagBits::Rescaled);
|
||||
if (is_rescaled) {
|
||||
ASSERT(True(dst.flags & ImageFlagBits::Rescaled));
|
||||
@@ -2391,20 +2559,30 @@ void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<Imag
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto TrackCopyAccesses = [this, dst_id, src_id]() {
|
||||
TrackGpuImageAccess(dst_id, ImageAccessType::Write);
|
||||
TrackGpuImageAccess(src_id, ImageAccessType::Read);
|
||||
};
|
||||
const auto dst_format_type = GetFormatType(dst.info.format);
|
||||
const auto src_format_type = GetFormatType(src.info.format);
|
||||
if (src_format_type == dst_format_type) {
|
||||
if constexpr (HAS_EMULATED_COPIES) {
|
||||
if (!runtime.CanImageBeCopied(dst, src)) {
|
||||
return runtime.EmulateCopyImage(dst, src, copies);
|
||||
runtime.EmulateCopyImage(dst, src, copies);
|
||||
TrackCopyAccesses();
|
||||
return;
|
||||
}
|
||||
}
|
||||
return runtime.CopyImage(dst, src, copies);
|
||||
runtime.CopyImage(dst, src, copies);
|
||||
TrackCopyAccesses();
|
||||
return;
|
||||
}
|
||||
UNIMPLEMENTED_IF(dst.info.type != ImageType::e2D);
|
||||
UNIMPLEMENTED_IF(src.info.type != ImageType::e2D);
|
||||
if (runtime.ShouldReinterpret(dst, src)) {
|
||||
return runtime.ReinterpretImage(dst, src, copies);
|
||||
runtime.ReinterpretImage(dst, src, copies);
|
||||
TrackCopyAccesses();
|
||||
return;
|
||||
}
|
||||
for (const ImageCopy& copy : copies) {
|
||||
UNIMPLEMENTED_IF(copy.dst_subresource.num_layers != 1);
|
||||
@@ -2457,6 +2635,7 @@ void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<Imag
|
||||
|
||||
runtime.ConvertImage(dst_framebuffer, dst_view, src_view);
|
||||
}
|
||||
TrackCopyAccesses();
|
||||
}
|
||||
|
||||
template <class P>
|
||||
|
||||
@@ -182,6 +182,11 @@ public:
|
||||
/// Return a reference to the given sampler id
|
||||
[[nodiscard]] Sampler& GetSampler(SamplerId id) noexcept;
|
||||
|
||||
/// Returns true when the runtime format supports linear filtering
|
||||
[[nodiscard]] bool SupportsLinearFilter(PixelFormat format) const noexcept {
|
||||
return runtime.SupportsLinearFilter(format);
|
||||
}
|
||||
|
||||
/// Refresh the state for graphics image view and sampler descriptors
|
||||
void SynchronizeGraphicsDescriptors();
|
||||
|
||||
@@ -258,6 +263,15 @@ public:
|
||||
/// Prepare an image to be used
|
||||
void PrepareImage(ImageId image_id, bool is_modification, bool invalidate);
|
||||
|
||||
/// Track that an image participates in upcoming GPU work with the given access type
|
||||
void TrackGpuImageAccess(ImageId image_id, ImageAccessType access);
|
||||
|
||||
/// Notify the cache that tracked GPU work has been submitted with the specified fence value
|
||||
void CommitPendingGpuAccesses(u64 fence_value);
|
||||
|
||||
/// Notify the cache that a fence value has completed so tracked accesses can be released
|
||||
void CompleteGpuAccesses(u64 completed_fence);
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
|
||||
private:
|
||||
@@ -408,6 +422,8 @@ private:
|
||||
/// Execute copies from one image to the other, even if they are incompatible
|
||||
void CopyImage(ImageId dst_id, ImageId src_id, std::vector<ImageCopy> copies);
|
||||
|
||||
void EnsureImageReady(ImageBase& image, ImageAccessType access);
|
||||
|
||||
/// Bind an image view as render target, downloading resources preemtively if needed
|
||||
void BindRenderTarget(ImageViewId* old_id, ImageViewId new_id);
|
||||
|
||||
@@ -429,6 +445,9 @@ private:
|
||||
|
||||
void QueueAsyncDecode(Image& image, ImageId image_id);
|
||||
void TickAsyncDecode();
|
||||
void EnforceSamplerBudget();
|
||||
void TrimInactiveSamplers(size_t budget);
|
||||
std::optional<size_t> QuerySamplerBudget() const;
|
||||
|
||||
Runtime& runtime;
|
||||
|
||||
@@ -462,6 +481,11 @@ private:
|
||||
Common::SlotId object_id;
|
||||
};
|
||||
|
||||
struct PendingImageAccess {
|
||||
ImageId image_id;
|
||||
ImageAccessType access;
|
||||
};
|
||||
|
||||
Common::SlotVector<Image> slot_images;
|
||||
Common::SlotVector<ImageMapView> slot_map_views;
|
||||
Common::SlotVector<ImageView> slot_image_views;
|
||||
@@ -477,6 +501,9 @@ private:
|
||||
std::vector<AsyncBuffer> uncommitted_async_buffers;
|
||||
std::deque<std::vector<AsyncBuffer>> async_buffers;
|
||||
std::deque<AsyncBuffer> async_buffers_death_ring;
|
||||
std::vector<PendingImageAccess> staged_gpu_accesses;
|
||||
std::deque<std::vector<PendingImageAccess>> committed_gpu_accesses;
|
||||
std::deque<u64> committed_gpu_ticks;
|
||||
|
||||
struct LRUItemParams {
|
||||
using ObjectType = ImageId;
|
||||
@@ -500,6 +527,7 @@ private:
|
||||
|
||||
u64 modification_tick = 0;
|
||||
u64 frame_tick = 0;
|
||||
u64 last_sampler_gc_frame = (std::numeric_limits<u64>::max)();
|
||||
|
||||
Common::ThreadWorker texture_decode_worker{1, "TextureDecoder"};
|
||||
std::vector<std::unique_ptr<AsyncDecodeContext>> async_decodes;
|
||||
|
||||