summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/Resources/ShaderResource.cpp
diff options
context:
space:
mode:
authorJMC47 <JMC4789@gmail.com>2026-04-03 13:38:04 -0400
committerGitHub <noreply@github.com>2026-04-03 13:38:04 -0400
commit798f581c38acfbd9196ba4884cfb9dfd6ef85684 (patch)
tree3421446055c78b54837f5d51372c3611fc65eebb /Source/Core/VideoCommon/Resources/ShaderResource.cpp
parentd3b89b4c39a312b52546c3604379b3f51ec3c876 (diff)
parent306daff51728d71efa833dfaa5b32b3693178874 (diff)
Merge pull request #14140 from iwubcode/blurry_bloom
VideoCommon: add a graphics mod feature to modify EFBs with a custom material, enhance bloom
Diffstat (limited to 'Source/Core/VideoCommon/Resources/ShaderResource.cpp')
-rw-r--r--Source/Core/VideoCommon/Resources/ShaderResource.cpp312
1 files changed, 276 insertions, 36 deletions
diff --git a/Source/Core/VideoCommon/Resources/ShaderResource.cpp b/Source/Core/VideoCommon/Resources/ShaderResource.cpp
index 47600f2c6a..08aa994055 100644
--- a/Source/Core/VideoCommon/Resources/ShaderResource.cpp
+++ b/Source/Core/VideoCommon/Resources/ShaderResource.cpp
@@ -10,6 +10,7 @@
#include "VideoCommon/AbstractGfx.h"
#include "VideoCommon/Assets/CustomAssetCache.h"
#include "VideoCommon/AsyncShaderCompiler.h"
+#include "VideoCommon/FramebufferShaderGen.h"
#include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/PipelineUtils.h"
#include "VideoCommon/PixelShaderGen.h"
@@ -20,20 +21,223 @@ namespace VideoCommon
{
namespace
{
-std::unique_ptr<AbstractShader>
-CompileGeometryShader(const GeometryShaderUid& uid, APIType api_type, ShaderHostConfig host_config)
+// TODO: the uniform buffer is combined due to the utility path only having a single
+// 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)
+{
+ shader_source.Write("UBO_BINDING(std140, 1) uniform {} {{\n", block_name);
+ shader_source.Write("\tvec4 source_resolution;\n");
+ shader_source.Write("\tvec4 target_resolution;\n");
+ 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");
+}
+
+// TODO: move this to a more standard post processing file
+// once post processing has been cleaned up
+void GeneratePostProcessingVertexShader(ShaderCode& shader_source,
+ const CustomVertexContents& custom_contents)
+{
+ // 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());
+ GenerateGeometryShaderCode(api_type, host_config, uid->GetUidData());
return g_gfx->CreateShaderFromSource(ShaderStage::Geometry, source_code.GetBuffer(), nullptr,
- fmt::format("Geometry shader: {}", *uid.GetUidData()));
+ fmt::format("Geometry shader: {}", *uid->GetUidData()));
}
-std::unique_ptr<AbstractShader> CompilePixelShader(const PixelShaderUid& uid,
- std::string_view preprocessor_settings,
- APIType api_type,
- const ShaderHostConfig& host_config,
- RasterSurfaceShaderData* shader_data)
+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;
@@ -89,19 +293,25 @@ std::unique_ptr<AbstractShader> CompilePixelShader(const PixelShaderUid& uid,
// Compile the shader
CustomPixelContents contents{.shader = shader_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 =
shader_data->shader_includer ? &*shader_data->shader_includer : nullptr;
return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), shader_includer,
"Custom Pixel Shader");
}
-std::unique_ptr<AbstractShader> CompileVertexShader(const VertexShaderUid& uid,
- std::string_view preprocessor_settings,
- APIType api_type,
- const ShaderHostConfig& host_config,
- const RasterSurfaceShaderData& shader_data)
+std::unique_ptr<AbstractShader>
+CompileVertexShader(VertexShaderUid* uid, std::string_view preprocessor_settings, APIType api_type,
+ const ShaderHostConfig& host_config, const RasterSurfaceShaderData& shader_data)
{
ShaderCode shader_code;
@@ -157,14 +367,22 @@ std::unique_ptr<AbstractShader> CompileVertexShader(const VertexShaderUid& uid,
// Compile the shader
CustomVertexContents contents{.shader = shader_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,
"Custom Vertex Shader");
}
} // namespace
ShaderResource::ShaderResource(Resource::ResourceContext resource_context,
- const GXPipelineUid& pipeline_uid,
+ const std::optional<GXPipelineUid>& pipeline_uid,
const std::string& preprocessor_setting,
const ShaderHostConfig& shader_host_config)
: 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
{
const ShaderHostConfig shader_host_config{.bits = m_shader_bits};
- 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)
+ if (m_uid)
+ {
+ // Draw based shader
+ 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
{
- m_resource_data->m_geometry_shader =
- CompileGeometryShader(actual_uid.gs_uid, g_backend_info.api_type, shader_host_config);
+ // 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;
return true;
}
@@ -291,7 +531,7 @@ Resource::TaskComplete ShaderResource::ProcessData()
{
std::string_view preprocessor_settings = m_preprocessor_settings;
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
m_resource_context.shader_compiler->QueueWorkItem(std::move(wi), 0);