From 2d8f9558515df4f5fccee9be9e659e40b710e164 Mon Sep 17 00:00:00 2001 From: iwubcode Date: Mon, 17 Nov 2025 18:32:47 -0600 Subject: [PATCH] VideoCommon: enhance 'CustomResourceManager' for post processing This expands the interface of 'CustomResourceManager' to get a Material for post processing a frame buffer (currently EFB). The flow is similar to the normal draw material but distinguishes itself by not needing a UID. The full shader is much simpler than the draw shader and is currently put inline with the shader resource. --- .../Resources/CustomResourceManager.cpp | 30 +- .../Resources/CustomResourceManager.h | 19 +- .../Resources/MaterialResource.cpp | 111 ++++-- .../VideoCommon/Resources/MaterialResource.h | 6 +- .../VideoCommon/Resources/ShaderResource.cpp | 316 +++++++++++++++--- .../VideoCommon/Resources/ShaderResource.h | 9 +- 6 files changed, 407 insertions(+), 84 deletions(-) diff --git a/Source/Core/VideoCommon/Resources/CustomResourceManager.cpp b/Source/Core/VideoCommon/Resources/CustomResourceManager.cpp index ffb16b055a..29e215b8ea 100644 --- a/Source/Core/VideoCommon/Resources/CustomResourceManager.cpp +++ b/Source/Core/VideoCommon/Resources/CustomResourceManager.cpp @@ -43,7 +43,8 @@ void CustomResourceManager::Shutdown() void CustomResourceManager::Reset() { - m_material_resources.clear(); + m_draw_material_resources.clear(); + m_postprocessing_material_resources.clear(); m_shader_resources.clear(); m_texture_data_resources.clear(); m_texture_sampler_resources.clear(); @@ -102,11 +103,11 @@ TextureDataResource* CustomResourceManager::GetTextureDataFromAsset( return resource.get(); } -MaterialResource* CustomResourceManager::GetMaterialFromAsset( +MaterialResource* CustomResourceManager::GetDrawMaterialFromAsset( const CustomAssetLibrary::AssetID& asset_id, const GXPipelineUid& pipeline_uid, std::shared_ptr 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) { resource = std::make_unique( @@ -116,11 +117,24 @@ MaterialResource* CustomResourceManager::GetMaterialFromAsset( return resource.get(); } -ShaderResource* -CustomResourceManager::GetShaderFromAsset(const CustomAssetLibrary::AssetID& asset_id, - std::size_t shader_key, const GXPipelineUid& pipeline_uid, - const std::string& preprocessor_settings, - std::shared_ptr library) +MaterialResource* CustomResourceManager::GetPostProcessingMaterialFromAsset( + const CustomAssetLibrary::AssetID& asset_id, + std::shared_ptr library) +{ + auto& resource = m_postprocessing_material_resources[asset_id]; + if (resource == nullptr) + { + resource = + std::make_unique(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& pipeline_uid, const std::string& preprocessor_settings, + std::shared_ptr library) { auto& resource = m_shader_resources[asset_id][shader_key]; if (resource == nullptr) diff --git a/Source/Core/VideoCommon/Resources/CustomResourceManager.h b/Source/Core/VideoCommon/Resources/CustomResourceManager.h index 04eaa46942..8bd0e75019 100644 --- a/Source/Core/VideoCommon/Resources/CustomResourceManager.h +++ b/Source/Core/VideoCommon/Resources/CustomResourceManager.h @@ -37,14 +37,20 @@ public: TextureDataResource* GetTextureDataFromAsset(const CustomAssetLibrary::AssetID& asset_id, std::shared_ptr library); - MaterialResource* GetMaterialFromAsset(const CustomAssetLibrary::AssetID& asset_id, - const GXPipelineUid& pipeline_uid, - std::shared_ptr library); + MaterialResource* + GetDrawMaterialFromAsset(const CustomAssetLibrary::AssetID& asset_id, + const GXPipelineUid& pipeline_uid, + std::shared_ptr library); + MaterialResource* + GetPostProcessingMaterialFromAsset(const CustomAssetLibrary::AssetID& asset_id, + std::shared_ptr library); ShaderResource* GetShaderFromAsset(const CustomAssetLibrary::AssetID& asset_id, - std::size_t shader_key, const GXPipelineUid& pipeline_uid, + std::size_t shader_key, + const std::optional& pipeline_uid, const std::string& preprocessor_settings, std::shared_ptr library); + TextureAndSamplerResource* GetTextureAndSamplerFromAsset(const CustomAssetLibrary::AssetID& asset_id, std::shared_ptr library); @@ -59,7 +65,7 @@ private: std::unique_ptr m_async_shader_compiler; using PipelineIdToMaterial = std::map>; - std::map m_material_resources; + std::map m_draw_material_resources; using ShaderKeyToShader = std::map>; std::map m_shader_resources; @@ -70,6 +76,9 @@ private: std::map> m_texture_sampler_resources; + std::map> + m_postprocessing_material_resources; + ShaderHostConfig m_host_config; Common::EventHook m_xfb_event; diff --git a/Source/Core/VideoCommon/Resources/MaterialResource.cpp b/Source/Core/VideoCommon/Resources/MaterialResource.cpp index 38486d7932..d41937d992 100644 --- a/Source/Core/VideoCommon/Resources/MaterialResource.cpp +++ b/Source/Core/VideoCommon/Resources/MaterialResource.cpp @@ -54,6 +54,13 @@ SamplerState CalculateSamplerAnisotropy(const SamplerState& initial_sampler) namespace VideoCommon { +MaterialResource::MaterialResource(Resource::ResourceContext resource_context) + : Resource(std::move(resource_context)) +{ + m_material_asset = m_resource_context.asset_cache->CreateAsset( + m_resource_context.primary_asset_id, m_resource_context.asset_library, this); +} + MaterialResource::MaterialResource(Resource::ResourceContext resource_context, const GXPipelineUid& 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( m_resource_context.primary_asset_id, m_resource_context.asset_library, this); m_uid_vertex_format_copy = - g_gfx->CreateNativeVertexFormat(m_uid.vertex_format->GetVertexDeclaration()); - m_uid.vertex_format = m_uid_vertex_format_copy.get(); + g_gfx->CreateNativeVertexFormat(m_uid->vertex_format->GetVertexDeclaration()); + m_uid->vertex_format = m_uid_vertex_format_copy.get(); } void MaterialResource::ResetData() @@ -98,7 +105,7 @@ Resource::TaskComplete MaterialResource::CollectPrimaryData() } 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; } @@ -140,8 +147,18 @@ Resource::TaskComplete MaterialResource::CollectDependencyData() if (m_load_data->m_material_data->next_material_asset != "") { - m_load_data->m_next_material = m_resource_context.resource_manager->GetMaterialFromAsset( - m_load_data->m_material_data->next_material_asset, m_uid, m_resource_context.asset_library); + if (m_uid) + { + 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); const auto data_processed = m_load_data->m_next_material->IsDataProcessed(); if (data_processed == TaskComplete::Error) @@ -222,33 +239,66 @@ Resource::TaskComplete MaterialResource::ProcessData() config.pixel_shader = m_shader_resource_data->GetPixelShader(); config.geometry_shader = m_shader_resource_data->GetGeometryShader(); - 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) + if (m_uid) { - config.rasterization_state.cull_mode = - *m_material_resource_data->m_material_data->cull_mode; + // Draw based pipeline + 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); - if (m_material_resource_data->m_pipeline) { WriteUniforms(m_material_resource_data.get()); @@ -268,7 +318,7 @@ Resource::TaskComplete MaterialResource::ProcessData() if (!m_processing_load_data) { auto wi = m_resource_context.shader_compiler->CreateWorkItem( - 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()); // 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_64bits_reset_withSeed(&shader_key_hash, static_cast(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(), data->m_preprocessor_settings.size()); diff --git a/Source/Core/VideoCommon/Resources/MaterialResource.h b/Source/Core/VideoCommon/Resources/MaterialResource.h index 4842f000ea..6f4835927f 100644 --- a/Source/Core/VideoCommon/Resources/MaterialResource.h +++ b/Source/Core/VideoCommon/Resources/MaterialResource.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,7 @@ namespace VideoCommon class MaterialResource final : public Resource { public: + explicit MaterialResource(Resource::ResourceContext resource_context); MaterialResource(Resource::ResourceContext resource_context, const GXPipelineUid& pipeline_uid); struct TextureLikeReference @@ -93,7 +95,9 @@ private: // Note: asset cache owns the asset, we access as a reference 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 m_uid; std::unique_ptr m_uid_vertex_format_copy; }; } // namespace VideoCommon 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 -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) { - 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())); + 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"); } -std::unique_ptr CompilePixelShader(const PixelShaderUid& uid, - std::string_view preprocessor_settings, - APIType api_type, - const ShaderHostConfig& host_config, - RasterSurfaceShaderData* shader_data) +// 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 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 +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 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 CompileVertexShader(const VertexShaderUid& uid, - std::string_view preprocessor_settings, - APIType api_type, - const ShaderHostConfig& host_config, - const RasterSurfaceShaderData& shader_data) +std::unique_ptr +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 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& 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) { - m_resource_data->m_geometry_shader = - CompileGeometryShader(actual_uid.gs_uid, g_backend_info.api_type, shader_host_config); + // 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 + { + // 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( - 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); diff --git a/Source/Core/VideoCommon/Resources/ShaderResource.h b/Source/Core/VideoCommon/Resources/ShaderResource.h index 0f91cd3714..2f35087aab 100644 --- a/Source/Core/VideoCommon/Resources/ShaderResource.h +++ b/Source/Core/VideoCommon/Resources/ShaderResource.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include "VideoCommon/Resources/Resource.h" @@ -16,7 +17,8 @@ namespace VideoCommon class ShaderResource final : public Resource { public: - ShaderResource(Resource::ResourceContext resource_context, const GXPipelineUid& pipeline_uid, + ShaderResource(Resource::ResourceContext resource_context, + const std::optional& pipeline_uid, const std::string& preprocessor_settings, const ShaderHostConfig& shader_host_config); @@ -61,7 +63,10 @@ private: bool m_processing_load_data = false; 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 m_uid; std::string m_preprocessor_settings; }; } // namespace VideoCommon