Merge pull request #14140 from iwubcode/blurry_bloom

VideoCommon: add a graphics mod feature to modify EFBs with a custom material, enhance bloom
This commit is contained in:
JMC47 authored and GitHub committed 2026-04-03 13:38:04 -04:00
commit 798f581c38
80 files changed
+1551 -193

No files matched your search

Whitespace-only changes.
@@ -0,0 +1,45 @@
void process_fragment(in DolphinFragmentInput frag_input, out DolphinFragmentOutput frag_output)
{
int layer = int(frag_input.tex0.z);
int r = int(efb_scale) * int(SPREAD_MULTIPLIER);
int coord;
int length;
int2 initial_coords = int2(frag_input.tex0.xy * source_resolution.xy);
vec4 brightness = float4(1.0, 1.0, 1.0, 1.0);
if (HORIZONTAL)
{
length = int(source_resolution.x);
coord = initial_coords.x;
brightness = BRIGHTNESS_MULTIPLIER * brightness;
}
else
{
length = int(source_resolution.y);
coord = initial_coords.y;
}
int offset;
vec4 count = vec4(0.0,0.0,0.0,0.0);
vec4 col = vec4(0.0,0.0,0.0,0.0);
for (offset = -r; offset <= r; offset++)
{
int pos = coord + offset;
if (pos <= length && pos >= 0)
{
int3 sample_coords;
if (HORIZONTAL)
{
sample_coords = int3(int2(pos, initial_coords.y), layer);
}
else
{
sample_coords = int3(int2(initial_coords.x, pos), layer);
}
col += texelFetch(samp0, sample_coords, 0);
count += vec4(1.0,1.0,1.0,1.0);
}
}
frag_output.main = col / count * brightness;
}
@@ -0,0 +1,23 @@
{
"properties":[
{
"code_name": "HORIZONTAL",
"default": true,
"description": "Whether to apply the blur horizontally or vertically",
"type": "bool"
},
{
"code_name": "SPREAD_MULTIPLIER",
"default": 1.0,
"description": "How much to spread the blur",
"type": "float"
},
{
"code_name": "BRIGHTNESS_MULTIPLIER",
"default": 1.0,
"description": "How bright the blur is",
"type": "float"
}
],
"samplers":[]
}
@@ -0,0 +1,4 @@
void process_vertex(in DolphinVertexInput vertex_input, out DolphinVertexOutput vertex_output)
{
dolphin_process_emulated_vertex(vertex_input, vertex_output);
}
@@ -0,0 +1,19 @@
{
"next_material_asset":"dolphin_bloom_blur_vertical",
"properties":[
{
"type": "bool",
"value": true
},
{
"type": "float",
"value": 1.0
},
{
"type": "float",
"value": 1.0
}
],
"textures":[],
"shader_asset": "dolphin_bloom_blur"
}
@@ -0,0 +1,19 @@
{
"next_material_asset":"",
"properties":[
{
"type": "bool",
"value": false
},
{
"type": "float",
"value": 1.0
},
{
"type": "float",
"value": 1.0
}
],
"textures":[],
"shader_asset": "dolphin_bloom_blur"
}
@@ -0,0 +1,41 @@
{
"meta":
{
"title": "Bloom Blurred",
"author": "Dolphin Team",
"description": "Modifies bloom effects to blur them to their native resolution. Results in bloom looking much more natural at higher resolutions but the blur may be too intense in some games."
},
"features":
[
{
"group": "Bloom",
"action": "custom_pipeline",
"action_data":
{
"material_asset": "dolphin_bloom_blur_horizontal"
}
}
],
"assets": [
{
"data": {
"metadata": "blur.rastershader",
"pixel_shader": "blur.ps.glsl",
"vertex_shader": "blur.vs.glsl"
},
"name": "dolphin_bloom_blur"
},
{
"data": {
"metadata": "blur_horizontal.rastermaterial"
},
"name": "dolphin_bloom_blur_horizontal"
},
{
"data": {
"metadata": "blur_vertical.rastermaterial"
},
"name": "dolphin_bloom_blur_vertical"
}
]
}
Whitespace-only changes.
@@ -0,0 +1,45 @@
void process_fragment(in DolphinFragmentInput frag_input, out DolphinFragmentOutput frag_output)
{
int layer = int(frag_input.tex0.z);
int r = int(efb_scale) * int(SPREAD_MULTIPLIER);
int coord;
int length;
int2 initial_coords = int2(frag_input.tex0.xy * source_resolution.xy);
vec4 brightness = float4(1.0, 1.0, 1.0, 1.0);
if (HORIZONTAL)
{
length = int(source_resolution.x);
coord = initial_coords.x;
brightness = BRIGHTNESS_MULTIPLIER * brightness;
}
else
{
length = int(source_resolution.y);
coord = initial_coords.y;
}
int offset;
vec4 count = vec4(0.0,0.0,0.0,0.0);
vec4 col = vec4(0.0,0.0,0.0,0.0);
for (offset = -r; offset <= r; offset++)
{
int pos = coord + offset;
if (pos <= length && pos >= 0)
{
int3 sample_coords;
if (HORIZONTAL)
{
sample_coords = int3(int2(pos, initial_coords.y), layer);
}
else
{
sample_coords = int3(int2(initial_coords.x, pos), layer);
}
col += texelFetch(samp0, sample_coords, 0);
count += vec4(1.0,1.0,1.0,1.0);
}
}
frag_output.main = col / count * brightness;
}
@@ -0,0 +1,23 @@
{
"properties":[
{
"code_name": "HORIZONTAL",
"default": true,
"description": "Whether to apply the blur horizontally or vertically",
"type": "bool"
},
{
"code_name": "SPREAD_MULTIPLIER",
"default": 1.0,
"description": "How much to spread the blur",
"type": "float"
},
{
"code_name": "BRIGHTNESS_MULTIPLIER",
"default": 1.0,
"description": "How bright the blur is",
"type": "float"
}
],
"samplers":[]
}
@@ -0,0 +1,4 @@
void process_vertex(in DolphinVertexInput vertex_input, out DolphinVertexOutput vertex_output)
{
dolphin_process_emulated_vertex(vertex_input, vertex_output);
}
@@ -0,0 +1,19 @@
{
"next_material_asset":"dolphin_dof_blur_vertical",
"properties":[
{
"type": "bool",
"value": true
},
{
"type": "float",
"value": 1.0
},
{
"type": "float",
"value": 1.0
}
],
"textures":[],
"shader_asset": "dolphin_dof_blur"
}
@@ -0,0 +1,19 @@
{
"next_material_asset":"",
"properties":[
{
"type": "bool",
"value": false
},
{
"type": "float",
"value": 1.0
},
{
"type": "float",
"value": 1.0
}
],
"textures":[],
"shader_asset": "dolphin_dof_blur"
}
@@ -0,0 +1,41 @@
{
"meta":
{
"title": "DOF Blurred",
"author": "Dolphin Team",
"description": "Modifies depth of field effects to blur them to their native resolution. Results in depth of field looking much more natural at higher resolutions but the blur may be too intense in some games."
},
"features":
[
{
"group": "DOF",
"action": "custom_pipeline",
"action_data":
{
"material_asset": "dolphin_dof_blur_horizontal"
}
}
],
"assets": [
{
"data": {
"metadata": "blur.rastershader",
"pixel_shader": "blur.ps.glsl",
"vertex_shader": "blur.vs.glsl"
},
"name": "dolphin_dof_blur"
},
{
"data": {
"metadata": "blur_horizontal.rastermaterial"
},
"name": "dolphin_dof_blur_horizontal"
},
{
"data": {
"metadata": "blur_vertical.rastermaterial"
},
"name": "dolphin_dof_blur_vertical"
}
]
}
Whitespace-only changes.
@@ -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"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,65 @@
{
"meta":
{
"title": "Bloom Blurred",
"author": "Dolphin Team",
"description": "Modifies bloom effects to blur them to their native resolution. Results in bloom looking much more natural at higher resolutions but the blur may be too intense in some games."
},
"groups":
[
{
"name": "CustomBloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000134_20x14_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000022_40x28_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000131_80x56_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000130_160x112_6"
}
]
}
],
"features":
[
{
"group": "CustomBloom",
"action": "custom_pipeline",
"action_data":
{
"material_asset": "dolphin_bloom_blur_horizontal"
}
}
],
"assets": [
{
"data": {
"metadata": "../../All Games Blurred Bloom/blur.rastershader",
"pixel_shader": "../../All Games Blurred Bloom/blur.ps.glsl",
"vertex_shader": "../../All Games Blurred Bloom/blur.vs.glsl"
},
"name": "dolphin_bloom_blur"
},
{
"data": {
"metadata": "../../All Games Blurred Bloom/blur_horizontal.rastermaterial"
},
"name": "dolphin_bloom_blur_horizontal"
},
{
"data": {
"metadata": "../../All Games Blurred Bloom/blur_vertical.rastermaterial"
},
"name": "dolphin_bloom_blur_vertical"
}
]
}
Whitespace-only changes.
@@ -0,0 +1,64 @@
{
"meta":
{
"title": "Bloom Removal",
"author": "Dolphin Team",
"description": "Skips drawing bloom effects. May be preferable when using a bloom solution from Dolphin's post processing shaders or a third party tool. Requires enabling 'EFB Copy To Texture Only'"
},
"groups":
[
{
"name": "CustomBloom",
"targets": [
{
"type": "draw_started",
"texture_filename": "efb1_n000134_20x14_6"
},
{
"type": "draw_started",
"texture_filename": "efb1_n000022_40x28_6"
},
{
"type": "draw_started",
"texture_filename": "efb1_n000131_80x56_6"
},
{
"type": "draw_started",
"texture_filename": "efb1_n000130_160x112_6"
}
]
},
{
"name": "Fixup",
"targets": [
{
"type": "draw_started",
"texture_filename": "tex1_256x179_802b7563f35b613a_6"
},
{
"type": "draw_started",
"texture_filename": "efb1_n000182_640x448_5"
},
{
"type": "draw_started",
"texture_filename": "efb1_n000380_320x224_6"
},
{
"type": "draw_started",
"texture_filename": "efb1_n000380_240x224_6"
}
]
}
],
"features":
[
{
"group": "CustomBloom",
"action": "skip"
},
{
"group": "Fixup",
"action": "skip"
}
]
}
Whitespace-only changes.
@@ -0,0 +1,65 @@
{
"meta":
{
"title": "Bloom Blurred",
"author": "Dolphin Team",
"description": "Modifies bloom effects to blur them to their native resolution. Results in bloom looking much more natural at higher resolutions but the blur may be too intense in some games."
},
"groups":
[
{
"name": "CustomBloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000134_20x14_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000022_40x28_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000131_80x56_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000130_160x112_6"
}
]
}
],
"features":
[
{
"group": "CustomBloom",
"action": "custom_pipeline",
"action_data":
{
"material_asset": "dolphin_bloom_blur_horizontal"
}
}
],
"assets": [
{
"data": {
"metadata": "../../All Games Blurred Bloom/blur.rastershader",
"pixel_shader": "../../All Games Blurred Bloom/blur.ps.glsl",
"vertex_shader": "../../All Games Blurred Bloom/blur.vs.glsl"
},
"name": "dolphin_bloom_blur"
},
{
"data": {
"metadata": "../../All Games Blurred Bloom/blur_horizontal.rastermaterial"
},
"name": "dolphin_bloom_blur_horizontal"
},
{
"data": {
"metadata": "../../All Games Blurred Bloom/blur_vertical.rastermaterial"
},
"name": "dolphin_bloom_blur_vertical"
}
]
}
Whitespace-only changes.
@@ -0,0 +1,64 @@
{
"meta":
{
"title": "Bloom Removal",
"author": "Dolphin Team",
"description": "Skips drawing bloom effects. May be preferable when using a bloom solution from Dolphin's post processing shaders or a third party tool."
},
"groups":
[
{
"name": "CustomBloom",
"targets": [
{
"type": "draw_started",
"texture_filename": "efb1_n000134_20x14_6"
},
{
"type": "draw_started",
"texture_filename": "efb1_n000022_40x28_6"
},
{
"type": "draw_started",
"texture_filename": "efb1_n000131_80x56_6"
},
{
"type": "draw_started",
"texture_filename": "efb1_n000130_160x112_6"
}
]
},
{
"name": "Fixup",
"targets": [
{
"type": "draw_started",
"texture_filename": "tex1_240x224_05a5e9c230d056cc_6"
},
{
"type": "draw_started",
"texture_filename": "efb1_n000182_640x448_5"
},
{
"type": "draw_started",
"texture_filename": "efb1_n000380_320x224_6"
},
{
"type": "draw_started",
"texture_filename": "efb1_n000380_240x224_6"
}
]
}
],
"features":
[
{
"group": "CustomBloom",
"action": "skip"
},
{
"group": "Fixup",
"action": "skip"
}
]
}
Whitespace-only changes.
@@ -0,0 +1,19 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000038_160x120_6"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,19 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000038_160x120_6"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,19 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000167_80x58_6"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,19 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000043_80x56_6"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,19 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000013_80x60_6"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,19 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000015_320x224_6"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,19 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000015_320x224_6"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,23 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000069_80x60_4"
},
{
"type": "efb",
"texture_filename": "efb1_n000068_160x120_4"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,23 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "autofire372"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000023_320x224_4"
},
{
"type": "efb",
"texture_filename": "efb1_n000024_320x224_4"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,19 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "Stalin15"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000010_80x60_6"
}
]
}
]
}
@@ -2,7 +2,7 @@
"meta": "meta":
{ {
"title": "Bloom and DOF Texture Definitions", "title": "Bloom and DOF Texture Definitions",
"author": "linckandrea" "author": "linckandrea, iwubcode"
}, },
"groups": "groups":
[ [
@@ -15,7 +15,7 @@
}, },
{ {
"type": "efb", "type": "efb",
"texture_filename": "efb1_n21_20x15_1" "texture_filename": "efb1_n21_40x30_1"
} }
] ]
}, },
@@ -25,10 +25,6 @@
{ {
"type": "efb", "type": "efb",
"texture_filename": "efb1_n10_320x240_4" "texture_filename": "efb1_n10_320x240_4"
},
{
"type": "efb",
"texture_filename": "efb1_n11_320x240_1"
} }
] ]
} }
Whitespace-only changes.
@@ -0,0 +1,23 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000010_82x58_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000011_42x30_6"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,23 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000010_82x58_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000011_42x30_6"
}
]
}
]
}
@@ -2,7 +2,7 @@
"meta": "meta":
{ {
"title": "Bloom Texture Definitions", "title": "Bloom Texture Definitions",
"author": "Silent Hell" "author": "Silent Hell, FrankyBuster"
}, },
"groups": "groups":
[ [
@@ -13,37 +13,17 @@
"type": "efb", "type": "efb",
"texture_filename": "efb1_n000007_160x120_6" "texture_filename": "efb1_n000007_160x120_6"
}, },
{
"type": "efb",
"texture_filename": "efb1_n000008_160x120_6"
},
{ {
"type": "efb", "type": "efb",
"texture_filename": "efb1_n000009_80x60_6" "texture_filename": "efb1_n000009_80x60_6"
}, },
{
"type": "efb",
"texture_filename": "efb1_n000010_80x60_6"
},
{ {
"type": "efb", "type": "efb",
"texture_filename": "efb1_n000011_40x30_6" "texture_filename": "efb1_n000011_40x30_6"
}, },
{
"type": "efb",
"texture_filename": "efb1_n000012_40x30_6"
},
{ {
"type": "efb", "type": "efb",
"texture_filename": "efb1_n000013_20x15_6" "texture_filename": "efb1_n000013_20x15_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000014_20x15_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000015_160x120_6"
} }
] ]
} }
Whitespace-only changes.
@@ -0,0 +1,23 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "Dr. Azathoth"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_320x240_6"
},
{
"type": "efb",
"texture_filename": "efb1_640x480_1"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,19 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000690_80x56_6"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,23 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000102_80x66_6"
},
{
"type": "efb",
"texture_filename": "efb1_n000025_320x232_6"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,19 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000038_80x60_4"
}
]
}
]
}
Whitespace-only changes.
@@ -0,0 +1,19 @@
{
"meta":
{
"title": "Bloom Texture Definitions",
"author": "iwubcode"
},
"groups":
[
{
"name": "Bloom",
"targets": [
{
"type": "efb",
"texture_filename": "efb1_n000102_80x66_6"
}
]
}
]
}
@@ -10,6 +10,7 @@
#include <picojson.h> #include <picojson.h>
#include "Common/CommonPaths.h" #include "Common/CommonPaths.h"
#include "Common/FileSearch.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/JsonUtil.h" #include "Common/JsonUtil.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
@@ -84,13 +85,22 @@ void GraphicsModGroupConfig::Load()
const auto try_add_mod = [&known_paths, this](const std::string& dir, const auto try_add_mod = [&known_paths, this](const std::string& dir,
GraphicsModConfig::Source source) { GraphicsModConfig::Source source) {
auto file = dir + DIR_SEP + "metadata.json"; const auto files = Common::DoFileSearch(dir, ".json", true);
UnifyPathSeparators(file); for (const auto& file : files)
if (known_paths.contains(file)) {
return; std::string basename;
SplitPath(file, nullptr, &basename, nullptr);
if (basename == "metadata")
{
auto file_copy = file;
UnifyPathSeparators(file_copy);
if (known_paths.contains(file_copy))
return;
if (auto mod = GraphicsModConfig::Create(file, source)) if (auto mod = GraphicsModConfig::Create(file_copy, source))
m_graphics_mods.push_back(std::move(*mod)); m_graphics_mods.push_back(std::move(*mod));
}
}
}; };
const std::set<std::string> graphics_mod_user_directories = const std::set<std::string> graphics_mod_user_directories =
@@ -3,7 +3,11 @@
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h" #include "VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h"
#include "Common/JsonUtil.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Core/System.h"
#include "VideoCommon/Resources/CustomResourceManager.h"
std::unique_ptr<CustomPipelineAction> std::unique_ptr<CustomPipelineAction>
CustomPipelineAction::Create(std::shared_ptr<VideoCommon::CustomAssetLibrary> library) CustomPipelineAction::Create(std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
@@ -15,58 +19,16 @@ std::unique_ptr<CustomPipelineAction>
CustomPipelineAction::Create(const picojson::value& json_data, CustomPipelineAction::Create(const picojson::value& json_data,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library) std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
{ {
std::vector<CustomPipelineAction::PipelinePassPassDescription> pipeline_passes; auto material_asset = ReadStringFromJson(json_data.get<picojson::object>(), "material_asset");
const auto& passes_json = json_data.get("passes"); if (!material_asset)
if (passes_json.is<picojson::array>())
{ {
for (const auto& passes_json_val : passes_json.get<picojson::array>()) ERROR_LOG_FMT(VIDEO,
{ "Failed to load custom pipeline action, 'material_asset' does not have a value");
CustomPipelineAction::PipelinePassPassDescription pipeline_pass;
if (!passes_json_val.is<picojson::object>())
{
ERROR_LOG_FMT(VIDEO,
"Failed to load custom pipeline action, 'passes' has an array value that "
"is not an object!");
return nullptr;
}
auto pass = passes_json_val.get<picojson::object>();
if (!pass.contains("pixel_material_asset"))
{
ERROR_LOG_FMT(VIDEO,
"Failed to load custom pipeline action, 'passes' value missing required "
"field 'pixel_material_asset'");
return nullptr;
}
auto pixel_material_asset_json = pass["pixel_material_asset"];
if (!pixel_material_asset_json.is<std::string>())
{
ERROR_LOG_FMT(VIDEO, "Failed to load custom pipeline action, 'passes' field "
"'pixel_material_asset' is not a string!");
return nullptr;
}
pipeline_pass.m_pixel_material_asset = pixel_material_asset_json.to_str();
pipeline_passes.push_back(std::move(pipeline_pass));
}
}
if (pipeline_passes.empty())
{
ERROR_LOG_FMT(VIDEO, "Failed to load custom pipeline action, must specify at least one pass");
return nullptr; return nullptr;
} }
if (pipeline_passes.size() > 1) return std::make_unique<CustomPipelineAction>(std::move(library), std::move(*material_asset));
{
ERROR_LOG_FMT(
VIDEO,
"Failed to load custom pipeline action, multiple passes are not currently supported");
return nullptr;
}
return std::make_unique<CustomPipelineAction>(std::move(library), std::move(pipeline_passes));
} }
CustomPipelineAction::CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAssetLibrary> library) CustomPipelineAction::CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
@@ -74,14 +36,26 @@ CustomPipelineAction::CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAs
{ {
} }
CustomPipelineAction::CustomPipelineAction( CustomPipelineAction::CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAssetLibrary> library,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library, std::string material_asset)
std::vector<PipelinePassPassDescription> pass_descriptions) : m_library(std::move(library)), m_material_asset(std::move(material_asset))
: m_library(std::move(library)), m_passes_config(std::move(pass_descriptions))
{ {
m_pipeline_passes.resize(m_passes_config.size());
} }
void CustomPipelineAction::OnDrawStarted(GraphicsModActionData::DrawStarted*) void CustomPipelineAction::OnDrawStarted(GraphicsModActionData::DrawStarted*)
{ {
// TODO
}
void CustomPipelineAction::AfterEFB(GraphicsModActionData::PostEFB* post_efb)
{
if (!post_efb) [[unlikely]]
return;
if (m_material_asset.empty())
return;
auto& resource_manager = Core::System::GetInstance().GetCustomResourceManager();
post_efb->material =
resource_manager.GetPostProcessingMaterialFromAsset(m_material_asset, m_library);
} }
@@ -6,22 +6,15 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <vector>
#include <picojson.h> #include <picojson.h>
#include "VideoCommon/Assets/CustomAssetLibrary.h" #include "VideoCommon/Assets/CustomAssetLibrary.h"
#include "VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
class CustomPipelineAction final : public GraphicsModAction class CustomPipelineAction final : public GraphicsModAction
{ {
public: public:
struct PipelinePassPassDescription
{
std::string m_pixel_material_asset;
};
static constexpr std::string_view factory_name = "custom_pipeline"; static constexpr std::string_view factory_name = "custom_pipeline";
static std::unique_ptr<CustomPipelineAction> static std::unique_ptr<CustomPipelineAction>
Create(const picojson::value& json_data, Create(const picojson::value& json_data,
@@ -30,11 +23,11 @@ public:
Create(std::shared_ptr<VideoCommon::CustomAssetLibrary> library); Create(std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
explicit CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAssetLibrary> library); explicit CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAssetLibrary> library, CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAssetLibrary> library,
std::vector<PipelinePassPassDescription> pass_descriptions); std::string material_asset);
void OnDrawStarted(GraphicsModActionData::DrawStarted*) override; void OnDrawStarted(GraphicsModActionData::DrawStarted*) override;
void AfterEFB(GraphicsModActionData::PostEFB*) override;
private: private:
std::shared_ptr<VideoCommon::CustomAssetLibrary> m_library; std::shared_ptr<VideoCommon::CustomAssetLibrary> m_library;
std::vector<PipelinePassPassDescription> m_passes_config; std::string m_material_asset;
std::vector<CustomPipeline> m_pipeline_passes;
}; };
@@ -10,7 +10,7 @@ void PrintAction::OnDrawStarted(GraphicsModActionData::DrawStarted*)
INFO_LOG_FMT(VIDEO, "OnDrawStarted Called"); INFO_LOG_FMT(VIDEO, "OnDrawStarted Called");
} }
void PrintAction::OnEFB(GraphicsModActionData::EFB* efb) void PrintAction::BeforeEFB(GraphicsModActionData::PreEFB* efb)
{ {
if (!efb) [[unlikely]] if (!efb) [[unlikely]]
return; return;
@@ -12,7 +12,7 @@ class PrintAction final : public GraphicsModAction
public: public:
static constexpr std::string_view factory_name = "print"; static constexpr std::string_view factory_name = "print";
void OnDrawStarted(GraphicsModActionData::DrawStarted*) override; void OnDrawStarted(GraphicsModActionData::DrawStarted*) override;
void OnEFB(GraphicsModActionData::EFB*) override; void BeforeEFB(GraphicsModActionData::PreEFB*) override;
void OnProjection(GraphicsModActionData::Projection*) override; void OnProjection(GraphicsModActionData::Projection*) override;
void OnProjectionAndTexture(GraphicsModActionData::Projection*) override; void OnProjectionAndTexture(GraphicsModActionData::Projection*) override;
void OnTextureLoad(GraphicsModActionData::TextureLoad*) override; void OnTextureLoad(GraphicsModActionData::TextureLoad*) override;
@@ -30,7 +30,7 @@ ScaleAction::ScaleAction(Common::Vec3 scale) : m_scale(scale)
{ {
} }
void ScaleAction::OnEFB(GraphicsModActionData::EFB* efb) void ScaleAction::BeforeEFB(GraphicsModActionData::PreEFB* efb)
{ {
if (!efb) [[unlikely]] if (!efb) [[unlikely]]
return; return;
@@ -16,7 +16,7 @@ public:
static constexpr std::string_view factory_name = "scale"; static constexpr std::string_view factory_name = "scale";
static std::unique_ptr<ScaleAction> Create(const picojson::value& json_data); static std::unique_ptr<ScaleAction> Create(const picojson::value& json_data);
explicit ScaleAction(Common::Vec3 scale); explicit ScaleAction(Common::Vec3 scale);
void OnEFB(GraphicsModActionData::EFB*) override; void BeforeEFB(GraphicsModActionData::PreEFB*) override;
void OnProjection(GraphicsModActionData::Projection*) override; void OnProjection(GraphicsModActionData::Projection*) override;
void OnProjectionAndTexture(GraphicsModActionData::Projection*) override; void OnProjectionAndTexture(GraphicsModActionData::Projection*) override;
@@ -14,7 +14,7 @@ void SkipAction::OnDrawStarted(GraphicsModActionData::DrawStarted* draw_started)
*draw_started->skip = true; *draw_started->skip = true;
} }
void SkipAction::OnEFB(GraphicsModActionData::EFB* efb) void SkipAction::BeforeEFB(GraphicsModActionData::PreEFB* efb)
{ {
if (!efb) [[unlikely]] if (!efb) [[unlikely]]
return; return;
@@ -10,5 +10,5 @@ class SkipAction final : public GraphicsModAction
public: public:
static constexpr std::string_view factory_name = "skip"; static constexpr std::string_view factory_name = "skip";
void OnDrawStarted(GraphicsModActionData::DrawStarted*) override; void OnDrawStarted(GraphicsModActionData::DrawStarted*) override;
void OnEFB(GraphicsModActionData::EFB*) override; void BeforeEFB(GraphicsModActionData::PreEFB*) override;
}; };
@@ -16,8 +16,9 @@ public:
GraphicsModAction& operator=(GraphicsModAction&&) = default; GraphicsModAction& operator=(GraphicsModAction&&) = default;
virtual void OnDrawStarted(GraphicsModActionData::DrawStarted*) {} virtual void OnDrawStarted(GraphicsModActionData::DrawStarted*) {}
virtual void OnEFB(GraphicsModActionData::EFB*) {} virtual void BeforeEFB(GraphicsModActionData::PreEFB*) {}
virtual void OnXFB() {} virtual void AfterEFB(GraphicsModActionData::PostEFB*) {}
virtual void BeforeXFB() {}
virtual void OnProjection(GraphicsModActionData::Projection*) {} virtual void OnProjection(GraphicsModActionData::Projection*) {}
virtual void OnProjectionAndTexture(GraphicsModActionData::Projection*) {} virtual void OnProjectionAndTexture(GraphicsModActionData::Projection*) {}
virtual void OnTextureLoad(GraphicsModActionData::TextureLoad*) {} virtual void OnTextureLoad(GraphicsModActionData::TextureLoad*) {}
@@ -12,6 +12,7 @@
#include "Common/Matrix.h" #include "Common/Matrix.h"
#include "Common/SmallVector.h" #include "Common/SmallVector.h"
#include "VideoCommon/Assets/TextureAsset.h" #include "VideoCommon/Assets/TextureAsset.h"
#include "VideoCommon/Resources/MaterialResource.h"
#include "VideoCommon/ShaderGenCommon.h" #include "VideoCommon/ShaderGenCommon.h"
namespace GraphicsModActionData namespace GraphicsModActionData
@@ -24,7 +25,7 @@ struct DrawStarted
std::span<u8>* material_uniform_buffer; std::span<u8>* material_uniform_buffer;
}; };
struct EFB struct PreEFB
{ {
u32 texture_width; u32 texture_width;
u32 texture_height; u32 texture_height;
@@ -33,6 +34,11 @@ struct EFB
u32* scaled_height; u32* scaled_height;
}; };
struct PostEFB
{
VideoCommon::MaterialResource* material = nullptr;
};
struct Projection struct Projection
{ {
Common::Matrix44* matrix; Common::Matrix44* matrix;
@@ -7,6 +7,7 @@
#include <string_view> #include <string_view>
#include <variant> #include <variant>
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "Common/VariantUtil.h" #include "Common/VariantUtil.h"
@@ -17,6 +18,7 @@
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h" #include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModAsset.h" #include "VideoCommon/GraphicsModSystem/Config/GraphicsModAsset.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h" #include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
#include "VideoCommon/GraphicsModSystem/Constants.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h"
#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoConfig.h"
#include "VideoCommon/VideoEvents.h" #include "VideoCommon/VideoEvents.h"
@@ -36,11 +38,17 @@ public:
return; return;
m_action_impl->OnDrawStarted(draw_started); m_action_impl->OnDrawStarted(draw_started);
} }
void OnEFB(GraphicsModActionData::EFB* efb) override void BeforeEFB(GraphicsModActionData::PreEFB* efb) override
{ {
if (!m_mod.m_enabled) if (!m_mod.m_enabled)
return; return;
m_action_impl->OnEFB(efb); m_action_impl->BeforeEFB(efb);
}
void AfterEFB(GraphicsModActionData::PostEFB* efb) override
{
if (!m_mod.m_enabled)
return;
m_action_impl->AfterEFB(efb);
} }
void OnProjection(GraphicsModActionData::Projection* projection) override void OnProjection(GraphicsModActionData::Projection* projection) override
{ {
@@ -191,6 +199,8 @@ void GraphicsModManager::Load(const GraphicsModGroupConfig& config)
const auto& mods = config.GetMods(); const auto& mods = config.GetMods();
auto filesystem_library = std::make_shared<VideoCommon::DirectFilesystemAssetLibrary>(); auto filesystem_library = std::make_shared<VideoCommon::DirectFilesystemAssetLibrary>();
filesystem_library->Watch(File::GetSysDirectory() + DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR);
filesystem_library->Watch(File::GetUserPath(D_GRAPHICSMOD_IDX));
std::map<std::string, std::vector<GraphicsTargetConfig>> group_to_targets; std::map<std::string, std::vector<GraphicsTargetConfig>> group_to_targets;
for (const auto& mod : mods) for (const auto& mod : mods)
@@ -43,7 +43,8 @@ void CustomResourceManager::Shutdown()
void CustomResourceManager::Reset() void CustomResourceManager::Reset()
{ {
m_material_resources.clear(); m_draw_material_resources.clear();
m_postprocessing_material_resources.clear();
m_shader_resources.clear(); m_shader_resources.clear();
m_texture_data_resources.clear(); m_texture_data_resources.clear();
m_texture_sampler_resources.clear(); m_texture_sampler_resources.clear();
@@ -102,11 +103,11 @@ TextureDataResource* CustomResourceManager::GetTextureDataFromAsset(
return resource.get(); return resource.get();
} }
MaterialResource* CustomResourceManager::GetMaterialFromAsset( MaterialResource* CustomResourceManager::GetDrawMaterialFromAsset(
const CustomAssetLibrary::AssetID& asset_id, const GXPipelineUid& pipeline_uid, const CustomAssetLibrary::AssetID& asset_id, const GXPipelineUid& pipeline_uid,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library) std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
{ {
auto& resource = m_material_resources[asset_id][PipelineToHash(pipeline_uid)]; auto& resource = m_draw_material_resources[asset_id][PipelineToHash(pipeline_uid)];
if (resource == nullptr) if (resource == nullptr)
{ {
resource = std::make_unique<MaterialResource>( resource = std::make_unique<MaterialResource>(
@@ -116,11 +117,24 @@ MaterialResource* CustomResourceManager::GetMaterialFromAsset(
return resource.get(); return resource.get();
} }
ShaderResource* MaterialResource* CustomResourceManager::GetPostProcessingMaterialFromAsset(
CustomResourceManager::GetShaderFromAsset(const CustomAssetLibrary::AssetID& asset_id, const CustomAssetLibrary::AssetID& asset_id,
std::size_t shader_key, const GXPipelineUid& pipeline_uid, std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
const std::string& preprocessor_settings, {
std::shared_ptr<VideoCommon::CustomAssetLibrary> library) auto& resource = m_postprocessing_material_resources[asset_id];
if (resource == nullptr)
{
resource =
std::make_unique<MaterialResource>(CreateResourceContext(asset_id, std::move(library)));
}
resource->Process();
return resource.get();
}
ShaderResource* CustomResourceManager::GetShaderFromAsset(
const CustomAssetLibrary::AssetID& asset_id, std::size_t shader_key,
const std::optional<GXPipelineUid>& pipeline_uid, const std::string& preprocessor_settings,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
{ {
auto& resource = m_shader_resources[asset_id][shader_key]; auto& resource = m_shader_resources[asset_id][shader_key];
if (resource == nullptr) if (resource == nullptr)
@@ -37,14 +37,20 @@ public:
TextureDataResource* TextureDataResource*
GetTextureDataFromAsset(const CustomAssetLibrary::AssetID& asset_id, GetTextureDataFromAsset(const CustomAssetLibrary::AssetID& asset_id,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library); std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
MaterialResource* GetMaterialFromAsset(const CustomAssetLibrary::AssetID& asset_id, MaterialResource*
const GXPipelineUid& pipeline_uid, GetDrawMaterialFromAsset(const CustomAssetLibrary::AssetID& asset_id,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library); const GXPipelineUid& pipeline_uid,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
MaterialResource*
GetPostProcessingMaterialFromAsset(const CustomAssetLibrary::AssetID& asset_id,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
ShaderResource* GetShaderFromAsset(const CustomAssetLibrary::AssetID& asset_id, ShaderResource* GetShaderFromAsset(const CustomAssetLibrary::AssetID& asset_id,
std::size_t shader_key, const GXPipelineUid& pipeline_uid, std::size_t shader_key,
const std::optional<GXPipelineUid>& pipeline_uid,
const std::string& preprocessor_settings, const std::string& preprocessor_settings,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library); std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
TextureAndSamplerResource* TextureAndSamplerResource*
GetTextureAndSamplerFromAsset(const CustomAssetLibrary::AssetID& asset_id, GetTextureAndSamplerFromAsset(const CustomAssetLibrary::AssetID& asset_id,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library); std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
@@ -59,7 +65,7 @@ private:
std::unique_ptr<AsyncShaderCompiler> m_async_shader_compiler; std::unique_ptr<AsyncShaderCompiler> m_async_shader_compiler;
using PipelineIdToMaterial = std::map<std::size_t, std::unique_ptr<MaterialResource>>; using PipelineIdToMaterial = std::map<std::size_t, std::unique_ptr<MaterialResource>>;
std::map<CustomAssetLibrary::AssetID, PipelineIdToMaterial> m_material_resources; std::map<CustomAssetLibrary::AssetID, PipelineIdToMaterial> m_draw_material_resources;
using ShaderKeyToShader = std::map<std::size_t, std::unique_ptr<ShaderResource>>; using ShaderKeyToShader = std::map<std::size_t, std::unique_ptr<ShaderResource>>;
std::map<CustomAssetLibrary::AssetID, ShaderKeyToShader> m_shader_resources; std::map<CustomAssetLibrary::AssetID, ShaderKeyToShader> m_shader_resources;
@@ -70,6 +76,9 @@ private:
std::map<CustomAssetLibrary::AssetID, std::unique_ptr<TextureAndSamplerResource>> std::map<CustomAssetLibrary::AssetID, std::unique_ptr<TextureAndSamplerResource>>
m_texture_sampler_resources; m_texture_sampler_resources;
std::map<CustomAssetLibrary::AssetID, std::unique_ptr<MaterialResource>>
m_postprocessing_material_resources;
ShaderHostConfig m_host_config; ShaderHostConfig m_host_config;
Common::EventHook m_xfb_event; Common::EventHook m_xfb_event;
@@ -54,6 +54,13 @@ SamplerState CalculateSamplerAnisotropy(const SamplerState& initial_sampler)
namespace VideoCommon namespace VideoCommon
{ {
MaterialResource::MaterialResource(Resource::ResourceContext resource_context)
: Resource(std::move(resource_context))
{
m_material_asset = m_resource_context.asset_cache->CreateAsset<MaterialAsset>(
m_resource_context.primary_asset_id, m_resource_context.asset_library, this);
}
MaterialResource::MaterialResource(Resource::ResourceContext resource_context, MaterialResource::MaterialResource(Resource::ResourceContext resource_context,
const GXPipelineUid& pipeline_uid) const GXPipelineUid& pipeline_uid)
: Resource(std::move(resource_context)), m_uid(pipeline_uid) : Resource(std::move(resource_context)), m_uid(pipeline_uid)
@@ -61,8 +68,8 @@ MaterialResource::MaterialResource(Resource::ResourceContext resource_context,
m_material_asset = m_resource_context.asset_cache->CreateAsset<MaterialAsset>( m_material_asset = m_resource_context.asset_cache->CreateAsset<MaterialAsset>(
m_resource_context.primary_asset_id, m_resource_context.asset_library, this); m_resource_context.primary_asset_id, m_resource_context.asset_library, this);
m_uid_vertex_format_copy = m_uid_vertex_format_copy =
g_gfx->CreateNativeVertexFormat(m_uid.vertex_format->GetVertexDeclaration()); g_gfx->CreateNativeVertexFormat(m_uid->vertex_format->GetVertexDeclaration());
m_uid.vertex_format = m_uid_vertex_format_copy.get(); m_uid->vertex_format = m_uid_vertex_format_copy.get();
} }
void MaterialResource::ResetData() void MaterialResource::ResetData()
@@ -98,7 +105,7 @@ Resource::TaskComplete MaterialResource::CollectPrimaryData()
} }
CreateTextureData(m_load_data.get()); CreateTextureData(m_load_data.get());
SetShaderKey(m_load_data.get(), &m_uid); SetShaderKey(m_load_data.get(), m_uid ? &*m_uid : nullptr);
return Resource::TaskComplete::Yes; return Resource::TaskComplete::Yes;
} }
@@ -140,8 +147,18 @@ Resource::TaskComplete MaterialResource::CollectDependencyData()
if (m_load_data->m_material_data->next_material_asset != "") if (m_load_data->m_material_data->next_material_asset != "")
{ {
m_load_data->m_next_material = m_resource_context.resource_manager->GetMaterialFromAsset( if (m_uid)
m_load_data->m_material_data->next_material_asset, m_uid, m_resource_context.asset_library); {
m_load_data->m_next_material = m_resource_context.resource_manager->GetDrawMaterialFromAsset(
m_load_data->m_material_data->next_material_asset, *m_uid,
m_resource_context.asset_library);
}
else
{
m_load_data->m_next_material =
m_resource_context.resource_manager->GetPostProcessingMaterialFromAsset(
m_load_data->m_material_data->next_material_asset, m_resource_context.asset_library);
}
m_load_data->m_next_material->AddReference(this); m_load_data->m_next_material->AddReference(this);
const auto data_processed = m_load_data->m_next_material->IsDataProcessed(); const auto data_processed = m_load_data->m_next_material->IsDataProcessed();
if (data_processed == TaskComplete::Error) if (data_processed == TaskComplete::Error)
@@ -222,33 +239,66 @@ Resource::TaskComplete MaterialResource::ProcessData()
config.pixel_shader = m_shader_resource_data->GetPixelShader(); config.pixel_shader = m_shader_resource_data->GetPixelShader();
config.geometry_shader = m_shader_resource_data->GetGeometryShader(); config.geometry_shader = m_shader_resource_data->GetGeometryShader();
const auto actual_uid = ApplyDriverBugs(*m_uid); if (m_uid)
if (m_material_resource_data->m_material_data->blending_state)
config.blending_state = *m_material_resource_data->m_material_data->blending_state;
else
config.blending_state = actual_uid.blending_state;
if (m_material_resource_data->m_material_data->depth_state)
config.depth_state = *m_material_resource_data->m_material_data->depth_state;
else
config.depth_state = actual_uid.depth_state;
config.framebuffer_state = std::move(m_frame_buffer_state);
config.framebuffer_state.additional_color_attachment_count = 0;
config.rasterization_state = actual_uid.rasterization_state;
if (m_material_resource_data->m_material_data->cull_mode)
{ {
config.rasterization_state.cull_mode = // Draw based pipeline
*m_material_resource_data->m_material_data->cull_mode; const auto actual_uid = ApplyDriverBugs(*m_uid);
if (m_material_resource_data->m_material_data->blending_state)
config.blending_state = *m_material_resource_data->m_material_data->blending_state;
else
config.blending_state = actual_uid.blending_state;
if (m_material_resource_data->m_material_data->depth_state)
config.depth_state = *m_material_resource_data->m_material_data->depth_state;
else
config.depth_state = actual_uid.depth_state;
config.framebuffer_state = std::move(m_frame_buffer_state);
config.framebuffer_state.additional_color_attachment_count = 0;
config.rasterization_state = actual_uid.rasterization_state;
if (m_material_resource_data->m_material_data->cull_mode)
{
config.rasterization_state.cull_mode =
*m_material_resource_data->m_material_data->cull_mode;
}
config.vertex_format = actual_uid.vertex_format;
config.usage = AbstractPipelineUsage::GX;
}
else
{
// Post processing based pipeline
// Many of these properties don't make sense to replace but we expose them for draw
// based materials, might as well allow them to be used if the user wants
if (m_material_resource_data->m_material_data->blending_state)
config.blending_state = *m_material_resource_data->m_material_data->blending_state;
else
config.blending_state = RenderState::GetNoBlendingBlendState();
if (m_material_resource_data->m_material_data->depth_state)
config.depth_state = *m_material_resource_data->m_material_data->depth_state;
else
config.depth_state = RenderState::GetNoDepthTestingDepthState();
config.framebuffer_state = RenderState::GetRGBA8FramebufferState();
config.rasterization_state =
RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles);
if (m_material_resource_data->m_material_data->cull_mode)
{
config.rasterization_state.cull_mode =
*m_material_resource_data->m_material_data->cull_mode;
}
config.vertex_format = nullptr;
config.usage = AbstractPipelineUsage::Utility;
} }
config.vertex_format = actual_uid.vertex_format;
config.usage = AbstractPipelineUsage::GX;
m_material_resource_data->m_pipeline = g_gfx->CreatePipeline(config); m_material_resource_data->m_pipeline = g_gfx->CreatePipeline(config);
if (m_material_resource_data->m_pipeline) if (m_material_resource_data->m_pipeline)
{ {
WriteUniforms(m_material_resource_data.get()); WriteUniforms(m_material_resource_data.get());
@@ -268,7 +318,7 @@ Resource::TaskComplete MaterialResource::ProcessData()
if (!m_processing_load_data) if (!m_processing_load_data)
{ {
auto wi = m_resource_context.shader_compiler->CreateWorkItem<WorkItem>( auto wi = m_resource_context.shader_compiler->CreateWorkItem<WorkItem>(
m_load_data, std::move(shader_data), &m_uid, m_load_data, std::move(shader_data), m_uid ? &*m_uid : nullptr,
g_framebuffer_manager->GetEFBFramebufferState()); g_framebuffer_manager->GetEFBFramebufferState());
// We don't need priority, that is already handled by the resource system // We don't need priority, that is already handled by the resource system
@@ -360,7 +410,8 @@ void MaterialResource::SetShaderKey(Data* data, GXPipelineUid* uid)
XXH3_INITSTATE(&shader_key_hash); XXH3_INITSTATE(&shader_key_hash);
XXH3_64bits_reset_withSeed(&shader_key_hash, static_cast<XXH64_hash_t>(1)); XXH3_64bits_reset_withSeed(&shader_key_hash, static_cast<XXH64_hash_t>(1));
UpdateHashWithPipeline(*uid, &shader_key_hash); if (uid)
UpdateHashWithPipeline(*uid, &shader_key_hash);
XXH3_64bits_update(&shader_key_hash, data->m_preprocessor_settings.c_str(), XXH3_64bits_update(&shader_key_hash, data->m_preprocessor_settings.c_str(),
data->m_preprocessor_settings.size()); data->m_preprocessor_settings.size());
@@ -5,6 +5,7 @@
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <optional>
#include <string_view> #include <string_view>
#include <variant> #include <variant>
#include <vector> #include <vector>
@@ -27,6 +28,7 @@ namespace VideoCommon
class MaterialResource final : public Resource class MaterialResource final : public Resource
{ {
public: public:
explicit MaterialResource(Resource::ResourceContext resource_context);
MaterialResource(Resource::ResourceContext resource_context, const GXPipelineUid& pipeline_uid); MaterialResource(Resource::ResourceContext resource_context, const GXPipelineUid& pipeline_uid);
struct TextureLikeReference struct TextureLikeReference
@@ -93,7 +95,9 @@ private:
// Note: asset cache owns the asset, we access as a reference // Note: asset cache owns the asset, we access as a reference
MaterialAsset* m_material_asset = nullptr; MaterialAsset* m_material_asset = nullptr;
GXPipelineUid m_uid; // If provided, denotes this material will be used as a custom draw material.
// If not provided, denotes this will be used as an efb post processing material.
std::optional<GXPipelineUid> m_uid;
std::unique_ptr<NativeVertexFormat> m_uid_vertex_format_copy; std::unique_ptr<NativeVertexFormat> m_uid_vertex_format_copy;
}; };
} // namespace VideoCommon } // namespace VideoCommon
@@ -10,6 +10,7 @@
#include "VideoCommon/AbstractGfx.h" #include "VideoCommon/AbstractGfx.h"
#include "VideoCommon/Assets/CustomAssetCache.h" #include "VideoCommon/Assets/CustomAssetCache.h"
#include "VideoCommon/AsyncShaderCompiler.h" #include "VideoCommon/AsyncShaderCompiler.h"
#include "VideoCommon/FramebufferShaderGen.h"
#include "VideoCommon/GeometryShaderGen.h" #include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/PipelineUtils.h" #include "VideoCommon/PipelineUtils.h"
#include "VideoCommon/PixelShaderGen.h" #include "VideoCommon/PixelShaderGen.h"
@@ -20,20 +21,223 @@ namespace VideoCommon
{ {
namespace namespace
{ {
std::unique_ptr<AbstractShader> // TODO: the uniform buffer is combined due to the utility path only having a single
CompileGeometryShader(const GeometryShaderUid& uid, APIType api_type, ShaderHostConfig host_config) // set of constants, this isn't ideal (both for the end user, where it's more readable
// to see 'custom_uniforms' before variables and from Dolphin because the
// standard uniforms have to be packed with the custom ones
void GeneratePostProcessUniformOutput(ShaderCode& shader_source, std::string_view block_name,
std::string_view uniforms)
{ {
const ShaderCode source_code = shader_source.Write("UBO_BINDING(std140, 1) uniform {} {{\n", block_name);
GenerateGeometryShaderCode(api_type, host_config, uid.GetUidData()); shader_source.Write("\tvec4 source_resolution;\n");
return g_gfx->CreateShaderFromSource(ShaderStage::Geometry, source_code.GetBuffer(), nullptr, shader_source.Write("\tvec4 target_resolution;\n");
fmt::format("Geometry shader: {}", *uid.GetUidData())); shader_source.Write("\tvec4 window_resolution;\n");
shader_source.Write("\tvec4 source_region;\n");
shader_source.Write("\tint source_layer;\n");
shader_source.Write("\tint source_layer_pad1;\n");
shader_source.Write("\tint source_layer_pad2;\n");
shader_source.Write("\tint source_layer_pad3;\n");
shader_source.Write("\tuint time;\n");
shader_source.Write("\tuint time_pad1;\n");
shader_source.Write("\tuint time_pad2;\n");
shader_source.Write("\tuint time_pad3;\n");
shader_source.Write("\tint graphics_api;\n");
shader_source.Write("\tint graphics_api_pad1;\n");
shader_source.Write("\tint graphics_api_pad2;\n");
shader_source.Write("\tint graphics_api_pad3;\n");
shader_source.Write("\tuint efb_scale;\n");
shader_source.Write("\tuint efb_scale_pad1;\n");
shader_source.Write("\tuint efb_scale_pad2;\n");
shader_source.Write("\tuint efb_scale_pad3;\n");
if (!uniforms.empty())
{
shader_source.Write("{}", uniforms);
}
shader_source.Write("}};\n");
} }
std::unique_ptr<AbstractShader> CompilePixelShader(const PixelShaderUid& uid, // TODO: move this to a more standard post processing file
std::string_view preprocessor_settings, // once post processing has been cleaned up
APIType api_type, void GeneratePostProcessingVertexShader(ShaderCode& shader_source,
const ShaderHostConfig& host_config, const CustomVertexContents& custom_contents)
RasterSurfaceShaderData* shader_data) {
// Note: if blocks are the same, they need to match for the OpenGL backend
GeneratePostProcessUniformOutput(shader_source, "PSBlock", custom_contents.uniforms);
// Define some defines that are shared with custom draw shaders,
// so that a common shader code could possibly be used in both
shader_source.Write("#define HAS_COLOR_0 0\n");
shader_source.Write("#define HAS_COLOR_1 0\n");
shader_source.Write("#define HAS_NORMAL 0\n");
shader_source.Write("#define HAS_BINORMAL 0\n");
shader_source.Write("#define HAS_TANGENT 0\n");
shader_source.Write("#define HAS_TEXTURE_COORD_0 1\n");
for (u32 i = 1; i < 8; i++)
{
shader_source.Write("#define HAS_TEXTURE_COORD_{} 0\n", i);
}
// Write the common structs, might want to consider
// moving these to another location?
shader_source.Write("struct DolphinVertexInput\n");
shader_source.Write("{{\n");
shader_source.Write("\tvec4 position;\n");
shader_source.Write("\tvec3 texture_coord_0;\n");
shader_source.Write("}};\n\n");
shader_source.Write("struct DolphinVertexOutput\n");
shader_source.Write("{{\n");
shader_source.Write("\tvec4 position;\n");
shader_source.Write("\tvec3 texture_coord_0;\n");
shader_source.Write("}};\n\n");
constexpr std::string_view emulated_vertex_definition =
"void dolphin_process_emulated_vertex(in DolphinVertexInput vertex_input, out "
"DolphinVertexOutput vertex_output)";
shader_source.Write("{}\n", emulated_vertex_definition);
shader_source.Write("{{\n");
shader_source.Write("\tvertex_output.position = vertex_input.position;\n");
shader_source.Write("\tvertex_output.texture_coord_0 = vertex_input.texture_coord_0;\n");
shader_source.Write("}}\n");
if (custom_contents.shader.empty())
{
shader_source.Write(
"void process_vertex(in DolphinVertexInput vertex_input, out DolphinVertexOutput "
"vertex_output)\n");
shader_source.Write("{{\n");
shader_source.Write("\tdolphin_process_emulated_vertex(vertex_input, vertex_output);\n");
shader_source.Write("}}\n");
}
else
{
shader_source.Write("{}\n", custom_contents.shader);
}
if (g_backend_info.bSupportsGeometryShaders)
{
shader_source.Write("VARYING_LOCATION(0) out VertexData {{\n");
shader_source.Write("\tvec3 v_tex0;\n");
shader_source.Write("}};\n");
}
else
{
shader_source.Write("VARYING_LOCATION(0) out vec3 v_tex0;\n");
}
shader_source.Write("void main()\n");
shader_source.Write("{{\n");
shader_source.Write("\tDolphinVertexInput vertex_input;\n");
shader_source.Write("\tvec3 vert = vec3(float((gl_VertexID << 1) & 2), "
"float(gl_VertexID & 2), 0.0f);\n");
shader_source.Write("\tvertex_input.position = vec4(vert.xy * "
"float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n");
shader_source.Write("\tvertex_input.texture_coord_0 = vec3(source_region.xy + "
"(source_region.zw * vert.xy), 0.0f);\n");
shader_source.Write("\tDolphinVertexOutput vertex_output;\n");
shader_source.Write("\tprocess_vertex(vertex_input, vertex_output);\n");
shader_source.Write("\tgl_Position = vertex_output.position;\n");
shader_source.Write("\tv_tex0 = vertex_output.texture_coord_0;\n");
// NDC space is flipped in Vulkan
if (g_backend_info.api_type == APIType::Vulkan)
{
shader_source.Write("\tgl_Position.y = -gl_Position.y;\n");
}
shader_source.Write("}}\n");
}
void GeneratePostProcessingPixelShader(ShaderCode& shader_source,
const CustomPixelContents& custom_contents)
{
GeneratePostProcessUniformOutput(shader_source, "PSBlock", custom_contents.uniforms);
shader_source.Write("SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n");
if (g_backend_info.bSupportsGeometryShaders)
{
shader_source.Write("VARYING_LOCATION(0) in VertexData {{\n");
shader_source.Write("\tvec3 v_tex0;\n");
shader_source.Write("}};\n");
}
else
{
shader_source.Write("VARYING_LOCATION(0) in float3 v_tex0;\n");
}
shader_source.Write("FRAGMENT_OUTPUT_LOCATION(0) out vec4 ocol0;\n");
shader_source.Write("struct DolphinFragmentInput\n");
shader_source.Write("{{\n");
for (u32 i = 0; i < 1; i++)
{
shader_source.Write("\tvec3 tex{};\n", i);
}
shader_source.Write("\n");
shader_source.Write("}};\n\n");
shader_source.Write("struct DolphinFragmentOutput\n");
shader_source.Write("{{\n");
shader_source.Write("\tvec4 main;\n");
shader_source.Write("}};\n\n");
constexpr std::string_view emulated_fragment_definition =
"void dolphin_process_emulated_fragment(in DolphinFragmentInput frag_input, out "
"DolphinFragmentOutput frag_output)";
shader_source.Write("{}\n", emulated_fragment_definition);
shader_source.Write("{{\n");
shader_source.Write("\tfrag_output.main = texture(samp0, frag_input.tex0);\n");
shader_source.Write("}}\n");
if (custom_contents.shader.empty())
{
shader_source.Write(
"void process_fragment(in DolphinFragmentInput frag_input, out DolphinFragmentOutput "
"frag_output)\n");
shader_source.Write("{{\n");
shader_source.Write("\tdolphin_process_emulated_fragment(frag_input, frag_output);\n");
shader_source.Write("}}\n");
}
else
{
shader_source.Write("{}\n", custom_contents.shader);
}
shader_source.Write("void main()\n");
shader_source.Write("{{\n");
shader_source.Write("\tDolphinFragmentInput frag_input;\n");
shader_source.Write("\tfrag_input.tex0 = v_tex0;\n");
shader_source.Write("\tDolphinFragmentOutput frag_output;\n");
shader_source.Write("\tprocess_fragment(frag_input, frag_output);\n");
shader_source.Write("\tocol0 = frag_output.main;\n");
shader_source.Write("}}\n");
}
std::unique_ptr<AbstractShader> CompileGeometryShader(GeometryShaderUid* uid, APIType api_type,
ShaderHostConfig host_config)
{
if (!uid)
{
return g_gfx->CreateShaderFromSource(
ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(1, 0),
nullptr, "Custom Post Processing Geometry Shader");
}
const ShaderCode source_code =
GenerateGeometryShaderCode(api_type, host_config, uid->GetUidData());
return g_gfx->CreateShaderFromSource(ShaderStage::Geometry, source_code.GetBuffer(), nullptr,
fmt::format("Geometry shader: {}", *uid->GetUidData()));
}
std::unique_ptr<AbstractShader>
CompilePixelShader(PixelShaderUid* uid, std::string_view preprocessor_settings, APIType api_type,
const ShaderHostConfig& host_config, RasterSurfaceShaderData* shader_data)
{ {
ShaderCode shader_code; ShaderCode shader_code;
@@ -89,19 +293,25 @@ std::unique_ptr<AbstractShader> CompilePixelShader(const PixelShaderUid& uid,
// Compile the shader // Compile the shader
CustomPixelContents contents{.shader = shader_code.GetBuffer(), CustomPixelContents contents{.shader = shader_code.GetBuffer(),
.uniforms = uniform_code.GetBuffer()}; .uniforms = uniform_code.GetBuffer()};
const ShaderCode source_code =
GeneratePixelShaderCode(api_type, host_config, uid.GetUidData(), contents); ShaderCode source_code;
if (uid)
{
source_code = GeneratePixelShaderCode(api_type, host_config, uid->GetUidData(), contents);
}
else
{
GeneratePostProcessingPixelShader(source_code, contents);
}
ShaderIncluder* shader_includer = ShaderIncluder* shader_includer =
shader_data->shader_includer ? &*shader_data->shader_includer : nullptr; shader_data->shader_includer ? &*shader_data->shader_includer : nullptr;
return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), shader_includer, return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), shader_includer,
"Custom Pixel Shader"); "Custom Pixel Shader");
} }
std::unique_ptr<AbstractShader> CompileVertexShader(const VertexShaderUid& uid, std::unique_ptr<AbstractShader>
std::string_view preprocessor_settings, CompileVertexShader(VertexShaderUid* uid, std::string_view preprocessor_settings, APIType api_type,
APIType api_type, const ShaderHostConfig& host_config, const RasterSurfaceShaderData& shader_data)
const ShaderHostConfig& host_config,
const RasterSurfaceShaderData& shader_data)
{ {
ShaderCode shader_code; ShaderCode shader_code;
@@ -157,14 +367,22 @@ std::unique_ptr<AbstractShader> CompileVertexShader(const VertexShaderUid& uid,
// Compile the shader // Compile the shader
CustomVertexContents contents{.shader = shader_code.GetBuffer(), CustomVertexContents contents{.shader = shader_code.GetBuffer(),
.uniforms = uniform_code.GetBuffer()}; .uniforms = uniform_code.GetBuffer()};
const ShaderCode source_code =
GenerateVertexShaderCode(api_type, host_config, uid.GetUidData(), contents); ShaderCode source_code;
if (uid)
{
source_code = GenerateVertexShaderCode(api_type, host_config, uid->GetUidData(), contents);
}
else
{
GeneratePostProcessingVertexShader(source_code, contents);
}
return g_gfx->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer(), nullptr, return g_gfx->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer(), nullptr,
"Custom Vertex Shader"); "Custom Vertex Shader");
} }
} // namespace } // namespace
ShaderResource::ShaderResource(Resource::ResourceContext resource_context, ShaderResource::ShaderResource(Resource::ResourceContext resource_context,
const GXPipelineUid& pipeline_uid, const std::optional<GXPipelineUid>& pipeline_uid,
const std::string& preprocessor_setting, const std::string& preprocessor_setting,
const ShaderHostConfig& shader_host_config) const ShaderHostConfig& shader_host_config)
: Resource(std::move(resource_context)), m_shader_host_config{.bits = shader_host_config.bits}, : Resource(std::move(resource_context)), m_shader_host_config{.bits = shader_host_config.bits},
@@ -257,24 +475,46 @@ Resource::TaskComplete ShaderResource::ProcessData()
bool Compile() override bool Compile() override
{ {
const ShaderHostConfig shader_host_config{.bits = m_shader_bits}; const ShaderHostConfig shader_host_config{.bits = m_shader_bits};
auto actual_uid = ApplyDriverBugs(*m_uid); if (m_uid)
ClearUnusedPixelShaderUidBits(g_backend_info.api_type, shader_host_config,
&actual_uid.ps_uid);
m_resource_data->m_needs_geometry_shader = shader_host_config.backend_geometry_shaders &&
!actual_uid.gs_uid.GetUidData()->IsPassthrough();
if (m_resource_data->m_needs_geometry_shader)
{ {
m_resource_data->m_geometry_shader = // Draw based shader
CompileGeometryShader(actual_uid.gs_uid, g_backend_info.api_type, shader_host_config); auto actual_uid = ApplyDriverBugs(*m_uid);
ClearUnusedPixelShaderUidBits(g_backend_info.api_type, shader_host_config,
&actual_uid.ps_uid);
m_resource_data->m_needs_geometry_shader = shader_host_config.backend_geometry_shaders &&
!actual_uid.gs_uid.GetUidData()->IsPassthrough();
if (m_resource_data->m_needs_geometry_shader)
{
m_resource_data->m_geometry_shader = CompileGeometryShader(
&actual_uid.gs_uid, g_backend_info.api_type, shader_host_config);
}
m_resource_data->m_pixel_shader =
CompilePixelShader(&actual_uid.ps_uid, m_preprocessor_settings, g_backend_info.api_type,
shader_host_config, m_resource_data->m_shader_data.get());
m_resource_data->m_vertex_shader = CompileVertexShader(
&actual_uid.vs_uid, m_preprocessor_settings, g_backend_info.api_type,
shader_host_config, *m_resource_data->m_shader_data);
}
else
{
// Post processing based shader
m_resource_data->m_needs_geometry_shader =
shader_host_config.backend_geometry_shaders && shader_host_config.stereo;
if (m_resource_data->m_needs_geometry_shader)
{
m_resource_data->m_geometry_shader =
CompileGeometryShader(nullptr, g_backend_info.api_type, shader_host_config);
}
m_resource_data->m_pixel_shader =
CompilePixelShader(nullptr, m_preprocessor_settings, g_backend_info.api_type,
shader_host_config, m_resource_data->m_shader_data.get());
m_resource_data->m_vertex_shader =
CompileVertexShader(nullptr, m_preprocessor_settings, g_backend_info.api_type,
shader_host_config, *m_resource_data->m_shader_data);
} }
m_resource_data->m_pixel_shader =
CompilePixelShader(actual_uid.ps_uid, m_preprocessor_settings, g_backend_info.api_type,
shader_host_config, m_resource_data->m_shader_data.get());
m_resource_data->m_vertex_shader =
CompileVertexShader(actual_uid.vs_uid, m_preprocessor_settings, g_backend_info.api_type,
shader_host_config, *m_resource_data->m_shader_data);
m_resource_data->m_processing_finished = true; m_resource_data->m_processing_finished = true;
return true; return true;
} }
@@ -291,7 +531,7 @@ Resource::TaskComplete ShaderResource::ProcessData()
{ {
std::string_view preprocessor_settings = m_preprocessor_settings; std::string_view preprocessor_settings = m_preprocessor_settings;
auto wi = m_resource_context.shader_compiler->CreateWorkItem<WorkItem>( auto wi = m_resource_context.shader_compiler->CreateWorkItem<WorkItem>(
m_load_data, &m_uid, m_shader_host_config.bits, preprocessor_settings); m_load_data, m_uid ? &*m_uid : nullptr, m_shader_host_config.bits, preprocessor_settings);
// We don't need priority, that is already handled by the resource system // We don't need priority, that is already handled by the resource system
m_resource_context.shader_compiler->QueueWorkItem(std::move(wi), 0); m_resource_context.shader_compiler->QueueWorkItem(std::move(wi), 0);
@@ -4,6 +4,7 @@
#pragma once #pragma once
#include <atomic> #include <atomic>
#include <optional>
#include "VideoCommon/Resources/Resource.h" #include "VideoCommon/Resources/Resource.h"
@@ -16,7 +17,8 @@ namespace VideoCommon
class ShaderResource final : public Resource class ShaderResource final : public Resource
{ {
public: public:
ShaderResource(Resource::ResourceContext resource_context, const GXPipelineUid& pipeline_uid, ShaderResource(Resource::ResourceContext resource_context,
const std::optional<GXPipelineUid>& pipeline_uid,
const std::string& preprocessor_settings, const std::string& preprocessor_settings,
const ShaderHostConfig& shader_host_config); const ShaderHostConfig& shader_host_config);
@@ -61,7 +63,10 @@ private:
bool m_processing_load_data = false; bool m_processing_load_data = false;
ShaderHostConfig m_shader_host_config; ShaderHostConfig m_shader_host_config;
GXPipelineUid m_uid;
// If provided, denotes this shader will be used as a custom draw shader.
// If not provided, denotes this will be used as an efb post processing shader.
std::optional<GXPipelineUid> m_uid;
std::string m_preprocessor_settings; std::string m_preprocessor_settings;
}; };
} // namespace VideoCommon } // namespace VideoCommon
+127 -3
View File
@@ -2258,16 +2258,16 @@ void TextureCacheBase::CopyRenderTargetToTexture(
{ {
for (const auto& action : g_graphics_mod_manager->GetXFBActions(info)) for (const auto& action : g_graphics_mod_manager->GetXFBActions(info))
{ {
action->OnXFB(); action->BeforeXFB();
} }
} }
else else
{ {
bool skip = false; bool skip = false;
GraphicsModActionData::EFB efb{tex_w, tex_h, &skip, &scaled_tex_w, &scaled_tex_h}; GraphicsModActionData::PreEFB efb{tex_w, tex_h, &skip, &scaled_tex_w, &scaled_tex_h};
for (const auto& action : g_graphics_mod_manager->GetEFBActions(info)) for (const auto& action : g_graphics_mod_manager->GetEFBActions(info))
{ {
action->OnEFB(&efb); action->BeforeEFB(&efb);
} }
if (skip == true) if (skip == true)
{ {
@@ -2329,6 +2329,26 @@ void TextureCacheBase::CopyRenderTargetToTexture(
isIntensity, gamma, clamp_top, clamp_bottom, isIntensity, gamma, clamp_top, clamp_bottom,
GetVRAMCopyFilterCoefficients(filter_coefficients)); GetVRAMCopyFilterCoefficients(filter_coefficients));
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)
{
GraphicsModActionData::PostEFB efb;
for (const auto& action : g_graphics_mod_manager->GetEFBActions(info))
{
action->AfterEFB(&efb);
if (efb.material)
{
ApplyMaterialToCacheEntry(*efb.material, entry.get());
}
}
}
}
if (is_xfb_copy && (g_ActiveConfig.bDumpXFBTarget || g_ActiveConfig.bGraphicMods)) if (is_xfb_copy && (g_ActiveConfig.bDumpXFBTarget || g_ActiveConfig.bGraphicMods))
{ {
const std::string id = fmt::format("{}x{}", tex_w, tex_h); const std::string id = fmt::format("{}x{}", tex_w, tex_h);
@@ -3035,6 +3055,110 @@ bool TextureCacheBase::DecodeTextureOnGPU(RcTcacheEntry& entry, u32 dst_level, c
return true; return true;
} }
void TextureCacheBase::ApplyMaterialToCacheEntry(const VideoCommon::MaterialResource& material,
TCacheEntry* entry)
{
const auto material_data = material.GetData();
if (!material_data) [[unlikely]]
return;
// Make a copy, we can't write to our texture and use its framebuffer
// at the same time
auto new_entry = AllocateCacheEntry(entry->texture->GetConfig());
new_entry->SetGeneralParameters(entry->addr, entry->size_in_bytes, entry->format,
entry->should_force_safe_hashing);
new_entry->SetDimensions(entry->native_width, entry->native_height, 1);
new_entry->SetEfbCopy(entry->memory_stride);
new_entry->may_have_overlapping_textures = false;
new_entry->frameCount = FRAMECOUNT_INVALID;
g_gfx->BeginUtilityDrawing();
entry->texture->FinishedRendering();
const auto custom_uniforms = material_data->GetUniforms();
// Set up uniforms.
// TODO: this struct should be shared with post processing
struct Uniforms
{
std::array<float, 4> source_resolution;
std::array<float, 4> target_resolution;
std::array<float, 4> window_resolution;
std::array<float, 4> source_rectangle;
s32 source_layer;
s32 source_layer_pad[3];
u32 time;
u32 time_pad[3];
s32 graphics_api;
s32 graphics_api_pad[3];
u32 efb_scale;
u32 efb_scale_pad[3];
} uniforms;
const float rcp_src_width = 1.0f / entry->texture->GetWidth();
const float rcp_src_height = 1.0f / entry->texture->GetHeight();
uniforms.source_resolution = {static_cast<float>(entry->texture->GetWidth()),
static_cast<float>(entry->texture->GetHeight()), rcp_src_width,
rcp_src_height};
// The target resolution is the same here, since we're
// injecting into the texture
uniforms.target_resolution = uniforms.source_resolution;
const auto present_rect = g_presenter->GetTargetRectangle();
uniforms.window_resolution = {static_cast<float>(present_rect.GetWidth()),
static_cast<float>(present_rect.GetHeight()),
1.0f / static_cast<float>(present_rect.GetWidth()),
1.0f / static_cast<float>(present_rect.GetHeight())};
uniforms.source_rectangle = {0, 0, 1, 1};
uniforms.source_layer = 0;
uniforms.time = 0;
uniforms.graphics_api = static_cast<s32>(g_backend_info.api_type);
uniforms.efb_scale = g_framebuffer_manager->GetEFBScale();
Common::UniqueBuffer<u8> uniform_buffer(custom_uniforms.size() + sizeof(uniforms));
std::memcpy(uniform_buffer.data(), &uniforms, sizeof(uniforms));
std::memcpy(uniform_buffer.data() + sizeof(uniforms), custom_uniforms.data(),
custom_uniforms.size());
g_vertex_manager->UploadUtilityUniforms(uniform_buffer.data(),
static_cast<u32>(uniform_buffer.size()));
// Set framebuffer and viewport based on new entry
g_gfx->SetAndDiscardFramebuffer(new_entry->framebuffer.get());
g_gfx->SetViewportAndScissor(new_entry->framebuffer->GetRect());
g_gfx->SetPipeline(material_data->GetPipeline());
g_gfx->SetTexture(0, entry->texture.get());
g_gfx->SetSamplerState(0, RenderState::GetPointSamplerState());
for (const auto texture : material_data->GetTextures())
{
g_gfx->SetTexture(texture.sampler_index, texture.texture);
g_gfx->SetSamplerState(texture.sampler_index, texture.sampler);
}
g_gfx->Draw(0, 3);
g_gfx->EndUtilityDrawing();
// Finish rendering new entry
new_entry->texture->FinishedRendering();
// Swap new entry and existing entry
std::swap(entry->texture, new_entry->texture);
std::swap(entry->framebuffer, new_entry->framebuffer);
// Return old entry to pool for use in another pass
// or future functionality
ReleaseToPool(new_entry.get());
if (auto* const next_material = material_data->GetNextMaterial(); next_material)
{
ApplyMaterialToCacheEntry(*next_material, entry);
}
}
u32 TCacheEntry::BytesPerRow() const u32 TCacheEntry::BytesPerRow() const
{ {
// RGBA takes two cache lines per block; all others take one // RGBA takes two cache lines per block; all others take one
@@ -41,6 +41,7 @@ namespace VideoCommon
{ {
class CustomTextureData; class CustomTextureData;
class GameTextureAsset; class GameTextureAsset;
class MaterialResource;
} // namespace VideoCommon } // namespace VideoCommon
constexpr std::string_view EFB_DUMP_PREFIX = "efb1"; constexpr std::string_view EFB_DUMP_PREFIX = "efb1";
@@ -407,6 +408,8 @@ private:
void DoSaveState(PointerWrap& p); void DoSaveState(PointerWrap& p);
void DoLoadState(PointerWrap& p); void DoLoadState(PointerWrap& p);
void ApplyMaterialToCacheEntry(const VideoCommon::MaterialResource& material, TCacheEntry* entry);
// m_textures_by_address is the authoritive version of what's actually "in" the texture cache // m_textures_by_address is the authoritive version of what's actually "in" the texture cache
// but it's possible for invalidated TCache entries to live on elsewhere // but it's possible for invalidated TCache entries to live on elsewhere
TexAddrCache m_textures_by_address; TexAddrCache m_textures_by_address;