mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-09 23:59:27 +01:00
Merge pull request #10518 from iwubcode/draw-mod
Introducing a 'GraphicsMod' system for creators
This commit is contained in:
commit
5d04e1e1de
0
Data/Sys/Load/GraphicMods/Arc Rise Fantasia/RPJ.txt
Normal file
0
Data/Sys/Load/GraphicMods/Arc Rise Fantasia/RPJ.txt
Normal file
19
Data/Sys/Load/GraphicMods/Arc Rise Fantasia/metadata.json
Normal file
19
Data/Sys/Load/GraphicMods/Arc Rise Fantasia/metadata.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n33_160x112_6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
0
Data/Sys/Load/GraphicMods/Battallion Wars 2/RBW.txt
Normal file
0
Data/Sys/Load/GraphicMods/Battallion Wars 2/RBW.txt
Normal file
19
Data/Sys/Load/GraphicMods/Battallion Wars 2/metadata.json
Normal file
19
Data/Sys/Load/GraphicMods/Battallion Wars 2/metadata.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n000023_80x60_6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n2_320x224_4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "HUD definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"name": "HUD",
|
||||
"targets": [
|
||||
{
|
||||
"type": "draw_started",
|
||||
"prettyname": "map border",
|
||||
"texture_filename": "tex1_180x180_6e5c9aa7004f377b_1"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"prettyname": "map border",
|
||||
"texture_filename": "tex1_180x180_6e5c9aa7004f377b_1"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"prettyname": "map player marker",
|
||||
"texture_filename": "tex1_64x64_f84b318707ee8455_1"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"prettyname": "map player marker",
|
||||
"texture_filename": "tex1_64x64_f84b318707ee8455_1"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"prettyname": "map player facing pointer",
|
||||
"texture_filename": "tex1_64x64_2ece13b26442de5a_0"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"prettyname": "map player facing pointer",
|
||||
"texture_filename": "tex1_64x64_2ece13b26442de5a_0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n000008_80x60_6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
0
Data/Sys/Load/GraphicMods/Last Story/SLS.txt
Normal file
0
Data/Sys/Load/GraphicMods/Last Story/SLS.txt
Normal file
103
Data/Sys/Load/GraphicMods/Last Story/metadata.json
Normal file
103
Data/Sys/Load/GraphicMods/Last Story/metadata.json
Normal file
@ -0,0 +1,103 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom and HUD Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "HUD",
|
||||
"targets": [
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "faces",
|
||||
"texture_filename": "tex1_512x256_ff742945bb1f5cd6_14"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "faces",
|
||||
"texture_filename": "tex1_512x256_ff742945bb1f5cd6_14"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "text",
|
||||
"texture_filename": "tex1_512x512_09ca8ef639199fa9_0"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "text",
|
||||
"texture_filename": "tex1_512x512_09ca8ef639199fa9_0"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "text2",
|
||||
"texture_filename": "tex1_512x512_79e54f19ff7811de_0"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "text2",
|
||||
"texture_filename": "tex1_512x512_79e54f19ff7811de_0"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "text3",
|
||||
"texture_filename": "tex1_512x256_cb8c5f14fa63398f_0"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "text3",
|
||||
"texture_filename": "tex1_512x256_cb8c5f14fa63398f_0"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "large numbers",
|
||||
"texture_filename": "tex1_128x256_5fd7e727abd256a9_0"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "large numbers",
|
||||
"texture_filename": "tex1_128x256_5fd7e727abd256a9_0"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "life gradient",
|
||||
"texture_filename": "tex1_128x64_e3c9e617a9fdf915_14"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "life gradient",
|
||||
"texture_filename": "tex1_128x64_e3c9e617a9fdf915_14"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "life outline",
|
||||
"texture_filename": "tex1_128x64_deeeaa33ca3dc0f1_14"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "life outline",
|
||||
"texture_filename": "tex1_128x64_deeeaa33ca3dc0f1_14"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n56_80x56_6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n23_80x57_6"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n22_160x114_6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
167
Data/Sys/Load/GraphicMods/Little King's Story/metadata.json
Normal file
167
Data/Sys/Load/GraphicMods/Little King's Story/metadata.json
Normal file
@ -0,0 +1,167 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom and HUD definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"name": "HUD",
|
||||
"targets": [
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_96x96_7b5b0f693c1200ad_5"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_40x48_b510b4434b7de70c_5"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_96x96_633c30835459df0f_5"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_24x68_715518a00c14e148_5"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_32x80_f310048c1139815d_14"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_12x16_1e9016c61dfffb7a_14"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_12x16_459c7d7576547909_14"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_12x16_d1b77f0000ff337a_14"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_20x16_798aee4dc7001432_14"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_108x122_911fa08f1554752c_5"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_64x64_96894941f5454ead_3"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_8x8_0017d44adaf2291b_14"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_10x8_7a6a869d5553c4a0_14"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_16x16_49c191eaf4314e9d_14"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_18x16_472b403cdcfc31a3_3"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"texture_filename": "tex1_30x26_629956a45175e53a_14"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_96x96_7b5b0f693c1200ad_5"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_40x48_b510b4434b7de70c_5"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_96x96_633c30835459df0f_5"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_24x68_715518a00c14e148_5"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_32x80_f310048c1139815d_14"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_12x16_1e9016c61dfffb7a_14"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_12x16_459c7d7576547909_14"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_12x16_d1b77f0000ff337a_14"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_20x16_798aee4dc7001432_14"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_108x122_911fa08f1554752c_5"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_64x64_96894941f5454ead_3"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_8x8_0017d44adaf2291b_14"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_10x8_7a6a869d5553c4a0_14"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_16x16_49c191eaf4314e9d_14"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_18x16_472b403cdcfc31a3_3"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"texture_filename": "tex1_30x26_629956a45175e53a_14"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n1_320x240_6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
0
Data/Sys/Load/GraphicMods/Mario Kart Wii/RMC.txt
Normal file
0
Data/Sys/Load/GraphicMods/Mario Kart Wii/RMC.txt
Normal file
19
Data/Sys/Load/GraphicMods/Mario Kart Wii/metadata.json
Normal file
19
Data/Sys/Load/GraphicMods/Mario Kart Wii/metadata.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n49_152x114_6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n3_80x56_6"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n2_160x112_6"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n6_320x224_6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n000019_128x128_4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
0
Data/Sys/Load/GraphicMods/Okami/ROW.txt
Normal file
0
Data/Sys/Load/GraphicMods/Okami/ROW.txt
Normal file
19
Data/Sys/Load/GraphicMods/Okami/metadata.json
Normal file
19
Data/Sys/Load/GraphicMods/Okami/metadata.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n51_320x240_6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
103
Data/Sys/Load/GraphicMods/Rune Factory Frontier/metadata.json
Normal file
103
Data/Sys/Load/GraphicMods/Rune Factory Frontier/metadata.json
Normal file
@ -0,0 +1,103 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom and HUD Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "HUD",
|
||||
"targets": [
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "hp_rp",
|
||||
"texture_filename": "tex1_200x64_29bf40765535b389_fb4403f0539ecfc6_9"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "hp_rp",
|
||||
"texture_filename": "tex1_200x64_29bf40765535b389_fb4403f0539ecfc6_9"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "hp_gradient",
|
||||
"texture_filename": "tex1_96x8_491977b196c249d8_1feafe410943bfac_8"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "hp_gradient",
|
||||
"texture_filename": "tex1_96x8_491977b196c249d8_1feafe410943bfac_8"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "rp_gradient",
|
||||
"texture_filename": "tex1_96x8_cdcb5c030686767c_2c5a8138bfca228c_8"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "rp_gradient",
|
||||
"texture_filename": "tex1_96x8_cdcb5c030686767c_2c5a8138bfca228c_8"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "spring_season",
|
||||
"texture_filename": "tex1_256x40_30d99f26895bc811_02e626cce31a83ae_9"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "spring_season",
|
||||
"texture_filename": "tex1_256x40_30d99f26895bc811_02e626cce31a83ae_9"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "quick_pick_box",
|
||||
"texture_filename": "tex1_128x128_b87c102764a80c67_0488ebdbd87cfc9d_9"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "quick_pick_box",
|
||||
"texture_filename": "tex1_128x128_b87c102764a80c67_0488ebdbd87cfc9d_9"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "face",
|
||||
"texture_filename": "tex1_48x48_92405f9277895cd2_914f5a4762aa04ae_9"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "face",
|
||||
"texture_filename": "tex1_48x48_92405f9277895cd2_914f5a4762aa04ae_9"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "sunny_icon",
|
||||
"texture_filename": "tex1_24x24_3791555ba7e8186f_e82e2316ceba262d_9"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "sunny_icon",
|
||||
"texture_filename": "tex1_24x24_3791555ba7e8186f_e82e2316ceba262d_9"
|
||||
},
|
||||
{
|
||||
"type": "draw_started",
|
||||
"pretty_name": "text",
|
||||
"texture_filename": "tex1_256x256_83aa16840fa69ffb_0"
|
||||
},
|
||||
{
|
||||
"type": "projection",
|
||||
"value": "2d",
|
||||
"pretty_name": "text",
|
||||
"texture_filename": "tex1_256x256_83aa16840fa69ffb_0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
27
Data/Sys/Load/GraphicMods/Super Mario Galaxy 2/metadata.json
Normal file
27
Data/Sys/Load/GraphicMods/Super Mario Galaxy 2/metadata.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n000031_80x57_4"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n000033_160x114_4"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n000038_320x228_4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n001461_40x28_1"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n001460_80x56_1"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n001459_160x112_1"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n001458_320x224_1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
0
Data/Sys/Load/GraphicMods/The Conduit/RCJ.txt
Normal file
0
Data/Sys/Load/GraphicMods/The Conduit/RCJ.txt
Normal file
31
Data/Sys/Load/GraphicMods/The Conduit/metadata.json
Normal file
31
Data/Sys/Load/GraphicMods/The Conduit/metadata.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n000022_40x28_6"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n000021_80x56_6"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n000020_160x112_6"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n000025_320x224_6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n55_80x57_6"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n54_160x114_6"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
31
Data/Sys/Load/GraphicMods/Xenoblade Chronicles/metadata.json
Normal file
31
Data/Sys/Load/GraphicMods/Xenoblade Chronicles/metadata.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"title": "Bloom Texture Definitions",
|
||||
"author": "iwubcode"
|
||||
},
|
||||
"groups":
|
||||
[
|
||||
{
|
||||
"name": "Bloom",
|
||||
"targets": [
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n15_20x16_4"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n9_40x30_4"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n7_80x58_4"
|
||||
},
|
||||
{
|
||||
"type": "efb",
|
||||
"texture_filename": "efb1_n1_320x228_4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -72,6 +72,7 @@
|
||||
#define BACKUP_DIR "Backup"
|
||||
#define RESOURCEPACK_DIR "ResourcePacks"
|
||||
#define DYNAMICINPUT_DIR "DynamicInputTextures"
|
||||
#define GRAPHICSMOD_DIR "GraphicMods"
|
||||
|
||||
// This one is only used to remove it if it was present
|
||||
#define SHADERCACHE_LEGACY_DIR "ShaderCache"
|
||||
@ -137,3 +138,6 @@
|
||||
// Subdirs in Sys
|
||||
#define GC_SYS_DIR "GC"
|
||||
#define WII_SYS_DIR "Wii"
|
||||
|
||||
// Subdirs in Config
|
||||
#define GRAPHICSMOD_CONFIG_DIR "GraphicMods"
|
||||
|
@ -967,6 +967,7 @@ static void RebuildUserDirectories(unsigned int dir_index)
|
||||
s_user_paths[D_BACKUP_IDX] = s_user_paths[D_USER_IDX] + BACKUP_DIR DIR_SEP;
|
||||
s_user_paths[D_RESOURCEPACK_IDX] = s_user_paths[D_USER_IDX] + RESOURCEPACK_DIR DIR_SEP;
|
||||
s_user_paths[D_DYNAMICINPUT_IDX] = s_user_paths[D_LOAD_IDX] + DYNAMICINPUT_DIR DIR_SEP;
|
||||
s_user_paths[D_GRAPHICSMOD_IDX] = s_user_paths[D_LOAD_IDX] + GRAPHICSMOD_DIR DIR_SEP;
|
||||
s_user_paths[F_DOLPHINCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + DOLPHIN_CONFIG;
|
||||
s_user_paths[F_GCPADCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + GCPAD_CONFIG;
|
||||
s_user_paths[F_WIIPADCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + WIIPAD_CONFIG;
|
||||
@ -1045,6 +1046,7 @@ static void RebuildUserDirectories(unsigned int dir_index)
|
||||
s_user_paths[D_HIRESTEXTURES_IDX] = s_user_paths[D_LOAD_IDX] + HIRES_TEXTURES_DIR DIR_SEP;
|
||||
s_user_paths[D_RIIVOLUTION_IDX] = s_user_paths[D_LOAD_IDX] + RIIVOLUTION_DIR DIR_SEP;
|
||||
s_user_paths[D_DYNAMICINPUT_IDX] = s_user_paths[D_LOAD_IDX] + DYNAMICINPUT_DIR DIR_SEP;
|
||||
s_user_paths[D_GRAPHICSMOD_IDX] = s_user_paths[D_LOAD_IDX] + GRAPHICSMOD_DIR DIR_SEP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ enum
|
||||
D_BACKUP_IDX,
|
||||
D_RESOURCEPACK_IDX,
|
||||
D_DYNAMICINPUT_IDX,
|
||||
D_GRAPHICSMOD_IDX,
|
||||
D_GBAUSER_IDX,
|
||||
D_GBASAVES_IDX,
|
||||
FIRST_FILE_USER_PATH_IDX,
|
||||
|
@ -101,6 +101,8 @@ const Info<int> GFX_SW_DRAW_END{{System::GFX, "Settings", "SWDrawEnd"}, 100000};
|
||||
|
||||
const Info<bool> GFX_PREFER_GLES{{System::GFX, "Settings", "PreferGLES"}, false};
|
||||
|
||||
const Info<bool> GFX_MODS_ENABLE{{System::GFX, "Settings", "EnableMods"}, false};
|
||||
|
||||
// Graphics.Enhancements
|
||||
|
||||
const Info<bool> GFX_ENHANCE_FORCE_FILTERING{{System::GFX, "Enhancements", "ForceFiltering"},
|
||||
|
@ -82,6 +82,8 @@ extern const Info<int> GFX_SW_DRAW_END;
|
||||
|
||||
extern const Info<bool> GFX_PREFER_GLES;
|
||||
|
||||
extern const Info<bool> GFX_MODS_ENABLE;
|
||||
|
||||
// Graphics.Enhancements
|
||||
|
||||
extern const Info<bool> GFX_ENHANCE_FORCE_FILTERING;
|
||||
|
@ -631,6 +631,21 @@
|
||||
<ClInclude Include="VideoCommon\FreeLookCamera.h" />
|
||||
<ClInclude Include="VideoCommon\GeometryShaderGen.h" />
|
||||
<ClInclude Include="VideoCommon\GeometryShaderManager.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsMod.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModFeature.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModGroup.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsTarget.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsTargetGroup.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Constants.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\MoveAction.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\PrintAction.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\ScaleAction.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\SkipAction.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\FBInfo.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModAction.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModActionFactory.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModGroup.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModManager.h" />
|
||||
<ClInclude Include="VideoCommon\GXPipelineTypes.h" />
|
||||
<ClInclude Include="VideoCommon\HiresTextures.h" />
|
||||
<ClInclude Include="VideoCommon\ImageWrite.h" />
|
||||
@ -1209,6 +1224,18 @@
|
||||
<ClCompile Include="VideoCommon\FreeLookCamera.cpp" />
|
||||
<ClCompile Include="VideoCommon\GeometryShaderGen.cpp" />
|
||||
<ClCompile Include="VideoCommon\GeometryShaderManager.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsMod.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModFeature.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModGroup.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsTarget.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsTargetGroup.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\MoveAction.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\PrintAction.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\ScaleAction.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\SkipAction.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\FBInfo.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModActionFactory.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModManager.cpp" />
|
||||
<ClCompile Include="VideoCommon\HiresTextures_DDSLoader.cpp" />
|
||||
<ClCompile Include="VideoCommon\HiresTextures.cpp" />
|
||||
<ClCompile Include="VideoCommon\IndexGenerator.cpp" />
|
||||
|
@ -129,6 +129,10 @@ add_executable(dolphin-emu
|
||||
Config/Graphics/PostProcessingConfigWindow.h
|
||||
Config/Graphics/SoftwareRendererWidget.cpp
|
||||
Config/Graphics/SoftwareRendererWidget.h
|
||||
Config/GraphicsModListWidget.cpp
|
||||
Config/GraphicsModListWidget.h
|
||||
Config/GraphicsModWarningWidget.cpp
|
||||
Config/GraphicsModWarningWidget.h
|
||||
Config/InfoWidget.cpp
|
||||
Config/InfoWidget.h
|
||||
Config/LogConfigWidget.cpp
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "DolphinQt/Config/Graphics/GraphicsInteger.h"
|
||||
#include "DolphinQt/Config/Graphics/GraphicsWindow.h"
|
||||
#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
|
||||
#include "DolphinQt/QtUtils/SignalBlocking.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
@ -72,13 +73,15 @@ void AdvancedWidget::CreateWidgets()
|
||||
m_dump_xfb_target = new GraphicsBool(tr("Dump XFB Target"), Config::GFX_DUMP_XFB_TARGET);
|
||||
m_disable_vram_copies =
|
||||
new GraphicsBool(tr("Disable EFB VRAM Copies"), Config::GFX_HACK_DISABLE_COPY_TO_VRAM);
|
||||
m_enable_graphics_mods = new ToolTipCheckBox(tr("Enable Graphics Mods"));
|
||||
|
||||
utility_layout->addWidget(m_load_custom_textures, 0, 0);
|
||||
utility_layout->addWidget(m_prefetch_custom_textures, 0, 1);
|
||||
|
||||
utility_layout->addWidget(m_disable_vram_copies, 1, 0);
|
||||
utility_layout->addWidget(m_enable_graphics_mods, 1, 1);
|
||||
|
||||
utility_layout->addWidget(m_dump_efb_target, 1, 1);
|
||||
utility_layout->addWidget(m_dump_efb_target, 2, 0);
|
||||
utility_layout->addWidget(m_dump_xfb_target, 2, 1);
|
||||
|
||||
// Texture dumping
|
||||
@ -165,6 +168,7 @@ void AdvancedWidget::ConnectWidgets()
|
||||
connect(m_dump_use_ffv1, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings);
|
||||
connect(m_enable_prog_scan, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings);
|
||||
connect(m_dump_textures, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings);
|
||||
connect(m_enable_graphics_mods, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings);
|
||||
}
|
||||
|
||||
void AdvancedWidget::LoadSettings()
|
||||
@ -175,6 +179,8 @@ void AdvancedWidget::LoadSettings()
|
||||
m_enable_prog_scan->setChecked(Config::Get(Config::SYSCONF_PROGRESSIVE_SCAN));
|
||||
m_dump_mip_textures->setEnabled(Config::Get(Config::GFX_DUMP_TEXTURES));
|
||||
m_dump_base_textures->setEnabled(Config::Get(Config::GFX_DUMP_TEXTURES));
|
||||
|
||||
SignalBlocking(m_enable_graphics_mods)->setChecked(Settings::Instance().GetGraphicModsEnabled());
|
||||
}
|
||||
|
||||
void AdvancedWidget::SaveSettings()
|
||||
@ -185,6 +191,7 @@ void AdvancedWidget::SaveSettings()
|
||||
Config::SetBase(Config::SYSCONF_PROGRESSIVE_SCAN, m_enable_prog_scan->isChecked());
|
||||
m_dump_mip_textures->setEnabled(Config::Get(Config::GFX_DUMP_TEXTURES));
|
||||
m_dump_base_textures->setEnabled(Config::Get(Config::GFX_DUMP_TEXTURES));
|
||||
Settings::Instance().SetGraphicModsEnabled(m_enable_graphics_mods->isChecked());
|
||||
}
|
||||
|
||||
void AdvancedWidget::OnBackendChanged()
|
||||
@ -245,6 +252,9 @@ void AdvancedWidget::AddDescriptions()
|
||||
QT_TR_NOOP("Disables the VRAM copy of the EFB, forcing a round-trip to RAM. Inhibits all "
|
||||
"upscaling.<br><br><dolphin_emphasis>If unsure, leave this "
|
||||
"unchecked.</dolphin_emphasis>");
|
||||
static const char TR_LOAD_GRAPHICS_MODS_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Loads graphics mods from User/Load/GraphicsMods/.<br><br><dolphin_emphasis>If "
|
||||
"unsure, leave this unchecked.</dolphin_emphasis>");
|
||||
static const char TR_INTERNAL_RESOLUTION_FRAME_DUMPING_DESCRIPTION[] = QT_TR_NOOP(
|
||||
"Creates frame dumps and screenshots at the internal resolution of the renderer, rather than "
|
||||
"the size of the window it is displayed within.<br><br>If the aspect ratio is widescreen, "
|
||||
@ -316,6 +326,7 @@ void AdvancedWidget::AddDescriptions()
|
||||
m_dump_efb_target->SetDescription(tr(TR_DUMP_EFB_DESCRIPTION));
|
||||
m_dump_xfb_target->SetDescription(tr(TR_DUMP_XFB_DESCRIPTION));
|
||||
m_disable_vram_copies->SetDescription(tr(TR_DISABLE_VRAM_COPIES_DESCRIPTION));
|
||||
m_enable_graphics_mods->SetDescription(tr(TR_LOAD_GRAPHICS_MODS_DESCRIPTION));
|
||||
m_use_fullres_framedumps->SetDescription(tr(TR_INTERNAL_RESOLUTION_FRAME_DUMPING_DESCRIPTION));
|
||||
#ifdef HAVE_FFMPEG
|
||||
m_dump_use_ffv1->SetDescription(tr(TR_USE_FFV1_DESCRIPTION));
|
||||
|
@ -42,6 +42,7 @@ private:
|
||||
GraphicsBool* m_dump_xfb_target;
|
||||
GraphicsBool* m_disable_vram_copies;
|
||||
GraphicsBool* m_load_custom_textures;
|
||||
ToolTipCheckBox* m_enable_graphics_mods;
|
||||
|
||||
// Texture dumping
|
||||
GraphicsBool* m_dump_textures;
|
||||
|
279
Source/Core/DolphinQt/Config/GraphicsModListWidget.cpp
Normal file
279
Source/Core/DolphinQt/Config/GraphicsModListWidget.cpp
Normal file
@ -0,0 +1,279 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/Config/GraphicsModListWidget.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "DolphinQt/Config/GraphicsModWarningWidget.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
#include "UICommon/GameFile.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
GraphicsModListWidget::GraphicsModListWidget(const UICommon::GameFile& game)
|
||||
: m_game_id(game.GetGameID()), m_mod_group(m_game_id)
|
||||
{
|
||||
CalculateGameRunning(Core::GetState());
|
||||
if (m_loaded_game_is_running && g_Config.graphics_mod_config)
|
||||
{
|
||||
m_mod_group.SetChangeCount(g_Config.graphics_mod_config->GetChangeCount());
|
||||
}
|
||||
CreateWidgets();
|
||||
ConnectWidgets();
|
||||
|
||||
RefreshModList();
|
||||
OnModChanged(std::nullopt);
|
||||
}
|
||||
|
||||
GraphicsModListWidget::~GraphicsModListWidget()
|
||||
{
|
||||
if (m_needs_save)
|
||||
{
|
||||
m_mod_group.Save();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsModListWidget::CreateWidgets()
|
||||
{
|
||||
auto* main_v_layout = new QVBoxLayout(this);
|
||||
|
||||
auto* main_layout = new QHBoxLayout;
|
||||
|
||||
auto* left_v_layout = new QVBoxLayout;
|
||||
|
||||
m_mod_list = new QListWidget;
|
||||
m_mod_list->setSortingEnabled(false);
|
||||
m_mod_list->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectItems);
|
||||
m_mod_list->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
|
||||
m_mod_list->setSelectionRectVisible(true);
|
||||
m_mod_list->setDragDropMode(QAbstractItemView::InternalMove);
|
||||
|
||||
m_refresh = new QPushButton(tr("&Refresh List"));
|
||||
QHBoxLayout* hlayout = new QHBoxLayout;
|
||||
hlayout->addStretch();
|
||||
hlayout->addWidget(m_refresh);
|
||||
|
||||
left_v_layout->addWidget(m_mod_list);
|
||||
left_v_layout->addLayout(hlayout);
|
||||
|
||||
auto* right_v_layout = new QVBoxLayout;
|
||||
|
||||
m_selected_mod_name = new QLabel();
|
||||
right_v_layout->addWidget(m_selected_mod_name);
|
||||
|
||||
m_mod_meta_layout = new QVBoxLayout;
|
||||
right_v_layout->addLayout(m_mod_meta_layout);
|
||||
right_v_layout->addStretch();
|
||||
|
||||
main_layout->addLayout(left_v_layout);
|
||||
main_layout->addLayout(right_v_layout, 1);
|
||||
|
||||
m_warning = new GraphicsModWarningWidget(this);
|
||||
main_v_layout->addWidget(m_warning);
|
||||
main_v_layout->addLayout(main_layout);
|
||||
|
||||
setLayout(main_v_layout);
|
||||
}
|
||||
|
||||
void GraphicsModListWidget::ConnectWidgets()
|
||||
{
|
||||
connect(m_warning, &GraphicsModWarningWidget::GraphicsModEnableSettings, this,
|
||||
&GraphicsModListWidget::OpenGraphicsSettings);
|
||||
|
||||
connect(m_mod_list, &QListWidget::itemSelectionChanged, this,
|
||||
&GraphicsModListWidget::ModSelectionChanged);
|
||||
|
||||
connect(m_mod_list, &QListWidget::itemChanged, this, &GraphicsModListWidget::ModItemChanged);
|
||||
|
||||
connect(m_mod_list->model(), &QAbstractItemModel::rowsMoved, this,
|
||||
&GraphicsModListWidget::SaveModList);
|
||||
|
||||
connect(m_refresh, &QPushButton::clicked, this, &GraphicsModListWidget::RefreshModList);
|
||||
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
|
||||
&GraphicsModListWidget::CalculateGameRunning);
|
||||
}
|
||||
|
||||
void GraphicsModListWidget::RefreshModList()
|
||||
{
|
||||
m_mod_list->setCurrentItem(nullptr);
|
||||
m_mod_list->clear();
|
||||
|
||||
m_mod_group = GraphicsModGroupConfig(m_game_id);
|
||||
m_mod_group.Load();
|
||||
|
||||
std::set<std::string> groups;
|
||||
|
||||
for (const auto& mod : m_mod_group.GetMods())
|
||||
{
|
||||
if (mod.m_groups.empty())
|
||||
continue;
|
||||
|
||||
for (const auto& group : mod.m_groups)
|
||||
{
|
||||
groups.insert(group.m_name);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& mod : m_mod_group.GetMods())
|
||||
{
|
||||
// Group only mods shouldn't be shown
|
||||
if (mod.m_features.empty())
|
||||
continue;
|
||||
|
||||
// If the group doesn't exist in the available mod's features, skip
|
||||
if (std::none_of(mod.m_features.begin(), mod.m_features.end(),
|
||||
[&groups](const GraphicsModFeatureConfig& feature) {
|
||||
return groups.count(feature.m_group) == 1;
|
||||
}))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(mod.m_title));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setData(Qt::UserRole, QString::fromStdString(mod.GetAbsolutePath()));
|
||||
item->setCheckState(mod.m_enabled ? Qt::Checked : Qt::Unchecked);
|
||||
|
||||
m_mod_list->addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsModListWidget::ModSelectionChanged()
|
||||
{
|
||||
if (m_mod_list->currentItem() == nullptr)
|
||||
return;
|
||||
if (m_mod_list->count() == 0)
|
||||
return;
|
||||
const auto absolute_path = m_mod_list->currentItem()->data(Qt::UserRole).toString().toStdString();
|
||||
OnModChanged(absolute_path);
|
||||
}
|
||||
|
||||
void GraphicsModListWidget::ModItemChanged(QListWidgetItem* item)
|
||||
{
|
||||
const auto absolute_path = item->data(Qt::UserRole).toString();
|
||||
GraphicsModConfig* mod = m_mod_group.GetMod(absolute_path.toStdString());
|
||||
if (!mod)
|
||||
return;
|
||||
|
||||
const bool was_enabled = mod->m_enabled;
|
||||
const bool should_enable = item->checkState() == Qt::Checked;
|
||||
mod->m_enabled = should_enable;
|
||||
if (was_enabled == should_enable)
|
||||
return;
|
||||
|
||||
m_mod_group.SetChangeCount(m_mod_group.GetChangeCount() + 1);
|
||||
if (m_loaded_game_is_running)
|
||||
{
|
||||
g_Config.graphics_mod_config = m_mod_group;
|
||||
}
|
||||
m_needs_save = true;
|
||||
}
|
||||
|
||||
void GraphicsModListWidget::OnModChanged(std::optional<std::string> absolute_path)
|
||||
{
|
||||
ClearLayoutRecursively(m_mod_meta_layout);
|
||||
|
||||
adjustSize();
|
||||
|
||||
if (!absolute_path)
|
||||
{
|
||||
m_selected_mod_name->setText(QStringLiteral("No graphics mod selected"));
|
||||
m_selected_mod_name->setAlignment(Qt::AlignCenter);
|
||||
return;
|
||||
}
|
||||
|
||||
GraphicsModConfig* mod = m_mod_group.GetMod(*absolute_path);
|
||||
if (!mod)
|
||||
return;
|
||||
|
||||
m_selected_mod_name->setText(QString::fromStdString(mod->m_title));
|
||||
m_selected_mod_name->setAlignment(Qt::AlignLeft);
|
||||
QFont font = m_selected_mod_name->font();
|
||||
font.setWeight(QFont::Bold);
|
||||
m_selected_mod_name->setFont(font);
|
||||
|
||||
if (!mod->m_author.empty())
|
||||
{
|
||||
auto* author_label = new QLabel(tr("By: ") + QString::fromStdString(mod->m_author));
|
||||
m_mod_meta_layout->addWidget(author_label);
|
||||
}
|
||||
|
||||
if (!mod->m_description.empty())
|
||||
{
|
||||
auto* description_label =
|
||||
new QLabel(tr("Description: ") + QString::fromStdString(mod->m_description));
|
||||
m_mod_meta_layout->addWidget(description_label);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsModListWidget::SaveModList()
|
||||
{
|
||||
for (int i = 0; i < m_mod_list->count(); i++)
|
||||
{
|
||||
const auto absolute_path = m_mod_list->model()
|
||||
->data(m_mod_list->model()->index(i, 0), Qt::UserRole)
|
||||
.toString()
|
||||
.toStdString();
|
||||
m_mod_group.GetMod(absolute_path)->m_weight = i;
|
||||
}
|
||||
|
||||
if (m_loaded_game_is_running)
|
||||
{
|
||||
g_Config.graphics_mod_config = m_mod_group;
|
||||
}
|
||||
m_needs_save = true;
|
||||
}
|
||||
|
||||
void GraphicsModListWidget::ClearLayoutRecursively(QLayout* layout)
|
||||
{
|
||||
while (QLayoutItem* child = layout->takeAt(0))
|
||||
{
|
||||
if (child == nullptr)
|
||||
continue;
|
||||
|
||||
if (child->widget())
|
||||
{
|
||||
layout->removeWidget(child->widget());
|
||||
delete child->widget();
|
||||
}
|
||||
else if (child->layout())
|
||||
{
|
||||
ClearLayoutRecursively(child->layout());
|
||||
layout->removeItem(child);
|
||||
}
|
||||
else
|
||||
{
|
||||
layout->removeItem(child);
|
||||
}
|
||||
delete child;
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsModListWidget::SaveToDisk()
|
||||
{
|
||||
m_needs_save = false;
|
||||
m_mod_group.Save();
|
||||
}
|
||||
|
||||
const GraphicsModGroupConfig& GraphicsModListWidget::GetGraphicsModConfig() const
|
||||
{
|
||||
return m_mod_group;
|
||||
}
|
||||
|
||||
void GraphicsModListWidget::CalculateGameRunning(Core::State state)
|
||||
{
|
||||
m_loaded_game_is_running =
|
||||
state == Core::State::Running ? m_game_id == SConfig::GetInstance().GetGameID() : false;
|
||||
}
|
75
Source/Core/DolphinQt/Config/GraphicsModListWidget.h
Normal file
75
Source/Core/DolphinQt/Config/GraphicsModListWidget.h
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
|
||||
|
||||
class GraphicsModWarningWidget;
|
||||
class QHBoxLayout;
|
||||
class QLabel;
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
class QModelIndex;
|
||||
class QPushButton;
|
||||
class QVBoxLayout;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
enum class State;
|
||||
}
|
||||
|
||||
namespace UICommon
|
||||
{
|
||||
class GameFile;
|
||||
}
|
||||
|
||||
class GraphicsModListWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GraphicsModListWidget(const UICommon::GameFile& game);
|
||||
~GraphicsModListWidget();
|
||||
|
||||
void SaveToDisk();
|
||||
|
||||
const GraphicsModGroupConfig& GetGraphicsModConfig() const;
|
||||
|
||||
signals:
|
||||
void OpenGraphicsSettings();
|
||||
|
||||
private:
|
||||
void CreateWidgets();
|
||||
void ConnectWidgets();
|
||||
|
||||
void RefreshModList();
|
||||
void ModSelectionChanged();
|
||||
void ModItemChanged(QListWidgetItem* item);
|
||||
|
||||
void OnModChanged(std::optional<std::string> absolute_path);
|
||||
|
||||
void SaveModList();
|
||||
|
||||
void ClearLayoutRecursively(QLayout* layout);
|
||||
|
||||
void CalculateGameRunning(Core::State state);
|
||||
bool m_loaded_game_is_running = false;
|
||||
bool m_needs_save = false;
|
||||
|
||||
QListWidget* m_mod_list;
|
||||
|
||||
QLabel* m_selected_mod_name;
|
||||
QVBoxLayout* m_mod_meta_layout;
|
||||
|
||||
QPushButton* m_refresh;
|
||||
GraphicsModWarningWidget* m_warning;
|
||||
|
||||
std::string m_game_id;
|
||||
GraphicsModGroupConfig m_mod_group;
|
||||
};
|
70
Source/Core/DolphinQt/Config/GraphicsModWarningWidget.cpp
Normal file
70
Source/Core/DolphinQt/Config/GraphicsModWarningWidget.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "DolphinQt/Config/GraphicsModWarningWidget.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPixmap>
|
||||
#include <QPushButton>
|
||||
#include <QStyle>
|
||||
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
GraphicsModWarningWidget::GraphicsModWarningWidget(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
CreateWidgets();
|
||||
ConnectWidgets();
|
||||
|
||||
connect(&Settings::Instance(), &Settings::EnableGfxModsChanged, this,
|
||||
&GraphicsModWarningWidget::Update);
|
||||
Update();
|
||||
}
|
||||
|
||||
void GraphicsModWarningWidget::CreateWidgets()
|
||||
{
|
||||
auto* icon = new QLabel;
|
||||
|
||||
const auto size = 1.5 * QFontMetrics(font()).height();
|
||||
|
||||
QPixmap warning_icon = style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(size, size);
|
||||
|
||||
icon->setPixmap(warning_icon);
|
||||
|
||||
m_text = new QLabel();
|
||||
m_config_button = new QPushButton(tr("Configure Dolphin"));
|
||||
|
||||
m_config_button->setHidden(true);
|
||||
|
||||
auto* layout = new QHBoxLayout;
|
||||
|
||||
layout->addWidget(icon);
|
||||
layout->addWidget(m_text, 1);
|
||||
layout->addWidget(m_config_button);
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void GraphicsModWarningWidget::Update()
|
||||
{
|
||||
bool hide_widget = true;
|
||||
bool hide_config_button = true;
|
||||
|
||||
if (!Settings::Instance().GetGraphicModsEnabled())
|
||||
{
|
||||
hide_widget = false;
|
||||
hide_config_button = false;
|
||||
m_text->setText(tr("Graphics mods are currently disabled."));
|
||||
}
|
||||
|
||||
setHidden(hide_widget);
|
||||
m_config_button->setHidden(hide_config_button);
|
||||
}
|
||||
|
||||
void GraphicsModWarningWidget::ConnectWidgets()
|
||||
{
|
||||
connect(m_config_button, &QPushButton::clicked, this,
|
||||
&GraphicsModWarningWidget::GraphicsModEnableSettings);
|
||||
}
|
28
Source/Core/DolphinQt/Config/GraphicsModWarningWidget.h
Normal file
28
Source/Core/DolphinQt/Config/GraphicsModWarningWidget.h
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
|
||||
class GraphicsModWarningWidget final : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GraphicsModWarningWidget(QWidget* parent);
|
||||
|
||||
signals:
|
||||
void GraphicsModEnableSettings();
|
||||
|
||||
private:
|
||||
void CreateWidgets();
|
||||
void ConnectWidgets();
|
||||
|
||||
void Update();
|
||||
|
||||
QLabel* m_text;
|
||||
QPushButton* m_config_button;
|
||||
};
|
@ -17,12 +17,14 @@
|
||||
#include "DolphinQt/Config/FilesystemWidget.h"
|
||||
#include "DolphinQt/Config/GameConfigWidget.h"
|
||||
#include "DolphinQt/Config/GeckoCodeWidget.h"
|
||||
#include "DolphinQt/Config/GraphicsModListWidget.h"
|
||||
#include "DolphinQt/Config/InfoWidget.h"
|
||||
#include "DolphinQt/Config/PatchesWidget.h"
|
||||
#include "DolphinQt/Config/VerifyWidget.h"
|
||||
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
||||
|
||||
#include "UICommon/GameFile.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
PropertiesDialog::PropertiesDialog(QWidget* parent, const UICommon::GameFile& game)
|
||||
: QDialog(parent)
|
||||
@ -43,12 +45,16 @@ PropertiesDialog::PropertiesDialog(QWidget* parent, const UICommon::GameFile& ga
|
||||
new GeckoCodeWidget(game.GetGameID(), game.GetGameTDBID(), game.GetRevision());
|
||||
PatchesWidget* patches = new PatchesWidget(game);
|
||||
GameConfigWidget* game_config = new GameConfigWidget(game);
|
||||
GraphicsModListWidget* graphics_mod_list = new GraphicsModListWidget(game);
|
||||
|
||||
connect(gecko, &GeckoCodeWidget::OpenGeneralSettings, this,
|
||||
&PropertiesDialog::OpenGeneralSettings);
|
||||
|
||||
connect(ar, &ARCodeWidget::OpenGeneralSettings, this, &PropertiesDialog::OpenGeneralSettings);
|
||||
|
||||
connect(graphics_mod_list, &GraphicsModListWidget::OpenGraphicsSettings, this,
|
||||
&PropertiesDialog::OpenGraphicsSettings);
|
||||
|
||||
const int padding_width = 120;
|
||||
const int padding_height = 100;
|
||||
tab_widget->addTab(GetWrappedWidget(game_config, this, padding_width, padding_height),
|
||||
@ -57,6 +63,8 @@ PropertiesDialog::PropertiesDialog(QWidget* parent, const UICommon::GameFile& ga
|
||||
tab_widget->addTab(GetWrappedWidget(ar, this, padding_width, padding_height), tr("AR Codes"));
|
||||
tab_widget->addTab(GetWrappedWidget(gecko, this, padding_width, padding_height),
|
||||
tr("Gecko Codes"));
|
||||
tab_widget->addTab(GetWrappedWidget(graphics_mod_list, this, padding_width, padding_height),
|
||||
tr("Graphics Mods"));
|
||||
tab_widget->addTab(GetWrappedWidget(info, this, padding_width, padding_height), tr("Info"));
|
||||
|
||||
if (game.GetPlatform() != DiscIO::Platform::ELFOrDOL)
|
||||
@ -82,6 +90,8 @@ PropertiesDialog::PropertiesDialog(QWidget* parent, const UICommon::GameFile& ga
|
||||
QDialogButtonBox* close_box = new QDialogButtonBox(QDialogButtonBox::Close);
|
||||
|
||||
connect(close_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
connect(close_box, &QDialogButtonBox::rejected, graphics_mod_list,
|
||||
&GraphicsModListWidget::SaveToDisk);
|
||||
|
||||
layout->addWidget(close_box);
|
||||
|
||||
|
@ -18,4 +18,5 @@ public:
|
||||
|
||||
signals:
|
||||
void OpenGeneralSettings();
|
||||
void OpenGraphicsSettings();
|
||||
};
|
||||
|
@ -76,6 +76,8 @@
|
||||
<ClCompile Include="Config\Graphics\HacksWidget.cpp" />
|
||||
<ClCompile Include="Config\Graphics\PostProcessingConfigWindow.cpp" />
|
||||
<ClCompile Include="Config\Graphics\SoftwareRendererWidget.cpp" />
|
||||
<ClCompile Include="Config\GraphicsModListWidget.cpp" />
|
||||
<ClCompile Include="Config\GraphicsModWarningWidget.cpp" />
|
||||
<ClCompile Include="Config\InfoWidget.cpp" />
|
||||
<ClCompile Include="Config\LogConfigWidget.cpp" />
|
||||
<ClCompile Include="Config\LogWidget.cpp" />
|
||||
@ -270,6 +272,8 @@
|
||||
<QtMoc Include="Config\Graphics\HacksWidget.h" />
|
||||
<QtMoc Include="Config\Graphics\PostProcessingConfigWindow.h" />
|
||||
<QtMoc Include="Config\Graphics\SoftwareRendererWidget.h" />
|
||||
<QtMoc Include="Config\GraphicsModListWidget.h" />
|
||||
<QtMoc Include="Config\GraphicsModWarningWidget.h" />
|
||||
<QtMoc Include="Config\InfoWidget.h" />
|
||||
<QtMoc Include="Config\LogConfigWidget.h" />
|
||||
<QtMoc Include="Config\LogWidget.h" />
|
||||
|
@ -540,6 +540,8 @@ void GameList::OpenProperties()
|
||||
properties->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
|
||||
connect(properties, &PropertiesDialog::OpenGeneralSettings, this, &GameList::OpenGeneralSettings);
|
||||
connect(properties, &PropertiesDialog::OpenGraphicsSettings, this,
|
||||
&GameList::OpenGraphicsSettings);
|
||||
|
||||
properties->show();
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ signals:
|
||||
void NetPlayHost(const UICommon::GameFile& game);
|
||||
void SelectionChanged(std::shared_ptr<const UICommon::GameFile> game_file);
|
||||
void OpenGeneralSettings();
|
||||
void OpenGraphicsSettings();
|
||||
|
||||
private:
|
||||
void ShowHeaderContextMenu(const QPoint& pos);
|
||||
|
@ -672,6 +672,7 @@ void MainWindow::ConnectGameList()
|
||||
&MainWindow::ShowRiivolutionBootWidget);
|
||||
|
||||
connect(m_game_list, &GameList::OpenGeneralSettings, this, &MainWindow::ShowGeneralWindow);
|
||||
connect(m_game_list, &GameList::OpenGraphicsSettings, this, &MainWindow::ShowGraphicsWindow);
|
||||
}
|
||||
|
||||
void MainWindow::ConnectRenderWidget()
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/Config/GraphicsSettings.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
@ -351,6 +352,22 @@ bool Settings::IsKeepWindowOnTopEnabled() const
|
||||
return Config::Get(Config::MAIN_KEEP_WINDOW_ON_TOP);
|
||||
}
|
||||
|
||||
bool Settings::GetGraphicModsEnabled() const
|
||||
{
|
||||
return Config::Get(Config::GFX_MODS_ENABLE);
|
||||
}
|
||||
|
||||
void Settings::SetGraphicModsEnabled(bool enabled)
|
||||
{
|
||||
if (GetGraphicModsEnabled() == enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Config::SetBaseOrCurrent(Config::GFX_MODS_ENABLE, enabled);
|
||||
emit EnableGfxModsChanged(enabled);
|
||||
}
|
||||
|
||||
int Settings::GetVolume() const
|
||||
{
|
||||
return Config::Get(Config::MAIN_AUDIO_VOLUME);
|
||||
|
@ -108,6 +108,8 @@ public:
|
||||
bool GetLockCursor() const;
|
||||
void SetKeepWindowOnTop(bool top);
|
||||
bool IsKeepWindowOnTopEnabled() const;
|
||||
bool GetGraphicModsEnabled() const;
|
||||
void SetGraphicModsEnabled(bool enabled);
|
||||
|
||||
// Audio
|
||||
int GetVolume() const;
|
||||
@ -200,6 +202,7 @@ signals:
|
||||
void DevicesChanged();
|
||||
void SDCardInsertionChanged(bool inserted);
|
||||
void USBKeyboardConnectionChanged(bool connected);
|
||||
void EnableGfxModsChanged(bool enabled);
|
||||
|
||||
private:
|
||||
Settings();
|
||||
|
@ -69,6 +69,7 @@ static void CreateLoadPath(std::string path)
|
||||
File::SetUserPath(D_LOAD_IDX, std::move(path));
|
||||
File::CreateFullPath(File::GetUserPath(D_HIRESTEXTURES_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_RIIVOLUTION_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_GRAPHICSMOD_IDX));
|
||||
}
|
||||
|
||||
static void CreateResourcePackPath(std::string path)
|
||||
@ -188,6 +189,7 @@ void CreateDirectories()
|
||||
File::CreateFullPath(File::GetUserPath(D_CACHE_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_COVERCACHE_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_CONFIG_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_CONFIG_IDX) + GRAPHICSMOD_CONFIG_DIR DIR_SEP);
|
||||
File::CreateFullPath(File::GetUserPath(D_DUMPDSP_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_DUMPSSL_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_DUMPTEXTURES_IDX));
|
||||
@ -197,6 +199,7 @@ void CreateDirectories()
|
||||
File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + EUR_DIR DIR_SEP);
|
||||
File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + JAP_DIR DIR_SEP);
|
||||
File::CreateFullPath(File::GetUserPath(D_HIRESTEXTURES_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_GRAPHICSMOD_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_MAILLOGS_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_MAPS_IDX));
|
||||
File::CreateFullPath(File::GetUserPath(D_SCREENSHOTS_IDX));
|
||||
|
@ -39,6 +39,32 @@ add_library(videocommon
|
||||
GeometryShaderGen.h
|
||||
GeometryShaderManager.cpp
|
||||
GeometryShaderManager.h
|
||||
GraphicsModSystem/Config/GraphicsMod.cpp
|
||||
GraphicsModSystem/Config/GraphicsMod.h
|
||||
GraphicsModSystem/Config/GraphicsModFeature.cpp
|
||||
GraphicsModSystem/Config/GraphicsModFeature.h
|
||||
GraphicsModSystem/Config/GraphicsModGroup.cpp
|
||||
GraphicsModSystem/Config/GraphicsModGroup.h
|
||||
GraphicsModSystem/Config/GraphicsTarget.cpp
|
||||
GraphicsModSystem/Config/GraphicsTarget.h
|
||||
GraphicsModSystem/Config/GraphicsTargetGroup.cpp
|
||||
GraphicsModSystem/Config/GraphicsTargetGroup.h
|
||||
GraphicsModSystem/Constants.h
|
||||
GraphicsModSystem/Runtime/Actions/MoveAction.cpp
|
||||
GraphicsModSystem/Runtime/Actions/MoveAction.h
|
||||
GraphicsModSystem/Runtime/Actions/PrintAction.cpp
|
||||
GraphicsModSystem/Runtime/Actions/PrintAction.h
|
||||
GraphicsModSystem/Runtime/Actions/ScaleAction.cpp
|
||||
GraphicsModSystem/Runtime/Actions/ScaleAction.h
|
||||
GraphicsModSystem/Runtime/Actions/SkipAction.cpp
|
||||
GraphicsModSystem/Runtime/Actions/SkipAction.h
|
||||
GraphicsModSystem/Runtime/FBInfo.cpp
|
||||
GraphicsModSystem/Runtime/FBInfo.h
|
||||
GraphicsModSystem/Runtime/GraphicsModAction.h
|
||||
GraphicsModSystem/Runtime/GraphicsModActionFactory.cpp
|
||||
GraphicsModSystem/Runtime/GraphicsModActionFactory.h
|
||||
GraphicsModSystem/Runtime/GraphicsModManager.cpp
|
||||
GraphicsModSystem/Runtime/GraphicsModManager.h
|
||||
HiresTextures.cpp
|
||||
HiresTextures.h
|
||||
HiresTextures_DDSLoader.cpp
|
||||
|
291
Source/Core/VideoCommon/GraphicsModSystem/Config/GraphicsMod.cpp
Normal file
291
Source/Core/VideoCommon/GraphicsModSystem/Config/GraphicsMod.cpp
Normal file
@ -0,0 +1,291 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Constants.h"
|
||||
|
||||
std::optional<GraphicsModConfig> GraphicsModConfig::Create(const std::string& file_path,
|
||||
Source source)
|
||||
{
|
||||
std::string json_data;
|
||||
if (!File::ReadFileToString(file_path, json_data))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load graphics mod json file '{}'", file_path);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
picojson::value root;
|
||||
const auto error = picojson::parse(root, json_data);
|
||||
|
||||
if (!error.empty())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load graphics mod json file '{}' due to parse error: {}",
|
||||
file_path, error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
GraphicsModConfig result;
|
||||
if (!result.DeserializeFromConfig(root))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
result.m_source = source;
|
||||
if (source == Source::User)
|
||||
{
|
||||
const std::string base_path = File::GetUserPath(D_GRAPHICSMOD_IDX);
|
||||
if (base_path.size() > file_path.size())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load graphics mod json file '{}' due to it not matching the base path: {}",
|
||||
file_path, base_path);
|
||||
return std::nullopt;
|
||||
}
|
||||
result.m_relative_path = file_path.substr(base_path.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string base_path = File::GetSysDirectory() + DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR;
|
||||
if (base_path.size() > file_path.size())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load graphics mod json file '{}' due to it not matching the base path: {}",
|
||||
file_path, base_path);
|
||||
return std::nullopt;
|
||||
}
|
||||
result.m_relative_path = file_path.substr(base_path.size());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<GraphicsModConfig> GraphicsModConfig::Create(const picojson::object* obj)
|
||||
{
|
||||
if (!obj)
|
||||
return std::nullopt;
|
||||
|
||||
const auto source_it = obj->find("source");
|
||||
if (source_it == obj->end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
const std::string source_str = source_it->second.to_str();
|
||||
|
||||
const auto path_it = obj->find("path");
|
||||
if (path_it == obj->end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
const std::string relative_path = path_it->second.to_str();
|
||||
|
||||
if (source_str == "system")
|
||||
{
|
||||
return Create(fmt::format("{}{}{}", File::GetSysDirectory(), DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR,
|
||||
relative_path),
|
||||
Source::System);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Create(File::GetUserPath(D_GRAPHICSMOD_IDX) + relative_path, Source::User);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GraphicsModConfig::GetAbsolutePath() const
|
||||
{
|
||||
if (m_source == Source::System)
|
||||
{
|
||||
return WithUnifiedPathSeparators(fmt::format("{}{}{}", File::GetSysDirectory(),
|
||||
DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR, m_relative_path));
|
||||
}
|
||||
else
|
||||
{
|
||||
return WithUnifiedPathSeparators(File::GetUserPath(D_GRAPHICSMOD_IDX) + m_relative_path);
|
||||
}
|
||||
}
|
||||
|
||||
bool GraphicsModConfig::DeserializeFromConfig(const picojson::value& value)
|
||||
{
|
||||
const auto& meta = value.get("meta");
|
||||
if (meta.is<picojson::object>())
|
||||
{
|
||||
const auto& title = meta.get("title");
|
||||
if (title.is<std::string>())
|
||||
{
|
||||
m_title = title.to_str();
|
||||
}
|
||||
|
||||
const auto& author = meta.get("author");
|
||||
if (author.is<std::string>())
|
||||
{
|
||||
m_author = author.to_str();
|
||||
}
|
||||
|
||||
const auto& description = meta.get("description");
|
||||
if (description.is<std::string>())
|
||||
{
|
||||
m_description = description.to_str();
|
||||
}
|
||||
}
|
||||
|
||||
const auto& groups = value.get("groups");
|
||||
if (groups.is<picojson::array>())
|
||||
{
|
||||
for (const auto& group_val : groups.get<picojson::array>())
|
||||
{
|
||||
if (!group_val.is<picojson::object>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO, "Failed to load mod configuration file, specified group is not a json object");
|
||||
return false;
|
||||
}
|
||||
GraphicsTargetGroupConfig group;
|
||||
if (!group.DeserializeFromConfig(group_val.get<picojson::object>()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_groups.push_back(group);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& features = value.get("features");
|
||||
if (features.is<picojson::array>())
|
||||
{
|
||||
for (const auto& feature_val : features.get<picojson::array>())
|
||||
{
|
||||
if (!feature_val.is<picojson::object>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO, "Failed to load mod configuration file, specified feature is not a json object");
|
||||
return false;
|
||||
}
|
||||
GraphicsModFeatureConfig feature;
|
||||
if (!feature.DeserializeFromConfig(feature_val.get<picojson::object>()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_features.push_back(feature);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphicsModConfig::SerializeToProfile(picojson::object* obj) const
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
auto& json_obj = *obj;
|
||||
switch (m_source)
|
||||
{
|
||||
case Source::User:
|
||||
{
|
||||
json_obj["source"] = picojson::value{"user"};
|
||||
}
|
||||
break;
|
||||
case Source::System:
|
||||
{
|
||||
json_obj["source"] = picojson::value{"system"};
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
json_obj["path"] = picojson::value{m_relative_path};
|
||||
|
||||
picojson::array serialized_groups;
|
||||
for (const auto& group : m_groups)
|
||||
{
|
||||
picojson::object serialized_group;
|
||||
group.SerializeToProfile(&serialized_group);
|
||||
serialized_groups.push_back(picojson::value{serialized_group});
|
||||
}
|
||||
json_obj["groups"] = picojson::value{serialized_groups};
|
||||
|
||||
picojson::array serialized_features;
|
||||
for (const auto& feature : m_features)
|
||||
{
|
||||
picojson::object serialized_feature;
|
||||
feature.SerializeToProfile(&serialized_feature);
|
||||
serialized_features.push_back(picojson::value{serialized_feature});
|
||||
}
|
||||
json_obj["features"] = picojson::value{serialized_features};
|
||||
|
||||
json_obj["enabled"] = picojson::value{m_enabled};
|
||||
|
||||
json_obj["weight"] = picojson::value{static_cast<double>(m_weight)};
|
||||
}
|
||||
|
||||
void GraphicsModConfig::DeserializeFromProfile(const picojson::object& obj)
|
||||
{
|
||||
if (const auto it = obj.find("groups"); it != obj.end())
|
||||
{
|
||||
if (it->second.is<picojson::array>())
|
||||
{
|
||||
auto serialized_groups = it->second.get<picojson::array>();
|
||||
if (serialized_groups.size() != m_groups.size())
|
||||
return;
|
||||
|
||||
for (std::size_t i = 0; i < serialized_groups.size(); i++)
|
||||
{
|
||||
const auto& serialized_group_val = serialized_groups[i];
|
||||
if (serialized_group_val.is<picojson::object>())
|
||||
{
|
||||
const auto& serialized_group = serialized_group_val.get<picojson::object>();
|
||||
m_groups[i].DeserializeFromProfile(serialized_group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto it = obj.find("features"); it != obj.end())
|
||||
{
|
||||
if (it->second.is<picojson::array>())
|
||||
{
|
||||
auto serialized_features = it->second.get<picojson::array>();
|
||||
if (serialized_features.size() != m_features.size())
|
||||
return;
|
||||
|
||||
for (std::size_t i = 0; i < serialized_features.size(); i++)
|
||||
{
|
||||
const auto& serialized_feature_val = serialized_features[i];
|
||||
if (serialized_feature_val.is<picojson::object>())
|
||||
{
|
||||
const auto& serialized_feature = serialized_feature_val.get<picojson::object>();
|
||||
m_features[i].DeserializeFromProfile(serialized_feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto it = obj.find("enabled"); it != obj.end())
|
||||
{
|
||||
if (it->second.is<bool>())
|
||||
{
|
||||
m_enabled = it->second.get<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto it = obj.find("weight"); it != obj.end())
|
||||
{
|
||||
if (it->second.is<double>())
|
||||
{
|
||||
m_weight = static_cast<u16>(it->second.get<double>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GraphicsModConfig::operator<(const GraphicsModConfig& other) const
|
||||
{
|
||||
return m_weight < other.m_weight;
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModFeature.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsTargetGroup.h"
|
||||
|
||||
struct GraphicsModConfig
|
||||
{
|
||||
std::string m_title;
|
||||
std::string m_author;
|
||||
std::string m_description;
|
||||
bool m_enabled = false;
|
||||
u16 m_weight = 0;
|
||||
std::string m_relative_path;
|
||||
|
||||
enum class Source
|
||||
{
|
||||
User,
|
||||
System
|
||||
};
|
||||
Source m_source = Source::User;
|
||||
|
||||
std::vector<GraphicsTargetGroupConfig> m_groups;
|
||||
std::vector<GraphicsModFeatureConfig> m_features;
|
||||
|
||||
static std::optional<GraphicsModConfig> Create(const std::string& file, Source source);
|
||||
static std::optional<GraphicsModConfig> Create(const picojson::object* obj);
|
||||
|
||||
std::string GetAbsolutePath() const;
|
||||
|
||||
bool DeserializeFromConfig(const picojson::value& value);
|
||||
|
||||
void SerializeToProfile(picojson::object* value) const;
|
||||
void DeserializeFromProfile(const picojson::object& value);
|
||||
|
||||
bool operator<(const GraphicsModConfig& other) const;
|
||||
};
|
@ -0,0 +1,48 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModFeature.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
bool GraphicsModFeatureConfig::DeserializeFromConfig(const picojson::object& obj)
|
||||
{
|
||||
if (auto group_iter = obj.find("group"); group_iter != obj.end())
|
||||
{
|
||||
if (!group_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load mod configuration file, specified feature's group is not a string");
|
||||
return false;
|
||||
}
|
||||
m_group = group_iter->second.get<std::string>();
|
||||
}
|
||||
|
||||
if (auto action_iter = obj.find("action"); action_iter != obj.end())
|
||||
{
|
||||
if (!action_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load mod configuration file, specified feature's action is not a string");
|
||||
return false;
|
||||
}
|
||||
m_action = action_iter->second.get<std::string>();
|
||||
}
|
||||
|
||||
if (auto action_data_iter = obj.find("action_data"); action_data_iter != obj.end())
|
||||
{
|
||||
m_action_data = action_data_iter->second;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphicsModFeatureConfig::SerializeToProfile(picojson::object*) const
|
||||
{
|
||||
}
|
||||
|
||||
void GraphicsModFeatureConfig::DeserializeFromProfile(const picojson::object&)
|
||||
{
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
struct GraphicsModFeatureConfig
|
||||
{
|
||||
std::string m_group;
|
||||
std::string m_action;
|
||||
picojson::value m_action_data;
|
||||
|
||||
bool DeserializeFromConfig(const picojson::object& value);
|
||||
|
||||
void SerializeToProfile(picojson::object* value) const;
|
||||
void DeserializeFromProfile(const picojson::object& value);
|
||||
};
|
@ -0,0 +1,191 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/FileSearch.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Constants.h"
|
||||
#include "VideoCommon/HiresTextures.h"
|
||||
|
||||
GraphicsModGroupConfig::GraphicsModGroupConfig(const std::string& game_id) : m_game_id(game_id)
|
||||
{
|
||||
}
|
||||
|
||||
GraphicsModGroupConfig::~GraphicsModGroupConfig() = default;
|
||||
|
||||
GraphicsModGroupConfig::GraphicsModGroupConfig(const GraphicsModGroupConfig&) = default;
|
||||
|
||||
GraphicsModGroupConfig::GraphicsModGroupConfig(GraphicsModGroupConfig&&) = default;
|
||||
|
||||
GraphicsModGroupConfig& GraphicsModGroupConfig::operator=(const GraphicsModGroupConfig&) = default;
|
||||
|
||||
GraphicsModGroupConfig& GraphicsModGroupConfig::operator=(GraphicsModGroupConfig&&) = default;
|
||||
|
||||
void GraphicsModGroupConfig::Load()
|
||||
{
|
||||
const std::string file_path = GetPath();
|
||||
|
||||
std::set<std::string> known_paths;
|
||||
if (File::Exists(file_path))
|
||||
{
|
||||
std::string json_data;
|
||||
if (!File::ReadFileToString(file_path, json_data))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load graphics mod group json file '{}'", file_path);
|
||||
return;
|
||||
}
|
||||
|
||||
picojson::value root;
|
||||
const auto error = picojson::parse(root, json_data);
|
||||
|
||||
if (!error.empty())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load graphics mod group json file '{}' due to parse error: {}",
|
||||
file_path, error);
|
||||
return;
|
||||
}
|
||||
if (!root.is<picojson::object>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load graphics mod group json file '{}' due to root not being an object!",
|
||||
file_path);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& mods = root.get("mods");
|
||||
if (mods.is<picojson::array>())
|
||||
{
|
||||
for (const auto& mod_json : mods.get<picojson::array>())
|
||||
{
|
||||
if (mod_json.is<picojson::object>())
|
||||
{
|
||||
const auto& mod_json_obj = mod_json.get<picojson::object>();
|
||||
auto graphics_mod = GraphicsModConfig::Create(&mod_json_obj);
|
||||
if (!graphics_mod)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
graphics_mod->DeserializeFromProfile(mod_json_obj);
|
||||
|
||||
auto mod_full_path = graphics_mod->GetAbsolutePath();
|
||||
known_paths.insert(std::move(mod_full_path));
|
||||
m_graphics_mods.push_back(*graphics_mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto try_add_mod = [&known_paths, this](const std::string& dir,
|
||||
GraphicsModConfig::Source source) {
|
||||
auto file = dir + DIR_SEP + "metadata.json";
|
||||
UnifyPathSeparators(file);
|
||||
if (known_paths.find(file) != known_paths.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto mod = GraphicsModConfig::Create(file, source);
|
||||
if (mod)
|
||||
{
|
||||
m_graphics_mods.push_back(*mod);
|
||||
}
|
||||
};
|
||||
|
||||
const std::set<std::string> graphics_mod_user_directories =
|
||||
GetTextureDirectoriesWithGameId(File::GetUserPath(D_GRAPHICSMOD_IDX), m_game_id);
|
||||
|
||||
for (const auto& graphics_mod_directory : graphics_mod_user_directories)
|
||||
{
|
||||
try_add_mod(graphics_mod_directory, GraphicsModConfig::Source::User);
|
||||
}
|
||||
|
||||
const std::set<std::string> graphics_mod_system_directories = GetTextureDirectoriesWithGameId(
|
||||
File::GetSysDirectory() + DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR, m_game_id);
|
||||
|
||||
for (const auto& graphics_mod_directory : graphics_mod_system_directories)
|
||||
{
|
||||
try_add_mod(graphics_mod_directory, GraphicsModConfig::Source::System);
|
||||
}
|
||||
|
||||
std::sort(m_graphics_mods.begin(), m_graphics_mods.end());
|
||||
for (auto& mod : m_graphics_mods)
|
||||
{
|
||||
m_path_to_graphics_mod[mod.GetAbsolutePath()] = &mod;
|
||||
}
|
||||
|
||||
m_change_count++;
|
||||
}
|
||||
|
||||
void GraphicsModGroupConfig::Save() const
|
||||
{
|
||||
const std::string file_path = GetPath();
|
||||
std::ofstream json_stream;
|
||||
File::OpenFStream(json_stream, file_path, std::ios_base::out);
|
||||
if (!json_stream.is_open())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to open graphics mod group json file '{}' for writing", file_path);
|
||||
return;
|
||||
}
|
||||
|
||||
picojson::object serialized_root;
|
||||
picojson::array serialized_mods;
|
||||
for (const auto& mod : m_graphics_mods)
|
||||
{
|
||||
picojson::object serialized_mod;
|
||||
mod.SerializeToProfile(&serialized_mod);
|
||||
serialized_mods.push_back(picojson::value{serialized_mod});
|
||||
}
|
||||
serialized_root["mods"] = picojson::value{serialized_mods};
|
||||
|
||||
const auto output = picojson::value{serialized_root}.serialize(true);
|
||||
json_stream << output;
|
||||
}
|
||||
|
||||
void GraphicsModGroupConfig::SetChangeCount(u32 change_count)
|
||||
{
|
||||
m_change_count = change_count;
|
||||
}
|
||||
|
||||
u32 GraphicsModGroupConfig::GetChangeCount() const
|
||||
{
|
||||
return m_change_count;
|
||||
}
|
||||
|
||||
const std::vector<GraphicsModConfig>& GraphicsModGroupConfig::GetMods() const
|
||||
{
|
||||
return m_graphics_mods;
|
||||
}
|
||||
|
||||
GraphicsModConfig* GraphicsModGroupConfig::GetMod(const std::string& absolute_path) const
|
||||
{
|
||||
if (const auto iter = m_path_to_graphics_mod.find(absolute_path);
|
||||
iter != m_path_to_graphics_mod.end())
|
||||
{
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::string& GraphicsModGroupConfig::GetGameID() const
|
||||
{
|
||||
return m_game_id;
|
||||
}
|
||||
|
||||
std::string GraphicsModGroupConfig::GetPath() const
|
||||
{
|
||||
const std::string game_mod_root = File::GetUserPath(D_CONFIG_IDX) + GRAPHICSMOD_CONFIG_DIR;
|
||||
return fmt::format("{}/{}.json", game_mod_root, m_game_id);
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
struct GraphicsModConfig;
|
||||
|
||||
class GraphicsModGroupConfig
|
||||
{
|
||||
public:
|
||||
explicit GraphicsModGroupConfig(const std::string& game_id);
|
||||
~GraphicsModGroupConfig();
|
||||
|
||||
GraphicsModGroupConfig(const GraphicsModGroupConfig&);
|
||||
GraphicsModGroupConfig(GraphicsModGroupConfig&&);
|
||||
|
||||
GraphicsModGroupConfig& operator=(const GraphicsModGroupConfig&);
|
||||
GraphicsModGroupConfig& operator=(GraphicsModGroupConfig&&);
|
||||
|
||||
void Load();
|
||||
void Save() const;
|
||||
|
||||
void SetChangeCount(u32 change_count);
|
||||
u32 GetChangeCount() const;
|
||||
|
||||
const std::vector<GraphicsModConfig>& GetMods() const;
|
||||
|
||||
GraphicsModConfig* GetMod(const std::string& absolute_path) const;
|
||||
|
||||
const std::string& GetGameID() const;
|
||||
|
||||
private:
|
||||
std::string GetPath() const;
|
||||
std::string m_game_id;
|
||||
std::vector<GraphicsModConfig> m_graphics_mods;
|
||||
std::map<std::string, GraphicsModConfig*> m_path_to_graphics_mod;
|
||||
u32 m_change_count = 0;
|
||||
};
|
@ -0,0 +1,254 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsTarget.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "VideoCommon/TextureCacheBase.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T, std::enable_if_t<std::is_base_of_v<FBTarget, T>, int> = 0>
|
||||
std::optional<T> DeserializeFBTargetFromConfig(const picojson::object& obj, std::string_view prefix)
|
||||
{
|
||||
T fb;
|
||||
const auto texture_filename_iter = obj.find("texture_filename");
|
||||
if (texture_filename_iter == obj.end())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load mod configuration file, option 'texture_filename' not found");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!texture_filename_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load mod configuration file, option 'texture_filename' is not a string type");
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto texture_filename = texture_filename_iter->second.get<std::string>();
|
||||
const auto texture_filename_without_prefix = texture_filename.substr(prefix.size() + 1);
|
||||
const auto split_str_values = SplitString(texture_filename_without_prefix, '_');
|
||||
if (split_str_values.size() == 1)
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is not valid");
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto split_width_height_values = SplitString(texture_filename_without_prefix, 'x');
|
||||
if (split_width_height_values.size() != 2)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
|
||||
"not valid, width and height separator found more matches than expected");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::size_t width_underscore_pos = split_width_height_values[0].find_last_of('_');
|
||||
std::string width_str;
|
||||
if (width_underscore_pos == std::string::npos)
|
||||
{
|
||||
width_str = split_width_height_values[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
width_str = split_width_height_values[0].substr(width_underscore_pos + 1);
|
||||
}
|
||||
if (!TryParse(width_str, &fb.m_width))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
|
||||
"not valid, width not a number");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::size_t height_underscore_pos = split_width_height_values[1].find_first_of('_');
|
||||
if (height_underscore_pos == std::string::npos ||
|
||||
height_underscore_pos == split_width_height_values[1].size() - 1)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
|
||||
"not valid, underscore after height is missing or incomplete");
|
||||
return std::nullopt;
|
||||
}
|
||||
const std::string height_str = split_width_height_values[1].substr(0, height_underscore_pos);
|
||||
if (!TryParse(height_str, &fb.m_height))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
|
||||
"not valid, height not a number");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::size_t format_underscore_pos =
|
||||
split_width_height_values[1].find_first_of('_', height_underscore_pos + 1);
|
||||
|
||||
std::string format_str;
|
||||
if (format_underscore_pos == std::string::npos)
|
||||
{
|
||||
format_str = split_width_height_values[1].substr(height_underscore_pos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
format_str = split_width_height_values[1].substr(
|
||||
height_underscore_pos + 1, (format_underscore_pos - height_underscore_pos) - 1);
|
||||
}
|
||||
u32 format;
|
||||
if (!TryParse(format_str, &format))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
|
||||
"not valid, texture format is not a number");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!IsValidTextureFormat(static_cast<TextureFormat>(format)))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
|
||||
"not valid, texture format is not valid");
|
||||
return std::nullopt;
|
||||
}
|
||||
fb.m_texture_format = static_cast<TextureFormat>(format);
|
||||
|
||||
return fb;
|
||||
}
|
||||
std::optional<std::string> ExtractTextureFilenameForConfig(const picojson::object& obj)
|
||||
{
|
||||
const auto texture_filename_iter = obj.find("texture_filename");
|
||||
if (texture_filename_iter == obj.end())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load mod configuration file, option 'texture_filename' not found");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!texture_filename_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load mod configuration file, option 'texture_filename' is not a string type");
|
||||
return std::nullopt;
|
||||
}
|
||||
std::string texture_info = texture_filename_iter->second.get<std::string>();
|
||||
if (texture_info.find(EFB_DUMP_PREFIX) != std::string::npos)
|
||||
{
|
||||
const auto letter_c_pos = texture_info.find_first_of('n');
|
||||
if (letter_c_pos == std::string::npos)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' "
|
||||
"is an efb without a count");
|
||||
return std::nullopt;
|
||||
}
|
||||
texture_info =
|
||||
texture_info.substr(letter_c_pos - 1, texture_info.find_first_of("_", letter_c_pos));
|
||||
}
|
||||
else if (texture_info.find(XFB_DUMP_PREFIX) != std::string::npos)
|
||||
{
|
||||
const auto letter_c_pos = texture_info.find_first_of('n');
|
||||
if (letter_c_pos == std::string::npos)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' "
|
||||
"is an xfb without a count");
|
||||
return std::nullopt;
|
||||
}
|
||||
texture_info =
|
||||
texture_info.substr(letter_c_pos - 1, texture_info.find_first_of("_", letter_c_pos));
|
||||
}
|
||||
return texture_info;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<GraphicsTargetConfig> DeserializeTargetFromConfig(const picojson::object& obj)
|
||||
{
|
||||
const auto type_iter = obj.find("type");
|
||||
if (type_iter == obj.end())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, option 'type' not found");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!type_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load mod configuration file, option 'type' is not a string type");
|
||||
return std::nullopt;
|
||||
}
|
||||
const std::string& type = type_iter->second.get<std::string>();
|
||||
if (type == "draw_started")
|
||||
{
|
||||
std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj);
|
||||
if (!texture_info.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
DrawStartedTextureTarget target;
|
||||
target.m_texture_info_string = texture_info.value();
|
||||
return target;
|
||||
}
|
||||
else if (type == "load_texture")
|
||||
{
|
||||
std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj);
|
||||
if (!texture_info.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
LoadTextureTarget target;
|
||||
target.m_texture_info_string = texture_info.value();
|
||||
return target;
|
||||
}
|
||||
else if (type == "efb")
|
||||
{
|
||||
return DeserializeFBTargetFromConfig<EFBTarget>(obj, EFB_DUMP_PREFIX);
|
||||
}
|
||||
else if (type == "xfb")
|
||||
{
|
||||
return DeserializeFBTargetFromConfig<XFBTarget>(obj, EFB_DUMP_PREFIX);
|
||||
}
|
||||
else if (type == "projection")
|
||||
{
|
||||
ProjectionTarget target;
|
||||
const auto texture_iter = obj.find("texture_filename");
|
||||
if (texture_iter != obj.end())
|
||||
{
|
||||
std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj);
|
||||
if (!texture_info.has_value())
|
||||
return std::nullopt;
|
||||
target.m_texture_info_string = texture_info;
|
||||
}
|
||||
const auto value_iter = obj.find("value");
|
||||
if (value_iter == obj.end())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, option 'value' not found");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!value_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load mod configuration file, option 'value' is not a string type");
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto& value_str = value_iter->second.get<std::string>();
|
||||
if (value_str == "2d")
|
||||
{
|
||||
target.m_projection_type = ProjectionType::Orthographic;
|
||||
}
|
||||
else if (value_str == "3d")
|
||||
{
|
||||
target.m_projection_type = ProjectionType::Perspective;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, option 'value' is not a valid "
|
||||
"value, valid values are: 2d, 3d");
|
||||
return std::nullopt;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load mod configuration file, option 'type' is not a valid value");
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void SerializeTargetToProfile(picojson::object*, const GraphicsTargetConfig&)
|
||||
{
|
||||
// Added for consistency, no functionality as of now
|
||||
}
|
||||
|
||||
void DeserializeTargetFromProfile(const picojson::object&, GraphicsTargetConfig*)
|
||||
{
|
||||
// Added for consistency, no functionality as of now
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoCommon/TextureDecoder.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
|
||||
struct TextureTarget
|
||||
{
|
||||
std::string m_texture_info_string;
|
||||
};
|
||||
|
||||
struct DrawStartedTextureTarget final : public TextureTarget
|
||||
{
|
||||
};
|
||||
|
||||
struct LoadTextureTarget final : public TextureTarget
|
||||
{
|
||||
};
|
||||
|
||||
struct FBTarget
|
||||
{
|
||||
u32 m_height = 0;
|
||||
u32 m_width = 0;
|
||||
TextureFormat m_texture_format = TextureFormat::I4;
|
||||
};
|
||||
|
||||
struct EFBTarget final : public FBTarget
|
||||
{
|
||||
};
|
||||
|
||||
struct XFBTarget final : public FBTarget
|
||||
{
|
||||
};
|
||||
|
||||
struct ProjectionTarget
|
||||
{
|
||||
std::optional<std::string> m_texture_info_string;
|
||||
ProjectionType m_projection_type = ProjectionType::Perspective;
|
||||
};
|
||||
|
||||
using GraphicsTargetConfig = std::variant<DrawStartedTextureTarget, LoadTextureTarget, EFBTarget,
|
||||
XFBTarget, ProjectionTarget>;
|
||||
|
||||
std::optional<GraphicsTargetConfig> DeserializeTargetFromConfig(const picojson::object& obj);
|
||||
|
||||
void SerializeTargetToProfile(picojson::object* obj, const GraphicsTargetConfig& target);
|
||||
void DeserializeTargetFromProfile(const picojson::object& obj, GraphicsTargetConfig* target);
|
@ -0,0 +1,88 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsTargetGroup.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
bool GraphicsTargetGroupConfig::DeserializeFromConfig(const picojson::object& obj)
|
||||
{
|
||||
if (auto name_iter = obj.find("name"); name_iter != obj.end())
|
||||
{
|
||||
if (!name_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO, "Failed to load mod configuration file, specified group's name is not a string");
|
||||
return false;
|
||||
}
|
||||
m_name = name_iter->second.get<std::string>();
|
||||
}
|
||||
|
||||
if (auto targets_iter = obj.find("targets"); targets_iter != obj.end())
|
||||
{
|
||||
if (!targets_iter->second.is<picojson::array>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load mod configuration file, specified group's targets is not an array");
|
||||
return false;
|
||||
}
|
||||
for (const auto& target_val : targets_iter->second.get<picojson::array>())
|
||||
{
|
||||
if (!target_val.is<picojson::object>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load shader configuration file, specified target is not a json object");
|
||||
return false;
|
||||
}
|
||||
const auto target = DeserializeTargetFromConfig(target_val.get<picojson::object>());
|
||||
if (!target)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_targets.push_back(*target);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphicsTargetGroupConfig::SerializeToProfile(picojson::object* obj) const
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
auto& json_obj = *obj;
|
||||
picojson::array serialized_targets;
|
||||
for (const auto& target : m_targets)
|
||||
{
|
||||
picojson::object serialized_target;
|
||||
SerializeTargetToProfile(&serialized_target, target);
|
||||
serialized_targets.push_back(picojson::value{serialized_target});
|
||||
}
|
||||
json_obj["targets"] = picojson::value{serialized_targets};
|
||||
}
|
||||
|
||||
void GraphicsTargetGroupConfig::DeserializeFromProfile(const picojson::object& obj)
|
||||
{
|
||||
if (const auto it = obj.find("targets"); it != obj.end())
|
||||
{
|
||||
if (it->second.is<picojson::array>())
|
||||
{
|
||||
auto serialized_targets = it->second.get<picojson::array>();
|
||||
if (serialized_targets.size() != m_targets.size())
|
||||
return;
|
||||
|
||||
for (std::size_t i = 0; i < serialized_targets.size(); i++)
|
||||
{
|
||||
const auto& serialized_target_val = serialized_targets[i];
|
||||
if (serialized_target_val.is<picojson::object>())
|
||||
{
|
||||
const auto& serialized_target = serialized_target_val.get<picojson::object>();
|
||||
DeserializeTargetFromProfile(serialized_target, &m_targets[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsTarget.h"
|
||||
|
||||
struct GraphicsTargetGroupConfig
|
||||
{
|
||||
std::string m_name;
|
||||
std::vector<GraphicsTargetConfig> m_targets;
|
||||
|
||||
bool DeserializeFromConfig(const picojson::object& obj);
|
||||
|
||||
void SerializeToProfile(picojson::object* obj) const;
|
||||
void DeserializeFromProfile(const picojson::object& obj);
|
||||
};
|
11
Source/Core/VideoCommon/GraphicsModSystem/Constants.h
Normal file
11
Source/Core/VideoCommon/GraphicsModSystem/Constants.h
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonPaths.h"
|
||||
|
||||
static const inline std::string DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR =
|
||||
LOAD_DIR DIR_SEP GRAPHICSMOD_DIR DIR_SEP;
|
@ -0,0 +1,47 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/MoveAction.h"
|
||||
|
||||
std::unique_ptr<MoveAction> MoveAction::Create(const picojson::value& json_data)
|
||||
{
|
||||
Common::Vec3 position_offset;
|
||||
const auto& x = json_data.get("X");
|
||||
if (x.is<double>())
|
||||
{
|
||||
position_offset.x = static_cast<float>(x.get<double>());
|
||||
}
|
||||
|
||||
const auto& y = json_data.get("Y");
|
||||
if (y.is<double>())
|
||||
{
|
||||
position_offset.y = static_cast<float>(y.get<double>());
|
||||
}
|
||||
|
||||
const auto& z = json_data.get("Z");
|
||||
if (z.is<double>())
|
||||
{
|
||||
position_offset.z = static_cast<float>(z.get<double>());
|
||||
}
|
||||
return std::make_unique<MoveAction>(position_offset);
|
||||
}
|
||||
|
||||
MoveAction::MoveAction(Common::Vec3 position_offset) : m_position_offset(position_offset)
|
||||
{
|
||||
}
|
||||
|
||||
void MoveAction::OnProjection(Common::Matrix44* matrix)
|
||||
{
|
||||
if (!matrix)
|
||||
return;
|
||||
|
||||
*matrix *= Common::Matrix44::Translate(m_position_offset);
|
||||
}
|
||||
|
||||
void MoveAction::OnProjectionAndTexture(Common::Matrix44* matrix)
|
||||
{
|
||||
if (!matrix)
|
||||
return;
|
||||
|
||||
*matrix *= Common::Matrix44::Translate(m_position_offset);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
|
||||
|
||||
class MoveAction final : public GraphicsModAction
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<MoveAction> Create(const picojson::value& json_data);
|
||||
explicit MoveAction(Common::Vec3 position_offset);
|
||||
void OnProjection(Common::Matrix44* matrix) override;
|
||||
void OnProjectionAndTexture(Common::Matrix44* matrix) override;
|
||||
|
||||
private:
|
||||
Common::Vec3 m_position_offset;
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
void PrintAction::OnDrawStarted(bool*)
|
||||
{
|
||||
INFO_LOG_FMT(VIDEO, "OnDrawStarted Called");
|
||||
}
|
||||
|
||||
void PrintAction::OnEFB(bool*, u32 texture_width, u32 texture_height, u32* scaled_width,
|
||||
u32* scaled_height)
|
||||
{
|
||||
if (!scaled_width || !scaled_height)
|
||||
return;
|
||||
|
||||
INFO_LOG_FMT(VIDEO, "OnEFB Called. Original [{}, {}], Scaled [{}, {}]", texture_width,
|
||||
texture_height, *scaled_width, *scaled_height);
|
||||
}
|
||||
|
||||
void PrintAction::OnProjection(Common::Matrix44*)
|
||||
{
|
||||
INFO_LOG_FMT(VIDEO, "OnProjection Called");
|
||||
}
|
||||
|
||||
void PrintAction::OnProjectionAndTexture(Common::Matrix44*)
|
||||
{
|
||||
INFO_LOG_FMT(VIDEO, "OnProjectionAndTexture Called");
|
||||
}
|
||||
|
||||
void PrintAction::OnTextureLoad()
|
||||
{
|
||||
INFO_LOG_FMT(VIDEO, "OnTextureLoad Called");
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
|
||||
|
||||
class PrintAction final : public GraphicsModAction
|
||||
{
|
||||
public:
|
||||
void OnDrawStarted(bool* skip) override;
|
||||
void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width,
|
||||
u32* scaled_height) override;
|
||||
void OnProjection(Common::Matrix44* matrix) override;
|
||||
void OnProjectionAndTexture(Common::Matrix44* matrix) override;
|
||||
void OnTextureLoad() override;
|
||||
};
|
@ -0,0 +1,59 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.h"
|
||||
|
||||
std::unique_ptr<ScaleAction> ScaleAction::Create(const picojson::value& json_data)
|
||||
{
|
||||
Common::Vec3 scale;
|
||||
const auto& x = json_data.get("X");
|
||||
if (x.is<double>())
|
||||
{
|
||||
scale.x = static_cast<float>(x.get<double>());
|
||||
}
|
||||
|
||||
const auto& y = json_data.get("Y");
|
||||
if (y.is<double>())
|
||||
{
|
||||
scale.y = static_cast<float>(y.get<double>());
|
||||
}
|
||||
|
||||
const auto& z = json_data.get("Z");
|
||||
if (z.is<double>())
|
||||
{
|
||||
scale.z = static_cast<float>(z.get<double>());
|
||||
}
|
||||
return std::make_unique<ScaleAction>(scale);
|
||||
}
|
||||
|
||||
ScaleAction::ScaleAction(Common::Vec3 scale) : m_scale(scale)
|
||||
{
|
||||
}
|
||||
|
||||
void ScaleAction::OnEFB(bool*, u32 texture_width, u32 texture_height, u32* scaled_width,
|
||||
u32* scaled_height)
|
||||
{
|
||||
if (scaled_width && m_scale.x > 0)
|
||||
*scaled_width = texture_width * m_scale.x;
|
||||
|
||||
if (scaled_height && m_scale.y > 0)
|
||||
*scaled_height = texture_height * m_scale.y;
|
||||
}
|
||||
|
||||
void ScaleAction::OnProjection(Common::Matrix44* matrix)
|
||||
{
|
||||
if (!matrix)
|
||||
return;
|
||||
auto& the_matrix = *matrix;
|
||||
the_matrix.data[0] = the_matrix.data[0] * m_scale.x;
|
||||
the_matrix.data[5] = the_matrix.data[5] * m_scale.y;
|
||||
}
|
||||
|
||||
void ScaleAction::OnProjectionAndTexture(Common::Matrix44* matrix)
|
||||
{
|
||||
if (!matrix)
|
||||
return;
|
||||
auto& the_matrix = *matrix;
|
||||
the_matrix.data[0] = the_matrix.data[0] * m_scale.x;
|
||||
the_matrix.data[5] = the_matrix.data[5] * m_scale.y;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
|
||||
|
||||
class ScaleAction final : public GraphicsModAction
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<ScaleAction> Create(const picojson::value& json_data);
|
||||
explicit ScaleAction(Common::Vec3 scale);
|
||||
void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width,
|
||||
u32* scaled_height) override;
|
||||
void OnProjection(Common::Matrix44* matrix) override;
|
||||
void OnProjectionAndTexture(Common::Matrix44* matrix) override;
|
||||
|
||||
private:
|
||||
Common::Vec3 m_scale;
|
||||
};
|
@ -0,0 +1,20 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.h"
|
||||
|
||||
void SkipAction::OnDrawStarted(bool* skip)
|
||||
{
|
||||
if (!skip)
|
||||
return;
|
||||
|
||||
*skip = true;
|
||||
}
|
||||
|
||||
void SkipAction::OnEFB(bool* skip, u32, u32, u32*, u32*)
|
||||
{
|
||||
if (!skip)
|
||||
return;
|
||||
|
||||
*skip = true;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
|
||||
|
||||
class SkipAction final : public GraphicsModAction
|
||||
{
|
||||
public:
|
||||
void OnDrawStarted(bool* skip) override;
|
||||
void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width,
|
||||
u32* scaled_height) override;
|
||||
};
|
22
Source/Core/VideoCommon/GraphicsModSystem/Runtime/FBInfo.cpp
Normal file
22
Source/Core/VideoCommon/GraphicsModSystem/Runtime/FBInfo.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/FBInfo.h"
|
||||
|
||||
#include "Common/Hash.h"
|
||||
|
||||
u32 FBInfo::CalculateHash() const
|
||||
{
|
||||
return Common::HashAdler32(reinterpret_cast<const u8*>(this), sizeof(FBInfo));
|
||||
}
|
||||
|
||||
bool FBInfo::operator==(const FBInfo& other) const
|
||||
{
|
||||
return m_height == other.m_height && m_width == other.m_width &&
|
||||
m_texture_format == other.m_texture_format;
|
||||
}
|
||||
|
||||
bool FBInfo::operator!=(const FBInfo& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
25
Source/Core/VideoCommon/GraphicsModSystem/Runtime/FBInfo.h
Normal file
25
Source/Core/VideoCommon/GraphicsModSystem/Runtime/FBInfo.h
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoCommon/TextureDecoder.h"
|
||||
|
||||
struct FBInfo
|
||||
{
|
||||
u32 m_height = 0;
|
||||
u32 m_width = 0;
|
||||
TextureFormat m_texture_format = TextureFormat::I4;
|
||||
u32 CalculateHash() const;
|
||||
bool operator==(const FBInfo& other) const;
|
||||
bool operator!=(const FBInfo& other) const;
|
||||
};
|
||||
|
||||
struct FBInfoHasher
|
||||
{
|
||||
std::size_t operator()(const FBInfo& fb_info) const noexcept
|
||||
{
|
||||
return static_cast<std::size_t>(fb_info.CalculateHash());
|
||||
}
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Matrix.h"
|
||||
|
||||
class GraphicsModAction
|
||||
{
|
||||
public:
|
||||
GraphicsModAction() = default;
|
||||
virtual ~GraphicsModAction() = default;
|
||||
GraphicsModAction(const GraphicsModAction&) = default;
|
||||
GraphicsModAction(GraphicsModAction&&) = default;
|
||||
GraphicsModAction& operator=(const GraphicsModAction&) = default;
|
||||
GraphicsModAction& operator=(GraphicsModAction&&) = default;
|
||||
|
||||
virtual void OnDrawStarted(bool* skip) {}
|
||||
virtual void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width,
|
||||
u32* scaled_height)
|
||||
{
|
||||
}
|
||||
virtual void OnXFB() {}
|
||||
virtual void OnProjection(Common::Matrix44* matrix) {}
|
||||
virtual void OnProjectionAndTexture(Common::Matrix44* matrix) {}
|
||||
virtual void OnTextureLoad() {}
|
||||
virtual void OnFrameEnd() {}
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h"
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/MoveAction.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.h"
|
||||
|
||||
namespace GraphicsModActionFactory
|
||||
{
|
||||
std::unique_ptr<GraphicsModAction> Create(std::string_view name, const picojson::value& json_data)
|
||||
{
|
||||
if (name == "print")
|
||||
{
|
||||
return std::make_unique<PrintAction>();
|
||||
}
|
||||
else if (name == "skip")
|
||||
{
|
||||
return std::make_unique<SkipAction>();
|
||||
}
|
||||
else if (name == "move")
|
||||
{
|
||||
return MoveAction::Create(json_data);
|
||||
}
|
||||
else if (name == "scale")
|
||||
{
|
||||
return ScaleAction::Create(json_data);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace GraphicsModActionFactory
|
@ -0,0 +1,16 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
|
||||
|
||||
namespace GraphicsModActionFactory
|
||||
{
|
||||
std::unique_ptr<GraphicsModAction> Create(std::string_view name, const picojson::value& json_data);
|
||||
}
|
@ -0,0 +1,279 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/VariantUtil.h"
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h"
|
||||
#include "VideoCommon/TextureInfo.h"
|
||||
|
||||
class GraphicsModManager::DecoratedAction final : public GraphicsModAction
|
||||
{
|
||||
public:
|
||||
DecoratedAction(std::unique_ptr<GraphicsModAction> action, GraphicsModConfig mod)
|
||||
: m_action_impl(std::move(action)), m_mod(std::move(mod))
|
||||
{
|
||||
}
|
||||
void OnDrawStarted(bool* skip) override
|
||||
{
|
||||
if (!m_mod.m_enabled)
|
||||
return;
|
||||
m_action_impl->OnDrawStarted(skip);
|
||||
}
|
||||
void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width,
|
||||
u32* scaled_height) override
|
||||
{
|
||||
if (!m_mod.m_enabled)
|
||||
return;
|
||||
m_action_impl->OnEFB(skip, texture_width, texture_height, scaled_width, scaled_height);
|
||||
}
|
||||
void OnProjection(Common::Matrix44* matrix) override
|
||||
{
|
||||
if (!m_mod.m_enabled)
|
||||
return;
|
||||
m_action_impl->OnProjection(matrix);
|
||||
}
|
||||
void OnProjectionAndTexture(Common::Matrix44* matrix) override
|
||||
{
|
||||
if (!m_mod.m_enabled)
|
||||
return;
|
||||
m_action_impl->OnProjectionAndTexture(matrix);
|
||||
}
|
||||
void OnTextureLoad() override
|
||||
{
|
||||
if (!m_mod.m_enabled)
|
||||
return;
|
||||
m_action_impl->OnTextureLoad();
|
||||
}
|
||||
void OnFrameEnd() override
|
||||
{
|
||||
if (!m_mod.m_enabled)
|
||||
return;
|
||||
m_action_impl->OnFrameEnd();
|
||||
}
|
||||
|
||||
private:
|
||||
GraphicsModConfig m_mod;
|
||||
std::unique_ptr<GraphicsModAction> m_action_impl;
|
||||
};
|
||||
|
||||
const std::vector<GraphicsModAction*>&
|
||||
GraphicsModManager::GetProjectionActions(ProjectionType projection_type) const
|
||||
{
|
||||
if (const auto it = m_projection_target_to_actions.find(projection_type);
|
||||
it != m_projection_target_to_actions.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return m_default;
|
||||
}
|
||||
|
||||
const std::vector<GraphicsModAction*>&
|
||||
GraphicsModManager::GetProjectionTextureActions(ProjectionType projection_type,
|
||||
const std::string& texture_name) const
|
||||
{
|
||||
const auto lookup = fmt::format("{}_{}", texture_name, static_cast<int>(projection_type));
|
||||
if (const auto it = m_projection_texture_target_to_actions.find(lookup);
|
||||
it != m_projection_texture_target_to_actions.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return m_default;
|
||||
}
|
||||
|
||||
const std::vector<GraphicsModAction*>&
|
||||
GraphicsModManager::GetDrawStartedActions(const std::string& texture_name) const
|
||||
{
|
||||
if (const auto it = m_draw_started_target_to_actions.find(texture_name);
|
||||
it != m_draw_started_target_to_actions.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return m_default;
|
||||
}
|
||||
|
||||
const std::vector<GraphicsModAction*>&
|
||||
GraphicsModManager::GetTextureLoadActions(const std::string& texture_name) const
|
||||
{
|
||||
if (const auto it = m_load_target_to_actions.find(texture_name);
|
||||
it != m_load_target_to_actions.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return m_default;
|
||||
}
|
||||
|
||||
const std::vector<GraphicsModAction*>& GraphicsModManager::GetEFBActions(const FBInfo& efb) const
|
||||
{
|
||||
if (const auto it = m_efb_target_to_actions.find(efb); it != m_efb_target_to_actions.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return m_default;
|
||||
}
|
||||
|
||||
const std::vector<GraphicsModAction*>& GraphicsModManager::GetXFBActions(const FBInfo& xfb) const
|
||||
{
|
||||
if (const auto it = m_efb_target_to_actions.find(xfb); it != m_efb_target_to_actions.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return m_default;
|
||||
}
|
||||
|
||||
void GraphicsModManager::Load(const GraphicsModGroupConfig& config)
|
||||
{
|
||||
Reset();
|
||||
|
||||
const auto& mods = config.GetMods();
|
||||
|
||||
std::map<std::string, std::vector<GraphicsTargetConfig>> group_to_targets;
|
||||
for (const auto& mod : mods)
|
||||
{
|
||||
for (const GraphicsTargetGroupConfig& group : mod.m_groups)
|
||||
{
|
||||
if (m_groups.find(group.m_name) != m_groups.end())
|
||||
{
|
||||
WARN_LOG_FMT(
|
||||
VIDEO,
|
||||
"Specified graphics mod group '{}' for mod '{}' is already specified by another mod.",
|
||||
group.m_name, mod.m_title);
|
||||
}
|
||||
m_groups.insert(group.m_name);
|
||||
|
||||
const auto internal_group = fmt::format("{}.{}", mod.m_title, group.m_name);
|
||||
for (const GraphicsTargetConfig& target : group.m_targets)
|
||||
{
|
||||
group_to_targets[group.m_name].push_back(target);
|
||||
group_to_targets[internal_group].push_back(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& mod : mods)
|
||||
{
|
||||
for (const GraphicsModFeatureConfig& feature : mod.m_features)
|
||||
{
|
||||
const auto create_action = [](const std::string_view& action_name,
|
||||
const picojson::value& json_data,
|
||||
GraphicsModConfig mod) -> std::unique_ptr<GraphicsModAction> {
|
||||
auto action = GraphicsModActionFactory::Create(action_name, json_data);
|
||||
if (action == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<DecoratedAction>(std::move(action), std::move(mod));
|
||||
};
|
||||
|
||||
const auto internal_group = fmt::format("{}.{}", mod.m_title, feature.m_group);
|
||||
|
||||
const auto add_target = [&](const GraphicsTargetConfig& target, GraphicsModConfig mod) {
|
||||
auto action = create_action(feature.m_action, feature.m_action_data, std::move(mod));
|
||||
if (action == nullptr)
|
||||
{
|
||||
WARN_LOG_FMT(VIDEO, "Failed to create action '{}' for group '{}'.", feature.m_action,
|
||||
feature.m_group);
|
||||
return;
|
||||
}
|
||||
m_actions.push_back(std::move(action));
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](const DrawStartedTextureTarget& the_target) {
|
||||
m_draw_started_target_to_actions[the_target.m_texture_info_string].push_back(
|
||||
m_actions.back().get());
|
||||
},
|
||||
[&](const LoadTextureTarget& the_target) {
|
||||
m_load_target_to_actions[the_target.m_texture_info_string].push_back(
|
||||
m_actions.back().get());
|
||||
},
|
||||
[&](const EFBTarget& the_target) {
|
||||
FBInfo info;
|
||||
info.m_height = the_target.m_height;
|
||||
info.m_width = the_target.m_width;
|
||||
info.m_texture_format = the_target.m_texture_format;
|
||||
m_efb_target_to_actions[info].push_back(m_actions.back().get());
|
||||
},
|
||||
[&](const XFBTarget& the_target) {
|
||||
FBInfo info;
|
||||
info.m_height = the_target.m_height;
|
||||
info.m_width = the_target.m_width;
|
||||
info.m_texture_format = the_target.m_texture_format;
|
||||
m_xfb_target_to_actions[info].push_back(m_actions.back().get());
|
||||
},
|
||||
[&](const ProjectionTarget& the_target) {
|
||||
if (the_target.m_texture_info_string)
|
||||
{
|
||||
const auto lookup = fmt::format("{}_{}", *the_target.m_texture_info_string,
|
||||
static_cast<int>(the_target.m_projection_type));
|
||||
m_projection_texture_target_to_actions[lookup].push_back(
|
||||
m_actions.back().get());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_projection_target_to_actions[the_target.m_projection_type].push_back(
|
||||
m_actions.back().get());
|
||||
}
|
||||
},
|
||||
},
|
||||
target);
|
||||
};
|
||||
|
||||
// Prefer groups in the pack over groups from another pack
|
||||
if (const auto local_it = group_to_targets.find(internal_group);
|
||||
local_it != group_to_targets.end())
|
||||
{
|
||||
for (const GraphicsTargetConfig& target : local_it->second)
|
||||
{
|
||||
add_target(target, mod);
|
||||
}
|
||||
}
|
||||
else if (const auto global_it = group_to_targets.find(feature.m_group);
|
||||
global_it != group_to_targets.end())
|
||||
{
|
||||
for (const GraphicsTargetConfig& target : global_it->second)
|
||||
{
|
||||
add_target(target, mod);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG_FMT(VIDEO, "Specified graphics mod group '{}' was not found for mod '{}'",
|
||||
feature.m_group, mod.m_title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsModManager::EndOfFrame()
|
||||
{
|
||||
for (auto&& action : m_actions)
|
||||
{
|
||||
action->OnFrameEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsModManager::Reset()
|
||||
{
|
||||
m_actions.clear();
|
||||
m_groups.clear();
|
||||
m_projection_target_to_actions.clear();
|
||||
m_projection_texture_target_to_actions.clear();
|
||||
m_draw_started_target_to_actions.clear();
|
||||
m_load_target_to_actions.clear();
|
||||
m_efb_target_to_actions.clear();
|
||||
m_xfb_target_to_actions.clear();
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/FBInfo.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
|
||||
#include "VideoCommon/TextureInfo.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
|
||||
class GraphicsModGroupConfig;
|
||||
class GraphicsModManager
|
||||
{
|
||||
public:
|
||||
const std::vector<GraphicsModAction*>& GetProjectionActions(ProjectionType projection_type) const;
|
||||
const std::vector<GraphicsModAction*>&
|
||||
GetProjectionTextureActions(ProjectionType projection_type,
|
||||
const std::string& texture_name) const;
|
||||
const std::vector<GraphicsModAction*>&
|
||||
GetDrawStartedActions(const std::string& texture_name) const;
|
||||
const std::vector<GraphicsModAction*>&
|
||||
GetTextureLoadActions(const std::string& texture_name) const;
|
||||
const std::vector<GraphicsModAction*>& GetEFBActions(const FBInfo& efb) const;
|
||||
const std::vector<GraphicsModAction*>& GetXFBActions(const FBInfo& xfb) const;
|
||||
|
||||
void Load(const GraphicsModGroupConfig& config);
|
||||
|
||||
void EndOfFrame();
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
|
||||
class DecoratedAction;
|
||||
|
||||
static inline const std::vector<GraphicsModAction*> m_default = {};
|
||||
std::list<std::unique_ptr<GraphicsModAction>> m_actions;
|
||||
std::unordered_map<ProjectionType, std::vector<GraphicsModAction*>>
|
||||
m_projection_target_to_actions;
|
||||
std::unordered_map<std::string, std::vector<GraphicsModAction*>>
|
||||
m_projection_texture_target_to_actions;
|
||||
std::unordered_map<std::string, std::vector<GraphicsModAction*>> m_draw_started_target_to_actions;
|
||||
std::unordered_map<std::string, std::vector<GraphicsModAction*>> m_load_target_to_actions;
|
||||
std::unordered_map<FBInfo, std::vector<GraphicsModAction*>, FBInfoHasher> m_efb_target_to_actions;
|
||||
std::unordered_map<FBInfo, std::vector<GraphicsModAction*>, FBInfoHasher> m_xfb_target_to_actions;
|
||||
|
||||
std::unordered_set<std::string> m_groups;
|
||||
};
|
@ -213,7 +213,7 @@ void HiresTexture::Prefetch()
|
||||
10000);
|
||||
}
|
||||
|
||||
std::string HiresTexture::GenBaseName(TextureInfo& texture_info, bool dump)
|
||||
std::string HiresTexture::GenBaseName(const TextureInfo& texture_info, bool dump)
|
||||
{
|
||||
if (!dump && s_textureMap.empty())
|
||||
return "";
|
||||
@ -261,7 +261,7 @@ u32 HiresTexture::CalculateMipCount(u32 width, u32 height)
|
||||
return mip_count;
|
||||
}
|
||||
|
||||
std::shared_ptr<HiresTexture> HiresTexture::Search(TextureInfo& texture_info)
|
||||
std::shared_ptr<HiresTexture> HiresTexture::Search(const TextureInfo& texture_info)
|
||||
{
|
||||
const std::string base_filename = GenBaseName(texture_info);
|
||||
|
||||
@ -433,17 +433,17 @@ std::set<std::string> GetTextureDirectoriesWithGameId(const std::string& root_di
|
||||
}
|
||||
}
|
||||
|
||||
const auto match_gameid = [game_id](const std::string& filename) {
|
||||
const auto match_gameid_or_all = [game_id](const std::string& filename) {
|
||||
std::string basename;
|
||||
SplitPath(filename, nullptr, &basename, nullptr);
|
||||
return basename == game_id || basename == game_id.substr(0, 3);
|
||||
return basename == game_id || basename == game_id.substr(0, 3) || basename == "all";
|
||||
};
|
||||
|
||||
// Look for any other directories that might be specific to the given gameid
|
||||
const auto files = Common::DoFileSearch({root_directory}, {".txt"}, true);
|
||||
for (const auto& file : files)
|
||||
{
|
||||
if (match_gameid(file))
|
||||
if (match_gameid_or_all(file))
|
||||
{
|
||||
// The following code is used to calculate the top directory
|
||||
// of a found gameid.txt file
|
||||
|
@ -25,9 +25,9 @@ public:
|
||||
static void Clear();
|
||||
static void Shutdown();
|
||||
|
||||
static std::shared_ptr<HiresTexture> Search(TextureInfo& texture_info);
|
||||
static std::shared_ptr<HiresTexture> Search(const TextureInfo& texture_info);
|
||||
|
||||
static std::string GenBaseName(TextureInfo& texture_info, bool dump = false);
|
||||
static std::string GenBaseName(const TextureInfo& texture_info, bool dump = false);
|
||||
|
||||
static u32 CalculateMipCount(u32 width, u32 height);
|
||||
|
||||
|
@ -67,6 +67,7 @@
|
||||
#include "VideoCommon/FramebufferManager.h"
|
||||
#include "VideoCommon/FramebufferShaderGen.h"
|
||||
#include "VideoCommon/FreeLookCamera.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
|
||||
#include "VideoCommon/NetPlayChatUI.h"
|
||||
#include "VideoCommon/NetPlayGolfUI.h"
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
@ -135,6 +136,22 @@ bool Renderer::Initialize()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
// If a config change occurred in a previous session,
|
||||
// remember the old change count value. By setting
|
||||
// our current change count to the old value, we
|
||||
// avoid loading the stale data when we
|
||||
// check for config changes.
|
||||
const u32 old_game_mod_changes = g_ActiveConfig.graphics_mod_config ?
|
||||
g_ActiveConfig.graphics_mod_config->GetChangeCount() :
|
||||
0;
|
||||
g_ActiveConfig.graphics_mod_config = GraphicsModGroupConfig(SConfig::GetInstance().GetGameID());
|
||||
g_ActiveConfig.graphics_mod_config->Load();
|
||||
g_ActiveConfig.graphics_mod_config->SetChangeCount(old_game_mod_changes);
|
||||
m_graphics_mod_manager.Load(*g_ActiveConfig.graphics_mod_config);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -465,12 +482,27 @@ void Renderer::CheckForConfigChanges()
|
||||
const bool old_force_filtering = g_ActiveConfig.bForceFiltering;
|
||||
const bool old_vsync = g_ActiveConfig.bVSyncActive;
|
||||
const bool old_bbox = g_ActiveConfig.bBBoxEnable;
|
||||
const u32 old_game_mod_changes =
|
||||
g_ActiveConfig.graphics_mod_config ? g_ActiveConfig.graphics_mod_config->GetChangeCount() : 0;
|
||||
const bool old_graphics_mods_enabled = g_ActiveConfig.bGraphicMods;
|
||||
|
||||
UpdateActiveConfig();
|
||||
FreeLook::UpdateActiveConfig();
|
||||
|
||||
g_freelook_camera.SetControlType(FreeLook::GetActiveConfig().camera_config.control_type);
|
||||
|
||||
if (g_ActiveConfig.bGraphicMods && !old_graphics_mods_enabled)
|
||||
{
|
||||
g_ActiveConfig.graphics_mod_config = GraphicsModGroupConfig(SConfig::GetInstance().GetGameID());
|
||||
g_ActiveConfig.graphics_mod_config->Load();
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.graphics_mod_config &&
|
||||
(old_game_mod_changes != g_ActiveConfig.graphics_mod_config->GetChangeCount()))
|
||||
{
|
||||
m_graphics_mod_manager.Load(*g_ActiveConfig.graphics_mod_config);
|
||||
}
|
||||
|
||||
// Update texture cache settings with any changed options.
|
||||
g_texture_cache->OnConfigChanged(g_ActiveConfig);
|
||||
|
||||
@ -1309,6 +1341,11 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6
|
||||
// behind the renderer.
|
||||
FlushFrameDump();
|
||||
|
||||
if (g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
m_graphics_mod_manager.EndOfFrame();
|
||||
}
|
||||
|
||||
if (xfb_addr && fb_width && fb_stride && fb_height)
|
||||
{
|
||||
// Get the current XFB from texture cache
|
||||
@ -1830,3 +1867,8 @@ std::unique_ptr<VideoCommon::AsyncShaderCompiler> Renderer::CreateAsyncShaderCom
|
||||
{
|
||||
return std::make_unique<VideoCommon::AsyncShaderCompiler>();
|
||||
}
|
||||
|
||||
const GraphicsModManager& Renderer::GetGraphicsModManager() const
|
||||
{
|
||||
return m_graphics_mod_manager;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "VideoCommon/BPMemory.h"
|
||||
#include "VideoCommon/FPSCounter.h"
|
||||
#include "VideoCommon/FrameDump.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
#include "VideoCommon/TextureConfig.h"
|
||||
|
||||
@ -268,6 +269,8 @@ public:
|
||||
// Will forcibly reload all textures on the next swap
|
||||
void ForceReloadTextures();
|
||||
|
||||
const GraphicsModManager& GetGraphicsModManager() const;
|
||||
|
||||
protected:
|
||||
// Bitmask containing information about which configuration has changed for the backend.
|
||||
enum ConfigChangeBits : u32
|
||||
@ -447,6 +450,8 @@ private:
|
||||
std::unique_ptr<NetPlayChatUI> m_netplay_chat_ui;
|
||||
|
||||
Common::Flag m_force_reload_textures;
|
||||
|
||||
GraphicsModManager m_graphics_mod_manager;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<Renderer> g_renderer;
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "VideoCommon/AbstractStagingTexture.h"
|
||||
#include "VideoCommon/BPMemory.h"
|
||||
#include "VideoCommon/FramebufferManager.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Runtime/FBInfo.h"
|
||||
#include "VideoCommon/HiresTextures.h"
|
||||
#include "VideoCommon/OpcodeDecoding.h"
|
||||
#include "VideoCommon/PixelShaderManager.h"
|
||||
@ -55,6 +56,8 @@ static const u64 TEXHASH_INVALID = 0;
|
||||
static const int TEXTURE_KILL_THRESHOLD = 64;
|
||||
static const int TEXTURE_POOL_KILL_THRESHOLD = 3;
|
||||
|
||||
static int xfb_count = 0;
|
||||
|
||||
std::unique_ptr<TextureCacheBase> g_texture_cache;
|
||||
|
||||
TextureCacheBase::TCacheEntry::TCacheEntry(std::unique_ptr<AbstractTexture> tex,
|
||||
@ -153,6 +156,9 @@ void TextureCacheBase::OnConfigChanged(const VideoConfig& config)
|
||||
HiresTexture::Update();
|
||||
}
|
||||
|
||||
const u32 change_count =
|
||||
config.graphics_mod_config ? config.graphics_mod_config->GetChangeCount() : 0;
|
||||
|
||||
// TODO: Invalidating texcache is really stupid in some of these cases
|
||||
if (config.iSafeTextureCache_ColorSamples != backup_config.color_samples ||
|
||||
config.bTexFmtOverlayEnable != backup_config.texfmt_overlay ||
|
||||
@ -160,7 +166,9 @@ void TextureCacheBase::OnConfigChanged(const VideoConfig& config)
|
||||
config.bHiresTextures != backup_config.hires_textures ||
|
||||
config.bEnableGPUTextureDecoding != backup_config.gpu_texture_decoding ||
|
||||
config.bDisableCopyToVRAM != backup_config.disable_vram_copies ||
|
||||
config.bArbitraryMipmapDetection != backup_config.arbitrary_mipmap_detection)
|
||||
config.bArbitraryMipmapDetection != backup_config.arbitrary_mipmap_detection ||
|
||||
config.bGraphicMods != backup_config.graphics_mods ||
|
||||
change_count != backup_config.graphics_mod_change_count)
|
||||
{
|
||||
Invalidate();
|
||||
TexDecoder_SetTexFmtOverlayOptions(config.bTexFmtOverlayEnable, config.bTexFmtOverlayCenter);
|
||||
@ -255,6 +263,9 @@ void TextureCacheBase::SetBackupConfig(const VideoConfig& config)
|
||||
backup_config.gpu_texture_decoding = config.bEnableGPUTextureDecoding;
|
||||
backup_config.disable_vram_copies = config.bDisableCopyToVRAM;
|
||||
backup_config.arbitrary_mipmap_detection = config.bArbitraryMipmapDetection;
|
||||
backup_config.graphics_mods = config.bGraphicMods;
|
||||
backup_config.graphics_mod_change_count =
|
||||
config.graphics_mod_config ? config.graphics_mod_config->GetChangeCount() : 0;
|
||||
}
|
||||
|
||||
TextureCacheBase::TCacheEntry*
|
||||
@ -1205,15 +1216,15 @@ private:
|
||||
std::vector<Level> levels;
|
||||
};
|
||||
|
||||
TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
||||
TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture_info)
|
||||
{
|
||||
// if this stage was not invalidated by changes to texture registers, keep the current texture
|
||||
if (TMEM::IsValid(stage) && bound_textures[stage])
|
||||
if (TMEM::IsValid(texture_info.GetStage()) && bound_textures[texture_info.GetStage()])
|
||||
{
|
||||
TCacheEntry* entry = bound_textures[stage];
|
||||
TCacheEntry* entry = bound_textures[texture_info.GetStage()];
|
||||
// If the TMEM configuration is such that this texture is more or less guaranteed to still
|
||||
// be in TMEM, then we know we can reuse the old entry without even hashing the memory
|
||||
if (TMEM::IsCached(stage))
|
||||
if (TMEM::IsCached(texture_info.GetStage()))
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
@ -1226,26 +1237,29 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
||||
}
|
||||
}
|
||||
|
||||
TextureInfo texture_info = TextureInfo::FromStage(stage);
|
||||
|
||||
auto entry = GetTexture(g_ActiveConfig.iSafeTextureCache_ColorSamples, texture_info);
|
||||
|
||||
if (!entry)
|
||||
return nullptr;
|
||||
|
||||
entry->frameCount = FRAMECOUNT_INVALID;
|
||||
bound_textures[stage] = entry;
|
||||
if (entry->texture_info_name.empty() && g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
entry->texture_info_name = texture_info.CalculateTextureName().GetFullName();
|
||||
}
|
||||
bound_textures[texture_info.GetStage()] = entry;
|
||||
|
||||
// We need to keep track of invalided textures until they have actually been replaced or
|
||||
// re-loaded
|
||||
TMEM::Bind(stage, entry->NumBlocksX(), entry->NumBlocksY(), entry->GetNumLevels() > 1,
|
||||
entry->format == TextureFormat::RGBA8);
|
||||
TMEM::Bind(texture_info.GetStage(), entry->NumBlocksX(), entry->NumBlocksY(),
|
||||
entry->GetNumLevels() > 1, entry->format == TextureFormat::RGBA8);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
TextureCacheBase::TCacheEntry*
|
||||
TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize, TextureInfo& texture_info)
|
||||
TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
|
||||
const TextureInfo& texture_info)
|
||||
{
|
||||
u32 expanded_width = texture_info.GetExpandedWidth();
|
||||
u32 expanded_height = texture_info.GetExpandedHeight();
|
||||
@ -1764,12 +1778,20 @@ TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride,
|
||||
SETSTAT(g_stats.num_textures_alive, static_cast<int>(textures_by_address.size()));
|
||||
INCSTAT(g_stats.num_textures_uploaded);
|
||||
|
||||
if (g_ActiveConfig.bDumpXFBTarget)
|
||||
if (g_ActiveConfig.bDumpXFBTarget || g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
// While this isn't really an xfb copy, we can treat it as such for dumping purposes
|
||||
static int xfb_count = 0;
|
||||
entry->texture->Save(
|
||||
fmt::format("{}xfb_loaded_{}.png", File::GetUserPath(D_DUMPTEXTURES_IDX), xfb_count++), 0);
|
||||
const std::string id = fmt::format("{}x{}", width, height);
|
||||
if (g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
entry->texture_info_name = fmt::format("{}_{}", XFB_DUMP_PREFIX, id);
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bDumpXFBTarget)
|
||||
{
|
||||
entry->texture->Save(fmt::format("{}{}_n{:06}_{}.png", File::GetUserPath(D_DUMPTEXTURES_IDX),
|
||||
XFB_DUMP_PREFIX, xfb_count++, id),
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
GetDisplayRectForXFBEntry(entry, width, height, display_rect);
|
||||
@ -2119,6 +2141,35 @@ void TextureCacheBase::CopyRenderTargetToTexture(
|
||||
const u32 bytes_per_row = num_blocks_x * bytes_per_block;
|
||||
const u32 covered_range = num_blocks_y * dstStride;
|
||||
|
||||
if (g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
FBInfo info;
|
||||
info.m_width = tex_w;
|
||||
info.m_height = tex_h;
|
||||
info.m_texture_format = baseFormat;
|
||||
if (is_xfb_copy)
|
||||
{
|
||||
for (const auto action : g_renderer->GetGraphicsModManager().GetXFBActions(info))
|
||||
{
|
||||
action->OnXFB();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool skip = false;
|
||||
for (const auto action : g_renderer->GetGraphicsModManager().GetEFBActions(info))
|
||||
{
|
||||
action->OnEFB(&skip, tex_w, tex_h, &scaled_tex_w, &scaled_tex_h);
|
||||
}
|
||||
if (skip == true)
|
||||
{
|
||||
if (copy_to_ram)
|
||||
UninitializeEFBMemory(dst, dstStride, bytes_per_row, num_blocks_y);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dstStride < bytes_per_row)
|
||||
{
|
||||
// This kind of efb copy results in a scrambled image.
|
||||
@ -2168,20 +2219,38 @@ void TextureCacheBase::CopyRenderTargetToTexture(
|
||||
isIntensity, gamma, clamp_top, clamp_bottom,
|
||||
GetVRAMCopyFilterCoefficients(filter_coefficients));
|
||||
|
||||
if (g_ActiveConfig.bDumpEFBTarget && !is_xfb_copy)
|
||||
if (is_xfb_copy && (g_ActiveConfig.bDumpXFBTarget || g_ActiveConfig.bGraphicMods))
|
||||
{
|
||||
static int efb_count = 0;
|
||||
entry->texture->Save(
|
||||
fmt::format("{}efb_frame_{}.png", File::GetUserPath(D_DUMPTEXTURES_IDX), efb_count++),
|
||||
0);
|
||||
}
|
||||
const std::string id = fmt::format("{}x{}", tex_w, tex_h);
|
||||
if (g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
entry->texture_info_name = fmt::format("{}_{}", XFB_DUMP_PREFIX, id);
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bDumpXFBTarget && is_xfb_copy)
|
||||
if (g_ActiveConfig.bDumpXFBTarget)
|
||||
{
|
||||
entry->texture->Save(fmt::format("{}{}_n{:06}_{}.png",
|
||||
File::GetUserPath(D_DUMPTEXTURES_IDX), XFB_DUMP_PREFIX,
|
||||
xfb_count++, id),
|
||||
0);
|
||||
}
|
||||
}
|
||||
else if (g_ActiveConfig.bDumpEFBTarget || g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
static int xfb_count = 0;
|
||||
entry->texture->Save(
|
||||
fmt::format("{}xfb_copy_{}.png", File::GetUserPath(D_DUMPTEXTURES_IDX), xfb_count++),
|
||||
0);
|
||||
const std::string id = fmt::format("{}x{}_{}", tex_w, tex_h, static_cast<int>(baseFormat));
|
||||
if (g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
entry->texture_info_name = fmt::format("{}_{}", EFB_DUMP_PREFIX, id);
|
||||
}
|
||||
|
||||
if (g_ActiveConfig.bDumpEFBTarget)
|
||||
{
|
||||
static int efb_count = 0;
|
||||
entry->texture->Save(fmt::format("{}{}_n{:06}_{}.png",
|
||||
File::GetUserPath(D_DUMPTEXTURES_IDX), EFB_DUMP_PREFIX,
|
||||
efb_count++, id),
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2225,15 +2294,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hack: Most games don't actually need the correct texture data in RAM
|
||||
// and we can just keep a copy in VRAM. We zero the memory so we
|
||||
// can check it hasn't changed before using our copy in VRAM.
|
||||
u8* ptr = dst;
|
||||
for (u32 i = 0; i < num_blocks_y; i++)
|
||||
{
|
||||
std::memset(ptr, 0, bytes_per_row);
|
||||
ptr += dstStride;
|
||||
}
|
||||
UninitializeEFBMemory(dst, dstStride, bytes_per_row, num_blocks_y);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2403,6 +2464,20 @@ void TextureCacheBase::ReleaseEFBCopyStagingTexture(std::unique_ptr<AbstractStag
|
||||
m_efb_copy_staging_texture_pool.push_back(std::move(tex));
|
||||
}
|
||||
|
||||
void TextureCacheBase::UninitializeEFBMemory(u8* dst, u32 stride, u32 bytes_per_row,
|
||||
u32 num_blocks_y)
|
||||
{
|
||||
// Hack: Most games don't actually need the correct texture data in RAM
|
||||
// and we can just keep a copy in VRAM. We zero the memory so we
|
||||
// can check it hasn't changed before using our copy in VRAM.
|
||||
u8* ptr = dst;
|
||||
for (u32 i = 0; i < num_blocks_y; i++)
|
||||
{
|
||||
std::memset(ptr, 0, bytes_per_row);
|
||||
ptr += stride;
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCacheBase::UninitializeXFBMemory(u8* dst, u32 stride, u32 bytes_per_row,
|
||||
u32 num_blocks_y)
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@ -29,6 +30,9 @@ class AbstractStagingTexture;
|
||||
class PointerWrap;
|
||||
struct VideoConfig;
|
||||
|
||||
constexpr std::string_view EFB_DUMP_PREFIX = "efb1";
|
||||
constexpr std::string_view XFB_DUMP_PREFIX = "xfb1";
|
||||
|
||||
struct TextureAndTLUTFormat
|
||||
{
|
||||
TextureAndTLUTFormat(TextureFormat texfmt_ = TextureFormat::I4,
|
||||
@ -151,6 +155,8 @@ public:
|
||||
u32 pending_efb_copy_height = 0;
|
||||
bool pending_efb_copy_invalidated = false;
|
||||
|
||||
std::string texture_info_name = "";
|
||||
|
||||
explicit TCacheEntry(std::unique_ptr<AbstractTexture> tex,
|
||||
std::unique_ptr<AbstractFramebuffer> fb);
|
||||
|
||||
@ -235,8 +241,9 @@ public:
|
||||
|
||||
void Invalidate();
|
||||
|
||||
TCacheEntry* Load(const u32 stage);
|
||||
TCacheEntry* GetTexture(const int textureCacheSafetyColorSampleSize, TextureInfo& texture_info);
|
||||
TCacheEntry* Load(const TextureInfo& texture_info);
|
||||
TCacheEntry* GetTexture(const int textureCacheSafetyColorSampleSize,
|
||||
const TextureInfo& texture_info);
|
||||
TCacheEntry* GetXFBTexture(u32 address, u32 width, u32 height, u32 stride,
|
||||
MathUtil::Rectangle<int>* display_rect);
|
||||
|
||||
@ -327,6 +334,7 @@ private:
|
||||
TexAddrCache::iterator InvalidateTexture(TexAddrCache::iterator t_iter,
|
||||
bool discard_pending_efb_copy = false);
|
||||
|
||||
void UninitializeEFBMemory(u8* dst, u32 stride, u32 bytes_per_row, u32 num_blocks_y);
|
||||
void UninitializeXFBMemory(u8* dst, u32 stride, u32 bytes_per_row, u32 num_blocks_y);
|
||||
|
||||
// Precomputing the coefficients for the previous, current, and next lines for the copy filter.
|
||||
@ -369,6 +377,8 @@ private:
|
||||
bool gpu_texture_decoding;
|
||||
bool disable_vram_copies;
|
||||
bool arbitrary_mipmap_detection;
|
||||
bool graphics_mods;
|
||||
u32 graphics_mod_change_count;
|
||||
};
|
||||
BackupConfig backup_config = {};
|
||||
|
||||
|
@ -50,6 +50,11 @@ static inline bool IsColorIndexed(TextureFormat format)
|
||||
format == TextureFormat::C14X2;
|
||||
}
|
||||
|
||||
static inline bool IsValidTextureFormat(TextureFormat format)
|
||||
{
|
||||
return format <= TextureFormat::RGBA8 || IsColorIndexed(format) || format == TextureFormat::CMPR;
|
||||
}
|
||||
|
||||
// The EFB Copy pipeline looks like:
|
||||
//
|
||||
// 1. Read EFB -> 2. Select color/depth -> 3. Downscale (optional)
|
||||
|
@ -39,20 +39,20 @@ TextureInfo TextureInfo::FromStage(u32 stage)
|
||||
|
||||
if (from_tmem)
|
||||
{
|
||||
return TextureInfo(&texMem[tmem_address_even], tlut_ptr, address, texture_format, tlut_format,
|
||||
width, height, true, &texMem[tmem_address_odd], &texMem[tmem_address_even],
|
||||
mip_count);
|
||||
return TextureInfo(stage, &texMem[tmem_address_even], tlut_ptr, address, texture_format,
|
||||
tlut_format, width, height, true, &texMem[tmem_address_odd],
|
||||
&texMem[tmem_address_even], mip_count);
|
||||
}
|
||||
|
||||
return TextureInfo(Memory::GetPointer(address), tlut_ptr, address, texture_format, tlut_format,
|
||||
width, height, false, nullptr, nullptr, mip_count);
|
||||
return TextureInfo(stage, Memory::GetPointer(address), tlut_ptr, address, texture_format,
|
||||
tlut_format, width, height, false, nullptr, nullptr, mip_count);
|
||||
}
|
||||
|
||||
TextureInfo::TextureInfo(const u8* ptr, const u8* tlut_ptr, u32 address,
|
||||
TextureInfo::TextureInfo(u32 stage, const u8* ptr, const u8* tlut_ptr, u32 address,
|
||||
TextureFormat texture_format, TLUTFormat tlut_format, u32 width,
|
||||
u32 height, bool from_tmem, const u8* tmem_odd, const u8* tmem_even,
|
||||
std::optional<u32> mip_count)
|
||||
: m_ptr(ptr), m_tlut_ptr(tlut_ptr), m_address(address), m_from_tmem(from_tmem),
|
||||
: m_stage(stage), m_ptr(ptr), m_tlut_ptr(tlut_ptr), m_address(address), m_from_tmem(from_tmem),
|
||||
m_tmem_odd(tmem_odd), m_texture_format(texture_format), m_tlut_format(tlut_format),
|
||||
m_raw_width(width), m_raw_height(height)
|
||||
{
|
||||
@ -100,7 +100,7 @@ std::string TextureInfo::NameDetails::GetFullName() const
|
||||
return fmt::format("{}_{}{}_{}", base_name, texture_name, tlut_name, format_name);
|
||||
}
|
||||
|
||||
TextureInfo::NameDetails TextureInfo::CalculateTextureName()
|
||||
TextureInfo::NameDetails TextureInfo::CalculateTextureName() const
|
||||
{
|
||||
if (!m_ptr)
|
||||
return NameDetails{};
|
||||
@ -240,6 +240,11 @@ u32 TextureInfo::GetRawHeight() const
|
||||
return m_raw_height;
|
||||
}
|
||||
|
||||
u32 TextureInfo::GetStage() const
|
||||
{
|
||||
return m_stage;
|
||||
}
|
||||
|
||||
bool TextureInfo::HasMipMaps() const
|
||||
{
|
||||
return !m_mip_levels.empty();
|
||||
|
@ -17,9 +17,10 @@ class TextureInfo
|
||||
{
|
||||
public:
|
||||
static TextureInfo FromStage(u32 stage);
|
||||
TextureInfo(const u8* ptr, const u8* tlut_ptr, u32 address, TextureFormat texture_format,
|
||||
TLUTFormat tlut_format, u32 width, u32 height, bool from_tmem, const u8* tmem_odd,
|
||||
const u8* tmem_even, std::optional<u32> mip_count);
|
||||
TextureInfo(u32 stage, const u8* ptr, const u8* tlut_ptr, u32 address,
|
||||
TextureFormat texture_format, TLUTFormat tlut_format, u32 width, u32 height,
|
||||
bool from_tmem, const u8* tmem_odd, const u8* tmem_even,
|
||||
std::optional<u32> mip_count);
|
||||
|
||||
struct NameDetails
|
||||
{
|
||||
@ -30,7 +31,7 @@ public:
|
||||
|
||||
std::string GetFullName() const;
|
||||
};
|
||||
NameDetails CalculateTextureName();
|
||||
NameDetails CalculateTextureName() const;
|
||||
|
||||
const u8* GetData() const;
|
||||
const u8* GetTlutAddress() const;
|
||||
@ -55,6 +56,8 @@ public:
|
||||
u32 GetRawWidth() const;
|
||||
u32 GetRawHeight() const;
|
||||
|
||||
u32 GetStage() const;
|
||||
|
||||
class MipLevel
|
||||
{
|
||||
public:
|
||||
@ -115,4 +118,6 @@ private:
|
||||
u32 m_block_height;
|
||||
u32 m_expanded_height;
|
||||
u32 m_raw_height;
|
||||
|
||||
u32 m_stage;
|
||||
};
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
#include "Common/BitSet.h"
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/EnumMap.h"
|
||||
@ -30,6 +29,7 @@
|
||||
#include "VideoCommon/RenderBase.h"
|
||||
#include "VideoCommon/Statistics.h"
|
||||
#include "VideoCommon/TextureCacheBase.h"
|
||||
#include "VideoCommon/TextureInfo.h"
|
||||
#include "VideoCommon/VertexLoaderManager.h"
|
||||
#include "VideoCommon/VertexShaderManager.h"
|
||||
#include "VideoCommon/VideoBackendBase.h"
|
||||
@ -337,7 +337,7 @@ bool VertexManagerBase::UploadTexelBuffer(const void* data, u32 data_size, Texel
|
||||
return false;
|
||||
}
|
||||
|
||||
void VertexManagerBase::LoadTextures()
|
||||
BitSet32 VertexManagerBase::UsedTextures() const
|
||||
{
|
||||
BitSet32 usedtextures;
|
||||
for (u32 i = 0; i < bpmem.genMode.numtevstages + 1u; ++i)
|
||||
@ -349,10 +349,7 @@ void VertexManagerBase::LoadTextures()
|
||||
if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages)
|
||||
usedtextures[bpmem.tevindref.getTexMap(bpmem.tevind[i].bt)] = true;
|
||||
|
||||
for (unsigned int i : usedtextures)
|
||||
g_texture_cache->Load(i);
|
||||
|
||||
g_texture_cache->BindTextures(usedtextures);
|
||||
return usedtextures;
|
||||
}
|
||||
|
||||
void VertexManagerBase::Flush()
|
||||
@ -455,7 +452,30 @@ void VertexManagerBase::Flush()
|
||||
|
||||
CalculateBinormals(VertexLoaderManager::GetCurrentVertexFormat());
|
||||
// Calculate ZSlope for zfreeze
|
||||
VertexShaderManager::SetConstants();
|
||||
const auto used_textures = UsedTextures();
|
||||
std::vector<std::string> texture_names;
|
||||
if (!m_cull_all)
|
||||
{
|
||||
if (!g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
for (const u32 i : used_textures)
|
||||
{
|
||||
g_texture_cache->Load(TextureInfo::FromStage(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const u32 i : used_textures)
|
||||
{
|
||||
const auto cache_entry = g_texture_cache->Load(TextureInfo::FromStage(i));
|
||||
if (cache_entry)
|
||||
{
|
||||
texture_names.push_back(cache_entry->texture_info_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VertexShaderManager::SetConstants(texture_names);
|
||||
if (!bpmem.genMode.zfreeze)
|
||||
{
|
||||
// Must be done after VertexShaderManager::SetConstants()
|
||||
@ -469,6 +489,18 @@ void VertexManagerBase::Flush()
|
||||
|
||||
if (!m_cull_all)
|
||||
{
|
||||
for (const auto& texture_name : texture_names)
|
||||
{
|
||||
bool skip = false;
|
||||
for (const auto action :
|
||||
g_renderer->GetGraphicsModManager().GetDrawStartedActions(texture_name))
|
||||
{
|
||||
action->OnDrawStarted(&skip);
|
||||
}
|
||||
if (skip == true)
|
||||
return;
|
||||
}
|
||||
|
||||
// Now the vertices can be flushed to the GPU. Everything following the CommitBuffer() call
|
||||
// must be careful to not upload any utility vertices, as the binding will be lost otherwise.
|
||||
const u32 num_indices = m_index_generator.GetIndexLen();
|
||||
@ -480,7 +512,7 @@ void VertexManagerBase::Flush()
|
||||
// Texture loading can cause palettes to be applied (-> uniforms -> draws).
|
||||
// Palette application does not use vertices, only a full-screen quad, so this is okay.
|
||||
// Same with GPU texture decoding, which uses compute shaders.
|
||||
LoadTextures();
|
||||
g_texture_cache->BindTextures(used_textures);
|
||||
|
||||
// Now we can upload uniforms, as nothing else will override them.
|
||||
GeometryShaderManager::SetConstants();
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/BitSet.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "VideoCommon/IndexGenerator.h"
|
||||
@ -173,7 +174,8 @@ protected:
|
||||
|
||||
void CalculateZSlope(NativeVertexFormat* format);
|
||||
void CalculateBinormals(NativeVertexFormat* format);
|
||||
void LoadTextures();
|
||||
|
||||
BitSet32 UsedTextures() const;
|
||||
|
||||
u8* m_cur_buffer_pointer = nullptr;
|
||||
u8* m_base_buffer_pointer = nullptr;
|
||||
|
@ -85,7 +85,7 @@ void VertexShaderManager::Dirty()
|
||||
|
||||
// Syncs the shader constant buffers with xfmem
|
||||
// TODO: A cleaner way to control the matrices without making a mess in the parameters field
|
||||
void VertexShaderManager::SetConstants()
|
||||
void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
||||
{
|
||||
if (constants.missing_color_hex != g_ActiveConfig.iMissingColorValue)
|
||||
{
|
||||
@ -302,7 +302,27 @@ void VertexShaderManager::SetConstants()
|
||||
g_stats.AddScissorRect();
|
||||
}
|
||||
|
||||
if (bProjectionChanged || g_freelook_camera.GetController()->IsDirty())
|
||||
std::vector<GraphicsModAction*> projection_actions;
|
||||
if (g_ActiveConfig.bGraphicMods)
|
||||
{
|
||||
for (const auto action :
|
||||
g_renderer->GetGraphicsModManager().GetProjectionActions(xfmem.projection.type))
|
||||
{
|
||||
projection_actions.push_back(action);
|
||||
}
|
||||
|
||||
for (const auto& texture : textures)
|
||||
{
|
||||
for (const auto action : g_renderer->GetGraphicsModManager().GetProjectionTextureActions(
|
||||
xfmem.projection.type, texture))
|
||||
{
|
||||
projection_actions.push_back(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bProjectionChanged || g_freelook_camera.GetController()->IsDirty() ||
|
||||
!projection_actions.empty())
|
||||
{
|
||||
bProjectionChanged = false;
|
||||
|
||||
@ -384,6 +404,11 @@ void VertexShaderManager::SetConstants()
|
||||
if (g_freelook_camera.IsActive() && xfmem.projection.type == ProjectionType::Perspective)
|
||||
corrected_matrix *= g_freelook_camera.GetView();
|
||||
|
||||
for (auto action : projection_actions)
|
||||
{
|
||||
action->OnProjection(&corrected_matrix);
|
||||
}
|
||||
|
||||
memcpy(constants.projection.data(), corrected_matrix.data.data(), 4 * sizeof(float4));
|
||||
|
||||
g_freelook_camera.GetController()->SetClean();
|
||||
|
@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoCommon/ConstantManager.h"
|
||||
@ -19,7 +20,7 @@ public:
|
||||
static void DoState(PointerWrap& p);
|
||||
|
||||
// constant management
|
||||
static void SetConstants();
|
||||
static void SetConstants(const std::vector<std::string>& textures);
|
||||
|
||||
static void InvalidateXFRange(int start, int end);
|
||||
static void SetTexMatrixChangedA(u32 value);
|
||||
|
@ -145,6 +145,8 @@ void VideoConfig::Refresh()
|
||||
bFastTextureSampling = Config::Get(Config::GFX_HACK_FAST_TEXTURE_SAMPLING);
|
||||
|
||||
bPerfQueriesEnable = Config::Get(Config::GFX_PERF_QUERIES_ENABLE);
|
||||
|
||||
bGraphicMods = Config::Get(Config::GFX_MODS_ENABLE);
|
||||
}
|
||||
|
||||
void VideoConfig::VerifyValidity()
|
||||
|
@ -9,10 +9,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
|
||||
// Log in two categories, and save three other options in the same byte
|
||||
@ -111,6 +113,8 @@ struct VideoConfig final
|
||||
bool bBorderlessFullscreen = false;
|
||||
bool bEnableGPUTextureDecoding = false;
|
||||
int iBitrateKbps = 0;
|
||||
bool bGraphicMods = false;
|
||||
std::optional<GraphicsModGroupConfig> graphics_mod_config;
|
||||
|
||||
// Hacks
|
||||
bool bEFBAccessEnable = false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user