Compare commits
@@ -32,6 +32,10 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
cheats_url:
|
||||
required: false
|
||||
type: string
|
||||
default: https://github.com/PCSX2/pcsx2_patches/releases/latest/download
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
@@ -97,6 +101,11 @@ jobs:
|
||||
if: steps.cache-deps.outputs.cache-hit != 'true'
|
||||
run: .github/workflows/scripts/linux/build-dependencies-qt.sh
|
||||
|
||||
- name: Download cheats
|
||||
run: |
|
||||
cd bin/resources
|
||||
aria2c -Z "${{ inputs.cheats_url }}/cheats_ni.zip" "${{ inputs.cheats_url }}/cheats_ws.zip"
|
||||
|
||||
- name: Generate CMake
|
||||
env:
|
||||
COMPILER: ${{ inputs.compiler }}
|
||||
|
||||
@@ -28,6 +28,10 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
cheats_url:
|
||||
required: false
|
||||
type: string
|
||||
default: https://github.com/PCSX2/pcsx2_patches/releases/latest/download
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
@@ -115,6 +119,11 @@ jobs:
|
||||
fi
|
||||
sudo make -C 3rdparty/${{ env.SDL }} install
|
||||
|
||||
- name: Download cheats
|
||||
run: |
|
||||
cd bin/resources
|
||||
aria2c -Z "${{ inputs.cheats_url }}/cheats_ni.zip" "${{ inputs.cheats_url }}/cheats_ws.zip"
|
||||
|
||||
- name: Generate CMake
|
||||
env:
|
||||
PLATFORM: ${{ inputs.platform }}
|
||||
|
||||
@@ -17,6 +17,10 @@ on:
|
||||
gui:
|
||||
required: true
|
||||
type: string
|
||||
cheats_url:
|
||||
required: false
|
||||
type: string
|
||||
default: https://github.com/PCSX2/pcsx2_patches/releases/latest/download
|
||||
|
||||
jobs:
|
||||
build_macos:
|
||||
@@ -58,7 +62,8 @@ jobs:
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: 1
|
||||
HOMEBREW_NO_ANALYTICS: 1
|
||||
run: |
|
||||
brew unlink libjpeg libpng # Conflicts with our self-built dependencies
|
||||
brew unlink libjpeg || true # Conflicts with our self-built dependencies
|
||||
brew unlink libpng || true
|
||||
# Unlike other packages, brew's MoltenVK build uses MoltenVK's minimum macOS version of 10.13 so we can use it
|
||||
if ! brew install molten-vk; then
|
||||
brew update
|
||||
@@ -78,6 +83,11 @@ jobs:
|
||||
GUI: ${{ inputs.gui }}
|
||||
run: .github/workflows/scripts/macos/build-dependencies.sh
|
||||
|
||||
- name: Download cheats
|
||||
run: |
|
||||
cd bin/resources
|
||||
aria2c -Z "${{ inputs.cheats_url }}/cheats_ni.zip" "${{ inputs.cheats_url }}/cheats_ws.zip"
|
||||
|
||||
- name: Generate CMake Files
|
||||
run: |
|
||||
QT_BUILD=$([ "${{ inputs.gui }}" == "Qt" ] && echo "ON" || echo "OFF")
|
||||
|
||||
@@ -29,6 +29,10 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: https://github.com/PCSX2/pcsx2-windows-dependencies/releases/download/2022-07-24/qt-6.3.1-x64.7z
|
||||
cheats_url:
|
||||
required: false
|
||||
type: string
|
||||
default: https://github.com/PCSX2/pcsx2_patches/releases/latest/download
|
||||
|
||||
jobs:
|
||||
build_windows_qt:
|
||||
@@ -77,6 +81,12 @@ jobs:
|
||||
7z x qt-*-x64.7z
|
||||
del qt-*-x64.7z
|
||||
|
||||
- name: Download cheats
|
||||
shell: cmd
|
||||
run: |
|
||||
cd bin/resources
|
||||
aria2c -Z "${{ inputs.cheats_url }}/cheats_ni.zip" "${{ inputs.cheats_url }}/cheats_ws.zip"
|
||||
|
||||
- name: Build PCSX2
|
||||
shell: cmd
|
||||
run: |
|
||||
|
||||
@@ -25,6 +25,10 @@ on:
|
||||
configuration:
|
||||
required: true
|
||||
type: string
|
||||
cheats_url:
|
||||
required: false
|
||||
type: string
|
||||
default: https://github.com/PCSX2/pcsx2_patches/releases/latest/download
|
||||
|
||||
jobs:
|
||||
build_windows_wx:
|
||||
@@ -71,6 +75,12 @@ jobs:
|
||||
if: inputs.configuration != 'CMake'
|
||||
uses: microsoft/setup-msbuild@v1
|
||||
|
||||
- name: Download cheats
|
||||
shell: cmd
|
||||
run: |
|
||||
cd bin/resources
|
||||
aria2c -Z "${{ inputs.cheats_url }}/cheats_ni.zip" "${{ inputs.cheats_url }}/cheats_ws.zip"
|
||||
|
||||
- name: Generate CMake
|
||||
if: inputs.configuration == 'CMake'
|
||||
id: cmake
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
03000000d0160000600a000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,
|
||||
03000000c82d00000031000000000000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00000531000000000000,8BitDo Adapter 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00000951000000000000,8BitDo Dogbone Modkit,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,
|
||||
03000000c82d00000951000000000000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows,
|
||||
03000000008000000210000000000000,8BitDo F30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
|
||||
030000003512000011ab000000000000,8BitDo F30 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000c82d00001028000000000000,8BitDo F30 Arcade Joystick,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,
|
||||
@@ -19,14 +19,15 @@
|
||||
03000000801000000900000000000000,8BitDo F30 Arcade Stick,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001038000000000000,8BitDo F30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00001151000000000000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000151000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00005106000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,guide:b2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00000151000000000000,8BitDo M30 Modkit,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00000451000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows,
|
||||
03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00008010000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d0000e002000000000000,8BitDo N30,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b6,platform:Windows,
|
||||
03000000c82d00000451000000000000,8BitDo N30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,start:b11,platform:Windows,
|
||||
03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
@@ -37,7 +38,8 @@
|
||||
03000000022000000090000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000203800000900000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00002038000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000751000000000000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00000751000000000000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00000851000000000000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00000360000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000361000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000660000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
@@ -45,7 +47,7 @@
|
||||
03000000c82d00000231000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000331000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000431000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00002867000000000000,8BitDo S30 Modkit,a:b0,b:b1,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00002867000000000000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b10,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000061000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
|
||||
@@ -54,11 +56,11 @@
|
||||
03000000c82d00003028000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
|
||||
030000003512000020ab000000000000,8BitDo SN30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000351000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a2,rightshoulder:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000351000000000000,8BitDo SN30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000021000000000000,8BitDo SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
|
||||
03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
|
||||
@@ -120,6 +122,7 @@
|
||||
030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,
|
||||
03000000120c0000200e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000120c0000210e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000120c0000f10e000000000000,Brook PS2 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
|
||||
03000000d81d00000b00000000000000,Buffalo BSGP1601 Series,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,
|
||||
030000005b1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,
|
||||
@@ -234,14 +237,14 @@
|
||||
030000000d0f00005100000000000000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
|
||||
030000000d0f0000ba00000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
|
||||
030000000d0f00008800000000000000,Hori Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,
|
||||
030000000d0f00008700000000000000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
|
||||
030000000d0f00001000000000000000,Hori Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
030000000d0f00003200000000000000,Hori Fightstick 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
030000000d0f0000c000000000000000,Hori Fightstick 4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
|
||||
030000000d0f00000d00000000000000,Hori Fightstick EX2,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,
|
||||
030000000d0f00003701000000000000,Hori Fightstick Mini,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Windows,
|
||||
030000000d0f00004000000000000000,Hori Fightstick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,
|
||||
030000000d0f00008800000000000000,Hori Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,
|
||||
030000000d0f00008700000000000000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
|
||||
030000000d0f00002100000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
030000000d0f00002700000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
|
||||
030000000d0f0000a000000000000000,Hori Grip TAC4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b13,x:b0,y:b3,platform:Windows,
|
||||
@@ -482,7 +485,6 @@
|
||||
03000000120c00001e0e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000120c0000a957000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000120c0000aa57000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000120c0000f10e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000120c0000f21c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000120c0000f31c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000120c0000f41c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
@@ -740,11 +742,17 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000008f0e00000300000009010000,2 In 1 Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
|
||||
03000000c82d00000031000001000000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000c82d00000531000000020000,8BitDo Adapter 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
|
||||
03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00001151000000020000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000a30c00002400000006020000,8BitDo M30,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,guide:b9,leftshoulder:b6,lefttrigger:b5,rightshoulder:b4,righttrigger:b7,start:b8,x:b3,y:b0,platform:Mac OS X,
|
||||
03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,lefttrigger:a5,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
|
||||
03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
030000003512000012ab000001000000,8BitDo NES30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
@@ -753,14 +761,17 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000022000000090000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000190000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,x:b4,y:b3,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Mac OS X,
|
||||
03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000660000000020000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000131000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000231000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000331000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000431000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00002867000000010000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000102800000900000000000000,8BitDo SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00004028000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
@@ -958,11 +969,16 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
# Linux
|
||||
030000005e0400008e02000020010000,8BitDo Adapter,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000c82d00000031000011010000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux,
|
||||
03000000021000000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00001151000011010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux,
|
||||
03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000008000000210000011010000,8BitDo NES30,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
|
||||
@@ -972,12 +988,15 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000c82d00000190000011010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000231000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000331000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000431000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00002867000000010000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Linux,
|
||||
05000000c82d00000060000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
030000003512000012ab000010010000,8BitDo SFC30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,
|
||||
@@ -985,6 +1004,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000102800000900000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00003028000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,
|
||||
@@ -1079,10 +1099,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000000d0f00005001000009040000,Hori Fighting Commander OCTA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000000d0f00008500000010010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000000d0f00008600000002010000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
030000000d0f00001000000011010000,Hori Fightstick 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000ad1b000003f5000033050000,Hori Fightstick VX,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b8,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,
|
||||
030000000d0f00008800000011010000,Hori Fighting Stick mini 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000000d0f00008700000011010000,Hori Fighting Stick mini 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000000d0f00001000000011010000,Hori Fightstick 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000ad1b000003f5000033050000,Hori Fightstick VX,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b8,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,
|
||||
030000000d0f00004d00000011010000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000000d0f00003801000011010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Linux,
|
||||
@@ -1209,8 +1229,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b16,b:b15,back:b4,leftshoulder:b6,leftstick:b12,leftx:a1,lefty:a0~,rightshoulder:b8,start:b9,x:b14,y:b17,platform:Linux,
|
||||
03000000d620000013a7000011010000,Nintendo Switch PowerA Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000d620000011a7000011010000,Nintendo Switch PowerA Core Plus Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
|
||||
050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,,misc1:b4,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
|
||||
050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
|
||||
050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux,
|
||||
05000000010000000100000003000000,Nintendo Wii Remote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
@@ -1440,6 +1460,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
050000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
050000005e040000130b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
030000005e040000120b000007050000,Xbox Series X Controller,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,misc1:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
|
||||
050000005e040000130b000007050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,
|
||||
|
||||
@@ -750,7 +750,7 @@ void ps_color_clamp_wrap(inout float3 C)
|
||||
}
|
||||
}
|
||||
|
||||
void ps_blend(inout float4 Color, float As, float2 pos_xy)
|
||||
void ps_blend(inout float4 Color, inout float As, float2 pos_xy)
|
||||
{
|
||||
if (SW_BLEND)
|
||||
{
|
||||
@@ -775,10 +775,38 @@ void ps_blend(inout float4 Color, float As, float2 pos_xy)
|
||||
float3 D = (PS_BLEND_D == 0) ? Cs : ((PS_BLEND_D == 1) ? Cd : (float3)0.0f);
|
||||
|
||||
// As/Af clamp alpha for Blend mix
|
||||
if (PS_BLEND_MIX)
|
||||
C = min(C, (float)1.0f);
|
||||
// We shouldn't clamp blend mix with clr1 as we want alpha higher
|
||||
if (PS_BLEND_MIX && PS_CLR_HW != 1)
|
||||
C = min(C, 1.0f);
|
||||
|
||||
Color.rgb = (PS_BLEND_A == PS_BLEND_B) ? D : trunc(((A - B) * C) + D);
|
||||
if (PS_BLEND_A == PS_BLEND_B)
|
||||
Color.rgb = D;
|
||||
else if (PS_BLEND_MIX)
|
||||
// In blend_mix, HW adds on some alpha factor * dst.
|
||||
// Truncating here wouldn't quite get the right result because it prevents the <1 bit here from combining with a <1 bit in dst to form a ≥1 amount that pushes over the truncation.
|
||||
// Instead, apply an offset to convert HW's round to a floor.
|
||||
// Since alpha is in 1/128 increments, subtracting (0.5 - 0.5/128 == 127/256) would get us what we want if GPUs blended in full precision.
|
||||
// But they don't. Details here: https://github.com/PCSX2/pcsx2/pull/6809#issuecomment-1211473399
|
||||
// Based on the scripts at the above link, the ideal choice for Intel GPUs is 126/256, AMD 120/256. Nvidia is a lost cause.
|
||||
// 124/256 seems like a reasonable compromise, providing the correct answer 99.3% of the time on Intel (vs 99.6% for 126/256), and 97% of the time on AMD (vs 97.4% for 120/256).
|
||||
Color.rgb = ((A - B) * C + D) - (124.0f/256.0f);
|
||||
else
|
||||
Color.rgb = trunc(((A - B) * C) + D);
|
||||
|
||||
if (PS_CLR_HW == 1)
|
||||
{
|
||||
// Replace Af with As so we can do proper compensation for Alpha.
|
||||
if (PS_BLEND_C == 2)
|
||||
As = Af;
|
||||
// Subtract 1 for alpha to compensate for the changed equation,
|
||||
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
|
||||
// we pick the lowest overflow from all colors because it's the safest,
|
||||
// we divide by 255 the color because we don't know Cd value,
|
||||
// changed alpha should only be done for hw blend.
|
||||
float min_color = min(min(Color.r, Color.g), Color.b);
|
||||
float alpha_compensate = max(1.0f, min_color / 255.0f);
|
||||
As -= alpha_compensate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -689,7 +689,7 @@ void ps_color_clamp_wrap(inout vec3 C)
|
||||
#endif
|
||||
}
|
||||
|
||||
void ps_blend(inout vec4 Color, float As)
|
||||
void ps_blend(inout vec4 Color, inout float As)
|
||||
{
|
||||
#if SW_BLEND
|
||||
|
||||
@@ -754,16 +754,41 @@ void ps_blend(inout vec4 Color, float As)
|
||||
#endif
|
||||
|
||||
// As/Af clamp alpha for Blend mix
|
||||
#if PS_BLEND_MIX
|
||||
C = min(C, float(1.0f));
|
||||
// We shouldn't clamp blend mix with clr1 as we want alpha higher
|
||||
#if PS_BLEND_MIX && PS_CLR_HW != 1
|
||||
C = min(C, 1.0f);
|
||||
#endif
|
||||
|
||||
#if PS_BLEND_A == PS_BLEND_B
|
||||
Color.rgb = D;
|
||||
#elif PS_BLEND_MIX
|
||||
// In blend_mix, HW adds on some alpha factor * dst.
|
||||
// Truncating here wouldn't quite get the right result because it prevents the <1 bit here from combining with a <1 bit in dst to form a ≥1 amount that pushes over the truncation.
|
||||
// Instead, apply an offset to convert HW's round to a floor.
|
||||
// Since alpha is in 1/128 increments, subtracting (0.5 - 0.5/128 == 127/256) would get us what we want if GPUs blended in full precision.
|
||||
// But they don't. Details here: https://github.com/PCSX2/pcsx2/pull/6809#issuecomment-1211473399
|
||||
// Based on the scripts at the above link, the ideal choice for Intel GPUs is 126/256, AMD 120/256. Nvidia is a lost cause.
|
||||
// 124/256 seems like a reasonable compromise, providing the correct answer 99.3% of the time on Intel (vs 99.6% for 126/256), and 97% of the time on AMD (vs 97.4% for 120/256).
|
||||
Color.rgb = ((A - B) * C + D) - (124.0f/256.0f);
|
||||
#else
|
||||
Color.rgb = trunc((A - B) * C + D);
|
||||
#endif
|
||||
|
||||
#if PS_CLR_HW == 1
|
||||
// Replace Af with As so we can do proper compensation for Alpha.
|
||||
#if PS_BLEND_C == 2
|
||||
As = Af;
|
||||
#endif
|
||||
// Subtract 1 for alpha to compensate for the changed equation,
|
||||
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
|
||||
// we pick the lowest overflow from all colors because it's the safest,
|
||||
// we divide by 255 the color because we don't know Cd value,
|
||||
// changed alpha should only be done for hw blend.
|
||||
float min_color = min(min(Color.r, Color.g), Color.b);
|
||||
float alpha_compensate = max(1.0f, min_color / 255.0f);
|
||||
As -= alpha_compensate;
|
||||
#endif
|
||||
|
||||
#else
|
||||
// Needed for Cd * (As/Ad/F + 1) blending modes
|
||||
#if PS_CLR_HW == 1 || PS_CLR_HW == 5
|
||||
|
||||
@@ -990,7 +990,7 @@ void ps_color_clamp_wrap(inout vec3 C)
|
||||
#endif
|
||||
}
|
||||
|
||||
void ps_blend(inout vec4 Color, float As)
|
||||
void ps_blend(inout vec4 Color, inout float As)
|
||||
{
|
||||
#if SW_BLEND
|
||||
|
||||
@@ -1053,16 +1053,41 @@ void ps_blend(inout vec4 Color, float As)
|
||||
#endif
|
||||
|
||||
// As/Af clamp alpha for Blend mix
|
||||
#if PS_BLEND_MIX
|
||||
// We shouldn't clamp blend mix with clr1 as we want alpha higher
|
||||
#if PS_BLEND_MIX && PS_CLR_HW != 1
|
||||
C = min(C, 1.0f);
|
||||
#endif
|
||||
|
||||
#if PS_BLEND_A == PS_BLEND_B
|
||||
Color.rgb = D;
|
||||
#elif PS_BLEND_MIX
|
||||
// In blend_mix, HW adds on some alpha factor * dst.
|
||||
// Truncating here wouldn't quite get the right result because it prevents the <1 bit here from combining with a <1 bit in dst to form a ≥1 amount that pushes over the truncation.
|
||||
// Instead, apply an offset to convert HW's round to a floor.
|
||||
// Since alpha is in 1/128 increments, subtracting (0.5 - 0.5/128 == 127/256) would get us what we want if GPUs blended in full precision.
|
||||
// But they don't. Details here: https://github.com/PCSX2/pcsx2/pull/6809#issuecomment-1211473399
|
||||
// Based on the scripts at the above link, the ideal choice for Intel GPUs is 126/256, AMD 120/256. Nvidia is a lost cause.
|
||||
// 124/256 seems like a reasonable compromise, providing the correct answer 99.3% of the time on Intel (vs 99.6% for 126/256), and 97% of the time on AMD (vs 97.4% for 120/256).
|
||||
Color.rgb = ((A - B) * C + D) - (124.0f/256.0f);
|
||||
#else
|
||||
Color.rgb = trunc((A - B) * C + D);
|
||||
#endif
|
||||
|
||||
#if PS_CLR_HW == 1
|
||||
// Replace Af with As so we can do proper compensation for Alpha.
|
||||
#if PS_BLEND_C == 2
|
||||
As = Af;
|
||||
#endif
|
||||
// Subtract 1 for alpha to compensate for the changed equation,
|
||||
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
|
||||
// we pick the lowest overflow from all colors because it's the safest,
|
||||
// we divide by 255 the color because we don't know Cd value,
|
||||
// changed alpha should only be done for hw blend.
|
||||
float min_color = min(min(Color.r, Color.g), Color.b);
|
||||
float alpha_compensate = max(1.0f, min_color / 255.0f);
|
||||
As -= alpha_compensate;
|
||||
#endif
|
||||
|
||||
#else
|
||||
#if PS_CLR_HW == 1 || PS_CLR_HW == 5
|
||||
// Needed for Cd * (As/Ad/F + 1) blending modes
|
||||
|
||||
@@ -56,6 +56,8 @@ target_sources(pcsx2-qt PRIVATE
|
||||
Settings/ControllerGlobalSettingsWidget.cpp
|
||||
Settings/ControllerGlobalSettingsWidget.h
|
||||
Settings/ControllerGlobalSettingsWidget.ui
|
||||
Settings/ControllerMacroEditWidget.ui
|
||||
Settings/ControllerMacroWidget.ui
|
||||
Settings/ControllerSettingsDialog.cpp
|
||||
Settings/ControllerSettingsDialog.h
|
||||
Settings/ControllerSettingsDialog.ui
|
||||
|
||||
@@ -639,13 +639,10 @@ void MainWindow::setStyleFromSettings()
|
||||
// Alternative dark theme.
|
||||
qApp->setStyle(QStyleFactory::create("Fusion"));
|
||||
|
||||
const QColor lighterGray(75, 75, 75);
|
||||
const QColor gray(128, 128, 128);
|
||||
const QColor black(25, 25, 25);
|
||||
const QColor slate(18, 18, 18);
|
||||
const QColor rubyish(172, 21, 31);
|
||||
|
||||
|
||||
QPalette darkPalette;
|
||||
darkPalette.setColor(QPalette::Window, slate);
|
||||
darkPalette.setColor(QPalette::WindowText, Qt::white);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <QtWidgets/QDoubleSpinBox>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QSlider>
|
||||
#include <QtWidgets/QSpinBox>
|
||||
|
||||
@@ -41,6 +42,10 @@
|
||||
|
||||
namespace SettingWidgetBinder
|
||||
{
|
||||
static constexpr const char* NULLABLE_PROPERTY = "SettingWidgetBinder_isNullable";
|
||||
static constexpr const char* IS_NULL_PROPERTY = "SettingWidgetBinder_isNull";
|
||||
static constexpr const char* GLOBAL_VALUE_PROPERTY = "SettingWidgetBinder_globalValue";
|
||||
|
||||
template <typename T>
|
||||
struct SettingAccessor
|
||||
{
|
||||
@@ -179,7 +184,7 @@ namespace SettingWidgetBinder
|
||||
}
|
||||
static void setNullableStringValue(QComboBox* widget, std::optional<QString> value)
|
||||
{
|
||||
isNullValue(widget) ? widget->setCurrentIndex(0) : setStringValue(widget, value.value());
|
||||
value.has_value() ? setStringValue(widget, value.value()) : widget->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
@@ -286,71 +291,256 @@ namespace SettingWidgetBinder
|
||||
template <>
|
||||
struct SettingAccessor<QSpinBox>
|
||||
{
|
||||
static bool isNullable(const QSpinBox* widget) { return widget->property(NULLABLE_PROPERTY).toBool(); }
|
||||
|
||||
static void updateNullState(QSpinBox* widget, bool isNull)
|
||||
{
|
||||
widget->setPrefix(isNull ? qApp->translate("SettingWidgetBinder", "Default: ") : QString());
|
||||
}
|
||||
|
||||
static bool getBoolValue(const QSpinBox* widget) { return widget->value() > 0; }
|
||||
static void setBoolValue(QSpinBox* widget, bool value) { widget->setValue(value ? 1 : 0); }
|
||||
static void makeNullableBool(QSpinBox* widget, bool globalSetting) { widget->setEnabled(false); }
|
||||
static std::optional<bool> getNullableBoolValue(const QSpinBox* widget) { return getBoolValue(widget); }
|
||||
static void setNullableBoolValue(QSpinBox* widget, std::optional<bool> value) { setBoolValue(widget, value.value_or(false)); }
|
||||
static void makeNullableBool(QSpinBox* widget, bool globalSetting)
|
||||
{
|
||||
widget->setProperty(NULLABLE_PROPERTY, QVariant(true));
|
||||
widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalSetting));
|
||||
}
|
||||
static std::optional<bool> getNullableBoolValue(const QSpinBox* widget)
|
||||
{
|
||||
if (widget->property(IS_NULL_PROPERTY).toBool())
|
||||
return std::nullopt;
|
||||
|
||||
return getBoolValue(widget);
|
||||
}
|
||||
static void setNullableBoolValue(QSpinBox* widget, std::optional<bool> value)
|
||||
{
|
||||
widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value()));
|
||||
setBoolValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toBool());
|
||||
updateNullState(widget, !value.has_value());
|
||||
}
|
||||
|
||||
static int getIntValue(const QSpinBox* widget) { return widget->value(); }
|
||||
static void setIntValue(QSpinBox* widget, int value) { widget->setValue(value); }
|
||||
static void makeNullableInt(QSpinBox* widget, int globalValue) { widget->setEnabled(false); }
|
||||
static std::optional<int> getNullableIntValue(const QSpinBox* widget) { return getIntValue(widget); }
|
||||
static void setNullableIntValue(QSpinBox* widget, std::optional<int> value) { setIntValue(widget, value.value_or(0)); }
|
||||
static void makeNullableInt(QSpinBox* widget, int globalValue)
|
||||
{
|
||||
widget->setProperty(NULLABLE_PROPERTY, QVariant(true));
|
||||
widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalValue));
|
||||
}
|
||||
static std::optional<int> getNullableIntValue(const QSpinBox* widget)
|
||||
{
|
||||
if (widget->property(IS_NULL_PROPERTY).toBool())
|
||||
return std::nullopt;
|
||||
|
||||
return getIntValue(widget);
|
||||
}
|
||||
static void setNullableIntValue(QSpinBox* widget, std::optional<int> value)
|
||||
{
|
||||
widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value()));
|
||||
setIntValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toInt());
|
||||
updateNullState(widget, !value.has_value());
|
||||
}
|
||||
|
||||
static float getFloatValue(const QSpinBox* widget) { return static_cast<float>(widget->value()); }
|
||||
static void setFloatValue(QSpinBox* widget, float value) { widget->setValue(static_cast<int>(value)); }
|
||||
static void makeNullableFloat(QSpinBox* widget, float globalValue) { widget->setEnabled(false); }
|
||||
static std::optional<float> getNullableFloatValue(const QSpinBox* widget) { return getFloatValue(widget); }
|
||||
static void setNullableFloatValue(QSpinBox* widget, std::optional<float> value) { setFloatValue(widget, value.value_or(0.0f)); }
|
||||
static void makeNullableFloat(QSpinBox* widget, float globalValue)
|
||||
{
|
||||
widget->setProperty(NULLABLE_PROPERTY, QVariant(true));
|
||||
widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalValue));
|
||||
}
|
||||
static std::optional<float> getNullableFloatValue(const QSpinBox* widget)
|
||||
{
|
||||
if (widget->property(IS_NULL_PROPERTY).toBool())
|
||||
return std::nullopt;
|
||||
|
||||
return getFloatValue(widget);
|
||||
}
|
||||
static void setNullableFloatValue(QSpinBox* widget, std::optional<float> value)
|
||||
{
|
||||
widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value()));
|
||||
setFloatValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toFloat());
|
||||
updateNullState(widget, !value.has_value());
|
||||
}
|
||||
|
||||
static QString getStringValue(const QSpinBox* widget) { return QString::number(widget->value()); }
|
||||
static void setStringValue(QSpinBox* widget, const QString& value) { widget->setValue(value.toInt()); }
|
||||
static void makeNullableString(QSpinBox* widget, const QString& globalValue) { widget->setEnabled(false); }
|
||||
static std::optional<QString> getNullableStringValue(const QSpinBox* widget) { return getStringValue(widget); }
|
||||
static void setNullableStringValue(QSpinBox* widget, std::optional<QString> value) { setStringValue(widget, value.value_or(QString())); }
|
||||
static void makeNullableString(QSpinBox* widget, const QString& globalValue)
|
||||
{
|
||||
widget->setProperty(NULLABLE_PROPERTY, QVariant(true));
|
||||
widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalValue));
|
||||
}
|
||||
static std::optional<QString> getNullableStringValue(const QSpinBox* widget)
|
||||
{
|
||||
if (widget->property(IS_NULL_PROPERTY).toBool())
|
||||
return std::nullopt;
|
||||
|
||||
return getStringValue(widget);
|
||||
}
|
||||
static void setNullableStringValue(QSpinBox* widget, std::optional<QString> value)
|
||||
{
|
||||
widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value()));
|
||||
setStringValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toString());
|
||||
updateNullState(widget, !value.has_value());
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static void connectValueChanged(QSpinBox* widget, F func)
|
||||
{
|
||||
widget->connect(widget, QOverload<int>::of(&QSpinBox::valueChanged), func);
|
||||
if (!isNullable(widget))
|
||||
{
|
||||
widget->connect(widget, QOverload<int>::of(&QSpinBox::valueChanged), func);
|
||||
}
|
||||
else
|
||||
{
|
||||
widget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
widget->connect(widget, &QSpinBox::customContextMenuRequested, widget, [widget, func](const QPoint& pt) {
|
||||
QMenu menu(widget);
|
||||
widget->connect(menu.addAction(qApp->translate("SettingWidgetBinder", "Reset")), &QAction::triggered, widget,
|
||||
[widget, func = std::move(func)]() {
|
||||
const bool old = widget->blockSignals(true);
|
||||
setNullableIntValue(widget, std::nullopt);
|
||||
widget->blockSignals(old);
|
||||
updateNullState(widget, true);
|
||||
func();
|
||||
});
|
||||
menu.exec(widget->mapToGlobal(pt));
|
||||
});
|
||||
widget->connect(widget, &QSpinBox::valueChanged, widget, [widget, func = std::move(func)]() {
|
||||
if (widget->property(IS_NULL_PROPERTY).toBool())
|
||||
{
|
||||
widget->setProperty(IS_NULL_PROPERTY, QVariant(false));
|
||||
updateNullState(widget, false);
|
||||
}
|
||||
func();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SettingAccessor<QDoubleSpinBox>
|
||||
{
|
||||
static bool isNullable(const QDoubleSpinBox* widget) { return widget->property(NULLABLE_PROPERTY).toBool(); }
|
||||
|
||||
static void updateNullState(QDoubleSpinBox* widget, bool isNull)
|
||||
{
|
||||
widget->setPrefix(isNull ? qApp->translate("SettingWidgetBinder", "Default: ") : QString());
|
||||
}
|
||||
|
||||
static bool getBoolValue(const QDoubleSpinBox* widget) { return widget->value() > 0.0; }
|
||||
static void setBoolValue(QDoubleSpinBox* widget, bool value) { widget->setValue(value ? 1.0 : 0.0); }
|
||||
static void makeNullableBool(QDoubleSpinBox* widget, bool globalSetting) { widget->setEnabled(false); }
|
||||
static std::optional<bool> getNullableBoolValue(const QDoubleSpinBox* widget) { return getBoolValue(widget); }
|
||||
static void setNullableBoolValue(QDoubleSpinBox* widget, std::optional<bool> value) { setBoolValue(widget, value.value_or(false)); }
|
||||
static void makeNullableBool(QDoubleSpinBox* widget, bool globalSetting)
|
||||
{
|
||||
widget->setProperty(NULLABLE_PROPERTY, QVariant(true));
|
||||
widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalSetting));
|
||||
}
|
||||
static std::optional<bool> getNullableBoolValue(const QDoubleSpinBox* widget)
|
||||
{
|
||||
if (widget->property(IS_NULL_PROPERTY).toBool())
|
||||
return std::nullopt;
|
||||
|
||||
return getBoolValue(widget);
|
||||
}
|
||||
static void setNullableBoolValue(QDoubleSpinBox* widget, std::optional<bool> value)
|
||||
{
|
||||
widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value()));
|
||||
setBoolValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toBool());
|
||||
updateNullState(widget, !value.has_value());
|
||||
}
|
||||
|
||||
static int getIntValue(const QDoubleSpinBox* widget) { return static_cast<int>(widget->value()); }
|
||||
static void setIntValue(QDoubleSpinBox* widget, int value) { widget->setValue(static_cast<double>(value)); }
|
||||
static void makeNullableInt(QDoubleSpinBox* widget, int globalValue) { widget->setEnabled(false); }
|
||||
static std::optional<int> getNullableIntValue(const QDoubleSpinBox* widget) { return getIntValue(widget); }
|
||||
static void setNullableIntValue(QDoubleSpinBox* widget, std::optional<int> value) { setIntValue(widget, value.value_or(0)); }
|
||||
static void makeNullableInt(QDoubleSpinBox* widget, int globalValue)
|
||||
{
|
||||
widget->setProperty(NULLABLE_PROPERTY, QVariant(true));
|
||||
widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalValue));
|
||||
}
|
||||
static std::optional<int> getNullableIntValue(const QDoubleSpinBox* widget)
|
||||
{
|
||||
if (widget->property(IS_NULL_PROPERTY).toBool())
|
||||
return std::nullopt;
|
||||
|
||||
return getIntValue(widget);
|
||||
}
|
||||
static void setNullableIntValue(QDoubleSpinBox* widget, std::optional<int> value)
|
||||
{
|
||||
widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value()));
|
||||
setIntValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toInt());
|
||||
updateNullState(widget, !value.has_value());
|
||||
}
|
||||
|
||||
static float getFloatValue(const QDoubleSpinBox* widget) { return static_cast<float>(widget->value()); }
|
||||
static void setFloatValue(QDoubleSpinBox* widget, float value) { widget->setValue(static_cast<double>(value)); }
|
||||
static void makeNullableFloat(QDoubleSpinBox* widget, float globalValue) { widget->setEnabled(false); }
|
||||
static std::optional<float> getNullableFloatValue(const QDoubleSpinBox* widget) { return getFloatValue(widget); }
|
||||
static void setNullableFloatValue(QDoubleSpinBox* widget, std::optional<float> value) { setFloatValue(widget, value.value_or(0.0f)); }
|
||||
static void makeNullableFloat(QDoubleSpinBox* widget, float globalValue)
|
||||
{
|
||||
widget->setProperty(NULLABLE_PROPERTY, QVariant(true));
|
||||
widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalValue));
|
||||
}
|
||||
static std::optional<float> getNullableFloatValue(const QDoubleSpinBox* widget)
|
||||
{
|
||||
if (widget->property(IS_NULL_PROPERTY).toBool())
|
||||
return std::nullopt;
|
||||
|
||||
return getFloatValue(widget);
|
||||
}
|
||||
static void setNullableFloatValue(QDoubleSpinBox* widget, std::optional<float> value)
|
||||
{
|
||||
widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value()));
|
||||
setFloatValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toFloat());
|
||||
updateNullState(widget, !value.has_value());
|
||||
}
|
||||
|
||||
static QString getStringValue(const QDoubleSpinBox* widget) { return QString::number(widget->value()); }
|
||||
static void setStringValue(QDoubleSpinBox* widget, const QString& value) { widget->setValue(value.toDouble()); }
|
||||
static void makeNullableString(QDoubleSpinBox* widget, const QString& globalValue) { widget->setEnabled(false); }
|
||||
static std::optional<QString> getNullableStringValue(const QDoubleSpinBox* widget) { return getStringValue(widget); }
|
||||
static void makeNullableString(QDoubleSpinBox* widget, const QString& globalValue)
|
||||
{
|
||||
widget->setProperty(NULLABLE_PROPERTY, QVariant(true));
|
||||
widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalValue));
|
||||
}
|
||||
static std::optional<QString> getNullableStringValue(const QDoubleSpinBox* widget)
|
||||
{
|
||||
if (widget->property(IS_NULL_PROPERTY).toBool())
|
||||
return std::nullopt;
|
||||
|
||||
return getStringValue(widget);
|
||||
}
|
||||
static void setNullableStringValue(QDoubleSpinBox* widget, std::optional<QString> value)
|
||||
{
|
||||
setStringValue(widget, value.value_or(QString()));
|
||||
widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value()));
|
||||
setStringValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toString());
|
||||
updateNullState(widget, !value.has_value());
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
static void connectValueChanged(QDoubleSpinBox* widget, F func)
|
||||
{
|
||||
widget->connect(widget, QOverload<double>::of(&QDoubleSpinBox::valueChanged), func);
|
||||
if (!isNullable(widget))
|
||||
{
|
||||
widget->connect(widget, QOverload<double>::of(&QDoubleSpinBox::valueChanged), func);
|
||||
}
|
||||
else
|
||||
{
|
||||
widget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
widget->connect(widget, &QDoubleSpinBox::customContextMenuRequested, widget, [widget, func](const QPoint& pt) {
|
||||
QMenu menu(widget);
|
||||
widget->connect(menu.addAction(qApp->translate("SettingWidgetBinder", "Reset")), &QAction::triggered, widget,
|
||||
[widget, func = std::move(func)]() {
|
||||
const bool old = widget->blockSignals(true);
|
||||
setNullableFloatValue(widget, std::nullopt);
|
||||
widget->blockSignals(old);
|
||||
updateNullState(widget, true);
|
||||
func();
|
||||
});
|
||||
menu.exec(widget->mapToGlobal(pt));
|
||||
});
|
||||
widget->connect(widget, QOverload<double>::of(&QDoubleSpinBox::valueChanged), widget, [widget, func = std::move(func)]() {
|
||||
if (widget->property(IS_NULL_PROPERTY).toBool())
|
||||
{
|
||||
widget->setProperty(IS_NULL_PROPERTY, QVariant(false));
|
||||
updateNullState(widget, false);
|
||||
}
|
||||
func();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -776,14 +966,15 @@ namespace SettingWidgetBinder
|
||||
}
|
||||
|
||||
template <typename WidgetType>
|
||||
static void BindWidgetToFolderSetting(SettingsInterface* sif, WidgetType* widget,
|
||||
QAbstractButton* browse_button, QAbstractButton* open_button, QAbstractButton* reset_button,
|
||||
std::string section, std::string key, std::string default_value)
|
||||
static void BindWidgetToFolderSetting(SettingsInterface* sif, WidgetType* widget, QAbstractButton* browse_button, QAbstractButton* open_button,
|
||||
QAbstractButton* reset_button, std::string section, std::string key, std::string default_value)
|
||||
{
|
||||
using Accessor = SettingAccessor<WidgetType>;
|
||||
|
||||
std::string current_path(Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str()));
|
||||
if (!Path::IsAbsolute(current_path))
|
||||
if (current_path.empty())
|
||||
current_path = default_value;
|
||||
else if (!Path::IsAbsolute(current_path))
|
||||
current_path = Path::Combine(EmuFolders::DataRoot, current_path);
|
||||
|
||||
const QString value(QString::fromStdString(current_path));
|
||||
@@ -836,9 +1027,8 @@ namespace SettingWidgetBinder
|
||||
}
|
||||
if (reset_button)
|
||||
{
|
||||
QObject::connect(reset_button, &QAbstractButton::clicked, reset_button, [widget, default_value = std::move(default_value)]() {
|
||||
Accessor::setStringValue(widget, QString::fromStdString(Path::Combine(EmuFolders::AppRoot, default_value)));
|
||||
});
|
||||
QObject::connect(reset_button, &QAbstractButton::clicked, reset_button,
|
||||
[widget, default_value = std::move(default_value)]() { Accessor::setStringValue(widget, QString::fromStdString(default_value)); });
|
||||
}
|
||||
}
|
||||
} // namespace SettingWidgetBinder
|
||||
|
||||
@@ -39,11 +39,11 @@ BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent)
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.patchRegion, "EmuCore", "PatchBios", false);
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.regionComboBox, "EmuCore", "PatchRegion", BiosZoneStrings, BiosZoneBytes, BiosZoneBytes[0]);
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.searchDirectory, m_ui.browseSearchDirectory, m_ui.openSearchDirectory,
|
||||
m_ui.resetSearchDirectory, "Folders", "Bios", "bios");
|
||||
m_ui.resetSearchDirectory, "Folders", "Bios", Path::Combine(EmuFolders::DataRoot, "bios"));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.patchRegion, tr("Patch Region"), tr("Unchecked"),
|
||||
tr("Patches the BIOS region byte in ROM. Not recommended unless you really know what you're doing."));
|
||||
dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"),
|
||||
dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Checked"),
|
||||
tr("Patches the BIOS to skip the console's boot animation."));
|
||||
|
||||
refreshList();
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>833</width>
|
||||
<height>560</height>
|
||||
<height>617</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -31,9 +31,73 @@
|
||||
<property name="title">
|
||||
<string>Controller Type</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QComboBox" name="controllerType"/>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="controllerType"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="bindings">
|
||||
<property name="text">
|
||||
<string>Bindings</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="gamepad-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="settings">
|
||||
<property name="text">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="checkbox-multiple-blank-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="macros">
|
||||
<property name="text">
|
||||
<string>Macros</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="flashlight-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
@@ -51,25 +115,31 @@
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="automaticBinding">
|
||||
<widget class="QToolButton" name="automaticBinding">
|
||||
<property name="text">
|
||||
<string>Automatic binding</string>
|
||||
<string>Automatic Mapping</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="gamepad-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="clearBindings">
|
||||
<widget class="QToolButton" name="clearBindings">
|
||||
<property name="text">
|
||||
<string>Clear Bindings</string>
|
||||
<string>Clear Mapping</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="file-reduce-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -77,6 +147,9 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stackedWidget" />
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
|
||||
@@ -415,25 +415,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="LargeMotorScale">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string>x</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>3.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -488,74 +469,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QGroupBox" name="groupBox_25">
|
||||
<property name="title">
|
||||
<string>Select</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_25">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="InputBindingWidget" name="Select">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QGroupBox" name="groupBox_26">
|
||||
<property name="title">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_26">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="InputBindingWidget" name="Start">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QGroupBox" name="groupBox_23">
|
||||
<property name="title">
|
||||
@@ -624,62 +537,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QGroupBox" name="groupBox_34">
|
||||
<property name="title">
|
||||
<string>Pressure Modifier</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_34">
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="PressureModifier">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string>x</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.500000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="InputBindingWidget" name="Pressure">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QGroupBox" name="groupBox_24">
|
||||
<property name="title">
|
||||
@@ -714,6 +571,74 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QGroupBox" name="groupBox_26">
|
||||
<property name="title">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_26">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="InputBindingWidget" name="Start">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QGroupBox" name="groupBox_25">
|
||||
<property name="title">
|
||||
<string>Select</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_25">
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="InputBindingWidget" name="Select">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="2" rowspan="3">
|
||||
@@ -1093,19 +1018,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="SmallMotorScale">
|
||||
<property name="suffix">
|
||||
<string>x</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>3.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -1181,6 +1093,19 @@
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QGridLayout" name="gridLayout_32">
|
||||
<item row="2" column="0" colspan="4">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="groupBox_27">
|
||||
<property name="title">
|
||||
@@ -1221,19 +1146,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="4">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QGroupBox" name="groupBox_28">
|
||||
<property name="title">
|
||||
@@ -1274,59 +1186,63 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2" colspan="2">
|
||||
<widget class="QGroupBox" name="groupBox_30">
|
||||
<item row="0" column="1">
|
||||
<widget class="QGroupBox" name="groupBox_34">
|
||||
<property name="title">
|
||||
<string>Analog Sensitivity</string>
|
||||
<string>Pressure Modifier</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QSlider" name="AxisScale">
|
||||
<property name="maximum">
|
||||
<number>200</number>
|
||||
<layout class="QGridLayout" name="gridLayout_34">
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="PressureModifier">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<property name="suffix">
|
||||
<string>x</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.500000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="AxisScaleLabel">
|
||||
<item row="0" column="0">
|
||||
<widget class="InputBindingWidget" name="Pressure">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>100%</string>
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="groupBox_33">
|
||||
<property name="title">
|
||||
<string>Analog Deadzone</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QSlider" name="Deadzone">
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="DeadzoneLabel">
|
||||
<property name="text">
|
||||
<string>0%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<item row="1" column="1">
|
||||
<widget class="QGroupBox" name="groupBox_31">
|
||||
<property name="title">
|
||||
<string>Analog</string>
|
||||
@@ -1366,7 +1282,6 @@
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../resources/resources.qrc"/>
|
||||
<include location="../resources/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <algorithm>
|
||||
@@ -45,15 +46,18 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett
|
||||
m_ui.controllerType, m_config_section, "Type", PAD::GetDefaultPadType(port));
|
||||
|
||||
connect(m_ui.controllerType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ControllerBindingWidget::onTypeChanged);
|
||||
connect(m_ui.automaticBinding, &QPushButton::clicked, this, &ControllerBindingWidget::doAutomaticBinding);
|
||||
connect(m_ui.clearBindings, &QPushButton::clicked, this, &ControllerBindingWidget::doClearBindings);
|
||||
connect(m_ui.bindings, &QPushButton::clicked, this, &ControllerBindingWidget::onBindingsClicked);
|
||||
connect(m_ui.settings, &QPushButton::clicked, this, &ControllerBindingWidget::onSettingsClicked);
|
||||
connect(m_ui.macros, &QPushButton::clicked, this, &ControllerBindingWidget::onMacrosClicked);
|
||||
connect(m_ui.automaticBinding, &QPushButton::clicked, this, &ControllerBindingWidget::onAutomaticBindingClicked);
|
||||
connect(m_ui.clearBindings, &QPushButton::clicked, this, &ControllerBindingWidget::onClearBindingsClicked);
|
||||
}
|
||||
|
||||
ControllerBindingWidget::~ControllerBindingWidget() = default;
|
||||
|
||||
QIcon ControllerBindingWidget::getIcon() const
|
||||
{
|
||||
return m_current_widget->getIcon();
|
||||
return m_bindings_widget->getIcon();
|
||||
}
|
||||
|
||||
void ControllerBindingWidget::populateControllerTypes()
|
||||
@@ -64,36 +68,101 @@ void ControllerBindingWidget::populateControllerTypes()
|
||||
|
||||
void ControllerBindingWidget::onTypeChanged()
|
||||
{
|
||||
const bool is_initializing = (m_current_widget == nullptr);
|
||||
const bool is_initializing = (m_ui.stackedWidget->count() == 0);
|
||||
m_controller_type = m_dialog->getStringValue(m_config_section.c_str(), "Type", PAD::GetDefaultPadType(m_port_number));
|
||||
|
||||
if (!is_initializing)
|
||||
if (m_bindings_widget)
|
||||
{
|
||||
m_ui.verticalLayout->removeWidget(m_current_widget);
|
||||
delete m_current_widget;
|
||||
m_current_widget = nullptr;
|
||||
m_ui.stackedWidget->removeWidget(m_bindings_widget);
|
||||
delete m_bindings_widget;
|
||||
m_bindings_widget = nullptr;
|
||||
}
|
||||
if (m_settings_widget)
|
||||
{
|
||||
m_ui.stackedWidget->removeWidget(m_settings_widget);
|
||||
delete m_settings_widget;
|
||||
m_settings_widget = nullptr;
|
||||
}
|
||||
if (m_macros_widget)
|
||||
{
|
||||
m_ui.stackedWidget->removeWidget(m_macros_widget);
|
||||
delete m_macros_widget;
|
||||
m_macros_widget = nullptr;
|
||||
}
|
||||
|
||||
const int index = m_ui.controllerType->findData(QString::fromStdString(m_controller_type));
|
||||
if (index >= 0 && index != m_ui.controllerType->currentIndex())
|
||||
{
|
||||
QSignalBlocker sb(m_ui.controllerType);
|
||||
m_ui.controllerType->setCurrentIndex(index);
|
||||
}
|
||||
const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(m_controller_type);
|
||||
const bool has_settings = (cinfo && cinfo->num_settings > 0);
|
||||
const bool has_macros = (cinfo && cinfo->num_bindings > 0);
|
||||
m_ui.settings->setEnabled(has_settings);
|
||||
m_ui.macros->setEnabled(has_macros);
|
||||
|
||||
if (m_controller_type == "DualShock2")
|
||||
m_current_widget = ControllerBindingWidget_DualShock2::createInstance(this);
|
||||
m_bindings_widget = ControllerBindingWidget_DualShock2::createInstance(this);
|
||||
else
|
||||
m_current_widget = new ControllerBindingWidget_Base(this);
|
||||
m_bindings_widget = new ControllerBindingWidget_Base(this);
|
||||
|
||||
m_ui.verticalLayout->addWidget(m_current_widget, 1);
|
||||
m_ui.stackedWidget->addWidget(m_bindings_widget);
|
||||
m_ui.stackedWidget->setCurrentWidget(m_bindings_widget);
|
||||
|
||||
if (has_settings)
|
||||
{
|
||||
m_settings_widget = new ControllerCustomSettingsWidget(this, m_ui.stackedWidget);
|
||||
m_ui.stackedWidget->addWidget(m_settings_widget);
|
||||
}
|
||||
|
||||
if (has_macros)
|
||||
{
|
||||
m_macros_widget = new ControllerMacroWidget(this);
|
||||
m_ui.stackedWidget->addWidget(m_macros_widget);
|
||||
}
|
||||
|
||||
updateHeaderToolButtons();
|
||||
|
||||
// no need to do this on first init, only changes
|
||||
if (!is_initializing)
|
||||
m_dialog->updateListDescription(m_port_number, this);
|
||||
}
|
||||
|
||||
void ControllerBindingWidget::doAutomaticBinding()
|
||||
void ControllerBindingWidget::updateHeaderToolButtons()
|
||||
{
|
||||
const QWidget* current_widget = m_ui.stackedWidget->currentWidget();
|
||||
const QSignalBlocker bindings_sb(m_ui.bindings);
|
||||
const QSignalBlocker settings_sb(m_ui.settings);
|
||||
const QSignalBlocker macros_sb(m_ui.macros);
|
||||
|
||||
const bool is_bindings = (current_widget == m_bindings_widget);
|
||||
m_ui.bindings->setChecked(is_bindings);
|
||||
m_ui.automaticBinding->setEnabled(is_bindings);
|
||||
m_ui.clearBindings->setEnabled(is_bindings);
|
||||
m_ui.macros->setChecked(current_widget == m_macros_widget);
|
||||
m_ui.settings->setChecked((current_widget == m_settings_widget));
|
||||
}
|
||||
|
||||
void ControllerBindingWidget::onBindingsClicked()
|
||||
{
|
||||
m_ui.stackedWidget->setCurrentWidget(m_bindings_widget);
|
||||
updateHeaderToolButtons();
|
||||
}
|
||||
|
||||
void ControllerBindingWidget::onSettingsClicked()
|
||||
{
|
||||
if (!m_settings_widget)
|
||||
return;
|
||||
|
||||
m_ui.stackedWidget->setCurrentWidget(m_settings_widget);
|
||||
updateHeaderToolButtons();
|
||||
}
|
||||
|
||||
void ControllerBindingWidget::onMacrosClicked()
|
||||
{
|
||||
if (!m_macros_widget)
|
||||
return;
|
||||
|
||||
m_ui.stackedWidget->setCurrentWidget(m_macros_widget);
|
||||
updateHeaderToolButtons();
|
||||
}
|
||||
|
||||
void ControllerBindingWidget::onAutomaticBindingClicked()
|
||||
{
|
||||
QMenu menu(this);
|
||||
bool added = false;
|
||||
@@ -118,7 +187,7 @@ void ControllerBindingWidget::doAutomaticBinding()
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void ControllerBindingWidget::doClearBindings()
|
||||
void ControllerBindingWidget::onClearBindingsClicked()
|
||||
{
|
||||
if (QMessageBox::question(QtUtils::GetRootWidget(this), tr("Clear Bindings"),
|
||||
tr("Are you sure you want to clear all bindings for this controller? This action cannot be undone.")) != QMessageBox::Yes)
|
||||
@@ -174,6 +243,388 @@ void ControllerBindingWidget::saveAndRefresh()
|
||||
g_emu_thread->applySettings();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ControllerMacroWidget::ControllerMacroWidget(ControllerBindingWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
setWindowTitle(tr("Controller Port %1 Macros").arg(parent->getPortNumber() + 1u));
|
||||
createWidgets(parent);
|
||||
}
|
||||
|
||||
ControllerMacroWidget::~ControllerMacroWidget() = default;
|
||||
|
||||
void ControllerMacroWidget::updateListItem(u32 index)
|
||||
{
|
||||
m_ui.portList->item(static_cast<int>(index))
|
||||
->setText(tr("Macro %1\n%2").arg(index + 1).arg(m_macros[index]->getSummary()));
|
||||
}
|
||||
|
||||
void ControllerMacroWidget::createWidgets(ControllerBindingWidget* parent)
|
||||
{
|
||||
for (u32 i = 0; i < NUM_MACROS; i++)
|
||||
{
|
||||
m_macros[i] = new ControllerMacroEditWidget(this, parent, i);
|
||||
m_ui.container->addWidget(m_macros[i]);
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem();
|
||||
item->setIcon(QIcon::fromTheme(QStringLiteral("flashlight-line")));
|
||||
m_ui.portList->addItem(item);
|
||||
updateListItem(i);
|
||||
}
|
||||
|
||||
m_ui.portList->setCurrentRow(0);
|
||||
m_ui.container->setCurrentIndex(0);
|
||||
|
||||
connect(m_ui.portList, &QListWidget::currentRowChanged, m_ui.container, &QStackedWidget::setCurrentIndex);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* parent, ControllerBindingWidget* bwidget,
|
||||
u32 index)
|
||||
: QWidget(parent)
|
||||
, m_parent(parent)
|
||||
, m_bwidget(bwidget)
|
||||
, m_index(index)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
ControllerSettingsDialog* dialog = m_bwidget->getDialog();
|
||||
const std::string& section = m_bwidget->getConfigSection();
|
||||
const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(m_bwidget->getControllerType());
|
||||
if (!cinfo)
|
||||
{
|
||||
// Shouldn't ever happen.
|
||||
return;
|
||||
}
|
||||
|
||||
// load binds (single string joined by &)
|
||||
const std::string binds_string(
|
||||
dialog->getStringValue(section.c_str(), fmt::format("Macro{}Binds", index + 1u).c_str(), ""));
|
||||
const std::vector<std::string_view> buttons_split(StringUtil::SplitString(binds_string, '&', true));
|
||||
|
||||
for (const std::string_view& button : buttons_split)
|
||||
{
|
||||
for (u32 i = 0; i < cinfo->num_bindings; i++)
|
||||
{
|
||||
if (button == cinfo->bindings[i].name)
|
||||
{
|
||||
m_binds.push_back(&cinfo->bindings[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// populate list view
|
||||
for (u32 i = 0; i < cinfo->num_bindings; i++)
|
||||
{
|
||||
const PAD::ControllerBindingInfo& bi = cinfo->bindings[i];
|
||||
if (bi.type == PAD::ControllerBindingType::Motor)
|
||||
continue;
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem();
|
||||
item->setText(QString::fromUtf8(bi.display_name));
|
||||
item->setCheckState((std::find(m_binds.begin(), m_binds.end(), &bi) != m_binds.end()) ? Qt::Checked :
|
||||
Qt::Unchecked);
|
||||
m_ui.bindList->addItem(item);
|
||||
}
|
||||
|
||||
m_frequency = dialog->getIntValue(section.c_str(), fmt::format("Macro{}Frequency", index + 1u).c_str(), 0);
|
||||
updateFrequencyText();
|
||||
|
||||
m_ui.trigger->initialize(dialog->getProfileSettingsInterface(), section, fmt::format("Macro{}", index + 1u));
|
||||
|
||||
connect(m_ui.increaseFrequency, &QAbstractButton::clicked, this, [this]() { modFrequency(1); });
|
||||
connect(m_ui.decreateFrequency, &QAbstractButton::clicked, this, [this]() { modFrequency(-1); });
|
||||
connect(m_ui.setFrequency, &QAbstractButton::clicked, this, &ControllerMacroEditWidget::onSetFrequencyClicked);
|
||||
connect(m_ui.bindList, &QListWidget::itemChanged, this, &ControllerMacroEditWidget::updateBinds);
|
||||
}
|
||||
|
||||
ControllerMacroEditWidget::~ControllerMacroEditWidget() = default;
|
||||
|
||||
QString ControllerMacroEditWidget::getSummary() const
|
||||
{
|
||||
QString str;
|
||||
for (const PAD::ControllerBindingInfo* bi : m_binds)
|
||||
{
|
||||
if (!str.isEmpty())
|
||||
str += static_cast<QChar>('/');
|
||||
str += QString::fromUtf8(bi->name);
|
||||
}
|
||||
return str.isEmpty() ? tr("Not Configured") : str;
|
||||
}
|
||||
|
||||
void ControllerMacroEditWidget::onSetFrequencyClicked()
|
||||
{
|
||||
bool okay;
|
||||
int new_freq = QInputDialog::getInt(this, tr("Set Frequency"), tr("Frequency: "), static_cast<int>(m_frequency), 0,
|
||||
std::numeric_limits<int>::max(), 1, &okay);
|
||||
if (!okay)
|
||||
return;
|
||||
|
||||
m_frequency = static_cast<u32>(new_freq);
|
||||
updateFrequency();
|
||||
}
|
||||
|
||||
void ControllerMacroEditWidget::modFrequency(s32 delta)
|
||||
{
|
||||
if (delta < 0 && m_frequency == 0)
|
||||
return;
|
||||
|
||||
m_frequency = static_cast<u32>(static_cast<s32>(m_frequency) + delta);
|
||||
updateFrequency();
|
||||
}
|
||||
|
||||
void ControllerMacroEditWidget::updateFrequency()
|
||||
{
|
||||
m_bwidget->getDialog()->setIntValue(m_bwidget->getConfigSection().c_str(),
|
||||
fmt::format("Macro{}Frequency", m_index + 1u).c_str(),
|
||||
static_cast<s32>(m_frequency));
|
||||
updateFrequencyText();
|
||||
}
|
||||
|
||||
void ControllerMacroEditWidget::updateFrequencyText()
|
||||
{
|
||||
if (m_frequency == 0)
|
||||
m_ui.frequencyText->setText(tr("Macro will not repeat."));
|
||||
else
|
||||
m_ui.frequencyText->setText(tr("Macro will toggle buttons every %1 frames.").arg(m_frequency));
|
||||
}
|
||||
|
||||
void ControllerMacroEditWidget::updateBinds()
|
||||
{
|
||||
ControllerSettingsDialog* dialog = m_bwidget->getDialog();
|
||||
const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(m_bwidget->getControllerType());
|
||||
if (!cinfo)
|
||||
return;
|
||||
|
||||
std::vector<const PAD::ControllerBindingInfo*> new_binds;
|
||||
for (u32 i = 0, bind_index = 0; i < cinfo->num_bindings; i++)
|
||||
{
|
||||
const PAD::ControllerBindingInfo& bi = cinfo->bindings[i];
|
||||
if (bi.type == PAD::ControllerBindingType::Motor)
|
||||
continue;
|
||||
|
||||
const QListWidgetItem* item = m_ui.bindList->item(static_cast<int>(bind_index));
|
||||
bind_index++;
|
||||
|
||||
if (!item)
|
||||
{
|
||||
// shouldn't happen
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item->checkState() == Qt::Checked)
|
||||
new_binds.push_back(&bi);
|
||||
}
|
||||
if (m_binds == new_binds)
|
||||
return;
|
||||
|
||||
m_binds = std::move(new_binds);
|
||||
|
||||
std::string binds_string;
|
||||
for (const PAD::ControllerBindingInfo* bi : m_binds)
|
||||
{
|
||||
if (!binds_string.empty())
|
||||
binds_string.append(" & ");
|
||||
binds_string.append(bi->name);
|
||||
}
|
||||
|
||||
const std::string& section = m_bwidget->getConfigSection();
|
||||
const std::string key(fmt::format("Macro{}Binds", m_index + 1u));
|
||||
if (binds_string.empty())
|
||||
dialog->clearSettingValue(section.c_str(), key.c_str());
|
||||
else
|
||||
dialog->setStringValue(section.c_str(), key.c_str(), binds_string.c_str());
|
||||
|
||||
m_parent->updateListItem(m_index);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ControllerCustomSettingsWidget::ControllerCustomSettingsWidget(ControllerBindingWidget* parent, QWidget* parent_widget)
|
||||
: QWidget(parent_widget)
|
||||
, m_parent(parent)
|
||||
{
|
||||
const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(parent->getControllerType());
|
||||
if (!cinfo || cinfo->num_settings == 0)
|
||||
return;
|
||||
|
||||
QGroupBox* gbox = new QGroupBox(tr("%1 Settings").arg(qApp->translate("PAD", cinfo->display_name)), this);
|
||||
QGridLayout* gbox_layout = new QGridLayout(gbox);
|
||||
createSettingWidgets(parent, gbox, gbox_layout, cinfo);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(gbox);
|
||||
|
||||
QHBoxLayout* bottom_hlayout = new QHBoxLayout();
|
||||
QPushButton* restore_defaults = new QPushButton(tr("Restore Default Settings"), this);
|
||||
restore_defaults->setIcon(QIcon::fromTheme(QStringLiteral("restart-line")));
|
||||
connect(restore_defaults, &QPushButton::clicked, this, &ControllerCustomSettingsWidget::restoreDefaults);
|
||||
bottom_hlayout->addStretch(1);
|
||||
bottom_hlayout->addWidget(restore_defaults);
|
||||
layout->addLayout(bottom_hlayout);
|
||||
layout->addStretch(1);
|
||||
}
|
||||
|
||||
ControllerCustomSettingsWidget::~ControllerCustomSettingsWidget() = default;
|
||||
|
||||
void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidget* parent, QWidget* widget_parent,
|
||||
QGridLayout* layout, const PAD::ControllerInfo* cinfo)
|
||||
{
|
||||
const std::string& section = parent->getConfigSection();
|
||||
SettingsInterface* sif = parent->getDialog()->getProfileSettingsInterface();
|
||||
int current_row = 0;
|
||||
|
||||
for (u32 i = 0; i < cinfo->num_settings; i++)
|
||||
{
|
||||
const PAD::ControllerSettingInfo& si = cinfo->settings[i];
|
||||
std::string key_name = si.key;
|
||||
|
||||
switch (si.type)
|
||||
{
|
||||
case PAD::ControllerSettingInfo::Type::Boolean:
|
||||
{
|
||||
QCheckBox* cb = new QCheckBox(qApp->translate(cinfo->name, si.visible_name), widget_parent);
|
||||
cb->setObjectName(QString::fromUtf8(si.key));
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, cb, section, std::move(key_name),
|
||||
si.BooleanDefaultValue());
|
||||
layout->addWidget(cb, current_row, 0, 1, 4);
|
||||
current_row++;
|
||||
}
|
||||
break;
|
||||
|
||||
case PAD::ControllerSettingInfo::Type::Integer:
|
||||
{
|
||||
QSpinBox* sb = new QSpinBox(widget_parent);
|
||||
sb->setObjectName(QString::fromUtf8(si.key));
|
||||
sb->setMinimum(si.IntegerMinValue());
|
||||
sb->setMaximum(si.IntegerMaxValue());
|
||||
sb->setSingleStep(si.IntegerStepValue());
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, sb, section, std::move(key_name), si.IntegerDefaultValue());
|
||||
layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.visible_name), widget_parent), current_row, 0);
|
||||
layout->addWidget(sb, current_row, 1, 1, 3);
|
||||
current_row++;
|
||||
}
|
||||
break;
|
||||
|
||||
case PAD::ControllerSettingInfo::Type::Float:
|
||||
{
|
||||
QDoubleSpinBox* sb = new QDoubleSpinBox(widget_parent);
|
||||
sb->setObjectName(QString::fromUtf8(si.key));
|
||||
sb->setMinimum(si.FloatMinValue());
|
||||
sb->setMaximum(si.FloatMaxValue());
|
||||
sb->setSingleStep(si.FloatStepValue());
|
||||
SettingWidgetBinder::BindWidgetToFloatSetting(sif, sb, section, std::move(key_name), si.FloatDefaultValue());
|
||||
layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.visible_name), widget_parent), current_row, 0);
|
||||
layout->addWidget(sb, current_row, 1, 1, 3);
|
||||
current_row++;
|
||||
}
|
||||
break;
|
||||
|
||||
case PAD::ControllerSettingInfo::Type::String:
|
||||
{
|
||||
QLineEdit* le = new QLineEdit(widget_parent);
|
||||
le->setObjectName(QString::fromUtf8(si.key));
|
||||
SettingWidgetBinder::BindWidgetToStringSetting(sif, le, section, std::move(key_name), si.StringDefaultValue());
|
||||
layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.visible_name), widget_parent), current_row, 0);
|
||||
layout->addWidget(le, current_row, 1, 1, 3);
|
||||
current_row++;
|
||||
}
|
||||
break;
|
||||
|
||||
case PAD::ControllerSettingInfo::Type::Path:
|
||||
{
|
||||
QLineEdit* le = new QLineEdit(widget_parent);
|
||||
le->setObjectName(QString::fromUtf8(si.key));
|
||||
QPushButton* browse_button = new QPushButton(tr("Browse..."), widget_parent);
|
||||
SettingWidgetBinder::BindWidgetToStringSetting(sif, le, section, std::move(key_name), si.StringDefaultValue());
|
||||
connect(browse_button, &QPushButton::clicked, [this, le]() {
|
||||
QString path = QFileDialog::getOpenFileName(this, tr("Select File"));
|
||||
if (!path.isEmpty())
|
||||
le->setText(path);
|
||||
});
|
||||
|
||||
QHBoxLayout* hbox = new QHBoxLayout();
|
||||
hbox->addWidget(le, 1);
|
||||
hbox->addWidget(browse_button);
|
||||
|
||||
layout->addWidget(new QLabel(qApp->translate(cinfo->name, si.visible_name), widget_parent), current_row, 0);
|
||||
layout->addLayout(hbox, current_row, 1, 1, 3);
|
||||
current_row++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
QLabel* label = new QLabel(si.description ? qApp->translate(cinfo->name, si.description) : QString(), widget_parent);
|
||||
label->setWordWrap(true);
|
||||
layout->addWidget(label, current_row++, 0, 1, 4);
|
||||
|
||||
layout->addItem(new QSpacerItem(1, 10, QSizePolicy::Minimum, QSizePolicy::Fixed), current_row++, 0, 1, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerCustomSettingsWidget::restoreDefaults()
|
||||
{
|
||||
const PAD::ControllerInfo* cinfo = PAD::GetControllerInfo(m_parent->getControllerType());
|
||||
if (!cinfo || cinfo->num_settings == 0)
|
||||
return;
|
||||
|
||||
for (u32 i = 0; i < cinfo->num_settings; i++)
|
||||
{
|
||||
const PAD::ControllerSettingInfo& si = cinfo->settings[i];
|
||||
const QString key(QString::fromStdString(si.key));
|
||||
|
||||
switch (si.type)
|
||||
{
|
||||
case PAD::ControllerSettingInfo::Type::Boolean:
|
||||
{
|
||||
QCheckBox* widget = findChild<QCheckBox*>(QString::fromStdString(si.key));
|
||||
if (widget)
|
||||
widget->setChecked(si.BooleanDefaultValue());
|
||||
}
|
||||
break;
|
||||
|
||||
case PAD::ControllerSettingInfo::Type::Integer:
|
||||
{
|
||||
QSpinBox* widget = findChild<QSpinBox*>(QString::fromStdString(si.key));
|
||||
if (widget)
|
||||
widget->setValue(si.IntegerDefaultValue());
|
||||
}
|
||||
break;
|
||||
|
||||
case PAD::ControllerSettingInfo::Type::Float:
|
||||
{
|
||||
QDoubleSpinBox* widget = findChild<QDoubleSpinBox*>(QString::fromStdString(si.key));
|
||||
if (widget)
|
||||
widget->setValue(si.FloatDefaultValue());
|
||||
}
|
||||
break;
|
||||
|
||||
case PAD::ControllerSettingInfo::Type::String:
|
||||
{
|
||||
QLineEdit* widget = findChild<QLineEdit*>(QString::fromStdString(si.key));
|
||||
if (widget)
|
||||
widget->setText(QString::fromUtf8(si.StringDefaultValue()));
|
||||
}
|
||||
break;
|
||||
|
||||
case PAD::ControllerSettingInfo::Type::Path:
|
||||
{
|
||||
QLineEdit* widget = findChild<QLineEdit*>(QString::fromStdString(si.key));
|
||||
if (widget)
|
||||
widget->setText(QString::fromUtf8(si.StringDefaultValue()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ControllerBindingWidget_Base::ControllerBindingWidget_Base(ControllerBindingWidget* parent)
|
||||
|
||||
@@ -15,13 +15,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PAD/Host/PAD.h"
|
||||
|
||||
#include <QtWidgets/QWidget>
|
||||
|
||||
#include "ui_ControllerBindingWidget.h"
|
||||
#include "ui_ControllerBindingWidget_DualShock2.h"
|
||||
#include "ui_ControllerMacroWidget.h"
|
||||
#include "ui_ControllerMacroEditWidget.h"
|
||||
|
||||
class InputBindingWidget;
|
||||
class ControllerSettingsDialog;
|
||||
class ControllerCustomSettingsWidget;
|
||||
class ControllerMacroWidget;
|
||||
class ControllerMacroEditWidget;
|
||||
class ControllerBindingWidget_Base;
|
||||
|
||||
class ControllerBindingWidget final : public QWidget
|
||||
@@ -41,11 +48,15 @@ public:
|
||||
|
||||
private Q_SLOTS:
|
||||
void onTypeChanged();
|
||||
void doAutomaticBinding();
|
||||
void doClearBindings();
|
||||
void onAutomaticBindingClicked();
|
||||
void onClearBindingsClicked();
|
||||
void onBindingsClicked();
|
||||
void onSettingsClicked();
|
||||
void onMacrosClicked();
|
||||
|
||||
private:
|
||||
void populateControllerTypes();
|
||||
void updateHeaderToolButtons();
|
||||
void doDeviceAutomaticBinding(const QString& device);
|
||||
void saveAndRefresh();
|
||||
|
||||
@@ -57,9 +68,87 @@ private:
|
||||
std::string m_controller_type;
|
||||
u32 m_port_number;
|
||||
|
||||
ControllerBindingWidget_Base* m_current_widget = nullptr;
|
||||
ControllerBindingWidget_Base* m_bindings_widget = nullptr;
|
||||
ControllerCustomSettingsWidget* m_settings_widget = nullptr;
|
||||
ControllerMacroWidget* m_macros_widget = nullptr;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ControllerMacroWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ControllerMacroWidget(ControllerBindingWidget* parent);
|
||||
~ControllerMacroWidget();
|
||||
|
||||
void updateListItem(u32 index);
|
||||
|
||||
private:
|
||||
static constexpr u32 NUM_MACROS = PAD::NUM_MACRO_BUTTONS_PER_CONTROLLER;
|
||||
|
||||
void createWidgets(ControllerBindingWidget* parent);
|
||||
|
||||
Ui::ControllerMacroWidget m_ui;
|
||||
ControllerSettingsDialog* m_dialog;
|
||||
std::array<ControllerMacroEditWidget*, NUM_MACROS> m_macros;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ControllerMacroEditWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ControllerMacroEditWidget(ControllerMacroWidget* parent, ControllerBindingWidget* bwidget, u32 index);
|
||||
~ControllerMacroEditWidget();
|
||||
|
||||
QString getSummary() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void onSetFrequencyClicked();
|
||||
void updateBinds();
|
||||
|
||||
private:
|
||||
void modFrequency(s32 delta);
|
||||
void updateFrequency();
|
||||
void updateFrequencyText();
|
||||
|
||||
Ui::ControllerMacroEditWidget m_ui;
|
||||
|
||||
ControllerMacroWidget* m_parent;
|
||||
ControllerBindingWidget* m_bwidget;
|
||||
u32 m_index;
|
||||
|
||||
std::vector<const PAD::ControllerBindingInfo*> m_binds;
|
||||
u32 m_frequency = 0;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ControllerCustomSettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ControllerCustomSettingsWidget(ControllerBindingWidget* parent, QWidget* parent_widget);
|
||||
~ControllerCustomSettingsWidget();
|
||||
|
||||
void createSettingWidgets(ControllerBindingWidget* parent, QWidget* widget_parent, QGridLayout* layout, const PAD::ControllerInfo* cinfo);
|
||||
|
||||
private Q_SLOTS:
|
||||
void restoreDefaults();
|
||||
|
||||
private:
|
||||
ControllerBindingWidget* m_parent;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class ControllerBindingWidget_Base : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ControllerMacroEditWidget</class>
|
||||
<widget class="QWidget" name="ControllerMacroEditWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>595</width>
|
||||
<height>473</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Binds/Buttons</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Select the buttons which you want to trigger with this macro. All buttons are activated concurrently.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QListWidget" name="bindList"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Trigger</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Select the trigger to activate this macro. This can be a single button, or combination of buttons (chord). Shift-click for multiple triggers.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="InputBindingWidget" name="trigger">
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>Frequency</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0,0,0">
|
||||
<item>
|
||||
<widget class="QLabel" name="frequencyText">
|
||||
<property name="text">
|
||||
<string>Macro will toggle every N frames.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="setFrequency">
|
||||
<property name="text">
|
||||
<string>Set...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="increaseFrequency">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="arrowType">
|
||||
<enum>Qt::UpArrow</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="decreateFrequency">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="arrowType">
|
||||
<enum>Qt::DownArrow</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>InputBindingWidget</class>
|
||||
<extends>QPushButton</extends>
|
||||
<header>Settings/InputBindingWidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ControllerMacroWidget</class>
|
||||
<widget class="QWidget" name="ControllerMacroWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>799</width>
|
||||
<height>493</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QListWidget" name="portList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QStackedWidget" name="container"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../resources/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -266,6 +266,14 @@ bool ControllerSettingsDialog::getBoolValue(const char* section, const char* key
|
||||
return Host::GetBaseBoolSettingValue(section, key, default_value);
|
||||
}
|
||||
|
||||
s32 ControllerSettingsDialog::getIntValue(const char* section, const char* key, s32 default_value) const
|
||||
{
|
||||
if (m_profile_interface)
|
||||
return m_profile_interface->GetIntValue(section, key, default_value);
|
||||
else
|
||||
return Host::GetBaseIntSettingValue(section, key, default_value);
|
||||
}
|
||||
|
||||
std::string ControllerSettingsDialog::getStringValue(const char* section, const char* key, const char* default_value) const
|
||||
{
|
||||
std::string value;
|
||||
@@ -291,6 +299,21 @@ void ControllerSettingsDialog::setBoolValue(const char* section, const char* key
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerSettingsDialog::setIntValue(const char* section, const char* key, s32 value)
|
||||
{
|
||||
if (m_profile_interface)
|
||||
{
|
||||
m_profile_interface->SetIntValue(section, key, value);
|
||||
m_profile_interface->Save();
|
||||
g_emu_thread->reloadGameSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
QtHost::SetBaseIntSettingValue(section, key, value);
|
||||
g_emu_thread->applySettings();
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerSettingsDialog::setStringValue(const char* section, const char* key, const char* value)
|
||||
{
|
||||
if (m_profile_interface)
|
||||
@@ -301,7 +324,7 @@ void ControllerSettingsDialog::setStringValue(const char* section, const char* k
|
||||
}
|
||||
else
|
||||
{
|
||||
QtHost::SetBaseStringSettingValue(key, section, value);
|
||||
QtHost::SetBaseStringSettingValue(section, key, value);
|
||||
g_emu_thread->applySettings();
|
||||
}
|
||||
}
|
||||
@@ -406,7 +429,6 @@ void ControllerSettingsDialog::updateListDescription(u32 global_slot, Controller
|
||||
const QVariant data(item->data(Qt::UserRole));
|
||||
if (data.type() == QVariant::UInt && data.toUInt() == global_slot)
|
||||
{
|
||||
const bool is_mtap_port = sioPadIsMultitapSlot(global_slot);
|
||||
const auto [port, slot] = sioConvertPadToPortAndSlot(global_slot);
|
||||
const bool mtap_enabled = getBoolValue("Pad", (port == 0) ? "MultitapPort1" : "MultitapPort2", false);
|
||||
|
||||
|
||||
@@ -64,8 +64,10 @@ public:
|
||||
|
||||
// Helper functions for updating setting values globally or in the profile.
|
||||
bool getBoolValue(const char* section, const char* key, bool default_value) const;
|
||||
s32 getIntValue(const char* section, const char* key, s32 default_value) const;
|
||||
std::string getStringValue(const char* section, const char* key, const char* default_value) const;
|
||||
void setBoolValue(const char* section, const char* key, bool value);
|
||||
void setIntValue(const char* section, const char* key, s32 value);
|
||||
void setStringValue(const char* section, const char* key, const char* value);
|
||||
void clearSettingValue(const char* section, const char* key);
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1276</width>
|
||||
<height>672</height>
|
||||
<width>1300</width>
|
||||
<height>680</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
||||
@@ -29,10 +29,10 @@ FolderSettingsWidget::FolderSettingsWidget(SettingsDialog* dialog, QWidget* pare
|
||||
|
||||
m_ui.setupUi(this);
|
||||
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.cache, m_ui.cacheBrowse, m_ui.cacheOpen, m_ui.cacheReset, "Folders", "Cache", "cache");
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.covers, m_ui.coversBrowse, m_ui.coversOpen, m_ui.coversReset, "Folders", "Covers", "covers");
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.snapshots, m_ui.snapshotsBrowse, m_ui.snapshotsOpen, m_ui.snapshotsReset, "Folders", "Snapshots", "snaps");
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.saveStates, m_ui.saveStatesBrowse, m_ui.saveStatesOpen, m_ui.saveStatesReset, "Folders", "SaveStates", "sstates");
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.cache, m_ui.cacheBrowse, m_ui.cacheOpen, m_ui.cacheReset, "Folders", "Cache", Path::Combine(EmuFolders::DataRoot, "cache"));
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.covers, m_ui.coversBrowse, m_ui.coversOpen, m_ui.coversReset, "Folders", "Covers", Path::Combine(EmuFolders::DataRoot, "covers"));
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.snapshots, m_ui.snapshotsBrowse, m_ui.snapshotsOpen, m_ui.snapshotsReset, "Folders", "Snapshots", Path::Combine(EmuFolders::DataRoot, "snaps"));
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.saveStates, m_ui.saveStatesBrowse, m_ui.saveStatesOpen, m_ui.saveStatesReset, "Folders", "SaveStates", Path::Combine(EmuFolders::DataRoot, "sstates"));
|
||||
}
|
||||
|
||||
FolderSettingsWidget::~FolderSettingsWidget() = default;
|
||||
|
||||
@@ -186,7 +186,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.crcFixLevel, "EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), -1);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.blending, "EmuCore/GS", "accurate_blending_unit", static_cast<int>(AccBlendLevel::Basic));
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.accurateDATE, "EmuCore/GS", "accurate_date", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.conservativeBufferAllocation, "EmuCore/GS", "conservative_framebuffer", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gpuPaletteConversion, "EmuCore/GS", "paltex", false);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.texturePreloading, "EmuCore/GS", "texture_preloading",
|
||||
static_cast<int>(TexturePreloadingLevel::Off));
|
||||
@@ -233,7 +232,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.loadTextureReplacementsAsync, "EmuCore/GS", "LoadTextureReplacementsAsync", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.precacheTextureReplacements, "EmuCore/GS", "PrecacheTextureReplacements", false);
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.texturesDirectory, m_ui.texturesBrowse, m_ui.texturesOpen, m_ui.texturesReset,
|
||||
"Folders", "Textures", "textures");
|
||||
"Folders", "Textures", Path::Combine(EmuFolders::DataRoot, "textures"));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Advanced Settings
|
||||
@@ -243,7 +242,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.skipPresentingDuplicateFrames, "EmuCore/GS", "SkipDuplicateFrames", false);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.overrideTextureBarriers, "EmuCore/GS", "OverrideTextureBarriers", -1, -1);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.overrideGeometryShader, "EmuCore/GS", "OverrideGeometryShaders", -1, -1);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.gsDumpCompression, "EmuCore/GS", "GSDumpCompression", static_cast<int>(GSDumpCompressionMethod::Uncompressed));
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.gsDumpCompression, "EmuCore/GS", "GSDumpCompression", static_cast<int>(GSDumpCompressionMethod::LZMA));
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableFramebufferFetch, "EmuCore/GS", "DisableFramebufferFetch", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDualSource, "EmuCore/GS", "DisableDualSourceBlend", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableHardwareReadbacks, "EmuCore/GS", "HWDisableReadbacks", false);
|
||||
@@ -251,6 +250,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// SW Settings
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.swTextureFiltering, "EmuCore/GS", "filter", static_cast<int>(BiFiltering::PS2));
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.extraSWThreads, "EmuCore/GS", "extrathreads", 2);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.swAutoFlush, "EmuCore/GS", "autoflush_sw", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.swMipmap, "EmuCore/GS", "mipmap", true);
|
||||
@@ -286,18 +286,25 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
|
||||
connect(m_ui.renderer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &GraphicsSettingsWidget::onRendererChanged);
|
||||
connect(m_ui.enableHWFixes, &QCheckBox::stateChanged, this, &GraphicsSettingsWidget::onEnableHardwareFixesChanged);
|
||||
connect(m_ui.textureFiltering, &QComboBox::currentIndexChanged, this, &GraphicsSettingsWidget::onTextureFilteringChange);
|
||||
connect(m_ui.swTextureFiltering, &QComboBox::currentIndexChanged, this, &GraphicsSettingsWidget::onSWTextureFilteringChange);
|
||||
updateRendererDependentOptions();
|
||||
|
||||
// only allow disabling readbacks for per-game settings, it's too dangerous
|
||||
#ifndef PCSX2_DEVBUILD
|
||||
m_ui.disableHardwareReadbacks->setEnabled(m_dialog->isPerGameSettings());
|
||||
|
||||
// allow Texture Offset for per-game settings only
|
||||
m_ui.textureOffsetX->setEnabled(m_dialog->isPerGameSettings());
|
||||
m_ui.textureOffsetY->setEnabled(m_dialog->isPerGameSettings());
|
||||
|
||||
// allow Skipdraw Range for per-game settings only
|
||||
m_ui.skipDrawStart->setEnabled(m_dialog->isPerGameSettings());
|
||||
m_ui.skipDrawEnd->setEnabled(m_dialog->isPerGameSettings());
|
||||
// Remove texture offset and skipdraw range for global settings.
|
||||
if (!m_dialog->isPerGameSettings())
|
||||
{
|
||||
m_ui.upscalingFixesLayout->removeRow(2);
|
||||
m_ui.hardwareFixesLayout->removeRow(2);
|
||||
m_ui.skipDrawStart = nullptr;
|
||||
m_ui.skipDrawEnd = nullptr;
|
||||
m_ui.textureOffsetX = nullptr;
|
||||
m_ui.textureOffsetY = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Display tab
|
||||
{
|
||||
@@ -347,7 +354,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
"The higher the setting, the more blending is emulated in the shader accurately, and the higher the speed penalty will be. "
|
||||
"Do note that Direct3D's blending is reduced in capability compared to OpenGL/Vulkan"));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.texturePreloading, tr("Texture Preloading"), tr(""),
|
||||
dialog->registerWidgetHelp(m_ui.texturePreloading, tr("Texture Preloading"), tr("Full (Hash Cache)"),
|
||||
tr("Uploads entire textures at once instead of small pieces, avoiding redundant uploads when possible. "
|
||||
"Improves performance in most games, but can make a small selection slower."));
|
||||
|
||||
@@ -355,10 +362,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
tr("Implement a more accurate algorithm to compute GS destination alpha testing. "
|
||||
"It improves shadow and transparency rendering."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.conservativeBufferAllocation, tr("Conservative Buffer Allocation"), tr("Checked"),
|
||||
tr("Disabled: Reserves a larger framebuffer to prevent FMV flickers. Increases GPU/memory requirements. "
|
||||
"Disabling this can amplify stuttering due to low RAM/VRAM."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.gpuPaletteConversion, tr("GPU Palette Conversion"), tr("Unchecked"),
|
||||
tr("When enabled GPU converts colormap-textures, otherwise the CPU will. "
|
||||
"It is a trade-off between GPU and CPU."));
|
||||
@@ -478,6 +481,20 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
|
||||
GraphicsSettingsWidget::~GraphicsSettingsWidget() = default;
|
||||
|
||||
void GraphicsSettingsWidget::onTextureFilteringChange()
|
||||
{
|
||||
const QSignalBlocker block(m_ui.swTextureFiltering);
|
||||
|
||||
m_ui.swTextureFiltering->setCurrentIndex(m_ui.textureFiltering->currentIndex());
|
||||
}
|
||||
|
||||
void GraphicsSettingsWidget::onSWTextureFilteringChange()
|
||||
{
|
||||
const QSignalBlocker block(m_ui.textureFiltering);
|
||||
|
||||
m_ui.textureFiltering->setCurrentIndex(m_ui.swTextureFiltering->currentIndex());
|
||||
}
|
||||
|
||||
void GraphicsSettingsWidget::onRendererChanged(int index)
|
||||
{
|
||||
if (m_dialog->isPerGameSettings())
|
||||
|
||||
@@ -35,6 +35,8 @@ Q_SIGNALS:
|
||||
void fullscreenModesChanged(const QStringList& modes);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onTextureFilteringChange();
|
||||
void onSWTextureFilteringChange();
|
||||
void onRendererChanged(int index);
|
||||
void onAdapterChanged(int index);
|
||||
void onEnableHardwareFixesChanged();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>657</width>
|
||||
<width>658</width>
|
||||
<height>890</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -642,38 +642,6 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="basicCheckboxGridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="gpuPaletteConversion">
|
||||
<property name="text">
|
||||
<string>GPU Palette Conversion</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="conservativeBufferAllocation">
|
||||
<property name="text">
|
||||
<string>Conservative Buffer Allocation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="accurateDATE">
|
||||
<property name="text">
|
||||
<string>Accurate Destination Alpha Test</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="enableHWFixes">
|
||||
<property name="text">
|
||||
<string>Manual Hardware Renderer Fixes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="text">
|
||||
@@ -700,13 +668,38 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="basicCheckboxGridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="accurateDATE">
|
||||
<property name="text">
|
||||
<string>Accurate Destination Alpha Test</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="gpuPaletteConversion">
|
||||
<property name="text">
|
||||
<string>GPU Palette Conversion</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="enableHWFixes">
|
||||
<property name="text">
|
||||
<string>Manual Hardware Renderer Fixes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="hardwareFixesTab">
|
||||
<attribute name="title">
|
||||
<string>Hardware Fixes</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<layout class="QFormLayout" name="hardwareFixesLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
@@ -890,7 +883,7 @@
|
||||
<attribute name="title">
|
||||
<string>Upscaling Fixes</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout_5">
|
||||
<layout class="QFormLayout" name="upscalingFixesLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="text">
|
||||
@@ -949,16 +942,16 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_16">
|
||||
<widget class="QLabel" name="textureOffsetLabel">
|
||||
<property name="text">
|
||||
<string>Texture Offsets:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1,0,1">
|
||||
<layout class="QHBoxLayout" name="textureOffsetLayout" stretch="0,1,0,1">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_17">
|
||||
<widget class="QLabel" name="textureOffsetXLabel">
|
||||
<property name="text">
|
||||
<string>X:</string>
|
||||
</property>
|
||||
@@ -972,7 +965,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_18">
|
||||
<widget class="QLabel" name="textureOffsetYLabel">
|
||||
<property name="text">
|
||||
<string>Y:</string>
|
||||
</property>
|
||||
@@ -1523,6 +1516,15 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="softwareRendererGroup">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>203</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -1531,29 +1533,65 @@
|
||||
<string>Rendering</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout_3">
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="swTextureFiltering">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Nearest</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bilinear (Forced)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bilinear (PS2)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bilinear (Forced excluding sprite)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<widget class="QLabel" name="label_37">
|
||||
<property name="text">
|
||||
<string>Extra Rendering Threads:</string>
|
||||
<string>Texture Filtering:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="extraSWThreads">
|
||||
<property name="suffix">
|
||||
<string> threads</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Extra Rendering Threads:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="swAutoFlush">
|
||||
<property name="text">
|
||||
<string>Auto Flush</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="swMipmap">
|
||||
<property name="text">
|
||||
@@ -1561,6 +1599,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="swAutoFlush">
|
||||
<property name="text">
|
||||
<string>Auto Flush</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -50,7 +50,7 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsDialog* dialog, QWidg
|
||||
// since the group box hasn't been resized at that point.
|
||||
m_ui.cardGroupBox->installEventFilter(this);
|
||||
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.directory, m_ui.browse, m_ui.open, m_ui.reset, "Folders", "MemoryCards", "memcards");
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.directory, m_ui.browse, m_ui.open, m_ui.reset, "Folders", "MemoryCards", Path::Combine(EmuFolders::DataRoot, "memcards"));
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.autoEject, "EmuCore", "McdEnableEjection", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.automaticManagement, "EmuCore", "McdFolderAutoManage", true);
|
||||
|
||||
|
||||
@@ -214,6 +214,9 @@ void SettingsDialog::addWidget(QWidget* widget, QString title, QString icon, QSt
|
||||
|
||||
void SettingsDialog::registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text)
|
||||
{
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
// construct rich text with formatted description
|
||||
QString full_text;
|
||||
full_text += "<table width='100%' cellpadding='0' cellspacing='0'><tr><td><strong>";
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>100</height>
|
||||
<height>120</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
|
||||
@@ -80,7 +80,7 @@ SystemSettingsWidget::SystemSettingsWidget(SettingsDialog* dialog, QWidget* pare
|
||||
tr("Higher values may increase internal framerate in games, but will increase CPU requirements substantially. "
|
||||
"Lower values will reduce the CPU load allowing lightweight games to run full speed on weaker CPUs."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.eeCycleSkipping, tr("Cycle Skip"), tr("Normal Speed"),
|
||||
dialog->registerWidgetHelp(m_ui.eeCycleSkipping, tr("Cycle Skip"), tr("None"),
|
||||
tr("Makes the emulated Emotion Engine skip cycles. "
|
||||
"Helps a small subset of games like SOTC. Most of the time it's harmful to performance."));
|
||||
|
||||
@@ -89,23 +89,15 @@ SystemSettingsWidget::SystemSettingsWidget(SettingsDialog* dialog, QWidget* pare
|
||||
"Safe for most games, but a few are incompatible and may hang."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.instantVU1, tr("Instant VU1"), tr("Checked"),
|
||||
tr("Runs VU1 instantly (when MTVU is disabled). Provides a modest speed improvement. "
|
||||
tr("Runs VU1 instantly. Provides a modest speed improvement in most games. "
|
||||
"Safe for most games, but a few games may exhibit graphical errors."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.fastCDVD, tr("Enable Fast CDVD"), tr("Unchecked"),
|
||||
tr("Fast disc access, less loading times. Check HDLoader compatibility lists for known games that have issues with this."));
|
||||
|
||||
updateVU1InstantState();
|
||||
connect(m_ui.MTVU, &QCheckBox::stateChanged, this, &SystemSettingsWidget::updateVU1InstantState);
|
||||
}
|
||||
|
||||
SystemSettingsWidget::~SystemSettingsWidget() = default;
|
||||
|
||||
void SystemSettingsWidget::updateVU1InstantState()
|
||||
{
|
||||
m_ui.instantVU1->setEnabled(!m_dialog->getEffectiveBoolValue("EmuCore/Speedhacks", "vuThread", false));
|
||||
}
|
||||
|
||||
int SystemSettingsWidget::getGlobalClampingModeIndex(bool vu) const
|
||||
{
|
||||
if (Host::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuSignOverflow" : "fpuFullMode", false))
|
||||
|
||||
@@ -29,9 +29,6 @@ public:
|
||||
SystemSettingsWidget(SettingsDialog* dialog, QWidget* parent);
|
||||
~SystemSettingsWidget();
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateVU1InstantState();
|
||||
|
||||
private:
|
||||
int getGlobalClampingModeIndex(bool vu) const;
|
||||
int getClampingModeIndex(bool vu) const;
|
||||
|
||||
@@ -335,6 +335,12 @@
|
||||
</QtUi>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtUi Include="Settings\ControllerMacroEditWidget.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<QtUi Include="Settings\ControllerMacroWidget.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<None Include="Settings\FolderSettingsWidget.ui" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
||||
@@ -405,6 +405,12 @@
|
||||
<QtUi Include="Tools\InputRecording\NewInputRecordingDlg.ui">
|
||||
<Filter>Tools\Input Recording</Filter>
|
||||
</QtUi>
|
||||
<QtUi Include="Settings\ControllerMacroWidget.ui">
|
||||
<Filter>Settings</Filter>
|
||||
</QtUi>
|
||||
<QtUi Include="Settings\ControllerMacroEditWidget.ui">
|
||||
<Filter>Settings</Filter>
|
||||
</QtUi>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Settings\FolderSettingsWidget.ui">
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M7 7V3a1 1 0 0 1 1-1h13a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-4v3.993c0 .556-.449 1.007-1.007 1.007H3.007A1.006 1.006 0 0 1 2 20.993l.003-12.986C2.003 7.451 2.452 7 3.01 7H7zm2 0h6.993C16.549 7 17 7.449 17 8.007V15h3V4H9v3zM4.003 9L4 20h11V9H4.003z" fill="#000000"/></svg>
|
||||
|
After Width: | Height: | Size: 395 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M13 9h8L11 24v-9H4l9-15v9zm-2 2V7.22L7.532 13H13v4.394L17.263 11H11z" fill="#000000"/></svg>
|
||||
|
After Width: | Height: | Size: 221 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M7 7V3a1 1 0 0 1 1-1h13a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-4v3.993c0 .556-.449 1.007-1.007 1.007H3.007A1.006 1.006 0 0 1 2 20.993l.003-12.986C2.003 7.451 2.452 7 3.01 7H7zm2 0h6.993C16.549 7 17 7.449 17 8.007V15h3V4H9v3zM4.003 9L4 20h11V9H4.003z" fill="#ffffff"/></svg>
|
||||
|
After Width: | Height: | Size: 395 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M13 9h8L11 24v-9H4l9-15v9zm-2 2V7.22L7.532 13H13v4.394L17.263 11H11z" fill="#ffffff"/></svg>
|
||||
|
After Width: | Height: | Size: 221 B |
@@ -7,6 +7,7 @@
|
||||
<file>icons/black/svg/artboard-2-line.svg</file>
|
||||
<file>icons/black/svg/book-open-line.svg</file>
|
||||
<file>icons/black/svg/brush-line.svg</file>
|
||||
<file>icons/black/svg/checkbox-multiple-blank-line.svg</file>
|
||||
<file>icons/black/svg/close-line.svg</file>
|
||||
<file>icons/black/svg/dashboard-line.svg</file>
|
||||
<file>icons/black/svg/disc-line.svg</file>
|
||||
@@ -21,6 +22,7 @@
|
||||
<file>icons/black/svg/file-search-line.svg</file>
|
||||
<file>icons/black/svg/file-settings-line.svg</file>
|
||||
<file>icons/black/svg/filter-line.svg</file>
|
||||
<file>icons/black/svg/flashlight-line.svg</file>
|
||||
<file>icons/black/svg/flask-line.svg</file>
|
||||
<file>icons/black/svg/folder-add-line.svg</file>
|
||||
<file>icons/black/svg/folder-open-line.svg</file>
|
||||
@@ -59,6 +61,7 @@
|
||||
<file>icons/white/svg/artboard-2-line.svg</file>
|
||||
<file>icons/white/svg/book-open-line.svg</file>
|
||||
<file>icons/white/svg/brush-line.svg</file>
|
||||
<file>icons/white/svg/checkbox-multiple-blank-line.svg</file>
|
||||
<file>icons/white/svg/close-line.svg</file>
|
||||
<file>icons/white/svg/dashboard-line.svg</file>
|
||||
<file>icons/white/svg/disc-line.svg</file>
|
||||
@@ -73,6 +76,7 @@
|
||||
<file>icons/white/svg/file-search-line.svg</file>
|
||||
<file>icons/white/svg/file-settings-line.svg</file>
|
||||
<file>icons/white/svg/filter-line.svg</file>
|
||||
<file>icons/white/svg/flashlight-line.svg</file>
|
||||
<file>icons/white/svg/flask-line.svg</file>
|
||||
<file>icons/white/svg/folder-add-line.svg</file>
|
||||
<file>icons/white/svg/folder-open-line.svg</file>
|
||||
|
||||
@@ -461,7 +461,6 @@ struct Pcsx2Config
|
||||
HWDisableReadbacks : 1,
|
||||
AccurateDATE : 1,
|
||||
GPUPaletteConversion : 1,
|
||||
ConservativeFramebuffer : 1,
|
||||
AutoFlushSW : 1,
|
||||
PreloadFrameWithGSData : 1,
|
||||
WrapGSMem : 1,
|
||||
@@ -532,7 +531,7 @@ struct Pcsx2Config
|
||||
CRCHackLevel CRCHack{CRCHackLevel::Automatic};
|
||||
BiFiltering TextureFiltering{BiFiltering::PS2};
|
||||
TexturePreloadingLevel TexturePreloading{TexturePreloadingLevel::Full};
|
||||
GSDumpCompressionMethod GSDumpCompression{GSDumpCompressionMethod::Uncompressed};
|
||||
GSDumpCompressionMethod GSDumpCompression{GSDumpCompressionMethod::LZMA};
|
||||
int Dithering{2};
|
||||
int MaxAnisotropy{0};
|
||||
int SWExtraThreads{2};
|
||||
|
||||
@@ -390,6 +390,7 @@ bool SocketAdapter::send(NetPacket* pkt)
|
||||
memcpy(arpRet->senderProtocolAddress.get(), arpPkt.targetProtocolAddress.get(), 4);
|
||||
arpRet->op = 2,
|
||||
arpRet->protocol = arpPkt.protocol;
|
||||
arpRet->hardwareType = arpPkt.hardwareType;
|
||||
|
||||
EthernetFrame* retARP = new EthernetFrame(arpRet);
|
||||
memcpy(retARP->destinationMAC, ps2MAC, 6);
|
||||
|
||||
@@ -829,7 +829,6 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
||||
// reload texture cache when trilinear filtering or TC options change
|
||||
if (
|
||||
(GSConfig.UseHardwareRenderer() && GSConfig.HWMipmap != old_config.HWMipmap) ||
|
||||
GSConfig.ConservativeFramebuffer != old_config.ConservativeFramebuffer ||
|
||||
GSConfig.TexturePreloading != old_config.TexturePreloading ||
|
||||
GSConfig.UserHacks_TriFilter != old_config.UserHacks_TriFilter ||
|
||||
GSConfig.GPUPaletteConversion != old_config.GPUPaletteConversion ||
|
||||
@@ -1456,7 +1455,6 @@ void GSApp::Init()
|
||||
m_default_configuration["pcrtc_overscan"] = "0";
|
||||
m_default_configuration["IntegerScaling"] = "0";
|
||||
m_default_configuration["deinterlace"] = "7";
|
||||
m_default_configuration["conservative_framebuffer"] = "1";
|
||||
m_default_configuration["linear_present"] = "1";
|
||||
m_default_configuration["LoadTextureReplacements"] = "0";
|
||||
m_default_configuration["LoadTextureReplacementsAsync"] = "1";
|
||||
@@ -1479,8 +1477,6 @@ void GSApp::Init()
|
||||
m_default_configuration["override_GL_ARB_draw_buffers_blend"] = "-1";
|
||||
m_default_configuration["override_GL_ARB_gpu_shader5"] = "-1";
|
||||
m_default_configuration["override_GL_ARB_shader_image_load_store"] = "-1";
|
||||
m_default_configuration["override_GL_ARB_sparse_texture"] = "-1";
|
||||
m_default_configuration["override_GL_ARB_sparse_texture2"] = "-1";
|
||||
m_default_configuration["override_GL_ARB_texture_barrier"] = "-1";
|
||||
m_default_configuration["OverrideTextureBarriers"] = "-1";
|
||||
m_default_configuration["OverrideGeometryShaders"] = "-1";
|
||||
|
||||
@@ -395,8 +395,11 @@ static wil::com_ptr_nothrow<IPin> GetFirstPin(IBaseFilter* pBF, PIN_DIRECTION di
|
||||
//
|
||||
|
||||
GSCapture::GSCapture()
|
||||
: m_capturing(false), m_frame(0)
|
||||
: m_capturing(false)
|
||||
, m_out_dir("/tmp/GS_Capture") // FIXME Later add an option
|
||||
#if defined(__unix__)
|
||||
, m_frame(0)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@@ -545,6 +548,9 @@ bool GSCapture::BeginCapture(float fps, GSVector2i recommendedResolution, float
|
||||
m_capturing = true;
|
||||
filename = m_out_dir + "/audio_recording.wav";
|
||||
return true;
|
||||
#else
|
||||
// FIXME: MACOS
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ class GSCapture
|
||||
std::recursive_mutex m_lock;
|
||||
bool m_capturing;
|
||||
GSVector2i m_size;
|
||||
u64 m_frame;
|
||||
std::string m_out_dir;
|
||||
int m_threads;
|
||||
|
||||
@@ -39,6 +38,7 @@ class GSCapture
|
||||
|
||||
#elif defined(__unix__)
|
||||
|
||||
u64 m_frame;
|
||||
std::vector<std::unique_ptr<GSPng::Worker>> m_workers;
|
||||
int m_compression_level;
|
||||
|
||||
|
||||
@@ -174,18 +174,6 @@ const CRC::Game CRC::m_games[] =
|
||||
{0xAA5EC3A3, TalesOfAbyss, JP, 0},
|
||||
{0xFB236A46, SonicUnleashed, US, 0},
|
||||
{0x8C913264, SonicUnleashed, EU, 0},
|
||||
{0xE04EA200, TriAceGames, EU, 0}, // StarOcean3
|
||||
{0x23A97857, TriAceGames, US, 0}, // StarOcean3
|
||||
{0xBEC32D49, TriAceGames, JP, 0}, // StarOcean3
|
||||
{0x8192A241, TriAceGames, JP, 0}, // StarOcean3 directors cut
|
||||
{0xCC96CE93, TriAceGames, US, 0}, // ValkyrieProfile2
|
||||
{0x774DE8E2, TriAceGames, JP, 0}, // ValkyrieProfile2
|
||||
{0x04CCB600, TriAceGames, EU, 0}, // ValkyrieProfile2
|
||||
{0xB65E141B, TriAceGames, DE, 0}, // ValkyrieProfile2
|
||||
{0x8510854E, TriAceGames, FR, 0}, // ValkyrieProfile2
|
||||
{0xC70FC973, TriAceGames, IT, 0}, // ValkyrieProfile2
|
||||
{0x47B9B2FD, TriAceGames, US, 0}, // RadiataStories
|
||||
{0xAC73005E, TriAceGames, JP, 0}, // RadiataStories
|
||||
{0xE8FCF8EC, SMTNocturne, US, 0},
|
||||
{0xF0A31EE3, SMTNocturne, EU, 0}, // SMTNocturne (Lucifers Call in EU)
|
||||
{0xAE0DE7B7, SMTNocturne, EU, 0}, // SMTNocturne (Lucifers Call in EU)
|
||||
@@ -237,9 +225,6 @@ const CRC::Game CRC::m_games[] =
|
||||
{0xDDF76A98, BurnoutGames, JP, 0}, // BurnoutDominator
|
||||
{0x8C9576B4, BurnoutGames, EU, 0}, // BurnoutDominator
|
||||
{0x8C9C76B4, BurnoutGames, EU, 0}, // BurnoutDominator
|
||||
{0x4A0E5B3A, MidnightClub3, US, 0}, // dub
|
||||
{0xEBE1972D, MidnightClub3, EU, 0}, // dub
|
||||
{0x60A42FF5, MidnightClub3, US, 0}, // remix
|
||||
{0x43AB7214, TalesOfLegendia, US, 0},
|
||||
{0x1F8640E0, TalesOfLegendia, JP, 0},
|
||||
{0xE4F5DA2B, TalesOfLegendia, KO, 0},
|
||||
@@ -258,12 +243,6 @@ const CRC::Game CRC::m_games[] =
|
||||
{0x97E9C87E, YakuzaGames, US, 0}, // Yakuza2
|
||||
{0xB1EBD841, YakuzaGames, US, 0}, // Yakuza2
|
||||
{0xC6B95C48, YakuzaGames, JP, 0}, // Yakuza2
|
||||
{0x9000252A, SkyGunner, JP, 0},
|
||||
{0x93092623, SkyGunner, JP, 0},
|
||||
{0xA9461CB2, SkyGunner, US, 0},
|
||||
{0xC71DE999, SkyGunner, US, 0}, // Regular Demo
|
||||
{0xAADF3287, SkyGunner, US, 0}, // Trade Demo
|
||||
{0xB799A60C, SkyGunner, NoRegion, 0},
|
||||
{0x2905C5C6, ZettaiZetsumeiToshi2, US, 0}, // Raw Danger!
|
||||
{0xC988ECBB, ZettaiZetsumeiToshi2, JP, 0},
|
||||
{0x90F4B057, ZettaiZetsumeiToshi2, CH, 0},
|
||||
|
||||
@@ -50,7 +50,6 @@ public:
|
||||
Lamune,
|
||||
Manhunt2,
|
||||
MetalSlug6,
|
||||
MidnightClub3,
|
||||
Okami,
|
||||
Oneechanbara2Special,
|
||||
PolyphonyDigitalGames,
|
||||
@@ -63,7 +62,6 @@ public:
|
||||
ShadowofRome,
|
||||
ShinOnimusha,
|
||||
Simple2000Vol114,
|
||||
SkyGunner,
|
||||
SMTNocturne,
|
||||
SonicUnleashed,
|
||||
Spartan,
|
||||
@@ -77,7 +75,6 @@ public:
|
||||
TombRaiderAnniversary,
|
||||
TombRaiderLegend,
|
||||
TombRaiderUnderworld,
|
||||
TriAceGames,
|
||||
UltramanFightingEvolution,
|
||||
UrbanReign,
|
||||
XenosagaE3,
|
||||
|
||||
@@ -76,12 +76,6 @@ __forceinline bool BitEqual(const T& a, const T& b)
|
||||
return eqb;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_ACCURATE_BUFFER_EMULATION
|
||||
static const GSVector2i default_rt_size(2048, 2048);
|
||||
#else
|
||||
static const GSVector2i default_rt_size(0, 0);
|
||||
#endif
|
||||
|
||||
extern Pcsx2Config::GSOptions GSConfig;
|
||||
|
||||
// Maximum texture size to skip preload/hash path.
|
||||
|
||||
@@ -815,8 +815,8 @@ void GSLocalMemory::WriteImage(int& tx, int& ty, const u8* src, int len, GIFRegB
|
||||
if (TRXREG.RRW == 0)
|
||||
return;
|
||||
|
||||
int l = (int)TRXPOS.DSAX;
|
||||
int r = l + (int)TRXREG.RRW;
|
||||
const int l = (int)TRXPOS.DSAX;
|
||||
const int r = l + (int)TRXREG.RRW;
|
||||
|
||||
// finish the incomplete row first
|
||||
|
||||
@@ -828,12 +828,32 @@ void GSLocalMemory::WriteImage(int& tx, int& ty, const u8* src, int len, GIFRegB
|
||||
len -= n;
|
||||
}
|
||||
|
||||
int la = (l + (bsx - 1)) & ~(bsx - 1);
|
||||
int ra = r & ~(bsx - 1);
|
||||
const int la = (l + (bsx - 1)) & ~(bsx - 1);
|
||||
const int ra = r & ~(bsx - 1);
|
||||
// Round up to the nearest byte (NFL 2K5 does r = 1, l = 0 bpp =4, causing divide by zero)
|
||||
int srcpitch = (((r - l) * trbpp) + 7) >> 3;
|
||||
const int srcpitch = (((r - l) * trbpp) + 7) >> 3;
|
||||
int h = len / srcpitch;
|
||||
|
||||
// Slow path for odd width 4bpp, the fast path expects everything to be perfectly aligned and great,
|
||||
// but things get hairy with 4bpp pixels and odd widths since the lowest size we can address is 8bits, it goes out of sync.
|
||||
// Although I call this a slow path, it's probably faster than modifying the data alignment every other line.
|
||||
// GT3 demo, Jak 2 Japanese subtitles, and the BG Dark Alliance minimap do this.
|
||||
if (trbpp == 4 && (TRXREG.RRW & 0x1))
|
||||
{
|
||||
int count = 0;
|
||||
const int t = TRXPOS.DSAY;
|
||||
const int b = t + (int)TRXREG.RRH;
|
||||
for (int y = t; y < b; y++)
|
||||
{
|
||||
for (int x = l; x < r; x++)
|
||||
{
|
||||
WritePixel4(x, y, src[count >> 1] >> ((count & 1) << 2), BITBLTBUF.DBP, BITBLTBUF.DBW);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ra - la >= bsx && h > 0) // "transfer width" >= "block width" && there is at least one full row
|
||||
{
|
||||
const u8* s = &src[-l * trbpp >> 3];
|
||||
|
||||
@@ -482,7 +482,7 @@ GSVector2i GSState::GetResolution()
|
||||
// The resolution of the framebuffer is double when in FRAME mode and interlaced.
|
||||
// Also we need a special check because no-interlace patches like to render in the original height, but in non-interlaced mode
|
||||
// which means it would normally go off the bottom of the screen. Advantages of emulation, i guess... Limited to Ignore Offsets + Deinterlacing = Off.
|
||||
if ((isinterlaced() && !m_regs->SMODE2.FFMD) || (GSConfig.InterlaceMode == GSInterlaceMode::Off && !GSConfig.PCRTCOffsets))
|
||||
if ((isinterlaced() && !m_regs->SMODE2.FFMD) || (GSConfig.InterlaceMode == GSInterlaceMode::Off && !GSConfig.PCRTCOffsets && !isinterlaced()))
|
||||
resolution.y *= 2;
|
||||
|
||||
if (ignore_offset)
|
||||
|
||||
@@ -161,12 +161,10 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
|
||||
}
|
||||
|
||||
t->SetScale(GSVector2(1, 1)); // Things seem to assume that all textures come out of here with scale 1...
|
||||
t->Commit(); // Clear won't be done if the texture isn't committed.
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GSTexture::Type::RenderTarget:
|
||||
case GSTexture::Type::SparseRenderTarget:
|
||||
{
|
||||
if (clear)
|
||||
ClearRenderTarget(t, 0);
|
||||
@@ -175,7 +173,6 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
|
||||
}
|
||||
break;
|
||||
case GSTexture::Type::DepthStencil:
|
||||
case GSTexture::Type::SparseDepthStencil:
|
||||
{
|
||||
if (clear)
|
||||
ClearDepth(t);
|
||||
@@ -215,12 +212,6 @@ void GSDevice::Recycle(GSTexture* t)
|
||||
{
|
||||
if (t)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
// Uncommit saves memory but it means a futur allocation when we want to reuse the texture.
|
||||
// Which is slow and defeat the purpose of the m_pool cache.
|
||||
// However, it can help to spot part of texture that we forgot to commit
|
||||
t->Uncommit();
|
||||
#endif
|
||||
t->last_frame_used = m_frame;
|
||||
|
||||
m_pool.push_front(t);
|
||||
@@ -259,16 +250,6 @@ void GSDevice::ClearSamplerCache()
|
||||
{
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateSparseRenderTarget(int w, int h, GSTexture::Format format, bool clear)
|
||||
{
|
||||
return FetchSurface(HasColorSparse() ? GSTexture::Type::SparseRenderTarget : GSTexture::Type::RenderTarget, w, h, 1, format, clear, true);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateSparseDepthStencil(int w, int h, GSTexture::Format format, bool clear)
|
||||
{
|
||||
return FetchSurface(HasDepthSparse() ? GSTexture::Type::SparseDepthStencil : GSTexture::Type::DepthStencil, w, h, 1, format, clear, true);
|
||||
}
|
||||
|
||||
GSTexture* GSDevice::CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear)
|
||||
{
|
||||
return FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, format, clear, true);
|
||||
@@ -292,7 +273,7 @@ GSTexture* GSDevice::CreateOffscreen(int w, int h, GSTexture::Format format)
|
||||
|
||||
GSTexture::Format GSDevice::GetDefaultTextureFormat(GSTexture::Type type)
|
||||
{
|
||||
if (type == GSTexture::Type::DepthStencil || type == GSTexture::Type::SparseDepthStencil)
|
||||
if (type == GSTexture::Type::DepthStencil)
|
||||
return GSTexture::Format::DepthStencil;
|
||||
else
|
||||
return GSTexture::Format::Color;
|
||||
|
||||
@@ -729,9 +729,6 @@ public:
|
||||
virtual void BeginScene() {}
|
||||
virtual void EndScene();
|
||||
|
||||
virtual bool HasDepthSparse() { return false; }
|
||||
virtual bool HasColorSparse() { return false; }
|
||||
|
||||
virtual void ClearRenderTarget(GSTexture* t, const GSVector4& c) {}
|
||||
virtual void ClearRenderTarget(GSTexture* t, u32 c) {}
|
||||
virtual void InvalidateRenderTarget(GSTexture* t) {}
|
||||
@@ -742,8 +739,6 @@ public:
|
||||
virtual void PopDebugGroup() {}
|
||||
virtual void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) {}
|
||||
|
||||
GSTexture* CreateSparseRenderTarget(int w, int h, GSTexture::Format format, bool clear = true);
|
||||
GSTexture* CreateSparseDepthStencil(int w, int h, GSTexture::Format format, bool clear = true);
|
||||
GSTexture* CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear = true);
|
||||
GSTexture* CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear = true);
|
||||
GSTexture* CreateTexture(int w, int h, bool mipmap, GSTexture::Format format, bool prefer_reuse = false);
|
||||
|
||||
@@ -762,6 +762,23 @@ void GSRenderer::QueueSnapshot(const std::string& path, u32 gsdump_frames)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_snapshot = "";
|
||||
|
||||
// append the game serial and title
|
||||
if (std::string name(GetDumpName()); !name.empty())
|
||||
{
|
||||
Path::SanitizeFileName(name);
|
||||
if (name.length() > 219)
|
||||
name.resize(219);
|
||||
m_snapshot += name;
|
||||
}
|
||||
if (std::string serial(GetDumpSerial()); !serial.empty())
|
||||
{
|
||||
Path::SanitizeFileName(serial);
|
||||
m_snapshot += '_';
|
||||
m_snapshot += serial;
|
||||
}
|
||||
|
||||
time_t cur_time = time(nullptr);
|
||||
char local_time[16];
|
||||
|
||||
@@ -774,30 +791,18 @@ void GSRenderer::QueueSnapshot(const std::string& path, u32 gsdump_frames)
|
||||
// the captured image is the 2nd image captured at this specific time.
|
||||
static int n = 2;
|
||||
|
||||
m_snapshot += '_';
|
||||
|
||||
if (cur_time == prev_snap)
|
||||
m_snapshot = fmt::format("gs_{0}_({1})", local_time, n++);
|
||||
m_snapshot += fmt::format("{0}_({1})", local_time, n++);
|
||||
else
|
||||
{
|
||||
n = 2;
|
||||
m_snapshot = fmt::format("gs_{}", local_time);
|
||||
m_snapshot += fmt::format("{}", local_time);
|
||||
}
|
||||
prev_snap = cur_time;
|
||||
}
|
||||
|
||||
// append the game serial and title
|
||||
if (std::string name(GetDumpName()); !name.empty())
|
||||
{
|
||||
Path::SanitizeFileName(name);
|
||||
m_snapshot += '_';
|
||||
m_snapshot += name;
|
||||
}
|
||||
if (std::string serial(GetDumpSerial()); !serial.empty())
|
||||
{
|
||||
Path::SanitizeFileName(serial);
|
||||
m_snapshot += '_';
|
||||
m_snapshot += serial;
|
||||
}
|
||||
|
||||
// prepend snapshots directory
|
||||
m_snapshot = Path::Combine(EmuFolders::Snapshots, m_snapshot);
|
||||
}
|
||||
|
||||
@@ -22,13 +22,10 @@
|
||||
GSTexture::GSTexture()
|
||||
: m_scale(1, 1)
|
||||
, m_size(0, 0)
|
||||
, m_committed_size(0, 0)
|
||||
, m_gpu_page_size(0, 0)
|
||||
, m_mipmap_levels(0)
|
||||
, m_type(Type::Invalid)
|
||||
, m_format(Format::Invalid)
|
||||
, m_state(State::Dirty)
|
||||
, m_sparse(false)
|
||||
, m_needs_mipmaps_generated(true)
|
||||
, last_frame_used(0)
|
||||
, OffsetHack_modxy(0.0f)
|
||||
@@ -73,12 +70,10 @@ void GSTexture::Swap(GSTexture* tex)
|
||||
{
|
||||
std::swap(m_scale, tex->m_scale);
|
||||
std::swap(m_size, tex->m_size);
|
||||
std::swap(m_committed_size, tex->m_committed_size);
|
||||
std::swap(m_mipmap_levels, tex->m_mipmap_levels);
|
||||
std::swap(m_type, tex->m_type);
|
||||
std::swap(m_format, tex->m_format);
|
||||
std::swap(m_state, tex->m_state);
|
||||
std::swap(m_sparse, tex->m_sparse);
|
||||
std::swap(m_needs_mipmaps_generated, tex->m_needs_mipmaps_generated);
|
||||
std::swap(last_frame_used, tex->last_frame_used);
|
||||
std::swap(OffsetHack_modxy, tex->OffsetHack_modxy);
|
||||
@@ -133,55 +128,3 @@ void GSTexture::GenerateMipmapsIfNeeded()
|
||||
m_needs_mipmaps_generated = false;
|
||||
GenerateMipmap();
|
||||
}
|
||||
|
||||
void GSTexture::CommitRegion(const GSVector2i& region)
|
||||
{
|
||||
if (!m_sparse)
|
||||
return;
|
||||
|
||||
GSVector2i aligned_region = RoundUpPage(region);
|
||||
aligned_region.x = std::max(m_committed_size.x, aligned_region.x);
|
||||
aligned_region.y = std::max(m_committed_size.y, aligned_region.y);
|
||||
if (aligned_region != m_committed_size)
|
||||
CommitPages(aligned_region, true);
|
||||
}
|
||||
|
||||
void GSTexture::Commit()
|
||||
{
|
||||
if (!m_sparse)
|
||||
return;
|
||||
|
||||
if (m_committed_size != m_size)
|
||||
CommitPages(m_size, true);
|
||||
}
|
||||
|
||||
void GSTexture::Uncommit()
|
||||
{
|
||||
if (!m_sparse)
|
||||
return;
|
||||
|
||||
GSVector2i zero = GSVector2i(0, 0);
|
||||
|
||||
if (m_committed_size != zero)
|
||||
CommitPages(m_committed_size, false);
|
||||
}
|
||||
|
||||
void GSTexture::SetGpuPageSize(const GSVector2i& page_size)
|
||||
{
|
||||
pxAssert(std::bitset<32>(page_size.x + 1).count() == 1);
|
||||
pxAssert(std::bitset<32>(page_size.y + 1).count() == 1);
|
||||
|
||||
m_gpu_page_size = page_size;
|
||||
}
|
||||
|
||||
GSVector2i GSTexture::RoundUpPage(GSVector2i v)
|
||||
{
|
||||
v.x = std::min(m_size.x, v.x);
|
||||
v.y = std::min(m_size.y, v.y);
|
||||
v.x += m_gpu_page_size.x;
|
||||
v.y += m_gpu_page_size.y;
|
||||
v.x &= ~m_gpu_page_size.x;
|
||||
v.y &= ~m_gpu_page_size.y;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -33,8 +33,6 @@ public:
|
||||
DepthStencil,
|
||||
Texture,
|
||||
Offscreen,
|
||||
SparseRenderTarget,
|
||||
SparseDepthStencil,
|
||||
};
|
||||
|
||||
enum class Format : u8
|
||||
@@ -63,25 +61,16 @@ public:
|
||||
protected:
|
||||
GSVector2 m_scale;
|
||||
GSVector2i m_size;
|
||||
GSVector2i m_committed_size;
|
||||
GSVector2i m_gpu_page_size;
|
||||
int m_mipmap_levels;
|
||||
Type m_type;
|
||||
Format m_format;
|
||||
State m_state;
|
||||
bool m_sparse;
|
||||
bool m_needs_mipmaps_generated;
|
||||
|
||||
public:
|
||||
GSTexture();
|
||||
virtual ~GSTexture() {}
|
||||
|
||||
virtual operator bool()
|
||||
{
|
||||
pxAssert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns the native handle of a texture.
|
||||
virtual void* GetNativeHandle() const = 0;
|
||||
|
||||
@@ -113,16 +102,15 @@ public:
|
||||
|
||||
bool IsRenderTargetOrDepthStencil() const
|
||||
{
|
||||
return (m_type >= Type::RenderTarget && m_type <= Type::DepthStencil) ||
|
||||
(m_type >= Type::SparseRenderTarget && m_type <= Type::SparseDepthStencil);
|
||||
return (m_type >= Type::RenderTarget && m_type <= Type::DepthStencil);
|
||||
}
|
||||
bool IsRenderTarget() const
|
||||
{
|
||||
return (m_type == Type::RenderTarget || m_type == Type::SparseRenderTarget);
|
||||
return (m_type == Type::RenderTarget);
|
||||
}
|
||||
bool IsDepthStencil() const
|
||||
{
|
||||
return (m_type == Type::DepthStencil || m_type == Type::SparseDepthStencil);
|
||||
return (m_type == Type::DepthStencil);
|
||||
}
|
||||
|
||||
State GetState() const { return m_state; }
|
||||
@@ -131,14 +119,6 @@ public:
|
||||
void GenerateMipmapsIfNeeded();
|
||||
void ClearMipmapGenerationFlag() { m_needs_mipmaps_generated = false; }
|
||||
|
||||
virtual void CommitPages(const GSVector2i& region, bool commit) {}
|
||||
void CommitRegion(const GSVector2i& region);
|
||||
void Commit();
|
||||
void Uncommit();
|
||||
GSVector2i GetCommittedSize() const { return m_committed_size; }
|
||||
void SetGpuPageSize(const GSVector2i& page_size);
|
||||
GSVector2i RoundUpPage(GSVector2i v);
|
||||
|
||||
// frame number (arbitrary base) the texture was recycled on
|
||||
// different purpose than texture cache ages, do not attempt to merge
|
||||
unsigned last_frame_used;
|
||||
@@ -146,7 +126,7 @@ public:
|
||||
float OffsetHack_modxy;
|
||||
|
||||
// Typical size of a RGBA texture
|
||||
virtual u32 GetMemUsage() { return m_size.x * m_size.y * (m_format == Format::UNorm8 ? 1 : 4); }
|
||||
u32 GetMemUsage() const { return m_size.x * m_size.y * (m_format == Format::UNorm8 ? 1 : 4); }
|
||||
|
||||
// Helper routines for formats/types
|
||||
static bool IsCompressedFormat(Format format) { return (format >= Format::BC1 && format <= Format::BC7); }
|
||||
|
||||
@@ -1370,7 +1370,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
const GSVector4 dRect(config.drawarea);
|
||||
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
|
||||
hdr_rt = CreateRenderTarget(size.x, size.y, GSTexture::Format::FloatColor);
|
||||
hdr_rt->CommitRegion(GSVector2i(config.drawarea.z, config.drawarea.w));
|
||||
// Warning: StretchRect must be called before BeginScene otherwise
|
||||
// vertices will be overwritten. Trust me you don't want to do that.
|
||||
StretchRect(config.rt, sRect, hdr_rt, dRect, ShaderConvert::COPY, false);
|
||||
|
||||
@@ -350,7 +350,7 @@ void GSDevice11::SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, u8
|
||||
|
||||
memset(&bd, 0, sizeof(bd));
|
||||
|
||||
if (bsel.blend_enable)
|
||||
if (bsel.blend_enable && (bsel.wrgba & 0x7))
|
||||
{
|
||||
bd.RenderTarget[0].BlendEnable = TRUE;
|
||||
bd.RenderTarget[0].BlendOp = s_d3d11_blend_ops[bsel.blend_op];
|
||||
|
||||
@@ -319,8 +319,7 @@ void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_f
|
||||
|
||||
GSTexture* GSDevice12::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
|
||||
{
|
||||
pxAssert(type != GSTexture::Type::Offscreen && type != GSTexture::Type::SparseRenderTarget &&
|
||||
type != GSTexture::Type::SparseDepthStencil);
|
||||
pxAssert(type != GSTexture::Type::Offscreen);
|
||||
|
||||
const u32 clamped_width = static_cast<u32>(std::clamp<int>(1, width, D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION));
|
||||
const u32 clamped_height = static_cast<u32>(std::clamp<int>(1, height, D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION));
|
||||
|
||||
@@ -390,22 +390,6 @@ bool GSC_BurnoutGames(const GSFrameInfo& fi, int& skip)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSC_MidnightClub3(const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
if (fi.TME && (fi.FBP > 0x01d00 && fi.FBP <= 0x02a00) && fi.FPSM == PSM_PSMCT32 && (fi.FBP >= 0x01600 && fi.FBP < 0x03260) && fi.TPSM == PSM_PSMT8H)
|
||||
{
|
||||
// Vram usage.
|
||||
// Tested: tokyo default cruise.
|
||||
// Move around a bit, stop car, wait as vram goes down, start moving again, vram spike.
|
||||
skip = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSC_TalesOfLegendia(const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
@@ -639,19 +623,6 @@ bool GSC_UrbanReign(const GSFrameInfo& fi, int& skip)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSC_SkyGunner(const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
if (!fi.TME && !(fi.FBP == 0x0 || fi.FBP == 0x00800 || fi.FBP == 0x008c0 || fi.FBP == 0x03e00) && fi.FPSM == PSM_PSMCT32 && (fi.TBP0 == 0x0 || fi.TBP0 == 0x01800) && fi.TPSM == PSM_PSMCT32)
|
||||
{
|
||||
skip = 1; // Huge Vram usage
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSC_SteambotChronicles(const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
@@ -710,41 +681,6 @@ bool GSC_GetawayGames(const GSFrameInfo& fi, int& skip)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSC_TriAceGames(const GSFrameInfo& fi, int& skip)
|
||||
{
|
||||
// Tri Ace Games: ValkyrieProfile2, RadiataStories, StarOcean3
|
||||
//
|
||||
// The games emulate a stencil buffer with the alpha channel of the RT
|
||||
// The operation of the stencil is selected with the palette
|
||||
// For example -1 wrap will be [240, 16, 32, 48 ....]
|
||||
// i.e. p[A>>4] = (A - 16) % 256
|
||||
//
|
||||
// The fastest and accurate solution will be to replace this pseudo stencil
|
||||
// by a dedicated GPU draw call
|
||||
// 1/ Use future GPU capabilities to do a "kind" of SW blending
|
||||
// 2/ Use a real stencil/atomic image, and then compute the RT alpha value
|
||||
//
|
||||
// Both of those solutions will increase code complexity (and only avoid upscaling
|
||||
// glitches)
|
||||
|
||||
if (skip == 0)
|
||||
{
|
||||
if (fi.TME && fi.FBP == fi.TBP0 && fi.FPSM == PSM_PSMCT32 && fi.TPSM == PSM_PSMT4HH)
|
||||
{
|
||||
skip = 1000;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(fi.TME && fi.FBP == fi.TBP0 && fi.FPSM == PSM_PSMCT32 && fi.TPSM == PSM_PSMT4HH))
|
||||
{
|
||||
skip = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Aggressive only hack
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -895,7 +831,6 @@ void GSState::SetupCrcHack()
|
||||
lut[CRC::KnightsOfTheTemple2] = GSC_KnightsOfTheTemple2;
|
||||
lut[CRC::Kunoichi] = GSC_Kunoichi;
|
||||
lut[CRC::Manhunt2] = GSC_Manhunt2;
|
||||
lut[CRC::MidnightClub3] = GSC_MidnightClub3;
|
||||
lut[CRC::SacredBlaze] = GSC_SacredBlaze;
|
||||
lut[CRC::SakuraTaisen] = GSC_SakuraTaisen;
|
||||
lut[CRC::SakuraWarsSoLongMyLove] = GSC_SakuraWarsSoLongMyLove;
|
||||
@@ -913,7 +848,6 @@ void GSState::SetupCrcHack()
|
||||
// Channel Effect
|
||||
lut[CRC::CrashBandicootWoC] = GSC_CrashBandicootWoC;
|
||||
lut[CRC::GiTS] = GSC_GiTS;
|
||||
lut[CRC::SkyGunner] = GSC_SkyGunner; // Maybe not a channel effect
|
||||
lut[CRC::Spartan] = GSC_Spartan;
|
||||
lut[CRC::SteambotChronicles] = GSC_SteambotChronicles;
|
||||
|
||||
@@ -941,11 +875,6 @@ void GSState::SetupCrcHack()
|
||||
{
|
||||
// Accurate Blending
|
||||
lut[CRC::GetawayGames] = GSC_GetawayGames; // Blending High
|
||||
|
||||
// These games emulate a stencil buffer with the alpha channel of the RT (too slow to move to CRC_Aggressive)
|
||||
// Needs at least Basic Blending,
|
||||
// see https://github.com/PCSX2/pcsx2/pull/2921
|
||||
lut[CRC::TriAceGames] = GSC_TriAceGames;
|
||||
}
|
||||
|
||||
if (CRC_Aggressive)
|
||||
|
||||
@@ -20,15 +20,15 @@
|
||||
#include "GS/Renderers/SW/GSTextureCacheSW.h"
|
||||
#include "GS/Renderers/SW/GSDrawScanline.h"
|
||||
#include "Host.h"
|
||||
#include "common/Align.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
GSRendererHW::GSRendererHW()
|
||||
: GSRenderer()
|
||||
, m_width(default_rt_size.x)
|
||||
, m_height(default_rt_size.y)
|
||||
, m_tc(new GSTextureCache())
|
||||
, m_src(nullptr)
|
||||
, m_reset(false)
|
||||
, m_tex_is_fb(false)
|
||||
, m_channel_shuffle(false)
|
||||
, m_userhacks_tcoffset(false)
|
||||
, m_userhacks_tcoffset_x(0)
|
||||
@@ -50,7 +50,7 @@ GSRendererHW::GSRendererHW()
|
||||
ResetStates();
|
||||
}
|
||||
|
||||
void GSRendererHW::SetScaling()
|
||||
GSVector2i GSRendererHW::GetOutputSize(int real_h)
|
||||
{
|
||||
GSVector2i crtc_size(GetResolution());
|
||||
|
||||
@@ -90,63 +90,10 @@ void GSRendererHW::SetScaling()
|
||||
}
|
||||
}
|
||||
|
||||
// Details of (potential) perf impact of a big framebuffer
|
||||
// 1/ extra memory
|
||||
// 2/ texture cache framebuffer rescaling/copy
|
||||
// 3/ upload of framebuffer (preload hack)
|
||||
// 4/ framebuffer clear (color/depth/stencil)
|
||||
// 5/ read back of the frambuffer
|
||||
//
|
||||
// With the solution
|
||||
// 1/ Nothing to do.Except the texture cache bug (channel shuffle effect)
|
||||
// most of the market is 1GB of VRAM (and soon 2GB)
|
||||
// 2/ limit rescaling/copy to the valid data of the framebuffer
|
||||
// 3/ ??? no solution so far
|
||||
// 4a/ stencil can be limited to valid data.
|
||||
// 4b/ is it useful to clear color? depth? (in any case, it ought to be few operation)
|
||||
// 5/ limit the read to the valid data
|
||||
// Include negative display offsets in the height here.
|
||||
crtc_size.y = std::max(crtc_size.y, real_h);
|
||||
|
||||
// Framebuffer width is always a multiple of 64 so at certain cases it can't cover some weird width values.
|
||||
// 480P , 576P use width as 720 which is not referencable by FBW * 64. so it produces 704 ( the closest value multiple by 64).
|
||||
// In such cases, let's just use the CRTC width.
|
||||
const int fb_width = std::max({(int)m_context->FRAME.FBW * 64, crtc_size.x, 512});
|
||||
// GS doesn't have a specific register for the FrameBuffer height. so we get the height
|
||||
// from physical units of the display rectangle in case the game uses a heigher value of height.
|
||||
//
|
||||
// Gregory: the framebuffer must have enough room to draw
|
||||
// * at least 2 frames such as FMV (see OI_BlitFMV)
|
||||
// * high resolution game such as snowblind engine game
|
||||
//
|
||||
// Autodetection isn't a good idea because it will create flickering
|
||||
// If memory consumption is an issue, there are 2 possibilities
|
||||
// * 1/ Avoid to create hundreds of RT
|
||||
// * 2/ Use sparse texture (requires recent HW)
|
||||
//
|
||||
// Avoid to alternate between 640x1280 and 1280x1024 on snow blind engine game
|
||||
// int fb_height = (fb_width < 1024) ? 1280 : 1024;
|
||||
//
|
||||
// Until performance issue is properly fixed, let's keep an option to reduce the framebuffer size.
|
||||
//
|
||||
// m_large_framebuffer has been inverted to m_conservative_framebuffer, it isn't an option that benefits being enabled all the time for everyone.
|
||||
int fb_height = MAX_FRAMEBUFFER_HEIGHT;
|
||||
if (GSConfig.ConservativeFramebuffer)
|
||||
{
|
||||
fb_height = fb_width < 1024 ? std::max(512, crtc_size.y) : 1024;
|
||||
}
|
||||
|
||||
const int upscaled_fb_w = fb_width * GSConfig.UpscaleMultiplier;
|
||||
const int upscaled_fb_h = fb_height * GSConfig.UpscaleMultiplier;
|
||||
const bool good_rt_size = m_width >= upscaled_fb_w && m_height >= upscaled_fb_h;
|
||||
|
||||
// No need to resize for native/custom resolutions as default size will be enough for native and we manually get RT Buffer size for custom.
|
||||
// don't resize until the display rectangle and register states are stabilized.
|
||||
if (good_rt_size)
|
||||
return;
|
||||
|
||||
m_tc->RemovePartial();
|
||||
m_width = upscaled_fb_w;
|
||||
m_height = upscaled_fb_h;
|
||||
printf("Frame buffer size set to %dx%d (%dx%d)\n", fb_width, fb_height, m_width, m_height);
|
||||
return crtc_size * GSVector2i(static_cast<int>(GSConfig.UpscaleMultiplier), static_cast<int>(GSConfig.UpscaleMultiplier));
|
||||
}
|
||||
|
||||
void GSRendererHW::SetTCOffset()
|
||||
@@ -174,10 +121,42 @@ void GSRendererHW::PurgeTextureCache()
|
||||
m_tc->RemoveAll();
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsPossibleTextureShuffle(GSTextureCache::Source* src) const
|
||||
bool GSRendererHW::UpdateTexIsFB(GSTextureCache::Target* dst, const GIFRegTEX0& TEX0)
|
||||
{
|
||||
if (GSConfig.AccurateBlendingUnit == AccBlendLevel::Minimum || !g_gs_device->Features().texture_barrier)
|
||||
return false;
|
||||
|
||||
// Texture is actually the frame buffer. Stencil emulation to compute shadow (Jak series/tri-ace game)
|
||||
// Will hit the "m_ps_sel.tex_is_fb = 1" path in the draw
|
||||
if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
|
||||
{
|
||||
if (m_context->FRAME.FBMSK == 0x00FFFFFF && TEX0.TBP0 == m_context->FRAME.Block())
|
||||
m_tex_is_fb = true;
|
||||
}
|
||||
else if (m_vt.m_primclass == GS_SPRITE_CLASS)
|
||||
{
|
||||
if (TEX0.TBP0 == m_context->FRAME.Block())
|
||||
{
|
||||
m_tex_is_fb = IsPossibleTextureShuffle(dst, TEX0);
|
||||
|
||||
if (!m_tex_is_fb && !m_vt.IsLinear())
|
||||
{
|
||||
// Make sure that we're not sampling away from the area we're rendering.
|
||||
// We need to take the absolute here, because Beyond Good and Evil undithers itself using a -1,-1 offset.
|
||||
const GSVector4 diff(m_vt.m_min.p.xyxy(m_vt.m_max.p) - m_vt.m_min.t.xyxy(m_vt.m_max.t));
|
||||
if ((diff.abs() < GSVector4(1.0f)).alltrue())
|
||||
m_tex_is_fb = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_tex_is_fb;
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsPossibleTextureShuffle(GSTextureCache::Target* dst, const GIFRegTEX0& TEX0) const
|
||||
{
|
||||
return (PRIM->TME && m_vt.m_primclass == GS_SPRITE_CLASS &&
|
||||
src->m_32_bits_fmt && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 16 &&
|
||||
dst->m_32_bits_fmt && GSLocalMemory::m_psm[TEX0.PSM].bpp == 16 &&
|
||||
GSLocalMemory::m_psm[m_context->FRAME.PSM].bpp == 16);
|
||||
}
|
||||
|
||||
@@ -192,11 +171,6 @@ void GSRendererHW::SetGameCRC(u32 crc, int options)
|
||||
|
||||
bool GSRendererHW::CanUpscale()
|
||||
{
|
||||
if (m_hacks.m_cu && !(this->*m_hacks.m_cu)())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return GSConfig.UpscaleMultiplier != 1;
|
||||
}
|
||||
|
||||
@@ -227,20 +201,12 @@ void GSRendererHW::VSync(u32 field, bool registers_written)
|
||||
if (m_reset)
|
||||
{
|
||||
m_tc->RemoveAll();
|
||||
|
||||
// Reset RT size.
|
||||
m_width = default_rt_size.x;
|
||||
m_height = default_rt_size.y;
|
||||
|
||||
m_reset = false;
|
||||
}
|
||||
|
||||
if (GSConfig.LoadTextureReplacements)
|
||||
GSTextureReplacements::ProcessAsyncLoadedTextures();
|
||||
|
||||
//Check if the frame buffer width or display width has changed
|
||||
SetScaling();
|
||||
|
||||
GSRenderer::VSync(field, registers_written);
|
||||
|
||||
m_tc->IncAge();
|
||||
@@ -286,7 +252,7 @@ GSTexture* GSRendererHW::GetOutput(int i, int& y_offset)
|
||||
|
||||
GSTexture* t = NULL;
|
||||
|
||||
if (GSTextureCache::Target* rt = m_tc->LookupTarget(TEX0, GetTargetSize(), fb_height))
|
||||
if (GSTextureCache::Target* rt = m_tc->LookupDisplayTarget(TEX0, GetOutputSize(fb_height), fb_height))
|
||||
{
|
||||
t = rt->m_texture;
|
||||
|
||||
@@ -321,7 +287,8 @@ GSTexture* GSRendererHW::GetFeedbackOutput()
|
||||
TEX0.TBW = m_regs->EXTBUF.EXBW;
|
||||
TEX0.PSM = m_regs->DISP[m_regs->EXTBUF.FBIN & 1].DISPFB.PSM;
|
||||
|
||||
GSTextureCache::Target* rt = m_tc->LookupTarget(TEX0, GetTargetSize(), /*GetFrameRect(i).bottom*/ m_regs->DISP[m_regs->EXTBUF.FBIN & 1].DISPLAY.DH);
|
||||
const int fb_height = /*GetFrameRect(i).bottom*/ m_regs->DISP[m_regs->EXTBUF.FBIN & 1].DISPLAY.DH;
|
||||
GSTextureCache::Target* rt = m_tc->LookupDisplayTarget(TEX0, GetOutputSize(fb_height), fb_height);
|
||||
|
||||
GSTexture* t = rt->m_texture;
|
||||
|
||||
@@ -763,35 +730,46 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
|
||||
}
|
||||
}
|
||||
|
||||
GSVector2 GSRendererHW::GetTextureScaleFactor(const bool force_upscaling)
|
||||
{
|
||||
GSVector2 scale_factor{ 1.0f, 1.0f };
|
||||
if (force_upscaling || CanUpscale())
|
||||
{
|
||||
const int multiplier = GetUpscaleMultiplier();
|
||||
scale_factor.x = multiplier;
|
||||
scale_factor.y = multiplier;
|
||||
}
|
||||
|
||||
return scale_factor;
|
||||
}
|
||||
|
||||
GSVector2 GSRendererHW::GetTextureScaleFactor()
|
||||
{
|
||||
return GetTextureScaleFactor(false);
|
||||
const float f_upscale = static_cast<float>(GetUpscaleMultiplier());
|
||||
return GSVector2(f_upscale, f_upscale);
|
||||
}
|
||||
|
||||
GSVector2i GSRendererHW::GetTargetSize()
|
||||
GSVector2i GSRendererHW::GetTargetSize(GSVector2i* unscaled_size)
|
||||
{
|
||||
const GSVector2i t_size = { m_width, m_height };
|
||||
if (GetUpscaleMultiplier() == 1 || CanUpscale())
|
||||
return t_size;
|
||||
// Undo the upscaling for native resolution draws.
|
||||
const GSVector2 up_s = GetTextureScaleFactor(true);
|
||||
return {
|
||||
static_cast<int>(std::ceil(static_cast<float>(t_size.x) / up_s.x)),
|
||||
static_cast<int>(std::ceil(static_cast<float>(t_size.y) / up_s.y)),
|
||||
};
|
||||
// Don't blindly expand out to the scissor size if we're not drawing to it.
|
||||
// e.g. Burnout 3, God of War II, etc.
|
||||
u32 min_height = std::min<u32>(m_context->scissor.in.w, m_r.w);
|
||||
|
||||
// Another thing these games like to do, is draw a 512x896 shuffle, which would result in us
|
||||
// expanding the target out to 896 height, but the extra area would all be black, with the
|
||||
// draw effectively changing nothing for the new area. So, instead, lets try to detect these
|
||||
// draws by double-checking we're not stretching the texture (gradient of <1).
|
||||
if (PRIM->TME && m_vt.m_primclass == GS_SPRITE_CLASS && m_src && (m_src->m_target || m_src->m_from_target))
|
||||
{
|
||||
const float diff = std::abs((m_vt.m_max.p.y - m_vt.m_min.p.y) - (m_vt.m_max.t.y - m_vt.m_min.t.y));
|
||||
if (diff <= 1.0f)
|
||||
{
|
||||
// Clamp to the texture size. We're working in unscaled coordinates here, so undo the upscaling.
|
||||
min_height = std::min(min_height, static_cast<u32>(static_cast<float>(m_src->m_texture->GetHeight()) / m_src->m_texture->GetScale().y));
|
||||
}
|
||||
}
|
||||
|
||||
// Align to even lines, reduces the chance of tiny resizes.
|
||||
min_height = Common::AlignUpPow2(min_height, 2);
|
||||
|
||||
const u32 width = m_context->FRAME.FBW * 64u;
|
||||
const u32 height = m_tc->GetTargetHeight(m_context->FRAME.FBP, m_context->FRAME.FBW, m_context->FRAME.PSM, min_height);
|
||||
if (unscaled_size)
|
||||
{
|
||||
unscaled_size->x = static_cast<int>(width);
|
||||
unscaled_size->y = static_cast<int>(height);
|
||||
}
|
||||
|
||||
GL_INS("Target size for %x %u %u: %ux%u", m_context->FRAME.FBP, m_context->FRAME.FBW, m_context->FRAME.PSM, width, height);
|
||||
|
||||
return GSVector2i(static_cast<int>(width * GSConfig.UpscaleMultiplier), static_cast<int>(height * GSConfig.UpscaleMultiplier));
|
||||
}
|
||||
|
||||
void GSRendererHW::InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r)
|
||||
@@ -1380,6 +1358,7 @@ void GSRendererHW::Draw()
|
||||
|
||||
m_src = nullptr;
|
||||
m_texture_shuffle = false;
|
||||
m_tex_is_fb = false;
|
||||
|
||||
if (PRIM->TME)
|
||||
{
|
||||
@@ -1605,31 +1584,29 @@ void GSRendererHW::Draw()
|
||||
}
|
||||
}
|
||||
|
||||
const GSVector2i t_size = GetTargetSize();
|
||||
// The rectangle of the draw
|
||||
m_r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(context->scissor.in));
|
||||
GSVector2i unscaled_size;
|
||||
const GSVector2i t_size = GetTargetSize(&unscaled_size);
|
||||
|
||||
// Ensure draw rect is clamped to framebuffer size. Necessary for updating valid area.
|
||||
m_r = m_r.rintersect(GSVector4i(0, 0, unscaled_size.x, unscaled_size.y));
|
||||
|
||||
TEX0.TBP0 = context->FRAME.Block();
|
||||
TEX0.TBW = context->FRAME.FBW;
|
||||
TEX0.PSM = context->FRAME.PSM;
|
||||
|
||||
GSTextureCache::Target* rt = nullptr;
|
||||
GSTexture* rt_tex = nullptr;
|
||||
if (!no_rt)
|
||||
{
|
||||
rt = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::RenderTarget, true, fm);
|
||||
rt_tex = rt->m_texture;
|
||||
}
|
||||
|
||||
TEX0.TBP0 = context->ZBUF.Block();
|
||||
TEX0.TBW = context->FRAME.FBW;
|
||||
TEX0.PSM = context->ZBUF.PSM;
|
||||
|
||||
GSTextureCache::Target* ds = nullptr;
|
||||
GSTexture* ds_tex = nullptr;
|
||||
if (!no_ds)
|
||||
{
|
||||
ds = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::DepthStencil, context->DepthWrite());
|
||||
ds_tex = ds->m_texture;
|
||||
}
|
||||
|
||||
if (rt)
|
||||
{
|
||||
@@ -1639,38 +1616,21 @@ void GSRendererHW::Draw()
|
||||
rt->m_32_bits_fmt = m_texture_shuffle || (GSLocalMemory::m_psm[context->FRAME.PSM].bpp != 16);
|
||||
}
|
||||
|
||||
// The rectangle of the draw
|
||||
m_r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(context->scissor.in));
|
||||
|
||||
{
|
||||
const GSVector2 up_s = GetTextureScaleFactor();
|
||||
const int up_w = static_cast<int>(std::ceil(static_cast<float>(m_r.z) * up_s.x));
|
||||
const int up_h = static_cast<int>(std::ceil(static_cast<float>(m_r.w) * up_s.y));
|
||||
const int new_w = std::max(up_w, std::max(rt_tex ? rt_tex->GetWidth() : 0, ds_tex ? ds_tex->GetWidth() : 0));
|
||||
const int new_h = std::max(up_h, std::max(rt_tex ? rt_tex->GetHeight() : 0, ds_tex ? ds_tex->GetHeight() : 0));
|
||||
std::array<GSTextureCache::Target*, 2> ts{ rt, ds };
|
||||
for (GSTextureCache::Target* t : ts)
|
||||
// We still need to make sure the dimensions of the targets match.
|
||||
const GSVector2 up_s(GetTextureScaleFactor());
|
||||
const int new_w = std::max(t_size.x, std::max(rt ? rt->m_texture->GetWidth() : 0, ds ? ds->m_texture->GetWidth() : 0));
|
||||
const int new_h = std::max(t_size.y, std::max(rt ? rt->m_texture->GetHeight() : 0, ds ? ds->m_texture->GetHeight() : 0));
|
||||
|
||||
if (rt)
|
||||
{
|
||||
if (t)
|
||||
{
|
||||
// Adjust texture size to fit current draw if necessary.
|
||||
GSTexture* tex = t->m_texture;
|
||||
assert(up_s == tex->GetScale());
|
||||
const int w = tex->GetWidth();
|
||||
const int h = tex->GetHeight();
|
||||
if (w != new_w || h != new_h)
|
||||
{
|
||||
const bool is_rt = t == rt;
|
||||
t->m_texture = is_rt ?
|
||||
g_gs_device->CreateSparseRenderTarget(new_w, new_h, tex->GetFormat()) :
|
||||
g_gs_device->CreateSparseDepthStencil(new_w, new_h, tex->GetFormat());
|
||||
const GSVector4i r{ 0, 0, w, h };
|
||||
g_gs_device->CopyRect(tex, t->m_texture, r, 0, 0);
|
||||
g_gs_device->Recycle(tex);
|
||||
t->m_texture->SetScale(up_s);
|
||||
(is_rt ? rt_tex : ds_tex) = t->m_texture;
|
||||
}
|
||||
}
|
||||
pxAssert(rt->m_texture->GetScale() == up_s);
|
||||
rt->ResizeTexture(new_w, new_h, up_s);
|
||||
}
|
||||
if (ds)
|
||||
{
|
||||
pxAssert(ds->m_texture->GetScale() == up_s);
|
||||
ds->ResizeTexture(new_w, new_h, up_s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1704,24 +1664,24 @@ void GSRendererHW::Draw()
|
||||
}
|
||||
}
|
||||
|
||||
if (s_save && s_n >= s_saven)
|
||||
if (rt && s_save && s_n >= s_saven)
|
||||
{
|
||||
s = StringUtil::StdStringFromFormat("%05d_f%lld_rt0_%05x_%s.bmp", s_n, frame, context->FRAME.Block(), psm_str(context->FRAME.PSM));
|
||||
|
||||
if (rt_tex)
|
||||
rt_tex->Save(m_dump_root + s);
|
||||
if (rt->m_texture)
|
||||
rt->m_texture->Save(m_dump_root + s);
|
||||
}
|
||||
|
||||
if (s_savez && s_n >= s_saven)
|
||||
if (ds && s_savez && s_n >= s_saven)
|
||||
{
|
||||
s = StringUtil::StdStringFromFormat("%05d_f%lld_rz0_%05x_%s.bmp", s_n, frame, context->ZBUF.Block(), psm_str(context->ZBUF.PSM));
|
||||
|
||||
if (ds_tex)
|
||||
ds_tex->Save(m_dump_root + s);
|
||||
if (ds->m_texture)
|
||||
ds->m_texture->Save(m_dump_root + s);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_hacks.m_oi && !(this->*m_hacks.m_oi)(rt_tex, ds_tex, m_src))
|
||||
if (m_hacks.m_oi && !(this->*m_hacks.m_oi)(rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, m_src))
|
||||
{
|
||||
GL_INS("Warning skipping a draw call (%d)", s_n);
|
||||
return;
|
||||
@@ -1746,7 +1706,7 @@ void GSRendererHW::Draw()
|
||||
|
||||
OI_GsMemClear();
|
||||
|
||||
OI_DoubleHalfClear(rt_tex, ds_tex);
|
||||
OI_DoubleHalfClear(rt, ds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1801,7 +1761,7 @@ void GSRendererHW::Draw()
|
||||
|
||||
//
|
||||
|
||||
DrawPrims(rt_tex, ds_tex, m_src);
|
||||
DrawPrims(rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, m_src);
|
||||
|
||||
//
|
||||
|
||||
@@ -1853,16 +1813,16 @@ void GSRendererHW::Draw()
|
||||
{
|
||||
s = StringUtil::StdStringFromFormat("%05d_f%lld_rt1_%05x_%s.bmp", s_n, frame, context->FRAME.Block(), psm_str(context->FRAME.PSM));
|
||||
|
||||
if (rt_tex)
|
||||
rt_tex->Save(m_dump_root + s);
|
||||
if (rt)
|
||||
rt->m_texture->Save(m_dump_root + s);
|
||||
}
|
||||
|
||||
if (s_savez && s_n >= s_saven)
|
||||
{
|
||||
s = StringUtil::StdStringFromFormat("%05d_f%lld_rz1_%05x_%s.bmp", s_n, frame, context->ZBUF.Block(), psm_str(context->ZBUF.PSM));
|
||||
|
||||
if (ds_tex)
|
||||
ds_tex->Save(m_dump_root + s);
|
||||
if (ds)
|
||||
rt->m_texture->Save(m_dump_root + s);
|
||||
}
|
||||
|
||||
if (s_savel > 0 && (s_n - s_saven) > s_savel)
|
||||
@@ -2080,14 +2040,17 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask()
|
||||
// Please bang my head against the wall!
|
||||
// 1/ Reduce the frame mask to a 16 bit format
|
||||
const u32 m = m_context->FRAME.FBMSK & GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk;
|
||||
|
||||
// fbmask is converted to a 16bit version to represent the 2 32bit channels it's writing to.
|
||||
// The lower 8 bits represents the Red/Blue channels, the top 8 bits is Green/Alpha, depending on write_ba.
|
||||
const u32 fbmask = ((m >> 3) & 0x1F) | ((m >> 6) & 0x3E0) | ((m >> 9) & 0x7C00) | ((m >> 16) & 0x8000);
|
||||
// FIXME GSVector will be nice here
|
||||
const u8 rg_mask = fbmask & 0xFF;
|
||||
const u8 ba_mask = (fbmask >> 8) & 0xFF;
|
||||
const u8 rb_mask = fbmask & 0xFF;
|
||||
const u8 ga_mask = (fbmask >> 8) & 0xFF;
|
||||
m_conf.colormask.wrgba = 0;
|
||||
|
||||
// 2 Select the new mask (Please someone put SSE here)
|
||||
if (rg_mask != 0xFF)
|
||||
if (rb_mask != 0xFF)
|
||||
{
|
||||
if (write_ba)
|
||||
{
|
||||
@@ -2099,11 +2062,11 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask()
|
||||
GL_INS("Color shuffle %s => R", read_ba ? "B" : "R");
|
||||
m_conf.colormask.wr = 1;
|
||||
}
|
||||
if (rg_mask)
|
||||
if (rb_mask)
|
||||
m_conf.ps.fbmask = 1;
|
||||
}
|
||||
|
||||
if (ba_mask != 0xFF)
|
||||
if (ga_mask != 0xFF)
|
||||
{
|
||||
if (write_ba)
|
||||
{
|
||||
@@ -2115,16 +2078,16 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask()
|
||||
GL_INS("Color shuffle %s => G", read_ba ? "A" : "G");
|
||||
m_conf.colormask.wg = 1;
|
||||
}
|
||||
if (ba_mask)
|
||||
if (ga_mask)
|
||||
m_conf.ps.fbmask = 1;
|
||||
}
|
||||
|
||||
if (m_conf.ps.fbmask && enable_fbmask_emulation)
|
||||
{
|
||||
m_conf.cb_ps.FbMask.r = rg_mask;
|
||||
m_conf.cb_ps.FbMask.g = rg_mask;
|
||||
m_conf.cb_ps.FbMask.b = ba_mask;
|
||||
m_conf.cb_ps.FbMask.a = ba_mask;
|
||||
m_conf.cb_ps.FbMask.r = rb_mask;
|
||||
m_conf.cb_ps.FbMask.g = ga_mask;
|
||||
m_conf.cb_ps.FbMask.b = rb_mask;
|
||||
m_conf.cb_ps.FbMask.a = ga_mask;
|
||||
|
||||
// No blending so hit unsafe path.
|
||||
if (!PRIM->ABE || !features.texture_barrier)
|
||||
@@ -2336,21 +2299,8 @@ void GSRendererHW::EmulateChannelShuffle(const GSTextureCache::Source* tex)
|
||||
m_conf.tex = *tex->m_from_target;
|
||||
if (m_conf.tex)
|
||||
{
|
||||
if (m_conf.tex == m_conf.rt)
|
||||
{
|
||||
// sample from fb instead
|
||||
m_conf.tex = nullptr;
|
||||
m_conf.ps.tex_is_fb = true;
|
||||
m_conf.require_one_barrier = true;
|
||||
}
|
||||
else if (m_conf.tex == m_conf.ds)
|
||||
{
|
||||
// if depth testing is disabled, we don't need to copy, and can just unbind the depth buffer
|
||||
// no need for a barrier for GL either, since it's not bound to depth and texture concurrently
|
||||
// otherwise, the backend should recognise the hazard, and copy the buffer (D3D/Vulkan).
|
||||
if (m_conf.depth.ztst == ZTST_ALWAYS)
|
||||
m_conf.ds = nullptr;
|
||||
}
|
||||
// Identify when we're sampling the current buffer, defer fixup for later.
|
||||
m_tex_is_fb |= (m_conf.tex == m_conf.rt || m_conf.tex == m_conf.ds);
|
||||
}
|
||||
|
||||
// Replace current draw with a fullscreen sprite
|
||||
@@ -2409,6 +2359,7 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool&
|
||||
// Get alpha value
|
||||
const bool alpha_c0_zero = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().max == 0) && !IsCoverageAlpha();
|
||||
const bool alpha_c0_one = (m_conf.ps.blend_c == 0 && (GetAlphaMinMax().min == 128) && (GetAlphaMinMax().max == 128)) || IsCoverageAlpha();
|
||||
const bool alpha_c0_high_min_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().min > 128) && !IsCoverageAlpha();
|
||||
const bool alpha_c0_high_max_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().max > 128) && !IsCoverageAlpha();
|
||||
const bool alpha_c2_zero = (m_conf.ps.blend_c == 2 && ALPHA.FIX == 0u);
|
||||
const bool alpha_c2_one = (m_conf.ps.blend_c == 2 && ALPHA.FIX == 128u);
|
||||
@@ -2762,12 +2713,26 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool&
|
||||
// For mixed blend, the source blend is done in the shader (so we use CONST_ONE as a factor).
|
||||
m_conf.blend = {true, GSDevice::CONST_ONE, blend.dst, blend.op, m_conf.ps.blend_c == 2, ALPHA.FIX};
|
||||
m_conf.ps.blend_mix = 1;
|
||||
|
||||
|
||||
// Elide DSB colour output if not used by dest.
|
||||
m_conf.ps.no_color1 |= !GSDevice::IsDualSourceBlendFactor(blend.dst);
|
||||
|
||||
if (blend_mix1)
|
||||
{
|
||||
if (m_conf.ps.blend_b == m_conf.ps.blend_d && (alpha_c0_high_min_one || alpha_c2_high_one))
|
||||
{
|
||||
// Replace Cs*As + Cd*(1 - As) with Cs*As - Cd*(As - 1).
|
||||
// Replace Cs*F + Cd*(1 - F) with Cs*F - Cd*(F - 1).
|
||||
// As - 1 or F - 1 subtraction is only done for the dual source output (hw blending part) since we are changing the equation.
|
||||
// Af will be replaced with As in shader and send it to dual source output.
|
||||
m_conf.blend = {true, GSDevice::CONST_ONE, GSDevice::SRC1_ALPHA, GSDevice::OP_SUBTRACT, false, 0};
|
||||
// clr_hw 1 will disable alpha clamp, we can reuse the old bits.
|
||||
m_conf.ps.clr_hw = 1;
|
||||
//m_conf.ps.blend_mix = 0;
|
||||
// DSB output will always be used.
|
||||
m_conf.ps.no_color1 = false;
|
||||
}
|
||||
|
||||
m_conf.ps.blend_a = 0;
|
||||
m_conf.ps.blend_b = 2;
|
||||
m_conf.ps.blend_d = 2;
|
||||
@@ -3150,6 +3115,41 @@ void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Source* tex)
|
||||
if (!m_channel_shuffle)
|
||||
m_conf.tex = tex->m_texture;
|
||||
m_conf.pal = tex->m_palette;
|
||||
|
||||
// Detect framebuffer read that will need special handling
|
||||
if (m_tex_is_fb)
|
||||
{
|
||||
if (m_conf.tex == m_conf.rt)
|
||||
{
|
||||
// This pattern is used by several games to emulate a stencil (shadow)
|
||||
// Ratchet & Clank, Jak do alpha integer multiplication (tfx) which is mostly equivalent to +1/-1
|
||||
// Tri-Ace (Star Ocean 3/RadiataStories/VP2) uses a palette to handle the +1/-1
|
||||
GL_DBG("Source and Target are the same! Let's sample the framebuffer");
|
||||
m_conf.tex = nullptr;
|
||||
m_conf.ps.tex_is_fb = true;
|
||||
if (m_prim_overlap == PRIM_OVERLAP_NO || !g_gs_device->Features().texture_barrier)
|
||||
m_conf.require_one_barrier = true;
|
||||
else
|
||||
m_conf.require_full_barrier = true;
|
||||
}
|
||||
else if (m_conf.tex == m_conf.ds)
|
||||
{
|
||||
// if depth testing is disabled, we don't need to copy, and can just unbind the depth buffer
|
||||
// no need for a barrier for GL either, since it's not bound to depth and texture concurrently
|
||||
// otherwise, the backend should recognise the hazard, and copy the buffer (D3D/Vulkan).
|
||||
if (m_conf.depth.ztst == ZTST_ALWAYS)
|
||||
{
|
||||
m_conf.ds = nullptr;
|
||||
m_tex_is_fb = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// weird... we detected a fb read, but didn't end up using it?
|
||||
DevCon.WriteLn("Tex-is-FB set but not used?");
|
||||
m_tex_is_fb = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GSRendererHW::EmulateATST(float& AREF, GSHWDrawConfig::PSSelector& ps, bool pass_2)
|
||||
@@ -3217,10 +3217,6 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
|
||||
area_out.x, area_out.y, area_out.z, area_out.w);
|
||||
#endif
|
||||
|
||||
const GSVector2i& rtsize = ds ? ds->GetSize() : rt->GetSize();
|
||||
const GSVector2& rtscale = ds ? ds->GetScale() : rt->GetScale();
|
||||
const GSDevice::FeatureSupport features(g_gs_device->Features());
|
||||
|
||||
const bool DATE = m_context->TEST.DATE && m_context->FRAME.PSM != PSM_PSMCT24;
|
||||
bool DATE_PRIMID = false;
|
||||
bool DATE_BARRIER = false;
|
||||
@@ -3250,32 +3246,12 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
|
||||
// Upscaling hack to avoid various line/grid issues
|
||||
MergeSprite(tex);
|
||||
|
||||
const GSDevice::FeatureSupport features(g_gs_device->Features());
|
||||
if (!features.framebuffer_fetch)
|
||||
m_prim_overlap = PrimitiveOverlap();
|
||||
else
|
||||
m_prim_overlap = PRIM_OVERLAP_UNKNOW;
|
||||
|
||||
// Detect framebuffer read that will need special handling
|
||||
if (features.texture_barrier && (m_context->FRAME.Block() == m_context->TEX0.TBP0) && PRIM->TME && GSConfig.AccurateBlendingUnit != AccBlendLevel::Minimum)
|
||||
{
|
||||
const u32 fb_mask = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk;
|
||||
if (((m_context->FRAME.FBMSK & fb_mask) == (fb_mask & 0x00FFFFFF)) && (m_vt.m_primclass == GS_TRIANGLE_CLASS))
|
||||
{
|
||||
// This pattern is used by several games to emulate a stencil (shadow)
|
||||
// Ratchet & Clank, Jak do alpha integer multiplication (tfx) which is mostly equivalent to +1/-1
|
||||
// Tri-Ace (Star Ocean 3/RadiataStories/VP2) uses a palette to handle the +1/-1
|
||||
GL_DBG("Source and Target are the same! Let's sample the framebuffer");
|
||||
m_conf.ps.tex_is_fb = 1;
|
||||
m_conf.require_full_barrier = !features.framebuffer_fetch;
|
||||
}
|
||||
else if (m_prim_overlap != PRIM_OVERLAP_NO)
|
||||
{
|
||||
// Note: It is fine if the texture fits in a single GS page. First access will cache
|
||||
// the page in the GS texture buffer.
|
||||
GL_INS("ERROR: Source and Target are the same!");
|
||||
}
|
||||
}
|
||||
|
||||
EmulateTextureShuffleAndFbmask();
|
||||
|
||||
// DATE: selection of the algorithm. Must be done before blending because GL42 is not compatible with blending
|
||||
@@ -3418,6 +3394,8 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
|
||||
m_conf.vs.fst = PRIM->FST;
|
||||
|
||||
// FIXME D3D11 and GL support half pixel center. Code could be easier!!!
|
||||
const GSVector2i rtsize(m_conf.ds ? m_conf.ds->GetSize() : m_conf.rt->GetSize());
|
||||
const GSVector2 rtscale(m_conf.ds ? m_conf.ds->GetScale() : m_conf.rt->GetScale());
|
||||
const float sx = 2.0f * rtscale.x / (rtsize.x << 4);
|
||||
const float sy = 2.0f * rtscale.y / (rtsize.y << 4);
|
||||
const float ox = (float)(int)m_context->XYOFFSET.OFX;
|
||||
@@ -3471,7 +3449,7 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
|
||||
|
||||
if (m_conf.ps.dither)
|
||||
{
|
||||
GL_DBG("DITHERING mode ENABLED (%d)", m_dithering);
|
||||
GL_DBG("DITHERING mode ENABLED (%d)", GSConfig.Dithering);
|
||||
|
||||
m_conf.ps.dither = GSConfig.Dithering;
|
||||
m_conf.cb_ps.DitherMatrix[0] = GSVector4(m_env.DIMX.DM00, m_env.DIMX.DM01, m_env.DIMX.DM02, m_env.DIMX.DM03);
|
||||
@@ -3601,12 +3579,6 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
|
||||
|
||||
SetupIA(sx, sy);
|
||||
|
||||
if (rt)
|
||||
rt->CommitRegion(GSVector2i(m_conf.drawarea.z, m_conf.drawarea.w));
|
||||
|
||||
if (ds)
|
||||
ds->CommitRegion(GSVector2i(m_conf.drawarea.z, m_conf.drawarea.w));
|
||||
|
||||
m_conf.alpha_second_pass.enable = ate_second_pass;
|
||||
|
||||
if (ate_second_pass)
|
||||
@@ -3722,13 +3694,6 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
|
||||
g_gs_device->RenderHW(m_conf);
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsDummyTexture() const
|
||||
{
|
||||
// Texture is actually the frame buffer. Stencil emulation to compute shadow (Jak series/tri-ace game)
|
||||
// Will hit the "m_ps_sel.tex_is_fb = 1" path in the draw
|
||||
return g_gs_device->Features().texture_barrier && (m_context->FRAME.Block() == m_context->TEX0.TBP0) && PRIM->TME && GSConfig.AccurateBlendingUnit != AccBlendLevel::Minimum && m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_context->FRAME.FBMSK == 0x00FFFFFF);
|
||||
}
|
||||
|
||||
bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_tex)
|
||||
{
|
||||
// Master enable.
|
||||
@@ -4177,10 +4142,8 @@ bool GSRendererHW::SwPrimRender()
|
||||
GSRendererHW::Hacks::Hacks()
|
||||
: m_oi_map(m_oi_list)
|
||||
, m_oo_map(m_oo_list)
|
||||
, m_cu_map(m_cu_list)
|
||||
, m_oi(NULL)
|
||||
, m_oo(NULL)
|
||||
, m_cu(NULL)
|
||||
{
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::BigMuthaTruckers, CRC::RegionCount, &GSRendererHW::OI_BigMuthaTruckers));
|
||||
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::DBZBT2, CRC::RegionCount, &GSRendererHW::OI_DBZBTGames));
|
||||
@@ -4206,7 +4169,6 @@ void GSRendererHW::Hacks::SetGameCRC(const CRC::Game& game)
|
||||
|
||||
m_oi = m_oi_map[hash];
|
||||
m_oo = m_oo_map[hash];
|
||||
m_cu = m_cu_map[hash];
|
||||
|
||||
if (GSConfig.PointListPalette)
|
||||
{
|
||||
@@ -4220,7 +4182,7 @@ void GSRendererHW::Hacks::SetGameCRC(const CRC::Game& game)
|
||||
// Trick to do a fast clear on the GS
|
||||
// Set frame buffer pointer on the start of the buffer. Set depth buffer pointer on the half buffer
|
||||
// FB + depth write will fill the full buffer.
|
||||
void GSRendererHW::OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds)
|
||||
void GSRendererHW::OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCache::Target*& ds)
|
||||
{
|
||||
// Note gs mem clear must be tested before calling this function
|
||||
|
||||
@@ -4266,20 +4228,35 @@ void GSRendererHW::OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds)
|
||||
GL_INS("OI_DoubleHalfClear:%s: base %x half %x. w_pages %d h_pages %d fbw %d. Color %x",
|
||||
clear_depth ? "depth" : "target", base << 5, half << 5, w_pages, h_pages, m_context->FRAME.FBW, color);
|
||||
|
||||
// Commit texture with a factor 2 on the height
|
||||
GSTexture* t = clear_depth ? ds : rt;
|
||||
const GSVector4i commitRect = ComputeBoundingBox(t->GetScale(), t->GetSize());
|
||||
t->CommitRegion(GSVector2i(commitRect.z, 2 * commitRect.w));
|
||||
// Handle the case where the game stacks FBP and ZBP immediately after one another.
|
||||
// We incorrectly compute the height here, because both the scissor and draw rectangle will only be half
|
||||
// the height of what's effectively being cleared. Spider-Man 2's shadows are a good test case here: it
|
||||
// draws the shadow map to a 128x128 texture, but relies on a 1 pixel border around the edge to "cut off"
|
||||
// the shadows. We cap it to a 256 height, because having a >=512 height framebuffer is very rare, and it
|
||||
// stops us doubling actual framebuffers unintentionally (very common).
|
||||
GSTextureCache::Target* t = clear_depth ? ds : rt;
|
||||
const u32 unscaled_height = static_cast<u32>(static_cast<float>(t->m_texture->GetHeight()) / t->m_texture->GetScale().y);
|
||||
if (unscaled_height == m_context->scissor.in.w && unscaled_height <= 256)
|
||||
{
|
||||
t->ResizeTexture(t->m_texture->GetWidth(), t->m_texture->GetHeight() * 2, t->m_texture->GetScale());
|
||||
if (clear_depth)
|
||||
rt = nullptr;
|
||||
else
|
||||
ds = nullptr;
|
||||
|
||||
// Feed it back into the height cache.
|
||||
m_tc->GetTargetHeight(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, unscaled_height * 2);
|
||||
}
|
||||
|
||||
if (clear_depth)
|
||||
{
|
||||
// Only pure clear are supported for depth
|
||||
ASSERT(color == 0);
|
||||
g_gs_device->ClearDepth(t);
|
||||
g_gs_device->ClearDepth(ds->m_texture);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_gs_device->ClearRenderTarget(t, color);
|
||||
g_gs_device->ClearRenderTarget(rt->m_texture, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4295,10 +4272,7 @@ void GSRendererHW::OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds)
|
||||
|
||||
// If both buffers are side by side we can expect a fast clear in on-going
|
||||
const u32 color = v[1].RGBAQ.U32[0];
|
||||
const GSVector4i commitRect = ComputeBoundingBox(rt->GetScale(), rt->GetSize());
|
||||
rt->CommitRegion(GSVector2i(commitRect.z, commitRect.w));
|
||||
|
||||
g_gs_device->ClearRenderTarget(rt, color);
|
||||
g_gs_device->ClearRenderTarget(rt->m_texture, color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4582,8 +4556,6 @@ bool GSRendererHW::OI_FFX(GSTexture* rt, GSTexture* ds, GSTextureCache::Source*
|
||||
{
|
||||
// random battle transition (z buffer written directly, clear it now)
|
||||
GL_INS("OI_FFX ZB clear");
|
||||
if (ds)
|
||||
ds->Commit(); // Don't bother to save few MB for a single game
|
||||
g_gs_device->ClearDepth(ds);
|
||||
}
|
||||
|
||||
@@ -4635,7 +4607,6 @@ bool GSRendererHW::OI_RozenMaidenGebetGarden(GSTexture* rt, GSTexture* ds, GSTex
|
||||
if (GSTextureCache::Target* tmp_rt = m_tc->LookupTarget(TEX0, GetTargetSize(), GSTextureCache::RenderTarget, true))
|
||||
{
|
||||
GL_INS("OI_RozenMaidenGebetGarden FB clear");
|
||||
tmp_rt->m_texture->Commit(); // Don't bother to save few MB for a single game
|
||||
g_gs_device->ClearRenderTarget(tmp_rt->m_texture, 0);
|
||||
}
|
||||
|
||||
@@ -4654,7 +4625,6 @@ bool GSRendererHW::OI_RozenMaidenGebetGarden(GSTexture* rt, GSTexture* ds, GSTex
|
||||
if (GSTextureCache::Target* tmp_ds = m_tc->LookupTarget(TEX0, GetTargetSize(), GSTextureCache::DepthStencil, true))
|
||||
{
|
||||
GL_INS("OI_RozenMaidenGebetGarden ZB clear");
|
||||
tmp_ds->m_texture->Commit(); // Don't bother to save few MB for a single game
|
||||
g_gs_device->ClearDepth(tmp_ds->m_texture);
|
||||
}
|
||||
|
||||
@@ -4689,12 +4659,14 @@ bool GSRendererHW::OI_SonicUnleashed(GSTexture* rt, GSTexture* ds, GSTextureCach
|
||||
|
||||
GL_INS("OI_SonicUnleashed replace draw by a copy");
|
||||
|
||||
GSTextureCache::Target* src = m_tc->LookupTarget(Texture, GetTargetSize(), GSTextureCache::RenderTarget, true);
|
||||
GSTextureCache::Target* src = m_tc->LookupTarget(Texture, GSVector2i(1, 1), GSTextureCache::RenderTarget, true);
|
||||
|
||||
const GSVector2i size = rt->GetSize();
|
||||
const GSVector2i rt_size(rt->GetSize());
|
||||
const GSVector2i src_size(src->m_texture->GetSize());
|
||||
const GSVector2i copy_size(std::min(rt_size.x, src_size.x), std::min(rt_size.y, src_size.y));
|
||||
|
||||
const GSVector4 sRect(0, 0, 1, 1);
|
||||
const GSVector4 dRect(0, 0, size.x, size.y);
|
||||
const GSVector4 sRect(0.0f, 0.0f, static_cast<float>(copy_size.x) / static_cast<float>(src_size.x), static_cast<float>(copy_size.y) / static_cast<float>(src_size.y));
|
||||
const GSVector4 dRect(0, 0, copy_size.x, copy_size.y);
|
||||
|
||||
g_gs_device->StretchRect(src->m_texture, sRect, rt, dRect, true, true, true, false);
|
||||
|
||||
@@ -4772,8 +4744,6 @@ bool GSRendererHW::OI_SuperManReturns(GSTexture* rt, GSTexture* ds, GSTextureCac
|
||||
ASSERT((v->RGBAQ.A << 24 | v->RGBAQ.B << 16 | v->RGBAQ.G << 8 | v->RGBAQ.R) == (int)v->XYZ.Z);
|
||||
|
||||
// Do a direct write
|
||||
if (rt)
|
||||
rt->Commit(); // Don't bother to save few MB for a single game
|
||||
g_gs_device->ClearRenderTarget(rt, GSVector4(m_vt.m_min.c));
|
||||
|
||||
m_tc->InvalidateVideoMemType(GSTextureCache::DepthStencil, ctx->FRAME.Block());
|
||||
@@ -4810,8 +4780,6 @@ bool GSRendererHW::OI_ArTonelico2(GSTexture* rt, GSTexture* ds, GSTextureCache::
|
||||
if (m_vertex.next == 2 && !PRIM->TME && m_context->FRAME.FBW == 10 && v->XYZ.Z == 0 && m_context->TEST.ZTST == ZTST_ALWAYS)
|
||||
{
|
||||
GL_INS("OI_ArTonelico2");
|
||||
if (ds)
|
||||
ds->Commit(); // Don't bother to save few MB for a single game
|
||||
g_gs_device->ClearDepth(ds);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,6 @@ public:
|
||||
static constexpr int MAX_FRAMEBUFFER_HEIGHT = 1280;
|
||||
|
||||
private:
|
||||
int m_width;
|
||||
int m_height;
|
||||
|
||||
static constexpr float SSR_UV_TOLERANCE = 1.0f;
|
||||
|
||||
#pragma region hacks
|
||||
@@ -43,7 +40,7 @@ private:
|
||||
// Require special argument
|
||||
bool OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* t, const GSVector4i& r_draw);
|
||||
void OI_GsMemClear(); // always on
|
||||
void OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds); // always on
|
||||
void OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCache::Target*& ds); // always on
|
||||
|
||||
bool OI_BigMuthaTruckers(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
bool OI_DBZBTGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
@@ -108,16 +105,13 @@ private:
|
||||
|
||||
std::list<HackEntry<OI_Ptr>> m_oi_list;
|
||||
std::list<HackEntry<OO_Ptr>> m_oo_list;
|
||||
std::list<HackEntry<CU_Ptr>> m_cu_list;
|
||||
|
||||
FunctionMap<OI_Ptr> m_oi_map;
|
||||
FunctionMap<OO_Ptr> m_oo_map;
|
||||
FunctionMap<CU_Ptr> m_cu_map;
|
||||
|
||||
public:
|
||||
OI_Ptr m_oi;
|
||||
OO_Ptr m_oo;
|
||||
CU_Ptr m_cu;
|
||||
|
||||
Hacks();
|
||||
|
||||
@@ -157,6 +151,7 @@ private:
|
||||
GSTextureCache::Source* m_src;
|
||||
|
||||
bool m_reset;
|
||||
bool m_tex_is_fb;
|
||||
bool m_channel_shuffle;
|
||||
bool m_userhacks_tcoffset;
|
||||
float m_userhacks_tcoffset_x;
|
||||
@@ -183,16 +178,15 @@ public:
|
||||
void SetGameCRC(u32 crc, int options) override;
|
||||
bool CanUpscale() override;
|
||||
int GetUpscaleMultiplier() override;
|
||||
void SetScaling();
|
||||
void Lines2Sprites();
|
||||
void EmulateAtst(GSVector4& FogColor_AREF, u8& atst, const bool pass_2);
|
||||
void ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba);
|
||||
GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
|
||||
GSVector4i ComputeBoundingBox(const GSVector2& rtscale, const GSVector2i& rtsize);
|
||||
void MergeSprite(GSTextureCache::Source* tex);
|
||||
GSVector2 GetTextureScaleFactor(const bool force_upscaling);
|
||||
GSVector2 GetTextureScaleFactor() override;
|
||||
GSVector2i GetTargetSize();
|
||||
GSVector2i GetOutputSize(int real_h);
|
||||
GSVector2i GetTargetSize(GSVector2i* unscaled_size = nullptr);
|
||||
|
||||
void Reset(bool hardware_reset) override;
|
||||
void UpdateSettings(const Pcsx2Config::GSOptions& old_config) override;
|
||||
@@ -208,8 +202,8 @@ public:
|
||||
void PurgeTextureCache() override;
|
||||
|
||||
// Called by the texture cache to know if current texture is useful
|
||||
bool IsDummyTexture() const;
|
||||
bool UpdateTexIsFB(GSTextureCache::Target* src, const GIFRegTEX0& TEX0);
|
||||
|
||||
// Called by the texture cache when optimizing the copy range for sources
|
||||
bool IsPossibleTextureShuffle(GSTextureCache::Source* src) const;
|
||||
bool IsPossibleTextureShuffle(GSTextureCache::Target* dst, const GIFRegTEX0& TEX0) const;
|
||||
};
|
||||
|
||||
@@ -83,6 +83,7 @@ void GSTextureCache::RemoveAll()
|
||||
m_hash_cache_memory_usage = 0;
|
||||
|
||||
m_palette_map.Clear();
|
||||
m_target_heights.clear();
|
||||
}
|
||||
|
||||
GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r, bool palette)
|
||||
@@ -416,7 +417,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask, const bool is_frame, const int real_h)
|
||||
{
|
||||
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
const GSVector2& new_s = g_gs_renderer->GetTextureScaleFactor();
|
||||
const GSVector2& new_s = static_cast<GSRendererHW*>(g_gs_renderer.get())->GetTextureScaleFactor();
|
||||
const u32 bp = TEX0.TBP0;
|
||||
GSVector2 res_size{ 0, 0 };
|
||||
GSVector2i new_size{ 0, 0 };
|
||||
@@ -468,6 +469,9 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
|
||||
{
|
||||
dst = t;
|
||||
GL_CACHE("TC: Lookup Frame %dx%d, perfect hit: %d (0x%x -> 0x%x %s)", size.x, size.y, dst->m_texture->GetID(), bp, t->m_end_block, psm_str(TEX0.PSM));
|
||||
if (real_h > 0)
|
||||
ScaleTargetForDisplay(dst, TEX0, real_h);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -517,8 +521,8 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
|
||||
if (new_s != old_s)
|
||||
{
|
||||
calcRescale(dst->m_texture);
|
||||
GSTexture* tex = type == RenderTarget ? g_gs_device->CreateSparseRenderTarget(new_size.x, new_size.y, GSTexture::Format::Color, clear) :
|
||||
g_gs_device->CreateSparseDepthStencil(new_size.x, new_size.y, GSTexture::Format::DepthStencil, clear);
|
||||
GSTexture* tex = type == RenderTarget ? g_gs_device->CreateRenderTarget(new_size.x, new_size.y, GSTexture::Format::Color, clear) :
|
||||
g_gs_device->CreateDepthStencil(new_size.x, new_size.y, GSTexture::Format::DepthStencil, clear);
|
||||
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
|
||||
g_gs_device->Recycle(dst->m_texture);
|
||||
tex->SetScale(new_s);
|
||||
@@ -619,7 +623,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
|
||||
return dst;
|
||||
}
|
||||
|
||||
GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_h)
|
||||
GSTextureCache::Target* GSTextureCache::LookupDisplayTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_h)
|
||||
{
|
||||
return LookupTarget(TEX0, size, RenderTarget, true, 0, true, real_h);
|
||||
}
|
||||
@@ -675,6 +679,9 @@ void GSTextureCache::ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb,
|
||||
|
||||
// We unconditionally preload the frame here, because otherwise we'll end up with blackness for one frame (when the expand happens).
|
||||
t->m_dirty.push_back(GSDirtyRect(GSVector4i(0, 0, t->m_TEX0.TBW * 64, needed_height), t->m_TEX0.PSM, t->m_TEX0.TBW));
|
||||
|
||||
// Inject the new height back into the cache.
|
||||
GetTargetHeight(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, static_cast<u32>(needed_height));
|
||||
}
|
||||
|
||||
// Goal: Depth And Target at the same address is not possible. On GS it is
|
||||
@@ -1187,6 +1194,20 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
|
||||
const int scaled_w = static_cast<int>(w * scale.x);
|
||||
const int scaled_h = static_cast<int>(h * scale.y);
|
||||
|
||||
// Expand the target when we used a more conservative size.
|
||||
const int required_dh = scaled_dy + scaled_h;
|
||||
if ((scaled_dx + scaled_w) <= dst->m_texture->GetWidth() && required_dh > dst->m_texture->GetHeight())
|
||||
{
|
||||
const int new_height = dy + h;
|
||||
if (new_height > GSRendererHW::MAX_FRAMEBUFFER_HEIGHT)
|
||||
return false;
|
||||
|
||||
const int scaled_new_height = static_cast<int>(static_cast<float>(new_height) * scale.y);
|
||||
GL_INS("Resize %dx%d target to %dx%d for move", dst->m_texture->GetWidth(), dst->m_texture->GetHeight(), dst->m_texture->GetHeight(), scaled_new_height);
|
||||
dst->ResizeTexture(dst->m_texture->GetWidth(), scaled_new_height);
|
||||
GetTargetHeight(DBP, DBW, DPSM, new_height);
|
||||
}
|
||||
|
||||
// Make sure the copy doesn't go out of bounds (it shouldn't).
|
||||
if ((scaled_sx + scaled_w) > src->m_texture->GetWidth() || (scaled_sy + scaled_h) > src->m_texture->GetHeight() ||
|
||||
(scaled_dx + scaled_w) > dst->m_texture->GetWidth() || (scaled_dy + scaled_h) > dst->m_texture->GetHeight())
|
||||
@@ -1230,6 +1251,36 @@ GSTextureCache::Target* GSTextureCache::GetTargetWithSharedBits(u32 BP, u32 PSM)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 GSTextureCache::GetTargetHeight(u32 fbp, u32 fbw, u32 psm, u32 min_height)
|
||||
{
|
||||
TargetHeightElem search = {};
|
||||
search.fbp = fbp;
|
||||
search.fbw = fbw;
|
||||
search.psm = psm;
|
||||
search.height = min_height;
|
||||
|
||||
for (auto it = m_target_heights.begin(); it != m_target_heights.end(); ++it)
|
||||
{
|
||||
TargetHeightElem& elem = const_cast<TargetHeightElem&>(*it);
|
||||
if (elem.bits == search.bits)
|
||||
{
|
||||
if (elem.height < min_height)
|
||||
{
|
||||
DbgCon.WriteLn("Expand height at %x %u %u from %u to %u", fbp, fbw, psm, elem.height, min_height);
|
||||
elem.height = min_height;
|
||||
}
|
||||
|
||||
m_target_heights.MoveFront(it.Index());
|
||||
elem.age = 0;
|
||||
return elem.height;
|
||||
}
|
||||
}
|
||||
|
||||
DbgCon.WriteLn("New height at %x %u %u: %u", fbp, fbw, psm, min_height);
|
||||
m_target_heights.push_front(search);
|
||||
return min_height;
|
||||
}
|
||||
|
||||
// Hack: remove Target that are strictly included in current rt. Typically uses for FMV
|
||||
// For example, game is rendered at 0x800->0x1000, fmv will be uploaded to 0x0->0x2800
|
||||
// FIXME In theory, we ought to report the data from the sub rt to the main rt. But let's
|
||||
@@ -1345,6 +1396,20 @@ void GSTextureCache::IncAge()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = m_target_heights.begin(); it != m_target_heights.end();)
|
||||
{
|
||||
TargetHeightElem& elem = const_cast<TargetHeightElem&>(*it);
|
||||
if (elem.age >= max_rt_age)
|
||||
{
|
||||
it = m_target_heights.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
elem.age++;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Fixme: Several issues in here. Not handling depth stencil, pitch conversion doesnt work.
|
||||
@@ -1393,7 +1458,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||
AttachPaletteToSource(src, psm.pal, true);
|
||||
}
|
||||
}
|
||||
else if (dst && GSRendererHW::GetInstance()->IsDummyTexture())
|
||||
else if (dst && GSRendererHW::GetInstance()->UpdateTexIsFB(dst, TEX0))
|
||||
{
|
||||
// This shortcut is a temporary solution. It isn't a good solution
|
||||
// as it won't work with Channel Shuffle/Texture Shuffle pattern
|
||||
@@ -1405,21 +1470,23 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||
// Be aware that you can't use StrechRect between BeginScene/EndScene.
|
||||
// So it could be tricky to put in the middle of the DrawPrims
|
||||
|
||||
// Texture is created to keep code compatibility
|
||||
GSTexture* dTex = g_gs_device->CreateTexture(tw, th, false, GSTexture::Format::Color, true);
|
||||
|
||||
// Keep a trace of origin of the texture
|
||||
src->m_texture = dTex;
|
||||
src->m_texture = dst->m_texture;
|
||||
src->m_target = true;
|
||||
src->m_shared_texture = true;
|
||||
src->m_from_target = &dst->m_texture;
|
||||
src->m_from_target_TEX0 = dst->m_TEX0;
|
||||
src->m_end_block = dst->m_end_block;
|
||||
src->m_texture->SetScale(dst->m_texture->GetScale());
|
||||
src->m_32_bits_fmt = dst->m_32_bits_fmt;
|
||||
|
||||
// Even if we sample the framebuffer directly we might need the palette
|
||||
// to handle the format conversion on GPU
|
||||
if (psm.pal > 0)
|
||||
AttachPaletteToSource(src, psm.pal, true);
|
||||
|
||||
// This will get immediately invalidated.
|
||||
m_temporary_source = src;
|
||||
}
|
||||
else if (dst)
|
||||
{
|
||||
@@ -1561,7 +1628,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||
{
|
||||
// if it looks like a texture shuffle, we might read up to +/- 8 pixels on either side.
|
||||
GSVector4 adjusted_src_range(*src_range);
|
||||
if (GSRendererHW::GetInstance()->IsPossibleTextureShuffle(src))
|
||||
if (GSRendererHW::GetInstance()->IsPossibleTextureShuffle(dst, TEX0))
|
||||
adjusted_src_range += GSVector4(-8.0f, 0.0f, 8.0f, 0.0f);
|
||||
|
||||
// don't forget to scale the copy range
|
||||
@@ -1575,13 +1642,55 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||
}
|
||||
}
|
||||
|
||||
// Don't be fooled by the name. 'dst' is the old target (hence the input)
|
||||
// 'src' is the new texture cache entry (hence the output)
|
||||
GSTexture* sTex = dst->m_texture;
|
||||
GSTexture* dTex = use_texture ?
|
||||
g_gs_device->CreateTexture(w, h, false, GSTexture::Format::Color, true) :
|
||||
g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Color, false);
|
||||
src->m_texture = dTex;
|
||||
// Assuming everything matches up, instead of copying the target, we can just sample it directly.
|
||||
// It's the same as doing the copy first, except we save GPU time.
|
||||
if (!half_right && // not the size change from above
|
||||
use_texture && // not reinterpreting the RT
|
||||
w == dst->m_texture->GetWidth() && h == dst->m_texture->GetHeight() && // same dimensions
|
||||
!m_temporary_source // not the shuffle case above
|
||||
)
|
||||
{
|
||||
// sample the target directly
|
||||
src->m_texture = dst->m_texture;
|
||||
src->m_shared_texture = true;
|
||||
src->m_target = true; // So renderer can check if a conversion is required
|
||||
src->m_from_target = &dst->m_texture; // avoid complex condition on the renderer
|
||||
src->m_from_target_TEX0 = dst->m_TEX0;
|
||||
src->m_32_bits_fmt = dst->m_32_bits_fmt;
|
||||
src->m_valid_rect = dst->m_valid;
|
||||
src->m_end_block = dst->m_end_block;
|
||||
|
||||
// kill the source afterwards, since we don't want to have to track changes to the target
|
||||
m_temporary_source = src;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't be fooled by the name. 'dst' is the old target (hence the input)
|
||||
// 'src' is the new texture cache entry (hence the output)
|
||||
GSTexture* sTex = dst->m_texture;
|
||||
GSTexture* dTex = use_texture ?
|
||||
g_gs_device->CreateTexture(w, h, false, GSTexture::Format::Color, true) :
|
||||
g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Color, false);
|
||||
src->m_texture = dTex;
|
||||
|
||||
if (use_texture)
|
||||
{
|
||||
g_gs_device->CopyRect(sTex, dTex, sRect, destX, destY);
|
||||
}
|
||||
else
|
||||
{
|
||||
GSVector4 sRectF(sRect);
|
||||
sRectF.z /= sTex->GetWidth();
|
||||
sRectF.w /= sTex->GetHeight();
|
||||
|
||||
g_gs_device->StretchRect(sTex, sRectF, dTex, GSVector4(destX, destY, w, h), shader, false);
|
||||
}
|
||||
|
||||
if (src->m_texture)
|
||||
src->m_texture->SetScale(scale);
|
||||
else
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
// GH: by default (m_paltex == 0) GS converts texture to the 32 bit format
|
||||
// However it is different here. We want to reuse a Render Target as a texture.
|
||||
@@ -1591,24 +1700,6 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||
AttachPaletteToSource(src, psm.pal, true);
|
||||
}
|
||||
|
||||
if (use_texture)
|
||||
{
|
||||
g_gs_device->CopyRect(sTex, dTex, sRect, destX, destY);
|
||||
}
|
||||
else
|
||||
{
|
||||
GSVector4 sRectF(sRect);
|
||||
sRectF.z /= sTex->GetWidth();
|
||||
sRectF.w /= sTex->GetHeight();
|
||||
|
||||
g_gs_device->StretchRect(sTex, sRectF, dTex, GSVector4(destX, destY, w, h), shader, false);
|
||||
}
|
||||
|
||||
if (src->m_texture)
|
||||
src->m_texture->SetScale(scale);
|
||||
else
|
||||
ASSERT(0);
|
||||
|
||||
// Offset hack. Can be enabled via GS options.
|
||||
// The offset will be used in Draw().
|
||||
float modxy = 0.0f;
|
||||
@@ -1813,16 +1904,16 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(const GIFRegTEX0& TEX0, int
|
||||
|
||||
if (type == RenderTarget)
|
||||
{
|
||||
t->m_texture = g_gs_device->CreateSparseRenderTarget(w, h, GSTexture::Format::Color, clear);
|
||||
t->m_texture = g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Color, clear);
|
||||
|
||||
t->m_used = true; // FIXME
|
||||
}
|
||||
else if (type == DepthStencil)
|
||||
{
|
||||
t->m_texture = g_gs_device->CreateSparseDepthStencil(w, h, GSTexture::Format::DepthStencil, clear);
|
||||
t->m_texture = g_gs_device->CreateDepthStencil(w, h, GSTexture::Format::DepthStencil, clear);
|
||||
}
|
||||
|
||||
t->m_texture->SetScale(g_gs_renderer->GetTextureScaleFactor());
|
||||
t->m_texture->SetScale(static_cast<GSRendererHW*>(g_gs_renderer.get())->GetTextureScaleFactor());
|
||||
|
||||
m_dst[type].push_front(t);
|
||||
|
||||
@@ -1999,6 +2090,47 @@ bool GSTextureCache::Surface::Overlaps(u32 bp, u32 bw, u32 psm, const GSVector4i
|
||||
return overlap;
|
||||
}
|
||||
|
||||
void GSTextureCache::Surface::ResizeTexture(int new_width, int new_height)
|
||||
{
|
||||
ResizeTexture(new_width, new_height, m_texture->GetScale());
|
||||
}
|
||||
|
||||
void GSTextureCache::Surface::ResizeTexture(int new_width, int new_height, GSVector2 new_scale)
|
||||
{
|
||||
const int width = m_texture->GetWidth();
|
||||
const int height = m_texture->GetHeight();
|
||||
if (width == new_width && height == new_height)
|
||||
return;
|
||||
|
||||
const bool clear = (new_width > width || new_height > height);
|
||||
GSTexture* tex = m_texture->IsDepthStencil() ?
|
||||
g_gs_device->CreateDepthStencil(new_width, new_height, m_texture->GetFormat(), clear) :
|
||||
g_gs_device->CreateRenderTarget(new_width, new_height, m_texture->GetFormat(), clear);
|
||||
if (!tex)
|
||||
{
|
||||
Console.Error("(GSTextureCache::Surface::ResizeTexture) Failed to allocate %dx%d texture", new_width, new_height);
|
||||
return;
|
||||
}
|
||||
|
||||
tex->SetScale(new_scale);
|
||||
|
||||
const GSVector4i rc(0, 0, std::min(width, new_width), std::min(height, new_height));
|
||||
if (tex->IsDepthStencil())
|
||||
{
|
||||
// Can't do partial copies in DirectX for depth textures, and it's probably not ideal in other
|
||||
// APIs either. So use a fullscreen quad setting depth instead.
|
||||
g_gs_device->StretchRect(m_texture, tex, GSVector4(rc), ShaderConvert::DEPTH_COPY, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fast memcpy()-like path for color targets.
|
||||
g_gs_device->CopyRect(m_texture, tex, rc, 0, 0);
|
||||
}
|
||||
|
||||
g_gs_device->Recycle(m_texture);
|
||||
m_texture = tex;
|
||||
}
|
||||
|
||||
// GSTextureCache::Source
|
||||
|
||||
GSTextureCache::Source::Source(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool dummy_container)
|
||||
@@ -2410,9 +2542,15 @@ void GSTextureCache::Target::UpdateIfDirtyIntersects(const GSVector4i& rc)
|
||||
|
||||
void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect)
|
||||
{
|
||||
m_valid = m_valid.runion(rect);
|
||||
if (m_valid.eq(GSVector4i::zero()))
|
||||
m_valid = rect;
|
||||
else
|
||||
m_valid = m_valid.runion(rect);
|
||||
|
||||
// Block of the bottom right texel of the validity rectangle, last valid block of the texture
|
||||
// TODO: This is not correct when the PSM changes. e.g. a 512x448 target being shuffled will become 512x896 temporarily, and
|
||||
// at the moment, we blow the valid rect out to twice the size. The only thing stopping everything breaking is the fact
|
||||
// that we clamp the draw rect to the target size in GSRendererHW::Draw().
|
||||
m_end_block = GSLocalMemory::m_psm[m_TEX0.PSM].info.bn(m_valid.z - 1, m_valid.w - 1, m_TEX0.TBP0, m_TEX0.TBW); // Valid only for color formats
|
||||
|
||||
// GL_CACHE("UpdateValidity (0x%x->0x%x) from R:%d,%d Valid: %d,%d", m_TEX0.TBP0, m_end_block, rect.z, rect.w, m_valid.z, m_valid.w);
|
||||
|
||||
@@ -92,6 +92,9 @@ public:
|
||||
void UpdateAge();
|
||||
bool Inside(u32 bp, u32 bw, u32 psm, const GSVector4i& rect);
|
||||
bool Overlaps(u32 bp, u32 bw, u32 psm, const GSVector4i& rect);
|
||||
|
||||
void ResizeTexture(int new_width, int new_height);
|
||||
void ResizeTexture(int new_width, int new_height, GSVector2 new_scale);
|
||||
};
|
||||
|
||||
struct PaletteKey
|
||||
@@ -243,6 +246,25 @@ public:
|
||||
void RemoveAt(Source* s);
|
||||
};
|
||||
|
||||
struct TargetHeightElem
|
||||
{
|
||||
union
|
||||
{
|
||||
u32 bits;
|
||||
|
||||
struct
|
||||
{
|
||||
u32 fbp : 9;
|
||||
u32 fbw : 6;
|
||||
u32 psm : 6;
|
||||
u32 pad : 11;
|
||||
};
|
||||
};
|
||||
|
||||
u32 height;
|
||||
u32 age;
|
||||
};
|
||||
|
||||
struct SurfaceOffsetKeyElem
|
||||
{
|
||||
u32 psm;
|
||||
@@ -278,6 +300,7 @@ protected:
|
||||
std::unordered_map<HashCacheKey, HashCacheEntry, HashCacheKeyHash> m_hash_cache;
|
||||
u64 m_hash_cache_memory_usage = 0;
|
||||
FastList<Target*> m_dst[2];
|
||||
FastList<TargetHeightElem> m_target_heights;
|
||||
static u8* m_temp;
|
||||
constexpr static size_t S_SURFACE_OFFSET_CACHE_MAX_SIZE = std::numeric_limits<u16>::max();
|
||||
std::unordered_map<SurfaceOffsetKey, SurfaceOffset, SurfaceOffsetKeyHash, SurfaceOffsetKeyEqual> m_surface_offset_cache;
|
||||
@@ -286,6 +309,10 @@ protected:
|
||||
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t = NULL, bool half_right = false, int x_offset = 0, int y_offset = 0, const GSVector2i* lod = nullptr, const GSVector4i* src_range = nullptr);
|
||||
Target* CreateTarget(const GIFRegTEX0& TEX0, int w, int h, int type, const bool clear);
|
||||
|
||||
/// Expands a target when the block pointer for a display framebuffer is within another target, but the read offset
|
||||
/// plus the height is larger than the current size of the target.
|
||||
void ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb, int real_h);
|
||||
|
||||
HashCacheEntry* LookupHashCache(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool& paltex, const u32* clut, const GSVector2i* lod);
|
||||
|
||||
static void PreloadTexture(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GSLocalMemory& mem, bool paltex, GSTexture* tex, u32 level);
|
||||
@@ -309,12 +336,14 @@ public:
|
||||
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r, bool palette = false);
|
||||
|
||||
Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask = 0, const bool is_frame = false, const int real_h = 0);
|
||||
Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_h);
|
||||
Target* LookupDisplayTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_h);
|
||||
|
||||
/// Looks up a target in the cache, and only returns it if the BP/BW/PSM match exactly.
|
||||
Target* GetExactTarget(u32 BP, u32 BW, u32 PSM) const;
|
||||
Target* GetTargetWithSharedBits(u32 BP, u32 PSM) const;
|
||||
|
||||
u32 GetTargetHeight(u32 fbp, u32 fbw, u32 psm, u32 min_height);
|
||||
|
||||
void InvalidateVideoMemType(int type, u32 bp);
|
||||
void InvalidateVideoMemSubTarget(GSTextureCache::Target* rt);
|
||||
void InvalidateVideoMem(const GSOffset& off, const GSVector4i& r, bool target = true);
|
||||
@@ -335,10 +364,6 @@ public:
|
||||
SurfaceOffset ComputeSurfaceOffset(const uint32_t bp, const uint32_t bw, const uint32_t psm, const GSVector4i& r, const Target* t);
|
||||
SurfaceOffset ComputeSurfaceOffset(const SurfaceOffsetKey& sok);
|
||||
|
||||
/// Expands a target when the block pointer for a display framebuffer is within another target, but the read offset
|
||||
/// plus the height is larger than the current size of the target.
|
||||
static void ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb, int real_h);
|
||||
|
||||
/// Invalidates a temporary source, a partial copy only created from the current RT/DS for the current draw.
|
||||
void InvalidateTemporarySource();
|
||||
|
||||
|
||||
@@ -402,7 +402,6 @@ GSTexture* GSDeviceMTL::CreateSurface(GSTexture::Type type, int width, int heigh
|
||||
[desc setUsage:MTLTextureUsageRenderTarget];
|
||||
break;
|
||||
case GSTexture::Type::RenderTarget:
|
||||
case GSTexture::Type::SparseRenderTarget:
|
||||
if (m_dev.features.slow_color_compression)
|
||||
[desc setUsage:MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget | MTLTextureUsagePixelFormatView]; // Force color compression off by including PixelFormatView
|
||||
else
|
||||
|
||||
@@ -721,10 +721,17 @@ struct PSMain
|
||||
return selector == 0 ? zero : selector == 1 ? one : two;
|
||||
}
|
||||
|
||||
void ps_blend(thread float4& Color, float As)
|
||||
void ps_blend(thread float4& Color, thread float& As)
|
||||
{
|
||||
if (SW_BLEND)
|
||||
{
|
||||
// PABE
|
||||
if (PS_PABE)
|
||||
{
|
||||
// No blending so early exit
|
||||
if (As < 1.f)
|
||||
return;
|
||||
}
|
||||
|
||||
float Ad = PS_DFMT == FMT_24 ? 1.f : trunc(current_color.a * 255.5f) / 128.f;
|
||||
|
||||
@@ -736,16 +743,39 @@ struct PSMain
|
||||
float C = pick(PS_BLEND_C, As, Ad, cb.alpha_fix);
|
||||
float3 D = pick(PS_BLEND_D, Cs, Cd, float3(0.f));
|
||||
|
||||
if (PS_BLEND_MIX)
|
||||
// As/Af clamp alpha for Blend mix
|
||||
// We shouldn't clamp blend mix with clr1 as we want alpha higher
|
||||
if (PS_BLEND_MIX && PS_CLR_HW != 1)
|
||||
C = min(C, 1.f);
|
||||
|
||||
if (PS_BLEND_A == PS_BLEND_B)
|
||||
Color.rgb = D;
|
||||
else if (PS_BLEND_MIX)
|
||||
// In blend_mix, HW adds on some alpha factor * dst.
|
||||
// Truncating here wouldn't quite get the right result because it prevents the <1 bit here from combining with a <1 bit in dst to form a ≥1 amount that pushes over the truncation.
|
||||
// Instead, apply an offset to convert HW's round to a floor.
|
||||
// Since alpha is in 1/128 increments, subtracting (0.5 - 0.5/128 == 127/256) would get us what we want if GPUs blended in full precision.
|
||||
// But they don't. Details here: https://github.com/PCSX2/pcsx2/pull/6809#issuecomment-1211473399
|
||||
// Based on the scripts at the above link, the ideal choice for Intel GPUs is 126/256, AMD 120/256. Nvidia is a lost cause.
|
||||
// 124/256 seems like a reasonable compromise, providing the correct answer 99.3% of the time on Intel (vs 99.6% for 126/256), and 97% of the time on AMD (vs 97.4% for 120/256).
|
||||
Color.rgb = ((A - B) * C + D) - (124.f/256.f);
|
||||
else
|
||||
Color.rgb = trunc((A - B) * C + D);
|
||||
|
||||
if (PS_PABE)
|
||||
Color.rgb = (As >= 1.f) ? Color.rgb : Cs;
|
||||
if (PS_CLR_HW == 1)
|
||||
{
|
||||
// Replace Af with As so we can do proper compensation for Alpha.
|
||||
if (PS_BLEND_C == 2)
|
||||
As = cb.alpha_fix;
|
||||
// Subtract 1 for alpha to compensate for the changed equation,
|
||||
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
|
||||
// we pick the lowest overflow from all colors because it's the safest,
|
||||
// we divide by 255 the color because we don't know Cd value,
|
||||
// changed alpha should only be done for hw blend.
|
||||
float min_color = min(min(Color.r, Color.g), Color.b);
|
||||
float alpha_compensate = max(1.f, min_color / 255.f);
|
||||
As -= alpha_compensate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Emulate_DSA
|
||||
// Replace function pointer to emulate DSA behavior
|
||||
void Init()
|
||||
{
|
||||
fprintf(stderr, "DSA is not supported. Expect slower performance\n");
|
||||
Console.Warning("DSA is not supported. Expect slower performance");
|
||||
glBindTextureUnit = BindTextureUnit;
|
||||
glCreateTextures = CreateTexture;
|
||||
glTextureStorage2D = TextureStorage;
|
||||
@@ -139,15 +139,6 @@ namespace Emulate_DSA
|
||||
|
||||
namespace GLLoader
|
||||
{
|
||||
|
||||
#define fprintf_once(out, ...) \
|
||||
do \
|
||||
if (s_first_load) \
|
||||
fprintf(out, __VA_ARGS__); \
|
||||
while (0);
|
||||
|
||||
bool s_first_load = true;
|
||||
|
||||
bool vendor_id_amd = false;
|
||||
bool vendor_id_nvidia = false;
|
||||
bool vendor_id_intel = false;
|
||||
@@ -162,10 +153,6 @@ namespace GLLoader
|
||||
bool found_GL_ARB_gpu_shader5 = false; // Require IvyBridge
|
||||
bool found_GL_ARB_shader_image_load_store = false; // Intel IB. Nvidia/AMD miss Mesa implementation.
|
||||
|
||||
// In case sparse2 isn't supported
|
||||
bool found_compatible_GL_ARB_sparse_texture2 = false;
|
||||
bool found_compatible_sparse_depth = false;
|
||||
|
||||
static bool mandatory(const std::string& ext)
|
||||
{
|
||||
if (!GLExtension::Has(ext))
|
||||
@@ -183,11 +170,11 @@ namespace GLLoader
|
||||
|
||||
if (!found)
|
||||
{
|
||||
fprintf_once(stdout, "INFO: %s is NOT SUPPORTED\n", name.c_str());
|
||||
DevCon.Warning("INFO: %s is NOT SUPPORTED", name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf_once(stdout, "INFO: %s is available\n", name.c_str());
|
||||
DevCon.WriteLn("INFO: %s is available", name.c_str());
|
||||
}
|
||||
|
||||
std::string opt("override_");
|
||||
@@ -276,9 +263,6 @@ namespace GLLoader
|
||||
|
||||
// Extra
|
||||
{
|
||||
// Bonus
|
||||
optional("GL_ARB_sparse_texture");
|
||||
optional("GL_ARB_sparse_texture2");
|
||||
// GL4.0
|
||||
found_GL_ARB_gpu_shader5 = optional("GL_ARB_gpu_shader5");
|
||||
// GL4.2
|
||||
@@ -300,32 +284,17 @@ namespace GLLoader
|
||||
}
|
||||
}
|
||||
|
||||
if (vendor_id_amd)
|
||||
{
|
||||
fprintf_once(stderr, "The OpenGL hardware renderer is slow on AMD GPUs due to an inefficient driver.\n"
|
||||
"Check out the link below for further information.\n"
|
||||
"https://github.com/PCSX2/pcsx2/wiki/OpenGL-and-AMD-GPUs---All-you-need-to-know\n");
|
||||
}
|
||||
|
||||
if (vendor_id_intel && (!GLExtension::Has("GL_ARB_texture_barrier") || !GLExtension::Has("GL_ARB_direct_state_access")))
|
||||
{
|
||||
// Assume that driver support is good when texture barrier and DSA is supported, disable the log then.
|
||||
fprintf_once(stderr, "The OpenGL renderer is inefficient on Intel GPUs due to an inefficient driver.\n"
|
||||
"Check out the link below for further information.\n"
|
||||
"https://github.com/PCSX2/pcsx2/wiki/OpenGL-and-Intel-GPUs-All-you-need-to-know\n");
|
||||
}
|
||||
|
||||
if (!GLExtension::Has("GL_ARB_viewport_array"))
|
||||
{
|
||||
glScissorIndexed = ReplaceGL::ScissorIndexed;
|
||||
glViewportIndexedf = ReplaceGL::ViewportIndexedf;
|
||||
fprintf_once(stderr, "GL_ARB_viewport_array is not supported! Function pointer will be replaced\n");
|
||||
Console.Warning("GL_ARB_viewport_array is not supported! Function pointer will be replaced");
|
||||
}
|
||||
|
||||
if (!GLExtension::Has("GL_ARB_texture_barrier"))
|
||||
{
|
||||
glTextureBarrier = ReplaceGL::TextureBarrier;
|
||||
fprintf_once(stderr, "GL_ARB_texture_barrier is not supported! Blending emulation will not be supported\n");
|
||||
Console.Warning("GL_ARB_texture_barrier is not supported! Blending emulation will not be supported");
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -339,70 +308,6 @@ namespace GLLoader
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_sparse2_compatible(const char* name, GLenum internal_fmt, int x_max, int y_max)
|
||||
{
|
||||
GLint index_count = 0;
|
||||
glGetInternalformativ(GL_TEXTURE_2D, internal_fmt, GL_NUM_VIRTUAL_PAGE_SIZES_ARB, 1, &index_count);
|
||||
if (!index_count)
|
||||
{
|
||||
fprintf_once(stdout, "%s isn't sparse compatible. No index found\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
GLint x, y;
|
||||
glGetInternalformativ(GL_TEXTURE_2D, internal_fmt, GL_VIRTUAL_PAGE_SIZE_X_ARB, 1, &x);
|
||||
glGetInternalformativ(GL_TEXTURE_2D, internal_fmt, GL_VIRTUAL_PAGE_SIZE_Y_ARB, 1, &y);
|
||||
if (x > x_max && y > y_max)
|
||||
{
|
||||
fprintf_once(stdout, "%s isn't sparse compatible. Page size (%d,%d) is too big (%d, %d)\n",
|
||||
name, x, y, x_max, y_max);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void check_sparse_compatibility()
|
||||
{
|
||||
if (!GLExtension::Has("GL_ARB_sparse_texture") ||
|
||||
!GLExtension::Has("GL_EXT_direct_state_access") ||
|
||||
theApp.GetConfigI("override_GL_ARB_sparse_texture") != 1)
|
||||
{
|
||||
found_compatible_GL_ARB_sparse_texture2 = false;
|
||||
found_compatible_sparse_depth = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
found_compatible_GL_ARB_sparse_texture2 = true;
|
||||
if (!GLExtension::Has("GL_ARB_sparse_texture2"))
|
||||
{
|
||||
// Only check format from GSTextureOGL
|
||||
found_compatible_GL_ARB_sparse_texture2 &= is_sparse2_compatible("GL_R8", GL_R8, 256, 256);
|
||||
|
||||
found_compatible_GL_ARB_sparse_texture2 &= is_sparse2_compatible("GL_R16UI", GL_R16UI, 256, 128);
|
||||
|
||||
found_compatible_GL_ARB_sparse_texture2 &= is_sparse2_compatible("GL_R32UI", GL_R32UI, 128, 128);
|
||||
found_compatible_GL_ARB_sparse_texture2 &= is_sparse2_compatible("GL_R32I", GL_R32I, 128, 128);
|
||||
found_compatible_GL_ARB_sparse_texture2 &= is_sparse2_compatible("GL_RGBA8", GL_RGBA8, 128, 128);
|
||||
|
||||
found_compatible_GL_ARB_sparse_texture2 &= is_sparse2_compatible("GL_RGBA16", GL_RGBA16, 128, 64);
|
||||
found_compatible_GL_ARB_sparse_texture2 &= is_sparse2_compatible("GL_RGBA16I", GL_RGBA16I, 128, 64);
|
||||
found_compatible_GL_ARB_sparse_texture2 &= is_sparse2_compatible("GL_RGBA16UI", GL_RGBA16UI, 128, 64);
|
||||
found_compatible_GL_ARB_sparse_texture2 &= is_sparse2_compatible("GL_RGBA16F", GL_RGBA16F, 128, 64);
|
||||
|
||||
found_compatible_GL_ARB_sparse_texture2 &= is_sparse2_compatible("GL_RGBA32F", GL_RGBA32F, 64, 64);
|
||||
}
|
||||
|
||||
// Can fit in 128x64 but 128x128 is enough
|
||||
// Disable sparse depth for AMD. Bad driver strikes again.
|
||||
// driver reports a compatible sparse format for depth texture but it isn't attachable to a frame buffer.
|
||||
found_compatible_sparse_depth = !vendor_id_amd && is_sparse2_compatible("GL_DEPTH32F_STENCIL8", GL_DEPTH32F_STENCIL8, 128, 128);
|
||||
|
||||
fprintf_once(stdout, "INFO: sparse color texture is %s\n", found_compatible_GL_ARB_sparse_texture2 ? "available" : "NOT SUPPORTED");
|
||||
fprintf_once(stdout, "INFO: sparse depth texture is %s\n", found_compatible_sparse_depth ? "available" : "NOT SUPPORTED");
|
||||
}
|
||||
|
||||
bool check_gl_requirements()
|
||||
{
|
||||
if (!check_gl_version(3, 3))
|
||||
@@ -411,12 +316,6 @@ namespace GLLoader
|
||||
if (!check_gl_supported_extension())
|
||||
return false;
|
||||
|
||||
// Bonus for sparse texture
|
||||
check_sparse_compatibility();
|
||||
|
||||
fprintf_once(stdout, "\n");
|
||||
|
||||
s_first_load = false;
|
||||
return true;
|
||||
}
|
||||
} // namespace GLLoader
|
||||
|
||||
@@ -45,7 +45,4 @@ namespace GLLoader
|
||||
extern bool found_GL_ARB_gpu_shader5;
|
||||
extern bool found_GL_ARB_shader_image_load_store;
|
||||
extern bool found_GL_ARB_clear_texture;
|
||||
|
||||
extern bool found_compatible_GL_ARB_sparse_texture2;
|
||||
extern bool found_compatible_sparse_depth;
|
||||
} // namespace GLLoader
|
||||
|
||||
@@ -774,7 +774,7 @@ void GSDeviceOGL::InvalidateRenderTarget(GSTexture* t)
|
||||
{
|
||||
OMSetFBO(m_fbo);
|
||||
|
||||
if (T->GetType() == GSTexture::Type::DepthStencil || T->GetType() == GSTexture::Type::SparseDepthStencil)
|
||||
if (T->GetType() == GSTexture::Type::DepthStencil)
|
||||
{
|
||||
OMAttachDs(T);
|
||||
const GLenum attachments[] = {GL_DEPTH_STENCIL_ATTACHMENT};
|
||||
@@ -1192,7 +1192,6 @@ void GSDeviceOGL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r
|
||||
PSSetShaderResource(6, sTex);
|
||||
#endif
|
||||
|
||||
dTex->CommitRegion(GSVector2i(r.z, r.w));
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
ASSERT(GLExtension::Has("GL_ARB_copy_image") && glCopyImageSubData);
|
||||
@@ -1242,7 +1241,6 @@ void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
||||
BeginScene();
|
||||
|
||||
GL_PUSH("StretchRect from %d to %d", sTex->GetID(), dTex->GetID());
|
||||
dTex->CommitRegion(GSVector2i((int)dRect.z + 1, (int)dRect.w + 1));
|
||||
if (draw_in_depth)
|
||||
OMSetRenderTargets(NULL, dTex);
|
||||
else
|
||||
@@ -1885,7 +1883,6 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
{
|
||||
GSVector2i size = config.rt->GetSize();
|
||||
hdr_rt = CreateRenderTarget(size.x, size.y, GSTexture::Format::FloatColor, false);
|
||||
hdr_rt->CommitRegion(GSVector2i(config.drawarea.z, config.drawarea.w));
|
||||
OMSetRenderTargets(hdr_rt, config.ds, &config.scissor);
|
||||
|
||||
// save blend state, since BlitRect destroys it
|
||||
|
||||
@@ -377,9 +377,6 @@ public:
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = NULL);
|
||||
void OMSetColorMaskState(OMColorMaskSelector sel = OMColorMaskSelector());
|
||||
|
||||
bool HasColorSparse() final { return GLLoader::found_compatible_GL_ARB_sparse_texture2; }
|
||||
bool HasDepthSparse() final { return GLLoader::found_compatible_sparse_depth; }
|
||||
|
||||
bool CreateTextureFX();
|
||||
std::string GetShaderSource(const std::string_view& entry, GLenum type, const std::string_view& common_header, const std::string_view& glsl_h_code, const std::string_view& macro_sel);
|
||||
std::string GenGlslHeader(const std::string_view& entry, GLenum type, const std::string_view& macro);
|
||||
|
||||
@@ -181,7 +181,6 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
||||
m_type = type;
|
||||
m_fbo_read = fbo_read;
|
||||
m_texture_id = 0;
|
||||
m_sparse = false;
|
||||
m_mipmap_levels = 1;
|
||||
int gl_fmt = 0;
|
||||
|
||||
@@ -287,56 +286,9 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case Type::Texture:
|
||||
// Only 32 bits input texture will be supported for mipmap
|
||||
m_mipmap_levels = levels;
|
||||
break;
|
||||
case Type::SparseRenderTarget:
|
||||
case Type::SparseDepthStencil:
|
||||
m_sparse = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_format)
|
||||
{
|
||||
case Format::UInt16:
|
||||
case Format::UNorm8:
|
||||
m_sparse &= GLLoader::found_compatible_GL_ARB_sparse_texture2;
|
||||
SetGpuPageSize(GSVector2i(255, 255));
|
||||
break;
|
||||
|
||||
case Format::Color:
|
||||
case Format::UInt32:
|
||||
case Format::PrimID:
|
||||
m_sparse &= GLLoader::found_compatible_GL_ARB_sparse_texture2;
|
||||
SetGpuPageSize(GSVector2i(127, 127));
|
||||
break;
|
||||
|
||||
case Format::FloatColor:
|
||||
m_sparse &= GLLoader::found_compatible_GL_ARB_sparse_texture2;
|
||||
SetGpuPageSize(GSVector2i(63, 63));
|
||||
break;
|
||||
|
||||
case Format::DepthStencil:
|
||||
m_sparse &= GLLoader::found_compatible_sparse_depth;
|
||||
SetGpuPageSize(GSVector2i(127, 127));
|
||||
break;
|
||||
|
||||
case GSTexture::Format::BC1:
|
||||
case GSTexture::Format::BC2:
|
||||
case GSTexture::Format::BC3:
|
||||
case GSTexture::Format::BC7:
|
||||
m_sparse = false;
|
||||
SetGpuPageSize(GSVector2i(127, 127));
|
||||
break;
|
||||
|
||||
case Format::Invalid:
|
||||
ASSERT(0);
|
||||
}
|
||||
// Only 32 bits input texture will be supported for mipmap
|
||||
if (m_type == Type::Texture)
|
||||
m_mipmap_levels = levels;
|
||||
|
||||
// Create a gl object (texture isn't allocated here)
|
||||
glCreateTextures(GL_TEXTURE_2D, 1, &m_texture_id);
|
||||
@@ -347,26 +299,8 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
||||
glTextureParameteri(m_texture_id, GL_TEXTURE_SWIZZLE_A, GL_RED);
|
||||
}
|
||||
|
||||
if (m_sparse)
|
||||
{
|
||||
GSVector2i old_size = m_size;
|
||||
m_size = RoundUpPage(m_size);
|
||||
if (m_size != old_size)
|
||||
{
|
||||
fprintf(stderr, "Sparse texture size (%dx%d) isn't a multiple of gpu page size (%dx%d)\n",
|
||||
old_size.x, old_size.y, m_gpu_page_size.x, m_gpu_page_size.y);
|
||||
}
|
||||
glTextureParameteri(m_texture_id, GL_TEXTURE_SPARSE_ARB, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_committed_size = m_size;
|
||||
}
|
||||
|
||||
m_mem_usage = (m_committed_size.x * m_committed_size.y) << m_int_shift;
|
||||
|
||||
static int every_512 = 0;
|
||||
GLState::available_vram -= m_mem_usage;
|
||||
GLState::available_vram -= GetMemUsage();
|
||||
if ((GLState::available_vram < 0) && (every_512 % 512 == 0))
|
||||
{
|
||||
fprintf(stderr, "Available VRAM is very low (%lld), a crash is expected! Enable conservative buffer allocation or reduce upscaling!\n", GLState::available_vram);
|
||||
@@ -394,7 +328,7 @@ GSTextureOGL::~GSTextureOGL()
|
||||
|
||||
glDeleteTextures(1, &m_texture_id);
|
||||
|
||||
GLState::available_vram += m_mem_usage;
|
||||
GLState::available_vram += GetMemUsage();
|
||||
}
|
||||
|
||||
void* GSTextureOGL::GetNativeHandle() const
|
||||
@@ -560,44 +494,6 @@ void GSTextureOGL::GenerateMipmap()
|
||||
glGenerateTextureMipmap(m_texture_id);
|
||||
}
|
||||
|
||||
void GSTextureOGL::CommitPages(const GSVector2i& region, bool commit)
|
||||
{
|
||||
GLState::available_vram += m_mem_usage;
|
||||
|
||||
if (commit)
|
||||
{
|
||||
if (m_committed_size.x == 0)
|
||||
{
|
||||
// Nothing allocated so far
|
||||
GL_INS("CommitPages initial %dx%d of %u", region.x, region.y, m_texture_id);
|
||||
glTexturePageCommitmentEXT(m_texture_id, GL_TEX_LEVEL_0, 0, 0, 0, region.x, region.y, 1, commit);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL_INS("CommitPages extend %dx%d to %dx%d of %u", m_committed_size.x, m_committed_size.y, region.x, region.y, m_texture_id);
|
||||
int w = region.x - m_committed_size.x;
|
||||
int h = region.y - m_committed_size.y;
|
||||
// Extend width
|
||||
glTexturePageCommitmentEXT(m_texture_id, GL_TEX_LEVEL_0, m_committed_size.x, 0, 0, w, m_committed_size.y, 1, commit);
|
||||
// Extend height
|
||||
glTexturePageCommitmentEXT(m_texture_id, GL_TEX_LEVEL_0, 0, m_committed_size.y, 0, region.x, h, 1, commit);
|
||||
}
|
||||
m_committed_size = region;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Release everything
|
||||
GL_INS("CommitPages release of %u", m_texture_id);
|
||||
|
||||
glTexturePageCommitmentEXT(m_texture_id, GL_TEX_LEVEL_0, 0, 0, 0, m_committed_size.x, m_committed_size.y, 1, commit);
|
||||
|
||||
m_committed_size = GSVector2i(0, 0);
|
||||
}
|
||||
|
||||
m_mem_usage = (m_committed_size.x * m_committed_size.y) << m_int_shift;
|
||||
GLState::available_vram -= m_mem_usage;
|
||||
}
|
||||
|
||||
GSTexture::GSMap GSTextureOGL::Read(const GSVector4i& r, AlignedBuffer<u8, 32>& buffer)
|
||||
{
|
||||
GSMap m;
|
||||
@@ -625,8 +521,8 @@ GSTexture::GSMap GSTextureOGL::Read(const GSVector4i& r, AlignedBuffer<u8, 32>&
|
||||
bool GSTextureOGL::Save(const std::string& fn)
|
||||
{
|
||||
// Collect the texture data
|
||||
u32 pitch = 4 * m_committed_size.x;
|
||||
u32 buf_size = pitch * m_committed_size.y * 2; // Note *2 for security (depth/stencil)
|
||||
u32 pitch = 4 * m_size.x;
|
||||
u32 buf_size = pitch * m_size.y * 2; // Note *2 for security (depth/stencil)
|
||||
std::unique_ptr<u8[]> image(new u8[buf_size]);
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
GSPng::Format fmt = GSPng::RGB_A_PNG;
|
||||
@@ -634,12 +530,12 @@ bool GSTextureOGL::Save(const std::string& fn)
|
||||
GSPng::Format fmt = GSPng::RGB_PNG;
|
||||
#endif
|
||||
|
||||
if (IsDepth())
|
||||
if (IsDepthStencil())
|
||||
{
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
||||
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_texture_id, 0);
|
||||
glReadPixels(0, 0, m_committed_size.x, m_committed_size.y, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, image.get());
|
||||
glReadPixels(0, 0, m_size.x, m_size.y, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, image.get());
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
@@ -648,7 +544,6 @@ bool GSTextureOGL::Save(const std::string& fn)
|
||||
else if (m_format == Format::PrimID)
|
||||
{
|
||||
// Note: 4.5 function used for accurate DATE
|
||||
// barely used outside of dev and not sparse anyway
|
||||
glGetTextureImage(m_texture_id, 0, GL_RED_INTEGER, GL_INT, buf_size, image.get());
|
||||
|
||||
fmt = GSPng::R32I_PNG;
|
||||
@@ -661,24 +556,24 @@ bool GSTextureOGL::Save(const std::string& fn)
|
||||
|
||||
if (m_format == Format::Color)
|
||||
{
|
||||
glReadPixels(0, 0, m_committed_size.x, m_committed_size.y, GL_RGBA, GL_UNSIGNED_BYTE, image.get());
|
||||
glReadPixels(0, 0, m_size.x, m_size.y, GL_RGBA, GL_UNSIGNED_BYTE, image.get());
|
||||
}
|
||||
else if (m_format == Format::UInt16)
|
||||
{
|
||||
glReadPixels(0, 0, m_committed_size.x, m_committed_size.y, GL_RED_INTEGER, GL_UNSIGNED_SHORT, image.get());
|
||||
glReadPixels(0, 0, m_size.x, m_size.y, GL_RED_INTEGER, GL_UNSIGNED_SHORT, image.get());
|
||||
fmt = GSPng::R16I_PNG;
|
||||
}
|
||||
else if (m_format == Format::UNorm8)
|
||||
{
|
||||
fmt = GSPng::R8I_PNG;
|
||||
glReadPixels(0, 0, m_committed_size.x, m_committed_size.y, GL_RED, GL_UNSIGNED_BYTE, image.get());
|
||||
glReadPixels(0, 0, m_size.x, m_size.y, GL_RED, GL_UNSIGNED_BYTE, image.get());
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
int compression = theApp.GetConfigI("png_compression_level");
|
||||
return GSPng::Save(fmt, fn, image.get(), m_committed_size.x, m_committed_size.y, pitch, compression);
|
||||
return GSPng::Save(fmt, fn, image.get(), m_size.x, m_size.y, pitch, compression);
|
||||
}
|
||||
|
||||
void GSTextureOGL::Swap(GSTexture* tex)
|
||||
@@ -696,10 +591,4 @@ void GSTextureOGL::Swap(GSTexture* tex)
|
||||
std::swap(m_int_format, static_cast<GSTextureOGL*>(tex)->m_int_format);
|
||||
std::swap(m_int_type, static_cast<GSTextureOGL*>(tex)->m_int_type);
|
||||
std::swap(m_int_shift, static_cast<GSTextureOGL*>(tex)->m_int_shift);
|
||||
std::swap(m_mem_usage, static_cast<GSTextureOGL*>(tex)->m_mem_usage);
|
||||
}
|
||||
|
||||
u32 GSTextureOGL::GetMemUsage()
|
||||
{
|
||||
return m_mem_usage;
|
||||
}
|
||||
|
||||
@@ -54,9 +54,6 @@ private:
|
||||
GLenum m_int_type;
|
||||
u32 m_int_shift;
|
||||
|
||||
// Allow to track size of allocated memory
|
||||
u32 m_mem_usage;
|
||||
|
||||
public:
|
||||
explicit GSTextureOGL(Type type, int width, int height, int levels, Format format, GLuint fbo_read);
|
||||
virtual ~GSTextureOGL();
|
||||
@@ -71,7 +68,6 @@ public:
|
||||
void Swap(GSTexture* tex) final;
|
||||
|
||||
GSMap Read(const GSVector4i& r, AlignedBuffer<u8, 32>& buffer);
|
||||
bool IsDepth() { return (m_type == Type::DepthStencil || m_type == Type::SparseDepthStencil); }
|
||||
bool IsIntegerFormat() const
|
||||
{
|
||||
return (m_int_format == GL_RED_INTEGER || m_int_format == GL_RGBA_INTEGER);
|
||||
@@ -88,8 +84,4 @@ public:
|
||||
|
||||
void Clear(const void* data);
|
||||
void Clear(const void* data, const GSVector4i& area);
|
||||
|
||||
void CommitPages(const GSVector2i& region, bool commit) final;
|
||||
|
||||
u32 GetMemUsage() final;
|
||||
};
|
||||
|
||||
@@ -405,8 +405,7 @@ VkFormat GSDeviceVK::LookupNativeFormat(GSTexture::Format format) const
|
||||
|
||||
GSTexture* GSDeviceVK::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
|
||||
{
|
||||
pxAssert(type != GSTexture::Type::Offscreen && type != GSTexture::Type::SparseRenderTarget &&
|
||||
type != GSTexture::Type::SparseDepthStencil);
|
||||
pxAssert(type != GSTexture::Type::Offscreen);
|
||||
|
||||
const u32 clamped_width = static_cast<u32>(std::clamp<int>(1, width, g_vulkan_context->GetMaxImageDimension2D()));
|
||||
const u32 clamped_height = static_cast<u32>(std::clamp<int>(1, height, g_vulkan_context->GetMaxImageDimension2D()));
|
||||
@@ -508,25 +507,38 @@ void GSDeviceVK::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if (dTexVK->GetState() == GSTexture::State::Cleared)
|
||||
{
|
||||
// otherwise we need to do an attachment clear
|
||||
const bool depth = (dTexVK->GetType() == GSTexture::Type::DepthStencil);
|
||||
OMSetRenderTargets(depth ? nullptr : dTexVK, depth ? dTexVK : nullptr, dtex_rc, false);
|
||||
BeginRenderPassForStretchRect(dTexVK, dtex_rc, GSVector4i(destX, destY, destX + r.width(), destY + r.height()));
|
||||
|
||||
// so use an attachment clear
|
||||
VkClearAttachment ca;
|
||||
ca.aspectMask = depth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
GSVector4::store<false>(ca.clearValue.color.float32, sTexVK->GetClearColor());
|
||||
ca.clearValue.depthStencil.depth = sTexVK->GetClearDepth();
|
||||
ca.clearValue.depthStencil.stencil = 0;
|
||||
ca.colorAttachment = 0;
|
||||
|
||||
const VkClearRect cr = { {{0, 0}, {static_cast<u32>(r.width()), static_cast<u32>(r.height())}}, 0u, 1u };
|
||||
vkCmdClearAttachments(g_vulkan_context->GetCurrentCommandBuffer(), 1, &ca, 1, &cr);
|
||||
return;
|
||||
// destination is cleared, if it's the same colour and rect, we can just avoid this entirely
|
||||
if (dTexVK->IsDepthStencil())
|
||||
{
|
||||
if (dTexVK->GetClearDepth() == sTexVK->GetClearDepth())
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((dTexVK->GetClearColor() == (sTexVK->GetClearColor())).alltrue())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise we need to do an attachment clear
|
||||
const bool depth = (dTexVK->GetType() == GSTexture::Type::DepthStencil);
|
||||
OMSetRenderTargets(depth ? nullptr : dTexVK, depth ? dTexVK : nullptr, dtex_rc, false);
|
||||
BeginRenderPassForStretchRect(dTexVK, dtex_rc, GSVector4i(destX, destY, destX + r.width(), destY + r.height()));
|
||||
|
||||
// so use an attachment clear
|
||||
VkClearAttachment ca;
|
||||
ca.aspectMask = depth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
GSVector4::store<false>(ca.clearValue.color.float32, sTexVK->GetClearColor());
|
||||
ca.clearValue.depthStencil.depth = sTexVK->GetClearDepth();
|
||||
ca.clearValue.depthStencil.stencil = 0;
|
||||
ca.colorAttachment = 0;
|
||||
|
||||
const VkClearRect cr = { {{0, 0}, {static_cast<u32>(r.width()), static_cast<u32>(r.height())}}, 0u, 1u };
|
||||
vkCmdClearAttachments(g_vulkan_context->GetCurrentCommandBuffer(), 1, &ca, 1, &cr);
|
||||
return;
|
||||
}
|
||||
|
||||
// commit the clear to the source first, then do normal copy
|
||||
@@ -1957,7 +1969,6 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector
|
||||
AddMacro(ss, "PS_CHANNEL_FETCH", sel.channel);
|
||||
AddMacro(ss, "PS_URBAN_CHAOS_HLE", sel.urban_chaos_hle);
|
||||
AddMacro(ss, "PS_TALES_OF_ABYSS_HLE", sel.tales_of_abyss_hle);
|
||||
AddMacro(ss, "PS_TEX_IS_FB", sel.tex_is_fb);
|
||||
AddMacro(ss, "PS_INVALID_TEX0", sel.invalid_tex0);
|
||||
AddMacro(ss, "PS_AEM", sel.aem);
|
||||
AddMacro(ss, "PS_TFX", sel.tfx);
|
||||
@@ -2980,22 +2991,32 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
}
|
||||
|
||||
if (config.tex && config.tex == config.ds)
|
||||
if (config.tex)
|
||||
{
|
||||
// requires a copy of the depth buffer. this is mainly for ico.
|
||||
copy_ds = static_cast<GSTextureVK*>(CreateDepthStencil(rtsize.x, rtsize.y, GSTexture::Format::DepthStencil, false));
|
||||
if (copy_ds)
|
||||
if (config.tex == config.ds)
|
||||
{
|
||||
EndRenderPass();
|
||||
// requires a copy of the depth buffer. this is mainly for ico.
|
||||
copy_ds = static_cast<GSTextureVK*>(CreateDepthStencil(rtsize.x, rtsize.y, GSTexture::Format::DepthStencil, false));
|
||||
if (copy_ds)
|
||||
{
|
||||
EndRenderPass();
|
||||
|
||||
GL_PUSH("Copy depth to temp texture for shuffle {%d,%d %dx%d}",
|
||||
config.drawarea.left, config.drawarea.top,
|
||||
config.drawarea.width(), config.drawarea.height());
|
||||
GL_PUSH("Copy depth to temp texture for shuffle {%d,%d %dx%d}",
|
||||
config.drawarea.left, config.drawarea.top,
|
||||
config.drawarea.width(), config.drawarea.height());
|
||||
|
||||
CopyRect(config.ds, copy_ds, config.drawarea, config.drawarea.left, config.drawarea.top);
|
||||
PSSetShaderResource(0, copy_ds, true);
|
||||
CopyRect(config.ds, copy_ds, config.drawarea, config.drawarea.left, config.drawarea.top);
|
||||
PSSetShaderResource(0, copy_ds, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// clear texture binding when it's bound to RT or DS
|
||||
if (!config.tex && m_tfx_textures[0] &&
|
||||
((!pipe.feedback_loop && config.rt && static_cast<GSTextureVK*>(config.rt)->GetView() == m_tfx_textures[0]) ||
|
||||
(config.ds && static_cast<GSTextureVK*>(config.ds)->GetView() == m_tfx_textures[0])))
|
||||
{
|
||||
PSSetShaderResource(0, nullptr, false);
|
||||
}
|
||||
|
||||
const bool render_area_okay =
|
||||
(!hdr_rt && DATE_rp != DATE_RENDER_PASS_STENCIL_ONE && CheckRenderPassArea(render_area));
|
||||
|
||||
@@ -172,9 +172,6 @@ const char* dialog_message(int ID, bool* updateText)
|
||||
return cvtString("Allows advanced atomic operations to speed up Accurate DATE.\n"
|
||||
"Only disable this if using Accurate DATE causes (GPU driver) issues.\n\n"
|
||||
"Note: This option is only supported by GPUs which support at least Direct3D 11.");
|
||||
case IDC_SPARSE_TEXTURE:
|
||||
return cvtString("Allows to reduce VRAM usage on the GPU.\n\n"
|
||||
"Note: Feature is currently experimental and works only on Nvidia GPUs.");
|
||||
case IDC_LINEAR_PRESENT:
|
||||
return cvtString("Use bilinear filtering when Upscaling/Downscaling the image to the screen. Disable it if you want a sharper/pixelated output.");
|
||||
// Exclusive for Hardware Renderer
|
||||
@@ -191,12 +188,6 @@ const char* dialog_message(int ID, bool* updateText)
|
||||
case IDC_DISABLE_PARTIAL_TC_INV:
|
||||
return cvtString("By default, the texture cache handles partial invalidations. Unfortunately it is very costly to compute CPU wise."
|
||||
"\n\nThis hack replaces the partial invalidation with a complete deletion of the texture to reduce the CPU load.\n\nIt helps snowblind engine games.");
|
||||
case IDC_CONSERVATIVE_FB:
|
||||
return cvtString("Disabled: Reserves a larger framebuffer to prevent FMV flickers.\n"
|
||||
"Increases GPU/memory requirements.\n"
|
||||
"Disabling this can amplify stuttering due to low RAM/VRAM.\n\n"
|
||||
"Note: It should be enabled for Armored Core, Destroy All Humans, Gran Turismo and possibly others.\n"
|
||||
"This option does not improve the graphics or the FPS.");
|
||||
case IDC_DITHERING:
|
||||
return cvtString("In the PS2's case, it reduces banding between colors and improves the perceived color depth.\n"
|
||||
"In the PS1's case, it was used more aggressively due to 16-bit colour.\n"
|
||||
|
||||
@@ -50,7 +50,6 @@ enum
|
||||
IDC_PRELOAD_TEXTURES,
|
||||
IDC_ACCURATE_DATE,
|
||||
IDC_PALTEX,
|
||||
IDC_CONSERVATIVE_FB,
|
||||
IDC_AFCOMBO,
|
||||
IDC_DITHERING,
|
||||
IDC_MIPMAP_HW,
|
||||
@@ -90,7 +89,6 @@ enum
|
||||
// OpenGL Advanced Settings
|
||||
IDC_GEOMETRY_SHADER_OVERRIDE,
|
||||
IDC_IMAGE_LOAD_STORE,
|
||||
IDC_SPARSE_TEXTURE,
|
||||
// On-screen Display
|
||||
IDC_OSD_LOG,
|
||||
IDC_OSD_MONITOR,
|
||||
|
||||
@@ -275,7 +275,6 @@ RendererTab::RendererTab(wxWindow* parent)
|
||||
const int space = wxSizerFlags().Border().GetBorderInPixels();
|
||||
auto hw_prereq = [this]{ return m_is_hardware; };
|
||||
auto sw_prereq = [this]{ return !m_is_hardware; };
|
||||
auto upscale_prereq = [this]{ return !m_is_native_res; };
|
||||
|
||||
PaddedBoxSizer<wxBoxSizer> tab_box(wxVERTICAL);
|
||||
PaddedBoxSizer<wxStaticBoxSizer> general_box(wxVERTICAL, this, "General GS Settings");
|
||||
@@ -285,7 +284,6 @@ RendererTab::RendererTab(wxWindow* parent)
|
||||
auto* hw_checks_box = new wxWrapSizer(wxHORIZONTAL);
|
||||
|
||||
m_ui.addCheckBox(hw_checks_box, "Accurate Destination Alpha Test", "accurate_date", IDC_ACCURATE_DATE, hw_prereq);
|
||||
m_ui.addCheckBox(hw_checks_box, "Conservative Buffer Allocation", "conservative_framebuffer", IDC_CONSERVATIVE_FB, upscale_prereq);
|
||||
|
||||
auto* paltex_prereq = m_ui.addCheckBox(hw_checks_box, "GPU Palette Conversion", "paltex", IDC_PALTEX, hw_prereq);
|
||||
auto aniso_prereq = [this, paltex_prereq]{ return m_is_hardware && paltex_prereq->GetValue() == false; };
|
||||
@@ -589,7 +587,6 @@ DebugTab::DebugTab(wxWindow* parent)
|
||||
m_ui.addComboBoxAndLabel(ogl_grid, "Texture Barriers:", "OverrideTextureBarriers", &theApp.m_gs_generic_list, -1, vk_ogl_hw_prereq);
|
||||
m_ui.addComboBoxAndLabel(ogl_grid, "Geometry Shader:", "OverrideGeometryShaders", &theApp.m_gs_generic_list, IDC_GEOMETRY_SHADER_OVERRIDE, vk_ogl_hw_prereq);
|
||||
m_ui.addComboBoxAndLabel(ogl_grid, "Image Load Store:", "override_GL_ARB_shader_image_load_store", &theApp.m_gs_generic_list, IDC_IMAGE_LOAD_STORE, ogl_hw_prereq);
|
||||
m_ui.addComboBoxAndLabel(ogl_grid, "Sparse Texture:", "override_GL_ARB_sparse_texture", &theApp.m_gs_generic_list, IDC_SPARSE_TEXTURE, ogl_hw_prereq);
|
||||
m_ui.addComboBoxAndLabel(ogl_grid, "Dump Compression:", "GSDumpCompression", &theApp.m_gs_dump_compression, -1);
|
||||
ogl_box->Add(ogl_grid);
|
||||
|
||||
@@ -804,7 +801,6 @@ void Dialog::Update()
|
||||
m_hacks_panel->m_is_native_res = !is_hw || !is_upscale;
|
||||
m_hacks_panel->m_is_hardware = is_hw;
|
||||
m_renderer_panel->m_is_hardware = is_hw;
|
||||
m_renderer_panel->m_is_native_res = !is_hw || !is_upscale;
|
||||
m_post_panel->m_is_vk_hw = renderer == GSRendererType::VK;
|
||||
m_debug_panel->m_is_ogl_hw = renderer == GSRendererType::OGL;
|
||||
m_debug_panel->m_is_vk_hw = renderer == GSRendererType::VK;
|
||||
|
||||
@@ -106,7 +106,6 @@ namespace GSSettingsDialog
|
||||
GSUIElementHolder m_ui;
|
||||
wxChoice* m_internal_resolution;
|
||||
bool m_is_hardware = false;
|
||||
bool m_is_native_res = false;
|
||||
|
||||
RendererTab(wxWindow* parent);
|
||||
void Load() { m_ui.Load(); }
|
||||
|
||||
@@ -270,7 +270,6 @@ void GameDatabase::parseAndInsert(const std::string_view& serial, const c4::yml:
|
||||
|
||||
static const char* s_gs_hw_fix_names[] = {
|
||||
"autoFlush",
|
||||
"conservativeFramebuffer",
|
||||
"cpuFramebufferConversion",
|
||||
"disableDepthSupport",
|
||||
"wrapGSMem",
|
||||
@@ -317,7 +316,6 @@ bool GameDatabaseSchema::isUserHackHWFix(GSHWFixId id)
|
||||
case GSHWFixId::Deinterlace:
|
||||
case GSHWFixId::Mipmap:
|
||||
case GSHWFixId::TexturePreloading:
|
||||
case GSHWFixId::ConservativeFramebuffer:
|
||||
case GSHWFixId::PointListPalette:
|
||||
return false;
|
||||
|
||||
@@ -446,9 +444,6 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
|
||||
case GSHWFixId::AutoFlush:
|
||||
return (static_cast<int>(config.UserHacks_AutoFlush) == value);
|
||||
|
||||
case GSHWFixId::ConservativeFramebuffer:
|
||||
return (static_cast<int>(config.ConservativeFramebuffer) == value);
|
||||
|
||||
case GSHWFixId::CPUFramebufferConversion:
|
||||
return (static_cast<int>(config.UserHacks_CPUFBConversion) == value);
|
||||
|
||||
@@ -542,10 +537,6 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
|
||||
config.UserHacks_AutoFlush = (value > 0);
|
||||
break;
|
||||
|
||||
case GSHWFixId::ConservativeFramebuffer:
|
||||
config.ConservativeFramebuffer = (value > 0);
|
||||
break;
|
||||
|
||||
case GSHWFixId::CPUFramebufferConversion:
|
||||
config.UserHacks_CPUFBConversion = (value > 0);
|
||||
break;
|
||||
|
||||
@@ -60,7 +60,6 @@ namespace GameDatabaseSchema
|
||||
{
|
||||
// boolean settings
|
||||
AutoFlush,
|
||||
ConservativeFramebuffer,
|
||||
CPUFramebufferConversion,
|
||||
DisableDepthSupport,
|
||||
WrapGSMem,
|
||||
|
||||
@@ -416,7 +416,8 @@ void VU_Thread::Get_MTVUChanges()
|
||||
{
|
||||
mtvuInterrupts.fetch_and(~InterruptFlagVUEBit, std::memory_order_relaxed);
|
||||
|
||||
VU0.VI[REG_VPU_STAT].UL &= ~0xFF00;
|
||||
if(INSTANT_VU1)
|
||||
VU0.VI[REG_VPU_STAT].UL &= ~0xFF00;
|
||||
//DevCon.Warning("E-Bit registered %x", VU0.VI[REG_VPU_STAT].UL);
|
||||
}
|
||||
if (interrupts & InterruptFlagVUTBit)
|
||||
@@ -458,10 +459,17 @@ void VU_Thread::ExecuteVU(u32 vu_addr, u32 vif_top, u32 vif_itop, u32 fbrst)
|
||||
CommitWritePos();
|
||||
gifUnit.TransferGSPacketData(GIF_TRANS_MTVU, NULL, 0);
|
||||
KickStart();
|
||||
u32 cycles = std::min(Get_vuCycles(), 3000u);
|
||||
cpuRegs.cycle += cycles * EmuConfig.Speedhacks.EECycleSkip;
|
||||
VU0.cycle += cycles * EmuConfig.Speedhacks.EECycleSkip;
|
||||
u32 cycles = std::max(Get_vuCycles(), 4u);
|
||||
u32 skip_cycles = std::min(cycles, 3000u);
|
||||
cpuRegs.cycle += skip_cycles * EmuConfig.Speedhacks.EECycleSkip;
|
||||
VU0.cycle += skip_cycles * EmuConfig.Speedhacks.EECycleSkip;
|
||||
Get_MTVUChanges();
|
||||
|
||||
if (!INSTANT_VU1)
|
||||
{
|
||||
VU0.VI[REG_VPU_STAT].UL |= 0x100;
|
||||
CPU_INT(VU_MTVU_BUSY, cycles);
|
||||
}
|
||||
}
|
||||
|
||||
void VU_Thread::VifUnpack(vifStruct& _vif, VIFregisters& _vifRegs, u8* data, u32 size)
|
||||
|
||||
@@ -133,7 +133,7 @@ void KeyStatus::Set(u32 pad, u32 index, float value)
|
||||
if (index == PAD_PRESSURE)
|
||||
{
|
||||
const float adjust_pmod = ((m_button[pad] & (1u << PAD_PRESSURE)) == 0) ? m_pressure_modifier[pad] : (1.0f / m_pressure_modifier[pad]);
|
||||
for (int i = 0; i < MAX_KEYS; i++)
|
||||
for (u32 i = 0; i < MAX_KEYS; i++)
|
||||
{
|
||||
if (i == index || IsAnalogKey(i) || IsTriggerKey(i))
|
||||
continue;
|
||||
|
||||
@@ -183,6 +183,62 @@ u8 PADpoll(u8 value)
|
||||
return pad_poll(value);
|
||||
}
|
||||
|
||||
const char* PAD::ControllerSettingInfo::StringDefaultValue() const
|
||||
{
|
||||
return default_value ? default_value : "";
|
||||
}
|
||||
|
||||
bool PAD::ControllerSettingInfo::BooleanDefaultValue() const
|
||||
{
|
||||
return default_value ? StringUtil::FromChars<bool>(default_value).value_or(false) : false;
|
||||
}
|
||||
|
||||
s32 PAD::ControllerSettingInfo::IntegerDefaultValue() const
|
||||
{
|
||||
return default_value ? StringUtil::FromChars<s32>(default_value).value_or(0) : 0;
|
||||
}
|
||||
|
||||
s32 PAD::ControllerSettingInfo::IntegerMinValue() const
|
||||
{
|
||||
static constexpr s32 fallback_value = std::numeric_limits<s32>::min();
|
||||
return min_value ? StringUtil::FromChars<s32>(min_value).value_or(fallback_value) : fallback_value;
|
||||
}
|
||||
|
||||
s32 PAD::ControllerSettingInfo::IntegerMaxValue() const
|
||||
{
|
||||
static constexpr s32 fallback_value = std::numeric_limits<s32>::max();
|
||||
return max_value ? StringUtil::FromChars<s32>(max_value).value_or(fallback_value) : fallback_value;
|
||||
}
|
||||
|
||||
s32 PAD::ControllerSettingInfo::IntegerStepValue() const
|
||||
{
|
||||
static constexpr s32 fallback_value = 1;
|
||||
return step_value ? StringUtil::FromChars<s32>(step_value).value_or(fallback_value) : fallback_value;
|
||||
}
|
||||
|
||||
float PAD::ControllerSettingInfo::FloatDefaultValue() const
|
||||
{
|
||||
return default_value ? StringUtil::FromChars<float>(default_value).value_or(0.0f) : 0.0f;
|
||||
}
|
||||
|
||||
float PAD::ControllerSettingInfo::FloatMinValue() const
|
||||
{
|
||||
static constexpr float fallback_value = std::numeric_limits<float>::min();
|
||||
return min_value ? StringUtil::FromChars<float>(min_value).value_or(fallback_value) : fallback_value;
|
||||
}
|
||||
|
||||
float PAD::ControllerSettingInfo::FloatMaxValue() const
|
||||
{
|
||||
static constexpr float fallback_value = std::numeric_limits<float>::max();
|
||||
return max_value ? StringUtil::FromChars<float>(max_value).value_or(fallback_value) : fallback_value;
|
||||
}
|
||||
|
||||
float PAD::ControllerSettingInfo::FloatStepValue() const
|
||||
{
|
||||
static constexpr float fallback_value = 0.1f;
|
||||
return step_value ? StringUtil::FromChars<float>(step_value).value_or(fallback_value) : fallback_value;
|
||||
}
|
||||
|
||||
std::string PAD::GetConfigSection(u32 pad_index)
|
||||
{
|
||||
return fmt::format("Pad{}", pad_index + 1);
|
||||
@@ -350,9 +406,31 @@ static const PAD::ControllerBindingInfo s_dualshock2_binds[] = {
|
||||
{"SmallMotor", "Small (High Frequency) Motor", PAD::ControllerBindingType::Motor, GenericInputBinding::SmallMotor},
|
||||
};
|
||||
|
||||
static const PAD::ControllerSettingInfo s_dualshock2_settings[] = {
|
||||
{PAD::ControllerSettingInfo::Type::Float, "Deadzone", "Analog Deadzone",
|
||||
"Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored.",
|
||||
"0.00", "0.00", "1.00", "0.01"},
|
||||
{PAD::ControllerSettingInfo::Type::Float, "AxisScale", "Analog Sensitivity",
|
||||
"Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent "
|
||||
"controllers, e.g. DualShock 4, Xbox One Controller.",
|
||||
"1.33", "0.01", "2.00", "0.01"},
|
||||
{PAD::ControllerSettingInfo::Type::Float, "LargeMotorScale", "Large Motor Vibration Scale",
|
||||
"Increases or decreases the intensity of low frequency vibration sent by the game.",
|
||||
"1.00", "0.00", "2.00", "0.01"},
|
||||
{PAD::ControllerSettingInfo::Type::Float, "SmallMotorScale", "Small Motor Vibration Scale",
|
||||
"Increases or decreases the intensity of high frequency vibration sent by the game.",
|
||||
"1.00", "0.00", "2.00", "0.01"},
|
||||
};
|
||||
|
||||
static const PAD::ControllerInfo s_controller_info[] = {
|
||||
{"None", "Not Connected", nullptr, 0, PAD::ControllerType::NotConnected, PAD::VibrationCapabilities::NoVibration},
|
||||
{"DualShock2", "DualShock 2", s_dualshock2_binds, std::size(s_dualshock2_binds), PAD::ControllerType::DualShock2, PAD::VibrationCapabilities::LargeSmallMotors},
|
||||
{PAD::ControllerType::NotConnected, "None", "Not Connected",
|
||||
nullptr, 0,
|
||||
nullptr, 0,
|
||||
PAD::VibrationCapabilities::NoVibration},
|
||||
{PAD::ControllerType::DualShock2, "DualShock2", "DualShock 2",
|
||||
s_dualshock2_binds, std::size(s_dualshock2_binds),
|
||||
s_dualshock2_settings, std::size(s_dualshock2_settings),
|
||||
PAD::VibrationCapabilities::LargeSmallMotors},
|
||||
};
|
||||
|
||||
const PAD::ControllerInfo* PAD::GetControllerInfo(ControllerType type)
|
||||
|
||||
@@ -71,13 +71,48 @@ namespace PAD
|
||||
GenericInputBinding generic_mapping;
|
||||
};
|
||||
|
||||
struct ControllerSettingInfo
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
Boolean,
|
||||
Integer,
|
||||
Float,
|
||||
String,
|
||||
Path,
|
||||
};
|
||||
|
||||
Type type;
|
||||
const char* key;
|
||||
const char* visible_name;
|
||||
const char* description;
|
||||
const char* default_value;
|
||||
const char* min_value;
|
||||
const char* max_value;
|
||||
const char* step_value;
|
||||
|
||||
const char* StringDefaultValue() const;
|
||||
bool BooleanDefaultValue() const;
|
||||
s32 IntegerDefaultValue() const;
|
||||
s32 IntegerMinValue() const;
|
||||
s32 IntegerMaxValue() const;
|
||||
s32 IntegerStepValue() const;
|
||||
float FloatDefaultValue() const;
|
||||
float FloatMinValue() const;
|
||||
float FloatMaxValue() const;
|
||||
float FloatStepValue() const;
|
||||
};
|
||||
|
||||
|
||||
struct ControllerInfo
|
||||
{
|
||||
ControllerType type;
|
||||
const char* name;
|
||||
const char* display_name;
|
||||
const ControllerBindingInfo* bindings;
|
||||
u32 num_bindings;
|
||||
ControllerType type;
|
||||
const ControllerSettingInfo* settings;
|
||||
u32 num_settings;
|
||||
PAD::VibrationCapabilities vibration_caps;
|
||||
};
|
||||
|
||||
|
||||
@@ -325,7 +325,6 @@ Pcsx2Config::GSOptions::GSOptions()
|
||||
HWDisableReadbacks = false;
|
||||
AccurateDATE = true;
|
||||
GPUPaletteConversion = false;
|
||||
ConservativeFramebuffer = true;
|
||||
AutoFlushSW = true;
|
||||
PreloadFrameWithGSData = false;
|
||||
WrapGSMem = false;
|
||||
@@ -546,7 +545,6 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
|
||||
GSSettingBool(HWDisableReadbacks);
|
||||
GSSettingBoolEx(AccurateDATE, "accurate_date");
|
||||
GSSettingBoolEx(GPUPaletteConversion, "paltex");
|
||||
GSSettingBoolEx(ConservativeFramebuffer, "conservative_framebuffer");
|
||||
GSSettingBoolEx(AutoFlushSW, "autoflush_sw");
|
||||
GSSettingBoolEx(PreloadFrameWithGSData, "preload_frame_with_gs_data");
|
||||
GSSettingBoolEx(WrapGSMem, "wrap_gs_mem");
|
||||
|
||||
@@ -246,6 +246,18 @@ __fi void cpuSetNextEventDelta( s32 delta )
|
||||
cpuSetNextEvent( cpuRegs.cycle, delta );
|
||||
}
|
||||
|
||||
__fi int cpuGetCycles(int interrupt)
|
||||
{
|
||||
if(interrupt == VU_MTVU_BUSY && (!THREAD_VU1 || INSTANT_VU1))
|
||||
return 1;
|
||||
else
|
||||
{
|
||||
const int cycles = (cpuRegs.sCycle[interrupt] + cpuRegs.eCycle[interrupt]) - cpuRegs.cycle;
|
||||
return std::max(1, cycles);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// tests the cpu cycle against the given start and delta values.
|
||||
// Returns true if the delta time has passed.
|
||||
__fi int cpuTestCycle( u32 startCycle, s32 delta )
|
||||
@@ -292,7 +304,7 @@ static __fi void _cpuTestInterrupts()
|
||||
}
|
||||
/* These are 'pcsx2 interrupts', they handle asynchronous stuff
|
||||
that depends on the cycle timings */
|
||||
|
||||
TESTINT(VU_MTVU_BUSY, MTVUInterrupt);
|
||||
TESTINT(DMAC_VIF1, vif1Interrupt);
|
||||
TESTINT(DMAC_GIF, gifInterrupt);
|
||||
TESTINT(DMAC_SIF0, EEsif0Interrupt);
|
||||
@@ -521,7 +533,7 @@ __fi void CPU_INT( EE_EventType n, s32 ecycle)
|
||||
// EE events happen 8 cycles in the future instead of whatever was requested.
|
||||
// This can be used on games with PATH3 masking issues for example, or when
|
||||
// some FMV look bad.
|
||||
if(CHECK_EETIMINGHACK) ecycle = 8;
|
||||
if(CHECK_EETIMINGHACK && n < VIF_VU0_FINISH) ecycle = 8;
|
||||
|
||||
cpuRegs.interrupt|= 1 << n;
|
||||
cpuRegs.sCycle[n] = cpuRegs.cycle;
|
||||
|
||||
@@ -415,7 +415,8 @@ enum EE_EventType
|
||||
DMAC_GIF_UNIT,
|
||||
VIF_VU0_FINISH,
|
||||
VIF_VU1_FINISH,
|
||||
IPU_PROCESS
|
||||
IPU_PROCESS,
|
||||
VU_MTVU_BUSY
|
||||
};
|
||||
|
||||
extern void CPU_INT( EE_EventType n, s32 ecycle );
|
||||
@@ -435,6 +436,7 @@ extern void cpuSetNextEvent( u32 startCycle, s32 delta );
|
||||
extern void cpuSetNextEventDelta( s32 delta );
|
||||
extern int cpuTestCycle( u32 startCycle, s32 delta );
|
||||
extern void cpuSetEvent();
|
||||
extern int cpuGetCycles(int interrupt);
|
||||
|
||||
extern void _cpuEventTest_Shared(); // for internal use by the Dynarecs and Ints inside R5900:
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ void ReadSettings()
|
||||
// Let's use xaudio2 until this is sorted (rama).
|
||||
|
||||
// CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, PortaudioOut->GetIdent());
|
||||
CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, StringUtil::UTF8StringToWideString(XAudio2Out->GetIdent()).c_str());
|
||||
CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, StringUtil::UTF8StringToWideString(CubebOut->GetIdent()).c_str());
|
||||
|
||||
// Find the driver index of this module:
|
||||
OutputModule = FindOutputModuleById(StringUtil::WideStringToUTF8String(omodid).c_str());
|
||||
|
||||
@@ -38,7 +38,10 @@ void vu1ResetRegs()
|
||||
void vu1Finish(bool add_cycles) {
|
||||
if (THREAD_VU1) {
|
||||
//if (VU0.VI[REG_VPU_STAT].UL & 0x100) DevCon.Error("MTVU: VU0.VI[REG_VPU_STAT].UL & 0x100");
|
||||
vu1Thread.WaitVU();
|
||||
if (INSTANT_VU1 || add_cycles)
|
||||
{
|
||||
vu1Thread.WaitVU();
|
||||
}
|
||||
vu1Thread.Get_MTVUChanges();
|
||||
return;
|
||||
}
|
||||
@@ -70,7 +73,6 @@ void vu1ExecMicro(u32 addr)
|
||||
// VU0.VI[REG_VPU_STAT].UL |= 0x0100;
|
||||
// }
|
||||
// Update 25/06/2022: Disabled this for now, let games YOLO it, if it breaks MTVU, disable MTVU (it doesn't work properly anyway) - Refraction
|
||||
|
||||
vu1Thread.ExecuteVU(addr, vif1Regs.top, vif1Regs.itop, VU0.VI[REG_FBRST].UL);
|
||||
return;
|
||||
}
|
||||
@@ -90,3 +92,8 @@ void vu1ExecMicro(u32 addr)
|
||||
else
|
||||
CpuVU1->Execute(vu1RunCycles);
|
||||
}
|
||||
|
||||
void MTVUInterrupt()
|
||||
{
|
||||
VU0.VI[REG_VPU_STAT].UL &= ~0xFF00;
|
||||
}
|
||||
|
||||
@@ -256,6 +256,7 @@ extern void vu1ResetRegs();
|
||||
extern void vu1ExecMicro(u32 addr);
|
||||
extern void vu1Exec(VURegs* VU);
|
||||
extern void iDumpVU1Registers();
|
||||
extern void MTVUInterrupt();
|
||||
|
||||
#ifdef VUM_LOG
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ void vif1TransferToMemory()
|
||||
u128* pMem = (u128*)dmaGetAddr(vif1ch.madr, false);
|
||||
|
||||
// VIF from gsMemory
|
||||
if (pMem == NULL) { // Is vif0ptag empty?
|
||||
if (pMem == NULL)
|
||||
{ // Is vif0ptag empty?
|
||||
Console.WriteLn("Vif1 Tag BUSERR");
|
||||
dmacRegs.stat.BEIS = true; // Bus Error
|
||||
vif1Regs.stat.FQC = 0;
|
||||
@@ -55,10 +56,11 @@ void vif1TransferToMemory()
|
||||
// stuff from the GS. The *only* way to handle this case safely is to flush the GS
|
||||
// completely and execute the transfer there-after.
|
||||
//Console.Warning("Real QWC %x", vif1ch.qwc);
|
||||
const u32 size = std::min(vif1.GSLastDownloadSize, (u32)vif1ch.qwc);
|
||||
const u32 size = std::min(vif1.GSLastDownloadSize, (u32)vif1ch.qwc);
|
||||
//const u128* pMemEnd = vif1.GSLastDownloadSize + pMem;
|
||||
|
||||
if (size) {
|
||||
if (size)
|
||||
{
|
||||
// Checking if any crazy game does a partial
|
||||
// gs primitive and then does a gs download...
|
||||
Gif_Path& p1 = gifUnit.gifPath[GIF_PATH_1];
|
||||
@@ -70,7 +72,7 @@ void vif1TransferToMemory()
|
||||
}
|
||||
|
||||
GetMTGS().InitAndReadFIFO(reinterpret_cast<u8*>(pMem), size);
|
||||
// pMem += size;
|
||||
// pMem += size;
|
||||
|
||||
//Some games such as Alex Ferguson's Player Manager 2001 reads less than GSLastDownloadSize by VIF then reads the remainder by FIFO
|
||||
//Clearing the memory is clearing memory it shouldn't be and kills it.
|
||||
@@ -89,25 +91,25 @@ void vif1TransferToMemory()
|
||||
|
||||
g_vif1Cycles += size * 2;
|
||||
vif1ch.madr += size * 16; // mgs3 scene changes
|
||||
if (vif1.GSLastDownloadSize >= vif1ch.qwc) {
|
||||
if (vif1.GSLastDownloadSize >= vif1ch.qwc)
|
||||
{
|
||||
vif1.GSLastDownloadSize -= vif1ch.qwc;
|
||||
vif1Regs.stat.FQC = std::min((u32)16, vif1.GSLastDownloadSize);
|
||||
vif1ch.qwc = 0;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
vif1Regs.stat.FQC = 0;
|
||||
vif1ch.qwc -= vif1.GSLastDownloadSize;
|
||||
vif1.GSLastDownloadSize = 0;
|
||||
//This could be potentially bad and cause hangs. I guess we will find out.
|
||||
DevCon.Warning("QWC left on VIF FIFO Reverse");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool _VIF1chain()
|
||||
{
|
||||
u32 *pMem;
|
||||
u32* pMem;
|
||||
|
||||
if (vif1ch.qwc == 0)
|
||||
{
|
||||
@@ -135,7 +137,7 @@ bool _VIF1chain()
|
||||
}
|
||||
|
||||
VIF_LOG("VIF1chain size=%d, madr=%lx, tadr=%lx",
|
||||
vif1ch.qwc, vif1ch.madr, vif1ch.tadr);
|
||||
vif1ch.qwc, vif1ch.madr, vif1ch.tadr);
|
||||
|
||||
if (vif1.irqoffset.enabled)
|
||||
return VIF1transfer(pMem + vif1.irqoffset.value, vif1ch.qwc * 4 - vif1.irqoffset.value, false);
|
||||
@@ -145,20 +147,21 @@ bool _VIF1chain()
|
||||
|
||||
__fi void vif1SetupTransfer()
|
||||
{
|
||||
tDMA_TAG *ptag;
|
||||
|
||||
tDMA_TAG* ptag;
|
||||
|
||||
ptag = dmaGetAddr(vif1ch.tadr, false); //Set memory pointer to TADR
|
||||
|
||||
if (!(vif1ch.transfer("Vif1 Tag", ptag))) return;
|
||||
if (!(vif1ch.transfer("Vif1 Tag", ptag)))
|
||||
return;
|
||||
|
||||
vif1ch.madr = ptag[1]._u32; //MADR = ADDR field + SPR
|
||||
vif1ch.madr = ptag[1]._u32; //MADR = ADDR field + SPR
|
||||
g_vif1Cycles += 1; // Add 1 g_vifCycles from the QW read for the tag
|
||||
vif1.inprogress &= ~1;
|
||||
|
||||
VIF_LOG("VIF1 Tag %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx",
|
||||
ptag[1]._u32, ptag[0]._u32, vif1ch.qwc, ptag->ID, vif1ch.madr, vif1ch.tadr);
|
||||
ptag[1]._u32, ptag[0]._u32, vif1ch.qwc, ptag->ID, vif1ch.madr, vif1ch.tadr);
|
||||
|
||||
if (!vif1.done && ((dmacRegs.ctrl.STD == STD_VIF1) && (ptag->ID == TAG_REFS))) // STD == VIF1
|
||||
if (!vif1.done && ((dmacRegs.ctrl.STD == STD_VIF1) && (ptag->ID == TAG_REFS))) // STD == VIF1
|
||||
{
|
||||
// there are still bugs, need to also check if gif->madr +16*qwc >= stadr, if not, stall
|
||||
if ((vif1ch.madr + vif1ch.qwc * 16) > dmacRegs.stadr.ADDR)
|
||||
@@ -177,7 +180,7 @@ __fi void vif1SetupTransfer()
|
||||
bool ret;
|
||||
|
||||
alignas(16) static u128 masked_tag;
|
||||
|
||||
|
||||
masked_tag._u64[0] = 0;
|
||||
masked_tag._u64[1] = *((u64*)ptag + 1);
|
||||
|
||||
@@ -185,7 +188,7 @@ __fi void vif1SetupTransfer()
|
||||
|
||||
if (vif1.irqoffset.enabled)
|
||||
{
|
||||
ret = VIF1transfer((u32*)&masked_tag + vif1.irqoffset.value, 4 - vif1.irqoffset.value, true); //Transfer Tag on stall
|
||||
ret = VIF1transfer((u32*)&masked_tag + vif1.irqoffset.value, 4 - vif1.irqoffset.value, true); //Transfer Tag on stall
|
||||
//ret = VIF1transfer((u32*)ptag + (2 + vif1.irqoffset), 2 - vif1.irqoffset); //Transfer Tag on stall
|
||||
}
|
||||
else
|
||||
@@ -194,7 +197,7 @@ __fi void vif1SetupTransfer()
|
||||
// to the VU's, which breaks stuff, this is where the 128bit packet will fail, so we ignore the first 2 words
|
||||
vif1.irqoffset.value = 2;
|
||||
vif1.irqoffset.enabled = true;
|
||||
ret = VIF1transfer((u32*)&masked_tag + 2, 2, true); //Transfer Tag
|
||||
ret = VIF1transfer((u32*)&masked_tag + 2, 2, true); //Transfer Tag
|
||||
//ret = VIF1transfer((u32*)ptag + 2, 2); //Transfer Tag
|
||||
}
|
||||
|
||||
@@ -202,7 +205,7 @@ __fi void vif1SetupTransfer()
|
||||
{
|
||||
vif1.inprogress &= ~1; // Better clear this so it has to do it again (Jak 1)
|
||||
vif1ch.qwc = 0; // Gumball 3000 pauses the DMA when the tag stalls so we need to reset the QWC, it'll be gotten again later
|
||||
return; // IRQ set by VIFTransfer
|
||||
return; // IRQ set by VIFTransfer
|
||||
}
|
||||
}
|
||||
vif1.irqoffset.value = 0;
|
||||
@@ -210,14 +213,15 @@ __fi void vif1SetupTransfer()
|
||||
|
||||
vif1.done |= hwDmacSrcChainWithStack(vif1ch, ptag->ID);
|
||||
|
||||
if(vif1ch.qwc > 0) vif1.inprogress |= 1;
|
||||
if (vif1ch.qwc > 0)
|
||||
vif1.inprogress |= 1;
|
||||
|
||||
//Check TIE bit of CHCR and IRQ bit of tag
|
||||
if (vif1ch.chcr.TIE && ptag->IRQ)
|
||||
{
|
||||
VIF_LOG("dmaIrq Set");
|
||||
|
||||
//End Transfer
|
||||
//End Transfer
|
||||
vif1.done = true;
|
||||
return;
|
||||
}
|
||||
@@ -229,23 +233,29 @@ __fi void vif1VUFinish()
|
||||
{
|
||||
vu1Thread.Get_MTVUChanges();
|
||||
|
||||
CPU_INT(VIF_VU1_FINISH, 128);
|
||||
if (THREAD_VU1 && !INSTANT_VU1 && (VU0.VI[REG_VPU_STAT].UL & 0x100))
|
||||
CPU_INT(VIF_VU1_FINISH, cpuGetCycles(VU_MTVU_BUSY));
|
||||
else
|
||||
CPU_INT(VIF_VU1_FINISH, 128);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (VU0.VI[REG_VPU_STAT].UL & 0x100)
|
||||
{
|
||||
u32 _cycles = VU1.cycle;
|
||||
//DevCon.Warning("Finishing VU1");
|
||||
vu1Finish(false);
|
||||
CPU_INT(VIF_VU1_FINISH, VU1.cycle - _cycles);
|
||||
if (THREAD_VU1 && !INSTANT_VU1 && (VU0.VI[REG_VPU_STAT].UL & 0x100))
|
||||
CPU_INT(VIF_VU1_FINISH, cpuGetCycles(VU_MTVU_BUSY));
|
||||
else
|
||||
CPU_INT(VIF_VU1_FINISH, VU1.cycle - _cycles);
|
||||
return;
|
||||
}
|
||||
|
||||
vif1Regs.stat.VEW = false;
|
||||
VIF_LOG("VU1 finished");
|
||||
|
||||
if(vif1.waitforvu)
|
||||
if (vif1.waitforvu)
|
||||
{
|
||||
vif1.waitforvu = false;
|
||||
//Check if VIF is already scheduled to interrupt, if it's waiting, kick it :P
|
||||
@@ -257,7 +267,7 @@ __fi void vif1VUFinish()
|
||||
vif1Interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//DevCon.Warning("VU1 state cleared");
|
||||
}
|
||||
|
||||
@@ -267,19 +277,22 @@ __fi void vif1Interrupt()
|
||||
|
||||
g_vif1Cycles = 0;
|
||||
|
||||
if( gifRegs.stat.APATH == 2 && gifUnit.gifPath[GIF_PATH_2].isDone())
|
||||
if (gifRegs.stat.APATH == 2 && gifUnit.gifPath[GIF_PATH_2].isDone())
|
||||
{
|
||||
gifRegs.stat.APATH = 0;
|
||||
gifRegs.stat.OPH = 0;
|
||||
vif1Regs.stat.VGW = false; //Let vif continue if it's stuck on a flush
|
||||
|
||||
if(gifUnit.checkPaths(1,0,1)) gifUnit.Execute(false, true);
|
||||
if (gifUnit.checkPaths(1, 0, 1))
|
||||
gifUnit.Execute(false, true);
|
||||
}
|
||||
//Some games (Fahrenheit being one) start vif first, let it loop through blankness while it sets MFIFO mode, so we need to check it here.
|
||||
if (dmacRegs.ctrl.MFD == MFD_VIF1) {
|
||||
if (dmacRegs.ctrl.MFD == MFD_VIF1)
|
||||
{
|
||||
//Console.WriteLn("VIFMFIFO\n");
|
||||
// Test changed because the Final Fantasy 12 opening somehow has the tag in *Undefined* mode, which is not in the documentation that I saw.
|
||||
if (vif1ch.chcr.MOD == NORMAL_MODE) Console.WriteLn("MFIFO mode is normal (which isn't normal here)! %x", vif1ch.chcr._u32);
|
||||
if (vif1ch.chcr.MOD == NORMAL_MODE)
|
||||
Console.WriteLn("MFIFO mode is normal (which isn't normal here)! %x", vif1ch.chcr._u32);
|
||||
vif1Regs.stat.FQC = std::min((u32)0x10, vif1ch.qwc);
|
||||
vifMFIFOInterrupt();
|
||||
return;
|
||||
@@ -287,42 +300,46 @@ __fi void vif1Interrupt()
|
||||
|
||||
// We need to check the direction, if it is downloading
|
||||
// from the GS then we handle that separately (KH2 for testing)
|
||||
if (vif1ch.chcr.DIR) {
|
||||
bool isDirect = (vif1.cmd & 0x7f) == 0x50;
|
||||
if (vif1ch.chcr.DIR)
|
||||
{
|
||||
bool isDirect = (vif1.cmd & 0x7f) == 0x50;
|
||||
bool isDirectHL = (vif1.cmd & 0x7f) == 0x51;
|
||||
if((isDirect && !gifUnit.CanDoPath2())
|
||||
|| (isDirectHL && !gifUnit.CanDoPath2HL())) {
|
||||
if ((isDirect && !gifUnit.CanDoPath2()) || (isDirectHL && !gifUnit.CanDoPath2HL()))
|
||||
{
|
||||
GUNIT_WARN("vif1Interrupt() - Waiting for Path 2 to be ready");
|
||||
CPU_INT(DMAC_VIF1, 128);
|
||||
if(gifRegs.stat.APATH == 3) vif1Regs.stat.VGW = 1; //We're waiting for path 3. Gunslinger II
|
||||
if (gifRegs.stat.APATH == 3)
|
||||
vif1Regs.stat.VGW = 1; //We're waiting for path 3. Gunslinger II
|
||||
return;
|
||||
}
|
||||
vif1Regs.stat.VGW = 0; //Path 3 isn't busy so we don't need to wait for it.
|
||||
vif1Regs.stat.FQC = std::min(vif1ch.qwc, (u32)16);
|
||||
//Simulated GS transfer time done, clear the flags
|
||||
}
|
||||
|
||||
if(vif1.waitforvu)
|
||||
|
||||
if (vif1.waitforvu)
|
||||
{
|
||||
//DevCon.Warning("Waiting on VU1");
|
||||
//CPU_INT(DMAC_VIF1, 16);
|
||||
CPU_INT(VIF_VU1_FINISH, 16);
|
||||
CPU_INT(VIF_VU1_FINISH, std::max(16, cpuGetCycles(VU_MTVU_BUSY)));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (vif1Regs.stat.VGW)
|
||||
return;
|
||||
|
||||
if (!vif1ch.chcr.STR) Console.WriteLn("Vif1 running when CHCR == %x", vif1ch.chcr._u32);
|
||||
|
||||
if (!vif1ch.chcr.STR)
|
||||
Console.WriteLn("Vif1 running when CHCR == %x", vif1ch.chcr._u32);
|
||||
|
||||
if (vif1.irq && vif1.vifstalled.enabled && vif1.vifstalled.value == VIF_IRQ_STALL)
|
||||
{
|
||||
VIF_LOG("VIF IRQ Firing");
|
||||
if (!vif1Regs.stat.ER1)
|
||||
vif1Regs.stat.INT = true;
|
||||
|
||||
|
||||
//Yakuza watches VIF_STAT so lets do this here.
|
||||
if (((vif1Regs.code >> 24) & 0x7f) != 0x7) {
|
||||
if (((vif1Regs.code >> 24) & 0x7f) != 0x7)
|
||||
{
|
||||
vif1Regs.stat.VIS = true;
|
||||
}
|
||||
|
||||
@@ -336,7 +353,7 @@ __fi void vif1Interrupt()
|
||||
//NFSHPS stalls when the whole packet has gone across (it stalls in the last 32bit cmd)
|
||||
//In this case VIF will end
|
||||
vif1Regs.stat.FQC = std::min((u32)0x10, vif1ch.qwc);
|
||||
if((vif1ch.qwc > 0 || !vif1.done) && !CHECK_VIF1STALLHACK)
|
||||
if ((vif1ch.qwc > 0 || !vif1.done) && !CHECK_VIF1STALLHACK)
|
||||
{
|
||||
vif1Regs.stat.VPS = VPS_DECODING; //If there's more data you need to say it's decoding the next VIF CMD (Onimusha - Blade Warriors)
|
||||
VIF_LOG("VIF1 Stalled");
|
||||
@@ -350,40 +367,62 @@ __fi void vif1Interrupt()
|
||||
//Mirroring change to VIF0
|
||||
if (vif1.cmd)
|
||||
{
|
||||
if (vif1.done && (vif1ch.qwc == 0)) vif1Regs.stat.VPS = VPS_WAITING;
|
||||
if (vif1.done && (vif1ch.qwc == 0))
|
||||
vif1Regs.stat.VPS = VPS_WAITING;
|
||||
}
|
||||
else
|
||||
{
|
||||
vif1Regs.stat.VPS = VPS_IDLE;
|
||||
}
|
||||
|
||||
|
||||
if (vif1.inprogress & 0x1)
|
||||
{
|
||||
_VIF1chain();
|
||||
// VIF_NORMAL_FROM_MEM_MODE is a very slow operation.
|
||||
// Timesplitters 2 depends on this beeing a bit higher than 128.
|
||||
if (vif1ch.chcr.DIR) vif1Regs.stat.FQC = std::min(vif1ch.qwc, (u32)16);
|
||||
|
||||
if(!(vif1Regs.stat.VGW && gifUnit.gifPath[GIF_PATH_3].state != GIF_PATH_IDLE)) //If we're waiting on GIF, stop looping, (can be over 1000 loops!)
|
||||
{
|
||||
_VIF1chain();
|
||||
// VIF_NORMAL_FROM_MEM_MODE is a very slow operation.
|
||||
// Timesplitters 2 depends on this beeing a bit higher than 128.
|
||||
if (vif1ch.chcr.DIR)
|
||||
vif1Regs.stat.FQC = std::min(vif1ch.qwc, (u32)16);
|
||||
|
||||
if (!(vif1Regs.stat.VGW && gifUnit.gifPath[GIF_PATH_3].state != GIF_PATH_IDLE)) //If we're waiting on GIF, stop looping, (can be over 1000 loops!)
|
||||
{
|
||||
if (vif1.waitforvu)
|
||||
{
|
||||
//if (cpuGetCycles(VU_MTVU_BUSY) > static_cast<int>(g_vif1Cycles))
|
||||
// DevCon.Warning("Waiting %d instead of %d", cpuGetCycles(VU_MTVU_BUSY), static_cast<int>(g_vif1Cycles));
|
||||
CPU_INT(DMAC_VIF1, std::max(static_cast<int>(g_vif1Cycles), cpuGetCycles(VU_MTVU_BUSY)));
|
||||
}
|
||||
else
|
||||
CPU_INT(DMAC_VIF1, g_vif1Cycles);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vif1.done)
|
||||
{
|
||||
if (!vif1.done)
|
||||
{
|
||||
|
||||
if (!(dmacRegs.ctrl.DMAE) || vif1Regs.stat.VSS) //Stopped or DMA Disabled
|
||||
{
|
||||
//Console.WriteLn("vif1 dma masked");
|
||||
return;
|
||||
}
|
||||
if (!(dmacRegs.ctrl.DMAE) || vif1Regs.stat.VSS) //Stopped or DMA Disabled
|
||||
{
|
||||
//Console.WriteLn("vif1 dma masked");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((vif1.inprogress & 0x1) == 0) vif1SetupTransfer();
|
||||
if (vif1ch.chcr.DIR) vif1Regs.stat.FQC = std::min(vif1ch.qwc, (u32)16);
|
||||
if ((vif1.inprogress & 0x1) == 0)
|
||||
vif1SetupTransfer();
|
||||
if (vif1ch.chcr.DIR)
|
||||
vif1Regs.stat.FQC = std::min(vif1ch.qwc, (u32)16);
|
||||
|
||||
if(!(vif1Regs.stat.VGW && gifUnit.gifPath[GIF_PATH_3].state != GIF_PATH_IDLE)) //If we're waiting on GIF, stop looping, (can be over 1000 loops!)
|
||||
CPU_INT(DMAC_VIF1, g_vif1Cycles);
|
||||
return;
|
||||
if (!(vif1Regs.stat.VGW && gifUnit.gifPath[GIF_PATH_3].state != GIF_PATH_IDLE)) //If we're waiting on GIF, stop looping, (can be over 1000 loops!)
|
||||
{
|
||||
if (vif1.waitforvu)
|
||||
{
|
||||
//if (cpuGetCycles(VU_MTVU_BUSY) > static_cast<int>(g_vif1Cycles))
|
||||
// DevCon.Warning("Waiting %d instead of %d", cpuGetCycles(VU_MTVU_BUSY), static_cast<int>(g_vif1Cycles));
|
||||
CPU_INT(DMAC_VIF1, std::max(static_cast<int>(g_vif1Cycles), cpuGetCycles(VU_MTVU_BUSY)));
|
||||
}
|
||||
else
|
||||
CPU_INT(DMAC_VIF1, g_vif1Cycles);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (vif1.vifstalled.enabled && vif1.done)
|
||||
@@ -393,52 +432,55 @@ __fi void vif1Interrupt()
|
||||
return; //Dont want to end if vif is stalled.
|
||||
}
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
if (vif1ch.qwc > 0) DevCon.WriteLn("VIF1 Ending with %x QWC left", vif1ch.qwc);
|
||||
if (vif1.cmd != 0) DevCon.WriteLn("vif1.cmd still set %x tag size %x", vif1.cmd, vif1.tag.size);
|
||||
if (vif1ch.qwc > 0)
|
||||
DevCon.WriteLn("VIF1 Ending with %x QWC left", vif1ch.qwc);
|
||||
if (vif1.cmd != 0)
|
||||
DevCon.WriteLn("vif1.cmd still set %x tag size %x", vif1.cmd, vif1.tag.size);
|
||||
#endif
|
||||
|
||||
if((vif1ch.chcr.DIR == VIF_NORMAL_TO_MEM_MODE) && vif1.GSLastDownloadSize <= 16)
|
||||
if ((vif1ch.chcr.DIR == VIF_NORMAL_TO_MEM_MODE) && vif1.GSLastDownloadSize <= 16)
|
||||
{
|
||||
//Reverse fifo has finished and nothing is left, so lets clear the outputting flag
|
||||
gifRegs.stat.OPH = false;
|
||||
}
|
||||
|
||||
if (vif1ch.chcr.DIR) vif1Regs.stat.FQC = std::min(vif1ch.qwc, (u32)16);
|
||||
if (vif1ch.chcr.DIR)
|
||||
vif1Regs.stat.FQC = std::min(vif1ch.qwc, (u32)16);
|
||||
|
||||
vif1ch.chcr.STR = false;
|
||||
vif1.vifstalled.enabled = false;
|
||||
vif1.irqoffset.enabled = false;
|
||||
if(vif1.queued_program) vifExecQueue(1);
|
||||
if (vif1.queued_program)
|
||||
vifExecQueue(1);
|
||||
g_vif1Cycles = 0;
|
||||
VIF_LOG("VIF1 DMA End");
|
||||
hwDmacIrq(DMAC_VIF1);
|
||||
|
||||
}
|
||||
|
||||
void dmaVIF1()
|
||||
{
|
||||
VIF_LOG("dmaVIF1 chcr = %lx, madr = %lx, qwc = %lx\n"
|
||||
" tadr = %lx, asr0 = %lx, asr1 = %lx",
|
||||
vif1ch.chcr._u32, vif1ch.madr, vif1ch.qwc,
|
||||
vif1ch.tadr, vif1ch.asr0, vif1ch.asr1);
|
||||
" tadr = %lx, asr0 = %lx, asr1 = %lx",
|
||||
vif1ch.chcr._u32, vif1ch.madr, vif1ch.qwc,
|
||||
vif1ch.tadr, vif1ch.asr0, vif1ch.asr1);
|
||||
|
||||
g_vif1Cycles = 0;
|
||||
vif1.inprogress = 0;
|
||||
|
||||
if (vif1ch.qwc > 0) // Normal Mode
|
||||
if (vif1ch.qwc > 0) // Normal Mode
|
||||
{
|
||||
|
||||
|
||||
// ignore tag if it's a GS download (Def Jam Fight for NY)
|
||||
if(vif1ch.chcr.MOD == CHAIN_MODE && vif1ch.chcr.DIR)
|
||||
if (vif1ch.chcr.MOD == CHAIN_MODE && vif1ch.chcr.DIR)
|
||||
{
|
||||
vif1.dmamode = VIF_CHAIN_MODE;
|
||||
//DevCon.Warning(L"VIF1 QWC on Chain CHCR " + vif1ch.chcr.desc());
|
||||
|
||||
|
||||
if ((vif1ch.chcr.tag().ID == TAG_REFE) || (vif1ch.chcr.tag().ID == TAG_END) || (vif1ch.chcr.tag().IRQ && vif1ch.chcr.TIE))
|
||||
{
|
||||
vif1.done = true;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
vif1.done = false;
|
||||
}
|
||||
@@ -448,12 +490,13 @@ void dmaVIF1()
|
||||
if (dmacRegs.ctrl.STD == STD_VIF1)
|
||||
Console.WriteLn("DMA Stall Control on VIF1 normal not implemented - Report which game to PCSX2 Team");
|
||||
|
||||
if (vif1ch.chcr.DIR) // from Memory
|
||||
if (vif1ch.chcr.DIR) // from Memory
|
||||
vif1.dmamode = VIF_NORMAL_FROM_MEM_MODE;
|
||||
else
|
||||
vif1.dmamode = VIF_NORMAL_TO_MEM_MODE;
|
||||
|
||||
if(vif1.irqoffset.enabled && !vif1.done) DevCon.Warning("Warning! VIF1 starting a Normal transfer with vif offset set (Possible force stop?)");
|
||||
if (vif1.irqoffset.enabled && !vif1.done)
|
||||
DevCon.Warning("Warning! VIF1 starting a Normal transfer with vif offset set (Possible force stop?)");
|
||||
vif1.done = true;
|
||||
}
|
||||
|
||||
@@ -464,10 +507,10 @@ void dmaVIF1()
|
||||
vif1.inprogress &= ~0x1;
|
||||
vif1.dmamode = VIF_CHAIN_MODE;
|
||||
vif1.done = false;
|
||||
|
||||
}
|
||||
|
||||
if (vif1ch.chcr.DIR) vif1Regs.stat.FQC = std::min((u32)0x10, vif1ch.qwc);
|
||||
if (vif1ch.chcr.DIR)
|
||||
vif1Regs.stat.FQC = std::min((u32)0x10, vif1ch.qwc);
|
||||
|
||||
// Check VIF isn't stalled before starting the loop.
|
||||
// Batman Vengence does something stupid and instead of cancelling a stall it tries to restart VIF, THEN check the stall
|
||||
|
||||
@@ -27,9 +27,9 @@ static u32 qwctag(u32 mask)
|
||||
static u32 QWCinVIFMFIFO(u32 DrainADDR, u32 qwc)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
|
||||
//Calculate what we have in the fifo.
|
||||
if(DrainADDR <= spr0ch.madr)
|
||||
if (DrainADDR <= spr0ch.madr)
|
||||
{
|
||||
//Drain is below the tadr, calculate the difference between them
|
||||
ret = (spr0ch.madr - DrainADDR) >> 4;
|
||||
@@ -37,7 +37,7 @@ static u32 QWCinVIFMFIFO(u32 DrainADDR, u32 qwc)
|
||||
else
|
||||
{
|
||||
u32 limit = dmacRegs.rbor.ADDR + dmacRegs.rbsr.RMSK + 16;
|
||||
//Drain is higher than SPR so it has looped round,
|
||||
//Drain is higher than SPR so it has looped round,
|
||||
//calculate from base to the SPR tag addr and what is left in the top of the ring
|
||||
ret = ((spr0ch.madr - dmacRegs.rbor.ADDR) + (limit - DrainADDR)) >> 4;
|
||||
}
|
||||
@@ -50,18 +50,19 @@ static __fi bool mfifoVIF1rbTransfer()
|
||||
{
|
||||
u32 msize = dmacRegs.rbor.ADDR + dmacRegs.rbsr.RMSK + 16;
|
||||
u32 mfifoqwc = std::min(QWCinVIFMFIFO(vif1ch.madr, vif1ch.qwc), vif1ch.qwc);
|
||||
u32 *src;
|
||||
u32* src;
|
||||
bool ret;
|
||||
|
||||
if (mfifoqwc == 0) {
|
||||
|
||||
if (mfifoqwc == 0)
|
||||
{
|
||||
DevCon.Warning("VIF MFIFO no QWC before transfer (in transfer function, bit late really)");
|
||||
return true; //Cant do anything, lets forget it
|
||||
return true; //Cant do anything, lets forget it
|
||||
}
|
||||
|
||||
/* Check if the transfer should wrap around the ring buffer */
|
||||
if ((vif1ch.madr + (mfifoqwc << 4)) > (msize))
|
||||
{
|
||||
int s1 = ((msize) - vif1ch.madr) >> 2;
|
||||
int s1 = ((msize)-vif1ch.madr) >> 2;
|
||||
|
||||
VIF_LOG("Split MFIFO");
|
||||
|
||||
@@ -69,7 +70,8 @@ static __fi bool mfifoVIF1rbTransfer()
|
||||
vif1ch.madr = qwctag(vif1ch.madr);
|
||||
|
||||
src = (u32*)PSM(vif1ch.madr);
|
||||
if (src == NULL) return false;
|
||||
if (src == NULL)
|
||||
return false;
|
||||
|
||||
if (vif1.irqoffset.enabled)
|
||||
ret = VIF1transfer(src + vif1.irqoffset.value, s1 - vif1.irqoffset.value);
|
||||
@@ -78,15 +80,17 @@ static __fi bool mfifoVIF1rbTransfer()
|
||||
|
||||
if (ret)
|
||||
{
|
||||
if(vif1.irqoffset.value != 0) DevCon.Warning("VIF1 MFIFO Offest != 0! vifoffset=%x", vif1.irqoffset.value);
|
||||
/* and second copy 's2' bytes from 'maddr' to '&data[s1]' */
|
||||
if (vif1.irqoffset.value != 0)
|
||||
DevCon.Warning("VIF1 MFIFO Offest != 0! vifoffset=%x", vif1.irqoffset.value);
|
||||
/* and second copy 's2' bytes from 'maddr' to '&data[s1]' */
|
||||
//DevCon.Warning("Loopyloop");
|
||||
vif1ch.tadr = qwctag(vif1ch.tadr);
|
||||
vif1ch.madr = qwctag(vif1ch.madr);
|
||||
|
||||
src = (u32*)PSM(vif1ch.madr);
|
||||
if (src == NULL) return false;
|
||||
VIF1transfer(src, ((mfifoqwc << 2) - s1));
|
||||
src = (u32*)PSM(vif1ch.madr);
|
||||
if (src == NULL)
|
||||
return false;
|
||||
VIF1transfer(src, ((mfifoqwc << 2) - s1));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -95,7 +99,8 @@ static __fi bool mfifoVIF1rbTransfer()
|
||||
|
||||
/* it doesn't, so just transfer 'qwc*4' words */
|
||||
src = (u32*)PSM(vif1ch.madr);
|
||||
if (src == NULL) return false;
|
||||
if (src == NULL)
|
||||
return false;
|
||||
|
||||
if (vif1.irqoffset.enabled)
|
||||
ret = VIF1transfer(src + vif1.irqoffset.value, mfifoqwc * 4 - vif1.irqoffset.value);
|
||||
@@ -115,10 +120,11 @@ static __fi void mfifo_VIF1chain()
|
||||
}
|
||||
|
||||
if (vif1ch.madr >= dmacRegs.rbor.ADDR &&
|
||||
vif1ch.madr < (dmacRegs.rbor.ADDR + dmacRegs.rbsr.RMSK + 16u))
|
||||
vif1ch.madr < (dmacRegs.rbor.ADDR + dmacRegs.rbsr.RMSK + 16u))
|
||||
{
|
||||
//if(vif1ch.madr == (dmacRegs.rbor.ADDR + dmacRegs.rbsr.RMSK + 16)) DevCon.Warning("Edge VIF1");
|
||||
if (QWCinVIFMFIFO(vif1ch.madr, vif1ch.qwc) == 0) {
|
||||
if (QWCinVIFMFIFO(vif1ch.madr, vif1ch.qwc) == 0)
|
||||
{
|
||||
VIF_LOG("VIF MFIFO Empty before transfer");
|
||||
vif1.inprogress |= 0x10;
|
||||
g_vif1Cycles += 4;
|
||||
@@ -132,16 +138,16 @@ static __fi void mfifo_VIF1chain()
|
||||
//It does an END tag (which normally doesn't increment TADR because it breaks Soul Calibur 2)
|
||||
//with a QWC of 1 (rare) so we need to increment the TADR in the case of MFIFO.
|
||||
vif1ch.tadr = vif1ch.madr;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
tDMA_TAG *pMem = dmaGetAddr(vif1ch.madr, !vif1ch.chcr.DIR);
|
||||
tDMA_TAG* pMem = dmaGetAddr(vif1ch.madr, !vif1ch.chcr.DIR);
|
||||
VIF_LOG("Non-MFIFO Location");
|
||||
|
||||
//No need to exit on non-mfifo as it is indirect anyway, so it can be transferring this while spr refills the mfifo
|
||||
|
||||
if (pMem == NULL) return;
|
||||
if (pMem == NULL)
|
||||
return;
|
||||
|
||||
if (vif1.irqoffset.enabled)
|
||||
VIF1transfer((u32*)pMem + vif1.irqoffset.value, vif1ch.qwc * 4 - vif1.irqoffset.value);
|
||||
@@ -152,19 +158,20 @@ static __fi void mfifo_VIF1chain()
|
||||
|
||||
void mfifoVifMaskMem(int id)
|
||||
{
|
||||
switch (id) {
|
||||
switch (id)
|
||||
{
|
||||
//These five transfer data following the tag, need to check its within the buffer (Front Mission 4)
|
||||
case TAG_CNT:
|
||||
case TAG_NEXT:
|
||||
case TAG_CALL:
|
||||
case TAG_CALL:
|
||||
case TAG_RET:
|
||||
case TAG_END:
|
||||
if(vif1ch.madr < dmacRegs.rbor.ADDR) //probably not needed but we will check anyway.
|
||||
if (vif1ch.madr < dmacRegs.rbor.ADDR) //probably not needed but we will check anyway.
|
||||
{
|
||||
//DevCon.Warning("VIF MFIFO MADR below bottom of ring buffer, wrapping VIF MADR = %x Ring Bottom %x", vif1ch.madr, dmacRegs.rbor.ADDR);
|
||||
vif1ch.madr = qwctag(vif1ch.madr);
|
||||
}
|
||||
if(vif1ch.madr > (dmacRegs.rbor.ADDR + (u32)dmacRegs.rbsr.RMSK)) //Usual scenario is the tag is near the end (Front Mission 4)
|
||||
if (vif1ch.madr > (dmacRegs.rbor.ADDR + (u32)dmacRegs.rbsr.RMSK)) //Usual scenario is the tag is near the end (Front Mission 4)
|
||||
{
|
||||
//DevCon.Warning("VIF MFIFO MADR outside top of ring buffer, wrapping VIF MADR = %x Ring Top %x", vif1ch.madr, (dmacRegs.rbor.ADDR + dmacRegs.rbsr.RMSK)+16);
|
||||
vif1ch.madr = qwctag(vif1ch.madr);
|
||||
@@ -178,13 +185,14 @@ void mfifoVifMaskMem(int id)
|
||||
|
||||
void mfifoVIF1transfer()
|
||||
{
|
||||
tDMA_TAG *ptag;
|
||||
tDMA_TAG* ptag;
|
||||
|
||||
g_vif1Cycles = 0;
|
||||
|
||||
if (vif1ch.qwc == 0)
|
||||
{
|
||||
if (QWCinVIFMFIFO(vif1ch.tadr, 1) == 0) {
|
||||
if (QWCinVIFMFIFO(vif1ch.tadr, 1) == 0)
|
||||
{
|
||||
VIF_LOG("VIF MFIFO Empty before tag");
|
||||
vif1.inprogress |= 0x10;
|
||||
g_vif1Cycles += 4;
|
||||
@@ -212,21 +220,20 @@ void mfifoVIF1transfer()
|
||||
|
||||
if (vif1.irqoffset.enabled)
|
||||
{
|
||||
ret = VIF1transfer((u32*)&masked_tag + vif1.irqoffset.value, 4 - vif1.irqoffset.value, true); //Transfer Tag on stall
|
||||
ret = VIF1transfer((u32*)&masked_tag + vif1.irqoffset.value, 4 - vif1.irqoffset.value, true); //Transfer Tag on stall
|
||||
//ret = VIF1transfer((u32*)ptag + (2 + vif1.irqoffset), 2 - vif1.irqoffset); //Transfer Tag on stall
|
||||
}
|
||||
else
|
||||
{
|
||||
vif1.irqoffset.value = 2;
|
||||
vif1.irqoffset.enabled = true;
|
||||
ret = VIF1transfer((u32*)&masked_tag + 2, 2, true); //Transfer Tag
|
||||
ret = VIF1transfer((u32*)&masked_tag + 2, 2, true); //Transfer Tag
|
||||
}
|
||||
|
||||
if (!ret && vif1.irqoffset.enabled)
|
||||
{
|
||||
vif1.inprogress &= ~1;
|
||||
return; //IRQ set by VIFTransfer
|
||||
|
||||
return; //IRQ set by VIFTransfer
|
||||
}
|
||||
g_vif1Cycles += 2;
|
||||
}
|
||||
@@ -234,12 +241,12 @@ void mfifoVIF1transfer()
|
||||
vif1.irqoffset.value = 0;
|
||||
vif1.irqoffset.enabled = false;
|
||||
|
||||
vif1ch.unsafeTransfer(ptag);
|
||||
vif1ch.unsafeTransfer(ptag);
|
||||
|
||||
vif1ch.madr = ptag[1]._u32;
|
||||
|
||||
VIF_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx spr0 madr = %x",
|
||||
ptag[1]._u32, ptag[0]._u32, vif1ch.qwc, ptag->ID, vif1ch.madr, vif1ch.tadr, spr0ch.madr);
|
||||
ptag[1]._u32, ptag[0]._u32, vif1ch.qwc, ptag->ID, vif1ch.madr, vif1ch.tadr, spr0ch.madr);
|
||||
|
||||
vif1.done |= hwDmacSrcChainWithStack(vif1ch, ptag->ID);
|
||||
|
||||
@@ -253,13 +260,14 @@ void mfifoVIF1transfer()
|
||||
|
||||
vif1ch.tadr = qwctag(vif1ch.tadr);
|
||||
|
||||
if(vif1ch.qwc > 0) vif1.inprogress |= 1;
|
||||
if (vif1ch.qwc > 0)
|
||||
vif1.inprogress |= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
DevCon.Warning("Vif MFIFO QWC not 0 on tag");
|
||||
}
|
||||
|
||||
|
||||
|
||||
VIF_LOG("mfifoVIF1transfer end %x madr %x, tadr %x", vif1ch.chcr._u32, vif1ch.madr, vif1ch.tadr);
|
||||
}
|
||||
@@ -269,33 +277,36 @@ void vifMFIFOInterrupt()
|
||||
g_vif1Cycles = 0;
|
||||
VIF_LOG("vif mfifo interrupt");
|
||||
|
||||
if (dmacRegs.ctrl.MFD != MFD_VIF1) {
|
||||
if (dmacRegs.ctrl.MFD != MFD_VIF1)
|
||||
{
|
||||
vif1Interrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
if( gifRegs.stat.APATH == 2 && gifUnit.gifPath[1].isDone())
|
||||
if (gifRegs.stat.APATH == 2 && gifUnit.gifPath[1].isDone())
|
||||
{
|
||||
gifRegs.stat.APATH = 0;
|
||||
gifRegs.stat.OPH = 0;
|
||||
|
||||
if(gifUnit.checkPaths(1,0,1)) gifUnit.Execute(false, true);
|
||||
if (gifUnit.checkPaths(1, 0, 1))
|
||||
gifUnit.Execute(false, true);
|
||||
}
|
||||
|
||||
if (vif1ch.chcr.DIR) {
|
||||
bool isDirect = (vif1.cmd & 0x7f) == 0x50;
|
||||
if (vif1ch.chcr.DIR)
|
||||
{
|
||||
bool isDirect = (vif1.cmd & 0x7f) == 0x50;
|
||||
bool isDirectHL = (vif1.cmd & 0x7f) == 0x51;
|
||||
if((isDirect && !gifUnit.CanDoPath2())
|
||||
|| (isDirectHL && !gifUnit.CanDoPath2HL())) {
|
||||
if ((isDirect && !gifUnit.CanDoPath2()) || (isDirectHL && !gifUnit.CanDoPath2HL()))
|
||||
{
|
||||
GUNIT_WARN("vifMFIFOInterrupt() - Waiting for Path 2 to be ready");
|
||||
CPU_INT(DMAC_MFIFO_VIF, 128);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(vif1.waitforvu)
|
||||
if (vif1.waitforvu)
|
||||
{
|
||||
// DevCon.Warning("Waiting on VU1 MFIFO");
|
||||
CPU_INT(VIF_VU1_FINISH, 16);
|
||||
//DevCon.Warning("Waiting on VU1 MFIFO");
|
||||
CPU_INT(VIF_VU1_FINISH, std::max(16, cpuGetCycles(VU_MTVU_BUSY)));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -303,25 +314,29 @@ void vifMFIFOInterrupt()
|
||||
// we handle that separately (KH2 for testing)
|
||||
|
||||
// Simulated GS transfer time done, clear the flags
|
||||
|
||||
if (vif1.irq && vif1.vifstalled.enabled && vif1.vifstalled.value == VIF_IRQ_STALL) {
|
||||
|
||||
if (vif1.irq && vif1.vifstalled.enabled && vif1.vifstalled.value == VIF_IRQ_STALL)
|
||||
{
|
||||
VIF_LOG("VIF MFIFO Code Interrupt detected");
|
||||
vif1Regs.stat.INT = true;
|
||||
|
||||
if (((vif1Regs.code >> 24) & 0x7f) != 0x7) {
|
||||
if (((vif1Regs.code >> 24) & 0x7f) != 0x7)
|
||||
{
|
||||
vif1Regs.stat.VIS = true;
|
||||
}
|
||||
|
||||
hwIntcIrq(INTC_VIF1);
|
||||
--vif1.irq;
|
||||
|
||||
if (vif1Regs.stat.test(VIF1_STAT_VSS | VIF1_STAT_VIS | VIF1_STAT_VFS)) {
|
||||
|
||||
if (vif1Regs.stat.test(VIF1_STAT_VSS | VIF1_STAT_VIS | VIF1_STAT_VFS))
|
||||
{
|
||||
//vif1Regs.stat.FQC = 0; // FQC=0
|
||||
//vif1ch.chcr.STR = false;
|
||||
vif1Regs.stat.FQC = std::min((u32)0x10, vif1ch.qwc);
|
||||
VIF_LOG("VIF1 MFIFO Stalled qwc = %x done = %x inprogress = %x", vif1ch.qwc, vif1.done, vif1.inprogress & 0x10);
|
||||
//Used to check if the MFIFO was empty, there's really no need if it's finished what it needed.
|
||||
if((vif1ch.qwc > 0 || !vif1.done)) {
|
||||
if ((vif1ch.qwc > 0 || !vif1.done))
|
||||
{
|
||||
vif1Regs.stat.VPS = VPS_DECODING; //If there's more data you need to say it's decoding the next VIF CMD (Onimusha - Blade Warriors)
|
||||
VIF_LOG("VIF1 MFIFO Stalled");
|
||||
return;
|
||||
@@ -330,33 +345,48 @@ void vifMFIFOInterrupt()
|
||||
}
|
||||
|
||||
//Mirroring change to VIF0
|
||||
if (vif1.cmd) {
|
||||
if (vif1.done && vif1ch.qwc == 0) vif1Regs.stat.VPS = VPS_WAITING;
|
||||
if (vif1.cmd)
|
||||
{
|
||||
if (vif1.done && vif1ch.qwc == 0)
|
||||
vif1Regs.stat.VPS = VPS_WAITING;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
vif1Regs.stat.VPS = VPS_IDLE;
|
||||
}
|
||||
|
||||
if(vif1.inprogress & 0x10) {
|
||||
if (vif1.inprogress & 0x10)
|
||||
{
|
||||
FireMFIFOEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
vif1.vifstalled.enabled = false;
|
||||
|
||||
if (!vif1.done || vif1ch.qwc) {
|
||||
switch(vif1.inprogress & 1) {
|
||||
if (!vif1.done || vif1ch.qwc)
|
||||
{
|
||||
switch (vif1.inprogress & 1)
|
||||
{
|
||||
case 0: //Set up transfer
|
||||
mfifoVIF1transfer();
|
||||
vif1Regs.stat.FQC = std::min((u32)0x10, vif1ch.qwc);
|
||||
[[fallthrough]];
|
||||
|
||||
case 1: //Transfer data
|
||||
if(vif1.inprogress & 0x1) //Just in case the tag breaks early (or something wierd happens)!
|
||||
if (vif1.inprogress & 0x1) //Just in case the tag breaks early (or something wierd happens)!
|
||||
mfifo_VIF1chain();
|
||||
//Sanity check! making sure we always have non-zero values
|
||||
if(!(vif1Regs.stat.VGW && gifUnit.gifPath[GIF_PATH_3].state != GIF_PATH_IDLE)) //If we're waiting on GIF, stop looping, (can be over 1000 loops!)
|
||||
CPU_INT(DMAC_MFIFO_VIF, (g_vif1Cycles == 0 ? 4 : g_vif1Cycles) );
|
||||
if (!(vif1Regs.stat.VGW && gifUnit.gifPath[GIF_PATH_3].state != GIF_PATH_IDLE)) //If we're waiting on GIF, stop looping, (can be over 1000 loops!)
|
||||
{
|
||||
if (vif1.waitforvu)
|
||||
{
|
||||
//if (cpuGetCycles(VU_MTVU_BUSY) > static_cast<int>(g_vif1Cycles))
|
||||
// DevCon.Warning("Waiting %d instead of %d", cpuGetCycles(VU_MTVU_BUSY), static_cast<int>(g_vif1Cycles));
|
||||
CPU_INT(DMAC_MFIFO_VIF, std::max(static_cast<int>((g_vif1Cycles == 0 ? 4 : g_vif1Cycles)), cpuGetCycles(VU_MTVU_BUSY)));
|
||||
}
|
||||
else
|
||||
CPU_INT(DMAC_MFIFO_VIF, (g_vif1Cycles == 0 ? 4 : g_vif1Cycles));
|
||||
}
|
||||
|
||||
vif1Regs.stat.FQC = std::min((u32)0x10, vif1ch.qwc);
|
||||
return;
|
||||
@@ -368,7 +398,8 @@ void vifMFIFOInterrupt()
|
||||
vif1.irqoffset.enabled = false;
|
||||
vif1.done = 1;
|
||||
|
||||
if (spr0ch.madr == vif1ch.tadr) {
|
||||
if (spr0ch.madr == vif1ch.tadr)
|
||||
{
|
||||
FireMFIFOEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -341,7 +341,6 @@ namespace Panels
|
||||
void Defaults_Click(wxCommandEvent& evt);
|
||||
void EECycleRate_Scroll(wxScrollEvent& event);
|
||||
void VUCycleRate_Scroll(wxScrollEvent& event);
|
||||
void VUThread_Enable(wxCommandEvent& evt);
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
@@ -167,16 +167,16 @@ Panels::SpeedHacksPanel::SpeedHacksPanel( wxWindow* parent )
|
||||
m_check_vuThread = new pxCheckBox( vuHacksPanel, _("MTVU (Multi-Threaded microVU1)"),
|
||||
_("Good Speedup and High Compatibility; may cause hanging... [Recommended on 3+ cores]") );
|
||||
|
||||
m_check_vu1Instant = new pxCheckBox(vuHacksPanel, _("Instant VU1 (without MTVU only)"),
|
||||
m_check_vu1Instant = new pxCheckBox(vuHacksPanel, _("Instant VU1"),
|
||||
_("Good Speedup and High Compatibility; may cause some graphical errors"));
|
||||
|
||||
m_check_vuFlagHack->SetToolTip( pxEt( L"Updates Status Flags only on blocks which will read them, instead of all the time. This is safe most of the time."
|
||||
) );
|
||||
|
||||
m_check_vuThread->SetToolTip( pxEt( L"Runs VU1 on its own thread (microVU1-only). Generally a speedup on CPUs with 3 or more cores. This is safe for most games, but a few games are incompatible and may hang. In the case of GS limited games, it may be a slowdown (especially on dual core CPUs)."
|
||||
m_check_vuThread->SetToolTip( pxEt( L"Runs VU1 on its own thread. Generally a speedup on CPUs with 3 or more cores. This is safe for most games, but a few games are incompatible and may hang. In the case of GS limited games, it may be a slowdown (especially on dual core CPUs)."
|
||||
) );
|
||||
|
||||
m_check_vu1Instant->SetToolTip(pxEt(L"Runs VU1 instantly (when MTVU is disabled). Provides a modest speed improvement. This is safe for most games, but a few games may exhibit graphical errors."
|
||||
m_check_vu1Instant->SetToolTip(pxEt(L"Runs VU1 instantly. Provides a modest speed improvement in most games. This is safe for most games, but a few games may exhibit graphical errors."
|
||||
));
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -237,7 +237,6 @@ Panels::SpeedHacksPanel::SpeedHacksPanel( wxWindow* parent )
|
||||
Bind(wxEVT_SCROLL_CHANGED, &SpeedHacksPanel::VUCycleRate_Scroll, this, m_slider_eeSkip->GetId());
|
||||
Bind(wxEVT_CHECKBOX, &SpeedHacksPanel::OnEnable_Toggled, this, m_check_Enable->GetId());
|
||||
Bind(wxEVT_BUTTON, &SpeedHacksPanel::Defaults_Click, this, wxID_DEFAULT);
|
||||
Bind(wxEVT_CHECKBOX, &SpeedHacksPanel::VUThread_Enable, this, m_check_vuThread->GetId());
|
||||
}
|
||||
|
||||
// Doesn't modify values - only locks(gray out)/unlocks as necessary.
|
||||
@@ -267,11 +266,6 @@ void Panels::SpeedHacksPanel::EnableStuff( AppConfig* configToUse )
|
||||
// Grayout MTVU on safest preset
|
||||
m_check_vuThread->Enable(hacksEnabled && (!hasPreset || configToUse->PresetIndex != 0));
|
||||
|
||||
// Disables the Instant VU1 checkbox when MTVU is checked in the GUI as reflected in the code.
|
||||
// Makes Instant VU1 toggleable when MTVU is unchecked in the GUI.
|
||||
// Some may think that having MTVU + Instant VU1 checked, can have bad side-effects when it doesn't.
|
||||
m_check_vu1Instant->Enable(hacksEnabled && !m_check_vuThread->GetValue());
|
||||
|
||||
// Layout necessary to ensure changed slider text gets re-aligned properly
|
||||
// and to properly gray/ungray pxStaticText stuff (I suspect it causes a
|
||||
// paint event to be sent on Windows)
|
||||
@@ -370,9 +364,3 @@ void Panels::SpeedHacksPanel::VUCycleRate_Scroll(wxScrollEvent &event)
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void Panels::SpeedHacksPanel::VUThread_Enable(wxCommandEvent& evt)
|
||||
{
|
||||
m_check_vu1Instant->Enable(!m_check_vuThread->GetValue());
|
||||
Layout();
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 30 KiB |