mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Merge pull request #3473 from ReinUsesLisp/shader-purge
gl_shader_cache: Rework shader cache and store texture arrays
This commit is contained in:
		
						commit
						666d431ad8
					
				@ -57,8 +57,6 @@ set(HASH_FILES
 | 
				
			|||||||
    "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
 | 
					    "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
 | 
				
			||||||
    "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
 | 
					    "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
 | 
				
			||||||
    "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
 | 
					    "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
 | 
				
			||||||
    "${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp"
 | 
					 | 
				
			||||||
    "${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
 | 
					 | 
				
			||||||
    "${VIDEO_CORE}/shader/decode/arithmetic.cpp"
 | 
					    "${VIDEO_CORE}/shader/decode/arithmetic.cpp"
 | 
				
			||||||
    "${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
 | 
					    "${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
 | 
				
			||||||
    "${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
 | 
					    "${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
 | 
				
			||||||
@ -91,8 +89,6 @@ set(HASH_FILES
 | 
				
			|||||||
    "${VIDEO_CORE}/shader/ast.h"
 | 
					    "${VIDEO_CORE}/shader/ast.h"
 | 
				
			||||||
    "${VIDEO_CORE}/shader/compiler_settings.cpp"
 | 
					    "${VIDEO_CORE}/shader/compiler_settings.cpp"
 | 
				
			||||||
    "${VIDEO_CORE}/shader/compiler_settings.h"
 | 
					    "${VIDEO_CORE}/shader/compiler_settings.h"
 | 
				
			||||||
    "${VIDEO_CORE}/shader/const_buffer_locker.cpp"
 | 
					 | 
				
			||||||
    "${VIDEO_CORE}/shader/const_buffer_locker.h"
 | 
					 | 
				
			||||||
    "${VIDEO_CORE}/shader/control_flow.cpp"
 | 
					    "${VIDEO_CORE}/shader/control_flow.cpp"
 | 
				
			||||||
    "${VIDEO_CORE}/shader/control_flow.h"
 | 
					    "${VIDEO_CORE}/shader/control_flow.h"
 | 
				
			||||||
    "${VIDEO_CORE}/shader/decode.cpp"
 | 
					    "${VIDEO_CORE}/shader/decode.cpp"
 | 
				
			||||||
@ -101,6 +97,8 @@ set(HASH_FILES
 | 
				
			|||||||
    "${VIDEO_CORE}/shader/node.h"
 | 
					    "${VIDEO_CORE}/shader/node.h"
 | 
				
			||||||
    "${VIDEO_CORE}/shader/node_helper.cpp"
 | 
					    "${VIDEO_CORE}/shader/node_helper.cpp"
 | 
				
			||||||
    "${VIDEO_CORE}/shader/node_helper.h"
 | 
					    "${VIDEO_CORE}/shader/node_helper.h"
 | 
				
			||||||
 | 
					    "${VIDEO_CORE}/shader/registry.cpp"
 | 
				
			||||||
 | 
					    "${VIDEO_CORE}/shader/registry.h"
 | 
				
			||||||
    "${VIDEO_CORE}/shader/shader_ir.cpp"
 | 
					    "${VIDEO_CORE}/shader/shader_ir.cpp"
 | 
				
			||||||
    "${VIDEO_CORE}/shader/shader_ir.h"
 | 
					    "${VIDEO_CORE}/shader/shader_ir.h"
 | 
				
			||||||
    "${VIDEO_CORE}/shader/track.cpp"
 | 
					    "${VIDEO_CORE}/shader/track.cpp"
 | 
				
			||||||
 | 
				
			|||||||
@ -38,8 +38,6 @@ add_custom_command(OUTPUT scm_rev.cpp
 | 
				
			|||||||
      "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
 | 
					      "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
 | 
				
			||||||
      "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
 | 
					      "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
 | 
				
			||||||
      "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
 | 
					      "${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
 | 
				
			||||||
      "${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp"
 | 
					 | 
				
			||||||
      "${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
 | 
					 | 
				
			||||||
      "${VIDEO_CORE}/shader/decode/arithmetic.cpp"
 | 
					      "${VIDEO_CORE}/shader/decode/arithmetic.cpp"
 | 
				
			||||||
      "${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
 | 
					      "${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
 | 
				
			||||||
      "${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
 | 
					      "${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
 | 
				
			||||||
@ -72,8 +70,6 @@ add_custom_command(OUTPUT scm_rev.cpp
 | 
				
			|||||||
      "${VIDEO_CORE}/shader/ast.h"
 | 
					      "${VIDEO_CORE}/shader/ast.h"
 | 
				
			||||||
      "${VIDEO_CORE}/shader/compiler_settings.cpp"
 | 
					      "${VIDEO_CORE}/shader/compiler_settings.cpp"
 | 
				
			||||||
      "${VIDEO_CORE}/shader/compiler_settings.h"
 | 
					      "${VIDEO_CORE}/shader/compiler_settings.h"
 | 
				
			||||||
      "${VIDEO_CORE}/shader/const_buffer_locker.cpp"
 | 
					 | 
				
			||||||
      "${VIDEO_CORE}/shader/const_buffer_locker.h"
 | 
					 | 
				
			||||||
      "${VIDEO_CORE}/shader/control_flow.cpp"
 | 
					      "${VIDEO_CORE}/shader/control_flow.cpp"
 | 
				
			||||||
      "${VIDEO_CORE}/shader/control_flow.h"
 | 
					      "${VIDEO_CORE}/shader/control_flow.h"
 | 
				
			||||||
      "${VIDEO_CORE}/shader/decode.cpp"
 | 
					      "${VIDEO_CORE}/shader/decode.cpp"
 | 
				
			||||||
@ -82,6 +78,8 @@ add_custom_command(OUTPUT scm_rev.cpp
 | 
				
			|||||||
      "${VIDEO_CORE}/shader/node.h"
 | 
					      "${VIDEO_CORE}/shader/node.h"
 | 
				
			||||||
      "${VIDEO_CORE}/shader/node_helper.cpp"
 | 
					      "${VIDEO_CORE}/shader/node_helper.cpp"
 | 
				
			||||||
      "${VIDEO_CORE}/shader/node_helper.h"
 | 
					      "${VIDEO_CORE}/shader/node_helper.h"
 | 
				
			||||||
 | 
					      "${VIDEO_CORE}/shader/registry.cpp"
 | 
				
			||||||
 | 
					      "${VIDEO_CORE}/shader/registry.h"
 | 
				
			||||||
      "${VIDEO_CORE}/shader/shader_ir.cpp"
 | 
					      "${VIDEO_CORE}/shader/shader_ir.cpp"
 | 
				
			||||||
      "${VIDEO_CORE}/shader/shader_ir.h"
 | 
					      "${VIDEO_CORE}/shader/shader_ir.h"
 | 
				
			||||||
      "${VIDEO_CORE}/shader/track.cpp"
 | 
					      "${VIDEO_CORE}/shader/track.cpp"
 | 
				
			||||||
 | 
				
			|||||||
@ -65,8 +65,6 @@ add_library(video_core STATIC
 | 
				
			|||||||
    renderer_opengl/gl_shader_decompiler.h
 | 
					    renderer_opengl/gl_shader_decompiler.h
 | 
				
			||||||
    renderer_opengl/gl_shader_disk_cache.cpp
 | 
					    renderer_opengl/gl_shader_disk_cache.cpp
 | 
				
			||||||
    renderer_opengl/gl_shader_disk_cache.h
 | 
					    renderer_opengl/gl_shader_disk_cache.h
 | 
				
			||||||
    renderer_opengl/gl_shader_gen.cpp
 | 
					 | 
				
			||||||
    renderer_opengl/gl_shader_gen.h
 | 
					 | 
				
			||||||
    renderer_opengl/gl_shader_manager.cpp
 | 
					    renderer_opengl/gl_shader_manager.cpp
 | 
				
			||||||
    renderer_opengl/gl_shader_manager.h
 | 
					    renderer_opengl/gl_shader_manager.h
 | 
				
			||||||
    renderer_opengl/gl_shader_util.cpp
 | 
					    renderer_opengl/gl_shader_util.cpp
 | 
				
			||||||
@ -118,8 +116,6 @@ add_library(video_core STATIC
 | 
				
			|||||||
    shader/ast.h
 | 
					    shader/ast.h
 | 
				
			||||||
    shader/compiler_settings.cpp
 | 
					    shader/compiler_settings.cpp
 | 
				
			||||||
    shader/compiler_settings.h
 | 
					    shader/compiler_settings.h
 | 
				
			||||||
    shader/const_buffer_locker.cpp
 | 
					 | 
				
			||||||
    shader/const_buffer_locker.h
 | 
					 | 
				
			||||||
    shader/control_flow.cpp
 | 
					    shader/control_flow.cpp
 | 
				
			||||||
    shader/control_flow.h
 | 
					    shader/control_flow.h
 | 
				
			||||||
    shader/decode.cpp
 | 
					    shader/decode.cpp
 | 
				
			||||||
@ -128,6 +124,8 @@ add_library(video_core STATIC
 | 
				
			|||||||
    shader/node_helper.cpp
 | 
					    shader/node_helper.cpp
 | 
				
			||||||
    shader/node_helper.h
 | 
					    shader/node_helper.h
 | 
				
			||||||
    shader/node.h
 | 
					    shader/node.h
 | 
				
			||||||
 | 
					    shader/registry.cpp
 | 
				
			||||||
 | 
					    shader/registry.h
 | 
				
			||||||
    shader/shader_ir.cpp
 | 
					    shader/shader_ir.cpp
 | 
				
			||||||
    shader/shader_ir.h
 | 
					    shader/shader_ir.h
 | 
				
			||||||
    shader/track.cpp
 | 
					    shader/track.cpp
 | 
				
			||||||
 | 
				
			|||||||
@ -16,11 +16,12 @@ namespace Tegra::Engines {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct SamplerDescriptor {
 | 
					struct SamplerDescriptor {
 | 
				
			||||||
    union {
 | 
					    union {
 | 
				
			||||||
        BitField<0, 20, Tegra::Shader::TextureType> texture_type;
 | 
					        u32 raw = 0;
 | 
				
			||||||
        BitField<20, 1, u32> is_array;
 | 
					        BitField<0, 2, Tegra::Shader::TextureType> texture_type;
 | 
				
			||||||
        BitField<21, 1, u32> is_buffer;
 | 
					        BitField<2, 3, Tegra::Texture::ComponentType> component_type;
 | 
				
			||||||
        BitField<22, 1, u32> is_shadow;
 | 
					        BitField<5, 1, u32> is_array;
 | 
				
			||||||
        u32 raw{};
 | 
					        BitField<6, 1, u32> is_buffer;
 | 
				
			||||||
 | 
					        BitField<7, 1, u32> is_shadow;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool operator==(const SamplerDescriptor& rhs) const noexcept {
 | 
					    bool operator==(const SamplerDescriptor& rhs) const noexcept {
 | 
				
			||||||
@ -31,68 +32,48 @@ struct SamplerDescriptor {
 | 
				
			|||||||
        return !operator==(rhs);
 | 
					        return !operator==(rhs);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static SamplerDescriptor FromTicTexture(Tegra::Texture::TextureType tic_texture_type) {
 | 
					    static SamplerDescriptor FromTIC(const Tegra::Texture::TICEntry& tic) {
 | 
				
			||||||
 | 
					        using Tegra::Shader::TextureType;
 | 
				
			||||||
        SamplerDescriptor result;
 | 
					        SamplerDescriptor result;
 | 
				
			||||||
        switch (tic_texture_type) {
 | 
					
 | 
				
			||||||
 | 
					        // This is going to be used to determine the shading language type.
 | 
				
			||||||
 | 
					        // Because of that we don't care about all component types on color textures.
 | 
				
			||||||
 | 
					        result.component_type.Assign(tic.r_type.Value());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (tic.texture_type.Value()) {
 | 
				
			||||||
        case Tegra::Texture::TextureType::Texture1D:
 | 
					        case Tegra::Texture::TextureType::Texture1D:
 | 
				
			||||||
            result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D);
 | 
					            result.texture_type.Assign(TextureType::Texture1D);
 | 
				
			||||||
            result.is_array.Assign(0);
 | 
					 | 
				
			||||||
            result.is_buffer.Assign(0);
 | 
					 | 
				
			||||||
            result.is_shadow.Assign(0);
 | 
					 | 
				
			||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
        case Tegra::Texture::TextureType::Texture2D:
 | 
					        case Tegra::Texture::TextureType::Texture2D:
 | 
				
			||||||
            result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
 | 
					            result.texture_type.Assign(TextureType::Texture2D);
 | 
				
			||||||
            result.is_array.Assign(0);
 | 
					 | 
				
			||||||
            result.is_buffer.Assign(0);
 | 
					 | 
				
			||||||
            result.is_shadow.Assign(0);
 | 
					 | 
				
			||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
        case Tegra::Texture::TextureType::Texture3D:
 | 
					        case Tegra::Texture::TextureType::Texture3D:
 | 
				
			||||||
            result.texture_type.Assign(Tegra::Shader::TextureType::Texture3D);
 | 
					            result.texture_type.Assign(TextureType::Texture3D);
 | 
				
			||||||
            result.is_array.Assign(0);
 | 
					 | 
				
			||||||
            result.is_buffer.Assign(0);
 | 
					 | 
				
			||||||
            result.is_shadow.Assign(0);
 | 
					 | 
				
			||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
        case Tegra::Texture::TextureType::TextureCubemap:
 | 
					        case Tegra::Texture::TextureType::TextureCubemap:
 | 
				
			||||||
            result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube);
 | 
					            result.texture_type.Assign(TextureType::TextureCube);
 | 
				
			||||||
            result.is_array.Assign(0);
 | 
					 | 
				
			||||||
            result.is_buffer.Assign(0);
 | 
					 | 
				
			||||||
            result.is_shadow.Assign(0);
 | 
					 | 
				
			||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
        case Tegra::Texture::TextureType::Texture1DArray:
 | 
					        case Tegra::Texture::TextureType::Texture1DArray:
 | 
				
			||||||
            result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D);
 | 
					            result.texture_type.Assign(TextureType::Texture1D);
 | 
				
			||||||
            result.is_array.Assign(1);
 | 
					            result.is_array.Assign(1);
 | 
				
			||||||
            result.is_buffer.Assign(0);
 | 
					 | 
				
			||||||
            result.is_shadow.Assign(0);
 | 
					 | 
				
			||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
        case Tegra::Texture::TextureType::Texture2DArray:
 | 
					        case Tegra::Texture::TextureType::Texture2DArray:
 | 
				
			||||||
            result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
 | 
					            result.texture_type.Assign(TextureType::Texture2D);
 | 
				
			||||||
            result.is_array.Assign(1);
 | 
					            result.is_array.Assign(1);
 | 
				
			||||||
            result.is_buffer.Assign(0);
 | 
					 | 
				
			||||||
            result.is_shadow.Assign(0);
 | 
					 | 
				
			||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
        case Tegra::Texture::TextureType::Texture1DBuffer:
 | 
					        case Tegra::Texture::TextureType::Texture1DBuffer:
 | 
				
			||||||
            result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D);
 | 
					            result.texture_type.Assign(TextureType::Texture1D);
 | 
				
			||||||
            result.is_array.Assign(0);
 | 
					 | 
				
			||||||
            result.is_buffer.Assign(1);
 | 
					            result.is_buffer.Assign(1);
 | 
				
			||||||
            result.is_shadow.Assign(0);
 | 
					 | 
				
			||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
        case Tegra::Texture::TextureType::Texture2DNoMipmap:
 | 
					        case Tegra::Texture::TextureType::Texture2DNoMipmap:
 | 
				
			||||||
            result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
 | 
					            result.texture_type.Assign(TextureType::Texture2D);
 | 
				
			||||||
            result.is_array.Assign(0);
 | 
					 | 
				
			||||||
            result.is_buffer.Assign(0);
 | 
					 | 
				
			||||||
            result.is_shadow.Assign(0);
 | 
					 | 
				
			||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
        case Tegra::Texture::TextureType::TextureCubeArray:
 | 
					        case Tegra::Texture::TextureType::TextureCubeArray:
 | 
				
			||||||
            result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube);
 | 
					            result.texture_type.Assign(TextureType::TextureCube);
 | 
				
			||||||
            result.is_array.Assign(1);
 | 
					            result.is_array.Assign(1);
 | 
				
			||||||
            result.is_buffer.Assign(0);
 | 
					 | 
				
			||||||
            result.is_shadow.Assign(0);
 | 
					 | 
				
			||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
            result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
 | 
					            result.texture_type.Assign(TextureType::Texture2D);
 | 
				
			||||||
            result.is_array.Assign(0);
 | 
					 | 
				
			||||||
            result.is_buffer.Assign(0);
 | 
					 | 
				
			||||||
            result.is_shadow.Assign(0);
 | 
					 | 
				
			||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -89,7 +89,7 @@ SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 con
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
 | 
					    const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
 | 
				
			||||||
    const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
 | 
					    const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
 | 
				
			||||||
    SamplerDescriptor result = SamplerDescriptor::FromTicTexture(tex_info.tic.texture_type.Value());
 | 
					    SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic);
 | 
				
			||||||
    result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
 | 
					    result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
 | 
				
			||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -638,7 +638,7 @@ SamplerDescriptor Maxwell3D::AccessBindlessSampler(ShaderType stage, u64 const_b
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
 | 
					    const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
 | 
				
			||||||
    const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
 | 
					    const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
 | 
				
			||||||
    SamplerDescriptor result = SamplerDescriptor::FromTicTexture(tex_info.tic.texture_type.Value());
 | 
					    SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic);
 | 
				
			||||||
    result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
 | 
					    result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
 | 
				
			||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -67,6 +67,7 @@ public:
 | 
				
			|||||||
        static constexpr std::size_t NumVaryings = 31;
 | 
					        static constexpr std::size_t NumVaryings = 31;
 | 
				
			||||||
        static constexpr std::size_t NumImages = 8; // TODO(Rodrigo): Investigate this number
 | 
					        static constexpr std::size_t NumImages = 8; // TODO(Rodrigo): Investigate this number
 | 
				
			||||||
        static constexpr std::size_t NumClipDistances = 8;
 | 
					        static constexpr std::size_t NumClipDistances = 8;
 | 
				
			||||||
 | 
					        static constexpr std::size_t NumTransformFeedbackBuffers = 4;
 | 
				
			||||||
        static constexpr std::size_t MaxShaderProgram = 6;
 | 
					        static constexpr std::size_t MaxShaderProgram = 6;
 | 
				
			||||||
        static constexpr std::size_t MaxShaderStage = 5;
 | 
					        static constexpr std::size_t MaxShaderStage = 5;
 | 
				
			||||||
        // Maximum number of const buffers per shader stage.
 | 
					        // Maximum number of const buffers per shader stage.
 | 
				
			||||||
@ -627,6 +628,22 @@ public:
 | 
				
			|||||||
            float depth_range_far;
 | 
					            float depth_range_far;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        struct alignas(32) TransformFeedbackBinding {
 | 
				
			||||||
 | 
					            u32 buffer_enable;
 | 
				
			||||||
 | 
					            u32 address_high;
 | 
				
			||||||
 | 
					            u32 address_low;
 | 
				
			||||||
 | 
					            s32 buffer_size;
 | 
				
			||||||
 | 
					            s32 buffer_offset;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        static_assert(sizeof(TransformFeedbackBinding) == 32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        struct alignas(16) TransformFeedbackLayout {
 | 
				
			||||||
 | 
					            u32 stream;
 | 
				
			||||||
 | 
					            u32 varying_count;
 | 
				
			||||||
 | 
					            u32 stride;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        static_assert(sizeof(TransformFeedbackLayout) == 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool IsShaderConfigEnabled(std::size_t index) const {
 | 
					        bool IsShaderConfigEnabled(std::size_t index) const {
 | 
				
			||||||
            // The VertexB is always enabled.
 | 
					            // The VertexB is always enabled.
 | 
				
			||||||
            if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
 | 
					            if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
 | 
				
			||||||
@ -683,7 +700,13 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                u32 rasterize_enable;
 | 
					                u32 rasterize_enable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                INSERT_UNION_PADDING_WORDS(0xF1);
 | 
					                std::array<TransformFeedbackBinding, NumTransformFeedbackBuffers> tfb_bindings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                INSERT_UNION_PADDING_WORDS(0xC0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                std::array<TransformFeedbackLayout, NumTransformFeedbackBuffers> tfb_layouts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                INSERT_UNION_PADDING_WORDS(0x1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                u32 tfb_enabled;
 | 
					                u32 tfb_enabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1202,7 +1225,11 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                u32 tex_cb_index;
 | 
					                u32 tex_cb_index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                INSERT_UNION_PADDING_WORDS(0x395);
 | 
					                INSERT_UNION_PADDING_WORDS(0x7D);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                std::array<std::array<u8, 128>, NumTransformFeedbackBuffers> tfb_varying_locs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                INSERT_UNION_PADDING_WORDS(0x298);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                struct {
 | 
					                struct {
 | 
				
			||||||
                    /// Compressed address of a buffer that holds information about bound SSBOs.
 | 
					                    /// Compressed address of a buffer that holds information about bound SSBOs.
 | 
				
			||||||
@ -1428,6 +1455,8 @@ ASSERT_REG_POSITION(tess_mode, 0xC8);
 | 
				
			|||||||
ASSERT_REG_POSITION(tess_level_outer, 0xC9);
 | 
					ASSERT_REG_POSITION(tess_level_outer, 0xC9);
 | 
				
			||||||
ASSERT_REG_POSITION(tess_level_inner, 0xCD);
 | 
					ASSERT_REG_POSITION(tess_level_inner, 0xCD);
 | 
				
			||||||
ASSERT_REG_POSITION(rasterize_enable, 0xDF);
 | 
					ASSERT_REG_POSITION(rasterize_enable, 0xDF);
 | 
				
			||||||
 | 
					ASSERT_REG_POSITION(tfb_bindings, 0xE0);
 | 
				
			||||||
 | 
					ASSERT_REG_POSITION(tfb_layouts, 0x1C0);
 | 
				
			||||||
ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
 | 
					ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
 | 
				
			||||||
ASSERT_REG_POSITION(rt, 0x200);
 | 
					ASSERT_REG_POSITION(rt, 0x200);
 | 
				
			||||||
ASSERT_REG_POSITION(viewport_transform, 0x280);
 | 
					ASSERT_REG_POSITION(viewport_transform, 0x280);
 | 
				
			||||||
@ -1526,6 +1555,7 @@ ASSERT_REG_POSITION(firmware, 0x8C0);
 | 
				
			|||||||
ASSERT_REG_POSITION(const_buffer, 0x8E0);
 | 
					ASSERT_REG_POSITION(const_buffer, 0x8E0);
 | 
				
			||||||
ASSERT_REG_POSITION(cb_bind[0], 0x904);
 | 
					ASSERT_REG_POSITION(cb_bind[0], 0x904);
 | 
				
			||||||
ASSERT_REG_POSITION(tex_cb_index, 0x982);
 | 
					ASSERT_REG_POSITION(tex_cb_index, 0x982);
 | 
				
			||||||
 | 
					ASSERT_REG_POSITION(tfb_varying_locs, 0xA00);
 | 
				
			||||||
ASSERT_REG_POSITION(ssbo_info, 0xD18);
 | 
					ASSERT_REG_POSITION(ssbo_info, 0xD18);
 | 
				
			||||||
ASSERT_REG_POSITION(tex_info_buffers.address[0], 0xD2A);
 | 
					ASSERT_REG_POSITION(tex_info_buffers.address[0], 0xD2A);
 | 
				
			||||||
ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F);
 | 
					ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F);
 | 
				
			||||||
 | 
				
			|||||||
@ -4,13 +4,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <algorithm>
 | 
					#include <algorithm>
 | 
				
			||||||
#include <limits>
 | 
					#include <limits>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "common/common_types.h"
 | 
				
			||||||
#include "video_core/guest_driver.h"
 | 
					#include "video_core/guest_driver.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace VideoCore {
 | 
					namespace VideoCore {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets) {
 | 
					void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32> bound_offsets) {
 | 
				
			||||||
    if (texture_handler_size_deduced) {
 | 
					    if (texture_handler_size) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const std::size_t size = bound_offsets.size();
 | 
					    const std::size_t size = bound_offsets.size();
 | 
				
			||||||
@ -29,7 +31,6 @@ void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32>&& bound_offse
 | 
				
			|||||||
    if (min_val > 2) {
 | 
					    if (min_val > 2) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    texture_handler_size_deduced = true;
 | 
					 | 
				
			||||||
    texture_handler_size = min_texture_handler_size * min_val;
 | 
					    texture_handler_size = min_texture_handler_size * min_val;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <optional>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "common/common_types.h"
 | 
					#include "common/common_types.h"
 | 
				
			||||||
@ -17,25 +18,29 @@ namespace VideoCore {
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
class GuestDriverProfile {
 | 
					class GuestDriverProfile {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    void DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets);
 | 
					    explicit GuestDriverProfile() = default;
 | 
				
			||||||
 | 
					    explicit GuestDriverProfile(std::optional<u32> texture_handler_size)
 | 
				
			||||||
 | 
					        : texture_handler_size{texture_handler_size} {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void DeduceTextureHandlerSize(std::vector<u32> bound_offsets);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u32 GetTextureHandlerSize() const {
 | 
					    u32 GetTextureHandlerSize() const {
 | 
				
			||||||
        return texture_handler_size;
 | 
					        return texture_handler_size.value_or(default_texture_handler_size);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool TextureHandlerSizeKnown() const {
 | 
					    bool IsTextureHandlerSizeKnown() const {
 | 
				
			||||||
        return texture_handler_size_deduced;
 | 
					        return texture_handler_size.has_value();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    // Minimum size of texture handler any driver can use.
 | 
					    // Minimum size of texture handler any driver can use.
 | 
				
			||||||
    static constexpr u32 min_texture_handler_size = 4;
 | 
					    static constexpr u32 min_texture_handler_size = 4;
 | 
				
			||||||
    // This goes with Vulkan and OpenGL standards but Nvidia GPUs can easily
 | 
					
 | 
				
			||||||
    // use 4 bytes instead. Thus, certain drivers may squish the size.
 | 
					    // This goes with Vulkan and OpenGL standards but Nvidia GPUs can easily use 4 bytes instead.
 | 
				
			||||||
 | 
					    // Thus, certain drivers may squish the size.
 | 
				
			||||||
    static constexpr u32 default_texture_handler_size = 8;
 | 
					    static constexpr u32 default_texture_handler_size = 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u32 texture_handler_size = default_texture_handler_size;
 | 
					    std::optional<u32> texture_handler_size = default_texture_handler_size;
 | 
				
			||||||
    bool texture_handler_size_deduced = false;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace VideoCore
 | 
					} // namespace VideoCore
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,6 @@ constexpr std::size_t NumQueryTypes = 1;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
enum class LoadCallbackStage {
 | 
					enum class LoadCallbackStage {
 | 
				
			||||||
    Prepare,
 | 
					    Prepare,
 | 
				
			||||||
    Decompile,
 | 
					 | 
				
			||||||
    Build,
 | 
					    Build,
 | 
				
			||||||
    Complete,
 | 
					    Complete,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -28,7 +28,6 @@
 | 
				
			|||||||
#include "video_core/renderer_opengl/gl_query_cache.h"
 | 
					#include "video_core/renderer_opengl/gl_query_cache.h"
 | 
				
			||||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
 | 
					#include "video_core/renderer_opengl/gl_rasterizer.h"
 | 
				
			||||||
#include "video_core/renderer_opengl/gl_shader_cache.h"
 | 
					#include "video_core/renderer_opengl/gl_shader_cache.h"
 | 
				
			||||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
 | 
					 | 
				
			||||||
#include "video_core/renderer_opengl/maxwell_to_gl.h"
 | 
					#include "video_core/renderer_opengl/maxwell_to_gl.h"
 | 
				
			||||||
#include "video_core/renderer_opengl/renderer_opengl.h"
 | 
					#include "video_core/renderer_opengl/renderer_opengl.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -76,7 +75,7 @@ Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
 | 
					std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
 | 
				
			||||||
                               const GLShader::ConstBufferEntry& entry) {
 | 
					                               const ConstBufferEntry& entry) {
 | 
				
			||||||
    if (!entry.IsIndirect()) {
 | 
					    if (!entry.IsIndirect()) {
 | 
				
			||||||
        return entry.GetSize();
 | 
					        return entry.GetSize();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -272,9 +271,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
 | 
				
			|||||||
        SetupDrawTextures(stage, shader);
 | 
					        SetupDrawTextures(stage, shader);
 | 
				
			||||||
        SetupDrawImages(stage, shader);
 | 
					        SetupDrawImages(stage, shader);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const ProgramVariant variant(primitive_mode);
 | 
					        const GLuint program_handle = shader->GetHandle();
 | 
				
			||||||
        const auto program_handle = shader->GetHandle(variant);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        switch (program) {
 | 
					        switch (program) {
 | 
				
			||||||
        case Maxwell::ShaderProgram::VertexA:
 | 
					        case Maxwell::ShaderProgram::VertexA:
 | 
				
			||||||
        case Maxwell::ShaderProgram::VertexB:
 | 
					        case Maxwell::ShaderProgram::VertexB:
 | 
				
			||||||
@ -295,7 +292,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
 | 
				
			|||||||
        // When a clip distance is enabled but not set in the shader it crops parts of the screen
 | 
					        // When a clip distance is enabled but not set in the shader it crops parts of the screen
 | 
				
			||||||
        // (sometimes it's half the screen, sometimes three quarters). To avoid this, enable the
 | 
					        // (sometimes it's half the screen, sometimes three quarters). To avoid this, enable the
 | 
				
			||||||
        // clip distances only when it's written by a shader stage.
 | 
					        // clip distances only when it's written by a shader stage.
 | 
				
			||||||
        clip_distances |= shader->GetShaderEntries().clip_distances;
 | 
					        clip_distances |= shader->GetEntries().clip_distances;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // When VertexA is enabled, we have dual vertex shaders
 | 
					        // When VertexA is enabled, we have dual vertex shaders
 | 
				
			||||||
        if (program == Maxwell::ShaderProgram::VertexA) {
 | 
					        if (program == Maxwell::ShaderProgram::VertexA) {
 | 
				
			||||||
@ -623,12 +620,7 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
 | 
				
			|||||||
    auto kernel = shader_cache.GetComputeKernel(code_addr);
 | 
					    auto kernel = shader_cache.GetComputeKernel(code_addr);
 | 
				
			||||||
    SetupComputeTextures(kernel);
 | 
					    SetupComputeTextures(kernel);
 | 
				
			||||||
    SetupComputeImages(kernel);
 | 
					    SetupComputeImages(kernel);
 | 
				
			||||||
 | 
					    program_manager.BindComputeShader(kernel->GetHandle());
 | 
				
			||||||
    const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
 | 
					 | 
				
			||||||
    const ProgramVariant variant(launch_desc.block_dim_x, launch_desc.block_dim_y,
 | 
					 | 
				
			||||||
                                 launch_desc.block_dim_z, launch_desc.shared_alloc,
 | 
					 | 
				
			||||||
                                 launch_desc.local_pos_alloc);
 | 
					 | 
				
			||||||
    program_manager.BindComputeShader(kernel->GetHandle(variant));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const std::size_t buffer_size =
 | 
					    const std::size_t buffer_size =
 | 
				
			||||||
        Tegra::Engines::KeplerCompute::NumConstBuffers *
 | 
					        Tegra::Engines::KeplerCompute::NumConstBuffers *
 | 
				
			||||||
@ -646,6 +638,7 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
 | 
				
			|||||||
    bind_ubo_pushbuffer.Bind();
 | 
					    bind_ubo_pushbuffer.Bind();
 | 
				
			||||||
    bind_ssbo_pushbuffer.Bind();
 | 
					    bind_ssbo_pushbuffer.Bind();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
 | 
				
			||||||
    glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z);
 | 
					    glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z);
 | 
				
			||||||
    ++num_queued_commands;
 | 
					    ++num_queued_commands;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -750,7 +743,7 @@ void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, const Shad
 | 
				
			|||||||
    const auto& shader_stage = stages[stage_index];
 | 
					    const auto& shader_stage = stages[stage_index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u32 binding = device.GetBaseBindings(stage_index).uniform_buffer;
 | 
					    u32 binding = device.GetBaseBindings(stage_index).uniform_buffer;
 | 
				
			||||||
    for (const auto& entry : shader->GetShaderEntries().const_buffers) {
 | 
					    for (const auto& entry : shader->GetEntries().const_buffers) {
 | 
				
			||||||
        const auto& buffer = shader_stage.const_buffers[entry.GetIndex()];
 | 
					        const auto& buffer = shader_stage.const_buffers[entry.GetIndex()];
 | 
				
			||||||
        SetupConstBuffer(binding++, buffer, entry);
 | 
					        SetupConstBuffer(binding++, buffer, entry);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -761,7 +754,7 @@ void RasterizerOpenGL::SetupComputeConstBuffers(const Shader& kernel) {
 | 
				
			|||||||
    const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
 | 
					    const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u32 binding = 0;
 | 
					    u32 binding = 0;
 | 
				
			||||||
    for (const auto& entry : kernel->GetShaderEntries().const_buffers) {
 | 
					    for (const auto& entry : kernel->GetEntries().const_buffers) {
 | 
				
			||||||
        const auto& config = launch_desc.const_buffer_config[entry.GetIndex()];
 | 
					        const auto& config = launch_desc.const_buffer_config[entry.GetIndex()];
 | 
				
			||||||
        const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value();
 | 
					        const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value();
 | 
				
			||||||
        Tegra::Engines::ConstBufferInfo buffer;
 | 
					        Tegra::Engines::ConstBufferInfo buffer;
 | 
				
			||||||
@ -773,7 +766,7 @@ void RasterizerOpenGL::SetupComputeConstBuffers(const Shader& kernel) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RasterizerOpenGL::SetupConstBuffer(u32 binding, const Tegra::Engines::ConstBufferInfo& buffer,
 | 
					void RasterizerOpenGL::SetupConstBuffer(u32 binding, const Tegra::Engines::ConstBufferInfo& buffer,
 | 
				
			||||||
                                        const GLShader::ConstBufferEntry& entry) {
 | 
					                                        const ConstBufferEntry& entry) {
 | 
				
			||||||
    if (!buffer.enabled) {
 | 
					    if (!buffer.enabled) {
 | 
				
			||||||
        // Set values to zero to unbind buffers
 | 
					        // Set values to zero to unbind buffers
 | 
				
			||||||
        bind_ubo_pushbuffer.Push(binding, buffer_cache.GetEmptyBuffer(sizeof(float)), 0,
 | 
					        bind_ubo_pushbuffer.Push(binding, buffer_cache.GetEmptyBuffer(sizeof(float)), 0,
 | 
				
			||||||
@ -797,7 +790,7 @@ void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, const Shad
 | 
				
			|||||||
    const auto cbufs{gpu.Maxwell3D().state.shader_stages[stage_index]};
 | 
					    const auto cbufs{gpu.Maxwell3D().state.shader_stages[stage_index]};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u32 binding = device.GetBaseBindings(stage_index).shader_storage_buffer;
 | 
					    u32 binding = device.GetBaseBindings(stage_index).shader_storage_buffer;
 | 
				
			||||||
    for (const auto& entry : shader->GetShaderEntries().global_memory_entries) {
 | 
					    for (const auto& entry : shader->GetEntries().global_memory_entries) {
 | 
				
			||||||
        const auto addr{cbufs.const_buffers[entry.GetCbufIndex()].address + entry.GetCbufOffset()};
 | 
					        const auto addr{cbufs.const_buffers[entry.GetCbufIndex()].address + entry.GetCbufOffset()};
 | 
				
			||||||
        const auto gpu_addr{memory_manager.Read<u64>(addr)};
 | 
					        const auto gpu_addr{memory_manager.Read<u64>(addr)};
 | 
				
			||||||
        const auto size{memory_manager.Read<u32>(addr + 8)};
 | 
					        const auto size{memory_manager.Read<u32>(addr + 8)};
 | 
				
			||||||
@ -811,7 +804,7 @@ void RasterizerOpenGL::SetupComputeGlobalMemory(const Shader& kernel) {
 | 
				
			|||||||
    const auto cbufs{gpu.KeplerCompute().launch_description.const_buffer_config};
 | 
					    const auto cbufs{gpu.KeplerCompute().launch_description.const_buffer_config};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u32 binding = 0;
 | 
					    u32 binding = 0;
 | 
				
			||||||
    for (const auto& entry : kernel->GetShaderEntries().global_memory_entries) {
 | 
					    for (const auto& entry : kernel->GetEntries().global_memory_entries) {
 | 
				
			||||||
        const auto addr{cbufs[entry.GetCbufIndex()].Address() + entry.GetCbufOffset()};
 | 
					        const auto addr{cbufs[entry.GetCbufIndex()].Address() + entry.GetCbufOffset()};
 | 
				
			||||||
        const auto gpu_addr{memory_manager.Read<u64>(addr)};
 | 
					        const auto gpu_addr{memory_manager.Read<u64>(addr)};
 | 
				
			||||||
        const auto size{memory_manager.Read<u32>(addr + 8)};
 | 
					        const auto size{memory_manager.Read<u32>(addr + 8)};
 | 
				
			||||||
@ -819,7 +812,7 @@ void RasterizerOpenGL::SetupComputeGlobalMemory(const Shader& kernel) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GLShader::GlobalMemoryEntry& entry,
 | 
					void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry,
 | 
				
			||||||
                                         GPUVAddr gpu_addr, std::size_t size) {
 | 
					                                         GPUVAddr gpu_addr, std::size_t size) {
 | 
				
			||||||
    const auto alignment{device.GetShaderStorageBufferAlignment()};
 | 
					    const auto alignment{device.GetShaderStorageBufferAlignment()};
 | 
				
			||||||
    const auto [ssbo, buffer_offset] =
 | 
					    const auto [ssbo, buffer_offset] =
 | 
				
			||||||
@ -831,7 +824,7 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader&
 | 
				
			|||||||
    MICROPROFILE_SCOPE(OpenGL_Texture);
 | 
					    MICROPROFILE_SCOPE(OpenGL_Texture);
 | 
				
			||||||
    const auto& maxwell3d = system.GPU().Maxwell3D();
 | 
					    const auto& maxwell3d = system.GPU().Maxwell3D();
 | 
				
			||||||
    u32 binding = device.GetBaseBindings(stage_index).sampler;
 | 
					    u32 binding = device.GetBaseBindings(stage_index).sampler;
 | 
				
			||||||
    for (const auto& entry : shader->GetShaderEntries().samplers) {
 | 
					    for (const auto& entry : shader->GetEntries().samplers) {
 | 
				
			||||||
        const auto shader_type = static_cast<ShaderType>(stage_index);
 | 
					        const auto shader_type = static_cast<ShaderType>(stage_index);
 | 
				
			||||||
        for (std::size_t i = 0; i < entry.Size(); ++i) {
 | 
					        for (std::size_t i = 0; i < entry.Size(); ++i) {
 | 
				
			||||||
            const auto texture = GetTextureInfo(maxwell3d, entry, shader_type, i);
 | 
					            const auto texture = GetTextureInfo(maxwell3d, entry, shader_type, i);
 | 
				
			||||||
@ -844,7 +837,7 @@ void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
 | 
				
			|||||||
    MICROPROFILE_SCOPE(OpenGL_Texture);
 | 
					    MICROPROFILE_SCOPE(OpenGL_Texture);
 | 
				
			||||||
    const auto& compute = system.GPU().KeplerCompute();
 | 
					    const auto& compute = system.GPU().KeplerCompute();
 | 
				
			||||||
    u32 binding = 0;
 | 
					    u32 binding = 0;
 | 
				
			||||||
    for (const auto& entry : kernel->GetShaderEntries().samplers) {
 | 
					    for (const auto& entry : kernel->GetEntries().samplers) {
 | 
				
			||||||
        for (std::size_t i = 0; i < entry.Size(); ++i) {
 | 
					        for (std::size_t i = 0; i < entry.Size(); ++i) {
 | 
				
			||||||
            const auto texture = GetTextureInfo(compute, entry, ShaderType::Compute, i);
 | 
					            const auto texture = GetTextureInfo(compute, entry, ShaderType::Compute, i);
 | 
				
			||||||
            SetupTexture(binding++, texture, entry);
 | 
					            SetupTexture(binding++, texture, entry);
 | 
				
			||||||
@ -853,7 +846,7 @@ void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
 | 
					void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
 | 
				
			||||||
                                    const GLShader::SamplerEntry& entry) {
 | 
					                                    const SamplerEntry& entry) {
 | 
				
			||||||
    const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
 | 
					    const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
 | 
				
			||||||
    if (!view) {
 | 
					    if (!view) {
 | 
				
			||||||
        // Can occur when texture addr is null or its memory is unmapped/invalid
 | 
					        // Can occur when texture addr is null or its memory is unmapped/invalid
 | 
				
			||||||
@ -876,7 +869,7 @@ void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextu
 | 
				
			|||||||
void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& shader) {
 | 
					void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& shader) {
 | 
				
			||||||
    const auto& maxwell3d = system.GPU().Maxwell3D();
 | 
					    const auto& maxwell3d = system.GPU().Maxwell3D();
 | 
				
			||||||
    u32 binding = device.GetBaseBindings(stage_index).image;
 | 
					    u32 binding = device.GetBaseBindings(stage_index).image;
 | 
				
			||||||
    for (const auto& entry : shader->GetShaderEntries().images) {
 | 
					    for (const auto& entry : shader->GetEntries().images) {
 | 
				
			||||||
        const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index);
 | 
					        const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index);
 | 
				
			||||||
        const auto tic = GetTextureInfo(maxwell3d, entry, shader_type).tic;
 | 
					        const auto tic = GetTextureInfo(maxwell3d, entry, shader_type).tic;
 | 
				
			||||||
        SetupImage(binding++, tic, entry);
 | 
					        SetupImage(binding++, tic, entry);
 | 
				
			||||||
@ -886,14 +879,14 @@ void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& sh
 | 
				
			|||||||
void RasterizerOpenGL::SetupComputeImages(const Shader& shader) {
 | 
					void RasterizerOpenGL::SetupComputeImages(const Shader& shader) {
 | 
				
			||||||
    const auto& compute = system.GPU().KeplerCompute();
 | 
					    const auto& compute = system.GPU().KeplerCompute();
 | 
				
			||||||
    u32 binding = 0;
 | 
					    u32 binding = 0;
 | 
				
			||||||
    for (const auto& entry : shader->GetShaderEntries().images) {
 | 
					    for (const auto& entry : shader->GetEntries().images) {
 | 
				
			||||||
        const auto tic = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute).tic;
 | 
					        const auto tic = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute).tic;
 | 
				
			||||||
        SetupImage(binding++, tic, entry);
 | 
					        SetupImage(binding++, tic, entry);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic,
 | 
					void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic,
 | 
				
			||||||
                                  const GLShader::ImageEntry& entry) {
 | 
					                                  const ImageEntry& entry) {
 | 
				
			||||||
    const auto view = texture_cache.GetImageSurface(tic, entry);
 | 
					    const auto view = texture_cache.GetImageSurface(tic, entry);
 | 
				
			||||||
    if (!view) {
 | 
					    if (!view) {
 | 
				
			||||||
        glBindImageTexture(binding, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8);
 | 
					        glBindImageTexture(binding, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8);
 | 
				
			||||||
 | 
				
			|||||||
@ -98,7 +98,7 @@ private:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// Configures a constant buffer.
 | 
					    /// Configures a constant buffer.
 | 
				
			||||||
    void SetupConstBuffer(u32 binding, const Tegra::Engines::ConstBufferInfo& buffer,
 | 
					    void SetupConstBuffer(u32 binding, const Tegra::Engines::ConstBufferInfo& buffer,
 | 
				
			||||||
                          const GLShader::ConstBufferEntry& entry);
 | 
					                          const ConstBufferEntry& entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Configures the current global memory entries to use for the draw command.
 | 
					    /// Configures the current global memory entries to use for the draw command.
 | 
				
			||||||
    void SetupDrawGlobalMemory(std::size_t stage_index, const Shader& shader);
 | 
					    void SetupDrawGlobalMemory(std::size_t stage_index, const Shader& shader);
 | 
				
			||||||
@ -107,7 +107,7 @@ private:
 | 
				
			|||||||
    void SetupComputeGlobalMemory(const Shader& kernel);
 | 
					    void SetupComputeGlobalMemory(const Shader& kernel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Configures a constant buffer.
 | 
					    /// Configures a constant buffer.
 | 
				
			||||||
    void SetupGlobalMemory(u32 binding, const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
 | 
					    void SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
 | 
				
			||||||
                           std::size_t size);
 | 
					                           std::size_t size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Configures the current textures to use for the draw command.
 | 
					    /// Configures the current textures to use for the draw command.
 | 
				
			||||||
@ -118,7 +118,7 @@ private:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// Configures a texture.
 | 
					    /// Configures a texture.
 | 
				
			||||||
    void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
 | 
					    void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
 | 
				
			||||||
                      const GLShader::SamplerEntry& entry);
 | 
					                      const SamplerEntry& entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Configures images in a graphics shader.
 | 
					    /// Configures images in a graphics shader.
 | 
				
			||||||
    void SetupDrawImages(std::size_t stage_index, const Shader& shader);
 | 
					    void SetupDrawImages(std::size_t stage_index, const Shader& shader);
 | 
				
			||||||
@ -127,8 +127,7 @@ private:
 | 
				
			|||||||
    void SetupComputeImages(const Shader& shader);
 | 
					    void SetupComputeImages(const Shader& shader);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Configures an image.
 | 
					    /// Configures an image.
 | 
				
			||||||
    void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic,
 | 
					    void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic, const ImageEntry& entry);
 | 
				
			||||||
                    const GLShader::ImageEntry& entry);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Syncs the viewport and depth range to match the guest state
 | 
					    /// Syncs the viewport and depth range to match the guest state
 | 
				
			||||||
    void SyncViewport();
 | 
					    void SyncViewport();
 | 
				
			||||||
 | 
				
			|||||||
@ -2,12 +2,16 @@
 | 
				
			|||||||
// Licensed under GPLv2 or any later version
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
// Refer to the license.txt file included.
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <atomic>
 | 
				
			||||||
 | 
					#include <functional>
 | 
				
			||||||
#include <mutex>
 | 
					#include <mutex>
 | 
				
			||||||
#include <optional>
 | 
					#include <optional>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <thread>
 | 
					#include <thread>
 | 
				
			||||||
#include <unordered_set>
 | 
					#include <unordered_set>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <boost/functional/hash.hpp>
 | 
					#include <boost/functional/hash.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "common/alignment.h"
 | 
					#include "common/alignment.h"
 | 
				
			||||||
#include "common/assert.h"
 | 
					#include "common/assert.h"
 | 
				
			||||||
#include "common/logging/log.h"
 | 
					#include "common/logging/log.h"
 | 
				
			||||||
@ -24,13 +28,14 @@
 | 
				
			|||||||
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
 | 
					#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
 | 
				
			||||||
#include "video_core/renderer_opengl/gl_state_tracker.h"
 | 
					#include "video_core/renderer_opengl/gl_state_tracker.h"
 | 
				
			||||||
#include "video_core/renderer_opengl/utils.h"
 | 
					#include "video_core/renderer_opengl/utils.h"
 | 
				
			||||||
 | 
					#include "video_core/shader/registry.h"
 | 
				
			||||||
#include "video_core/shader/shader_ir.h"
 | 
					#include "video_core/shader/shader_ir.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace OpenGL {
 | 
					namespace OpenGL {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Tegra::Engines::ShaderType;
 | 
					using Tegra::Engines::ShaderType;
 | 
				
			||||||
using VideoCommon::Shader::ConstBufferLocker;
 | 
					 | 
				
			||||||
using VideoCommon::Shader::ProgramCode;
 | 
					using VideoCommon::Shader::ProgramCode;
 | 
				
			||||||
 | 
					using VideoCommon::Shader::Registry;
 | 
				
			||||||
using VideoCommon::Shader::ShaderIR;
 | 
					using VideoCommon::Shader::ShaderIR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
@ -56,7 +61,7 @@ constexpr bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Calculates the size of a program stream
 | 
					/// Calculates the size of a program stream
 | 
				
			||||||
std::size_t CalculateProgramSize(const GLShader::ProgramCode& program) {
 | 
					std::size_t CalculateProgramSize(const ProgramCode& program) {
 | 
				
			||||||
    constexpr std::size_t start_offset = 10;
 | 
					    constexpr std::size_t start_offset = 10;
 | 
				
			||||||
    // This is the encoded version of BRA that jumps to itself. All Nvidia
 | 
					    // This is the encoded version of BRA that jumps to itself. All Nvidia
 | 
				
			||||||
    // shaders end with one.
 | 
					    // shaders end with one.
 | 
				
			||||||
@ -109,32 +114,9 @@ constexpr GLenum GetGLShaderType(ShaderType shader_type) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Describes primitive behavior on geometry shaders
 | 
					 | 
				
			||||||
constexpr std::pair<const char*, u32> GetPrimitiveDescription(GLenum primitive_mode) {
 | 
					 | 
				
			||||||
    switch (primitive_mode) {
 | 
					 | 
				
			||||||
    case GL_POINTS:
 | 
					 | 
				
			||||||
        return {"points", 1};
 | 
					 | 
				
			||||||
    case GL_LINES:
 | 
					 | 
				
			||||||
    case GL_LINE_STRIP:
 | 
					 | 
				
			||||||
        return {"lines", 2};
 | 
					 | 
				
			||||||
    case GL_LINES_ADJACENCY:
 | 
					 | 
				
			||||||
    case GL_LINE_STRIP_ADJACENCY:
 | 
					 | 
				
			||||||
        return {"lines_adjacency", 4};
 | 
					 | 
				
			||||||
    case GL_TRIANGLES:
 | 
					 | 
				
			||||||
    case GL_TRIANGLE_STRIP:
 | 
					 | 
				
			||||||
    case GL_TRIANGLE_FAN:
 | 
					 | 
				
			||||||
        return {"triangles", 3};
 | 
					 | 
				
			||||||
    case GL_TRIANGLES_ADJACENCY:
 | 
					 | 
				
			||||||
    case GL_TRIANGLE_STRIP_ADJACENCY:
 | 
					 | 
				
			||||||
        return {"triangles_adjacency", 6};
 | 
					 | 
				
			||||||
    default:
 | 
					 | 
				
			||||||
        return {"points", 1};
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Hashes one (or two) program streams
 | 
					/// Hashes one (or two) program streams
 | 
				
			||||||
u64 GetUniqueIdentifier(ShaderType shader_type, bool is_a, const ProgramCode& code,
 | 
					u64 GetUniqueIdentifier(ShaderType shader_type, bool is_a, const ProgramCode& code,
 | 
				
			||||||
                        const ProgramCode& code_b) {
 | 
					                        const ProgramCode& code_b = {}) {
 | 
				
			||||||
    u64 unique_identifier = boost::hash_value(code);
 | 
					    u64 unique_identifier = boost::hash_value(code);
 | 
				
			||||||
    if (is_a) {
 | 
					    if (is_a) {
 | 
				
			||||||
        // VertexA programs include two programs
 | 
					        // VertexA programs include two programs
 | 
				
			||||||
@ -143,24 +125,6 @@ u64 GetUniqueIdentifier(ShaderType shader_type, bool is_a, const ProgramCode& co
 | 
				
			|||||||
    return unique_identifier;
 | 
					    return unique_identifier;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Creates an unspecialized program from code streams
 | 
					 | 
				
			||||||
std::string GenerateGLSL(const Device& device, ShaderType shader_type, const ShaderIR& ir,
 | 
					 | 
				
			||||||
                         const std::optional<ShaderIR>& ir_b) {
 | 
					 | 
				
			||||||
    switch (shader_type) {
 | 
					 | 
				
			||||||
    case ShaderType::Vertex:
 | 
					 | 
				
			||||||
        return GLShader::GenerateVertexShader(device, ir, ir_b ? &*ir_b : nullptr);
 | 
					 | 
				
			||||||
    case ShaderType::Geometry:
 | 
					 | 
				
			||||||
        return GLShader::GenerateGeometryShader(device, ir);
 | 
					 | 
				
			||||||
    case ShaderType::Fragment:
 | 
					 | 
				
			||||||
        return GLShader::GenerateFragmentShader(device, ir);
 | 
					 | 
				
			||||||
    case ShaderType::Compute:
 | 
					 | 
				
			||||||
        return GLShader::GenerateComputeShader(device, ir);
 | 
					 | 
				
			||||||
    default:
 | 
					 | 
				
			||||||
        UNIMPLEMENTED_MSG("Unimplemented shader_type={}", static_cast<u32>(shader_type));
 | 
					 | 
				
			||||||
        return {};
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
constexpr const char* GetShaderTypeName(ShaderType shader_type) {
 | 
					constexpr const char* GetShaderTypeName(ShaderType shader_type) {
 | 
				
			||||||
    switch (shader_type) {
 | 
					    switch (shader_type) {
 | 
				
			||||||
    case ShaderType::Vertex:
 | 
					    case ShaderType::Vertex:
 | 
				
			||||||
@ -196,102 +160,38 @@ constexpr ShaderType GetShaderType(Maxwell::ShaderProgram program_type) {
 | 
				
			|||||||
    return {};
 | 
					    return {};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string GetShaderId(u64 unique_identifier, ShaderType shader_type) {
 | 
					std::string MakeShaderID(u64 unique_identifier, ShaderType shader_type) {
 | 
				
			||||||
    return fmt::format("{}{:016X}", GetShaderTypeName(shader_type), unique_identifier);
 | 
					    return fmt::format("{}{:016X}", GetShaderTypeName(shader_type), unique_identifier);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Tegra::Engines::ConstBufferEngineInterface& GetConstBufferEngineInterface(Core::System& system,
 | 
					std::shared_ptr<Registry> MakeRegistry(const ShaderDiskCacheEntry& entry) {
 | 
				
			||||||
                                                                          ShaderType shader_type) {
 | 
					    const VideoCore::GuestDriverProfile guest_profile{entry.texture_handler_size};
 | 
				
			||||||
    if (shader_type == ShaderType::Compute) {
 | 
					    const VideoCommon::Shader::SerializedRegistryInfo info{guest_profile, entry.bound_buffer,
 | 
				
			||||||
        return system.GPU().KeplerCompute();
 | 
					                                                           entry.graphics_info, entry.compute_info};
 | 
				
			||||||
    } else {
 | 
					    const auto registry = std::make_shared<Registry>(entry.type, info);
 | 
				
			||||||
        return system.GPU().Maxwell3D();
 | 
					    for (const auto& [address, value] : entry.keys) {
 | 
				
			||||||
 | 
					        const auto [buffer, offset] = address;
 | 
				
			||||||
 | 
					        registry->InsertKey(buffer, offset, value);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					    for (const auto& [offset, sampler] : entry.bound_samplers) {
 | 
				
			||||||
 | 
					        registry->InsertBoundSampler(offset, sampler);
 | 
				
			||||||
std::unique_ptr<ConstBufferLocker> MakeLocker(Core::System& system, ShaderType shader_type) {
 | 
					 | 
				
			||||||
    return std::make_unique<ConstBufferLocker>(shader_type,
 | 
					 | 
				
			||||||
                                               GetConstBufferEngineInterface(system, shader_type));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void FillLocker(ConstBufferLocker& locker, const ShaderDiskCacheUsage& usage) {
 | 
					 | 
				
			||||||
    locker.SetBoundBuffer(usage.bound_buffer);
 | 
					 | 
				
			||||||
    for (const auto& key : usage.keys) {
 | 
					 | 
				
			||||||
        const auto [buffer, offset] = key.first;
 | 
					 | 
				
			||||||
        locker.InsertKey(buffer, offset, key.second);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    for (const auto& [offset, sampler] : usage.bound_samplers) {
 | 
					    for (const auto& [key, sampler] : entry.bindless_samplers) {
 | 
				
			||||||
        locker.InsertBoundSampler(offset, sampler);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    for (const auto& [key, sampler] : usage.bindless_samplers) {
 | 
					 | 
				
			||||||
        const auto [buffer, offset] = key;
 | 
					        const auto [buffer, offset] = key;
 | 
				
			||||||
        locker.InsertBindlessSampler(buffer, offset, sampler);
 | 
					        registry->InsertBindlessSampler(buffer, offset, sampler);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return registry;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CachedProgram BuildShader(const Device& device, u64 unique_identifier, ShaderType shader_type,
 | 
					std::shared_ptr<OGLProgram> BuildShader(const Device& device, ShaderType shader_type,
 | 
				
			||||||
                          const ProgramCode& code, const ProgramCode& code_b,
 | 
					                                        u64 unique_identifier, const ShaderIR& ir,
 | 
				
			||||||
                          ConstBufferLocker& locker, const ProgramVariant& variant,
 | 
					                                        const Registry& registry, bool hint_retrievable = false) {
 | 
				
			||||||
                          bool hint_retrievable = false) {
 | 
					    const std::string shader_id = MakeShaderID(unique_identifier, shader_type);
 | 
				
			||||||
    LOG_INFO(Render_OpenGL, "called. {}", GetShaderId(unique_identifier, shader_type));
 | 
					    LOG_INFO(Render_OpenGL, "{}", shader_id);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    const bool is_compute = shader_type == ShaderType::Compute;
 | 
					 | 
				
			||||||
    const u32 main_offset = is_compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
 | 
					 | 
				
			||||||
    const ShaderIR ir(code, main_offset, COMPILER_SETTINGS, locker);
 | 
					 | 
				
			||||||
    std::optional<ShaderIR> ir_b;
 | 
					 | 
				
			||||||
    if (!code_b.empty()) {
 | 
					 | 
				
			||||||
        ir_b.emplace(code_b, main_offset, COMPILER_SETTINGS, locker);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::string source = fmt::format(R"(// {}
 | 
					 | 
				
			||||||
#version 430 core
 | 
					 | 
				
			||||||
#extension GL_ARB_separate_shader_objects : enable
 | 
					 | 
				
			||||||
)",
 | 
					 | 
				
			||||||
                                     GetShaderId(unique_identifier, shader_type));
 | 
					 | 
				
			||||||
    if (device.HasShaderBallot()) {
 | 
					 | 
				
			||||||
        source += "#extension GL_ARB_shader_ballot : require\n";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (device.HasVertexViewportLayer()) {
 | 
					 | 
				
			||||||
        source += "#extension GL_ARB_shader_viewport_layer_array : require\n";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (device.HasImageLoadFormatted()) {
 | 
					 | 
				
			||||||
        source += "#extension GL_EXT_shader_image_load_formatted : require\n";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (device.HasWarpIntrinsics()) {
 | 
					 | 
				
			||||||
        source += "#extension GL_NV_gpu_shader5 : require\n"
 | 
					 | 
				
			||||||
                  "#extension GL_NV_shader_thread_group : require\n"
 | 
					 | 
				
			||||||
                  "#extension GL_NV_shader_thread_shuffle : require\n";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // This pragma stops Nvidia's driver from over optimizing math (probably using fp16 operations)
 | 
					 | 
				
			||||||
    // on places where we don't want to.
 | 
					 | 
				
			||||||
    // Thanks to Ryujinx for finding this workaround.
 | 
					 | 
				
			||||||
    source += "#pragma optionNV(fastmath off)\n";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (shader_type == ShaderType::Geometry) {
 | 
					 | 
				
			||||||
        const auto [glsl_topology, max_vertices] = GetPrimitiveDescription(variant.primitive_mode);
 | 
					 | 
				
			||||||
        source += fmt::format("#define MAX_VERTEX_INPUT {}\n", max_vertices);
 | 
					 | 
				
			||||||
        source += fmt::format("layout ({}) in;\n", glsl_topology);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (shader_type == ShaderType::Compute) {
 | 
					 | 
				
			||||||
        if (variant.local_memory_size > 0) {
 | 
					 | 
				
			||||||
            source += fmt::format("#define LOCAL_MEMORY_SIZE {}\n",
 | 
					 | 
				
			||||||
                                  Common::AlignUp(variant.local_memory_size, 4) / 4);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        source +=
 | 
					 | 
				
			||||||
            fmt::format("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;\n",
 | 
					 | 
				
			||||||
                        variant.block_x, variant.block_y, variant.block_z);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (variant.shared_memory_size > 0) {
 | 
					 | 
				
			||||||
            // shared_memory_size is described in number of words
 | 
					 | 
				
			||||||
            source += fmt::format("shared uint smem[{}];\n", variant.shared_memory_size);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    source += '\n';
 | 
					 | 
				
			||||||
    source += GenerateGLSL(device, shader_type, ir, ir_b);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const std::string glsl = DecompileShader(device, ir, registry, shader_type, shader_id);
 | 
				
			||||||
    OGLShader shader;
 | 
					    OGLShader shader;
 | 
				
			||||||
    shader.Create(source.c_str(), GetGLShaderType(shader_type));
 | 
					    shader.Create(glsl.c_str(), GetGLShaderType(shader_type));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto program = std::make_shared<OGLProgram>();
 | 
					    auto program = std::make_shared<OGLProgram>();
 | 
				
			||||||
    program->Create(true, hint_retrievable, shader.handle);
 | 
					    program->Create(true, hint_retrievable, shader.handle);
 | 
				
			||||||
@ -299,7 +199,7 @@ CachedProgram BuildShader(const Device& device, u64 unique_identifier, ShaderTyp
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::unordered_set<GLenum> GetSupportedFormats() {
 | 
					std::unordered_set<GLenum> GetSupportedFormats() {
 | 
				
			||||||
    GLint num_formats{};
 | 
					    GLint num_formats;
 | 
				
			||||||
    glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
 | 
					    glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<GLint> formats(num_formats);
 | 
					    std::vector<GLint> formats(num_formats);
 | 
				
			||||||
@ -314,115 +214,82 @@ std::unordered_set<GLenum> GetSupportedFormats() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
} // Anonymous namespace
 | 
					} // Anonymous namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CachedShader::CachedShader(const ShaderParameters& params, ShaderType shader_type,
 | 
					CachedShader::CachedShader(const u8* host_ptr, VAddr cpu_addr, std::size_t size_in_bytes,
 | 
				
			||||||
                           GLShader::ShaderEntries entries, ProgramCode code, ProgramCode code_b)
 | 
					                           std::shared_ptr<VideoCommon::Shader::Registry> registry,
 | 
				
			||||||
    : RasterizerCacheObject{params.host_ptr}, system{params.system},
 | 
					                           ShaderEntries entries, std::shared_ptr<OGLProgram> program)
 | 
				
			||||||
      disk_cache{params.disk_cache}, device{params.device}, cpu_addr{params.cpu_addr},
 | 
					    : RasterizerCacheObject{host_ptr}, registry{std::move(registry)}, entries{std::move(entries)},
 | 
				
			||||||
      unique_identifier{params.unique_identifier}, shader_type{shader_type},
 | 
					      cpu_addr{cpu_addr}, size_in_bytes{size_in_bytes}, program{std::move(program)} {}
 | 
				
			||||||
      entries{std::move(entries)}, code{std::move(code)}, code_b{std::move(code_b)} {
 | 
					 | 
				
			||||||
    if (!params.precompiled_variants) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    for (const auto& pair : *params.precompiled_variants) {
 | 
					 | 
				
			||||||
        auto locker = MakeLocker(system, shader_type);
 | 
					 | 
				
			||||||
        const auto& usage = pair->first;
 | 
					 | 
				
			||||||
        FillLocker(*locker, usage);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::unique_ptr<LockerVariant>* locker_variant = nullptr;
 | 
					CachedShader::~CachedShader() = default;
 | 
				
			||||||
        const auto it =
 | 
					
 | 
				
			||||||
            std::find_if(locker_variants.begin(), locker_variants.end(), [&](const auto& variant) {
 | 
					GLuint CachedShader::GetHandle() const {
 | 
				
			||||||
                return variant->locker->HasEqualKeys(*locker);
 | 
					    DEBUG_ASSERT(registry->IsConsistent());
 | 
				
			||||||
            });
 | 
					    return program->handle;
 | 
				
			||||||
        if (it == locker_variants.end()) {
 | 
					 | 
				
			||||||
            locker_variant = &locker_variants.emplace_back();
 | 
					 | 
				
			||||||
            *locker_variant = std::make_unique<LockerVariant>();
 | 
					 | 
				
			||||||
            locker_variant->get()->locker = std::move(locker);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            locker_variant = &*it;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        locker_variant->get()->programs.emplace(usage.variant, pair->second);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params,
 | 
					Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params,
 | 
				
			||||||
                                           Maxwell::ShaderProgram program_type, ProgramCode code,
 | 
					                                           Maxwell::ShaderProgram program_type, ProgramCode code,
 | 
				
			||||||
                                           ProgramCode code_b) {
 | 
					                                           ProgramCode code_b) {
 | 
				
			||||||
    const auto shader_type = GetShaderType(program_type);
 | 
					    const auto shader_type = GetShaderType(program_type);
 | 
				
			||||||
    params.disk_cache.SaveRaw(
 | 
					    const std::size_t size_in_bytes = code.size() * sizeof(u64);
 | 
				
			||||||
        ShaderDiskCacheRaw(params.unique_identifier, shader_type, code, code_b));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ConstBufferLocker locker(shader_type, params.system.GPU().Maxwell3D());
 | 
					    auto registry = std::make_shared<Registry>(shader_type, params.system.GPU().Maxwell3D());
 | 
				
			||||||
    const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, locker);
 | 
					    const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
 | 
				
			||||||
    // TODO(Rodrigo): Handle VertexA shaders
 | 
					    // TODO(Rodrigo): Handle VertexA shaders
 | 
				
			||||||
    // std::optional<ShaderIR> ir_b;
 | 
					    // std::optional<ShaderIR> ir_b;
 | 
				
			||||||
    // if (!code_b.empty()) {
 | 
					    // if (!code_b.empty()) {
 | 
				
			||||||
    //     ir_b.emplace(code_b, STAGE_MAIN_OFFSET);
 | 
					    //     ir_b.emplace(code_b, STAGE_MAIN_OFFSET);
 | 
				
			||||||
    // }
 | 
					    // }
 | 
				
			||||||
    return std::shared_ptr<CachedShader>(new CachedShader(
 | 
					    auto program = BuildShader(params.device, shader_type, params.unique_identifier, ir, *registry);
 | 
				
			||||||
        params, shader_type, GLShader::GetEntries(ir), std::move(code), std::move(code_b)));
 | 
					
 | 
				
			||||||
 | 
					    ShaderDiskCacheEntry entry;
 | 
				
			||||||
 | 
					    entry.type = shader_type;
 | 
				
			||||||
 | 
					    entry.code = std::move(code);
 | 
				
			||||||
 | 
					    entry.code_b = std::move(code_b);
 | 
				
			||||||
 | 
					    entry.unique_identifier = params.unique_identifier;
 | 
				
			||||||
 | 
					    entry.bound_buffer = registry->GetBoundBuffer();
 | 
				
			||||||
 | 
					    entry.graphics_info = registry->GetGraphicsInfo();
 | 
				
			||||||
 | 
					    entry.keys = registry->GetKeys();
 | 
				
			||||||
 | 
					    entry.bound_samplers = registry->GetBoundSamplers();
 | 
				
			||||||
 | 
					    entry.bindless_samplers = registry->GetBindlessSamplers();
 | 
				
			||||||
 | 
					    params.disk_cache.SaveEntry(std::move(entry));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return std::shared_ptr<CachedShader>(new CachedShader(params.host_ptr, params.cpu_addr,
 | 
				
			||||||
 | 
					                                                          size_in_bytes, std::move(registry),
 | 
				
			||||||
 | 
					                                                          MakeEntries(ir), std::move(program)));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code) {
 | 
					Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code) {
 | 
				
			||||||
    params.disk_cache.SaveRaw(
 | 
					    const std::size_t size_in_bytes = code.size() * sizeof(u64);
 | 
				
			||||||
        ShaderDiskCacheRaw(params.unique_identifier, ShaderType::Compute, code));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ConstBufferLocker locker(Tegra::Engines::ShaderType::Compute,
 | 
					    auto& engine = params.system.GPU().KeplerCompute();
 | 
				
			||||||
                             params.system.GPU().KeplerCompute());
 | 
					    auto registry = std::make_shared<Registry>(ShaderType::Compute, engine);
 | 
				
			||||||
    const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, locker);
 | 
					    const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
 | 
				
			||||||
    return std::shared_ptr<CachedShader>(new CachedShader(
 | 
					    const u64 uid = params.unique_identifier;
 | 
				
			||||||
        params, ShaderType::Compute, GLShader::GetEntries(ir), std::move(code), {}));
 | 
					    auto program = BuildShader(params.device, ShaderType::Compute, uid, ir, *registry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ShaderDiskCacheEntry entry;
 | 
				
			||||||
 | 
					    entry.type = ShaderType::Compute;
 | 
				
			||||||
 | 
					    entry.code = std::move(code);
 | 
				
			||||||
 | 
					    entry.unique_identifier = uid;
 | 
				
			||||||
 | 
					    entry.bound_buffer = registry->GetBoundBuffer();
 | 
				
			||||||
 | 
					    entry.compute_info = registry->GetComputeInfo();
 | 
				
			||||||
 | 
					    entry.keys = registry->GetKeys();
 | 
				
			||||||
 | 
					    entry.bound_samplers = registry->GetBoundSamplers();
 | 
				
			||||||
 | 
					    entry.bindless_samplers = registry->GetBindlessSamplers();
 | 
				
			||||||
 | 
					    params.disk_cache.SaveEntry(std::move(entry));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return std::shared_ptr<CachedShader>(new CachedShader(params.host_ptr, params.cpu_addr,
 | 
				
			||||||
 | 
					                                                          size_in_bytes, std::move(registry),
 | 
				
			||||||
 | 
					                                                          MakeEntries(ir), std::move(program)));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Shader CachedShader::CreateFromCache(const ShaderParameters& params,
 | 
					Shader CachedShader::CreateFromCache(const ShaderParameters& params,
 | 
				
			||||||
                                     const UnspecializedShader& unspecialized) {
 | 
					                                     const PrecompiledShader& precompiled_shader,
 | 
				
			||||||
    return std::shared_ptr<CachedShader>(new CachedShader(params, unspecialized.type,
 | 
					                                     std::size_t size_in_bytes) {
 | 
				
			||||||
                                                          unspecialized.entries, unspecialized.code,
 | 
					    return std::shared_ptr<CachedShader>(new CachedShader(
 | 
				
			||||||
                                                          unspecialized.code_b));
 | 
					        params.host_ptr, params.cpu_addr, size_in_bytes, precompiled_shader.registry,
 | 
				
			||||||
}
 | 
					        precompiled_shader.entries, precompiled_shader.program));
 | 
				
			||||||
 | 
					 | 
				
			||||||
GLuint CachedShader::GetHandle(const ProgramVariant& variant) {
 | 
					 | 
				
			||||||
    EnsureValidLockerVariant();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const auto [entry, is_cache_miss] = curr_locker_variant->programs.try_emplace(variant);
 | 
					 | 
				
			||||||
    auto& program = entry->second;
 | 
					 | 
				
			||||||
    if (!is_cache_miss) {
 | 
					 | 
				
			||||||
        return program->handle;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    program = BuildShader(device, unique_identifier, shader_type, code, code_b,
 | 
					 | 
				
			||||||
                          *curr_locker_variant->locker, variant);
 | 
					 | 
				
			||||||
    disk_cache.SaveUsage(GetUsage(variant, *curr_locker_variant->locker));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    LabelGLObject(GL_PROGRAM, program->handle, cpu_addr);
 | 
					 | 
				
			||||||
    return program->handle;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool CachedShader::EnsureValidLockerVariant() {
 | 
					 | 
				
			||||||
    const auto previous_variant = curr_locker_variant;
 | 
					 | 
				
			||||||
    if (curr_locker_variant && !curr_locker_variant->locker->IsConsistent()) {
 | 
					 | 
				
			||||||
        curr_locker_variant = nullptr;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!curr_locker_variant) {
 | 
					 | 
				
			||||||
        for (auto& variant : locker_variants) {
 | 
					 | 
				
			||||||
            if (variant->locker->IsConsistent()) {
 | 
					 | 
				
			||||||
                curr_locker_variant = variant.get();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!curr_locker_variant) {
 | 
					 | 
				
			||||||
        auto& new_variant = locker_variants.emplace_back();
 | 
					 | 
				
			||||||
        new_variant = std::make_unique<LockerVariant>();
 | 
					 | 
				
			||||||
        new_variant->locker = MakeLocker(system, shader_type);
 | 
					 | 
				
			||||||
        curr_locker_variant = new_variant.get();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return previous_variant == curr_locker_variant;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant,
 | 
					 | 
				
			||||||
                                            const ConstBufferLocker& locker) const {
 | 
					 | 
				
			||||||
    return ShaderDiskCacheUsage{unique_identifier,         variant,
 | 
					 | 
				
			||||||
                                locker.GetBoundBuffer(),   locker.GetKeys(),
 | 
					 | 
				
			||||||
                                locker.GetBoundSamplers(), locker.GetBindlessSamplers()};
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
 | 
					ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
 | 
				
			||||||
@ -432,16 +299,12 @@ ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System&
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
 | 
					void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
 | 
				
			||||||
                                      const VideoCore::DiskResourceLoadCallback& callback) {
 | 
					                                      const VideoCore::DiskResourceLoadCallback& callback) {
 | 
				
			||||||
    const auto transferable = disk_cache.LoadTransferable();
 | 
					    const std::optional transferable = disk_cache.LoadTransferable();
 | 
				
			||||||
    if (!transferable) {
 | 
					    if (!transferable) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const auto [raws, shader_usages] = *transferable;
 | 
					 | 
				
			||||||
    if (!GenerateUnspecializedShaders(stop_loading, callback, raws) || stop_loading) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto dumps = disk_cache.LoadPrecompiled();
 | 
					    const std::vector gl_cache = disk_cache.LoadPrecompiled();
 | 
				
			||||||
    const auto supported_formats = GetSupportedFormats();
 | 
					    const auto supported_formats = GetSupportedFormats();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Track if precompiled cache was altered during loading to know if we have to
 | 
					    // Track if precompiled cache was altered during loading to know if we have to
 | 
				
			||||||
@ -450,77 +313,82 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Inform the frontend about shader build initialization
 | 
					    // Inform the frontend about shader build initialization
 | 
				
			||||||
    if (callback) {
 | 
					    if (callback) {
 | 
				
			||||||
        callback(VideoCore::LoadCallbackStage::Build, 0, shader_usages.size());
 | 
					        callback(VideoCore::LoadCallbackStage::Build, 0, transferable->size());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::mutex mutex;
 | 
					    std::mutex mutex;
 | 
				
			||||||
    std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex
 | 
					    std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex
 | 
				
			||||||
    std::atomic_bool compilation_failed = false;
 | 
					    std::atomic_bool gl_cache_failed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto Worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
 | 
					    const auto find_precompiled = [&gl_cache](u64 id) {
 | 
				
			||||||
                            std::size_t end, const std::vector<ShaderDiskCacheUsage>& shader_usages,
 | 
					        return std::find_if(gl_cache.begin(), gl_cache.end(),
 | 
				
			||||||
                            const ShaderDumpsMap& dumps) {
 | 
					                            [id](const auto& entry) { return entry.unique_identifier == id; });
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
 | 
				
			||||||
 | 
					                            std::size_t end) {
 | 
				
			||||||
        context->MakeCurrent();
 | 
					        context->MakeCurrent();
 | 
				
			||||||
        SCOPE_EXIT({ return context->DoneCurrent(); });
 | 
					        SCOPE_EXIT({ return context->DoneCurrent(); });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (std::size_t i = begin; i < end; ++i) {
 | 
					        for (std::size_t i = begin; i < end; ++i) {
 | 
				
			||||||
            if (stop_loading || compilation_failed) {
 | 
					            if (stop_loading) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            const auto& usage{shader_usages[i]};
 | 
					            const auto& entry = (*transferable)[i];
 | 
				
			||||||
            const auto& unspecialized{unspecialized_shaders.at(usage.unique_identifier)};
 | 
					            const u64 uid = entry.unique_identifier;
 | 
				
			||||||
            const auto dump{dumps.find(usage)};
 | 
					            const auto it = find_precompiled(uid);
 | 
				
			||||||
 | 
					            const auto precompiled_entry = it != gl_cache.end() ? &*it : nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            CachedProgram shader;
 | 
					            const bool is_compute = entry.type == ShaderType::Compute;
 | 
				
			||||||
            if (dump != dumps.end()) {
 | 
					            const u32 main_offset = is_compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
 | 
				
			||||||
                // If the shader is dumped, attempt to load it with
 | 
					            auto registry = MakeRegistry(entry);
 | 
				
			||||||
                shader = GeneratePrecompiledProgram(dump->second, supported_formats);
 | 
					            const ShaderIR ir(entry.code, main_offset, COMPILER_SETTINGS, *registry);
 | 
				
			||||||
                if (!shader) {
 | 
					 | 
				
			||||||
                    compilation_failed = true;
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (!shader) {
 | 
					 | 
				
			||||||
                auto locker{MakeLocker(system, unspecialized.type)};
 | 
					 | 
				
			||||||
                FillLocker(*locker, usage);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                shader = BuildShader(device, usage.unique_identifier, unspecialized.type,
 | 
					            std::shared_ptr<OGLProgram> program;
 | 
				
			||||||
                                     unspecialized.code, unspecialized.code_b, *locker,
 | 
					            if (precompiled_entry) {
 | 
				
			||||||
                                     usage.variant, true);
 | 
					                // If the shader is precompiled, attempt to load it with
 | 
				
			||||||
 | 
					                program = GeneratePrecompiledProgram(entry, *precompiled_entry, supported_formats);
 | 
				
			||||||
 | 
					                if (!program) {
 | 
				
			||||||
 | 
					                    gl_cache_failed = true;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!program) {
 | 
				
			||||||
 | 
					                // Otherwise compile it from GLSL
 | 
				
			||||||
 | 
					                program = BuildShader(device, entry.type, uid, ir, *registry, true);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            PrecompiledShader shader;
 | 
				
			||||||
 | 
					            shader.program = std::move(program);
 | 
				
			||||||
 | 
					            shader.registry = std::move(registry);
 | 
				
			||||||
 | 
					            shader.entries = MakeEntries(ir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            std::scoped_lock lock{mutex};
 | 
					            std::scoped_lock lock{mutex};
 | 
				
			||||||
            if (callback) {
 | 
					            if (callback) {
 | 
				
			||||||
                callback(VideoCore::LoadCallbackStage::Build, ++built_shaders,
 | 
					                callback(VideoCore::LoadCallbackStage::Build, ++built_shaders,
 | 
				
			||||||
                         shader_usages.size());
 | 
					                         transferable->size());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            runtime_cache.emplace(entry.unique_identifier, std::move(shader));
 | 
				
			||||||
            precompiled_programs.emplace(usage, std::move(shader));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // TODO(Rodrigo): Is there a better way to do this?
 | 
					 | 
				
			||||||
            precompiled_variants[usage.unique_identifier].push_back(
 | 
					 | 
				
			||||||
                precompiled_programs.find(usage));
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1ULL)};
 | 
					    const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1ULL)};
 | 
				
			||||||
    const std::size_t bucket_size{shader_usages.size() / num_workers};
 | 
					    const std::size_t bucket_size{transferable->size() / num_workers};
 | 
				
			||||||
    std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers);
 | 
					    std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers);
 | 
				
			||||||
    std::vector<std::thread> threads(num_workers);
 | 
					    std::vector<std::thread> threads(num_workers);
 | 
				
			||||||
    for (std::size_t i = 0; i < num_workers; ++i) {
 | 
					    for (std::size_t i = 0; i < num_workers; ++i) {
 | 
				
			||||||
        const bool is_last_worker = i + 1 == num_workers;
 | 
					        const bool is_last_worker = i + 1 == num_workers;
 | 
				
			||||||
        const std::size_t start{bucket_size * i};
 | 
					        const std::size_t start{bucket_size * i};
 | 
				
			||||||
        const std::size_t end{is_last_worker ? shader_usages.size() : start + bucket_size};
 | 
					        const std::size_t end{is_last_worker ? transferable->size() : start + bucket_size};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // On some platforms the shared context has to be created from the GUI thread
 | 
					        // On some platforms the shared context has to be created from the GUI thread
 | 
				
			||||||
        contexts[i] = emu_window.CreateSharedContext();
 | 
					        contexts[i] = emu_window.CreateSharedContext();
 | 
				
			||||||
        threads[i] = std::thread(Worker, contexts[i].get(), start, end, shader_usages, dumps);
 | 
					        threads[i] = std::thread(worker, contexts[i].get(), start, end);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    for (auto& thread : threads) {
 | 
					    for (auto& thread : threads) {
 | 
				
			||||||
        thread.join();
 | 
					        thread.join();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (compilation_failed) {
 | 
					    if (gl_cache_failed) {
 | 
				
			||||||
        // Invalidate the precompiled cache if a shader dumped shader was rejected
 | 
					        // Invalidate the precompiled cache if a shader dumped shader was rejected
 | 
				
			||||||
        disk_cache.InvalidatePrecompiled();
 | 
					        disk_cache.InvalidatePrecompiled();
 | 
				
			||||||
        precompiled_cache_altered = true;
 | 
					        precompiled_cache_altered = true;
 | 
				
			||||||
@ -533,11 +401,12 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
 | 
				
			|||||||
    // TODO(Rodrigo): Do state tracking for transferable shaders and do a dummy draw
 | 
					    // TODO(Rodrigo): Do state tracking for transferable shaders and do a dummy draw
 | 
				
			||||||
    // before precompiling them
 | 
					    // before precompiling them
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (std::size_t i = 0; i < shader_usages.size(); ++i) {
 | 
					    for (std::size_t i = 0; i < transferable->size(); ++i) {
 | 
				
			||||||
        const auto& usage{shader_usages[i]};
 | 
					        const u64 id = (*transferable)[i].unique_identifier;
 | 
				
			||||||
        if (dumps.find(usage) == dumps.end()) {
 | 
					        const auto it = find_precompiled(id);
 | 
				
			||||||
            const auto& program{precompiled_programs.at(usage)};
 | 
					        if (it == gl_cache.end()) {
 | 
				
			||||||
            disk_cache.SaveDump(usage, program->handle);
 | 
					            const GLuint program = runtime_cache.at(id).program->handle;
 | 
				
			||||||
 | 
					            disk_cache.SavePrecompiled(id, program);
 | 
				
			||||||
            precompiled_cache_altered = true;
 | 
					            precompiled_cache_altered = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -547,80 +416,29 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PrecompiledVariants* ShaderCacheOpenGL::GetPrecompiledVariants(u64 unique_identifier) const {
 | 
					std::shared_ptr<OGLProgram> ShaderCacheOpenGL::GeneratePrecompiledProgram(
 | 
				
			||||||
    const auto it = precompiled_variants.find(unique_identifier);
 | 
					    const ShaderDiskCacheEntry& entry, const ShaderDiskCachePrecompiled& precompiled_entry,
 | 
				
			||||||
    return it == precompiled_variants.end() ? nullptr : &it->second;
 | 
					    const std::unordered_set<GLenum>& supported_formats) {
 | 
				
			||||||
}
 | 
					    if (supported_formats.find(precompiled_entry.binary_format) == supported_formats.end()) {
 | 
				
			||||||
 | 
					        LOG_INFO(Render_OpenGL, "Precompiled cache entry with unsupported format, removing");
 | 
				
			||||||
CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram(
 | 
					 | 
				
			||||||
    const ShaderDiskCacheDump& dump, const std::unordered_set<GLenum>& supported_formats) {
 | 
					 | 
				
			||||||
    if (supported_formats.find(dump.binary_format) == supported_formats.end()) {
 | 
					 | 
				
			||||||
        LOG_INFO(Render_OpenGL, "Precompiled cache entry with unsupported format - removing");
 | 
					 | 
				
			||||||
        return {};
 | 
					        return {};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    CachedProgram shader = std::make_shared<OGLProgram>();
 | 
					    auto program = std::make_shared<OGLProgram>();
 | 
				
			||||||
    shader->handle = glCreateProgram();
 | 
					    program->handle = glCreateProgram();
 | 
				
			||||||
    glProgramParameteri(shader->handle, GL_PROGRAM_SEPARABLE, GL_TRUE);
 | 
					    glProgramParameteri(program->handle, GL_PROGRAM_SEPARABLE, GL_TRUE);
 | 
				
			||||||
    glProgramBinary(shader->handle, dump.binary_format, dump.binary.data(),
 | 
					    glProgramBinary(program->handle, precompiled_entry.binary_format,
 | 
				
			||||||
                    static_cast<GLsizei>(dump.binary.size()));
 | 
					                    precompiled_entry.binary.data(),
 | 
				
			||||||
 | 
					                    static_cast<GLsizei>(precompiled_entry.binary.size()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GLint link_status{};
 | 
					    GLint link_status;
 | 
				
			||||||
    glGetProgramiv(shader->handle, GL_LINK_STATUS, &link_status);
 | 
					    glGetProgramiv(program->handle, GL_LINK_STATUS, &link_status);
 | 
				
			||||||
    if (link_status == GL_FALSE) {
 | 
					    if (link_status == GL_FALSE) {
 | 
				
			||||||
        LOG_INFO(Render_OpenGL, "Precompiled cache rejected by the driver - removing");
 | 
					        LOG_INFO(Render_OpenGL, "Precompiled cache rejected by the driver, removing");
 | 
				
			||||||
        return {};
 | 
					        return {};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return shader;
 | 
					    return program;
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool ShaderCacheOpenGL::GenerateUnspecializedShaders(
 | 
					 | 
				
			||||||
    const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback,
 | 
					 | 
				
			||||||
    const std::vector<ShaderDiskCacheRaw>& raws) {
 | 
					 | 
				
			||||||
    if (callback) {
 | 
					 | 
				
			||||||
        callback(VideoCore::LoadCallbackStage::Decompile, 0, raws.size());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (std::size_t i = 0; i < raws.size(); ++i) {
 | 
					 | 
				
			||||||
        if (stop_loading) {
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        const auto& raw{raws[i]};
 | 
					 | 
				
			||||||
        const u64 unique_identifier{raw.GetUniqueIdentifier()};
 | 
					 | 
				
			||||||
        const u64 calculated_hash{
 | 
					 | 
				
			||||||
            GetUniqueIdentifier(raw.GetType(), raw.HasProgramA(), raw.GetCode(), raw.GetCodeB())};
 | 
					 | 
				
			||||||
        if (unique_identifier != calculated_hash) {
 | 
					 | 
				
			||||||
            LOG_ERROR(Render_OpenGL,
 | 
					 | 
				
			||||||
                      "Invalid hash in entry={:016x} (obtained hash={:016x}) - "
 | 
					 | 
				
			||||||
                      "removing shader cache",
 | 
					 | 
				
			||||||
                      raw.GetUniqueIdentifier(), calculated_hash);
 | 
					 | 
				
			||||||
            disk_cache.InvalidateTransferable();
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const u32 main_offset =
 | 
					 | 
				
			||||||
            raw.GetType() == ShaderType::Compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
 | 
					 | 
				
			||||||
        ConstBufferLocker locker(raw.GetType());
 | 
					 | 
				
			||||||
        const ShaderIR ir(raw.GetCode(), main_offset, COMPILER_SETTINGS, locker);
 | 
					 | 
				
			||||||
        // TODO(Rodrigo): Handle VertexA shaders
 | 
					 | 
				
			||||||
        // std::optional<ShaderIR> ir_b;
 | 
					 | 
				
			||||||
        // if (raw.HasProgramA()) {
 | 
					 | 
				
			||||||
        //     ir_b.emplace(raw.GetProgramCodeB(), main_offset);
 | 
					 | 
				
			||||||
        // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        UnspecializedShader unspecialized;
 | 
					 | 
				
			||||||
        unspecialized.entries = GLShader::GetEntries(ir);
 | 
					 | 
				
			||||||
        unspecialized.type = raw.GetType();
 | 
					 | 
				
			||||||
        unspecialized.code = raw.GetCode();
 | 
					 | 
				
			||||||
        unspecialized.code_b = raw.GetCodeB();
 | 
					 | 
				
			||||||
        unspecialized_shaders.emplace(raw.GetUniqueIdentifier(), unspecialized);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (callback) {
 | 
					 | 
				
			||||||
            callback(VideoCore::LoadCallbackStage::Decompile, i, raws.size());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
 | 
					Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
 | 
				
			||||||
@ -648,17 +466,17 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const auto unique_identifier = GetUniqueIdentifier(
 | 
					    const auto unique_identifier = GetUniqueIdentifier(
 | 
				
			||||||
        GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b);
 | 
					        GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b);
 | 
				
			||||||
    const auto precompiled_variants = GetPrecompiledVariants(unique_identifier);
 | 
					 | 
				
			||||||
    const auto cpu_addr{*memory_manager.GpuToCpuAddress(address)};
 | 
					    const auto cpu_addr{*memory_manager.GpuToCpuAddress(address)};
 | 
				
			||||||
    const ShaderParameters params{system,   disk_cache, precompiled_variants, device,
 | 
					    const ShaderParameters params{system,   disk_cache, device,
 | 
				
			||||||
                                  cpu_addr, host_ptr,   unique_identifier};
 | 
					                                  cpu_addr, host_ptr,   unique_identifier};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto found = unspecialized_shaders.find(unique_identifier);
 | 
					    const auto found = runtime_cache.find(unique_identifier);
 | 
				
			||||||
    if (found == unspecialized_shaders.end()) {
 | 
					    if (found == runtime_cache.end()) {
 | 
				
			||||||
        shader = CachedShader::CreateStageFromMemory(params, program, std::move(code),
 | 
					        shader = CachedShader::CreateStageFromMemory(params, program, std::move(code),
 | 
				
			||||||
                                                     std::move(code_b));
 | 
					                                                     std::move(code_b));
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        shader = CachedShader::CreateFromCache(params, found->second);
 | 
					        const std::size_t size_in_bytes = code.size() * sizeof(u64);
 | 
				
			||||||
 | 
					        shader = CachedShader::CreateFromCache(params, found->second, size_in_bytes);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    Register(shader);
 | 
					    Register(shader);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -673,19 +491,19 @@ Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) {
 | 
				
			|||||||
        return kernel;
 | 
					        return kernel;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // No kernel found - create a new one
 | 
					    // No kernel found, create a new one
 | 
				
			||||||
    auto code{GetShaderCode(memory_manager, code_addr, host_ptr)};
 | 
					    auto code{GetShaderCode(memory_manager, code_addr, host_ptr)};
 | 
				
			||||||
    const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code, {})};
 | 
					    const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)};
 | 
				
			||||||
    const auto precompiled_variants = GetPrecompiledVariants(unique_identifier);
 | 
					 | 
				
			||||||
    const auto cpu_addr{*memory_manager.GpuToCpuAddress(code_addr)};
 | 
					    const auto cpu_addr{*memory_manager.GpuToCpuAddress(code_addr)};
 | 
				
			||||||
    const ShaderParameters params{system,   disk_cache, precompiled_variants, device,
 | 
					    const ShaderParameters params{system,   disk_cache, device,
 | 
				
			||||||
                                  cpu_addr, host_ptr,   unique_identifier};
 | 
					                                  cpu_addr, host_ptr,   unique_identifier};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto found = unspecialized_shaders.find(unique_identifier);
 | 
					    const auto found = runtime_cache.find(unique_identifier);
 | 
				
			||||||
    if (found == unspecialized_shaders.end()) {
 | 
					    if (found == runtime_cache.end()) {
 | 
				
			||||||
        kernel = CachedShader::CreateKernelFromMemory(params, std::move(code));
 | 
					        kernel = CachedShader::CreateKernelFromMemory(params, std::move(code));
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        kernel = CachedShader::CreateFromCache(params, found->second);
 | 
					        const std::size_t size_in_bytes = code.size() * sizeof(u64);
 | 
				
			||||||
 | 
					        kernel = CachedShader::CreateFromCache(params, found->second, size_in_bytes);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Register(kernel);
 | 
					    Register(kernel);
 | 
				
			||||||
 | 
				
			|||||||
@ -22,7 +22,7 @@
 | 
				
			|||||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
 | 
					#include "video_core/renderer_opengl/gl_resource_manager.h"
 | 
				
			||||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
 | 
					#include "video_core/renderer_opengl/gl_shader_decompiler.h"
 | 
				
			||||||
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
 | 
					#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
 | 
				
			||||||
#include "video_core/shader/const_buffer_locker.h"
 | 
					#include "video_core/shader/registry.h"
 | 
				
			||||||
#include "video_core/shader/shader_ir.h"
 | 
					#include "video_core/shader/shader_ir.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Core {
 | 
					namespace Core {
 | 
				
			||||||
@ -41,22 +41,17 @@ class RasterizerOpenGL;
 | 
				
			|||||||
struct UnspecializedShader;
 | 
					struct UnspecializedShader;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Shader = std::shared_ptr<CachedShader>;
 | 
					using Shader = std::shared_ptr<CachedShader>;
 | 
				
			||||||
using CachedProgram = std::shared_ptr<OGLProgram>;
 | 
					 | 
				
			||||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
 | 
					using Maxwell = Tegra::Engines::Maxwell3D::Regs;
 | 
				
			||||||
using PrecompiledPrograms = std::unordered_map<ShaderDiskCacheUsage, CachedProgram>;
 | 
					 | 
				
			||||||
using PrecompiledVariants = std::vector<PrecompiledPrograms::iterator>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct UnspecializedShader {
 | 
					struct PrecompiledShader {
 | 
				
			||||||
    GLShader::ShaderEntries entries;
 | 
					    std::shared_ptr<OGLProgram> program;
 | 
				
			||||||
    Tegra::Engines::ShaderType type;
 | 
					    std::shared_ptr<VideoCommon::Shader::Registry> registry;
 | 
				
			||||||
    ProgramCode code;
 | 
					    ShaderEntries entries;
 | 
				
			||||||
    ProgramCode code_b;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ShaderParameters {
 | 
					struct ShaderParameters {
 | 
				
			||||||
    Core::System& system;
 | 
					    Core::System& system;
 | 
				
			||||||
    ShaderDiskCacheOpenGL& disk_cache;
 | 
					    ShaderDiskCacheOpenGL& disk_cache;
 | 
				
			||||||
    const PrecompiledVariants* precompiled_variants;
 | 
					 | 
				
			||||||
    const Device& device;
 | 
					    const Device& device;
 | 
				
			||||||
    VAddr cpu_addr;
 | 
					    VAddr cpu_addr;
 | 
				
			||||||
    u8* host_ptr;
 | 
					    u8* host_ptr;
 | 
				
			||||||
@ -65,61 +60,45 @@ struct ShaderParameters {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class CachedShader final : public RasterizerCacheObject {
 | 
					class CachedShader final : public RasterizerCacheObject {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
 | 
					    ~CachedShader();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets the GL program handle for the shader
 | 
				
			||||||
 | 
					    GLuint GetHandle() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the guest CPU address of the shader
 | 
				
			||||||
 | 
					    VAddr GetCpuAddr() const override {
 | 
				
			||||||
 | 
					        return cpu_addr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the size in bytes of the shader
 | 
				
			||||||
 | 
					    std::size_t GetSizeInBytes() const override {
 | 
				
			||||||
 | 
					        return size_in_bytes;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets the shader entries for the shader
 | 
				
			||||||
 | 
					    const ShaderEntries& GetEntries() const {
 | 
				
			||||||
 | 
					        return entries;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static Shader CreateStageFromMemory(const ShaderParameters& params,
 | 
					    static Shader CreateStageFromMemory(const ShaderParameters& params,
 | 
				
			||||||
                                        Maxwell::ShaderProgram program_type,
 | 
					                                        Maxwell::ShaderProgram program_type,
 | 
				
			||||||
                                        ProgramCode program_code, ProgramCode program_code_b);
 | 
					                                        ProgramCode program_code, ProgramCode program_code_b);
 | 
				
			||||||
    static Shader CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code);
 | 
					    static Shader CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static Shader CreateFromCache(const ShaderParameters& params,
 | 
					    static Shader CreateFromCache(const ShaderParameters& params,
 | 
				
			||||||
                                  const UnspecializedShader& unspecialized);
 | 
					                                  const PrecompiledShader& precompiled_shader,
 | 
				
			||||||
 | 
					                                  std::size_t size_in_bytes);
 | 
				
			||||||
    VAddr GetCpuAddr() const override {
 | 
					 | 
				
			||||||
        return cpu_addr;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::size_t GetSizeInBytes() const override {
 | 
					 | 
				
			||||||
        return code.size() * sizeof(u64);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Gets the shader entries for the shader
 | 
					 | 
				
			||||||
    const GLShader::ShaderEntries& GetShaderEntries() const {
 | 
					 | 
				
			||||||
        return entries;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Gets the GL program handle for the shader
 | 
					 | 
				
			||||||
    GLuint GetHandle(const ProgramVariant& variant);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    struct LockerVariant {
 | 
					    explicit CachedShader(const u8* host_ptr, VAddr cpu_addr, std::size_t size_in_bytes,
 | 
				
			||||||
        std::unique_ptr<VideoCommon::Shader::ConstBufferLocker> locker;
 | 
					                          std::shared_ptr<VideoCommon::Shader::Registry> registry,
 | 
				
			||||||
        std::unordered_map<ProgramVariant, CachedProgram> programs;
 | 
					                          ShaderEntries entries, std::shared_ptr<OGLProgram> program);
 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    explicit CachedShader(const ShaderParameters& params, Tegra::Engines::ShaderType shader_type,
 | 
					    std::shared_ptr<VideoCommon::Shader::Registry> registry;
 | 
				
			||||||
                          GLShader::ShaderEntries entries, ProgramCode program_code,
 | 
					    ShaderEntries entries;
 | 
				
			||||||
                          ProgramCode program_code_b);
 | 
					    VAddr cpu_addr = 0;
 | 
				
			||||||
 | 
					    std::size_t size_in_bytes = 0;
 | 
				
			||||||
    bool EnsureValidLockerVariant();
 | 
					    std::shared_ptr<OGLProgram> program;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ShaderDiskCacheUsage GetUsage(const ProgramVariant& variant,
 | 
					 | 
				
			||||||
                                  const VideoCommon::Shader::ConstBufferLocker& locker) const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Core::System& system;
 | 
					 | 
				
			||||||
    ShaderDiskCacheOpenGL& disk_cache;
 | 
					 | 
				
			||||||
    const Device& device;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    VAddr cpu_addr{};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    u64 unique_identifier{};
 | 
					 | 
				
			||||||
    Tegra::Engines::ShaderType shader_type{};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    GLShader::ShaderEntries entries;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ProgramCode code;
 | 
					 | 
				
			||||||
    ProgramCode code_b;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    LockerVariant* curr_locker_variant = nullptr;
 | 
					 | 
				
			||||||
    std::vector<std::unique_ptr<LockerVariant>> locker_variants;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
 | 
					class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
 | 
				
			||||||
@ -142,25 +121,15 @@ protected:
 | 
				
			|||||||
    void FlushObjectInner(const Shader& object) override {}
 | 
					    void FlushObjectInner(const Shader& object) override {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    bool GenerateUnspecializedShaders(const std::atomic_bool& stop_loading,
 | 
					    std::shared_ptr<OGLProgram> GeneratePrecompiledProgram(
 | 
				
			||||||
                                      const VideoCore::DiskResourceLoadCallback& callback,
 | 
					        const ShaderDiskCacheEntry& entry, const ShaderDiskCachePrecompiled& precompiled_entry,
 | 
				
			||||||
                                      const std::vector<ShaderDiskCacheRaw>& raws);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump,
 | 
					 | 
				
			||||||
        const std::unordered_set<GLenum>& supported_formats);
 | 
					        const std::unordered_set<GLenum>& supported_formats);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const PrecompiledVariants* GetPrecompiledVariants(u64 unique_identifier) const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Core::System& system;
 | 
					    Core::System& system;
 | 
				
			||||||
    Core::Frontend::EmuWindow& emu_window;
 | 
					    Core::Frontend::EmuWindow& emu_window;
 | 
				
			||||||
    const Device& device;
 | 
					    const Device& device;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ShaderDiskCacheOpenGL disk_cache;
 | 
					    ShaderDiskCacheOpenGL disk_cache;
 | 
				
			||||||
 | 
					    std::unordered_map<u64, PrecompiledShader> runtime_cache;
 | 
				
			||||||
    PrecompiledPrograms precompiled_programs;
 | 
					 | 
				
			||||||
    std::unordered_map<u64, PrecompiledVariants> precompiled_variants;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::unordered_map<u64, UnspecializedShader> unspecialized_shaders;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
 | 
					    std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@
 | 
				
			|||||||
#include "video_core/shader/node.h"
 | 
					#include "video_core/shader/node.h"
 | 
				
			||||||
#include "video_core/shader/shader_ir.h"
 | 
					#include "video_core/shader/shader_ir.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace OpenGL::GLShader {
 | 
					namespace OpenGL {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace {
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -36,6 +36,7 @@ using Tegra::Shader::IpaInterpMode;
 | 
				
			|||||||
using Tegra::Shader::IpaMode;
 | 
					using Tegra::Shader::IpaMode;
 | 
				
			||||||
using Tegra::Shader::IpaSampleMode;
 | 
					using Tegra::Shader::IpaSampleMode;
 | 
				
			||||||
using Tegra::Shader::Register;
 | 
					using Tegra::Shader::Register;
 | 
				
			||||||
 | 
					using VideoCommon::Shader::Registry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace std::string_literals;
 | 
					using namespace std::string_literals;
 | 
				
			||||||
using namespace VideoCommon::Shader;
 | 
					using namespace VideoCommon::Shader;
 | 
				
			||||||
@ -56,6 +57,25 @@ using TextureIR = std::variant<TextureOffset, TextureDerivates, TextureArgument>
 | 
				
			|||||||
constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
 | 
					constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
 | 
				
			||||||
    static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float));
 | 
					    static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr std::string_view CommonDeclarations = R"(#define ftoi floatBitsToInt
 | 
				
			||||||
 | 
					#define ftou floatBitsToUint
 | 
				
			||||||
 | 
					#define itof intBitsToFloat
 | 
				
			||||||
 | 
					#define utof uintBitsToFloat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bvec2 HalfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{
 | 
				
			||||||
 | 
					    bvec2 is_nan1 = isnan(pair1);
 | 
				
			||||||
 | 
					    bvec2 is_nan2 = isnan(pair2);
 | 
				
			||||||
 | 
					    return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || is_nan2.y);
 | 
				
			||||||
 | 
					}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const float fswzadd_modifiers_a[] = float[4](-1.0f,  1.0f, -1.0f,  0.0f );
 | 
				
			||||||
 | 
					const float fswzadd_modifiers_b[] = float[4](-1.0f, -1.0f,  1.0f, -1.0f );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					layout (std140, binding = {}) uniform vs_config {{
 | 
				
			||||||
 | 
					    float y_direction;
 | 
				
			||||||
 | 
					}};
 | 
				
			||||||
 | 
					)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ShaderWriter final {
 | 
					class ShaderWriter final {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    void AddExpression(std::string_view text) {
 | 
					    void AddExpression(std::string_view text) {
 | 
				
			||||||
@ -269,12 +289,41 @@ const char* GetImageTypeDeclaration(Tegra::Shader::ImageType image_type) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Describes primitive behavior on geometry shaders
 | 
				
			||||||
 | 
					std::pair<const char*, u32> GetPrimitiveDescription(Maxwell::PrimitiveTopology topology) {
 | 
				
			||||||
 | 
					    switch (topology) {
 | 
				
			||||||
 | 
					    case Maxwell::PrimitiveTopology::Points:
 | 
				
			||||||
 | 
					        return {"points", 1};
 | 
				
			||||||
 | 
					    case Maxwell::PrimitiveTopology::Lines:
 | 
				
			||||||
 | 
					    case Maxwell::PrimitiveTopology::LineStrip:
 | 
				
			||||||
 | 
					        return {"lines", 2};
 | 
				
			||||||
 | 
					    case Maxwell::PrimitiveTopology::LinesAdjacency:
 | 
				
			||||||
 | 
					    case Maxwell::PrimitiveTopology::LineStripAdjacency:
 | 
				
			||||||
 | 
					        return {"lines_adjacency", 4};
 | 
				
			||||||
 | 
					    case Maxwell::PrimitiveTopology::Triangles:
 | 
				
			||||||
 | 
					    case Maxwell::PrimitiveTopology::TriangleStrip:
 | 
				
			||||||
 | 
					    case Maxwell::PrimitiveTopology::TriangleFan:
 | 
				
			||||||
 | 
					        return {"triangles", 3};
 | 
				
			||||||
 | 
					    case Maxwell::PrimitiveTopology::TrianglesAdjacency:
 | 
				
			||||||
 | 
					    case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
 | 
				
			||||||
 | 
					        return {"triangles_adjacency", 6};
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        UNIMPLEMENTED_MSG("topology={}", static_cast<int>(topology));
 | 
				
			||||||
 | 
					        return {"points", 1};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Generates code to use for a swizzle operation.
 | 
					/// Generates code to use for a swizzle operation.
 | 
				
			||||||
constexpr const char* GetSwizzle(u32 element) {
 | 
					constexpr const char* GetSwizzle(std::size_t element) {
 | 
				
			||||||
    constexpr std::array swizzle = {".x", ".y", ".z", ".w"};
 | 
					    constexpr std::array swizzle = {".x", ".y", ".z", ".w"};
 | 
				
			||||||
    return swizzle.at(element);
 | 
					    return swizzle.at(element);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr const char* GetColorSwizzle(std::size_t element) {
 | 
				
			||||||
 | 
					    constexpr std::array swizzle = {".r", ".g", ".b", ".a"};
 | 
				
			||||||
 | 
					    return swizzle.at(element);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Translate topology
 | 
					/// Translate topology
 | 
				
			||||||
std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
 | 
					std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
 | 
				
			||||||
    switch (topology) {
 | 
					    switch (topology) {
 | 
				
			||||||
@ -343,9 +392,54 @@ std::string FlowStackTopName(MetaStackClass stack) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class GLSLDecompiler final {
 | 
					class GLSLDecompiler final {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderType stage,
 | 
					    explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry,
 | 
				
			||||||
                            std::string suffix)
 | 
					                            ShaderType stage, std::string_view identifier, std::string_view suffix)
 | 
				
			||||||
        : device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {}
 | 
					        : device{device}, ir{ir}, registry{registry}, stage{stage},
 | 
				
			||||||
 | 
					          identifier{identifier}, suffix{suffix}, header{ir.GetHeader()} {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void Decompile() {
 | 
				
			||||||
 | 
					        DeclareHeader();
 | 
				
			||||||
 | 
					        DeclareVertex();
 | 
				
			||||||
 | 
					        DeclareGeometry();
 | 
				
			||||||
 | 
					        DeclareFragment();
 | 
				
			||||||
 | 
					        DeclareCompute();
 | 
				
			||||||
 | 
					        DeclareRegisters();
 | 
				
			||||||
 | 
					        DeclareCustomVariables();
 | 
				
			||||||
 | 
					        DeclarePredicates();
 | 
				
			||||||
 | 
					        DeclareLocalMemory();
 | 
				
			||||||
 | 
					        DeclareInternalFlags();
 | 
				
			||||||
 | 
					        DeclareInputAttributes();
 | 
				
			||||||
 | 
					        DeclareOutputAttributes();
 | 
				
			||||||
 | 
					        DeclareConstantBuffers();
 | 
				
			||||||
 | 
					        DeclareGlobalMemory();
 | 
				
			||||||
 | 
					        DeclareSamplers();
 | 
				
			||||||
 | 
					        DeclareImages();
 | 
				
			||||||
 | 
					        DeclarePhysicalAttributeReader();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        code.AddLine("void main() {{");
 | 
				
			||||||
 | 
					        ++code.scope;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (stage == ShaderType::Vertex) {
 | 
				
			||||||
 | 
					            code.AddLine("gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (ir.IsDecompiled()) {
 | 
				
			||||||
 | 
					            DecompileAST();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            DecompileBranchMode();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        --code.scope;
 | 
				
			||||||
 | 
					        code.AddLine("}}");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string GetResult() {
 | 
				
			||||||
 | 
					        return code.GetResult();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    friend class ASTDecompiler;
 | 
				
			||||||
 | 
					    friend class ExprDecompiler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void DecompileBranchMode() {
 | 
					    void DecompileBranchMode() {
 | 
				
			||||||
        // VM's program counter
 | 
					        // VM's program counter
 | 
				
			||||||
@ -387,42 +481,35 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    void DecompileAST();
 | 
					    void DecompileAST();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void Decompile() {
 | 
					    void DeclareHeader() {
 | 
				
			||||||
        DeclareVertex();
 | 
					        if (!identifier.empty()) {
 | 
				
			||||||
        DeclareGeometry();
 | 
					            code.AddLine("// {}", identifier);
 | 
				
			||||||
        DeclareRegisters();
 | 
					 | 
				
			||||||
        DeclareCustomVariables();
 | 
					 | 
				
			||||||
        DeclarePredicates();
 | 
					 | 
				
			||||||
        DeclareLocalMemory();
 | 
					 | 
				
			||||||
        DeclareInternalFlags();
 | 
					 | 
				
			||||||
        DeclareInputAttributes();
 | 
					 | 
				
			||||||
        DeclareOutputAttributes();
 | 
					 | 
				
			||||||
        DeclareConstantBuffers();
 | 
					 | 
				
			||||||
        DeclareGlobalMemory();
 | 
					 | 
				
			||||||
        DeclareSamplers();
 | 
					 | 
				
			||||||
        DeclareImages();
 | 
					 | 
				
			||||||
        DeclarePhysicalAttributeReader();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        code.AddLine("void execute_{}() {{", suffix);
 | 
					 | 
				
			||||||
        ++code.scope;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (ir.IsDecompiled()) {
 | 
					 | 
				
			||||||
            DecompileAST();
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            DecompileBranchMode();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        code.AddLine("#version 430 core");
 | 
				
			||||||
        --code.scope;
 | 
					        code.AddLine("#extension GL_ARB_separate_shader_objects : enable");
 | 
				
			||||||
        code.AddLine("}}");
 | 
					        if (device.HasShaderBallot()) {
 | 
				
			||||||
 | 
					            code.AddLine("#extension GL_ARB_shader_ballot : require");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (device.HasVertexViewportLayer()) {
 | 
				
			||||||
    std::string GetResult() {
 | 
					            code.AddLine("#extension GL_ARB_shader_viewport_layer_array : require");
 | 
				
			||||||
        return code.GetResult();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (device.HasImageLoadFormatted()) {
 | 
				
			||||||
 | 
					            code.AddLine("#extension GL_EXT_shader_image_load_formatted : require");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (device.HasWarpIntrinsics()) {
 | 
				
			||||||
 | 
					            code.AddLine("#extension GL_NV_gpu_shader5 : require");
 | 
				
			||||||
 | 
					            code.AddLine("#extension GL_NV_shader_thread_group : require");
 | 
				
			||||||
 | 
					            code.AddLine("#extension GL_NV_shader_thread_shuffle : require");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // This pragma stops Nvidia's driver from over optimizing math (probably using fp16
 | 
				
			||||||
 | 
					        // operations) on places where we don't want to.
 | 
				
			||||||
 | 
					        // Thanks to Ryujinx for finding this workaround.
 | 
				
			||||||
 | 
					        code.AddLine("#pragma optionNV(fastmath off)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					        code.AddNewLine();
 | 
				
			||||||
    friend class ASTDecompiler;
 | 
					
 | 
				
			||||||
    friend class ExprDecompiler;
 | 
					        code.AddLine(CommonDeclarations, EmulationUniformBlockBinding);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void DeclareVertex() {
 | 
					    void DeclareVertex() {
 | 
				
			||||||
        if (!IsVertexShader(stage))
 | 
					        if (!IsVertexShader(stage))
 | 
				
			||||||
@ -436,9 +523,15 @@ private:
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const auto& info = registry.GetGraphicsInfo();
 | 
				
			||||||
 | 
					        const auto input_topology = info.primitive_topology;
 | 
				
			||||||
 | 
					        const auto [glsl_topology, max_vertices] = GetPrimitiveDescription(input_topology);
 | 
				
			||||||
 | 
					        max_input_vertices = max_vertices;
 | 
				
			||||||
 | 
					        code.AddLine("layout ({}) in;", glsl_topology);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const auto topology = GetTopologyName(header.common3.output_topology);
 | 
					        const auto topology = GetTopologyName(header.common3.output_topology);
 | 
				
			||||||
        const auto max_vertices = header.common4.max_output_vertices.Value();
 | 
					        const auto max_output_vertices = header.common4.max_output_vertices.Value();
 | 
				
			||||||
        code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices);
 | 
					        code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_output_vertices);
 | 
				
			||||||
        code.AddNewLine();
 | 
					        code.AddNewLine();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        code.AddLine("in gl_PerVertex {{");
 | 
					        code.AddLine("in gl_PerVertex {{");
 | 
				
			||||||
@ -450,6 +543,29 @@ private:
 | 
				
			|||||||
        DeclareVertexRedeclarations();
 | 
					        DeclareVertexRedeclarations();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void DeclareFragment() {
 | 
				
			||||||
 | 
					        if (stage != ShaderType::Fragment) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) {
 | 
				
			||||||
 | 
					            code.AddLine("layout (location = {}) out vec4 frag_color{};", rt, rt);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void DeclareCompute() {
 | 
				
			||||||
 | 
					        if (stage != ShaderType::Compute) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const auto& info = registry.GetComputeInfo();
 | 
				
			||||||
 | 
					        if (const u32 size = info.shared_memory_size_in_words; size > 0) {
 | 
				
			||||||
 | 
					            code.AddLine("shared uint smem[{}];", size);
 | 
				
			||||||
 | 
					            code.AddNewLine();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        code.AddLine("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;",
 | 
				
			||||||
 | 
					                     info.workgroup_size[0], info.workgroup_size[1], info.workgroup_size[2]);
 | 
				
			||||||
 | 
					        code.AddNewLine();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void DeclareVertexRedeclarations() {
 | 
					    void DeclareVertexRedeclarations() {
 | 
				
			||||||
        code.AddLine("out gl_PerVertex {{");
 | 
					        code.AddLine("out gl_PerVertex {{");
 | 
				
			||||||
        ++code.scope;
 | 
					        ++code.scope;
 | 
				
			||||||
@ -525,18 +641,16 @@ private:
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void DeclareLocalMemory() {
 | 
					    void DeclareLocalMemory() {
 | 
				
			||||||
 | 
					        u64 local_memory_size = 0;
 | 
				
			||||||
        if (stage == ShaderType::Compute) {
 | 
					        if (stage == ShaderType::Compute) {
 | 
				
			||||||
            code.AddLine("#ifdef LOCAL_MEMORY_SIZE");
 | 
					            local_memory_size = registry.GetComputeInfo().local_memory_size_in_words * 4ULL;
 | 
				
			||||||
            code.AddLine("uint {}[LOCAL_MEMORY_SIZE];", GetLocalMemory());
 | 
					        } else {
 | 
				
			||||||
            code.AddLine("#endif");
 | 
					            local_memory_size = header.GetLocalMemorySize();
 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        const u64 local_memory_size = header.GetLocalMemorySize();
 | 
					 | 
				
			||||||
        if (local_memory_size == 0) {
 | 
					        if (local_memory_size == 0) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const auto element_count = Common::AlignUp(local_memory_size, 4) / 4;
 | 
					        const u64 element_count = Common::AlignUp(local_memory_size, 4) / 4;
 | 
				
			||||||
        code.AddLine("uint {}[{}];", GetLocalMemory(), element_count);
 | 
					        code.AddLine("uint {}[{}];", GetLocalMemory(), element_count);
 | 
				
			||||||
        code.AddNewLine();
 | 
					        code.AddNewLine();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -925,7 +1039,8 @@ private:
 | 
				
			|||||||
                // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
 | 
					                // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
 | 
				
			||||||
                // set an 0x80000000 index for those and the shader fails to build. Find out why
 | 
					                // set an 0x80000000 index for those and the shader fails to build. Find out why
 | 
				
			||||||
                // this happens and what's its intent.
 | 
					                // this happens and what's its intent.
 | 
				
			||||||
                return fmt::format("gs_{}[{} % MAX_VERTEX_INPUT]", name, Visit(buffer).AsUint());
 | 
					                return fmt::format("gs_{}[{} % {}]", name, Visit(buffer).AsUint(),
 | 
				
			||||||
 | 
					                                   max_input_vertices.value());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return std::string(name);
 | 
					            return std::string(name);
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@ -1945,7 +2060,7 @@ private:
 | 
				
			|||||||
            // TODO(Subv): Figure out how dual-source blending is configured in the Switch.
 | 
					            // TODO(Subv): Figure out how dual-source blending is configured in the Switch.
 | 
				
			||||||
            for (u32 component = 0; component < 4; ++component) {
 | 
					            for (u32 component = 0; component < 4; ++component) {
 | 
				
			||||||
                if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
 | 
					                if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
 | 
				
			||||||
                    code.AddLine("FragColor{}[{}] = {};", render_target, component,
 | 
					                    code.AddLine("frag_color{}{} = {};", render_target, GetColorSwizzle(component),
 | 
				
			||||||
                                 SafeGetRegister(current_reg).AsFloat());
 | 
					                                 SafeGetRegister(current_reg).AsFloat());
 | 
				
			||||||
                    ++current_reg;
 | 
					                    ++current_reg;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -2298,7 +2413,11 @@ private:
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string GetLocalMemory() const {
 | 
					    std::string GetLocalMemory() const {
 | 
				
			||||||
        return "lmem_" + suffix;
 | 
					        if (suffix.empty()) {
 | 
				
			||||||
 | 
					            return "lmem";
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return "lmem_" + std::string{suffix};
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string GetInternalFlag(InternalFlag flag) const {
 | 
					    std::string GetInternalFlag(InternalFlag flag) const {
 | 
				
			||||||
@ -2307,8 +2426,12 @@ private:
 | 
				
			|||||||
        const auto index = static_cast<u32>(flag);
 | 
					        const auto index = static_cast<u32>(flag);
 | 
				
			||||||
        ASSERT(index < static_cast<u32>(InternalFlag::Amount));
 | 
					        ASSERT(index < static_cast<u32>(InternalFlag::Amount));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (suffix.empty()) {
 | 
				
			||||||
 | 
					            return InternalFlagNames[index];
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
            return fmt::format("{}_{}", InternalFlagNames[index], suffix);
 | 
					            return fmt::format("{}_{}", InternalFlagNames[index], suffix);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string GetSampler(const Sampler& sampler) const {
 | 
					    std::string GetSampler(const Sampler& sampler) const {
 | 
				
			||||||
        return GetDeclarationWithSuffix(static_cast<u32>(sampler.GetIndex()), "sampler");
 | 
					        return GetDeclarationWithSuffix(static_cast<u32>(sampler.GetIndex()), "sampler");
 | 
				
			||||||
@ -2319,7 +2442,11 @@ private:
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string GetDeclarationWithSuffix(u32 index, std::string_view name) const {
 | 
					    std::string GetDeclarationWithSuffix(u32 index, std::string_view name) const {
 | 
				
			||||||
        return fmt::format("{}_{}_{}", name, index, suffix);
 | 
					        if (suffix.empty()) {
 | 
				
			||||||
 | 
					            return fmt::format("{}{}", name, index);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return fmt::format("{}{}_{}", name, index, suffix);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u32 GetNumPhysicalInputAttributes() const {
 | 
					    u32 GetNumPhysicalInputAttributes() const {
 | 
				
			||||||
@ -2334,17 +2461,30 @@ private:
 | 
				
			|||||||
        return std::min<u32>(device.GetMaxVaryings(), Maxwell::NumVaryings);
 | 
					        return std::min<u32>(device.GetMaxVaryings(), Maxwell::NumVaryings);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool IsRenderTargetEnabled(u32 render_target) const {
 | 
				
			||||||
 | 
					        for (u32 component = 0; component < 4; ++component) {
 | 
				
			||||||
 | 
					            if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const Device& device;
 | 
					    const Device& device;
 | 
				
			||||||
    const ShaderIR& ir;
 | 
					    const ShaderIR& ir;
 | 
				
			||||||
 | 
					    const Registry& registry;
 | 
				
			||||||
    const ShaderType stage;
 | 
					    const ShaderType stage;
 | 
				
			||||||
    const std::string suffix;
 | 
					    const std::string_view identifier;
 | 
				
			||||||
 | 
					    const std::string_view suffix;
 | 
				
			||||||
    const Header header;
 | 
					    const Header header;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ShaderWriter code;
 | 
					    ShaderWriter code;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::optional<u32> max_input_vertices;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string GetFlowVariable(u32 i) {
 | 
					std::string GetFlowVariable(u32 index) {
 | 
				
			||||||
    return fmt::format("flow_var_{}", i);
 | 
					    return fmt::format("flow_var{}", index);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ExprDecompiler {
 | 
					class ExprDecompiler {
 | 
				
			||||||
@ -2531,7 +2671,7 @@ void GLSLDecompiler::DecompileAST() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
} // Anonymous namespace
 | 
					} // Anonymous namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir) {
 | 
					ShaderEntries MakeEntries(const VideoCommon::Shader::ShaderIR& ir) {
 | 
				
			||||||
    ShaderEntries entries;
 | 
					    ShaderEntries entries;
 | 
				
			||||||
    for (const auto& cbuf : ir.GetConstantBuffers()) {
 | 
					    for (const auto& cbuf : ir.GetConstantBuffers()) {
 | 
				
			||||||
        entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(),
 | 
					        entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(),
 | 
				
			||||||
@ -2555,28 +2695,12 @@ ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir) {
 | 
				
			|||||||
    return entries;
 | 
					    return entries;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string GetCommonDeclarations() {
 | 
					std::string DecompileShader(const Device& device, const ShaderIR& ir, const Registry& registry,
 | 
				
			||||||
    return R"(#define ftoi floatBitsToInt
 | 
					                            ShaderType stage, std::string_view identifier,
 | 
				
			||||||
#define ftou floatBitsToUint
 | 
					                            std::string_view suffix) {
 | 
				
			||||||
#define itof intBitsToFloat
 | 
					    GLSLDecompiler decompiler(device, ir, registry, stage, identifier, suffix);
 | 
				
			||||||
#define utof uintBitsToFloat
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bvec2 HalfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {
 | 
					 | 
				
			||||||
    bvec2 is_nan1 = isnan(pair1);
 | 
					 | 
				
			||||||
    bvec2 is_nan2 = isnan(pair2);
 | 
					 | 
				
			||||||
    return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || is_nan2.y);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const float fswzadd_modifiers_a[] = float[4](-1.0f,  1.0f, -1.0f,  0.0f );
 | 
					 | 
				
			||||||
const float fswzadd_modifiers_b[] = float[4](-1.0f, -1.0f,  1.0f, -1.0f );
 | 
					 | 
				
			||||||
)";
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string Decompile(const Device& device, const ShaderIR& ir, ShaderType stage,
 | 
					 | 
				
			||||||
                      const std::string& suffix) {
 | 
					 | 
				
			||||||
    GLSLDecompiler decompiler(device, ir, stage, suffix);
 | 
					 | 
				
			||||||
    decompiler.Decompile();
 | 
					    decompiler.Decompile();
 | 
				
			||||||
    return decompiler.GetResult();
 | 
					    return decompiler.GetResult();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace OpenGL::GLShader
 | 
					} // namespace OpenGL
 | 
				
			||||||
 | 
				
			|||||||
@ -6,22 +6,18 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <array>
 | 
					#include <array>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <string_view>
 | 
				
			||||||
#include <utility>
 | 
					#include <utility>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
#include "common/common_types.h"
 | 
					#include "common/common_types.h"
 | 
				
			||||||
#include "video_core/engines/maxwell_3d.h"
 | 
					#include "video_core/engines/maxwell_3d.h"
 | 
				
			||||||
#include "video_core/engines/shader_type.h"
 | 
					#include "video_core/engines/shader_type.h"
 | 
				
			||||||
 | 
					#include "video_core/shader/registry.h"
 | 
				
			||||||
#include "video_core/shader/shader_ir.h"
 | 
					#include "video_core/shader/shader_ir.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace VideoCommon::Shader {
 | 
					 | 
				
			||||||
class ShaderIR;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace OpenGL {
 | 
					namespace OpenGL {
 | 
				
			||||||
class Device;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace OpenGL::GLShader {
 | 
					class Device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
 | 
					using Maxwell = Tegra::Engines::Maxwell3D::Regs;
 | 
				
			||||||
using SamplerEntry = VideoCommon::Shader::Sampler;
 | 
					using SamplerEntry = VideoCommon::Shader::Sampler;
 | 
				
			||||||
@ -78,11 +74,11 @@ struct ShaderEntries {
 | 
				
			|||||||
    std::size_t shader_length{};
 | 
					    std::size_t shader_length{};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir);
 | 
					ShaderEntries MakeEntries(const VideoCommon::Shader::ShaderIR& ir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string GetCommonDeclarations();
 | 
					std::string DecompileShader(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
 | 
				
			||||||
 | 
					                            const VideoCommon::Shader::Registry& registry,
 | 
				
			||||||
 | 
					                            Tegra::Engines::ShaderType stage, std::string_view identifier,
 | 
				
			||||||
 | 
					                            std::string_view suffix = {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string Decompile(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
 | 
					} // namespace OpenGL
 | 
				
			||||||
                      Tegra::Engines::ShaderType stage, const std::string& suffix);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace OpenGL::GLShader
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -31,32 +31,24 @@ namespace {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
using ShaderCacheVersionHash = std::array<u8, 64>;
 | 
					using ShaderCacheVersionHash = std::array<u8, 64>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum class TransferableEntryKind : u32 {
 | 
					 | 
				
			||||||
    Raw,
 | 
					 | 
				
			||||||
    Usage,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct ConstBufferKey {
 | 
					struct ConstBufferKey {
 | 
				
			||||||
    u32 cbuf{};
 | 
					    u32 cbuf = 0;
 | 
				
			||||||
    u32 offset{};
 | 
					    u32 offset = 0;
 | 
				
			||||||
    u32 value{};
 | 
					    u32 value = 0;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct BoundSamplerKey {
 | 
					struct BoundSamplerKey {
 | 
				
			||||||
    u32 offset{};
 | 
					    u32 offset = 0;
 | 
				
			||||||
    Tegra::Engines::SamplerDescriptor sampler{};
 | 
					    Tegra::Engines::SamplerDescriptor sampler;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct BindlessSamplerKey {
 | 
					struct BindlessSamplerKey {
 | 
				
			||||||
    u32 cbuf{};
 | 
					    u32 cbuf = 0;
 | 
				
			||||||
    u32 offset{};
 | 
					    u32 offset = 0;
 | 
				
			||||||
    Tegra::Engines::SamplerDescriptor sampler{};
 | 
					    Tegra::Engines::SamplerDescriptor sampler;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
constexpr u32 NativeVersion = 12;
 | 
					constexpr u32 NativeVersion = 20;
 | 
				
			||||||
 | 
					 | 
				
			||||||
// Making sure sizes doesn't change by accident
 | 
					 | 
				
			||||||
static_assert(sizeof(ProgramVariant) == 20);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
ShaderCacheVersionHash GetShaderCacheVersionHash() {
 | 
					ShaderCacheVersionHash GetShaderCacheVersionHash() {
 | 
				
			||||||
    ShaderCacheVersionHash hash{};
 | 
					    ShaderCacheVersionHash hash{};
 | 
				
			||||||
@ -67,61 +59,124 @@ ShaderCacheVersionHash GetShaderCacheVersionHash() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
} // Anonymous namespace
 | 
					} // Anonymous namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, ShaderType type, ProgramCode code,
 | 
					ShaderDiskCacheEntry::ShaderDiskCacheEntry() = default;
 | 
				
			||||||
                                       ProgramCode code_b)
 | 
					 | 
				
			||||||
    : unique_identifier{unique_identifier}, type{type}, code{std::move(code)}, code_b{std::move(
 | 
					 | 
				
			||||||
                                                                                   code_b)} {}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
ShaderDiskCacheRaw::ShaderDiskCacheRaw() = default;
 | 
					ShaderDiskCacheEntry::~ShaderDiskCacheEntry() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ShaderDiskCacheRaw::~ShaderDiskCacheRaw() = default;
 | 
					bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) {
 | 
				
			||||||
 | 
					    if (file.ReadBytes(&type, sizeof(u32)) != sizeof(u32)) {
 | 
				
			||||||
bool ShaderDiskCacheRaw::Load(FileUtil::IOFile& file) {
 | 
					 | 
				
			||||||
    if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64) ||
 | 
					 | 
				
			||||||
        file.ReadBytes(&type, sizeof(u32)) != sizeof(u32)) {
 | 
					 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    u32 code_size{};
 | 
					    u32 code_size;
 | 
				
			||||||
    u32 code_size_b{};
 | 
					    u32 code_size_b;
 | 
				
			||||||
    if (file.ReadBytes(&code_size, sizeof(u32)) != sizeof(u32) ||
 | 
					    if (file.ReadBytes(&code_size, sizeof(u32)) != sizeof(u32) ||
 | 
				
			||||||
        file.ReadBytes(&code_size_b, sizeof(u32)) != sizeof(u32)) {
 | 
					        file.ReadBytes(&code_size_b, sizeof(u32)) != sizeof(u32)) {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    code.resize(code_size);
 | 
					    code.resize(code_size);
 | 
				
			||||||
    code_b.resize(code_size_b);
 | 
					    code_b.resize(code_size_b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (file.ReadArray(code.data(), code_size) != code_size)
 | 
					    if (file.ReadArray(code.data(), code_size) != code_size) {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (HasProgramA() && file.ReadArray(code_b.data(), code_size_b) != code_size_b) {
 | 
					    if (HasProgramA() && file.ReadArray(code_b.data(), code_size_b) != code_size_b) {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u8 is_texture_handler_size_known;
 | 
				
			||||||
 | 
					    u32 texture_handler_size_value;
 | 
				
			||||||
 | 
					    u32 num_keys;
 | 
				
			||||||
 | 
					    u32 num_bound_samplers;
 | 
				
			||||||
 | 
					    u32 num_bindless_samplers;
 | 
				
			||||||
 | 
					    if (file.ReadArray(&unique_identifier, 1) != 1 || file.ReadArray(&bound_buffer, 1) != 1 ||
 | 
				
			||||||
 | 
					        file.ReadArray(&is_texture_handler_size_known, 1) != 1 ||
 | 
				
			||||||
 | 
					        file.ReadArray(&texture_handler_size_value, 1) != 1 ||
 | 
				
			||||||
 | 
					        file.ReadArray(&graphics_info, 1) != 1 || file.ReadArray(&compute_info, 1) != 1 ||
 | 
				
			||||||
 | 
					        file.ReadArray(&num_keys, 1) != 1 || file.ReadArray(&num_bound_samplers, 1) != 1 ||
 | 
				
			||||||
 | 
					        file.ReadArray(&num_bindless_samplers, 1) != 1) {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (is_texture_handler_size_known) {
 | 
				
			||||||
 | 
					        texture_handler_size = texture_handler_size_value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<ConstBufferKey> flat_keys(num_keys);
 | 
				
			||||||
 | 
					    std::vector<BoundSamplerKey> flat_bound_samplers(num_bound_samplers);
 | 
				
			||||||
 | 
					    std::vector<BindlessSamplerKey> flat_bindless_samplers(num_bindless_samplers);
 | 
				
			||||||
 | 
					    if (file.ReadArray(flat_keys.data(), flat_keys.size()) != flat_keys.size() ||
 | 
				
			||||||
 | 
					        file.ReadArray(flat_bound_samplers.data(), flat_bound_samplers.size()) !=
 | 
				
			||||||
 | 
					            flat_bound_samplers.size() ||
 | 
				
			||||||
 | 
					        file.ReadArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) !=
 | 
				
			||||||
 | 
					            flat_bindless_samplers.size()) {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (const auto& key : flat_keys) {
 | 
				
			||||||
 | 
					        keys.insert({{key.cbuf, key.offset}, key.value});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (const auto& key : flat_bound_samplers) {
 | 
				
			||||||
 | 
					        bound_samplers.emplace(key.offset, key.sampler);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (const auto& key : flat_bindless_samplers) {
 | 
				
			||||||
 | 
					        bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const {
 | 
					bool ShaderDiskCacheEntry::Save(FileUtil::IOFile& file) const {
 | 
				
			||||||
    if (file.WriteObject(unique_identifier) != 1 || file.WriteObject(static_cast<u32>(type)) != 1 ||
 | 
					    if (file.WriteObject(static_cast<u32>(type)) != 1 ||
 | 
				
			||||||
        file.WriteObject(static_cast<u32>(code.size())) != 1 ||
 | 
					        file.WriteObject(static_cast<u32>(code.size())) != 1 ||
 | 
				
			||||||
        file.WriteObject(static_cast<u32>(code_b.size())) != 1) {
 | 
					        file.WriteObject(static_cast<u32>(code_b.size())) != 1) {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (file.WriteArray(code.data(), code.size()) != code.size()) {
 | 
				
			||||||
    if (file.WriteArray(code.data(), code.size()) != code.size())
 | 
					 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (HasProgramA() && file.WriteArray(code_b.data(), code_b.size()) != code_b.size()) {
 | 
					    if (HasProgramA() && file.WriteArray(code_b.data(), code_b.size()) != code_b.size()) {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return true;
 | 
					
 | 
				
			||||||
 | 
					    if (file.WriteObject(unique_identifier) != 1 || file.WriteObject(bound_buffer) != 1 ||
 | 
				
			||||||
 | 
					        file.WriteObject(static_cast<u8>(texture_handler_size.has_value())) != 1 ||
 | 
				
			||||||
 | 
					        file.WriteObject(texture_handler_size.value_or(0)) != 1 ||
 | 
				
			||||||
 | 
					        file.WriteObject(graphics_info) != 1 || file.WriteObject(compute_info) != 1 ||
 | 
				
			||||||
 | 
					        file.WriteObject(static_cast<u32>(keys.size())) != 1 ||
 | 
				
			||||||
 | 
					        file.WriteObject(static_cast<u32>(bound_samplers.size())) != 1 ||
 | 
				
			||||||
 | 
					        file.WriteObject(static_cast<u32>(bindless_samplers.size())) != 1) {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<ConstBufferKey> flat_keys;
 | 
				
			||||||
 | 
					    flat_keys.reserve(keys.size());
 | 
				
			||||||
 | 
					    for (const auto& [address, value] : keys) {
 | 
				
			||||||
 | 
					        flat_keys.push_back(ConstBufferKey{address.first, address.second, value});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<BoundSamplerKey> flat_bound_samplers;
 | 
				
			||||||
 | 
					    flat_bound_samplers.reserve(bound_samplers.size());
 | 
				
			||||||
 | 
					    for (const auto& [address, sampler] : bound_samplers) {
 | 
				
			||||||
 | 
					        flat_bound_samplers.push_back(BoundSamplerKey{address, sampler});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<BindlessSamplerKey> flat_bindless_samplers;
 | 
				
			||||||
 | 
					    flat_bindless_samplers.reserve(bindless_samplers.size());
 | 
				
			||||||
 | 
					    for (const auto& [address, sampler] : bindless_samplers) {
 | 
				
			||||||
 | 
					        flat_bindless_samplers.push_back(
 | 
				
			||||||
 | 
					            BindlessSamplerKey{address.first, address.second, sampler});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return file.WriteArray(flat_keys.data(), flat_keys.size()) == flat_keys.size() &&
 | 
				
			||||||
 | 
					           file.WriteArray(flat_bound_samplers.data(), flat_bound_samplers.size()) ==
 | 
				
			||||||
 | 
					               flat_bound_samplers.size() &&
 | 
				
			||||||
 | 
					           file.WriteArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) ==
 | 
				
			||||||
 | 
					               flat_bindless_samplers.size();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {}
 | 
					ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
 | 
					ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
 | 
					std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() {
 | 
				
			||||||
ShaderDiskCacheOpenGL::LoadTransferable() {
 | 
					 | 
				
			||||||
    // Skip games without title id
 | 
					    // Skip games without title id
 | 
				
			||||||
    const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
 | 
					    const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
 | 
				
			||||||
    if (!Settings::values.use_disk_shader_cache || !has_title_id) {
 | 
					    if (!Settings::values.use_disk_shader_cache || !has_title_id) {
 | 
				
			||||||
@ -130,17 +185,14 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    FileUtil::IOFile file(GetTransferablePath(), "rb");
 | 
					    FileUtil::IOFile file(GetTransferablePath(), "rb");
 | 
				
			||||||
    if (!file.IsOpen()) {
 | 
					    if (!file.IsOpen()) {
 | 
				
			||||||
        LOG_INFO(Render_OpenGL, "No transferable shader cache found for game with title id={}",
 | 
					        LOG_INFO(Render_OpenGL, "No transferable shader cache found");
 | 
				
			||||||
                 GetTitleID());
 | 
					 | 
				
			||||||
        is_usable = true;
 | 
					        is_usable = true;
 | 
				
			||||||
        return {};
 | 
					        return {};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u32 version{};
 | 
					    u32 version{};
 | 
				
			||||||
    if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) {
 | 
					    if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) {
 | 
				
			||||||
        LOG_ERROR(Render_OpenGL,
 | 
					        LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it");
 | 
				
			||||||
                  "Failed to get transferable cache version for title id={}, skipping",
 | 
					 | 
				
			||||||
                  GetTitleID());
 | 
					 | 
				
			||||||
        return {};
 | 
					        return {};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -158,105 +210,42 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Version is valid, load the shaders
 | 
					    // Version is valid, load the shaders
 | 
				
			||||||
    constexpr const char error_loading[] = "Failed to load transferable raw entry, skipping";
 | 
					    std::vector<ShaderDiskCacheEntry> entries;
 | 
				
			||||||
    std::vector<ShaderDiskCacheRaw> raws;
 | 
					 | 
				
			||||||
    std::vector<ShaderDiskCacheUsage> usages;
 | 
					 | 
				
			||||||
    while (file.Tell() < file.GetSize()) {
 | 
					    while (file.Tell() < file.GetSize()) {
 | 
				
			||||||
        TransferableEntryKind kind{};
 | 
					        ShaderDiskCacheEntry& entry = entries.emplace_back();
 | 
				
			||||||
        if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) {
 | 
					 | 
				
			||||||
            LOG_ERROR(Render_OpenGL, "Failed to read transferable file, skipping");
 | 
					 | 
				
			||||||
            return {};
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        switch (kind) {
 | 
					 | 
				
			||||||
        case TransferableEntryKind::Raw: {
 | 
					 | 
				
			||||||
            ShaderDiskCacheRaw entry;
 | 
					 | 
				
			||||||
        if (!entry.Load(file)) {
 | 
					        if (!entry.Load(file)) {
 | 
				
			||||||
                LOG_ERROR(Render_OpenGL, error_loading);
 | 
					            LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping");
 | 
				
			||||||
                return {};
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            transferable.insert({entry.GetUniqueIdentifier(), {}});
 | 
					 | 
				
			||||||
            raws.push_back(std::move(entry));
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        case TransferableEntryKind::Usage: {
 | 
					 | 
				
			||||||
            ShaderDiskCacheUsage usage;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            u32 num_keys{};
 | 
					 | 
				
			||||||
            u32 num_bound_samplers{};
 | 
					 | 
				
			||||||
            u32 num_bindless_samplers{};
 | 
					 | 
				
			||||||
            if (file.ReadArray(&usage.unique_identifier, 1) != 1 ||
 | 
					 | 
				
			||||||
                file.ReadArray(&usage.variant, 1) != 1 ||
 | 
					 | 
				
			||||||
                file.ReadArray(&usage.bound_buffer, 1) != 1 || file.ReadArray(&num_keys, 1) != 1 ||
 | 
					 | 
				
			||||||
                file.ReadArray(&num_bound_samplers, 1) != 1 ||
 | 
					 | 
				
			||||||
                file.ReadArray(&num_bindless_samplers, 1) != 1) {
 | 
					 | 
				
			||||||
                LOG_ERROR(Render_OpenGL, error_loading);
 | 
					 | 
				
			||||||
                return {};
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            std::vector<ConstBufferKey> keys(num_keys);
 | 
					 | 
				
			||||||
            std::vector<BoundSamplerKey> bound_samplers(num_bound_samplers);
 | 
					 | 
				
			||||||
            std::vector<BindlessSamplerKey> bindless_samplers(num_bindless_samplers);
 | 
					 | 
				
			||||||
            if (file.ReadArray(keys.data(), keys.size()) != keys.size() ||
 | 
					 | 
				
			||||||
                file.ReadArray(bound_samplers.data(), bound_samplers.size()) !=
 | 
					 | 
				
			||||||
                    bound_samplers.size() ||
 | 
					 | 
				
			||||||
                file.ReadArray(bindless_samplers.data(), bindless_samplers.size()) !=
 | 
					 | 
				
			||||||
                    bindless_samplers.size()) {
 | 
					 | 
				
			||||||
                LOG_ERROR(Render_OpenGL, error_loading);
 | 
					 | 
				
			||||||
                return {};
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            for (const auto& key : keys) {
 | 
					 | 
				
			||||||
                usage.keys.insert({{key.cbuf, key.offset}, key.value});
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            for (const auto& key : bound_samplers) {
 | 
					 | 
				
			||||||
                usage.bound_samplers.emplace(key.offset, key.sampler);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            for (const auto& key : bindless_samplers) {
 | 
					 | 
				
			||||||
                usage.bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler});
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            usages.push_back(std::move(usage));
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        default:
 | 
					 | 
				
			||||||
            LOG_ERROR(Render_OpenGL, "Unknown transferable shader cache entry kind={}, skipping",
 | 
					 | 
				
			||||||
                      static_cast<u32>(kind));
 | 
					 | 
				
			||||||
            return {};
 | 
					            return {};
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    is_usable = true;
 | 
					    is_usable = true;
 | 
				
			||||||
    return {{std::move(raws), std::move(usages)}};
 | 
					    return {std::move(entries)};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>
 | 
					std::vector<ShaderDiskCachePrecompiled> ShaderDiskCacheOpenGL::LoadPrecompiled() {
 | 
				
			||||||
ShaderDiskCacheOpenGL::LoadPrecompiled() {
 | 
					 | 
				
			||||||
    if (!is_usable) {
 | 
					    if (!is_usable) {
 | 
				
			||||||
        return {};
 | 
					        return {};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string path = GetPrecompiledPath();
 | 
					    FileUtil::IOFile file(GetPrecompiledPath(), "rb");
 | 
				
			||||||
    FileUtil::IOFile file(path, "rb");
 | 
					 | 
				
			||||||
    if (!file.IsOpen()) {
 | 
					    if (!file.IsOpen()) {
 | 
				
			||||||
        LOG_INFO(Render_OpenGL, "No precompiled shader cache found for game with title id={}",
 | 
					        LOG_INFO(Render_OpenGL, "No precompiled shader cache found");
 | 
				
			||||||
                 GetTitleID());
 | 
					 | 
				
			||||||
        return {};
 | 
					        return {};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto result = LoadPrecompiledFile(file);
 | 
					    if (const auto result = LoadPrecompiledFile(file)) {
 | 
				
			||||||
    if (!result) {
 | 
					        return *result;
 | 
				
			||||||
        LOG_INFO(Render_OpenGL,
 | 
					    }
 | 
				
			||||||
                 "Failed to load precompiled cache for game with title id={}, removing",
 | 
					
 | 
				
			||||||
                 GetTitleID());
 | 
					    LOG_INFO(Render_OpenGL, "Failed to load precompiled cache");
 | 
				
			||||||
    file.Close();
 | 
					    file.Close();
 | 
				
			||||||
    InvalidatePrecompiled();
 | 
					    InvalidatePrecompiled();
 | 
				
			||||||
    return {};
 | 
					    return {};
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return *result;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::optional<std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>
 | 
					std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::LoadPrecompiledFile(
 | 
				
			||||||
ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
 | 
					    FileUtil::IOFile& file) {
 | 
				
			||||||
    // Read compressed file from disk and decompress to virtual precompiled cache file
 | 
					    // Read compressed file from disk and decompress to virtual precompiled cache file
 | 
				
			||||||
    std::vector<u8> compressed(file.GetSize());
 | 
					    std::vector<u8> compressed(file.GetSize());
 | 
				
			||||||
    file.ReadBytes(compressed.data(), compressed.size());
 | 
					    file.ReadBytes(compressed.data(), compressed.size());
 | 
				
			||||||
@ -275,58 +264,22 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
 | 
				
			|||||||
        return {};
 | 
					        return {};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ShaderDumpsMap dumps;
 | 
					    std::vector<ShaderDiskCachePrecompiled> entries;
 | 
				
			||||||
    while (precompiled_cache_virtual_file_offset < precompiled_cache_virtual_file.GetSize()) {
 | 
					    while (precompiled_cache_virtual_file_offset < precompiled_cache_virtual_file.GetSize()) {
 | 
				
			||||||
        u32 num_keys{};
 | 
					        u32 binary_size;
 | 
				
			||||||
        u32 num_bound_samplers{};
 | 
					        auto& entry = entries.emplace_back();
 | 
				
			||||||
        u32 num_bindless_samplers{};
 | 
					        if (!LoadObjectFromPrecompiled(entry.unique_identifier) ||
 | 
				
			||||||
        ShaderDiskCacheUsage usage;
 | 
					            !LoadObjectFromPrecompiled(entry.binary_format) ||
 | 
				
			||||||
        if (!LoadObjectFromPrecompiled(usage.unique_identifier) ||
 | 
					            !LoadObjectFromPrecompiled(binary_size)) {
 | 
				
			||||||
            !LoadObjectFromPrecompiled(usage.variant) ||
 | 
					 | 
				
			||||||
            !LoadObjectFromPrecompiled(usage.bound_buffer) ||
 | 
					 | 
				
			||||||
            !LoadObjectFromPrecompiled(num_keys) ||
 | 
					 | 
				
			||||||
            !LoadObjectFromPrecompiled(num_bound_samplers) ||
 | 
					 | 
				
			||||||
            !LoadObjectFromPrecompiled(num_bindless_samplers)) {
 | 
					 | 
				
			||||||
            return {};
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        std::vector<ConstBufferKey> keys(num_keys);
 | 
					 | 
				
			||||||
        std::vector<BoundSamplerKey> bound_samplers(num_bound_samplers);
 | 
					 | 
				
			||||||
        std::vector<BindlessSamplerKey> bindless_samplers(num_bindless_samplers);
 | 
					 | 
				
			||||||
        if (!LoadArrayFromPrecompiled(keys.data(), keys.size()) ||
 | 
					 | 
				
			||||||
            !LoadArrayFromPrecompiled(bound_samplers.data(), bound_samplers.size()) !=
 | 
					 | 
				
			||||||
                bound_samplers.size() ||
 | 
					 | 
				
			||||||
            !LoadArrayFromPrecompiled(bindless_samplers.data(), bindless_samplers.size()) !=
 | 
					 | 
				
			||||||
                bindless_samplers.size()) {
 | 
					 | 
				
			||||||
            return {};
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        for (const auto& key : keys) {
 | 
					 | 
				
			||||||
            usage.keys.insert({{key.cbuf, key.offset}, key.value});
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        for (const auto& key : bound_samplers) {
 | 
					 | 
				
			||||||
            usage.bound_samplers.emplace(key.offset, key.sampler);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        for (const auto& key : bindless_samplers) {
 | 
					 | 
				
			||||||
            usage.bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler});
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ShaderDiskCacheDump dump;
 | 
					 | 
				
			||||||
        if (!LoadObjectFromPrecompiled(dump.binary_format)) {
 | 
					 | 
				
			||||||
            return {};
 | 
					            return {};
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        u32 binary_length{};
 | 
					        entry.binary.resize(binary_size);
 | 
				
			||||||
        if (!LoadObjectFromPrecompiled(binary_length)) {
 | 
					        if (!LoadArrayFromPrecompiled(entry.binary.data(), entry.binary.size())) {
 | 
				
			||||||
            return {};
 | 
					            return {};
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        dump.binary.resize(binary_length);
 | 
					 | 
				
			||||||
        if (!LoadArrayFromPrecompiled(dump.binary.data(), dump.binary.size())) {
 | 
					 | 
				
			||||||
            return {};
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return entries;
 | 
				
			||||||
        dumps.emplace(std::move(usage), dump);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return dumps;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ShaderDiskCacheOpenGL::InvalidateTransferable() {
 | 
					void ShaderDiskCacheOpenGL::InvalidateTransferable() {
 | 
				
			||||||
@ -346,13 +299,13 @@ void ShaderDiskCacheOpenGL::InvalidatePrecompiled() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) {
 | 
					void ShaderDiskCacheOpenGL::SaveEntry(const ShaderDiskCacheEntry& entry) {
 | 
				
			||||||
    if (!is_usable) {
 | 
					    if (!is_usable) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const u64 id = entry.GetUniqueIdentifier();
 | 
					    const u64 id = entry.unique_identifier;
 | 
				
			||||||
    if (transferable.find(id) != transferable.end()) {
 | 
					    if (stored_transferable.find(id) != stored_transferable.end()) {
 | 
				
			||||||
        // The shader already exists
 | 
					        // The shader already exists
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -361,71 +314,17 @@ void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) {
 | 
				
			|||||||
    if (!file.IsOpen()) {
 | 
					    if (!file.IsOpen()) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (file.WriteObject(TransferableEntryKind::Raw) != 1 || !entry.Save(file)) {
 | 
					    if (!entry.Save(file)) {
 | 
				
			||||||
        LOG_ERROR(Render_OpenGL, "Failed to save raw transferable cache entry, removing");
 | 
					        LOG_ERROR(Render_OpenGL, "Failed to save raw transferable cache entry, removing");
 | 
				
			||||||
        file.Close();
 | 
					        file.Close();
 | 
				
			||||||
        InvalidateTransferable();
 | 
					        InvalidateTransferable();
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    transferable.insert({id, {}});
 | 
					
 | 
				
			||||||
 | 
					    stored_transferable.insert(id);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) {
 | 
					void ShaderDiskCacheOpenGL::SavePrecompiled(u64 unique_identifier, GLuint program) {
 | 
				
			||||||
    if (!is_usable) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const auto it = transferable.find(usage.unique_identifier);
 | 
					 | 
				
			||||||
    ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    auto& usages{it->second};
 | 
					 | 
				
			||||||
    if (usages.find(usage) != usages.end()) {
 | 
					 | 
				
			||||||
        // Skip this variant since the shader is already stored.
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    usages.insert(usage);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    FileUtil::IOFile file = AppendTransferableFile();
 | 
					 | 
				
			||||||
    if (!file.IsOpen())
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    const auto Close = [&] {
 | 
					 | 
				
			||||||
        LOG_ERROR(Render_OpenGL, "Failed to save usage transferable cache entry, removing");
 | 
					 | 
				
			||||||
        file.Close();
 | 
					 | 
				
			||||||
        InvalidateTransferable();
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (file.WriteObject(TransferableEntryKind::Usage) != 1 ||
 | 
					 | 
				
			||||||
        file.WriteObject(usage.unique_identifier) != 1 || file.WriteObject(usage.variant) != 1 ||
 | 
					 | 
				
			||||||
        file.WriteObject(usage.bound_buffer) != 1 ||
 | 
					 | 
				
			||||||
        file.WriteObject(static_cast<u32>(usage.keys.size())) != 1 ||
 | 
					 | 
				
			||||||
        file.WriteObject(static_cast<u32>(usage.bound_samplers.size())) != 1 ||
 | 
					 | 
				
			||||||
        file.WriteObject(static_cast<u32>(usage.bindless_samplers.size())) != 1) {
 | 
					 | 
				
			||||||
        Close();
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    for (const auto& [pair, value] : usage.keys) {
 | 
					 | 
				
			||||||
        const auto [cbuf, offset] = pair;
 | 
					 | 
				
			||||||
        if (file.WriteObject(ConstBufferKey{cbuf, offset, value}) != 1) {
 | 
					 | 
				
			||||||
            Close();
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    for (const auto& [offset, sampler] : usage.bound_samplers) {
 | 
					 | 
				
			||||||
        if (file.WriteObject(BoundSamplerKey{offset, sampler}) != 1) {
 | 
					 | 
				
			||||||
            Close();
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    for (const auto& [pair, sampler] : usage.bindless_samplers) {
 | 
					 | 
				
			||||||
        const auto [cbuf, offset] = pair;
 | 
					 | 
				
			||||||
        if (file.WriteObject(BindlessSamplerKey{cbuf, offset, sampler}) != 1) {
 | 
					 | 
				
			||||||
            Close();
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint program) {
 | 
					 | 
				
			||||||
    if (!is_usable) {
 | 
					    if (!is_usable) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -437,51 +336,19 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p
 | 
				
			|||||||
        SavePrecompiledHeaderToVirtualPrecompiledCache();
 | 
					        SavePrecompiledHeaderToVirtualPrecompiledCache();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GLint binary_length{};
 | 
					    GLint binary_length;
 | 
				
			||||||
    glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binary_length);
 | 
					    glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binary_length);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GLenum binary_format{};
 | 
					    GLenum binary_format;
 | 
				
			||||||
    std::vector<u8> binary(binary_length);
 | 
					    std::vector<u8> binary(binary_length);
 | 
				
			||||||
    glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data());
 | 
					    glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto Close = [&] {
 | 
					    if (!SaveObjectToPrecompiled(unique_identifier) || !SaveObjectToPrecompiled(binary_format) ||
 | 
				
			||||||
        LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016X}, removing",
 | 
					        !SaveObjectToPrecompiled(static_cast<u32>(binary.size())) ||
 | 
				
			||||||
                  usage.unique_identifier);
 | 
					 | 
				
			||||||
        InvalidatePrecompiled();
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!SaveObjectToPrecompiled(usage.unique_identifier) ||
 | 
					 | 
				
			||||||
        !SaveObjectToPrecompiled(usage.variant) || !SaveObjectToPrecompiled(usage.bound_buffer) ||
 | 
					 | 
				
			||||||
        !SaveObjectToPrecompiled(static_cast<u32>(usage.keys.size())) ||
 | 
					 | 
				
			||||||
        !SaveObjectToPrecompiled(static_cast<u32>(usage.bound_samplers.size())) ||
 | 
					 | 
				
			||||||
        !SaveObjectToPrecompiled(static_cast<u32>(usage.bindless_samplers.size()))) {
 | 
					 | 
				
			||||||
        Close();
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    for (const auto& [pair, value] : usage.keys) {
 | 
					 | 
				
			||||||
        const auto [cbuf, offset] = pair;
 | 
					 | 
				
			||||||
        if (SaveObjectToPrecompiled(ConstBufferKey{cbuf, offset, value}) != 1) {
 | 
					 | 
				
			||||||
            Close();
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    for (const auto& [offset, sampler] : usage.bound_samplers) {
 | 
					 | 
				
			||||||
        if (SaveObjectToPrecompiled(BoundSamplerKey{offset, sampler}) != 1) {
 | 
					 | 
				
			||||||
            Close();
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    for (const auto& [pair, sampler] : usage.bindless_samplers) {
 | 
					 | 
				
			||||||
        const auto [cbuf, offset] = pair;
 | 
					 | 
				
			||||||
        if (SaveObjectToPrecompiled(BindlessSamplerKey{cbuf, offset, sampler}) != 1) {
 | 
					 | 
				
			||||||
            Close();
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!SaveObjectToPrecompiled(static_cast<u32>(binary_format)) ||
 | 
					 | 
				
			||||||
        !SaveObjectToPrecompiled(static_cast<u32>(binary_length)) ||
 | 
					 | 
				
			||||||
        !SaveArrayToPrecompiled(binary.data(), binary.size())) {
 | 
					        !SaveArrayToPrecompiled(binary.data(), binary.size())) {
 | 
				
			||||||
        Close();
 | 
					        LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016X}, removing",
 | 
				
			||||||
 | 
					                  unique_identifier);
 | 
				
			||||||
 | 
					        InvalidatePrecompiled();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -534,7 +401,6 @@ void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
 | 
				
			|||||||
    if (file.WriteBytes(compressed.data(), compressed.size()) != compressed.size()) {
 | 
					    if (file.WriteBytes(compressed.data(), compressed.size()) != compressed.size()) {
 | 
				
			||||||
        LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version in path={}",
 | 
					        LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version in path={}",
 | 
				
			||||||
                  precompiled_path);
 | 
					                  precompiled_path);
 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -19,8 +19,7 @@
 | 
				
			|||||||
#include "common/common_types.h"
 | 
					#include "common/common_types.h"
 | 
				
			||||||
#include "core/file_sys/vfs_vector.h"
 | 
					#include "core/file_sys/vfs_vector.h"
 | 
				
			||||||
#include "video_core/engines/shader_type.h"
 | 
					#include "video_core/engines/shader_type.h"
 | 
				
			||||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
 | 
					#include "video_core/shader/registry.h"
 | 
				
			||||||
#include "video_core/shader/const_buffer_locker.h"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Core {
 | 
					namespace Core {
 | 
				
			||||||
class System;
 | 
					class System;
 | 
				
			||||||
@ -32,139 +31,39 @@ class IOFile;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace OpenGL {
 | 
					namespace OpenGL {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ShaderDiskCacheUsage;
 | 
					 | 
				
			||||||
struct ShaderDiskCacheDump;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using ProgramCode = std::vector<u64>;
 | 
					using ProgramCode = std::vector<u64>;
 | 
				
			||||||
using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Describes the different variants a program can be compiled with.
 | 
					/// Describes a shader and how it's used by the guest GPU
 | 
				
			||||||
struct ProgramVariant final {
 | 
					struct ShaderDiskCacheEntry {
 | 
				
			||||||
    ProgramVariant() = default;
 | 
					    ShaderDiskCacheEntry();
 | 
				
			||||||
 | 
					    ~ShaderDiskCacheEntry();
 | 
				
			||||||
    /// Graphics constructor.
 | 
					 | 
				
			||||||
    explicit constexpr ProgramVariant(GLenum primitive_mode) noexcept
 | 
					 | 
				
			||||||
        : primitive_mode{primitive_mode} {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Compute constructor.
 | 
					 | 
				
			||||||
    explicit constexpr ProgramVariant(u32 block_x, u32 block_y, u32 block_z, u32 shared_memory_size,
 | 
					 | 
				
			||||||
                                      u32 local_memory_size) noexcept
 | 
					 | 
				
			||||||
        : block_x{block_x}, block_y{static_cast<u16>(block_y)}, block_z{static_cast<u16>(block_z)},
 | 
					 | 
				
			||||||
          shared_memory_size{shared_memory_size}, local_memory_size{local_memory_size} {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Graphics specific parameters.
 | 
					 | 
				
			||||||
    GLenum primitive_mode{};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Compute specific parameters.
 | 
					 | 
				
			||||||
    u32 block_x{};
 | 
					 | 
				
			||||||
    u16 block_y{};
 | 
					 | 
				
			||||||
    u16 block_z{};
 | 
					 | 
				
			||||||
    u32 shared_memory_size{};
 | 
					 | 
				
			||||||
    u32 local_memory_size{};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool operator==(const ProgramVariant& rhs) const noexcept {
 | 
					 | 
				
			||||||
        return std::tie(primitive_mode, block_x, block_y, block_z, shared_memory_size,
 | 
					 | 
				
			||||||
                        local_memory_size) == std::tie(rhs.primitive_mode, rhs.block_x, rhs.block_y,
 | 
					 | 
				
			||||||
                                                       rhs.block_z, rhs.shared_memory_size,
 | 
					 | 
				
			||||||
                                                       rhs.local_memory_size);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool operator!=(const ProgramVariant& rhs) const noexcept {
 | 
					 | 
				
			||||||
        return !operator==(rhs);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
static_assert(std::is_trivially_copyable_v<ProgramVariant>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Describes how a shader is used.
 | 
					 | 
				
			||||||
struct ShaderDiskCacheUsage {
 | 
					 | 
				
			||||||
    u64 unique_identifier{};
 | 
					 | 
				
			||||||
    ProgramVariant variant;
 | 
					 | 
				
			||||||
    u32 bound_buffer{};
 | 
					 | 
				
			||||||
    VideoCommon::Shader::KeyMap keys;
 | 
					 | 
				
			||||||
    VideoCommon::Shader::BoundSamplerMap bound_samplers;
 | 
					 | 
				
			||||||
    VideoCommon::Shader::BindlessSamplerMap bindless_samplers;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool operator==(const ShaderDiskCacheUsage& rhs) const {
 | 
					 | 
				
			||||||
        return std::tie(unique_identifier, variant, keys, bound_samplers, bindless_samplers) ==
 | 
					 | 
				
			||||||
               std::tie(rhs.unique_identifier, rhs.variant, rhs.keys, rhs.bound_samplers,
 | 
					 | 
				
			||||||
                        rhs.bindless_samplers);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool operator!=(const ShaderDiskCacheUsage& rhs) const {
 | 
					 | 
				
			||||||
        return !operator==(rhs);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace OpenGL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace std {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template <>
 | 
					 | 
				
			||||||
struct hash<OpenGL::ProgramVariant> {
 | 
					 | 
				
			||||||
    std::size_t operator()(const OpenGL::ProgramVariant& variant) const noexcept {
 | 
					 | 
				
			||||||
        return (static_cast<std::size_t>(variant.primitive_mode) << 6) ^
 | 
					 | 
				
			||||||
               static_cast<std::size_t>(variant.block_x) ^
 | 
					 | 
				
			||||||
               (static_cast<std::size_t>(variant.block_y) << 32) ^
 | 
					 | 
				
			||||||
               (static_cast<std::size_t>(variant.block_z) << 48) ^
 | 
					 | 
				
			||||||
               (static_cast<std::size_t>(variant.shared_memory_size) << 16) ^
 | 
					 | 
				
			||||||
               (static_cast<std::size_t>(variant.local_memory_size) << 36);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template <>
 | 
					 | 
				
			||||||
struct hash<OpenGL::ShaderDiskCacheUsage> {
 | 
					 | 
				
			||||||
    std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const noexcept {
 | 
					 | 
				
			||||||
        return static_cast<std::size_t>(usage.unique_identifier) ^
 | 
					 | 
				
			||||||
               std::hash<OpenGL::ProgramVariant>{}(usage.variant);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace std
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace OpenGL {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Describes a shader how it's used by the guest GPU
 | 
					 | 
				
			||||||
class ShaderDiskCacheRaw {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
    explicit ShaderDiskCacheRaw(u64 unique_identifier, Tegra::Engines::ShaderType type,
 | 
					 | 
				
			||||||
                                ProgramCode code, ProgramCode code_b = {});
 | 
					 | 
				
			||||||
    ShaderDiskCacheRaw();
 | 
					 | 
				
			||||||
    ~ShaderDiskCacheRaw();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool Load(FileUtil::IOFile& file);
 | 
					    bool Load(FileUtil::IOFile& file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool Save(FileUtil::IOFile& file) const;
 | 
					    bool Save(FileUtil::IOFile& file) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u64 GetUniqueIdentifier() const {
 | 
					 | 
				
			||||||
        return unique_identifier;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool HasProgramA() const {
 | 
					    bool HasProgramA() const {
 | 
				
			||||||
        return !code.empty() && !code_b.empty();
 | 
					        return !code.empty() && !code_b.empty();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Tegra::Engines::ShaderType GetType() const {
 | 
					 | 
				
			||||||
        return type;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const ProgramCode& GetCode() const {
 | 
					 | 
				
			||||||
        return code;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const ProgramCode& GetCodeB() const {
 | 
					 | 
				
			||||||
        return code_b;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
    u64 unique_identifier{};
 | 
					 | 
				
			||||||
    Tegra::Engines::ShaderType type{};
 | 
					    Tegra::Engines::ShaderType type{};
 | 
				
			||||||
    ProgramCode code;
 | 
					    ProgramCode code;
 | 
				
			||||||
    ProgramCode code_b;
 | 
					    ProgramCode code_b;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u64 unique_identifier = 0;
 | 
				
			||||||
 | 
					    std::optional<u32> texture_handler_size;
 | 
				
			||||||
 | 
					    u32 bound_buffer = 0;
 | 
				
			||||||
 | 
					    VideoCommon::Shader::GraphicsInfo graphics_info;
 | 
				
			||||||
 | 
					    VideoCommon::Shader::ComputeInfo compute_info;
 | 
				
			||||||
 | 
					    VideoCommon::Shader::KeyMap keys;
 | 
				
			||||||
 | 
					    VideoCommon::Shader::BoundSamplerMap bound_samplers;
 | 
				
			||||||
 | 
					    VideoCommon::Shader::BindlessSamplerMap bindless_samplers;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Contains an OpenGL dumped binary program
 | 
					/// Contains an OpenGL dumped binary program
 | 
				
			||||||
struct ShaderDiskCacheDump {
 | 
					struct ShaderDiskCachePrecompiled {
 | 
				
			||||||
    GLenum binary_format{};
 | 
					    u64 unique_identifier = 0;
 | 
				
			||||||
 | 
					    GLenum binary_format = 0;
 | 
				
			||||||
    std::vector<u8> binary;
 | 
					    std::vector<u8> binary;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -174,11 +73,10 @@ public:
 | 
				
			|||||||
    ~ShaderDiskCacheOpenGL();
 | 
					    ~ShaderDiskCacheOpenGL();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Loads transferable cache. If file has a old version or on failure, it deletes the file.
 | 
					    /// Loads transferable cache. If file has a old version or on failure, it deletes the file.
 | 
				
			||||||
    std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
 | 
					    std::optional<std::vector<ShaderDiskCacheEntry>> LoadTransferable();
 | 
				
			||||||
    LoadTransferable();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Loads current game's precompiled cache. Invalidates on failure.
 | 
					    /// Loads current game's precompiled cache. Invalidates on failure.
 | 
				
			||||||
    std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> LoadPrecompiled();
 | 
					    std::vector<ShaderDiskCachePrecompiled> LoadPrecompiled();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Removes the transferable (and precompiled) cache file.
 | 
					    /// Removes the transferable (and precompiled) cache file.
 | 
				
			||||||
    void InvalidateTransferable();
 | 
					    void InvalidateTransferable();
 | 
				
			||||||
@ -187,21 +85,18 @@ public:
 | 
				
			|||||||
    void InvalidatePrecompiled();
 | 
					    void InvalidatePrecompiled();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Saves a raw dump to the transferable file. Checks for collisions.
 | 
					    /// Saves a raw dump to the transferable file. Checks for collisions.
 | 
				
			||||||
    void SaveRaw(const ShaderDiskCacheRaw& entry);
 | 
					    void SaveEntry(const ShaderDiskCacheEntry& entry);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Saves shader usage to the transferable file. Does not check for collisions.
 | 
					 | 
				
			||||||
    void SaveUsage(const ShaderDiskCacheUsage& usage);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Saves a dump entry to the precompiled file. Does not check for collisions.
 | 
					    /// Saves a dump entry to the precompiled file. Does not check for collisions.
 | 
				
			||||||
    void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program);
 | 
					    void SavePrecompiled(u64 unique_identifier, GLuint program);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Serializes virtual precompiled shader cache file to real file
 | 
					    /// Serializes virtual precompiled shader cache file to real file
 | 
				
			||||||
    void SaveVirtualPrecompiledFile();
 | 
					    void SaveVirtualPrecompiledFile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    /// Loads the transferable cache. Returns empty on failure.
 | 
					    /// Loads the transferable cache. Returns empty on failure.
 | 
				
			||||||
    std::optional<std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>
 | 
					    std::optional<std::vector<ShaderDiskCachePrecompiled>> LoadPrecompiledFile(
 | 
				
			||||||
    LoadPrecompiledFile(FileUtil::IOFile& file);
 | 
					        FileUtil::IOFile& file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Opens current game's transferable file and write it's header if it doesn't exist
 | 
					    /// Opens current game's transferable file and write it's header if it doesn't exist
 | 
				
			||||||
    FileUtil::IOFile AppendTransferableFile() const;
 | 
					    FileUtil::IOFile AppendTransferableFile() const;
 | 
				
			||||||
@ -270,7 +165,7 @@ private:
 | 
				
			|||||||
    std::size_t precompiled_cache_virtual_file_offset = 0;
 | 
					    std::size_t precompiled_cache_virtual_file_offset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Stored transferable shaders
 | 
					    // Stored transferable shaders
 | 
				
			||||||
    std::unordered_map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable;
 | 
					    std::unordered_set<u64> stored_transferable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // The cache has been loaded at boot
 | 
					    // The cache has been loaded at boot
 | 
				
			||||||
    bool is_usable{};
 | 
					    bool is_usable{};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,109 +0,0 @@
 | 
				
			|||||||
// Copyright 2018 yuzu Emulator Project
 | 
					 | 
				
			||||||
// Licensed under GPLv2 or any later version
 | 
					 | 
				
			||||||
// Refer to the license.txt file included.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <fmt/format.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "video_core/engines/maxwell_3d.h"
 | 
					 | 
				
			||||||
#include "video_core/engines/shader_type.h"
 | 
					 | 
				
			||||||
#include "video_core/renderer_opengl/gl_device.h"
 | 
					 | 
				
			||||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
 | 
					 | 
				
			||||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
 | 
					 | 
				
			||||||
#include "video_core/shader/shader_ir.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace OpenGL::GLShader {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using Tegra::Engines::Maxwell3D;
 | 
					 | 
				
			||||||
using Tegra::Engines::ShaderType;
 | 
					 | 
				
			||||||
using VideoCommon::Shader::CompileDepth;
 | 
					 | 
				
			||||||
using VideoCommon::Shader::CompilerSettings;
 | 
					 | 
				
			||||||
using VideoCommon::Shader::ProgramCode;
 | 
					 | 
				
			||||||
using VideoCommon::Shader::ShaderIR;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string GenerateVertexShader(const Device& device, const ShaderIR& ir, const ShaderIR* ir_b) {
 | 
					 | 
				
			||||||
    std::string out = GetCommonDeclarations();
 | 
					 | 
				
			||||||
    out += fmt::format(R"(
 | 
					 | 
				
			||||||
layout (std140, binding = {}) uniform vs_config {{
 | 
					 | 
				
			||||||
    float y_direction;
 | 
					 | 
				
			||||||
}};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
)",
 | 
					 | 
				
			||||||
                       EmulationUniformBlockBinding);
 | 
					 | 
				
			||||||
    out += Decompile(device, ir, ShaderType::Vertex, "vertex");
 | 
					 | 
				
			||||||
    if (ir_b) {
 | 
					 | 
				
			||||||
        out += Decompile(device, *ir_b, ShaderType::Vertex, "vertex_b");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    out += R"(
 | 
					 | 
				
			||||||
void main() {
 | 
					 | 
				
			||||||
    gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);
 | 
					 | 
				
			||||||
    execute_vertex();
 | 
					 | 
				
			||||||
)";
 | 
					 | 
				
			||||||
    if (ir_b) {
 | 
					 | 
				
			||||||
        out += "    execute_vertex_b();";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    out += "}\n";
 | 
					 | 
				
			||||||
    return out;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string GenerateGeometryShader(const Device& device, const ShaderIR& ir) {
 | 
					 | 
				
			||||||
    std::string out = GetCommonDeclarations();
 | 
					 | 
				
			||||||
    out += fmt::format(R"(
 | 
					 | 
				
			||||||
layout (std140, binding = {}) uniform gs_config {{
 | 
					 | 
				
			||||||
    float y_direction;
 | 
					 | 
				
			||||||
}};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
)",
 | 
					 | 
				
			||||||
                       EmulationUniformBlockBinding);
 | 
					 | 
				
			||||||
    out += Decompile(device, ir, ShaderType::Geometry, "geometry");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    out += R"(
 | 
					 | 
				
			||||||
void main() {
 | 
					 | 
				
			||||||
    execute_geometry();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
)";
 | 
					 | 
				
			||||||
    return out;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string GenerateFragmentShader(const Device& device, const ShaderIR& ir) {
 | 
					 | 
				
			||||||
    std::string out = GetCommonDeclarations();
 | 
					 | 
				
			||||||
    out += fmt::format(R"(
 | 
					 | 
				
			||||||
layout (location = 0) out vec4 FragColor0;
 | 
					 | 
				
			||||||
layout (location = 1) out vec4 FragColor1;
 | 
					 | 
				
			||||||
layout (location = 2) out vec4 FragColor2;
 | 
					 | 
				
			||||||
layout (location = 3) out vec4 FragColor3;
 | 
					 | 
				
			||||||
layout (location = 4) out vec4 FragColor4;
 | 
					 | 
				
			||||||
layout (location = 5) out vec4 FragColor5;
 | 
					 | 
				
			||||||
layout (location = 6) out vec4 FragColor6;
 | 
					 | 
				
			||||||
layout (location = 7) out vec4 FragColor7;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
layout (std140, binding = {}) uniform fs_config {{
 | 
					 | 
				
			||||||
    float y_direction;
 | 
					 | 
				
			||||||
}};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
)",
 | 
					 | 
				
			||||||
                       EmulationUniformBlockBinding);
 | 
					 | 
				
			||||||
    out += Decompile(device, ir, ShaderType::Fragment, "fragment");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    out += R"(
 | 
					 | 
				
			||||||
void main() {
 | 
					 | 
				
			||||||
    execute_fragment();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
)";
 | 
					 | 
				
			||||||
    return out;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string GenerateComputeShader(const Device& device, const ShaderIR& ir) {
 | 
					 | 
				
			||||||
    std::string out = GetCommonDeclarations();
 | 
					 | 
				
			||||||
    out += Decompile(device, ir, ShaderType::Compute, "compute");
 | 
					 | 
				
			||||||
    out += R"(
 | 
					 | 
				
			||||||
void main() {
 | 
					 | 
				
			||||||
    execute_compute();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
)";
 | 
					 | 
				
			||||||
    return out;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace OpenGL::GLShader
 | 
					 | 
				
			||||||
@ -1,34 +0,0 @@
 | 
				
			|||||||
// Copyright 2018 yuzu Emulator Project
 | 
					 | 
				
			||||||
// Licensed under GPLv2 or any later version
 | 
					 | 
				
			||||||
// Refer to the license.txt file included.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <vector>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "common/common_types.h"
 | 
					 | 
				
			||||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
 | 
					 | 
				
			||||||
#include "video_core/shader/shader_ir.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace OpenGL {
 | 
					 | 
				
			||||||
class Device;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace OpenGL::GLShader {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using VideoCommon::Shader::ProgramCode;
 | 
					 | 
				
			||||||
using VideoCommon::Shader::ShaderIR;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Generates the GLSL vertex shader program source code for the given VS program
 | 
					 | 
				
			||||||
std::string GenerateVertexShader(const Device& device, const ShaderIR& ir, const ShaderIR* ir_b);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Generates the GLSL geometry shader program source code for the given GS program
 | 
					 | 
				
			||||||
std::string GenerateGeometryShader(const Device& device, const ShaderIR& ir);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Generates the GLSL fragment shader program source code for the given FS program
 | 
					 | 
				
			||||||
std::string GenerateFragmentShader(const Device& device, const ShaderIR& ir);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Generates the GLSL compute shader program source code for the given CS program
 | 
					 | 
				
			||||||
std::string GenerateComputeShader(const Device& device, const ShaderIR& ir);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace OpenGL::GLShader
 | 
					 | 
				
			||||||
@ -161,8 +161,8 @@ CachedShader::CachedShader(Core::System& system, Tegra::Engines::ShaderType stag
 | 
				
			|||||||
                           GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr,
 | 
					                           GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr,
 | 
				
			||||||
                           ProgramCode program_code, u32 main_offset)
 | 
					                           ProgramCode program_code, u32 main_offset)
 | 
				
			||||||
    : RasterizerCacheObject{host_ptr}, gpu_addr{gpu_addr}, cpu_addr{cpu_addr},
 | 
					    : RasterizerCacheObject{host_ptr}, gpu_addr{gpu_addr}, cpu_addr{cpu_addr},
 | 
				
			||||||
      program_code{std::move(program_code)}, locker{stage, GetEngine(system, stage)},
 | 
					      program_code{std::move(program_code)}, registry{stage, GetEngine(system, stage)},
 | 
				
			||||||
      shader_ir{this->program_code, main_offset, compiler_settings, locker},
 | 
					      shader_ir{this->program_code, main_offset, compiler_settings, registry},
 | 
				
			||||||
      entries{GenerateShaderEntries(shader_ir)} {}
 | 
					      entries{GenerateShaderEntries(shader_ir)} {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CachedShader::~CachedShader() = default;
 | 
					CachedShader::~CachedShader() = default;
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@
 | 
				
			|||||||
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
 | 
					#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
 | 
				
			||||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
 | 
					#include "video_core/renderer_vulkan/vk_resource_manager.h"
 | 
				
			||||||
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
 | 
					#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
 | 
				
			||||||
#include "video_core/shader/const_buffer_locker.h"
 | 
					#include "video_core/shader/registry.h"
 | 
				
			||||||
#include "video_core/shader/shader_ir.h"
 | 
					#include "video_core/shader/shader_ir.h"
 | 
				
			||||||
#include "video_core/surface.h"
 | 
					#include "video_core/surface.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -147,7 +147,7 @@ private:
 | 
				
			|||||||
    GPUVAddr gpu_addr{};
 | 
					    GPUVAddr gpu_addr{};
 | 
				
			||||||
    VAddr cpu_addr{};
 | 
					    VAddr cpu_addr{};
 | 
				
			||||||
    ProgramCode program_code;
 | 
					    ProgramCode program_code;
 | 
				
			||||||
    VideoCommon::Shader::ConstBufferLocker locker;
 | 
					    VideoCommon::Shader::Registry registry;
 | 
				
			||||||
    VideoCommon::Shader::ShaderIR shader_ir;
 | 
					    VideoCommon::Shader::ShaderIR shader_ir;
 | 
				
			||||||
    ShaderEntries entries;
 | 
					    ShaderEntries entries;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,126 +0,0 @@
 | 
				
			|||||||
// Copyright 2019 yuzu Emulator Project
 | 
					 | 
				
			||||||
// Licensed under GPLv2 or any later version
 | 
					 | 
				
			||||||
// Refer to the license.txt file included.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <algorithm>
 | 
					 | 
				
			||||||
#include <tuple>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "common/common_types.h"
 | 
					 | 
				
			||||||
#include "video_core/engines/maxwell_3d.h"
 | 
					 | 
				
			||||||
#include "video_core/engines/shader_type.h"
 | 
					 | 
				
			||||||
#include "video_core/shader/const_buffer_locker.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace VideoCommon::Shader {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using Tegra::Engines::SamplerDescriptor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ConstBufferLocker::ConstBufferLocker(Tegra::Engines::ShaderType shader_stage)
 | 
					 | 
				
			||||||
    : stage{shader_stage} {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ConstBufferLocker::ConstBufferLocker(Tegra::Engines::ShaderType shader_stage,
 | 
					 | 
				
			||||||
                                     Tegra::Engines::ConstBufferEngineInterface& engine)
 | 
					 | 
				
			||||||
    : stage{shader_stage}, engine{&engine} {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ConstBufferLocker::~ConstBufferLocker() = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::optional<u32> ConstBufferLocker::ObtainKey(u32 buffer, u32 offset) {
 | 
					 | 
				
			||||||
    const std::pair<u32, u32> key = {buffer, offset};
 | 
					 | 
				
			||||||
    const auto iter = keys.find(key);
 | 
					 | 
				
			||||||
    if (iter != keys.end()) {
 | 
					 | 
				
			||||||
        return iter->second;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!engine) {
 | 
					 | 
				
			||||||
        return std::nullopt;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const u32 value = engine->AccessConstBuffer32(stage, buffer, offset);
 | 
					 | 
				
			||||||
    keys.emplace(key, value);
 | 
					 | 
				
			||||||
    return value;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::optional<SamplerDescriptor> ConstBufferLocker::ObtainBoundSampler(u32 offset) {
 | 
					 | 
				
			||||||
    const u32 key = offset;
 | 
					 | 
				
			||||||
    const auto iter = bound_samplers.find(key);
 | 
					 | 
				
			||||||
    if (iter != bound_samplers.end()) {
 | 
					 | 
				
			||||||
        return iter->second;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!engine) {
 | 
					 | 
				
			||||||
        return std::nullopt;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const SamplerDescriptor value = engine->AccessBoundSampler(stage, offset);
 | 
					 | 
				
			||||||
    bound_samplers.emplace(key, value);
 | 
					 | 
				
			||||||
    return value;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::optional<Tegra::Engines::SamplerDescriptor> ConstBufferLocker::ObtainBindlessSampler(
 | 
					 | 
				
			||||||
    u32 buffer, u32 offset) {
 | 
					 | 
				
			||||||
    const std::pair key = {buffer, offset};
 | 
					 | 
				
			||||||
    const auto iter = bindless_samplers.find(key);
 | 
					 | 
				
			||||||
    if (iter != bindless_samplers.end()) {
 | 
					 | 
				
			||||||
        return iter->second;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!engine) {
 | 
					 | 
				
			||||||
        return std::nullopt;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const SamplerDescriptor value = engine->AccessBindlessSampler(stage, buffer, offset);
 | 
					 | 
				
			||||||
    bindless_samplers.emplace(key, value);
 | 
					 | 
				
			||||||
    return value;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::optional<u32> ConstBufferLocker::ObtainBoundBuffer() {
 | 
					 | 
				
			||||||
    if (bound_buffer_saved) {
 | 
					 | 
				
			||||||
        return bound_buffer;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!engine) {
 | 
					 | 
				
			||||||
        return std::nullopt;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    bound_buffer_saved = true;
 | 
					 | 
				
			||||||
    bound_buffer = engine->GetBoundBuffer();
 | 
					 | 
				
			||||||
    return bound_buffer;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ConstBufferLocker::InsertKey(u32 buffer, u32 offset, u32 value) {
 | 
					 | 
				
			||||||
    keys.insert_or_assign({buffer, offset}, value);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ConstBufferLocker::InsertBoundSampler(u32 offset, SamplerDescriptor sampler) {
 | 
					 | 
				
			||||||
    bound_samplers.insert_or_assign(offset, sampler);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ConstBufferLocker::InsertBindlessSampler(u32 buffer, u32 offset, SamplerDescriptor sampler) {
 | 
					 | 
				
			||||||
    bindless_samplers.insert_or_assign({buffer, offset}, sampler);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void ConstBufferLocker::SetBoundBuffer(u32 buffer) {
 | 
					 | 
				
			||||||
    bound_buffer_saved = true;
 | 
					 | 
				
			||||||
    bound_buffer = buffer;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool ConstBufferLocker::IsConsistent() const {
 | 
					 | 
				
			||||||
    if (!engine) {
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return std::all_of(keys.begin(), keys.end(),
 | 
					 | 
				
			||||||
                       [this](const auto& pair) {
 | 
					 | 
				
			||||||
                           const auto [cbuf, offset] = pair.first;
 | 
					 | 
				
			||||||
                           const auto value = pair.second;
 | 
					 | 
				
			||||||
                           return value == engine->AccessConstBuffer32(stage, cbuf, offset);
 | 
					 | 
				
			||||||
                       }) &&
 | 
					 | 
				
			||||||
           std::all_of(bound_samplers.begin(), bound_samplers.end(),
 | 
					 | 
				
			||||||
                       [this](const auto& sampler) {
 | 
					 | 
				
			||||||
                           const auto [key, value] = sampler;
 | 
					 | 
				
			||||||
                           return value == engine->AccessBoundSampler(stage, key);
 | 
					 | 
				
			||||||
                       }) &&
 | 
					 | 
				
			||||||
           std::all_of(bindless_samplers.begin(), bindless_samplers.end(),
 | 
					 | 
				
			||||||
                       [this](const auto& sampler) {
 | 
					 | 
				
			||||||
                           const auto [cbuf, offset] = sampler.first;
 | 
					 | 
				
			||||||
                           const auto value = sampler.second;
 | 
					 | 
				
			||||||
                           return value == engine->AccessBindlessSampler(stage, cbuf, offset);
 | 
					 | 
				
			||||||
                       });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool ConstBufferLocker::HasEqualKeys(const ConstBufferLocker& rhs) const {
 | 
					 | 
				
			||||||
    return std::tie(keys, bound_samplers, bindless_samplers) ==
 | 
					 | 
				
			||||||
           std::tie(rhs.keys, rhs.bound_samplers, rhs.bindless_samplers);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace VideoCommon::Shader
 | 
					 | 
				
			||||||
@ -1,103 +0,0 @@
 | 
				
			|||||||
// Copyright 2019 yuzu Emulator Project
 | 
					 | 
				
			||||||
// Licensed under GPLv2 or any later version
 | 
					 | 
				
			||||||
// Refer to the license.txt file included.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <optional>
 | 
					 | 
				
			||||||
#include <unordered_map>
 | 
					 | 
				
			||||||
#include "common/common_types.h"
 | 
					 | 
				
			||||||
#include "common/hash.h"
 | 
					 | 
				
			||||||
#include "video_core/engines/const_buffer_engine_interface.h"
 | 
					 | 
				
			||||||
#include "video_core/engines/shader_type.h"
 | 
					 | 
				
			||||||
#include "video_core/guest_driver.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace VideoCommon::Shader {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using KeyMap = std::unordered_map<std::pair<u32, u32>, u32, Common::PairHash>;
 | 
					 | 
				
			||||||
using BoundSamplerMap = std::unordered_map<u32, Tegra::Engines::SamplerDescriptor>;
 | 
					 | 
				
			||||||
using BindlessSamplerMap =
 | 
					 | 
				
			||||||
    std::unordered_map<std::pair<u32, u32>, Tegra::Engines::SamplerDescriptor, Common::PairHash>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * The ConstBufferLocker is a class use to interface the 3D and compute engines with the shader
 | 
					 | 
				
			||||||
 * compiler. with it, the shader can obtain required data from GPU state and store it for disk
 | 
					 | 
				
			||||||
 * shader compilation.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class ConstBufferLocker {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
    explicit ConstBufferLocker(Tegra::Engines::ShaderType shader_stage);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    explicit ConstBufferLocker(Tegra::Engines::ShaderType shader_stage,
 | 
					 | 
				
			||||||
                               Tegra::Engines::ConstBufferEngineInterface& engine);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ~ConstBufferLocker();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Retrieves a key from the locker, if it's registered, it will give the registered value, if
 | 
					 | 
				
			||||||
    /// not it will obtain it from maxwell3d and register it.
 | 
					 | 
				
			||||||
    std::optional<u32> ObtainKey(u32 buffer, u32 offset);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::optional<Tegra::Engines::SamplerDescriptor> ObtainBoundSampler(u32 offset);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::optional<u32> ObtainBoundBuffer();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Inserts a key.
 | 
					 | 
				
			||||||
    void InsertKey(u32 buffer, u32 offset, u32 value);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Inserts a bound sampler key.
 | 
					 | 
				
			||||||
    void InsertBoundSampler(u32 offset, Tegra::Engines::SamplerDescriptor sampler);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Inserts a bindless sampler key.
 | 
					 | 
				
			||||||
    void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Set the bound buffer for this locker.
 | 
					 | 
				
			||||||
    void SetBoundBuffer(u32 buffer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Checks keys and samplers against engine's current const buffers. Returns true if they are
 | 
					 | 
				
			||||||
    /// the same value, false otherwise;
 | 
					 | 
				
			||||||
    bool IsConsistent() const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Returns true if the keys are equal to the other ones in the locker.
 | 
					 | 
				
			||||||
    bool HasEqualKeys(const ConstBufferLocker& rhs) const;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Gives an getter to the const buffer keys in the database.
 | 
					 | 
				
			||||||
    const KeyMap& GetKeys() const {
 | 
					 | 
				
			||||||
        return keys;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Gets samplers database.
 | 
					 | 
				
			||||||
    const BoundSamplerMap& GetBoundSamplers() const {
 | 
					 | 
				
			||||||
        return bound_samplers;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Gets bindless samplers database.
 | 
					 | 
				
			||||||
    const BindlessSamplerMap& GetBindlessSamplers() const {
 | 
					 | 
				
			||||||
        return bindless_samplers;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Gets bound buffer used on this shader
 | 
					 | 
				
			||||||
    u32 GetBoundBuffer() const {
 | 
					 | 
				
			||||||
        return bound_buffer;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Obtains access to the guest driver's profile.
 | 
					 | 
				
			||||||
    VideoCore::GuestDriverProfile* AccessGuestDriverProfile() const {
 | 
					 | 
				
			||||||
        if (engine) {
 | 
					 | 
				
			||||||
            return &engine->AccessGuestDriverProfile();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return nullptr;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
    const Tegra::Engines::ShaderType stage;
 | 
					 | 
				
			||||||
    Tegra::Engines::ConstBufferEngineInterface* engine = nullptr;
 | 
					 | 
				
			||||||
    KeyMap keys;
 | 
					 | 
				
			||||||
    BoundSamplerMap bound_samplers;
 | 
					 | 
				
			||||||
    BindlessSamplerMap bindless_samplers;
 | 
					 | 
				
			||||||
    bool bound_buffer_saved{};
 | 
					 | 
				
			||||||
    u32 bound_buffer{};
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace VideoCommon::Shader
 | 
					 | 
				
			||||||
@ -13,6 +13,7 @@
 | 
				
			|||||||
#include "common/common_types.h"
 | 
					#include "common/common_types.h"
 | 
				
			||||||
#include "video_core/shader/ast.h"
 | 
					#include "video_core/shader/ast.h"
 | 
				
			||||||
#include "video_core/shader/control_flow.h"
 | 
					#include "video_core/shader/control_flow.h"
 | 
				
			||||||
 | 
					#include "video_core/shader/registry.h"
 | 
				
			||||||
#include "video_core/shader/shader_ir.h"
 | 
					#include "video_core/shader/shader_ir.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace VideoCommon::Shader {
 | 
					namespace VideoCommon::Shader {
 | 
				
			||||||
@ -64,11 +65,11 @@ struct BlockInfo {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct CFGRebuildState {
 | 
					struct CFGRebuildState {
 | 
				
			||||||
    explicit CFGRebuildState(const ProgramCode& program_code, u32 start, ConstBufferLocker& locker)
 | 
					    explicit CFGRebuildState(const ProgramCode& program_code, u32 start, Registry& registry)
 | 
				
			||||||
        : program_code{program_code}, locker{locker}, start{start} {}
 | 
					        : program_code{program_code}, registry{registry}, start{start} {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const ProgramCode& program_code;
 | 
					    const ProgramCode& program_code;
 | 
				
			||||||
    ConstBufferLocker& locker;
 | 
					    Registry& registry;
 | 
				
			||||||
    u32 start{};
 | 
					    u32 start{};
 | 
				
			||||||
    std::vector<BlockInfo> block_info;
 | 
					    std::vector<BlockInfo> block_info;
 | 
				
			||||||
    std::list<u32> inspect_queries;
 | 
					    std::list<u32> inspect_queries;
 | 
				
			||||||
@ -438,7 +439,7 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
 | 
				
			|||||||
            const s32 pc_target = offset + result.relative_position;
 | 
					            const s32 pc_target = offset + result.relative_position;
 | 
				
			||||||
            std::vector<CaseBranch> branches;
 | 
					            std::vector<CaseBranch> branches;
 | 
				
			||||||
            for (u32 i = 0; i < result.entries; i++) {
 | 
					            for (u32 i = 0; i < result.entries; i++) {
 | 
				
			||||||
                auto key = state.locker.ObtainKey(result.buffer, result.offset + i * 4);
 | 
					                auto key = state.registry.ObtainKey(result.buffer, result.offset + i * 4);
 | 
				
			||||||
                if (!key) {
 | 
					                if (!key) {
 | 
				
			||||||
                    return {ParseResult::AbnormalFlow, parse_info};
 | 
					                    return {ParseResult::AbnormalFlow, parse_info};
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -656,14 +657,14 @@ void DecompileShader(CFGRebuildState& state) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
 | 
					std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
 | 
				
			||||||
                                                const CompilerSettings& settings,
 | 
					                                                const CompilerSettings& settings,
 | 
				
			||||||
                                                ConstBufferLocker& locker) {
 | 
					                                                Registry& registry) {
 | 
				
			||||||
    auto result_out = std::make_unique<ShaderCharacteristics>();
 | 
					    auto result_out = std::make_unique<ShaderCharacteristics>();
 | 
				
			||||||
    if (settings.depth == CompileDepth::BruteForce) {
 | 
					    if (settings.depth == CompileDepth::BruteForce) {
 | 
				
			||||||
        result_out->settings.depth = CompileDepth::BruteForce;
 | 
					        result_out->settings.depth = CompileDepth::BruteForce;
 | 
				
			||||||
        return result_out;
 | 
					        return result_out;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    CFGRebuildState state{program_code, start_address, locker};
 | 
					    CFGRebuildState state{program_code, start_address, registry};
 | 
				
			||||||
    // Inspect Code and generate blocks
 | 
					    // Inspect Code and generate blocks
 | 
				
			||||||
    state.labels.clear();
 | 
					    state.labels.clear();
 | 
				
			||||||
    state.labels.emplace(start_address);
 | 
					    state.labels.emplace(start_address);
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@
 | 
				
			|||||||
#include "video_core/engines/shader_bytecode.h"
 | 
					#include "video_core/engines/shader_bytecode.h"
 | 
				
			||||||
#include "video_core/shader/ast.h"
 | 
					#include "video_core/shader/ast.h"
 | 
				
			||||||
#include "video_core/shader/compiler_settings.h"
 | 
					#include "video_core/shader/compiler_settings.h"
 | 
				
			||||||
 | 
					#include "video_core/shader/registry.h"
 | 
				
			||||||
#include "video_core/shader/shader_ir.h"
 | 
					#include "video_core/shader/shader_ir.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace VideoCommon::Shader {
 | 
					namespace VideoCommon::Shader {
 | 
				
			||||||
@ -111,6 +112,6 @@ struct ShaderCharacteristics {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
 | 
					std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address,
 | 
				
			||||||
                                                const CompilerSettings& settings,
 | 
					                                                const CompilerSettings& settings,
 | 
				
			||||||
                                                ConstBufferLocker& locker);
 | 
					                                                Registry& registry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace VideoCommon::Shader
 | 
					} // namespace VideoCommon::Shader
 | 
				
			||||||
 | 
				
			|||||||
@ -34,13 +34,9 @@ constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) {
 | 
				
			|||||||
    return (absolute_offset % SchedPeriod) == 0;
 | 
					    return (absolute_offset % SchedPeriod) == 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile* gpu_driver,
 | 
					void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile& gpu_driver,
 | 
				
			||||||
                              const std::list<Sampler>& used_samplers) {
 | 
					                              const std::list<Sampler>& used_samplers) {
 | 
				
			||||||
    if (gpu_driver == nullptr) {
 | 
					    if (gpu_driver.IsTextureHandlerSizeKnown() || used_samplers.size() <= 1) {
 | 
				
			||||||
        LOG_CRITICAL(HW_GPU, "GPU driver profile has not been created yet");
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (gpu_driver->TextureHandlerSizeKnown() || used_samplers.size() <= 1) {
 | 
					 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    u32 count{};
 | 
					    u32 count{};
 | 
				
			||||||
@ -53,17 +49,13 @@ void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile* gpu_driver,
 | 
				
			|||||||
        bound_offsets.emplace_back(sampler.GetOffset());
 | 
					        bound_offsets.emplace_back(sampler.GetOffset());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (count > 1) {
 | 
					    if (count > 1) {
 | 
				
			||||||
        gpu_driver->DeduceTextureHandlerSize(std::move(bound_offsets));
 | 
					        gpu_driver.DeduceTextureHandlerSize(std::move(bound_offsets));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::optional<u32> TryDeduceSamplerSize(const Sampler& sampler_to_deduce,
 | 
					std::optional<u32> TryDeduceSamplerSize(const Sampler& sampler_to_deduce,
 | 
				
			||||||
                                        VideoCore::GuestDriverProfile* gpu_driver,
 | 
					                                        VideoCore::GuestDriverProfile& gpu_driver,
 | 
				
			||||||
                                        const std::list<Sampler>& used_samplers) {
 | 
					                                        const std::list<Sampler>& used_samplers) {
 | 
				
			||||||
    if (gpu_driver == nullptr) {
 | 
					 | 
				
			||||||
        LOG_CRITICAL(HW_GPU, "GPU Driver profile has not been created yet");
 | 
					 | 
				
			||||||
        return std::nullopt;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const u32 base_offset = sampler_to_deduce.GetOffset();
 | 
					    const u32 base_offset = sampler_to_deduce.GetOffset();
 | 
				
			||||||
    u32 max_offset{std::numeric_limits<u32>::max()};
 | 
					    u32 max_offset{std::numeric_limits<u32>::max()};
 | 
				
			||||||
    for (const auto& sampler : used_samplers) {
 | 
					    for (const auto& sampler : used_samplers) {
 | 
				
			||||||
@ -77,7 +69,7 @@ std::optional<u32> TryDeduceSamplerSize(const Sampler& sampler_to_deduce,
 | 
				
			|||||||
    if (max_offset == std::numeric_limits<u32>::max()) {
 | 
					    if (max_offset == std::numeric_limits<u32>::max()) {
 | 
				
			||||||
        return std::nullopt;
 | 
					        return std::nullopt;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return ((max_offset - base_offset) * 4) / gpu_driver->GetTextureHandlerSize();
 | 
					    return ((max_offset - base_offset) * 4) / gpu_driver.GetTextureHandlerSize();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // Anonymous namespace
 | 
					} // Anonymous namespace
 | 
				
			||||||
@ -149,7 +141,7 @@ void ShaderIR::Decode() {
 | 
				
			|||||||
    std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
 | 
					    std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    decompiled = false;
 | 
					    decompiled = false;
 | 
				
			||||||
    auto info = ScanFlow(program_code, main_offset, settings, locker);
 | 
					    auto info = ScanFlow(program_code, main_offset, settings, registry);
 | 
				
			||||||
    auto& shader_info = *info;
 | 
					    auto& shader_info = *info;
 | 
				
			||||||
    coverage_begin = shader_info.start;
 | 
					    coverage_begin = shader_info.start;
 | 
				
			||||||
    coverage_end = shader_info.end;
 | 
					    coverage_end = shader_info.end;
 | 
				
			||||||
@ -364,7 +356,7 @@ u32 ShaderIR::DecodeInstr(NodeBlock& bb, u32 pc) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void ShaderIR::PostDecode() {
 | 
					void ShaderIR::PostDecode() {
 | 
				
			||||||
    // Deduce texture handler size if needed
 | 
					    // Deduce texture handler size if needed
 | 
				
			||||||
    auto gpu_driver = locker.AccessGuestDriverProfile();
 | 
					    auto gpu_driver = registry.AccessGuestDriverProfile();
 | 
				
			||||||
    DeduceTextureHandlerSize(gpu_driver, used_samplers);
 | 
					    DeduceTextureHandlerSize(gpu_driver, used_samplers);
 | 
				
			||||||
    // Deduce Indexed Samplers
 | 
					    // Deduce Indexed Samplers
 | 
				
			||||||
    if (!uses_indexed_samplers) {
 | 
					    if (!uses_indexed_samplers) {
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@
 | 
				
			|||||||
#include "common/logging/log.h"
 | 
					#include "common/logging/log.h"
 | 
				
			||||||
#include "video_core/engines/shader_bytecode.h"
 | 
					#include "video_core/engines/shader_bytecode.h"
 | 
				
			||||||
#include "video_core/shader/node_helper.h"
 | 
					#include "video_core/shader/node_helper.h"
 | 
				
			||||||
 | 
					#include "video_core/shader/registry.h"
 | 
				
			||||||
#include "video_core/shader/shader_ir.h"
 | 
					#include "video_core/shader/shader_ir.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace VideoCommon::Shader {
 | 
					namespace VideoCommon::Shader {
 | 
				
			||||||
@ -359,8 +360,8 @@ ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(std::optional<SamplerInfo> sample
 | 
				
			|||||||
    if (sampler_info) {
 | 
					    if (sampler_info) {
 | 
				
			||||||
        return *sampler_info;
 | 
					        return *sampler_info;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const auto sampler =
 | 
					    const auto sampler = buffer ? registry.ObtainBindlessSampler(*buffer, offset)
 | 
				
			||||||
        buffer ? locker.ObtainBindlessSampler(*buffer, offset) : locker.ObtainBoundSampler(offset);
 | 
					                                : registry.ObtainBoundSampler(offset);
 | 
				
			||||||
    if (!sampler) {
 | 
					    if (!sampler) {
 | 
				
			||||||
        LOG_WARNING(HW_GPU, "Unknown sampler info");
 | 
					        LOG_WARNING(HW_GPU, "Unknown sampler info");
 | 
				
			||||||
        return SamplerInfo{TextureType::Texture2D, false, false, false};
 | 
					        return SamplerInfo{TextureType::Texture2D, false, false, false};
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										161
									
								
								src/video_core/shader/registry.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								src/video_core/shader/registry.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,161 @@
 | 
				
			|||||||
 | 
					// Copyright 2019 yuzu Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					#include <tuple>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "common/assert.h"
 | 
				
			||||||
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					#include "video_core/engines/kepler_compute.h"
 | 
				
			||||||
 | 
					#include "video_core/engines/maxwell_3d.h"
 | 
				
			||||||
 | 
					#include "video_core/engines/shader_type.h"
 | 
				
			||||||
 | 
					#include "video_core/shader/registry.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace VideoCommon::Shader {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using Tegra::Engines::ConstBufferEngineInterface;
 | 
				
			||||||
 | 
					using Tegra::Engines::SamplerDescriptor;
 | 
				
			||||||
 | 
					using Tegra::Engines::ShaderType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GraphicsInfo MakeGraphicsInfo(ShaderType shader_stage, ConstBufferEngineInterface& engine) {
 | 
				
			||||||
 | 
					    if (shader_stage == ShaderType::Compute) {
 | 
				
			||||||
 | 
					        return {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    auto& graphics = static_cast<Tegra::Engines::Maxwell3D&>(engine);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    GraphicsInfo info;
 | 
				
			||||||
 | 
					    info.tfb_layouts = graphics.regs.tfb_layouts;
 | 
				
			||||||
 | 
					    info.tfb_varying_locs = graphics.regs.tfb_varying_locs;
 | 
				
			||||||
 | 
					    info.primitive_topology = graphics.regs.draw.topology;
 | 
				
			||||||
 | 
					    info.tessellation_primitive = graphics.regs.tess_mode.prim;
 | 
				
			||||||
 | 
					    info.tessellation_spacing = graphics.regs.tess_mode.spacing;
 | 
				
			||||||
 | 
					    info.tfb_enabled = graphics.regs.tfb_enabled;
 | 
				
			||||||
 | 
					    info.tessellation_clockwise = graphics.regs.tess_mode.cw;
 | 
				
			||||||
 | 
					    return info;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ComputeInfo MakeComputeInfo(ShaderType shader_stage, ConstBufferEngineInterface& engine) {
 | 
				
			||||||
 | 
					    if (shader_stage != ShaderType::Compute) {
 | 
				
			||||||
 | 
					        return {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    auto& compute = static_cast<Tegra::Engines::KeplerCompute&>(engine);
 | 
				
			||||||
 | 
					    const auto& launch = compute.launch_description;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ComputeInfo info;
 | 
				
			||||||
 | 
					    info.workgroup_size = {launch.block_dim_x, launch.block_dim_y, launch.block_dim_z};
 | 
				
			||||||
 | 
					    info.local_memory_size_in_words = launch.local_pos_alloc;
 | 
				
			||||||
 | 
					    info.shared_memory_size_in_words = launch.shared_alloc;
 | 
				
			||||||
 | 
					    return info;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // Anonymous namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Registry::Registry(Tegra::Engines::ShaderType shader_stage, const SerializedRegistryInfo& info)
 | 
				
			||||||
 | 
					    : stage{shader_stage}, stored_guest_driver_profile{info.guest_driver_profile},
 | 
				
			||||||
 | 
					      bound_buffer{info.bound_buffer}, graphics_info{info.graphics}, compute_info{info.compute} {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Registry::Registry(Tegra::Engines::ShaderType shader_stage,
 | 
				
			||||||
 | 
					                   Tegra::Engines::ConstBufferEngineInterface& engine)
 | 
				
			||||||
 | 
					    : stage{shader_stage}, engine{&engine}, bound_buffer{engine.GetBoundBuffer()},
 | 
				
			||||||
 | 
					      graphics_info{MakeGraphicsInfo(shader_stage, engine)}, compute_info{MakeComputeInfo(
 | 
				
			||||||
 | 
					                                                                 shader_stage, engine)} {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Registry::~Registry() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::optional<u32> Registry::ObtainKey(u32 buffer, u32 offset) {
 | 
				
			||||||
 | 
					    const std::pair<u32, u32> key = {buffer, offset};
 | 
				
			||||||
 | 
					    const auto iter = keys.find(key);
 | 
				
			||||||
 | 
					    if (iter != keys.end()) {
 | 
				
			||||||
 | 
					        return iter->second;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!engine) {
 | 
				
			||||||
 | 
					        return std::nullopt;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const u32 value = engine->AccessConstBuffer32(stage, buffer, offset);
 | 
				
			||||||
 | 
					    keys.emplace(key, value);
 | 
				
			||||||
 | 
					    return value;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::optional<SamplerDescriptor> Registry::ObtainBoundSampler(u32 offset) {
 | 
				
			||||||
 | 
					    const u32 key = offset;
 | 
				
			||||||
 | 
					    const auto iter = bound_samplers.find(key);
 | 
				
			||||||
 | 
					    if (iter != bound_samplers.end()) {
 | 
				
			||||||
 | 
					        return iter->second;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!engine) {
 | 
				
			||||||
 | 
					        return std::nullopt;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const SamplerDescriptor value = engine->AccessBoundSampler(stage, offset);
 | 
				
			||||||
 | 
					    bound_samplers.emplace(key, value);
 | 
				
			||||||
 | 
					    return value;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::optional<Tegra::Engines::SamplerDescriptor> Registry::ObtainBindlessSampler(u32 buffer,
 | 
				
			||||||
 | 
					                                                                                 u32 offset) {
 | 
				
			||||||
 | 
					    const std::pair key = {buffer, offset};
 | 
				
			||||||
 | 
					    const auto iter = bindless_samplers.find(key);
 | 
				
			||||||
 | 
					    if (iter != bindless_samplers.end()) {
 | 
				
			||||||
 | 
					        return iter->second;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!engine) {
 | 
				
			||||||
 | 
					        return std::nullopt;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const SamplerDescriptor value = engine->AccessBindlessSampler(stage, buffer, offset);
 | 
				
			||||||
 | 
					    bindless_samplers.emplace(key, value);
 | 
				
			||||||
 | 
					    return value;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Registry::InsertKey(u32 buffer, u32 offset, u32 value) {
 | 
				
			||||||
 | 
					    keys.insert_or_assign({buffer, offset}, value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Registry::InsertBoundSampler(u32 offset, SamplerDescriptor sampler) {
 | 
				
			||||||
 | 
					    bound_samplers.insert_or_assign(offset, sampler);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Registry::InsertBindlessSampler(u32 buffer, u32 offset, SamplerDescriptor sampler) {
 | 
				
			||||||
 | 
					    bindless_samplers.insert_or_assign({buffer, offset}, sampler);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Registry::IsConsistent() const {
 | 
				
			||||||
 | 
					    if (!engine) {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return std::all_of(keys.begin(), keys.end(),
 | 
				
			||||||
 | 
					                       [this](const auto& pair) {
 | 
				
			||||||
 | 
					                           const auto [cbuf, offset] = pair.first;
 | 
				
			||||||
 | 
					                           const auto value = pair.second;
 | 
				
			||||||
 | 
					                           return value == engine->AccessConstBuffer32(stage, cbuf, offset);
 | 
				
			||||||
 | 
					                       }) &&
 | 
				
			||||||
 | 
					           std::all_of(bound_samplers.begin(), bound_samplers.end(),
 | 
				
			||||||
 | 
					                       [this](const auto& sampler) {
 | 
				
			||||||
 | 
					                           const auto [key, value] = sampler;
 | 
				
			||||||
 | 
					                           return value == engine->AccessBoundSampler(stage, key);
 | 
				
			||||||
 | 
					                       }) &&
 | 
				
			||||||
 | 
					           std::all_of(bindless_samplers.begin(), bindless_samplers.end(),
 | 
				
			||||||
 | 
					                       [this](const auto& sampler) {
 | 
				
			||||||
 | 
					                           const auto [cbuf, offset] = sampler.first;
 | 
				
			||||||
 | 
					                           const auto value = sampler.second;
 | 
				
			||||||
 | 
					                           return value == engine->AccessBindlessSampler(stage, cbuf, offset);
 | 
				
			||||||
 | 
					                       });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Registry::HasEqualKeys(const Registry& rhs) const {
 | 
				
			||||||
 | 
					    return std::tie(keys, bound_samplers, bindless_samplers) ==
 | 
				
			||||||
 | 
					           std::tie(rhs.keys, rhs.bound_samplers, rhs.bindless_samplers);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const GraphicsInfo& Registry::GetGraphicsInfo() const {
 | 
				
			||||||
 | 
					    ASSERT(stage != Tegra::Engines::ShaderType::Compute);
 | 
				
			||||||
 | 
					    return graphics_info;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ComputeInfo& Registry::GetComputeInfo() const {
 | 
				
			||||||
 | 
					    ASSERT(stage == Tegra::Engines::ShaderType::Compute);
 | 
				
			||||||
 | 
					    return compute_info;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace VideoCommon::Shader
 | 
				
			||||||
							
								
								
									
										137
									
								
								src/video_core/shader/registry.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								src/video_core/shader/registry.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,137 @@
 | 
				
			|||||||
 | 
					// Copyright 2019 yuzu Emulator Project
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <array>
 | 
				
			||||||
 | 
					#include <optional>
 | 
				
			||||||
 | 
					#include <type_traits>
 | 
				
			||||||
 | 
					#include <unordered_map>
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					#include "common/hash.h"
 | 
				
			||||||
 | 
					#include "video_core/engines/const_buffer_engine_interface.h"
 | 
				
			||||||
 | 
					#include "video_core/engines/maxwell_3d.h"
 | 
				
			||||||
 | 
					#include "video_core/engines/shader_type.h"
 | 
				
			||||||
 | 
					#include "video_core/guest_driver.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace VideoCommon::Shader {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using KeyMap = std::unordered_map<std::pair<u32, u32>, u32, Common::PairHash>;
 | 
				
			||||||
 | 
					using BoundSamplerMap = std::unordered_map<u32, Tegra::Engines::SamplerDescriptor>;
 | 
				
			||||||
 | 
					using BindlessSamplerMap =
 | 
				
			||||||
 | 
					    std::unordered_map<std::pair<u32, u32>, Tegra::Engines::SamplerDescriptor, Common::PairHash>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct GraphicsInfo {
 | 
				
			||||||
 | 
					    using Maxwell = Tegra::Engines::Maxwell3D::Regs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::array<Maxwell::TransformFeedbackLayout, Maxwell::NumTransformFeedbackBuffers>
 | 
				
			||||||
 | 
					        tfb_layouts{};
 | 
				
			||||||
 | 
					    std::array<std::array<u8, 128>, Maxwell::NumTransformFeedbackBuffers> tfb_varying_locs{};
 | 
				
			||||||
 | 
					    Maxwell::PrimitiveTopology primitive_topology{};
 | 
				
			||||||
 | 
					    Maxwell::TessellationPrimitive tessellation_primitive{};
 | 
				
			||||||
 | 
					    Maxwell::TessellationSpacing tessellation_spacing{};
 | 
				
			||||||
 | 
					    bool tfb_enabled = false;
 | 
				
			||||||
 | 
					    bool tessellation_clockwise = false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(std::is_trivially_copyable_v<GraphicsInfo> &&
 | 
				
			||||||
 | 
					              std::is_standard_layout_v<GraphicsInfo>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ComputeInfo {
 | 
				
			||||||
 | 
					    std::array<u32, 3> workgroup_size{};
 | 
				
			||||||
 | 
					    u32 shared_memory_size_in_words = 0;
 | 
				
			||||||
 | 
					    u32 local_memory_size_in_words = 0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(std::is_trivially_copyable_v<ComputeInfo> && std::is_standard_layout_v<ComputeInfo>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SerializedRegistryInfo {
 | 
				
			||||||
 | 
					    VideoCore::GuestDriverProfile guest_driver_profile;
 | 
				
			||||||
 | 
					    u32 bound_buffer = 0;
 | 
				
			||||||
 | 
					    GraphicsInfo graphics;
 | 
				
			||||||
 | 
					    ComputeInfo compute;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The Registry is a class use to interface the 3D and compute engines with the shader compiler.
 | 
				
			||||||
 | 
					 * With it, the shader can obtain required data from GPU state and store it for disk shader
 | 
				
			||||||
 | 
					 * compilation.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class Registry {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    explicit Registry(Tegra::Engines::ShaderType shader_stage, const SerializedRegistryInfo& info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    explicit Registry(Tegra::Engines::ShaderType shader_stage,
 | 
				
			||||||
 | 
					                      Tegra::Engines::ConstBufferEngineInterface& engine);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ~Registry();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Retrieves a key from the registry, if it's registered, it will give the registered value, if
 | 
				
			||||||
 | 
					    /// not it will obtain it from maxwell3d and register it.
 | 
				
			||||||
 | 
					    std::optional<u32> ObtainKey(u32 buffer, u32 offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::optional<Tegra::Engines::SamplerDescriptor> ObtainBoundSampler(u32 offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Inserts a key.
 | 
				
			||||||
 | 
					    void InsertKey(u32 buffer, u32 offset, u32 value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Inserts a bound sampler key.
 | 
				
			||||||
 | 
					    void InsertBoundSampler(u32 offset, Tegra::Engines::SamplerDescriptor sampler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Inserts a bindless sampler key.
 | 
				
			||||||
 | 
					    void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Checks keys and samplers against engine's current const buffers.
 | 
				
			||||||
 | 
					    /// Returns true if they are the same value, false otherwise.
 | 
				
			||||||
 | 
					    bool IsConsistent() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns true if the keys are equal to the other ones in the registry.
 | 
				
			||||||
 | 
					    bool HasEqualKeys(const Registry& rhs) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns graphics information from this shader
 | 
				
			||||||
 | 
					    const GraphicsInfo& GetGraphicsInfo() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns compute information from this shader
 | 
				
			||||||
 | 
					    const ComputeInfo& GetComputeInfo() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gives an getter to the const buffer keys in the database.
 | 
				
			||||||
 | 
					    const KeyMap& GetKeys() const {
 | 
				
			||||||
 | 
					        return keys;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets samplers database.
 | 
				
			||||||
 | 
					    const BoundSamplerMap& GetBoundSamplers() const {
 | 
				
			||||||
 | 
					        return bound_samplers;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets bindless samplers database.
 | 
				
			||||||
 | 
					    const BindlessSamplerMap& GetBindlessSamplers() const {
 | 
				
			||||||
 | 
					        return bindless_samplers;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets bound buffer used on this shader
 | 
				
			||||||
 | 
					    u32 GetBoundBuffer() const {
 | 
				
			||||||
 | 
					        return bound_buffer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Obtains access to the guest driver's profile.
 | 
				
			||||||
 | 
					    VideoCore::GuestDriverProfile& AccessGuestDriverProfile() {
 | 
				
			||||||
 | 
					        return engine ? engine->AccessGuestDriverProfile() : stored_guest_driver_profile;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    const Tegra::Engines::ShaderType stage;
 | 
				
			||||||
 | 
					    VideoCore::GuestDriverProfile stored_guest_driver_profile;
 | 
				
			||||||
 | 
					    Tegra::Engines::ConstBufferEngineInterface* engine = nullptr;
 | 
				
			||||||
 | 
					    KeyMap keys;
 | 
				
			||||||
 | 
					    BoundSamplerMap bound_samplers;
 | 
				
			||||||
 | 
					    BindlessSamplerMap bindless_samplers;
 | 
				
			||||||
 | 
					    u32 bound_buffer;
 | 
				
			||||||
 | 
					    GraphicsInfo graphics_info;
 | 
				
			||||||
 | 
					    ComputeInfo compute_info;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace VideoCommon::Shader
 | 
				
			||||||
@ -11,6 +11,7 @@
 | 
				
			|||||||
#include "common/logging/log.h"
 | 
					#include "common/logging/log.h"
 | 
				
			||||||
#include "video_core/engines/shader_bytecode.h"
 | 
					#include "video_core/engines/shader_bytecode.h"
 | 
				
			||||||
#include "video_core/shader/node_helper.h"
 | 
					#include "video_core/shader/node_helper.h"
 | 
				
			||||||
 | 
					#include "video_core/shader/registry.h"
 | 
				
			||||||
#include "video_core/shader/shader_ir.h"
 | 
					#include "video_core/shader/shader_ir.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace VideoCommon::Shader {
 | 
					namespace VideoCommon::Shader {
 | 
				
			||||||
@ -24,8 +25,8 @@ using Tegra::Shader::PredOperation;
 | 
				
			|||||||
using Tegra::Shader::Register;
 | 
					using Tegra::Shader::Register;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, CompilerSettings settings,
 | 
					ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, CompilerSettings settings,
 | 
				
			||||||
                   ConstBufferLocker& locker)
 | 
					                   Registry& registry)
 | 
				
			||||||
    : program_code{program_code}, main_offset{main_offset}, settings{settings}, locker{locker} {
 | 
					    : program_code{program_code}, main_offset{main_offset}, settings{settings}, registry{registry} {
 | 
				
			||||||
    Decode();
 | 
					    Decode();
 | 
				
			||||||
    PostDecode();
 | 
					    PostDecode();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -18,8 +18,8 @@
 | 
				
			|||||||
#include "video_core/engines/shader_header.h"
 | 
					#include "video_core/engines/shader_header.h"
 | 
				
			||||||
#include "video_core/shader/ast.h"
 | 
					#include "video_core/shader/ast.h"
 | 
				
			||||||
#include "video_core/shader/compiler_settings.h"
 | 
					#include "video_core/shader/compiler_settings.h"
 | 
				
			||||||
#include "video_core/shader/const_buffer_locker.h"
 | 
					 | 
				
			||||||
#include "video_core/shader/node.h"
 | 
					#include "video_core/shader/node.h"
 | 
				
			||||||
 | 
					#include "video_core/shader/registry.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace VideoCommon::Shader {
 | 
					namespace VideoCommon::Shader {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -69,7 +69,7 @@ struct GlobalMemoryUsage {
 | 
				
			|||||||
class ShaderIR final {
 | 
					class ShaderIR final {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, CompilerSettings settings,
 | 
					    explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, CompilerSettings settings,
 | 
				
			||||||
                      ConstBufferLocker& locker);
 | 
					                      Registry& registry);
 | 
				
			||||||
    ~ShaderIR();
 | 
					    ~ShaderIR();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const std::map<u32, NodeBlock>& GetBasicBlocks() const {
 | 
					    const std::map<u32, NodeBlock>& GetBasicBlocks() const {
 | 
				
			||||||
@ -414,7 +414,7 @@ private:
 | 
				
			|||||||
    const ProgramCode& program_code;
 | 
					    const ProgramCode& program_code;
 | 
				
			||||||
    const u32 main_offset;
 | 
					    const u32 main_offset;
 | 
				
			||||||
    const CompilerSettings settings;
 | 
					    const CompilerSettings settings;
 | 
				
			||||||
    ConstBufferLocker& locker;
 | 
					    Registry& registry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool decompiled{};
 | 
					    bool decompiled{};
 | 
				
			||||||
    bool disable_flow_stack{};
 | 
					    bool disable_flow_stack{};
 | 
				
			||||||
 | 
				
			|||||||
@ -81,26 +81,20 @@ std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, cons
 | 
				
			|||||||
                MakeTrackSampler<BindlessSamplerNode>(cbuf->GetIndex(), immediate->GetValue());
 | 
					                MakeTrackSampler<BindlessSamplerNode>(cbuf->GetIndex(), immediate->GetValue());
 | 
				
			||||||
            return {tracked, track};
 | 
					            return {tracked, track};
 | 
				
			||||||
        } else if (const auto operation = std::get_if<OperationNode>(&*offset)) {
 | 
					        } else if (const auto operation = std::get_if<OperationNode>(&*offset)) {
 | 
				
			||||||
            auto bound_buffer = locker.ObtainBoundBuffer();
 | 
					            const u32 bound_buffer = registry.GetBoundBuffer();
 | 
				
			||||||
            if (!bound_buffer) {
 | 
					            if (bound_buffer != cbuf->GetIndex()) {
 | 
				
			||||||
                return {};
 | 
					                return {};
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (*bound_buffer != cbuf->GetIndex()) {
 | 
					            const auto pair = DecoupleIndirectRead(*operation);
 | 
				
			||||||
                return {};
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            auto pair = DecoupleIndirectRead(*operation);
 | 
					 | 
				
			||||||
            if (!pair) {
 | 
					            if (!pair) {
 | 
				
			||||||
                return {};
 | 
					                return {};
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            auto [gpr, base_offset] = *pair;
 | 
					            auto [gpr, base_offset] = *pair;
 | 
				
			||||||
            const auto offset_inm = std::get_if<ImmediateNode>(&*base_offset);
 | 
					            const auto offset_inm = std::get_if<ImmediateNode>(&*base_offset);
 | 
				
			||||||
            auto gpu_driver = locker.AccessGuestDriverProfile();
 | 
					            const auto& gpu_driver = registry.AccessGuestDriverProfile();
 | 
				
			||||||
            if (gpu_driver == nullptr) {
 | 
					 | 
				
			||||||
                return {};
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            const u32 bindless_cv = NewCustomVariable();
 | 
					            const u32 bindless_cv = NewCustomVariable();
 | 
				
			||||||
            const Node op = Operation(OperationCode::UDiv, NO_PRECISE, gpr,
 | 
					            const Node op =
 | 
				
			||||||
                                      Immediate(gpu_driver->GetTextureHandlerSize()));
 | 
					                Operation(OperationCode::UDiv, gpr, Immediate(gpu_driver.GetTextureHandlerSize()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const Node cv_node = GetCustomVariable(bindless_cv);
 | 
					            const Node cv_node = GetCustomVariable(bindless_cv);
 | 
				
			||||||
            Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op));
 | 
					            Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op));
 | 
				
			||||||
 | 
				
			|||||||
@ -34,18 +34,6 @@ constexpr char PROGRESSBAR_STYLE_PREPARE[] = R"(
 | 
				
			|||||||
QProgressBar {}
 | 
					QProgressBar {}
 | 
				
			||||||
QProgressBar::chunk {})";
 | 
					QProgressBar::chunk {})";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
constexpr char PROGRESSBAR_STYLE_DECOMPILE[] = R"(
 | 
					 | 
				
			||||||
QProgressBar {
 | 
					 | 
				
			||||||
  background-color: black;
 | 
					 | 
				
			||||||
  border: 2px solid white;
 | 
					 | 
				
			||||||
  border-radius: 4px;
 | 
					 | 
				
			||||||
  padding: 2px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
QProgressBar::chunk {
 | 
					 | 
				
			||||||
  background-color: #0ab9e6;
 | 
					 | 
				
			||||||
  width: 1px;
 | 
					 | 
				
			||||||
})";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
constexpr char PROGRESSBAR_STYLE_BUILD[] = R"(
 | 
					constexpr char PROGRESSBAR_STYLE_BUILD[] = R"(
 | 
				
			||||||
QProgressBar {
 | 
					QProgressBar {
 | 
				
			||||||
  background-color: black;
 | 
					  background-color: black;
 | 
				
			||||||
@ -100,13 +88,11 @@ LoadingScreen::LoadingScreen(QWidget* parent)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    stage_translations = {
 | 
					    stage_translations = {
 | 
				
			||||||
        {VideoCore::LoadCallbackStage::Prepare, tr("Loading...")},
 | 
					        {VideoCore::LoadCallbackStage::Prepare, tr("Loading...")},
 | 
				
			||||||
        {VideoCore::LoadCallbackStage::Decompile, tr("Preparing Shaders %1 / %2")},
 | 
					 | 
				
			||||||
        {VideoCore::LoadCallbackStage::Build, tr("Loading Shaders %1 / %2")},
 | 
					        {VideoCore::LoadCallbackStage::Build, tr("Loading Shaders %1 / %2")},
 | 
				
			||||||
        {VideoCore::LoadCallbackStage::Complete, tr("Launching...")},
 | 
					        {VideoCore::LoadCallbackStage::Complete, tr("Launching...")},
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    progressbar_style = {
 | 
					    progressbar_style = {
 | 
				
			||||||
        {VideoCore::LoadCallbackStage::Prepare, PROGRESSBAR_STYLE_PREPARE},
 | 
					        {VideoCore::LoadCallbackStage::Prepare, PROGRESSBAR_STYLE_PREPARE},
 | 
				
			||||||
        {VideoCore::LoadCallbackStage::Decompile, PROGRESSBAR_STYLE_DECOMPILE},
 | 
					 | 
				
			||||||
        {VideoCore::LoadCallbackStage::Build, PROGRESSBAR_STYLE_BUILD},
 | 
					        {VideoCore::LoadCallbackStage::Build, PROGRESSBAR_STYLE_BUILD},
 | 
				
			||||||
        {VideoCore::LoadCallbackStage::Complete, PROGRESSBAR_STYLE_COMPLETE},
 | 
					        {VideoCore::LoadCallbackStage::Complete, PROGRESSBAR_STYLE_COMPLETE},
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@ -192,8 +178,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // update labels and progress bar
 | 
					    // update labels and progress bar
 | 
				
			||||||
    if (stage == VideoCore::LoadCallbackStage::Decompile ||
 | 
					    if (stage == VideoCore::LoadCallbackStage::Build) {
 | 
				
			||||||
        stage == VideoCore::LoadCallbackStage::Build) {
 | 
					 | 
				
			||||||
        ui->stage->setText(stage_translations[stage].arg(value).arg(total));
 | 
					        ui->stage->setText(stage_translations[stage].arg(value).arg(total));
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        ui->stage->setText(stage_translations[stage]);
 | 
					        ui->stage->setText(stage_translations[stage]);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user