mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	renderer_vulkan: Add shader generators (#6630)
This commit is contained in:
		
							parent
							
								
									7edc86a9bc
								
							
						
					
					
						commit
						2e479fcec5
					
				
					 7 changed files with 4238 additions and 1 deletions
				
			
		|  | @ -104,6 +104,12 @@ add_library(video_core STATIC | ||||||
|     renderer_vulkan/vk_instance.h |     renderer_vulkan/vk_instance.h | ||||||
|     renderer_vulkan/vk_platform.cpp |     renderer_vulkan/vk_platform.cpp | ||||||
|     renderer_vulkan/vk_platform.h |     renderer_vulkan/vk_platform.h | ||||||
|  |     renderer_vulkan/vk_shader_gen.cpp | ||||||
|  |     renderer_vulkan/vk_shader_gen.h | ||||||
|  |     renderer_vulkan/vk_shader_gen_spv.cpp | ||||||
|  |     renderer_vulkan/vk_shader_gen_spv.h | ||||||
|  |     renderer_vulkan/vk_shader_util.cpp | ||||||
|  |     renderer_vulkan/vk_shader_util.h | ||||||
|     shader/debug_data.h |     shader/debug_data.h | ||||||
|     shader/shader.cpp |     shader/shader.cpp | ||||||
|     shader/shader.h |     shader/shader.h | ||||||
|  | @ -133,7 +139,7 @@ create_target_directory_groups(video_core) | ||||||
| 
 | 
 | ||||||
| target_link_libraries(video_core PUBLIC citra_common citra_core) | target_link_libraries(video_core PUBLIC citra_common citra_core) | ||||||
| target_link_libraries(video_core PRIVATE Boost::serialization dds-ktx json-headers nihstro-headers tsl::robin_map) | target_link_libraries(video_core PRIVATE Boost::serialization dds-ktx json-headers nihstro-headers tsl::robin_map) | ||||||
| target_link_libraries(video_core PRIVATE vulkan-headers vma glad) | target_link_libraries(video_core PRIVATE vulkan-headers vma glad sirit SPIRV glslang) | ||||||
| set_target_properties(video_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO}) | set_target_properties(video_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO}) | ||||||
| 
 | 
 | ||||||
| if ("x86_64" IN_LIST ARCHITECTURE) | if ("x86_64" IN_LIST ARCHITECTURE) | ||||||
|  |  | ||||||
							
								
								
									
										1880
									
								
								src/video_core/renderer_vulkan/vk_shader_gen.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1880
									
								
								src/video_core/renderer_vulkan/vk_shader_gen.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										274
									
								
								src/video_core/renderer_vulkan/vk_shader_gen.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								src/video_core/renderer_vulkan/vk_shader_gen.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,274 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <optional> | ||||||
|  | #include "common/hash.h" | ||||||
|  | #include "video_core/regs.h" | ||||||
|  | #include "video_core/shader/shader.h" | ||||||
|  | 
 | ||||||
|  | namespace Vulkan { | ||||||
|  | 
 | ||||||
|  | class Instance; | ||||||
|  | 
 | ||||||
|  | enum Attributes { | ||||||
|  |     ATTRIBUTE_POSITION, | ||||||
|  |     ATTRIBUTE_COLOR, | ||||||
|  |     ATTRIBUTE_TEXCOORD0, | ||||||
|  |     ATTRIBUTE_TEXCOORD1, | ||||||
|  |     ATTRIBUTE_TEXCOORD2, | ||||||
|  |     ATTRIBUTE_TEXCOORD0_W, | ||||||
|  |     ATTRIBUTE_NORMQUAT, | ||||||
|  |     ATTRIBUTE_VIEW, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Doesn't include const_color because we don't sync it, see comment in BuildFromRegs()
 | ||||||
|  | struct TevStageConfigRaw { | ||||||
|  |     u32 sources_raw; | ||||||
|  |     u32 modifiers_raw; | ||||||
|  |     u32 ops_raw; | ||||||
|  |     u32 scales_raw; | ||||||
|  |     explicit operator Pica::TexturingRegs::TevStageConfig() const noexcept { | ||||||
|  |         Pica::TexturingRegs::TevStageConfig stage; | ||||||
|  |         stage.sources_raw = sources_raw; | ||||||
|  |         stage.modifiers_raw = modifiers_raw; | ||||||
|  |         stage.ops_raw = ops_raw; | ||||||
|  |         stage.const_color = 0; | ||||||
|  |         stage.scales_raw = scales_raw; | ||||||
|  |         return stage; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct PicaFSConfigState { | ||||||
|  |     union { | ||||||
|  |         BitField<0, 3, Pica::FramebufferRegs::CompareFunc> alpha_test_func; | ||||||
|  |         BitField<3, 2, Pica::RasterizerRegs::ScissorMode> scissor_test_mode; | ||||||
|  |         BitField<5, 3, Pica::TexturingRegs::TextureConfig::TextureType> texture0_type; | ||||||
|  |         BitField<8, 1, u32> texture2_use_coord1; | ||||||
|  |         BitField<9, 8, u32> combiner_buffer_input; | ||||||
|  |         BitField<17, 1, Pica::RasterizerRegs::DepthBuffering> depthmap_enable; | ||||||
|  |         BitField<18, 3, Pica::TexturingRegs::FogMode> fog_mode; | ||||||
|  |         BitField<21, 1, u32> fog_flip; | ||||||
|  |         BitField<22, 1, u32> emulate_logic_op; | ||||||
|  |         BitField<23, 4, Pica::FramebufferRegs::LogicOp> logic_op; | ||||||
|  |         BitField<27, 1, u32> shadow_rendering; | ||||||
|  |         BitField<28, 1, u32> shadow_texture_orthographic; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     std::array<TevStageConfigRaw, 6> tev_stages; | ||||||
|  | 
 | ||||||
|  |     struct { | ||||||
|  |         union { | ||||||
|  |             BitField<0, 3, u16> num; | ||||||
|  |             BitField<3, 1, u16> directional; | ||||||
|  |             BitField<4, 1, u16> two_sided_diffuse; | ||||||
|  |             BitField<5, 1, u16> dist_atten_enable; | ||||||
|  |             BitField<6, 1, u16> spot_atten_enable; | ||||||
|  |             BitField<7, 1, u16> geometric_factor_0; | ||||||
|  |             BitField<8, 1, u16> geometric_factor_1; | ||||||
|  |             BitField<9, 1, u16> shadow_enable; | ||||||
|  |         } light[8]; | ||||||
|  | 
 | ||||||
|  |         union { | ||||||
|  |             BitField<0, 1, u32> enable; | ||||||
|  |             BitField<1, 4, u32> src_num; | ||||||
|  |             BitField<5, 2, Pica::LightingRegs::LightingBumpMode> bump_mode; | ||||||
|  |             BitField<7, 2, u32> bump_selector; | ||||||
|  |             BitField<9, 1, u32> bump_renorm; | ||||||
|  |             BitField<10, 1, u32> clamp_highlights; | ||||||
|  |             BitField<11, 4, Pica::LightingRegs::LightingConfig> config; | ||||||
|  |             BitField<15, 1, u32> enable_primary_alpha; | ||||||
|  |             BitField<16, 1, u32> enable_secondary_alpha; | ||||||
|  |             BitField<17, 1, u32> enable_shadow; | ||||||
|  |             BitField<18, 1, u32> shadow_primary; | ||||||
|  |             BitField<19, 1, u32> shadow_secondary; | ||||||
|  |             BitField<20, 1, u32> shadow_invert; | ||||||
|  |             BitField<21, 1, u32> shadow_alpha; | ||||||
|  |             BitField<22, 2, u32> shadow_selector; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         struct { | ||||||
|  |             union { | ||||||
|  |                 BitField<0, 1, u32> enable; | ||||||
|  |                 BitField<1, 1, u32> abs_input; | ||||||
|  |                 BitField<2, 3, Pica::LightingRegs::LightingLutInput> type; | ||||||
|  |             }; | ||||||
|  |             float scale; | ||||||
|  |         } lut_d0, lut_d1, lut_sp, lut_fr, lut_rr, lut_rg, lut_rb; | ||||||
|  |     } lighting; | ||||||
|  | 
 | ||||||
|  |     struct { | ||||||
|  |         union { | ||||||
|  |             BitField<0, 1, u32> enable; | ||||||
|  |             BitField<1, 2, u32> coord; | ||||||
|  |             BitField<3, 3, Pica::TexturingRegs::ProcTexClamp> u_clamp; | ||||||
|  |             BitField<6, 3, Pica::TexturingRegs::ProcTexClamp> v_clamp; | ||||||
|  |             BitField<9, 4, Pica::TexturingRegs::ProcTexCombiner> color_combiner; | ||||||
|  |             BitField<13, 4, Pica::TexturingRegs::ProcTexCombiner> alpha_combiner; | ||||||
|  |             BitField<17, 3, Pica::TexturingRegs::ProcTexFilter> lut_filter; | ||||||
|  |             BitField<20, 1, u32> separate_alpha; | ||||||
|  |             BitField<21, 1, u32> noise_enable; | ||||||
|  |             BitField<22, 2, Pica::TexturingRegs::ProcTexShift> u_shift; | ||||||
|  |             BitField<24, 2, Pica::TexturingRegs::ProcTexShift> v_shift; | ||||||
|  |         }; | ||||||
|  |         s32 lut_width; | ||||||
|  |         s32 lut_offset0; | ||||||
|  |         s32 lut_offset1; | ||||||
|  |         s32 lut_offset2; | ||||||
|  |         s32 lut_offset3; | ||||||
|  |         u8 lod_min; | ||||||
|  |         u8 lod_max; | ||||||
|  |     } proctex; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * This struct contains all state used to generate the GLSL fragment shader that emulates the | ||||||
|  |  * current Pica register configuration. This struct is used as a cache key for generated GLSL shader | ||||||
|  |  * programs. The functions in gl_shader_gen.cpp should retrieve state from this struct only, not by | ||||||
|  |  * directly accessing Pica registers. This should reduce the risk of bugs in shader generation where | ||||||
|  |  * Pica state is not being captured in the shader cache key, thereby resulting in (what should be) | ||||||
|  |  * two separate shaders sharing the same key. | ||||||
|  |  */ | ||||||
|  | struct PicaFSConfig : Common::HashableStruct<PicaFSConfigState> { | ||||||
|  |     PicaFSConfig(const Pica::Regs& regs, const Instance& instance); | ||||||
|  | 
 | ||||||
|  |     bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { | ||||||
|  |         return (stage_index < 4) && (state.combiner_buffer_input & (1 << stage_index)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { | ||||||
|  |         return (stage_index < 4) && ((state.combiner_buffer_input >> 4) & (1 << stage_index)); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class AttribLoadFlags { | ||||||
|  |     Float = 1 << 0, | ||||||
|  |     Sint = 1 << 1, | ||||||
|  |     Uint = 1 << 2, | ||||||
|  |     ZeroW = 1 << 3, | ||||||
|  | }; | ||||||
|  | DECLARE_ENUM_FLAG_OPERATORS(AttribLoadFlags) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * This struct contains common information to identify a GL vertex/geometry shader generated from | ||||||
|  |  * PICA vertex/geometry shader. | ||||||
|  |  */ | ||||||
|  | struct PicaShaderConfigCommon { | ||||||
|  |     void Init(const Pica::RasterizerRegs& rasterizer, const Pica::ShaderRegs& regs, | ||||||
|  |               Pica::Shader::ShaderSetup& setup); | ||||||
|  | 
 | ||||||
|  |     u64 program_hash; | ||||||
|  |     u64 swizzle_hash; | ||||||
|  |     u32 main_offset; | ||||||
|  |     bool sanitize_mul; | ||||||
|  | 
 | ||||||
|  |     u32 num_outputs; | ||||||
|  |     // Load operations to apply to the input vertex data
 | ||||||
|  |     std::array<AttribLoadFlags, 16> load_flags; | ||||||
|  | 
 | ||||||
|  |     // output_map[output register index] -> output attribute index
 | ||||||
|  |     std::array<u32, 16> output_map; | ||||||
|  | 
 | ||||||
|  |     bool use_geometry_shader; | ||||||
|  |     u32 vs_output_attributes; | ||||||
|  |     u32 gs_output_attributes; | ||||||
|  | 
 | ||||||
|  |     struct SemanticMap { | ||||||
|  |         u32 attribute_index; | ||||||
|  |         u32 component_index; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // semantic_maps[semantic name] -> GS output attribute index + component index
 | ||||||
|  |     std::array<SemanticMap, 24> semantic_maps; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * This struct contains information to identify a GL vertex shader generated from PICA vertex | ||||||
|  |  * shader. | ||||||
|  |  */ | ||||||
|  | struct PicaVSConfig : Common::HashableStruct<PicaShaderConfigCommon> { | ||||||
|  |     explicit PicaVSConfig(const Pica::RasterizerRegs& rasterizer, const Pica::ShaderRegs& regs, | ||||||
|  |                           Pica::Shader::ShaderSetup& setup, const Instance& instance); | ||||||
|  |     bool use_clip_planes; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct PicaGSConfigCommonRaw { | ||||||
|  |     void Init(const Pica::Regs& regs); | ||||||
|  | 
 | ||||||
|  |     u32 vs_output_attributes; | ||||||
|  |     u32 gs_output_attributes; | ||||||
|  | 
 | ||||||
|  |     struct SemanticMap { | ||||||
|  |         u32 attribute_index; | ||||||
|  |         u32 component_index; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // semantic_maps[semantic name] -> GS output attribute index + component index
 | ||||||
|  |     std::array<SemanticMap, 24> semantic_maps; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * This struct contains information to identify a GL geometry shader generated from PICA no-geometry | ||||||
|  |  * shader pipeline | ||||||
|  |  */ | ||||||
|  | struct PicaFixedGSConfig : Common::HashableStruct<PicaGSConfigCommonRaw> { | ||||||
|  |     explicit PicaFixedGSConfig(const Pica::Regs& regs, const Instance& instance); | ||||||
|  |     bool use_clip_planes; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generates the GLSL vertex shader program source code that accepts vertices from software shader | ||||||
|  |  * and directly passes them to the fragment shader. | ||||||
|  |  * @param separable_shader generates shader that can be used for separate shader object | ||||||
|  |  * @returns String of the shader source code | ||||||
|  |  */ | ||||||
|  | std::string GenerateTrivialVertexShader(bool use_clip_planes); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generates the GLSL vertex shader program source code for the given VS program | ||||||
|  |  * @returns String of the shader source code; boost::none on failure | ||||||
|  |  */ | ||||||
|  | std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, | ||||||
|  |                                                 const PicaVSConfig& config); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generates the GLSL fixed geometry shader program source code for non-GS PICA pipeline | ||||||
|  |  * @returns String of the shader source code | ||||||
|  |  */ | ||||||
|  | std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generates the GLSL fragment shader program source code for the current Pica state | ||||||
|  |  * @param config ShaderCacheKey object generated for the current Pica state, used for the shader | ||||||
|  |  *               configuration (NOTE: Use state in this struct only, not the Pica registers!) | ||||||
|  |  * @param separable_shader generates shader that can be used for separate shader object | ||||||
|  |  * @returns String of the shader source code | ||||||
|  |  */ | ||||||
|  | std::string GenerateFragmentShader(const PicaFSConfig& config); | ||||||
|  | 
 | ||||||
|  | } // namespace Vulkan
 | ||||||
|  | 
 | ||||||
|  | namespace std { | ||||||
|  | template <> | ||||||
|  | struct hash<Vulkan::PicaFSConfig> { | ||||||
|  |     std::size_t operator()(const Vulkan::PicaFSConfig& k) const noexcept { | ||||||
|  |         return k.Hash(); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | struct hash<Vulkan::PicaVSConfig> { | ||||||
|  |     std::size_t operator()(const Vulkan::PicaVSConfig& k) const noexcept { | ||||||
|  |         return k.Hash(); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | struct hash<Vulkan::PicaFixedGSConfig> { | ||||||
|  |     std::size_t operator()(const Vulkan::PicaFixedGSConfig& k) const noexcept { | ||||||
|  |         return k.Hash(); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | } // namespace std
 | ||||||
							
								
								
									
										1516
									
								
								src/video_core/renderer_vulkan/vk_shader_gen_spv.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1516
									
								
								src/video_core/renderer_vulkan/vk_shader_gen_spv.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										300
									
								
								src/video_core/renderer_vulkan/vk_shader_gen_spv.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								src/video_core/renderer_vulkan/vk_shader_gen_spv.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,300 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include <sirit/sirit.h> | ||||||
|  | 
 | ||||||
|  | #include "video_core/renderer_vulkan/vk_shader_gen.h" | ||||||
|  | 
 | ||||||
|  | namespace Core { | ||||||
|  | class TelemetrySession; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace Vulkan { | ||||||
|  | 
 | ||||||
|  | using Sirit::Id; | ||||||
|  | 
 | ||||||
|  | struct VectorIds { | ||||||
|  |     /// Returns the type id of the vector with the provided size
 | ||||||
|  |     [[nodiscard]] constexpr Id Get(u32 size) const { | ||||||
|  |         return ids[size - 2]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::array<Id, 3> ids; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class FragmentModule : public Sirit::Module { | ||||||
|  |     static constexpr u32 NUM_TEV_STAGES = 6; | ||||||
|  |     static constexpr u32 NUM_LIGHTS = 8; | ||||||
|  |     static constexpr u32 NUM_LIGHTING_SAMPLERS = 24; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     explicit FragmentModule(Core::TelemetrySession& telemetry, const PicaFSConfig& config); | ||||||
|  |     ~FragmentModule(); | ||||||
|  | 
 | ||||||
|  |     /// Emits SPIR-V bytecode corresponding to the provided pica fragment configuration
 | ||||||
|  |     void Generate(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     /// Undos the vulkan perspective transformation and applies the PICA one
 | ||||||
|  |     void WriteDepth(); | ||||||
|  | 
 | ||||||
|  |     /// Emits code to emulate the scissor rectangle
 | ||||||
|  |     void WriteScissor(); | ||||||
|  | 
 | ||||||
|  |     /// Writes the code to emulate fragment lighting
 | ||||||
|  |     void WriteLighting(); | ||||||
|  | 
 | ||||||
|  |     /// Writes the code to emulate fog
 | ||||||
|  |     void WriteFog(); | ||||||
|  | 
 | ||||||
|  |     /// Writes the code to emulate gas rendering
 | ||||||
|  |     void WriteGas(); | ||||||
|  | 
 | ||||||
|  |     /// Writes the code to emulate the specified TEV stage
 | ||||||
|  |     void WriteTevStage(s32 index); | ||||||
|  | 
 | ||||||
|  |     /// Defines the tex3 proctex sampling function
 | ||||||
|  |     void DefineProcTexSampler(); | ||||||
|  | 
 | ||||||
|  |     /// Writes the if-statement condition used to evaluate alpha testing.
 | ||||||
|  |     void WriteAlphaTestCondition(Pica::FramebufferRegs::CompareFunc func); | ||||||
|  | 
 | ||||||
|  |     /// Samples the current fragment texel from the provided texture unit
 | ||||||
|  |     [[nodiscard]] Id SampleTexture(u32 texture_unit); | ||||||
|  | 
 | ||||||
|  |     /// Samples the current fragment texel from shadow plane
 | ||||||
|  |     [[nodiscard]] Id SampleShadow(); | ||||||
|  | 
 | ||||||
|  |     [[nodiscard]] Id AppendProcTexShiftOffset(Id v, Pica::TexturingRegs::ProcTexShift mode, | ||||||
|  |                                               Pica::TexturingRegs::ProcTexClamp clamp_mode); | ||||||
|  | 
 | ||||||
|  |     [[nodiscard]] Id AppendProcTexClamp(Id var, Pica::TexturingRegs::ProcTexClamp mode); | ||||||
|  | 
 | ||||||
|  |     [[nodiscard]] Id AppendProcTexCombineAndMap(Pica::TexturingRegs::ProcTexCombiner combiner, Id u, | ||||||
|  |                                                 Id v, Id offset); | ||||||
|  | 
 | ||||||
|  |     /// Rounds the provided variable to the nearest 1/255th
 | ||||||
|  |     [[nodiscard]] Id Byteround(Id variable_id, u32 size = 1); | ||||||
|  | 
 | ||||||
|  |     /// LUT sampling uitlity
 | ||||||
|  |     /// For NoiseLUT/ColorMap/AlphaMap, coord=0.0 is lut[0], coord=127.0/128.0 is lut[127] and
 | ||||||
|  |     /// coord=1.0 is lut[127]+lut_diff[127]. For other indices, the result is interpolated using
 | ||||||
|  |     /// value entries and difference entries.
 | ||||||
|  |     [[nodiscard]] Id ProcTexLookupLUT(Id offset, Id coord); | ||||||
|  | 
 | ||||||
|  |     /// Generates random noise with proctex
 | ||||||
|  |     [[nodiscard]] Id ProcTexNoiseCoef(Id x); | ||||||
|  | 
 | ||||||
|  |     /// Samples a color value from the rgba texture lut
 | ||||||
|  |     [[nodiscard]] Id SampleProcTexColor(Id lut_coord, Id level); | ||||||
|  | 
 | ||||||
|  |     /// Lookups the lighting LUT at the provided lut_index
 | ||||||
|  |     [[nodiscard]] Id LookupLightingLUT(Id lut_index, Id index, Id delta); | ||||||
|  | 
 | ||||||
|  |     /// Writes the specified TEV stage source component(s)
 | ||||||
|  |     [[nodiscard]] Id AppendSource(Pica::TexturingRegs::TevStageConfig::Source source, s32 index); | ||||||
|  | 
 | ||||||
|  |     /// Writes the color components to use for the specified TEV stage color modifier
 | ||||||
|  |     [[nodiscard]] Id AppendColorModifier( | ||||||
|  |         Pica::TexturingRegs::TevStageConfig::ColorModifier modifier, | ||||||
|  |         Pica::TexturingRegs::TevStageConfig::Source source, s32 index); | ||||||
|  | 
 | ||||||
|  |     /// Writes the alpha component to use for the specified TEV stage alpha modifier
 | ||||||
|  |     [[nodiscard]] Id AppendAlphaModifier( | ||||||
|  |         Pica::TexturingRegs::TevStageConfig::AlphaModifier modifier, | ||||||
|  |         Pica::TexturingRegs::TevStageConfig::Source source, s32 index); | ||||||
|  | 
 | ||||||
|  |     /// Writes the combiner function for the color components for the specified TEV stage operation
 | ||||||
|  |     [[nodiscard]] Id AppendColorCombiner(Pica::TexturingRegs::TevStageConfig::Operation operation); | ||||||
|  | 
 | ||||||
|  |     /// Writes the combiner function for the alpha component for the specified TEV stage operation
 | ||||||
|  |     [[nodiscard]] Id AppendAlphaCombiner(Pica::TexturingRegs::TevStageConfig::Operation operation); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     /// Creates a constant array of integers
 | ||||||
|  |     template <typename... T> | ||||||
|  |     void InitTableS32(Id table, T... elems) { | ||||||
|  |         const Id table_const{ConstS32(elems...)}; | ||||||
|  |         OpStore(table, table_const); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /// Loads the member specified from the shader_data uniform struct
 | ||||||
|  |     template <typename... Ids> | ||||||
|  |     [[nodiscard]] Id GetShaderDataMember(Id type, Ids... ids) { | ||||||
|  |         const Id uniform_ptr{TypePointer(spv::StorageClass::Uniform, type)}; | ||||||
|  |         return OpLoad(type, OpAccessChain(uniform_ptr, shader_data_id, ids...)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Pads the provided vector by inserting args at the end
 | ||||||
|  |     template <typename... Args> | ||||||
|  |     [[nodiscard]] Id PadVectorF32(Id vector, Id pad_type_id, Args&&... args) { | ||||||
|  |         return OpCompositeConstruct(pad_type_id, vector, ConstF32(args...)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Defines a input variable
 | ||||||
|  |     [[nodiscard]] Id DefineInput(Id type, u32 location) { | ||||||
|  |         const Id input_id{DefineVar(type, spv::StorageClass::Input)}; | ||||||
|  |         Decorate(input_id, spv::Decoration::Location, location); | ||||||
|  |         return input_id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Defines a input variable
 | ||||||
|  |     [[nodiscard]] Id DefineOutput(Id type, u32 location) { | ||||||
|  |         const Id output_id{DefineVar(type, spv::StorageClass::Output)}; | ||||||
|  |         Decorate(output_id, spv::Decoration::Location, location); | ||||||
|  |         return output_id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Defines a uniform constant variable
 | ||||||
|  |     [[nodiscard]] Id DefineUniformConst(Id type, u32 set, u32 binding, bool readonly = false) { | ||||||
|  |         const Id uniform_id{DefineVar(type, spv::StorageClass::UniformConstant)}; | ||||||
|  |         Decorate(uniform_id, spv::Decoration::DescriptorSet, set); | ||||||
|  |         Decorate(uniform_id, spv::Decoration::Binding, binding); | ||||||
|  |         if (readonly) { | ||||||
|  |             Decorate(uniform_id, spv::Decoration::NonWritable); | ||||||
|  |         } | ||||||
|  |         return uniform_id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <bool global = true> | ||||||
|  |     [[nodiscard]] Id DefineVar(Id type, spv::StorageClass storage_class) { | ||||||
|  |         const Id pointer_type_id{TypePointer(storage_class, type)}; | ||||||
|  |         return global ? AddGlobalVariable(pointer_type_id, storage_class) | ||||||
|  |                       : AddLocalVariable(pointer_type_id, storage_class); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns the id of a signed integer constant of value
 | ||||||
|  |     [[nodiscard]] Id ConstU32(u32 value) { | ||||||
|  |         return Constant(u32_id, value); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename... Args> | ||||||
|  |     [[nodiscard]] Id ConstU32(Args&&... values) { | ||||||
|  |         constexpr u32 size = static_cast<u32>(sizeof...(values)); | ||||||
|  |         static_assert(size >= 2); | ||||||
|  |         const std::array constituents{Constant(u32_id, values)...}; | ||||||
|  |         const Id type = size <= 4 ? uvec_ids.Get(size) : TypeArray(u32_id, ConstU32(size)); | ||||||
|  |         return ConstantComposite(type, constituents); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns the id of a signed integer constant of value
 | ||||||
|  |     [[nodiscard]] Id ConstS32(s32 value) { | ||||||
|  |         return Constant(i32_id, value); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename... Args> | ||||||
|  |     [[nodiscard]] Id ConstS32(Args&&... values) { | ||||||
|  |         constexpr u32 size = static_cast<u32>(sizeof...(values)); | ||||||
|  |         static_assert(size >= 2); | ||||||
|  |         const std::array constituents{Constant(i32_id, values)...}; | ||||||
|  |         const Id type = size <= 4 ? ivec_ids.Get(size) : TypeArray(i32_id, ConstU32(size)); | ||||||
|  |         return ConstantComposite(type, constituents); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns the id of a float constant of value
 | ||||||
|  |     [[nodiscard]] Id ConstF32(f32 value) { | ||||||
|  |         return Constant(f32_id, value); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename... Args> | ||||||
|  |     [[nodiscard]] Id ConstF32(Args... values) { | ||||||
|  |         constexpr u32 size = static_cast<u32>(sizeof...(values)); | ||||||
|  |         static_assert(size >= 2); | ||||||
|  |         const std::array constituents{Constant(f32_id, values)...}; | ||||||
|  |         const Id type = size <= 4 ? vec_ids.Get(size) : TypeArray(f32_id, ConstU32(size)); | ||||||
|  |         return ConstantComposite(type, constituents); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void DefineArithmeticTypes(); | ||||||
|  |     void DefineEntryPoint(); | ||||||
|  |     void DefineUniformStructs(); | ||||||
|  |     void DefineInterface(); | ||||||
|  |     Id CompareShadow(Id pixel, Id z); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     Core::TelemetrySession& telemetry; | ||||||
|  |     PicaFSConfig config; | ||||||
|  |     Id void_id{}; | ||||||
|  |     Id bool_id{}; | ||||||
|  |     Id f32_id{}; | ||||||
|  |     Id i32_id{}; | ||||||
|  |     Id u32_id{}; | ||||||
|  | 
 | ||||||
|  |     VectorIds vec_ids{}; | ||||||
|  |     VectorIds ivec_ids{}; | ||||||
|  |     VectorIds uvec_ids{}; | ||||||
|  |     VectorIds bvec_ids{}; | ||||||
|  | 
 | ||||||
|  |     Id image2d_id{}; | ||||||
|  |     Id image_cube_id{}; | ||||||
|  |     Id image_buffer_id{}; | ||||||
|  |     Id image_r32_id{}; | ||||||
|  |     Id sampler_id{}; | ||||||
|  |     Id shader_data_id{}; | ||||||
|  | 
 | ||||||
|  |     Id primary_color_id{}; | ||||||
|  |     Id texcoord0_id{}; | ||||||
|  |     Id texcoord1_id{}; | ||||||
|  |     Id texcoord2_id{}; | ||||||
|  |     Id texcoord0_w_id{}; | ||||||
|  |     Id normquat_id{}; | ||||||
|  |     Id view_id{}; | ||||||
|  |     Id color_id{}; | ||||||
|  | 
 | ||||||
|  |     Id gl_frag_coord_id{}; | ||||||
|  |     Id gl_frag_depth_id{}; | ||||||
|  |     Id depth{}; | ||||||
|  | 
 | ||||||
|  |     Id tex0_id{}; | ||||||
|  |     Id tex1_id{}; | ||||||
|  |     Id tex2_id{}; | ||||||
|  |     Id tex_cube_id{}; | ||||||
|  |     Id texture_buffer_lut_lf_id{}; | ||||||
|  |     Id texture_buffer_lut_rg_id{}; | ||||||
|  |     Id texture_buffer_lut_rgba_id{}; | ||||||
|  |     Id shadow_buffer_id{}; | ||||||
|  |     Id shadow_texture_px_id{}; | ||||||
|  |     Id shadow_texture_nx_id{}; | ||||||
|  |     Id shadow_texture_py_id{}; | ||||||
|  |     Id shadow_texture_ny_id{}; | ||||||
|  |     Id shadow_texture_pz_id{}; | ||||||
|  |     Id shadow_texture_nz_id{}; | ||||||
|  | 
 | ||||||
|  |     Id texture_buffer_lut_lf{}; | ||||||
|  |     Id texture_buffer_lut_rg{}; | ||||||
|  |     Id texture_buffer_lut_rgba{}; | ||||||
|  | 
 | ||||||
|  |     Id rounded_primary_color{}; | ||||||
|  |     Id primary_fragment_color{}; | ||||||
|  |     Id secondary_fragment_color{}; | ||||||
|  |     Id combiner_buffer{}; | ||||||
|  |     Id next_combiner_buffer{}; | ||||||
|  |     Id last_tex_env_out{}; | ||||||
|  | 
 | ||||||
|  |     Id color_results_1{}; | ||||||
|  |     Id color_results_2{}; | ||||||
|  |     Id color_results_3{}; | ||||||
|  |     Id alpha_results_1{}; | ||||||
|  |     Id alpha_results_2{}; | ||||||
|  |     Id alpha_results_3{}; | ||||||
|  | 
 | ||||||
|  |     Id proctex_func{}; | ||||||
|  |     Id noise1d_table{}; | ||||||
|  |     Id noise2d_table{}; | ||||||
|  |     Id lut_offsets{}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generates the SPIR-V fragment shader program source code for the current Pica state | ||||||
|  |  * @param config ShaderCacheKey object generated for the current Pica state, used for the shader | ||||||
|  |  *               configuration (NOTE: Use state in this struct only, not the Pica registers!) | ||||||
|  |  * @param separable_shader generates shader that can be used for separate shader object | ||||||
|  |  * @returns String of the shader source code | ||||||
|  |  */ | ||||||
|  | std::vector<u32> GenerateFragmentShaderSPV(const PicaFSConfig& config); | ||||||
|  | 
 | ||||||
|  | } // namespace Vulkan
 | ||||||
							
								
								
									
										233
									
								
								src/video_core/renderer_vulkan/vk_shader_util.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								src/video_core/renderer_vulkan/vk_shader_util.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,233 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <SPIRV/GlslangToSpv.h> | ||||||
|  | #include <glslang/Include/ResourceLimits.h> | ||||||
|  | #include <glslang/Public/ShaderLang.h> | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/literals.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "video_core/renderer_vulkan/vk_shader_util.h" | ||||||
|  | 
 | ||||||
|  | namespace Vulkan { | ||||||
|  | 
 | ||||||
|  | using namespace Common::Literals; | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | constexpr TBuiltInResource DefaultTBuiltInResource = { | ||||||
|  |     .maxLights = 32, | ||||||
|  |     .maxClipPlanes = 6, | ||||||
|  |     .maxTextureUnits = 32, | ||||||
|  |     .maxTextureCoords = 32, | ||||||
|  |     .maxVertexAttribs = 64, | ||||||
|  |     .maxVertexUniformComponents = 4096, | ||||||
|  |     .maxVaryingFloats = 64, | ||||||
|  |     .maxVertexTextureImageUnits = 32, | ||||||
|  |     .maxCombinedTextureImageUnits = 80, | ||||||
|  |     .maxTextureImageUnits = 32, | ||||||
|  |     .maxFragmentUniformComponents = 4096, | ||||||
|  |     .maxDrawBuffers = 32, | ||||||
|  |     .maxVertexUniformVectors = 128, | ||||||
|  |     .maxVaryingVectors = 8, | ||||||
|  |     .maxFragmentUniformVectors = 16, | ||||||
|  |     .maxVertexOutputVectors = 16, | ||||||
|  |     .maxFragmentInputVectors = 15, | ||||||
|  |     .minProgramTexelOffset = -8, | ||||||
|  |     .maxProgramTexelOffset = 7, | ||||||
|  |     .maxClipDistances = 8, | ||||||
|  |     .maxComputeWorkGroupCountX = 65535, | ||||||
|  |     .maxComputeWorkGroupCountY = 65535, | ||||||
|  |     .maxComputeWorkGroupCountZ = 65535, | ||||||
|  |     .maxComputeWorkGroupSizeX = 1024, | ||||||
|  |     .maxComputeWorkGroupSizeY = 1024, | ||||||
|  |     .maxComputeWorkGroupSizeZ = 64, | ||||||
|  |     .maxComputeUniformComponents = 1024, | ||||||
|  |     .maxComputeTextureImageUnits = 16, | ||||||
|  |     .maxComputeImageUniforms = 8, | ||||||
|  |     .maxComputeAtomicCounters = 8, | ||||||
|  |     .maxComputeAtomicCounterBuffers = 1, | ||||||
|  |     .maxVaryingComponents = 60, | ||||||
|  |     .maxVertexOutputComponents = 64, | ||||||
|  |     .maxGeometryInputComponents = 64, | ||||||
|  |     .maxGeometryOutputComponents = 128, | ||||||
|  |     .maxFragmentInputComponents = 128, | ||||||
|  |     .maxImageUnits = 8, | ||||||
|  |     .maxCombinedImageUnitsAndFragmentOutputs = 8, | ||||||
|  |     .maxCombinedShaderOutputResources = 8, | ||||||
|  |     .maxImageSamples = 0, | ||||||
|  |     .maxVertexImageUniforms = 0, | ||||||
|  |     .maxTessControlImageUniforms = 0, | ||||||
|  |     .maxTessEvaluationImageUniforms = 0, | ||||||
|  |     .maxGeometryImageUniforms = 0, | ||||||
|  |     .maxFragmentImageUniforms = 8, | ||||||
|  |     .maxCombinedImageUniforms = 8, | ||||||
|  |     .maxGeometryTextureImageUnits = 16, | ||||||
|  |     .maxGeometryOutputVertices = 256, | ||||||
|  |     .maxGeometryTotalOutputComponents = 1024, | ||||||
|  |     .maxGeometryUniformComponents = 1024, | ||||||
|  |     .maxGeometryVaryingComponents = 64, | ||||||
|  |     .maxTessControlInputComponents = 128, | ||||||
|  |     .maxTessControlOutputComponents = 128, | ||||||
|  |     .maxTessControlTextureImageUnits = 16, | ||||||
|  |     .maxTessControlUniformComponents = 1024, | ||||||
|  |     .maxTessControlTotalOutputComponents = 4096, | ||||||
|  |     .maxTessEvaluationInputComponents = 128, | ||||||
|  |     .maxTessEvaluationOutputComponents = 128, | ||||||
|  |     .maxTessEvaluationTextureImageUnits = 16, | ||||||
|  |     .maxTessEvaluationUniformComponents = 1024, | ||||||
|  |     .maxTessPatchComponents = 120, | ||||||
|  |     .maxPatchVertices = 32, | ||||||
|  |     .maxTessGenLevel = 64, | ||||||
|  |     .maxViewports = 16, | ||||||
|  |     .maxVertexAtomicCounters = 0, | ||||||
|  |     .maxTessControlAtomicCounters = 0, | ||||||
|  |     .maxTessEvaluationAtomicCounters = 0, | ||||||
|  |     .maxGeometryAtomicCounters = 0, | ||||||
|  |     .maxFragmentAtomicCounters = 8, | ||||||
|  |     .maxCombinedAtomicCounters = 8, | ||||||
|  |     .maxAtomicCounterBindings = 1, | ||||||
|  |     .maxVertexAtomicCounterBuffers = 0, | ||||||
|  |     .maxTessControlAtomicCounterBuffers = 0, | ||||||
|  |     .maxTessEvaluationAtomicCounterBuffers = 0, | ||||||
|  |     .maxGeometryAtomicCounterBuffers = 0, | ||||||
|  |     .maxFragmentAtomicCounterBuffers = 1, | ||||||
|  |     .maxCombinedAtomicCounterBuffers = 1, | ||||||
|  |     .maxAtomicCounterBufferSize = 16384, | ||||||
|  |     .maxTransformFeedbackBuffers = 4, | ||||||
|  |     .maxTransformFeedbackInterleavedComponents = 64, | ||||||
|  |     .maxCullDistances = 8, | ||||||
|  |     .maxCombinedClipAndCullDistances = 8, | ||||||
|  |     .maxSamples = 4, | ||||||
|  |     .maxMeshOutputVerticesNV = 256, | ||||||
|  |     .maxMeshOutputPrimitivesNV = 512, | ||||||
|  |     .maxMeshWorkGroupSizeX_NV = 32, | ||||||
|  |     .maxMeshWorkGroupSizeY_NV = 1, | ||||||
|  |     .maxMeshWorkGroupSizeZ_NV = 1, | ||||||
|  |     .maxTaskWorkGroupSizeX_NV = 32, | ||||||
|  |     .maxTaskWorkGroupSizeY_NV = 1, | ||||||
|  |     .maxTaskWorkGroupSizeZ_NV = 1, | ||||||
|  |     .maxMeshViewCountNV = 4, | ||||||
|  |     .maxDualSourceDrawBuffersEXT = 1, | ||||||
|  |     .limits = | ||||||
|  |         TLimits{ | ||||||
|  |             .nonInductiveForLoops = 1, | ||||||
|  |             .whileLoops = 1, | ||||||
|  |             .doWhileLoops = 1, | ||||||
|  |             .generalUniformIndexing = 1, | ||||||
|  |             .generalAttributeMatrixVectorIndexing = 1, | ||||||
|  |             .generalVaryingIndexing = 1, | ||||||
|  |             .generalSamplerIndexing = 1, | ||||||
|  |             .generalVariableIndexing = 1, | ||||||
|  |             .generalConstantMatrixVectorIndexing = 1, | ||||||
|  |         }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | EShLanguage ToEshShaderStage(vk::ShaderStageFlagBits stage) { | ||||||
|  |     switch (stage) { | ||||||
|  |     case vk::ShaderStageFlagBits::eVertex: | ||||||
|  |         return EShLanguage::EShLangVertex; | ||||||
|  |     case vk::ShaderStageFlagBits::eGeometry: | ||||||
|  |         return EShLanguage::EShLangGeometry; | ||||||
|  |     case vk::ShaderStageFlagBits::eFragment: | ||||||
|  |         return EShLanguage::EShLangFragment; | ||||||
|  |     case vk::ShaderStageFlagBits::eCompute: | ||||||
|  |         return EShLanguage::EShLangCompute; | ||||||
|  |     default: | ||||||
|  |         UNREACHABLE_MSG("Unkown shader stage {}", stage); | ||||||
|  |     } | ||||||
|  |     return EShLanguage::EShLangVertex; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool InitializeCompiler() { | ||||||
|  |     static bool glslang_initialized = false; | ||||||
|  | 
 | ||||||
|  |     if (glslang_initialized) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!glslang::InitializeProcess()) { | ||||||
|  |         LOG_CRITICAL(Render_Vulkan, "Failed to initialize glslang shader compiler"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::atexit([]() { glslang::FinalizeProcess(); }); | ||||||
|  | 
 | ||||||
|  |     glslang_initialized = true; | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | } // Anonymous namespace
 | ||||||
|  | 
 | ||||||
|  | vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device) { | ||||||
|  |     if (!InitializeCompiler()) { | ||||||
|  |         return VK_NULL_HANDLE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     EProfile profile = ECoreProfile; | ||||||
|  |     EShMessages messages = | ||||||
|  |         static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules); | ||||||
|  |     EShLanguage lang = ToEshShaderStage(stage); | ||||||
|  | 
 | ||||||
|  |     const int default_version = 450; | ||||||
|  |     const char* pass_source_code = code.data(); | ||||||
|  |     int pass_source_code_length = static_cast<int>(code.size()); | ||||||
|  | 
 | ||||||
|  |     auto shader = std::make_unique<glslang::TShader>(lang); | ||||||
|  |     shader->setEnvTarget(glslang::EShTargetSpv, | ||||||
|  |                          glslang::EShTargetLanguageVersion::EShTargetSpv_1_3); | ||||||
|  |     shader->setStringsWithLengths(&pass_source_code, &pass_source_code_length, 1); | ||||||
|  | 
 | ||||||
|  |     glslang::TShader::ForbidIncluder includer; | ||||||
|  |     if (!shader->parse(&DefaultTBuiltInResource, default_version, profile, false, true, messages, | ||||||
|  |                        includer)) [[unlikely]] { | ||||||
|  |         LOG_INFO(Render_Vulkan, "Shader Info Log:\n{}\n{}", shader->getInfoLog(), | ||||||
|  |                  shader->getInfoDebugLog()); | ||||||
|  |         return VK_NULL_HANDLE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Even though there's only a single shader, we still need to link it to generate SPV
 | ||||||
|  |     auto program = std::make_unique<glslang::TProgram>(); | ||||||
|  |     program->addShader(shader.get()); | ||||||
|  |     if (!program->link(messages)) { | ||||||
|  |         LOG_INFO(Render_Vulkan, "Program Info Log:\n{}\n{}", program->getInfoLog(), | ||||||
|  |                  program->getInfoDebugLog()); | ||||||
|  |         return VK_NULL_HANDLE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     glslang::TIntermediate* intermediate = program->getIntermediate(lang); | ||||||
|  |     std::vector<u32> out_code; | ||||||
|  |     spv::SpvBuildLogger logger; | ||||||
|  |     glslang::SpvOptions options; | ||||||
|  | 
 | ||||||
|  |     // Enable optimizations on the generated SPIR-V code.
 | ||||||
|  |     options.disableOptimizer = false; | ||||||
|  |     options.validate = false; | ||||||
|  |     options.optimizeSize = true; | ||||||
|  | 
 | ||||||
|  |     out_code.reserve(8_KiB); | ||||||
|  |     glslang::GlslangToSpv(*intermediate, out_code, &logger, &options); | ||||||
|  | 
 | ||||||
|  |     const std::string spv_messages = logger.getAllMessages(); | ||||||
|  |     if (!spv_messages.empty()) { | ||||||
|  |         LOG_INFO(Render_Vulkan, "SPIR-V conversion messages: {}", spv_messages); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return CompileSPV(out_code, device); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | vk::ShaderModule CompileSPV(std::span<const u32> code, vk::Device device) { | ||||||
|  |     const vk::ShaderModuleCreateInfo shader_info = { | ||||||
|  |         .codeSize = code.size() * sizeof(u32), | ||||||
|  |         .pCode = code.data(), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |         return device.createShaderModule(shader_info); | ||||||
|  |     } catch (vk::SystemError& err) { | ||||||
|  |         UNREACHABLE_MSG("{}", err.what()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return VK_NULL_HANDLE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Vulkan
 | ||||||
							
								
								
									
										28
									
								
								src/video_core/renderer_vulkan/vk_shader_util.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/video_core/renderer_vulkan/vk_shader_util.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <span> | ||||||
|  | 
 | ||||||
|  | #include "video_core/renderer_vulkan/vk_common.h" | ||||||
|  | 
 | ||||||
|  | namespace Vulkan { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Creates a vulkan shader module from GLSL by converting it to SPIR-V using glslang. | ||||||
|  |  * @param code The string containing GLSL code. | ||||||
|  |  * @param stage The pipeline stage the shader will be used in. | ||||||
|  |  * @param device The vulkan device handle. | ||||||
|  |  */ | ||||||
|  | vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Creates a vulkan shader module from SPIR-V bytecode. | ||||||
|  |  * @param code The SPIR-V bytecode data. | ||||||
|  |  * @param device The vulkan device handle | ||||||
|  |  */ | ||||||
|  | vk::ShaderModule CompileSPV(std::span<const u32> code, vk::Device device); | ||||||
|  | 
 | ||||||
|  | } // namespace Vulkan
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue