mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	video_core: Abstract shader generators. (#6990)
* video_core: Abstract shader generators. * shader: Extract common generator structures and move generators to specific namespaces. * shader: Minor fixes and clean-up.
This commit is contained in:
		
							parent
							
								
									1492d73ccb
								
							
						
					
					
						commit
						50f22d1f59
					
				
					 35 changed files with 1374 additions and 3344 deletions
				
			
		|  | @ -4,24 +4,24 @@ include(GenerateBuildInfo) | ||||||
| # The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR) | # The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR) | ||||||
| set(VIDEO_CORE "${SRC_DIR}/src/video_core") | set(VIDEO_CORE "${SRC_DIR}/src/video_core") | ||||||
| set(HASH_FILES | set(HASH_FILES | ||||||
|     "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp" |  | ||||||
|     "${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}/renderer_opengl/gl_shader_util.cpp" |     "${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp" | ||||||
|     "${VIDEO_CORE}/renderer_opengl/gl_shader_util.h" |     "${VIDEO_CORE}/renderer_opengl/gl_shader_util.h" | ||||||
|     "${VIDEO_CORE}/renderer_vulkan/vk_shader_gen.cpp" |  | ||||||
|     "${VIDEO_CORE}/renderer_vulkan/vk_shader_gen.h" |  | ||||||
|     "${VIDEO_CORE}/renderer_vulkan/vk_shader_gen_spv.cpp" |  | ||||||
|     "${VIDEO_CORE}/renderer_vulkan/vk_shader_gen_spv.h" |  | ||||||
|     "${VIDEO_CORE}/renderer_vulkan/vk_shader_util.cpp" |     "${VIDEO_CORE}/renderer_vulkan/vk_shader_util.cpp" | ||||||
|     "${VIDEO_CORE}/renderer_vulkan/vk_shader_util.h" |     "${VIDEO_CORE}/renderer_vulkan/vk_shader_util.h" | ||||||
|  |     "${VIDEO_CORE}/shader/generator/glsl_shader_decompiler.cpp" | ||||||
|  |     "${VIDEO_CORE}/shader/generator/glsl_shader_decompiler.h" | ||||||
|  |     "${VIDEO_CORE}/shader/generator/glsl_shader_gen.cpp" | ||||||
|  |     "${VIDEO_CORE}/shader/generator/glsl_shader_gen.h" | ||||||
|  |     "${VIDEO_CORE}/shader/generator/shader_gen.cpp" | ||||||
|  |     "${VIDEO_CORE}/shader/generator/shader_gen.h" | ||||||
|  |     "${VIDEO_CORE}/shader/generator/shader_uniforms.cpp" | ||||||
|  |     "${VIDEO_CORE}/shader/generator/shader_uniforms.h" | ||||||
|  |     "${VIDEO_CORE}/shader/generator/spv_shader_gen.cpp" | ||||||
|  |     "${VIDEO_CORE}/shader/generator/spv_shader_gen.h" | ||||||
|     "${VIDEO_CORE}/shader/shader.cpp" |     "${VIDEO_CORE}/shader/shader.cpp" | ||||||
|     "${VIDEO_CORE}/shader/shader.h" |     "${VIDEO_CORE}/shader/shader.h" | ||||||
|     "${VIDEO_CORE}/shader/shader_uniforms.cpp" |  | ||||||
|     "${VIDEO_CORE}/shader/shader_uniforms.h" |  | ||||||
|     "${VIDEO_CORE}/pica.cpp" |     "${VIDEO_CORE}/pica.cpp" | ||||||
|     "${VIDEO_CORE}/pica.h" |     "${VIDEO_CORE}/pica.h" | ||||||
|     "${VIDEO_CORE}/regs_framebuffer.h" |     "${VIDEO_CORE}/regs_framebuffer.h" | ||||||
|  |  | ||||||
|  | @ -15,14 +15,22 @@ add_custom_command(OUTPUT scm_rev.cpp | ||||||
|     DEPENDS |     DEPENDS | ||||||
|       # WARNING! It was too much work to try and make a common location for this list, |       # WARNING! It was too much work to try and make a common location for this list, | ||||||
|       # so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well |       # so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well | ||||||
|       "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp" |  | ||||||
|       "${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}/renderer_opengl/gl_shader_util.cpp" |       "${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp" | ||||||
|       "${VIDEO_CORE}/renderer_opengl/gl_shader_util.h" |       "${VIDEO_CORE}/renderer_opengl/gl_shader_util.h" | ||||||
|  |       "${VIDEO_CORE}/renderer_vulkan/vk_shader_util.cpp" | ||||||
|  |       "${VIDEO_CORE}/renderer_vulkan/vk_shader_util.h" | ||||||
|  |       "${VIDEO_CORE}/shader/generator/glsl_shader_decompiler.cpp" | ||||||
|  |       "${VIDEO_CORE}/shader/generator/glsl_shader_decompiler.h" | ||||||
|  |       "${VIDEO_CORE}/shader/generator/glsl_shader_gen.cpp" | ||||||
|  |       "${VIDEO_CORE}/shader/generator/glsl_shader_gen.h" | ||||||
|  |       "${VIDEO_CORE}/shader/generator/shader_gen.cpp" | ||||||
|  |       "${VIDEO_CORE}/shader/generator/shader_gen.h" | ||||||
|  |       "${VIDEO_CORE}/shader/generator/shader_uniforms.cpp" | ||||||
|  |       "${VIDEO_CORE}/shader/generator/shader_uniforms.h" | ||||||
|  |       "${VIDEO_CORE}/shader/generator/spv_shader_gen.cpp" | ||||||
|  |       "${VIDEO_CORE}/shader/generator/spv_shader_gen.h" | ||||||
|       "${VIDEO_CORE}/shader/shader.cpp" |       "${VIDEO_CORE}/shader/shader.cpp" | ||||||
|       "${VIDEO_CORE}/shader/shader.h" |       "${VIDEO_CORE}/shader/shader.h" | ||||||
|       "${VIDEO_CORE}/pica.cpp" |       "${VIDEO_CORE}/pica.cpp" | ||||||
|  |  | ||||||
|  | @ -61,12 +61,8 @@ add_library(video_core STATIC | ||||||
|     renderer_opengl/gl_rasterizer_cache.cpp |     renderer_opengl/gl_rasterizer_cache.cpp | ||||||
|     renderer_opengl/gl_resource_manager.cpp |     renderer_opengl/gl_resource_manager.cpp | ||||||
|     renderer_opengl/gl_resource_manager.h |     renderer_opengl/gl_resource_manager.h | ||||||
|     renderer_opengl/gl_shader_decompiler.cpp |  | ||||||
|     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 | ||||||
|  | @ -130,10 +126,6 @@ add_library(video_core STATIC | ||||||
|     renderer_vulkan/vk_present_window.h |     renderer_vulkan/vk_present_window.h | ||||||
|     renderer_vulkan/vk_renderpass_cache.cpp |     renderer_vulkan/vk_renderpass_cache.cpp | ||||||
|     renderer_vulkan/vk_renderpass_cache.h |     renderer_vulkan/vk_renderpass_cache.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.cpp | ||||||
|     renderer_vulkan/vk_shader_util.h |     renderer_vulkan/vk_shader_util.h | ||||||
|     renderer_vulkan/vk_stream_buffer.cpp |     renderer_vulkan/vk_stream_buffer.cpp | ||||||
|  | @ -143,6 +135,16 @@ add_library(video_core STATIC | ||||||
|     renderer_vulkan/vk_texture_runtime.cpp |     renderer_vulkan/vk_texture_runtime.cpp | ||||||
|     renderer_vulkan/vk_texture_runtime.h |     renderer_vulkan/vk_texture_runtime.h | ||||||
|     shader/debug_data.h |     shader/debug_data.h | ||||||
|  |     shader/generator/glsl_shader_decompiler.cpp | ||||||
|  |     shader/generator/glsl_shader_decompiler.h | ||||||
|  |     shader/generator/glsl_shader_gen.cpp | ||||||
|  |     shader/generator/glsl_shader_gen.h | ||||||
|  |     shader/generator/shader_gen.cpp | ||||||
|  |     shader/generator/shader_gen.h | ||||||
|  |     shader/generator/shader_uniforms.cpp | ||||||
|  |     shader/generator/shader_uniforms.h | ||||||
|  |     shader/generator/spv_shader_gen.cpp | ||||||
|  |     shader/generator/spv_shader_gen.h | ||||||
|     shader/shader.cpp |     shader/shader.cpp | ||||||
|     shader/shader.h |     shader/shader.h | ||||||
|     shader/shader_interpreter.cpp |     shader/shader_interpreter.cpp | ||||||
|  | @ -151,8 +153,6 @@ add_library(video_core STATIC | ||||||
|     shader/shader_jit_x64_compiler.cpp |     shader/shader_jit_x64_compiler.cpp | ||||||
|     shader/shader_jit_x64.h |     shader/shader_jit_x64.h | ||||||
|     shader/shader_jit_x64_compiler.h |     shader/shader_jit_x64_compiler.h | ||||||
|     shader/shader_uniforms.cpp |  | ||||||
|     shader/shader_uniforms.h |  | ||||||
|     texture/etc1.cpp |     texture/etc1.cpp | ||||||
|     texture/etc1.h |     texture/etc1.h | ||||||
|     texture/texture_decode.cpp |     texture/texture_decode.cpp | ||||||
|  |  | ||||||
|  | @ -54,7 +54,7 @@ RasterizerAccelerated::HardwareVertex::HardwareVertex(const Pica::Shader::Output | ||||||
| 
 | 
 | ||||||
| RasterizerAccelerated::RasterizerAccelerated(Memory::MemorySystem& memory_) | RasterizerAccelerated::RasterizerAccelerated(Memory::MemorySystem& memory_) | ||||||
|     : memory{memory_}, regs{Pica::g_state.regs} { |     : memory{memory_}, regs{Pica::g_state.regs} { | ||||||
|     uniform_block_data.lighting_lut_dirty.fill(true); |     fs_uniform_block_data.lighting_lut_dirty.fill(true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -135,7 +135,7 @@ void RasterizerAccelerated::SyncEntireState() { | ||||||
|     SyncFixedState(); |     SyncFixedState(); | ||||||
| 
 | 
 | ||||||
|     // Sync uniforms
 |     // Sync uniforms
 | ||||||
|     SyncClipCoef(); |     SyncClipPlane(); | ||||||
|     SyncDepthScale(); |     SyncDepthScale(); | ||||||
|     SyncDepthOffset(); |     SyncDepthOffset(); | ||||||
|     SyncAlphaTest(); |     SyncAlphaTest(); | ||||||
|  | @ -199,7 +199,7 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) { | ||||||
|     case PICA_REG_INDEX(texturing.fog_lut_data[5]): |     case PICA_REG_INDEX(texturing.fog_lut_data[5]): | ||||||
|     case PICA_REG_INDEX(texturing.fog_lut_data[6]): |     case PICA_REG_INDEX(texturing.fog_lut_data[6]): | ||||||
|     case PICA_REG_INDEX(texturing.fog_lut_data[7]): |     case PICA_REG_INDEX(texturing.fog_lut_data[7]): | ||||||
|         uniform_block_data.fog_lut_dirty = true; |         fs_uniform_block_data.fog_lut_dirty = true; | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     // ProcTex state
 |     // ProcTex state
 | ||||||
|  | @ -227,19 +227,19 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) { | ||||||
|         using Pica::TexturingRegs; |         using Pica::TexturingRegs; | ||||||
|         switch (regs.texturing.proctex_lut_config.ref_table.Value()) { |         switch (regs.texturing.proctex_lut_config.ref_table.Value()) { | ||||||
|         case TexturingRegs::ProcTexLutTable::Noise: |         case TexturingRegs::ProcTexLutTable::Noise: | ||||||
|             uniform_block_data.proctex_noise_lut_dirty = true; |             fs_uniform_block_data.proctex_noise_lut_dirty = true; | ||||||
|             break; |             break; | ||||||
|         case TexturingRegs::ProcTexLutTable::ColorMap: |         case TexturingRegs::ProcTexLutTable::ColorMap: | ||||||
|             uniform_block_data.proctex_color_map_dirty = true; |             fs_uniform_block_data.proctex_color_map_dirty = true; | ||||||
|             break; |             break; | ||||||
|         case TexturingRegs::ProcTexLutTable::AlphaMap: |         case TexturingRegs::ProcTexLutTable::AlphaMap: | ||||||
|             uniform_block_data.proctex_alpha_map_dirty = true; |             fs_uniform_block_data.proctex_alpha_map_dirty = true; | ||||||
|             break; |             break; | ||||||
|         case TexturingRegs::ProcTexLutTable::Color: |         case TexturingRegs::ProcTexLutTable::Color: | ||||||
|             uniform_block_data.proctex_lut_dirty = true; |             fs_uniform_block_data.proctex_lut_dirty = true; | ||||||
|             break; |             break; | ||||||
|         case TexturingRegs::ProcTexLutTable::ColorDiff: |         case TexturingRegs::ProcTexLutTable::ColorDiff: | ||||||
|             uniform_block_data.proctex_diff_lut_dirty = true; |             fs_uniform_block_data.proctex_diff_lut_dirty = true; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|  | @ -588,8 +588,8 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) { | ||||||
|     case PICA_REG_INDEX(lighting.lut_data[6]): |     case PICA_REG_INDEX(lighting.lut_data[6]): | ||||||
|     case PICA_REG_INDEX(lighting.lut_data[7]): { |     case PICA_REG_INDEX(lighting.lut_data[7]): { | ||||||
|         const auto& lut_config = regs.lighting.lut_config; |         const auto& lut_config = regs.lighting.lut_config; | ||||||
|         uniform_block_data.lighting_lut_dirty[lut_config.type] = true; |         fs_uniform_block_data.lighting_lut_dirty[lut_config.type] = true; | ||||||
|         uniform_block_data.lighting_lut_dirty_any = true; |         fs_uniform_block_data.lighting_lut_dirty_any = true; | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -616,11 +616,12 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) { | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     // Clipping plane
 |     // Clipping plane
 | ||||||
|  |     case PICA_REG_INDEX(rasterizer.clip_enable): | ||||||
|     case PICA_REG_INDEX(rasterizer.clip_coef[0]): |     case PICA_REG_INDEX(rasterizer.clip_coef[0]): | ||||||
|     case PICA_REG_INDEX(rasterizer.clip_coef[1]): |     case PICA_REG_INDEX(rasterizer.clip_coef[1]): | ||||||
|     case PICA_REG_INDEX(rasterizer.clip_coef[2]): |     case PICA_REG_INDEX(rasterizer.clip_coef[2]): | ||||||
|     case PICA_REG_INDEX(rasterizer.clip_coef[3]): |     case PICA_REG_INDEX(rasterizer.clip_coef[3]): | ||||||
|         SyncClipCoef(); |         SyncClipPlane(); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -631,18 +632,18 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) { | ||||||
| void RasterizerAccelerated::SyncDepthScale() { | void RasterizerAccelerated::SyncDepthScale() { | ||||||
|     const f32 depth_scale = f24::FromRaw(regs.rasterizer.viewport_depth_range).ToFloat32(); |     const f32 depth_scale = f24::FromRaw(regs.rasterizer.viewport_depth_range).ToFloat32(); | ||||||
| 
 | 
 | ||||||
|     if (depth_scale != uniform_block_data.data.depth_scale) { |     if (depth_scale != fs_uniform_block_data.data.depth_scale) { | ||||||
|         uniform_block_data.data.depth_scale = depth_scale; |         fs_uniform_block_data.data.depth_scale = depth_scale; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerAccelerated::SyncDepthOffset() { | void RasterizerAccelerated::SyncDepthOffset() { | ||||||
|     const f32 depth_offset = f24::FromRaw(regs.rasterizer.viewport_depth_near_plane).ToFloat32(); |     const f32 depth_offset = f24::FromRaw(regs.rasterizer.viewport_depth_near_plane).ToFloat32(); | ||||||
| 
 | 
 | ||||||
|     if (depth_offset != uniform_block_data.data.depth_offset) { |     if (depth_offset != fs_uniform_block_data.data.depth_offset) { | ||||||
|         uniform_block_data.data.depth_offset = depth_offset; |         fs_uniform_block_data.data.depth_offset = depth_offset; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -654,9 +655,9 @@ void RasterizerAccelerated::SyncFogColor() { | ||||||
|         fog_color_regs.b.Value() / 255.0f, |         fog_color_regs.b.Value() / 255.0f, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     if (fog_color != uniform_block_data.data.fog_color) { |     if (fog_color != fs_uniform_block_data.data.fog_color) { | ||||||
|         uniform_block_data.data.fog_color = fog_color; |         fs_uniform_block_data.data.fog_color = fog_color; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -674,13 +675,13 @@ void RasterizerAccelerated::SyncProcTexNoise() { | ||||||
|         Pica::f16::FromRaw(regs.texturing.proctex_noise_v.phase).ToFloat32(), |         Pica::f16::FromRaw(regs.texturing.proctex_noise_v.phase).ToFloat32(), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     if (proctex_noise_f != uniform_block_data.data.proctex_noise_f || |     if (proctex_noise_f != fs_uniform_block_data.data.proctex_noise_f || | ||||||
|         proctex_noise_a != uniform_block_data.data.proctex_noise_a || |         proctex_noise_a != fs_uniform_block_data.data.proctex_noise_a || | ||||||
|         proctex_noise_p != uniform_block_data.data.proctex_noise_p) { |         proctex_noise_p != fs_uniform_block_data.data.proctex_noise_p) { | ||||||
|         uniform_block_data.data.proctex_noise_f = proctex_noise_f; |         fs_uniform_block_data.data.proctex_noise_f = proctex_noise_f; | ||||||
|         uniform_block_data.data.proctex_noise_a = proctex_noise_a; |         fs_uniform_block_data.data.proctex_noise_a = proctex_noise_a; | ||||||
|         uniform_block_data.data.proctex_noise_p = proctex_noise_p; |         fs_uniform_block_data.data.proctex_noise_p = proctex_noise_p; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -688,25 +689,25 @@ void RasterizerAccelerated::SyncProcTexBias() { | ||||||
|     const auto proctex_bias = Pica::f16::FromRaw(regs.texturing.proctex.bias_low | |     const auto proctex_bias = Pica::f16::FromRaw(regs.texturing.proctex.bias_low | | ||||||
|                                                  (regs.texturing.proctex_lut.bias_high << 8)) |                                                  (regs.texturing.proctex_lut.bias_high << 8)) | ||||||
|                                   .ToFloat32(); |                                   .ToFloat32(); | ||||||
|     if (proctex_bias != uniform_block_data.data.proctex_bias) { |     if (proctex_bias != fs_uniform_block_data.data.proctex_bias) { | ||||||
|         uniform_block_data.data.proctex_bias = proctex_bias; |         fs_uniform_block_data.data.proctex_bias = proctex_bias; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerAccelerated::SyncAlphaTest() { | void RasterizerAccelerated::SyncAlphaTest() { | ||||||
|     if (regs.framebuffer.output_merger.alpha_test.ref != |     if (regs.framebuffer.output_merger.alpha_test.ref != | ||||||
|         static_cast<u32>(uniform_block_data.data.alphatest_ref)) { |         static_cast<u32>(fs_uniform_block_data.data.alphatest_ref)) { | ||||||
|         uniform_block_data.data.alphatest_ref = regs.framebuffer.output_merger.alpha_test.ref; |         fs_uniform_block_data.data.alphatest_ref = regs.framebuffer.output_merger.alpha_test.ref; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerAccelerated::SyncCombinerColor() { | void RasterizerAccelerated::SyncCombinerColor() { | ||||||
|     const auto combiner_color = ColorRGBA8(regs.texturing.tev_combiner_buffer_color.raw); |     const auto combiner_color = ColorRGBA8(regs.texturing.tev_combiner_buffer_color.raw); | ||||||
|     if (combiner_color != uniform_block_data.data.tev_combiner_buffer_color) { |     if (combiner_color != fs_uniform_block_data.data.tev_combiner_buffer_color) { | ||||||
|         uniform_block_data.data.tev_combiner_buffer_color = combiner_color; |         fs_uniform_block_data.data.tev_combiner_buffer_color = combiner_color; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -714,51 +715,51 @@ void RasterizerAccelerated::SyncTevConstColor( | ||||||
|     const size_t stage_index, const Pica::TexturingRegs::TevStageConfig& tev_stage) { |     const size_t stage_index, const Pica::TexturingRegs::TevStageConfig& tev_stage) { | ||||||
|     const auto const_color = ColorRGBA8(tev_stage.const_color); |     const auto const_color = ColorRGBA8(tev_stage.const_color); | ||||||
| 
 | 
 | ||||||
|     if (const_color == uniform_block_data.data.const_color[stage_index]) { |     if (const_color == fs_uniform_block_data.data.const_color[stage_index]) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     uniform_block_data.data.const_color[stage_index] = const_color; |     fs_uniform_block_data.data.const_color[stage_index] = const_color; | ||||||
|     uniform_block_data.dirty = true; |     fs_uniform_block_data.dirty = true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerAccelerated::SyncGlobalAmbient() { | void RasterizerAccelerated::SyncGlobalAmbient() { | ||||||
|     const auto color = LightColor(regs.lighting.global_ambient); |     const auto color = LightColor(regs.lighting.global_ambient); | ||||||
|     if (color != uniform_block_data.data.lighting_global_ambient) { |     if (color != fs_uniform_block_data.data.lighting_global_ambient) { | ||||||
|         uniform_block_data.data.lighting_global_ambient = color; |         fs_uniform_block_data.data.lighting_global_ambient = color; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerAccelerated::SyncLightSpecular0(int light_index) { | void RasterizerAccelerated::SyncLightSpecular0(int light_index) { | ||||||
|     const auto color = LightColor(regs.lighting.light[light_index].specular_0); |     const auto color = LightColor(regs.lighting.light[light_index].specular_0); | ||||||
|     if (color != uniform_block_data.data.light_src[light_index].specular_0) { |     if (color != fs_uniform_block_data.data.light_src[light_index].specular_0) { | ||||||
|         uniform_block_data.data.light_src[light_index].specular_0 = color; |         fs_uniform_block_data.data.light_src[light_index].specular_0 = color; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerAccelerated::SyncLightSpecular1(int light_index) { | void RasterizerAccelerated::SyncLightSpecular1(int light_index) { | ||||||
|     const auto color = LightColor(regs.lighting.light[light_index].specular_1); |     const auto color = LightColor(regs.lighting.light[light_index].specular_1); | ||||||
|     if (color != uniform_block_data.data.light_src[light_index].specular_1) { |     if (color != fs_uniform_block_data.data.light_src[light_index].specular_1) { | ||||||
|         uniform_block_data.data.light_src[light_index].specular_1 = color; |         fs_uniform_block_data.data.light_src[light_index].specular_1 = color; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerAccelerated::SyncLightDiffuse(int light_index) { | void RasterizerAccelerated::SyncLightDiffuse(int light_index) { | ||||||
|     const auto color = LightColor(regs.lighting.light[light_index].diffuse); |     const auto color = LightColor(regs.lighting.light[light_index].diffuse); | ||||||
|     if (color != uniform_block_data.data.light_src[light_index].diffuse) { |     if (color != fs_uniform_block_data.data.light_src[light_index].diffuse) { | ||||||
|         uniform_block_data.data.light_src[light_index].diffuse = color; |         fs_uniform_block_data.data.light_src[light_index].diffuse = color; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerAccelerated::SyncLightAmbient(int light_index) { | void RasterizerAccelerated::SyncLightAmbient(int light_index) { | ||||||
|     const auto color = LightColor(regs.lighting.light[light_index].ambient); |     const auto color = LightColor(regs.lighting.light[light_index].ambient); | ||||||
|     if (color != uniform_block_data.data.light_src[light_index].ambient) { |     if (color != fs_uniform_block_data.data.light_src[light_index].ambient) { | ||||||
|         uniform_block_data.data.light_src[light_index].ambient = color; |         fs_uniform_block_data.data.light_src[light_index].ambient = color; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -769,9 +770,9 @@ void RasterizerAccelerated::SyncLightPosition(int light_index) { | ||||||
|         Pica::f16::FromRaw(regs.lighting.light[light_index].z).ToFloat32(), |         Pica::f16::FromRaw(regs.lighting.light[light_index].z).ToFloat32(), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     if (position != uniform_block_data.data.light_src[light_index].position) { |     if (position != fs_uniform_block_data.data.light_src[light_index].position) { | ||||||
|         uniform_block_data.data.light_src[light_index].position = position; |         fs_uniform_block_data.data.light_src[light_index].position = position; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -780,9 +781,9 @@ void RasterizerAccelerated::SyncLightSpotDirection(int light_index) { | ||||||
|     const auto spot_direction = |     const auto spot_direction = | ||||||
|         Common::Vec3f{light.spot_x / 2047.0f, light.spot_y / 2047.0f, light.spot_z / 2047.0f}; |         Common::Vec3f{light.spot_x / 2047.0f, light.spot_y / 2047.0f, light.spot_z / 2047.0f}; | ||||||
| 
 | 
 | ||||||
|     if (spot_direction != uniform_block_data.data.light_src[light_index].spot_direction) { |     if (spot_direction != fs_uniform_block_data.data.light_src[light_index].spot_direction) { | ||||||
|         uniform_block_data.data.light_src[light_index].spot_direction = spot_direction; |         fs_uniform_block_data.data.light_src[light_index].spot_direction = spot_direction; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -790,9 +791,9 @@ void RasterizerAccelerated::SyncLightDistanceAttenuationBias(int light_index) { | ||||||
|     const f32 dist_atten_bias = |     const f32 dist_atten_bias = | ||||||
|         Pica::f20::FromRaw(regs.lighting.light[light_index].dist_atten_bias).ToFloat32(); |         Pica::f20::FromRaw(regs.lighting.light[light_index].dist_atten_bias).ToFloat32(); | ||||||
| 
 | 
 | ||||||
|     if (dist_atten_bias != uniform_block_data.data.light_src[light_index].dist_atten_bias) { |     if (dist_atten_bias != fs_uniform_block_data.data.light_src[light_index].dist_atten_bias) { | ||||||
|         uniform_block_data.data.light_src[light_index].dist_atten_bias = dist_atten_bias; |         fs_uniform_block_data.data.light_src[light_index].dist_atten_bias = dist_atten_bias; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -800,9 +801,9 @@ void RasterizerAccelerated::SyncLightDistanceAttenuationScale(int light_index) { | ||||||
|     const f32 dist_atten_scale = |     const f32 dist_atten_scale = | ||||||
|         Pica::f20::FromRaw(regs.lighting.light[light_index].dist_atten_scale).ToFloat32(); |         Pica::f20::FromRaw(regs.lighting.light[light_index].dist_atten_scale).ToFloat32(); | ||||||
| 
 | 
 | ||||||
|     if (dist_atten_scale != uniform_block_data.data.light_src[light_index].dist_atten_scale) { |     if (dist_atten_scale != fs_uniform_block_data.data.light_src[light_index].dist_atten_scale) { | ||||||
|         uniform_block_data.data.light_src[light_index].dist_atten_scale = dist_atten_scale; |         fs_uniform_block_data.data.light_src[light_index].dist_atten_scale = dist_atten_scale; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -811,28 +812,28 @@ void RasterizerAccelerated::SyncShadowBias() { | ||||||
|     const f32 constant = Pica::f16::FromRaw(shadow.constant).ToFloat32(); |     const f32 constant = Pica::f16::FromRaw(shadow.constant).ToFloat32(); | ||||||
|     const f32 linear = Pica::f16::FromRaw(shadow.linear).ToFloat32(); |     const f32 linear = Pica::f16::FromRaw(shadow.linear).ToFloat32(); | ||||||
| 
 | 
 | ||||||
|     if (constant != uniform_block_data.data.shadow_bias_constant || |     if (constant != fs_uniform_block_data.data.shadow_bias_constant || | ||||||
|         linear != uniform_block_data.data.shadow_bias_linear) { |         linear != fs_uniform_block_data.data.shadow_bias_linear) { | ||||||
|         uniform_block_data.data.shadow_bias_constant = constant; |         fs_uniform_block_data.data.shadow_bias_constant = constant; | ||||||
|         uniform_block_data.data.shadow_bias_linear = linear; |         fs_uniform_block_data.data.shadow_bias_linear = linear; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerAccelerated::SyncShadowTextureBias() { | void RasterizerAccelerated::SyncShadowTextureBias() { | ||||||
|     const s32 bias = regs.texturing.shadow.bias << 1; |     const s32 bias = regs.texturing.shadow.bias << 1; | ||||||
|     if (bias != uniform_block_data.data.shadow_texture_bias) { |     if (bias != fs_uniform_block_data.data.shadow_texture_bias) { | ||||||
|         uniform_block_data.data.shadow_texture_bias = bias; |         fs_uniform_block_data.data.shadow_texture_bias = bias; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerAccelerated::SyncTextureLodBias(int tex_index) { | void RasterizerAccelerated::SyncTextureLodBias(int tex_index) { | ||||||
|     const auto pica_textures = regs.texturing.GetTextures(); |     const auto pica_textures = regs.texturing.GetTextures(); | ||||||
|     const f32 bias = pica_textures[tex_index].config.lod.bias / 256.0f; |     const f32 bias = pica_textures[tex_index].config.lod.bias / 256.0f; | ||||||
|     if (bias != uniform_block_data.data.tex_lod_bias[tex_index]) { |     if (bias != fs_uniform_block_data.data.tex_lod_bias[tex_index]) { | ||||||
|         uniform_block_data.data.tex_lod_bias[tex_index] = bias; |         fs_uniform_block_data.data.tex_lod_bias[tex_index] = bias; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -840,19 +841,22 @@ void RasterizerAccelerated::SyncTextureBorderColor(int tex_index) { | ||||||
|     const auto pica_textures = regs.texturing.GetTextures(); |     const auto pica_textures = regs.texturing.GetTextures(); | ||||||
|     const auto params = pica_textures[tex_index].config; |     const auto params = pica_textures[tex_index].config; | ||||||
|     const Common::Vec4f border_color = ColorRGBA8(params.border_color.raw); |     const Common::Vec4f border_color = ColorRGBA8(params.border_color.raw); | ||||||
|     if (border_color != uniform_block_data.data.tex_border_color[tex_index]) { |     if (border_color != fs_uniform_block_data.data.tex_border_color[tex_index]) { | ||||||
|         uniform_block_data.data.tex_border_color[tex_index] = border_color; |         fs_uniform_block_data.data.tex_border_color[tex_index] = border_color; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerAccelerated::SyncClipCoef() { | void RasterizerAccelerated::SyncClipPlane() { | ||||||
|  |     const bool enable_clip1 = regs.rasterizer.clip_enable != 0; | ||||||
|     const auto raw_clip_coef = regs.rasterizer.GetClipCoef(); |     const auto raw_clip_coef = regs.rasterizer.GetClipCoef(); | ||||||
|     const Common::Vec4f new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(), |     const Common::Vec4f new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(), | ||||||
|                                          raw_clip_coef.z.ToFloat32(), raw_clip_coef.w.ToFloat32()}; |                                          raw_clip_coef.z.ToFloat32(), raw_clip_coef.w.ToFloat32()}; | ||||||
|     if (new_clip_coef != uniform_block_data.data.clip_coef) { |     if (enable_clip1 != vs_uniform_block_data.data.enable_clip1 || | ||||||
|         uniform_block_data.data.clip_coef = new_clip_coef; |         new_clip_coef != vs_uniform_block_data.data.clip_coef) { | ||||||
|         uniform_block_data.dirty = true; |         vs_uniform_block_data.data.enable_clip1 = enable_clip1; | ||||||
|  |         vs_uniform_block_data.data.clip_coef = new_clip_coef; | ||||||
|  |         vs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ | ||||||
| #include "common/vector_math.h" | #include "common/vector_math.h" | ||||||
| #include "video_core/rasterizer_interface.h" | #include "video_core/rasterizer_interface.h" | ||||||
| #include "video_core/regs_texturing.h" | #include "video_core/regs_texturing.h" | ||||||
| #include "video_core/shader/shader_uniforms.h" | #include "video_core/shader/generator/shader_uniforms.h" | ||||||
| 
 | 
 | ||||||
| namespace Memory { | namespace Memory { | ||||||
| class MemorySystem; | class MemorySystem; | ||||||
|  | @ -100,13 +100,19 @@ protected: | ||||||
|     /// Syncs the texture border color to match the PICA registers
 |     /// Syncs the texture border color to match the PICA registers
 | ||||||
|     void SyncTextureBorderColor(int tex_index); |     void SyncTextureBorderColor(int tex_index); | ||||||
| 
 | 
 | ||||||
|     /// Syncs the clip coefficients to match the PICA register
 |     /// Syncs the clip plane state to match the PICA register
 | ||||||
|     void SyncClipCoef(); |     void SyncClipPlane(); | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     /// Structure that keeps tracks of the uniform state
 |     /// Structure that keeps tracks of the vertex shader uniform state
 | ||||||
|     struct UniformBlockData { |     struct VSUniformBlockData { | ||||||
|         Pica::Shader::UniformData data{}; |         Pica::Shader::Generator::VSUniformData data{}; | ||||||
|  |         bool dirty = true; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /// Structure that keeps tracks of the fragment shader uniform state
 | ||||||
|  |     struct FSUniformBlockData { | ||||||
|  |         Pica::Shader::Generator::FSUniformData data{}; | ||||||
|         std::array<bool, Pica::LightingRegs::NumLightingSampler> lighting_lut_dirty{}; |         std::array<bool, Pica::LightingRegs::NumLightingSampler> lighting_lut_dirty{}; | ||||||
|         bool lighting_lut_dirty_any = true; |         bool lighting_lut_dirty_any = true; | ||||||
|         bool fog_lut_dirty = true; |         bool fog_lut_dirty = true; | ||||||
|  | @ -149,7 +155,8 @@ protected: | ||||||
|     std::vector<HardwareVertex> vertex_batch; |     std::vector<HardwareVertex> vertex_batch; | ||||||
|     bool shader_dirty = true; |     bool shader_dirty = true; | ||||||
| 
 | 
 | ||||||
|     UniformBlockData uniform_block_data{}; |     VSUniformBlockData vs_uniform_block_data{}; | ||||||
|  |     FSUniformBlockData fs_uniform_block_data{}; | ||||||
|     std::array<std::array<Common::Vec2f, 256>, Pica::LightingRegs::NumLightingSampler> |     std::array<std::array<Common::Vec2f, 256>, Pica::LightingRegs::NumLightingSampler> | ||||||
|         lighting_lut_data{}; |         lighting_lut_data{}; | ||||||
|     std::array<Common::Vec2f, 128> fog_lut_data{}; |     std::array<Common::Vec2f, 128> fog_lut_data{}; | ||||||
|  |  | ||||||
|  | @ -168,7 +168,7 @@ void Driver::CheckExtensionSupport() { | ||||||
|     arb_clear_texture = GLAD_GL_ARB_clear_texture; |     arb_clear_texture = GLAD_GL_ARB_clear_texture; | ||||||
|     arb_get_texture_sub_image = GLAD_GL_ARB_get_texture_sub_image; |     arb_get_texture_sub_image = GLAD_GL_ARB_get_texture_sub_image; | ||||||
|     arb_texture_compression_bptc = GLAD_GL_ARB_texture_compression_bptc; |     arb_texture_compression_bptc = GLAD_GL_ARB_texture_compression_bptc; | ||||||
|     ext_clip_cull_distance = GLAD_GL_EXT_clip_cull_distance; |     clip_cull_distance = !is_gles || GLAD_GL_EXT_clip_cull_distance; | ||||||
|     ext_texture_compression_s3tc = GLAD_GL_EXT_texture_compression_s3tc; |     ext_texture_compression_s3tc = GLAD_GL_EXT_texture_compression_s3tc; | ||||||
|     shader_framebuffer_fetch = |     shader_framebuffer_fetch = | ||||||
|         GLAD_GL_EXT_shader_framebuffer_fetch || GLAD_GL_ARM_shader_framebuffer_fetch; |         GLAD_GL_EXT_shader_framebuffer_fetch || GLAD_GL_ARM_shader_framebuffer_fetch; | ||||||
|  |  | ||||||
|  | @ -100,9 +100,9 @@ public: | ||||||
|         return arb_get_texture_sub_image; |         return arb_get_texture_sub_image; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns true if the implementation supports EXT_clip_cull_distance
 |     /// Returns true if the implementation supports shader-defined clipping planes
 | ||||||
|     bool HasExtClipCullDistance() const { |     bool HasClipCullDistance() const { | ||||||
|         return ext_clip_cull_distance; |         return clip_cull_distance; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns true if the implementation supports (EXT/ARM)_shader_framebuffer_fetch
 |     /// Returns true if the implementation supports (EXT/ARM)_shader_framebuffer_fetch
 | ||||||
|  | @ -132,7 +132,7 @@ private: | ||||||
|     bool arb_buffer_storage{}; |     bool arb_buffer_storage{}; | ||||||
|     bool arb_clear_texture{}; |     bool arb_clear_texture{}; | ||||||
|     bool arb_get_texture_sub_image{}; |     bool arb_get_texture_sub_image{}; | ||||||
|     bool ext_clip_cull_distance{}; |     bool clip_cull_distance{}; | ||||||
|     bool ext_texture_compression_s3tc{}; |     bool ext_texture_compression_s3tc{}; | ||||||
|     bool arb_texture_compression_bptc{}; |     bool arb_texture_compression_bptc{}; | ||||||
|     bool shader_framebuffer_fetch{}; |     bool shader_framebuffer_fetch{}; | ||||||
|  |  | ||||||
|  | @ -11,9 +11,9 @@ | ||||||
| #include "video_core/regs_framebuffer.h" | #include "video_core/regs_framebuffer.h" | ||||||
| #include "video_core/regs_rasterizer.h" | #include "video_core/regs_rasterizer.h" | ||||||
| #include "video_core/renderer_opengl/gl_rasterizer.h" | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||||||
| #include "video_core/renderer_opengl/gl_shader_gen.h" |  | ||||||
| #include "video_core/renderer_opengl/pica_to_gl.h" | #include "video_core/renderer_opengl/pica_to_gl.h" | ||||||
| #include "video_core/renderer_opengl/renderer_opengl.h" | #include "video_core/renderer_opengl/renderer_opengl.h" | ||||||
|  | #include "video_core/shader/generator/glsl_shader_gen.h" | ||||||
| #include "video_core/texture/texture_decode.h" | #include "video_core/texture/texture_decode.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
|  | @ -28,6 +28,7 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); | ||||||
| MICROPROFILE_DEFINE(OpenGL_Display, "OpenGL", "Display", MP_RGB(128, 128, 192)); | MICROPROFILE_DEFINE(OpenGL_Display, "OpenGL", "Display", MP_RGB(128, 128, 192)); | ||||||
| 
 | 
 | ||||||
| using VideoCore::SurfaceType; | using VideoCore::SurfaceType; | ||||||
|  | using namespace Pica::Shader::Generator; | ||||||
| 
 | 
 | ||||||
| constexpr std::size_t VERTEX_BUFFER_SIZE = 16 * 1024 * 1024; | constexpr std::size_t VERTEX_BUFFER_SIZE = 16 * 1024 * 1024; | ||||||
| constexpr std::size_t INDEX_BUFFER_SIZE = 2 * 1024 * 1024; | constexpr std::size_t INDEX_BUFFER_SIZE = 2 * 1024 * 1024; | ||||||
|  | @ -95,10 +96,12 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory, | ||||||
|     hw_vao.Create(); |     hw_vao.Create(); | ||||||
| 
 | 
 | ||||||
|     glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); |     glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); | ||||||
|  |     uniform_size_aligned_vs_pica = | ||||||
|  |         Common::AlignUp<std::size_t>(sizeof(VSPicaUniformData), uniform_buffer_alignment); | ||||||
|     uniform_size_aligned_vs = |     uniform_size_aligned_vs = | ||||||
|         Common::AlignUp<std::size_t>(sizeof(Pica::Shader::VSUniformData), uniform_buffer_alignment); |         Common::AlignUp<std::size_t>(sizeof(VSUniformData), uniform_buffer_alignment); | ||||||
|     uniform_size_aligned_fs = |     uniform_size_aligned_fs = | ||||||
|         Common::AlignUp<std::size_t>(sizeof(Pica::Shader::UniformData), uniform_buffer_alignment); |         Common::AlignUp<std::size_t>(sizeof(FSUniformData), uniform_buffer_alignment); | ||||||
| 
 | 
 | ||||||
|     // Set vertex attributes for software shader path
 |     // Set vertex attributes for software shader path
 | ||||||
|     state.draw.vertex_array = sw_vao.handle; |     state.draw.vertex_array = sw_vao.handle; | ||||||
|  | @ -405,16 +408,16 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) { | ||||||
| 
 | 
 | ||||||
|     // Update scissor uniforms
 |     // Update scissor uniforms
 | ||||||
|     const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor(); |     const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor(); | ||||||
|     if (uniform_block_data.data.scissor_x1 != scissor_x1 || |     if (fs_uniform_block_data.data.scissor_x1 != scissor_x1 || | ||||||
|         uniform_block_data.data.scissor_x2 != scissor_x2 || |         fs_uniform_block_data.data.scissor_x2 != scissor_x2 || | ||||||
|         uniform_block_data.data.scissor_y1 != scissor_y1 || |         fs_uniform_block_data.data.scissor_y1 != scissor_y1 || | ||||||
|         uniform_block_data.data.scissor_y2 != scissor_y2) { |         fs_uniform_block_data.data.scissor_y2 != scissor_y2) { | ||||||
| 
 | 
 | ||||||
|         uniform_block_data.data.scissor_x1 = scissor_x1; |         fs_uniform_block_data.data.scissor_x1 = scissor_x1; | ||||||
|         uniform_block_data.data.scissor_x2 = scissor_x2; |         fs_uniform_block_data.data.scissor_x2 = scissor_x2; | ||||||
|         uniform_block_data.data.scissor_y1 = scissor_y1; |         fs_uniform_block_data.data.scissor_y1 = scissor_y1; | ||||||
|         uniform_block_data.data.scissor_y2 = scissor_y2; |         fs_uniform_block_data.data.scissor_y2 = scissor_y2; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sync and bind the texture surfaces
 |     // Sync and bind the texture surfaces
 | ||||||
|  | @ -831,9 +834,9 @@ void RasterizerOpenGL::SyncBlendColor() { | ||||||
|     state.blend.color.blue = blend_color[2]; |     state.blend.color.blue = blend_color[2]; | ||||||
|     state.blend.color.alpha = blend_color[3]; |     state.blend.color.alpha = blend_color[3]; | ||||||
| 
 | 
 | ||||||
|     if (blend_color != uniform_block_data.data.blend_color) { |     if (blend_color != fs_uniform_block_data.data.blend_color) { | ||||||
|         uniform_block_data.data.blend_color = blend_color; |         fs_uniform_block_data.data.blend_color = blend_color; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -921,7 +924,7 @@ void RasterizerOpenGL::SyncAndUploadLUTsLF() { | ||||||
|         sizeof(Common::Vec2f) * 256 * Pica::LightingRegs::NumLightingSampler + |         sizeof(Common::Vec2f) * 256 * Pica::LightingRegs::NumLightingSampler + | ||||||
|         sizeof(Common::Vec2f) * 128; // fog
 |         sizeof(Common::Vec2f) * 128; // fog
 | ||||||
| 
 | 
 | ||||||
|     if (!uniform_block_data.lighting_lut_dirty_any && !uniform_block_data.fog_lut_dirty) { |     if (!fs_uniform_block_data.lighting_lut_dirty_any && !fs_uniform_block_data.fog_lut_dirty) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -931,9 +934,9 @@ void RasterizerOpenGL::SyncAndUploadLUTsLF() { | ||||||
|         texture_lf_buffer.Map(max_size, sizeof(Common::Vec4f)); |         texture_lf_buffer.Map(max_size, sizeof(Common::Vec4f)); | ||||||
| 
 | 
 | ||||||
|     // Sync the lighting luts
 |     // Sync the lighting luts
 | ||||||
|     if (uniform_block_data.lighting_lut_dirty_any || invalidate) { |     if (fs_uniform_block_data.lighting_lut_dirty_any || invalidate) { | ||||||
|         for (unsigned index = 0; index < uniform_block_data.lighting_lut_dirty.size(); index++) { |         for (unsigned index = 0; index < fs_uniform_block_data.lighting_lut_dirty.size(); index++) { | ||||||
|             if (uniform_block_data.lighting_lut_dirty[index] || invalidate) { |             if (fs_uniform_block_data.lighting_lut_dirty[index] || invalidate) { | ||||||
|                 std::array<Common::Vec2f, 256> new_data; |                 std::array<Common::Vec2f, 256> new_data; | ||||||
|                 const auto& source_lut = Pica::g_state.lighting.luts[index]; |                 const auto& source_lut = Pica::g_state.lighting.luts[index]; | ||||||
|                 std::transform(source_lut.begin(), source_lut.end(), new_data.begin(), |                 std::transform(source_lut.begin(), source_lut.end(), new_data.begin(), | ||||||
|  | @ -945,19 +948,19 @@ void RasterizerOpenGL::SyncAndUploadLUTsLF() { | ||||||
|                     lighting_lut_data[index] = new_data; |                     lighting_lut_data[index] = new_data; | ||||||
|                     std::memcpy(buffer + bytes_used, new_data.data(), |                     std::memcpy(buffer + bytes_used, new_data.data(), | ||||||
|                                 new_data.size() * sizeof(Common::Vec2f)); |                                 new_data.size() * sizeof(Common::Vec2f)); | ||||||
|                     uniform_block_data.data.lighting_lut_offset[index / 4][index % 4] = |                     fs_uniform_block_data.data.lighting_lut_offset[index / 4][index % 4] = | ||||||
|                         static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec2f)); |                         static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec2f)); | ||||||
|                     uniform_block_data.dirty = true; |                     fs_uniform_block_data.dirty = true; | ||||||
|                     bytes_used += new_data.size() * sizeof(Common::Vec2f); |                     bytes_used += new_data.size() * sizeof(Common::Vec2f); | ||||||
|                 } |                 } | ||||||
|                 uniform_block_data.lighting_lut_dirty[index] = false; |                 fs_uniform_block_data.lighting_lut_dirty[index] = false; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         uniform_block_data.lighting_lut_dirty_any = false; |         fs_uniform_block_data.lighting_lut_dirty_any = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sync the fog lut
 |     // Sync the fog lut
 | ||||||
|     if (uniform_block_data.fog_lut_dirty || invalidate) { |     if (fs_uniform_block_data.fog_lut_dirty || invalidate) { | ||||||
|         std::array<Common::Vec2f, 128> new_data; |         std::array<Common::Vec2f, 128> new_data; | ||||||
| 
 | 
 | ||||||
|         std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(), |         std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(), | ||||||
|  | @ -969,12 +972,12 @@ void RasterizerOpenGL::SyncAndUploadLUTsLF() { | ||||||
|             fog_lut_data = new_data; |             fog_lut_data = new_data; | ||||||
|             std::memcpy(buffer + bytes_used, new_data.data(), |             std::memcpy(buffer + bytes_used, new_data.data(), | ||||||
|                         new_data.size() * sizeof(Common::Vec2f)); |                         new_data.size() * sizeof(Common::Vec2f)); | ||||||
|             uniform_block_data.data.fog_lut_offset = |             fs_uniform_block_data.data.fog_lut_offset = | ||||||
|                 static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f)); |                 static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f)); | ||||||
|             uniform_block_data.dirty = true; |             fs_uniform_block_data.dirty = true; | ||||||
|             bytes_used += new_data.size() * sizeof(Common::Vec2f); |             bytes_used += new_data.size() * sizeof(Common::Vec2f); | ||||||
|         } |         } | ||||||
|         uniform_block_data.fog_lut_dirty = false; |         fs_uniform_block_data.fog_lut_dirty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     texture_lf_buffer.Unmap(bytes_used); |     texture_lf_buffer.Unmap(bytes_used); | ||||||
|  | @ -986,10 +989,10 @@ void RasterizerOpenGL::SyncAndUploadLUTs() { | ||||||
|         sizeof(Common::Vec4f) * 256 +     // proctex
 |         sizeof(Common::Vec4f) * 256 +     // proctex
 | ||||||
|         sizeof(Common::Vec4f) * 256;      // proctex diff
 |         sizeof(Common::Vec4f) * 256;      // proctex diff
 | ||||||
| 
 | 
 | ||||||
|     if (!uniform_block_data.proctex_noise_lut_dirty && |     if (!fs_uniform_block_data.proctex_noise_lut_dirty && | ||||||
|         !uniform_block_data.proctex_color_map_dirty && |         !fs_uniform_block_data.proctex_color_map_dirty && | ||||||
|         !uniform_block_data.proctex_alpha_map_dirty && !uniform_block_data.proctex_lut_dirty && |         !fs_uniform_block_data.proctex_alpha_map_dirty && | ||||||
|         !uniform_block_data.proctex_diff_lut_dirty) { |         !fs_uniform_block_data.proctex_lut_dirty && !fs_uniform_block_data.proctex_diff_lut_dirty) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1012,34 +1015,34 @@ void RasterizerOpenGL::SyncAndUploadLUTs() { | ||||||
|                 std::memcpy(buffer + bytes_used, new_data.data(), |                 std::memcpy(buffer + bytes_used, new_data.data(), | ||||||
|                             new_data.size() * sizeof(Common::Vec2f)); |                             new_data.size() * sizeof(Common::Vec2f)); | ||||||
|                 lut_offset = static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec2f)); |                 lut_offset = static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec2f)); | ||||||
|                 uniform_block_data.dirty = true; |                 fs_uniform_block_data.dirty = true; | ||||||
|                 bytes_used += new_data.size() * sizeof(Common::Vec2f); |                 bytes_used += new_data.size() * sizeof(Common::Vec2f); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|     // Sync the proctex noise lut
 |     // Sync the proctex noise lut
 | ||||||
|     if (uniform_block_data.proctex_noise_lut_dirty || invalidate) { |     if (fs_uniform_block_data.proctex_noise_lut_dirty || invalidate) { | ||||||
|         sync_proc_tex_value_lut(Pica::g_state.proctex.noise_table, proctex_noise_lut_data, |         sync_proc_tex_value_lut(Pica::g_state.proctex.noise_table, proctex_noise_lut_data, | ||||||
|                                 uniform_block_data.data.proctex_noise_lut_offset); |                                 fs_uniform_block_data.data.proctex_noise_lut_offset); | ||||||
|         uniform_block_data.proctex_noise_lut_dirty = false; |         fs_uniform_block_data.proctex_noise_lut_dirty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sync the proctex color map
 |     // Sync the proctex color map
 | ||||||
|     if (uniform_block_data.proctex_color_map_dirty || invalidate) { |     if (fs_uniform_block_data.proctex_color_map_dirty || invalidate) { | ||||||
|         sync_proc_tex_value_lut(Pica::g_state.proctex.color_map_table, proctex_color_map_data, |         sync_proc_tex_value_lut(Pica::g_state.proctex.color_map_table, proctex_color_map_data, | ||||||
|                                 uniform_block_data.data.proctex_color_map_offset); |                                 fs_uniform_block_data.data.proctex_color_map_offset); | ||||||
|         uniform_block_data.proctex_color_map_dirty = false; |         fs_uniform_block_data.proctex_color_map_dirty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sync the proctex alpha map
 |     // Sync the proctex alpha map
 | ||||||
|     if (uniform_block_data.proctex_alpha_map_dirty || invalidate) { |     if (fs_uniform_block_data.proctex_alpha_map_dirty || invalidate) { | ||||||
|         sync_proc_tex_value_lut(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data, |         sync_proc_tex_value_lut(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data, | ||||||
|                                 uniform_block_data.data.proctex_alpha_map_offset); |                                 fs_uniform_block_data.data.proctex_alpha_map_offset); | ||||||
|         uniform_block_data.proctex_alpha_map_dirty = false; |         fs_uniform_block_data.proctex_alpha_map_dirty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sync the proctex lut
 |     // Sync the proctex lut
 | ||||||
|     if (uniform_block_data.proctex_lut_dirty || invalidate) { |     if (fs_uniform_block_data.proctex_lut_dirty || invalidate) { | ||||||
|         std::array<Common::Vec4f, 256> new_data; |         std::array<Common::Vec4f, 256> new_data; | ||||||
| 
 | 
 | ||||||
|         std::transform(Pica::g_state.proctex.color_table.begin(), |         std::transform(Pica::g_state.proctex.color_table.begin(), | ||||||
|  | @ -1053,16 +1056,16 @@ void RasterizerOpenGL::SyncAndUploadLUTs() { | ||||||
|             proctex_lut_data = new_data; |             proctex_lut_data = new_data; | ||||||
|             std::memcpy(buffer + bytes_used, new_data.data(), |             std::memcpy(buffer + bytes_used, new_data.data(), | ||||||
|                         new_data.size() * sizeof(Common::Vec4f)); |                         new_data.size() * sizeof(Common::Vec4f)); | ||||||
|             uniform_block_data.data.proctex_lut_offset = |             fs_uniform_block_data.data.proctex_lut_offset = | ||||||
|                 static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec4f)); |                 static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec4f)); | ||||||
|             uniform_block_data.dirty = true; |             fs_uniform_block_data.dirty = true; | ||||||
|             bytes_used += new_data.size() * sizeof(Common::Vec4f); |             bytes_used += new_data.size() * sizeof(Common::Vec4f); | ||||||
|         } |         } | ||||||
|         uniform_block_data.proctex_lut_dirty = false; |         fs_uniform_block_data.proctex_lut_dirty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sync the proctex difference lut
 |     // Sync the proctex difference lut
 | ||||||
|     if (uniform_block_data.proctex_diff_lut_dirty || invalidate) { |     if (fs_uniform_block_data.proctex_diff_lut_dirty || invalidate) { | ||||||
|         std::array<Common::Vec4f, 256> new_data; |         std::array<Common::Vec4f, 256> new_data; | ||||||
| 
 | 
 | ||||||
|         std::transform(Pica::g_state.proctex.color_diff_table.begin(), |         std::transform(Pica::g_state.proctex.color_diff_table.begin(), | ||||||
|  | @ -1076,12 +1079,12 @@ void RasterizerOpenGL::SyncAndUploadLUTs() { | ||||||
|             proctex_diff_lut_data = new_data; |             proctex_diff_lut_data = new_data; | ||||||
|             std::memcpy(buffer + bytes_used, new_data.data(), |             std::memcpy(buffer + bytes_used, new_data.data(), | ||||||
|                         new_data.size() * sizeof(Common::Vec4f)); |                         new_data.size() * sizeof(Common::Vec4f)); | ||||||
|             uniform_block_data.data.proctex_diff_lut_offset = |             fs_uniform_block_data.data.proctex_diff_lut_offset = | ||||||
|                 static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec4f)); |                 static_cast<GLint>((offset + bytes_used) / sizeof(Common::Vec4f)); | ||||||
|             uniform_block_data.dirty = true; |             fs_uniform_block_data.dirty = true; | ||||||
|             bytes_used += new_data.size() * sizeof(Common::Vec4f); |             bytes_used += new_data.size() * sizeof(Common::Vec4f); | ||||||
|         } |         } | ||||||
|         uniform_block_data.proctex_diff_lut_dirty = false; |         fs_uniform_block_data.proctex_diff_lut_dirty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     texture_buffer.Unmap(bytes_used); |     texture_buffer.Unmap(bytes_used); | ||||||
|  | @ -1092,38 +1095,47 @@ void RasterizerOpenGL::UploadUniforms(bool accelerate_draw) { | ||||||
|     state.draw.uniform_buffer = uniform_buffer.GetHandle(); |     state.draw.uniform_buffer = uniform_buffer.GetHandle(); | ||||||
|     state.Apply(); |     state.Apply(); | ||||||
| 
 | 
 | ||||||
|     const bool sync_vs = accelerate_draw; |     const bool sync_vs_pica = accelerate_draw; | ||||||
|     const bool sync_fs = uniform_block_data.dirty; |     const bool sync_vs = vs_uniform_block_data.dirty; | ||||||
|     if (!sync_vs && !sync_fs) { |     const bool sync_fs = fs_uniform_block_data.dirty; | ||||||
|  |     if (!sync_vs_pica && !sync_vs && !sync_fs) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::size_t uniform_size = uniform_size_aligned_vs + uniform_size_aligned_fs; |     std::size_t uniform_size = | ||||||
|  |         uniform_size_aligned_vs_pica + uniform_size_aligned_vs + uniform_size_aligned_fs; | ||||||
|     std::size_t used_bytes = 0; |     std::size_t used_bytes = 0; | ||||||
| 
 | 
 | ||||||
|     const auto [uniforms, offset, invalidate] = |     const auto [uniforms, offset, invalidate] = | ||||||
|         uniform_buffer.Map(uniform_size, uniform_buffer_alignment); |         uniform_buffer.Map(uniform_size, uniform_buffer_alignment); | ||||||
| 
 | 
 | ||||||
|     if (sync_vs) { |     if (sync_vs || invalidate) { | ||||||
|         Pica::Shader::VSUniformData vs_uniforms; |         std::memcpy(uniforms + used_bytes, &vs_uniform_block_data.data, | ||||||
|         vs_uniforms.uniforms.SetFromRegs(regs.vs, Pica::g_state.vs); |                     sizeof(vs_uniform_block_data.data)); | ||||||
|         std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms)); |         glBindBufferRange(GL_UNIFORM_BUFFER, UniformBindings::VSData, uniform_buffer.GetHandle(), | ||||||
|         glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(Pica::Shader::UniformBindings::VS), |                           offset + used_bytes, sizeof(vs_uniform_block_data.data)); | ||||||
|                           uniform_buffer.GetHandle(), offset + used_bytes, |         vs_uniform_block_data.dirty = false; | ||||||
|                           sizeof(Pica::Shader::VSUniformData)); |  | ||||||
|         used_bytes += uniform_size_aligned_vs; |         used_bytes += uniform_size_aligned_vs; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (sync_fs || invalidate) { |     if (sync_fs || invalidate) { | ||||||
|         std::memcpy(uniforms + used_bytes, &uniform_block_data.data, |         std::memcpy(uniforms + used_bytes, &fs_uniform_block_data.data, | ||||||
|                     sizeof(Pica::Shader::UniformData)); |                     sizeof(fs_uniform_block_data.data)); | ||||||
|         glBindBufferRange( |         glBindBufferRange(GL_UNIFORM_BUFFER, UniformBindings::FSData, uniform_buffer.GetHandle(), | ||||||
|             GL_UNIFORM_BUFFER, static_cast<GLuint>(Pica::Shader::UniformBindings::Common), |                           offset + used_bytes, sizeof(fs_uniform_block_data.data)); | ||||||
|             uniform_buffer.GetHandle(), offset + used_bytes, sizeof(Pica::Shader::UniformData)); |         fs_uniform_block_data.dirty = false; | ||||||
|         uniform_block_data.dirty = false; |  | ||||||
|         used_bytes += uniform_size_aligned_fs; |         used_bytes += uniform_size_aligned_fs; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (sync_vs_pica) { | ||||||
|  |         VSPicaUniformData vs_uniforms; | ||||||
|  |         vs_uniforms.uniforms.SetFromRegs(regs.vs, Pica::g_state.vs); | ||||||
|  |         std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms)); | ||||||
|  |         glBindBufferRange(GL_UNIFORM_BUFFER, UniformBindings::VSPicaData, | ||||||
|  |                           uniform_buffer.GetHandle(), offset + used_bytes, sizeof(vs_uniforms)); | ||||||
|  |         used_bytes += uniform_size_aligned_vs_pica; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     uniform_buffer.Unmap(used_bytes); |     uniform_buffer.Unmap(used_bytes); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -148,6 +148,7 @@ private: | ||||||
|     OGLStreamBuffer texture_buffer; |     OGLStreamBuffer texture_buffer; | ||||||
|     OGLStreamBuffer texture_lf_buffer; |     OGLStreamBuffer texture_lf_buffer; | ||||||
|     GLint uniform_buffer_alignment; |     GLint uniform_buffer_alignment; | ||||||
|  |     std::size_t uniform_size_aligned_vs_pica; | ||||||
|     std::size_t uniform_size_aligned_vs; |     std::size_t uniform_size_aligned_vs; | ||||||
|     std::size_t uniform_size_aligned_fs; |     std::size_t uniform_size_aligned_fs; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| // Copyright 2017 Citra Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <array> |  | ||||||
| #include <functional> |  | ||||||
| #include <optional> |  | ||||||
| #include <string> |  | ||||||
| #include "common/common_types.h" |  | ||||||
| #include "video_core/shader/shader.h" |  | ||||||
| 
 |  | ||||||
| namespace OpenGL::ShaderDecompiler { |  | ||||||
| 
 |  | ||||||
| using RegGetter = std::function<std::string(u32)>; |  | ||||||
| 
 |  | ||||||
| struct ProgramResult { |  | ||||||
|     std::string code; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| std::string GetCommonDeclarations(); |  | ||||||
| 
 |  | ||||||
| std::optional<ProgramResult> DecompileProgram(const Pica::Shader::ProgramCode& program_code, |  | ||||||
|                                               const Pica::Shader::SwizzleData& swizzle_data, |  | ||||||
|                                               u32 main_offset, const RegGetter& inputreg_getter, |  | ||||||
|                                               const RegGetter& outputreg_getter, bool sanitize_mul); |  | ||||||
| 
 |  | ||||||
| } // namespace OpenGL::ShaderDecompiler
 |  | ||||||
|  | @ -297,35 +297,33 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCache::LoadDecompiledEntry() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ShaderDiskCacheDecompiled entry; |     ShaderDiskCacheDecompiled entry; | ||||||
|     entry.result.code = std::move(code); |     entry.code = std::move(code); | ||||||
|     entry.sanitize_mul = sanitize_mul; |     entry.sanitize_mul = sanitize_mul; | ||||||
| 
 | 
 | ||||||
|     return entry; |     return entry; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ShaderDiskCache::SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier, | void ShaderDiskCache::SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier, | ||||||
|                                            const ShaderDecompiler::ProgramResult& result, |                                            const std::string& code, bool sanitize_mul) { | ||||||
|                                            bool sanitize_mul) { |  | ||||||
|     if (!IsUsable()) |     if (!IsUsable()) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Decompiled)) != 1 || |     if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Decompiled)) != 1 || | ||||||
|         file.WriteObject(unique_identifier) != 1 || file.WriteObject(sanitize_mul) != 1 || |         file.WriteObject(unique_identifier) != 1 || file.WriteObject(sanitize_mul) != 1 || | ||||||
|         file.WriteObject(static_cast<u32>(result.code.size())) != 1 || |         file.WriteObject(static_cast<u32>(code.size())) != 1 || | ||||||
|         file.WriteArray(result.code.data(), result.code.size()) != result.code.size()) { |         file.WriteArray(code.data(), code.size()) != code.size()) { | ||||||
|         LOG_ERROR(Render_OpenGL, "Failed to save decompiled cache entry - removing"); |         LOG_ERROR(Render_OpenGL, "Failed to save decompiled cache entry - removing"); | ||||||
|         file.Close(); |         file.Close(); | ||||||
|         InvalidatePrecompiled(); |         InvalidatePrecompiled(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ShaderDiskCache::SaveDecompiledToCache(u64 unique_identifier, | bool ShaderDiskCache::SaveDecompiledToCache(u64 unique_identifier, const std::string& code, | ||||||
|                                             const ShaderDecompiler::ProgramResult& result, |  | ||||||
|                                             bool sanitize_mul) { |                                             bool sanitize_mul) { | ||||||
|     if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Decompiled)) || |     if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Decompiled)) || | ||||||
|         !SaveObjectToPrecompiled(unique_identifier) || !SaveObjectToPrecompiled(sanitize_mul) || |         !SaveObjectToPrecompiled(unique_identifier) || !SaveObjectToPrecompiled(sanitize_mul) || | ||||||
|         !SaveObjectToPrecompiled(static_cast<u32>(result.code.size())) || |         !SaveObjectToPrecompiled(static_cast<u32>(code.size())) || | ||||||
|         !SaveArrayToPrecompiled(result.code.data(), result.code.size())) { |         !SaveArrayToPrecompiled(code.data(), code.size())) { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -374,8 +372,7 @@ void ShaderDiskCache::SaveRaw(const ShaderDiskCacheRaw& entry) { | ||||||
|     transferable_file.Flush(); |     transferable_file.Flush(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ShaderDiskCache::SaveDecompiled(u64 unique_identifier, | void ShaderDiskCache::SaveDecompiled(u64 unique_identifier, const std::string& code, | ||||||
|                                      const ShaderDecompiler::ProgramResult& code, |  | ||||||
|                                      bool sanitize_mul) { |                                      bool sanitize_mul) { | ||||||
|     if (!IsUsable()) |     if (!IsUsable()) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|  | @ -20,8 +20,7 @@ | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| #include "video_core/regs.h" | #include "video_core/regs.h" | ||||||
| #include "video_core/renderer_opengl/gl_shader_decompiler.h" | #include "video_core/shader/generator/glsl_shader_gen.h" | ||||||
| #include "video_core/renderer_opengl/gl_shader_gen.h" |  | ||||||
| 
 | 
 | ||||||
| namespace Core { | namespace Core { | ||||||
| class System; | class System; | ||||||
|  | @ -38,6 +37,7 @@ struct ShaderDiskCacheDump; | ||||||
| 
 | 
 | ||||||
| using RawShaderConfig = Pica::Regs; | using RawShaderConfig = Pica::Regs; | ||||||
| using ProgramCode = std::vector<u32>; | using ProgramCode = std::vector<u32>; | ||||||
|  | using ProgramType = Pica::Shader::Generator::ProgramType; | ||||||
| using ShaderDecompiledMap = std::unordered_map<u64, ShaderDiskCacheDecompiled>; | using ShaderDecompiledMap = std::unordered_map<u64, ShaderDiskCacheDecompiled>; | ||||||
| using ShaderDumpsMap = std::unordered_map<u64, ShaderDiskCacheDump>; | using ShaderDumpsMap = std::unordered_map<u64, ShaderDiskCacheDump>; | ||||||
| 
 | 
 | ||||||
|  | @ -78,7 +78,7 @@ private: | ||||||
| 
 | 
 | ||||||
| /// Contains decompiled data from a shader
 | /// Contains decompiled data from a shader
 | ||||||
| struct ShaderDiskCacheDecompiled { | struct ShaderDiskCacheDecompiled { | ||||||
|     ShaderDecompiler::ProgramResult result; |     std::string code; | ||||||
|     bool sanitize_mul; |     bool sanitize_mul; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -109,8 +109,7 @@ public: | ||||||
|     void SaveRaw(const ShaderDiskCacheRaw& entry); |     void SaveRaw(const ShaderDiskCacheRaw& entry); | ||||||
| 
 | 
 | ||||||
|     /// Saves a decompiled entry to the precompiled file. Does not check for collisions.
 |     /// Saves a decompiled entry to the precompiled file. Does not check for collisions.
 | ||||||
|     void SaveDecompiled(u64 unique_identifier, const ShaderDecompiler::ProgramResult& code, |     void SaveDecompiled(u64 unique_identifier, const std::string& code, bool sanitize_mul); | ||||||
|                         bool sanitize_mul); |  | ||||||
| 
 | 
 | ||||||
|     /// 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(u64 unique_identifier, GLuint program); |     void SaveDump(u64 unique_identifier, GLuint program); | ||||||
|  | @ -132,11 +131,10 @@ private: | ||||||
| 
 | 
 | ||||||
|     /// Saves a decompiled entry to the passed file. Does not check for collisions.
 |     /// Saves a decompiled entry to the passed file. Does not check for collisions.
 | ||||||
|     void SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier, |     void SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier, | ||||||
|                               const ShaderDecompiler::ProgramResult& code, bool sanitize_mul); |                               const std::string& code, bool sanitize_mul); | ||||||
| 
 | 
 | ||||||
|     /// Saves a decompiled entry to the virtual precompiled cache. Does not check for collisions.
 |     /// Saves a decompiled entry to the virtual precompiled cache. Does not check for collisions.
 | ||||||
|     bool SaveDecompiledToCache(u64 unique_identifier, const ShaderDecompiler::ProgramResult& code, |     bool SaveDecompiledToCache(u64 unique_identifier, const std::string& code, bool sanitize_mul); | ||||||
|                                bool sanitize_mul); |  | ||||||
| 
 | 
 | ||||||
|     /// Returns if the cache can be used
 |     /// Returns if the cache can be used
 | ||||||
|     bool IsUsable() const; |     bool IsUsable() const; | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1,266 +0,0 @@ | ||||||
| // Copyright 2015 Citra Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| #include <functional> |  | ||||||
| #include <optional> |  | ||||||
| #include "common/hash.h" |  | ||||||
| #include "video_core/regs.h" |  | ||||||
| #include "video_core/shader/shader.h" |  | ||||||
| 
 |  | ||||||
| namespace OpenGL { |  | ||||||
| 
 |  | ||||||
| class Driver; |  | ||||||
| 
 |  | ||||||
| namespace ShaderDecompiler { |  | ||||||
| struct ProgramResult; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| enum class ProgramType : u32 { VS, GS, FS }; |  | ||||||
| 
 |  | ||||||
| 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 { |  | ||||||
|     Pica::FramebufferRegs::CompareFunc alpha_test_func; |  | ||||||
|     Pica::RasterizerRegs::ScissorMode scissor_test_mode; |  | ||||||
|     Pica::TexturingRegs::TextureConfig::TextureType texture0_type; |  | ||||||
|     bool texture2_use_coord1; |  | ||||||
|     std::array<TevStageConfigRaw, 6> tev_stages; |  | ||||||
|     u8 combiner_buffer_input; |  | ||||||
| 
 |  | ||||||
|     Pica::RasterizerRegs::DepthBuffering depthmap_enable; |  | ||||||
|     Pica::TexturingRegs::FogMode fog_mode; |  | ||||||
|     bool fog_flip; |  | ||||||
|     bool alphablend_enable; |  | ||||||
|     Pica::FramebufferRegs::LogicOp logic_op; |  | ||||||
| 
 |  | ||||||
|     struct { |  | ||||||
|         struct { |  | ||||||
|             unsigned num; |  | ||||||
|             bool directional; |  | ||||||
|             bool two_sided_diffuse; |  | ||||||
|             bool dist_atten_enable; |  | ||||||
|             bool spot_atten_enable; |  | ||||||
|             bool geometric_factor_0; |  | ||||||
|             bool geometric_factor_1; |  | ||||||
|             bool shadow_enable; |  | ||||||
|         } light[8]; |  | ||||||
| 
 |  | ||||||
|         bool enable; |  | ||||||
|         unsigned src_num; |  | ||||||
|         Pica::LightingRegs::LightingBumpMode bump_mode; |  | ||||||
|         unsigned bump_selector; |  | ||||||
|         bool bump_renorm; |  | ||||||
|         bool clamp_highlights; |  | ||||||
| 
 |  | ||||||
|         Pica::LightingRegs::LightingConfig config; |  | ||||||
|         bool enable_primary_alpha; |  | ||||||
|         bool enable_secondary_alpha; |  | ||||||
| 
 |  | ||||||
|         bool enable_shadow; |  | ||||||
|         bool shadow_primary; |  | ||||||
|         bool shadow_secondary; |  | ||||||
|         bool shadow_invert; |  | ||||||
|         bool shadow_alpha; |  | ||||||
|         unsigned shadow_selector; |  | ||||||
| 
 |  | ||||||
|         struct { |  | ||||||
|             bool enable; |  | ||||||
|             bool abs_input; |  | ||||||
|             Pica::LightingRegs::LightingLutInput type; |  | ||||||
|             float scale; |  | ||||||
|         } lut_d0, lut_d1, lut_sp, lut_fr, lut_rr, lut_rg, lut_rb; |  | ||||||
|     } lighting; |  | ||||||
| 
 |  | ||||||
|     struct { |  | ||||||
|         bool enable; |  | ||||||
|         u32 coord; |  | ||||||
|         Pica::TexturingRegs::ProcTexClamp u_clamp, v_clamp; |  | ||||||
|         Pica::TexturingRegs::ProcTexCombiner color_combiner, alpha_combiner; |  | ||||||
|         bool separate_alpha; |  | ||||||
|         bool noise_enable; |  | ||||||
|         Pica::TexturingRegs::ProcTexShift u_shift, v_shift; |  | ||||||
|         u32 lut_width; |  | ||||||
|         u32 lut_offset0; |  | ||||||
|         u32 lut_offset1; |  | ||||||
|         u32 lut_offset2; |  | ||||||
|         u32 lut_offset3; |  | ||||||
|         u32 lod_min; |  | ||||||
|         u32 lod_max; |  | ||||||
|         Pica::TexturingRegs::ProcTexFilter lut_filter; |  | ||||||
|     } proctex; |  | ||||||
| 
 |  | ||||||
|     struct { |  | ||||||
|         bool emulate_blending; |  | ||||||
|         Pica::FramebufferRegs::BlendEquation eq; |  | ||||||
|         Pica::FramebufferRegs::BlendFactor src_factor; |  | ||||||
|         Pica::FramebufferRegs::BlendFactor dst_factor; |  | ||||||
|     } rgb_blend, alpha_blend; |  | ||||||
| 
 |  | ||||||
|     bool shadow_rendering; |  | ||||||
|     bool shadow_texture_orthographic; |  | ||||||
|     bool use_custom_normal_map; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * 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> { |  | ||||||
| 
 |  | ||||||
|     /// Construct a PicaFSConfig with the given Pica register configuration.
 |  | ||||||
|     static PicaFSConfig BuildFromRegs(const Pica::Regs& regs, bool has_blend_minmax_factor, |  | ||||||
|                                       bool use_normal = false); |  | ||||||
| 
 |  | ||||||
|     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)); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * This struct contains common information to identify a GL vertex/geometry shader generated from |  | ||||||
|  * PICA vertex/geometry shader. |  | ||||||
|  */ |  | ||||||
| struct PicaShaderConfigCommon { |  | ||||||
|     void Init(const Pica::ShaderRegs& regs, Pica::Shader::ShaderSetup& setup); |  | ||||||
| 
 |  | ||||||
|     u64 program_hash; |  | ||||||
|     u64 swizzle_hash; |  | ||||||
|     u32 main_offset; |  | ||||||
|     bool sanitize_mul; |  | ||||||
| 
 |  | ||||||
|     u32 num_outputs; |  | ||||||
| 
 |  | ||||||
|     // output_map[output register index] -> output attribute index
 |  | ||||||
|     std::array<u32, 16> output_map; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * This struct contains information to identify a GL vertex shader generated from PICA vertex |  | ||||||
|  * shader. |  | ||||||
|  */ |  | ||||||
| struct PicaVSConfig : Common::HashableStruct<PicaShaderConfigCommon> { |  | ||||||
|     explicit PicaVSConfig(const Pica::ShaderRegs& regs, Pica::Shader::ShaderSetup& setup) { |  | ||||||
|         state.Init(regs, setup); |  | ||||||
|     } |  | ||||||
|     explicit PicaVSConfig(const PicaShaderConfigCommon& conf) { |  | ||||||
|         state = conf; |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 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) { |  | ||||||
|         state.Init(regs); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * 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 |  | ||||||
|  */ |  | ||||||
| ShaderDecompiler::ProgramResult GenerateTrivialVertexShader(bool separable_shader); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * 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<ShaderDecompiler::ProgramResult> GenerateVertexShader( |  | ||||||
|     const Pica::Shader::ShaderSetup& setup, const PicaVSConfig& config, bool separable_shader); |  | ||||||
| 
 |  | ||||||
| /*
 |  | ||||||
|  * Generates the GLSL fixed geometry shader program source code for non-GS PICA pipeline |  | ||||||
|  * @returns String of the shader source code |  | ||||||
|  */ |  | ||||||
| ShaderDecompiler::ProgramResult GenerateFixedGeometryShader(const PicaFixedGSConfig& config, |  | ||||||
|                                                             bool separable_shader); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * 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 |  | ||||||
|  */ |  | ||||||
| ShaderDecompiler::ProgramResult GenerateFragmentShader(const PicaFSConfig& config, |  | ||||||
|                                                        bool separable_shader); |  | ||||||
| 
 |  | ||||||
| } // namespace OpenGL
 |  | ||||||
| 
 |  | ||||||
| namespace std { |  | ||||||
| template <> |  | ||||||
| struct hash<OpenGL::PicaFSConfig> { |  | ||||||
|     std::size_t operator()(const OpenGL::PicaFSConfig& k) const noexcept { |  | ||||||
|         return k.Hash(); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| template <> |  | ||||||
| struct hash<OpenGL::PicaVSConfig> { |  | ||||||
|     std::size_t operator()(const OpenGL::PicaVSConfig& k) const noexcept { |  | ||||||
|         return k.Hash(); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| template <> |  | ||||||
| struct hash<OpenGL::PicaFixedGSConfig> { |  | ||||||
|     std::size_t operator()(const OpenGL::PicaFixedGSConfig& k) const noexcept { |  | ||||||
|         return k.Hash(); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| } // namespace std
 |  | ||||||
|  | @ -14,9 +14,11 @@ | ||||||
| #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_shader_manager.h" | #include "video_core/renderer_opengl/gl_shader_manager.h" | ||||||
| #include "video_core/renderer_opengl/gl_state.h" | #include "video_core/renderer_opengl/gl_state.h" | ||||||
| #include "video_core/shader/shader_uniforms.h" | #include "video_core/shader/generator/shader_uniforms.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
|  | using namespace Pica::Shader::Generator; | ||||||
|  | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
| static u64 GetUniqueIdentifier(const Pica::Regs& regs, const ProgramCode& code) { | static u64 GetUniqueIdentifier(const Pica::Regs& regs, const ProgramCode& code) { | ||||||
|  | @ -74,7 +76,7 @@ static std::set<GLenum> GetSupportedFormats() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static std::tuple<PicaVSConfig, Pica::Shader::ShaderSetup> BuildVSConfigFromRaw( | static std::tuple<PicaVSConfig, Pica::Shader::ShaderSetup> BuildVSConfigFromRaw( | ||||||
|     const ShaderDiskCacheRaw& raw) { |     const ShaderDiskCacheRaw& raw, const Driver& driver) { | ||||||
|     Pica::Shader::ProgramCode program_code{}; |     Pica::Shader::ProgramCode program_code{}; | ||||||
|     Pica::Shader::SwizzleData swizzle_data{}; |     Pica::Shader::SwizzleData swizzle_data{}; | ||||||
|     std::copy_n(raw.GetProgramCode().begin(), Pica::Shader::MAX_PROGRAM_CODE_LENGTH, |     std::copy_n(raw.GetProgramCode().begin(), Pica::Shader::MAX_PROGRAM_CODE_LENGTH, | ||||||
|  | @ -84,7 +86,8 @@ static std::tuple<PicaVSConfig, Pica::Shader::ShaderSetup> BuildVSConfigFromRaw( | ||||||
|     Pica::Shader::ShaderSetup setup; |     Pica::Shader::ShaderSetup setup; | ||||||
|     setup.program_code = program_code; |     setup.program_code = program_code; | ||||||
|     setup.swizzle_data = swizzle_data; |     setup.swizzle_data = swizzle_data; | ||||||
|     return {PicaVSConfig{raw.GetRawShaderConfig().vs, setup}, setup}; |     return {PicaVSConfig{raw.GetRawShaderConfig(), setup, driver.HasClipCullDistance(), true}, | ||||||
|  |             setup}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -130,8 +133,10 @@ private: | ||||||
| 
 | 
 | ||||||
| class TrivialVertexShader { | class TrivialVertexShader { | ||||||
| public: | public: | ||||||
|     explicit TrivialVertexShader(bool separable) : program(separable) { |     explicit TrivialVertexShader(const Driver& driver, bool separable) : program(separable) { | ||||||
|         program.Create(GenerateTrivialVertexShader(separable).code.c_str(), GL_VERTEX_SHADER); |         const auto code = | ||||||
|  |             GLSL::GenerateTrivialVertexShader(driver.HasClipCullDistance(), separable); | ||||||
|  |         program.Create(code.c_str(), GL_VERTEX_SHADER); | ||||||
|     } |     } | ||||||
|     GLuint Get() const { |     GLuint Get() const { | ||||||
|         return program.GetHandle(); |         return program.GetHandle(); | ||||||
|  | @ -141,20 +146,18 @@ private: | ||||||
|     OGLShaderStage program; |     OGLShaderStage program; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <typename KeyConfigType, | template <typename KeyConfigType, std::string (*CodeGenerator)(const KeyConfigType&, bool), | ||||||
|           ShaderDecompiler::ProgramResult (*CodeGenerator)(const KeyConfigType&, bool), |  | ||||||
|           GLenum ShaderType> |           GLenum ShaderType> | ||||||
| class ShaderCache { | class ShaderCache { | ||||||
| public: | public: | ||||||
|     explicit ShaderCache(bool separable) : separable(separable) {} |     explicit ShaderCache(bool separable) : separable(separable) {} | ||||||
|     std::tuple<GLuint, std::optional<ShaderDecompiler::ProgramResult>> Get( |     std::tuple<GLuint, std::optional<std::string>> Get(const KeyConfigType& config) { | ||||||
|         const KeyConfigType& config) { |  | ||||||
|         auto [iter, new_shader] = shaders.emplace(config, OGLShaderStage{separable}); |         auto [iter, new_shader] = shaders.emplace(config, OGLShaderStage{separable}); | ||||||
|         OGLShaderStage& cached_shader = iter->second; |         OGLShaderStage& cached_shader = iter->second; | ||||||
|         std::optional<ShaderDecompiler::ProgramResult> result{}; |         std::optional<std::string> result{}; | ||||||
|         if (new_shader) { |         if (new_shader) { | ||||||
|             result = CodeGenerator(config, separable); |             result = CodeGenerator(config, separable); | ||||||
|             cached_shader.Create(result->code.c_str(), ShaderType); |             cached_shader.Create(result->c_str(), ShaderType); | ||||||
|         } |         } | ||||||
|         return {cached_shader.GetHandle(), std::move(result)}; |         return {cached_shader.GetHandle(), std::move(result)}; | ||||||
|     } |     } | ||||||
|  | @ -180,29 +183,27 @@ private: | ||||||
| // program buffer from the previous shader, which is hashed into the config, resulting several
 | // program buffer from the previous shader, which is hashed into the config, resulting several
 | ||||||
| // different config values from the same shader program.
 | // different config values from the same shader program.
 | ||||||
| template <typename KeyConfigType, | template <typename KeyConfigType, | ||||||
|           std::optional<ShaderDecompiler::ProgramResult> (*CodeGenerator)( |           std::string (*CodeGenerator)(const Pica::Shader::ShaderSetup&, const KeyConfigType&, | ||||||
|               const Pica::Shader::ShaderSetup&, const KeyConfigType&, bool), |                                        bool), | ||||||
|           GLenum ShaderType> |           GLenum ShaderType> | ||||||
| class ShaderDoubleCache { | class ShaderDoubleCache { | ||||||
| public: | public: | ||||||
|     explicit ShaderDoubleCache(bool separable) : separable(separable) {} |     explicit ShaderDoubleCache(bool separable) : separable(separable) {} | ||||||
|     std::tuple<GLuint, std::optional<ShaderDecompiler::ProgramResult>> Get( |     std::tuple<GLuint, std::optional<std::string>> Get(const KeyConfigType& key, | ||||||
|         const KeyConfigType& key, const Pica::Shader::ShaderSetup& setup) { |                                                        const Pica::Shader::ShaderSetup& setup) { | ||||||
|         std::optional<ShaderDecompiler::ProgramResult> result{}; |         std::optional<std::string> result{}; | ||||||
|         auto map_it = shader_map.find(key); |         auto map_it = shader_map.find(key); | ||||||
|         if (map_it == shader_map.end()) { |         if (map_it == shader_map.end()) { | ||||||
|             auto program_opt = CodeGenerator(setup, key, separable); |             auto program = CodeGenerator(setup, key, separable); | ||||||
|             if (!program_opt) { |             if (program.empty()) { | ||||||
|                 shader_map[key] = nullptr; |                 shader_map[key] = nullptr; | ||||||
|                 return {0, std::nullopt}; |                 return {0, std::nullopt}; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             std::string& program = program_opt->code; |  | ||||||
|             auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{separable}); |             auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{separable}); | ||||||
|             OGLShaderStage& cached_shader = iter->second; |             OGLShaderStage& cached_shader = iter->second; | ||||||
|             if (new_shader) { |             if (new_shader) { | ||||||
|                 result.emplace(); |                 result = program; | ||||||
|                 result->code = program; |  | ||||||
|                 cached_shader.Create(program.c_str(), ShaderType); |                 cached_shader.Create(program.c_str(), ShaderType); | ||||||
|             } |             } | ||||||
|             shader_map[key] = &cached_shader; |             shader_map[key] = &cached_shader; | ||||||
|  | @ -237,18 +238,19 @@ private: | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| using ProgrammableVertexShaders = | using ProgrammableVertexShaders = | ||||||
|     ShaderDoubleCache<PicaVSConfig, &GenerateVertexShader, GL_VERTEX_SHADER>; |     ShaderDoubleCache<PicaVSConfig, &GLSL::GenerateVertexShader, GL_VERTEX_SHADER>; | ||||||
| 
 | 
 | ||||||
| using FixedGeometryShaders = | using FixedGeometryShaders = | ||||||
|     ShaderCache<PicaFixedGSConfig, &GenerateFixedGeometryShader, GL_GEOMETRY_SHADER>; |     ShaderCache<PicaFixedGSConfig, &GLSL::GenerateFixedGeometryShader, GL_GEOMETRY_SHADER>; | ||||||
| 
 | 
 | ||||||
| using FragmentShaders = ShaderCache<PicaFSConfig, &GenerateFragmentShader, GL_FRAGMENT_SHADER>; | using FragmentShaders = | ||||||
|  |     ShaderCache<PicaFSConfig, &GLSL::GenerateFragmentShader, GL_FRAGMENT_SHADER>; | ||||||
| 
 | 
 | ||||||
| class ShaderProgramManager::Impl { | class ShaderProgramManager::Impl { | ||||||
| public: | public: | ||||||
|     explicit Impl(bool separable) |     explicit Impl(const Driver& driver, bool separable) | ||||||
|         : separable(separable), programmable_vertex_shaders(separable), |         : separable(separable), programmable_vertex_shaders(separable), | ||||||
|           trivial_vertex_shader(separable), fixed_geometry_shaders(separable), |           trivial_vertex_shader(driver, separable), fixed_geometry_shaders(separable), | ||||||
|           fragment_shaders(separable), disk_cache(separable) { |           fragment_shaders(separable), disk_cache(separable) { | ||||||
|         if (separable) |         if (separable) | ||||||
|             pipeline.Create(); |             pipeline.Create(); | ||||||
|  | @ -299,13 +301,13 @@ ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, con | ||||||
|                                            bool separable) |                                            bool separable) | ||||||
|     : emu_window{emu_window_}, driver{driver_}, |     : emu_window{emu_window_}, driver{driver_}, | ||||||
|       strict_context_required{emu_window.StrictContextRequired()}, impl{std::make_unique<Impl>( |       strict_context_required{emu_window.StrictContextRequired()}, impl{std::make_unique<Impl>( | ||||||
|                                                                        separable)} {} |                                                                        driver_, separable)} {} | ||||||
| 
 | 
 | ||||||
| ShaderProgramManager::~ShaderProgramManager() = default; | ShaderProgramManager::~ShaderProgramManager() = default; | ||||||
| 
 | 
 | ||||||
| bool ShaderProgramManager::UseProgrammableVertexShader(const Pica::Regs& regs, | bool ShaderProgramManager::UseProgrammableVertexShader(const Pica::Regs& regs, | ||||||
|                                                        Pica::Shader::ShaderSetup& setup) { |                                                        Pica::Shader::ShaderSetup& setup) { | ||||||
|     PicaVSConfig config{regs.vs, setup}; |     PicaVSConfig config{regs, setup, driver.HasClipCullDistance(), true}; | ||||||
|     auto [handle, result] = impl->programmable_vertex_shaders.Get(config, setup); |     auto [handle, result] = impl->programmable_vertex_shaders.Get(config, setup); | ||||||
|     if (handle == 0) |     if (handle == 0) | ||||||
|         return false; |         return false; | ||||||
|  | @ -333,7 +335,7 @@ void ShaderProgramManager::UseTrivialVertexShader() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ShaderProgramManager::UseFixedGeometryShader(const Pica::Regs& regs) { | void ShaderProgramManager::UseFixedGeometryShader(const Pica::Regs& regs) { | ||||||
|     PicaFixedGSConfig gs_config(regs); |     PicaFixedGSConfig gs_config(regs, driver.HasClipCullDistance()); | ||||||
|     auto [handle, _] = impl->fixed_geometry_shaders.Get(gs_config); |     auto [handle, _] = impl->fixed_geometry_shaders.Get(gs_config); | ||||||
|     impl->current.gs = handle; |     impl->current.gs = handle; | ||||||
|     impl->current.gs_hash = gs_config.Hash(); |     impl->current.gs_hash = gs_config.Hash(); | ||||||
|  | @ -345,8 +347,8 @@ void ShaderProgramManager::UseTrivialGeometryShader() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ShaderProgramManager::UseFragmentShader(const Pica::Regs& regs, bool use_normal) { | void ShaderProgramManager::UseFragmentShader(const Pica::Regs& regs, bool use_normal) { | ||||||
|     PicaFSConfig config = |     PicaFSConfig config(regs, false, driver.IsOpenGLES(), false, driver.HasBlendMinMaxFactor(), | ||||||
|         PicaFSConfig::BuildFromRegs(regs, driver.HasBlendMinMaxFactor(), use_normal); |                         use_normal); | ||||||
|     auto [handle, result] = impl->fragment_shaders.Get(config); |     auto [handle, result] = impl->fragment_shaders.Get(config); | ||||||
|     impl->current.fs = handle; |     impl->current.fs = handle; | ||||||
|     impl->current.fs_hash = config.Hash(); |     impl->current.fs_hash = config.Hash(); | ||||||
|  | @ -463,13 +465,13 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, | ||||||
|                 // we have both the binary shader and the decompiled, so inject it into the
 |                 // we have both the binary shader and the decompiled, so inject it into the
 | ||||||
|                 // cache
 |                 // cache
 | ||||||
|                 if (raw.GetProgramType() == ProgramType::VS) { |                 if (raw.GetProgramType() == ProgramType::VS) { | ||||||
|                     auto [conf, setup] = BuildVSConfigFromRaw(raw); |                     auto [conf, setup] = BuildVSConfigFromRaw(raw, driver); | ||||||
|                     std::scoped_lock lock(mutex); |                     std::scoped_lock lock(mutex); | ||||||
|                     impl->programmable_vertex_shaders.Inject(conf, decomp->second.result.code, |                     impl->programmable_vertex_shaders.Inject(conf, decomp->second.code, | ||||||
|                                                              std::move(shader)); |                                                              std::move(shader)); | ||||||
|                 } else if (raw.GetProgramType() == ProgramType::FS) { |                 } else if (raw.GetProgramType() == ProgramType::FS) { | ||||||
|                     PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig(), |                     PicaFSConfig conf(raw.GetRawShaderConfig(), false, driver.IsOpenGLES(), false, | ||||||
|                                                                     driver.HasBlendMinMaxFactor()); |                                       driver.HasBlendMinMaxFactor()); | ||||||
|                     std::scoped_lock lock(mutex); |                     std::scoped_lock lock(mutex); | ||||||
|                     impl->fragment_shaders.Inject(conf, std::move(shader)); |                     impl->fragment_shaders.Inject(conf, std::move(shader)); | ||||||
|                 } else { |                 } else { | ||||||
|  | @ -566,24 +568,24 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, | ||||||
| 
 | 
 | ||||||
|             bool sanitize_mul = false; |             bool sanitize_mul = false; | ||||||
|             GLuint handle{0}; |             GLuint handle{0}; | ||||||
|             std::optional<ShaderDecompiler::ProgramResult> result; |             std::string code; | ||||||
|             // Otherwise decompile and build the shader at boot and save the result to the
 |             // Otherwise decompile and build the shader at boot and save the result to the
 | ||||||
|             // precompiled file
 |             // precompiled file
 | ||||||
|             if (raw.GetProgramType() == ProgramType::VS) { |             if (raw.GetProgramType() == ProgramType::VS) { | ||||||
|                 auto [conf, setup] = BuildVSConfigFromRaw(raw); |                 auto [conf, setup] = BuildVSConfigFromRaw(raw, driver); | ||||||
|                 result = GenerateVertexShader(setup, conf, impl->separable); |                 code = GLSL::GenerateVertexShader(setup, conf, impl->separable); | ||||||
|                 OGLShaderStage stage{impl->separable}; |                 OGLShaderStage stage{impl->separable}; | ||||||
|                 stage.Create(result->code.c_str(), GL_VERTEX_SHADER); |                 stage.Create(code.c_str(), GL_VERTEX_SHADER); | ||||||
|                 handle = stage.GetHandle(); |                 handle = stage.GetHandle(); | ||||||
|                 sanitize_mul = conf.state.sanitize_mul; |                 sanitize_mul = conf.state.sanitize_mul; | ||||||
|                 std::scoped_lock lock(mutex); |                 std::scoped_lock lock(mutex); | ||||||
|                 impl->programmable_vertex_shaders.Inject(conf, result->code, std::move(stage)); |                 impl->programmable_vertex_shaders.Inject(conf, code, std::move(stage)); | ||||||
|             } else if (raw.GetProgramType() == ProgramType::FS) { |             } else if (raw.GetProgramType() == ProgramType::FS) { | ||||||
|                 PicaFSConfig conf = PicaFSConfig::BuildFromRegs(raw.GetRawShaderConfig(), |                 PicaFSConfig conf(raw.GetRawShaderConfig(), false, driver.IsOpenGLES(), false, | ||||||
|                                                                 driver.HasBlendMinMaxFactor()); |                                   driver.HasBlendMinMaxFactor()); | ||||||
|                 result = GenerateFragmentShader(conf, impl->separable); |                 code = GLSL::GenerateFragmentShader(conf, impl->separable); | ||||||
|                 OGLShaderStage stage{impl->separable}; |                 OGLShaderStage stage{impl->separable}; | ||||||
|                 stage.Create(result->code.c_str(), GL_FRAGMENT_SHADER); |                 stage.Create(code.c_str(), GL_FRAGMENT_SHADER); | ||||||
|                 handle = stage.GetHandle(); |                 handle = stage.GetHandle(); | ||||||
|                 std::scoped_lock lock(mutex); |                 std::scoped_lock lock(mutex); | ||||||
|                 impl->fragment_shaders.Inject(conf, std::move(stage)); |                 impl->fragment_shaders.Inject(conf, std::move(stage)); | ||||||
|  | @ -602,8 +604,8 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, | ||||||
| 
 | 
 | ||||||
|             std::scoped_lock lock(mutex); |             std::scoped_lock lock(mutex); | ||||||
|             // If this is a new separable shader, add it the precompiled cache
 |             // If this is a new separable shader, add it the precompiled cache
 | ||||||
|             if (result) { |             if (!code.empty()) { | ||||||
|                 disk_cache.SaveDecompiled(unique_identifier, *result, sanitize_mul); |                 disk_cache.SaveDecompiled(unique_identifier, code, sanitize_mul); | ||||||
|                 disk_cache.SaveDump(unique_identifier, handle); |                 disk_cache.SaveDump(unique_identifier, handle); | ||||||
|                 precompiled_cache_altered = true; |                 precompiled_cache_altered = true; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -24,6 +24,12 @@ namespace OpenGL { | ||||||
| class Driver; | class Driver; | ||||||
| class OpenGLState; | class OpenGLState; | ||||||
| 
 | 
 | ||||||
|  | enum UniformBindings { | ||||||
|  |     VSPicaData = 0, | ||||||
|  |     VSData = 1, | ||||||
|  |     FSData = 2, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /// A class that manage different shader stages and configures them with given config data.
 | /// A class that manage different shader stages and configures them with given config data.
 | ||||||
| class ShaderProgramManager { | class ShaderProgramManager { | ||||||
| public: | public: | ||||||
|  |  | ||||||
|  | @ -14,9 +14,9 @@ | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
| GLuint LoadShader(std::string_view source, GLenum type) { | GLuint LoadShader(std::string_view source, GLenum type) { | ||||||
|     const std::string version = GLES ? R"(#version 320 es |     std::string preamble; | ||||||
| 
 |     if (GLES) { | ||||||
| #define CITRA_GLES |         preamble = R"(#version 320 es | ||||||
| 
 | 
 | ||||||
| #if defined(GL_ANDROID_extension_pack_es31a) | #if defined(GL_ANDROID_extension_pack_es31a) | ||||||
| #extension GL_ANDROID_extension_pack_es31a : enable | #extension GL_ANDROID_extension_pack_es31a : enable | ||||||
|  | @ -25,8 +25,10 @@ GLuint LoadShader(std::string_view source, GLenum type) { | ||||||
| #if defined(GL_EXT_clip_cull_distance) | #if defined(GL_EXT_clip_cull_distance) | ||||||
| #extension GL_EXT_clip_cull_distance : enable | #extension GL_EXT_clip_cull_distance : enable | ||||||
| #endif // defined(GL_EXT_clip_cull_distance)
 | #endif // defined(GL_EXT_clip_cull_distance)
 | ||||||
| )" | )"; | ||||||
|                                      : "#version 430 core\n"; |     } else { | ||||||
|  |         preamble = "#version 430 core\n"; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     std::string_view debug_type; |     std::string_view debug_type; | ||||||
|     switch (type) { |     switch (type) { | ||||||
|  | @ -43,8 +45,8 @@ GLuint LoadShader(std::string_view source, GLenum type) { | ||||||
|         UNREACHABLE(); |         UNREACHABLE(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::array<const GLchar*, 2> src_arr{version.data(), source.data()}; |     std::array<const GLchar*, 2> src_arr{preamble.data(), source.data()}; | ||||||
|     std::array<GLint, 2> lengths{static_cast<GLint>(version.size()), |     std::array<GLint, 2> lengths{static_cast<GLint>(preamble.size()), | ||||||
|                                  static_cast<GLint>(source.size())}; |                                  static_cast<GLint>(source.size())}; | ||||||
|     GLuint shader_id = glCreateShader(type); |     GLuint shader_id = glCreateShader(type); | ||||||
|     glShaderSource(shader_id, static_cast<GLsizei>(src_arr.size()), src_arr.data(), lengths.data()); |     glShaderSource(shader_id, static_cast<GLsizei>(src_arr.size()), src_arr.data(), lengths.data()); | ||||||
|  |  | ||||||
|  | @ -9,21 +9,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
| // High precision may or may not supported in GLES3. If it isn't, use medium precision instead.
 |  | ||||||
| static constexpr char fragment_shader_precision_OES[] = R"( |  | ||||||
| #ifdef GL_FRAGMENT_PRECISION_HIGH |  | ||||||
| precision highp int; |  | ||||||
| precision highp float; |  | ||||||
| precision highp samplerBuffer; |  | ||||||
| precision highp uimage2D; |  | ||||||
| #else |  | ||||||
| precision mediump int; |  | ||||||
| precision mediump float; |  | ||||||
| precision mediump samplerBuffer; |  | ||||||
| precision mediump uimage2D; |  | ||||||
| #endif // GL_FRAGMENT_PRECISION_HIGH
 |  | ||||||
| )"; |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * Utility function to create and compile an OpenGL GLSL shader |  * Utility function to create and compile an OpenGL GLSL shader | ||||||
|  * @param source String of the GLSL shader program |  * @param source String of the GLSL shader program | ||||||
|  |  | ||||||
|  | @ -11,12 +11,12 @@ | ||||||
| #include "core/hw/hw.h" | #include "core/hw/hw.h" | ||||||
| #include "core/hw/lcd.h" | #include "core/hw/lcd.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| #include "video_core/renderer_opengl/gl_shader_util.h" |  | ||||||
| #include "video_core/renderer_opengl/gl_state.h" | #include "video_core/renderer_opengl/gl_state.h" | ||||||
| #include "video_core/renderer_opengl/gl_texture_mailbox.h" | #include "video_core/renderer_opengl/gl_texture_mailbox.h" | ||||||
| #include "video_core/renderer_opengl/gl_vars.h" | #include "video_core/renderer_opengl/gl_vars.h" | ||||||
| #include "video_core/renderer_opengl/post_processing_opengl.h" | #include "video_core/renderer_opengl/post_processing_opengl.h" | ||||||
| #include "video_core/renderer_opengl/renderer_opengl.h" | #include "video_core/renderer_opengl/renderer_opengl.h" | ||||||
|  | #include "video_core/shader/generator/glsl_shader_gen.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
| #include "video_core/host_shaders/opengl_present_anaglyph_frag.h" | #include "video_core/host_shaders/opengl_present_anaglyph_frag.h" | ||||||
|  | @ -387,11 +387,7 @@ void RendererOpenGL::InitOpenGLObjects() { | ||||||
| 
 | 
 | ||||||
| void RendererOpenGL::ReloadShader() { | void RendererOpenGL::ReloadShader() { | ||||||
|     // Link shaders and get variable locations
 |     // Link shaders and get variable locations
 | ||||||
|     std::string shader_data; |     std::string shader_data = fragment_shader_precision_OES; | ||||||
|     if (GLES) { |  | ||||||
|         shader_data += fragment_shader_precision_OES; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) { |     if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) { | ||||||
|         if (Settings::values.anaglyph_shader_name.GetValue() == "dubois (builtin)") { |         if (Settings::values.anaglyph_shader_name.GetValue() == "dubois (builtin)") { | ||||||
|             shader_data += HostShaders::OPENGL_PRESENT_ANAGLYPH_FRAG; |             shader_data += HostShaders::OPENGL_PRESENT_ANAGLYPH_FRAG; | ||||||
|  |  | ||||||
|  | @ -5,7 +5,8 @@ | ||||||
| #include "common/thread_worker.h" | #include "common/thread_worker.h" | ||||||
| #include "video_core/rasterizer_cache/pixel_format.h" | #include "video_core/rasterizer_cache/pixel_format.h" | ||||||
| #include "video_core/renderer_vulkan/vk_common.h" | #include "video_core/renderer_vulkan/vk_common.h" | ||||||
| #include "video_core/renderer_vulkan/vk_shader_gen.h" | #include "video_core/shader/generator/glsl_shader_gen.h" | ||||||
|  | #include "video_core/shader/generator/spv_shader_gen.h" | ||||||
| 
 | 
 | ||||||
| namespace Common { | namespace Common { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -14,19 +14,14 @@ | ||||||
| #include "video_core/renderer_vulkan/vk_pipeline_cache.h" | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" | ||||||
| #include "video_core/renderer_vulkan/vk_renderpass_cache.h" | #include "video_core/renderer_vulkan/vk_renderpass_cache.h" | ||||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||||
| #include "video_core/renderer_vulkan/vk_shader_gen_spv.h" |  | ||||||
| #include "video_core/renderer_vulkan/vk_shader_util.h" | #include "video_core/renderer_vulkan/vk_shader_util.h" | ||||||
| 
 | 
 | ||||||
|  | using namespace Pica::Shader::Generator; | ||||||
|  | 
 | ||||||
| MICROPROFILE_DEFINE(Vulkan_Bind, "Vulkan", "Pipeline Bind", MP_RGB(192, 32, 32)); | MICROPROFILE_DEFINE(Vulkan_Bind, "Vulkan", "Pipeline Bind", MP_RGB(192, 32, 32)); | ||||||
| 
 | 
 | ||||||
| namespace Vulkan { | namespace Vulkan { | ||||||
| 
 | 
 | ||||||
| enum ProgramType : u32 { |  | ||||||
|     VS = 0, |  | ||||||
|     GS = 2, |  | ||||||
|     FS = 1, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| u32 AttribBytes(Pica::PipelineRegs::VertexAttributeFormat format, u32 size) { | u32 AttribBytes(Pica::PipelineRegs::VertexAttributeFormat format, u32 size) { | ||||||
|     switch (format) { |     switch (format) { | ||||||
|     case Pica::PipelineRegs::VertexAttributeFormat::FLOAT: |     case Pica::PipelineRegs::VertexAttributeFormat::FLOAT: | ||||||
|  | @ -52,14 +47,14 @@ AttribLoadFlags MakeAttribLoadFlag(Pica::PipelineRegs::VertexAttributeFormat for | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| constexpr std::array<vk::DescriptorSetLayoutBinding, 5> BUFFER_BINDINGS = {{ | constexpr std::array<vk::DescriptorSetLayoutBinding, 6> BUFFER_BINDINGS = {{ | ||||||
|     {0, vk::DescriptorType::eUniformBufferDynamic, 1, vk::ShaderStageFlagBits::eVertex}, |     {0, vk::DescriptorType::eUniformBufferDynamic, 1, vk::ShaderStageFlagBits::eVertex}, | ||||||
|     {1, vk::DescriptorType::eUniformBufferDynamic, 1, |     {1, vk::DescriptorType::eUniformBufferDynamic, 1, | ||||||
|      vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eGeometry | |      vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eGeometry}, | ||||||
|          vk::ShaderStageFlagBits::eFragment}, |     {2, vk::DescriptorType::eUniformBufferDynamic, 1, vk::ShaderStageFlagBits::eFragment}, | ||||||
|     {2, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment}, |  | ||||||
|     {3, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment}, |     {3, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment}, | ||||||
|     {4, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment}, |     {4, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment}, | ||||||
|  |     {5, vk::DescriptorType::eUniformTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment}, | ||||||
| }}; | }}; | ||||||
| 
 | 
 | ||||||
| constexpr std::array<vk::DescriptorSetLayoutBinding, 4> TEXTURE_BINDINGS = {{ | constexpr std::array<vk::DescriptorSetLayoutBinding, 4> TEXTURE_BINDINGS = {{ | ||||||
|  | @ -88,8 +83,9 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, | ||||||
|       descriptor_set_providers{DescriptorSetProvider{instance, pool, BUFFER_BINDINGS}, |       descriptor_set_providers{DescriptorSetProvider{instance, pool, BUFFER_BINDINGS}, | ||||||
|                                DescriptorSetProvider{instance, pool, TEXTURE_BINDINGS}, |                                DescriptorSetProvider{instance, pool, TEXTURE_BINDINGS}, | ||||||
|                                DescriptorSetProvider{instance, pool, SHADOW_BINDINGS}}, |                                DescriptorSetProvider{instance, pool, SHADOW_BINDINGS}}, | ||||||
|       trivial_vertex_shader{instance, vk::ShaderStageFlagBits::eVertex, |       trivial_vertex_shader{ | ||||||
|                             GenerateTrivialVertexShader(instance.IsShaderClipDistanceSupported())} { |           instance, vk::ShaderStageFlagBits::eVertex, | ||||||
|  |           GLSL::GenerateTrivialVertexShader(instance.IsShaderClipDistanceSupported(), true)} { | ||||||
|     BuildLayout(); |     BuildLayout(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -294,8 +290,8 @@ bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) { | ||||||
| bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs, | bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs, | ||||||
|                                                 Pica::Shader::ShaderSetup& setup, |                                                 Pica::Shader::ShaderSetup& setup, | ||||||
|                                                 const VertexLayout& layout) { |                                                 const VertexLayout& layout) { | ||||||
|     PicaVSConfig config{regs.rasterizer, regs.vs, setup, instance}; |     PicaVSConfig config{regs, setup, instance.IsShaderClipDistanceSupported(), | ||||||
|     config.state.use_geometry_shader = instance.UseGeometryShaders(); |                         instance.UseGeometryShaders()}; | ||||||
| 
 | 
 | ||||||
|     for (u32 i = 0; i < layout.attribute_count; i++) { |     for (u32 i = 0; i < layout.attribute_count; i++) { | ||||||
|         const VertexAttribute& attr = layout.attributes[i]; |         const VertexAttribute& attr = layout.attributes[i]; | ||||||
|  | @ -313,14 +309,13 @@ bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs, | ||||||
| 
 | 
 | ||||||
|     auto [it, new_config] = programmable_vertex_map.try_emplace(config); |     auto [it, new_config] = programmable_vertex_map.try_emplace(config); | ||||||
|     if (new_config) { |     if (new_config) { | ||||||
|         auto code = GenerateVertexShader(setup, config); |         auto program = GLSL::GenerateVertexShader(setup, config, true); | ||||||
|         if (!code) { |         if (program.empty()) { | ||||||
|             LOG_ERROR(Render_Vulkan, "Failed to retrieve programmable vertex shader"); |             LOG_ERROR(Render_Vulkan, "Failed to retrieve programmable vertex shader"); | ||||||
|             programmable_vertex_map[config] = nullptr; |             programmable_vertex_map[config] = nullptr; | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         std::string& program = code.value(); |  | ||||||
|         auto [iter, new_program] = programmable_vertex_cache.try_emplace(program, instance); |         auto [iter, new_program] = programmable_vertex_cache.try_emplace(program, instance); | ||||||
|         auto& shader = iter->second; |         auto& shader = iter->second; | ||||||
| 
 | 
 | ||||||
|  | @ -359,13 +354,13 @@ bool PipelineCache::UseFixedGeometryShader(const Pica::Regs& regs) { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const PicaFixedGSConfig gs_config{regs, instance}; |     const PicaFixedGSConfig gs_config{regs, instance.IsShaderClipDistanceSupported()}; | ||||||
|     auto [it, new_shader] = fixed_geometry_shaders.try_emplace(gs_config, instance); |     auto [it, new_shader] = fixed_geometry_shaders.try_emplace(gs_config, instance); | ||||||
|     auto& shader = it->second; |     auto& shader = it->second; | ||||||
| 
 | 
 | ||||||
|     if (new_shader) { |     if (new_shader) { | ||||||
|         workers.QueueWork([gs_config, device = instance.GetDevice(), &shader]() { |         workers.QueueWork([gs_config, device = instance.GetDevice(), &shader]() { | ||||||
|             const std::string code = GenerateFixedGeometryShader(gs_config); |             const auto code = GLSL::GenerateFixedGeometryShader(gs_config, true); | ||||||
|             shader.module = Compile(code, vk::ShaderStageFlagBits::eGeometry, device); |             shader.module = Compile(code, vk::ShaderStageFlagBits::eGeometry, device); | ||||||
|             shader.MarkDone(); |             shader.MarkDone(); | ||||||
|         }); |         }); | ||||||
|  | @ -383,7 +378,9 @@ void PipelineCache::UseTrivialGeometryShader() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void PipelineCache::UseFragmentShader(const Pica::Regs& regs) { | void PipelineCache::UseFragmentShader(const Pica::Regs& regs) { | ||||||
|     const PicaFSConfig config{regs, instance}; |     const PicaFSConfig config{regs, instance.IsFragmentShaderInterlockSupported(), | ||||||
|  |                               instance.NeedsLogicOpEmulation(), | ||||||
|  |                               !instance.IsCustomBorderColorSupported(), false}; | ||||||
| 
 | 
 | ||||||
|     const auto [it, new_shader] = fragment_shaders.try_emplace(config, instance); |     const auto [it, new_shader] = fragment_shaders.try_emplace(config, instance); | ||||||
|     auto& shader = it->second; |     auto& shader = it->second; | ||||||
|  | @ -395,12 +392,12 @@ void PipelineCache::UseFragmentShader(const Pica::Regs& regs) { | ||||||
|                                texture0_type == Pica::TexturingRegs::TextureConfig::ShadowCube || |                                texture0_type == Pica::TexturingRegs::TextureConfig::ShadowCube || | ||||||
|                                config.state.shadow_rendering.Value(); |                                config.state.shadow_rendering.Value(); | ||||||
|         if (use_spirv && !is_shadow) { |         if (use_spirv && !is_shadow) { | ||||||
|             const std::vector code = GenerateFragmentShaderSPV(config); |             const std::vector code = SPIRV::GenerateFragmentShader(config); | ||||||
|             shader.module = CompileSPV(code, instance.GetDevice()); |             shader.module = CompileSPV(code, instance.GetDevice()); | ||||||
|             shader.MarkDone(); |             shader.MarkDone(); | ||||||
|         } else { |         } else { | ||||||
|             workers.QueueWork([config, device = instance.GetDevice(), &shader]() { |             workers.QueueWork([config, device = instance.GetDevice(), &shader]() { | ||||||
|                 const std::string code = GenerateFragmentShader(config); |                 const std::string code = GLSL::GenerateFragmentShader(config, true); | ||||||
|                 shader.module = Compile(code, vk::ShaderStageFlagBits::eFragment, device); |                 shader.module = Compile(code, vk::ShaderStageFlagBits::eFragment, device); | ||||||
|                 shader.MarkDone(); |                 shader.MarkDone(); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|  | @ -9,6 +9,8 @@ | ||||||
| 
 | 
 | ||||||
| #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | ||||||
| #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" | #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" | ||||||
|  | #include "video_core/shader/generator/glsl_shader_gen.h" | ||||||
|  | #include "video_core/shader/generator/spv_shader_gen.h" | ||||||
| 
 | 
 | ||||||
| namespace Pica { | namespace Pica { | ||||||
| struct Regs; | struct Regs; | ||||||
|  | @ -22,7 +24,7 @@ class RenderpassCache; | ||||||
| class DescriptorPool; | class DescriptorPool; | ||||||
| 
 | 
 | ||||||
| constexpr u32 NUM_RASTERIZER_SETS = 3; | constexpr u32 NUM_RASTERIZER_SETS = 3; | ||||||
| constexpr u32 NUM_DYNAMIC_OFFSETS = 2; | constexpr u32 NUM_DYNAMIC_OFFSETS = 3; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Stores a collection of rasterizer pipelines used during rendering. |  * Stores a collection of rasterizer pipelines used during rendering. | ||||||
|  | @ -113,10 +115,10 @@ private: | ||||||
| 
 | 
 | ||||||
|     std::array<u64, MAX_SHADER_STAGES> shader_hashes; |     std::array<u64, MAX_SHADER_STAGES> shader_hashes; | ||||||
|     std::array<Shader*, MAX_SHADER_STAGES> current_shaders; |     std::array<Shader*, MAX_SHADER_STAGES> current_shaders; | ||||||
|     std::unordered_map<PicaVSConfig, Shader*> programmable_vertex_map; |     std::unordered_map<Pica::Shader::Generator::PicaVSConfig, Shader*> programmable_vertex_map; | ||||||
|     std::unordered_map<std::string, Shader> programmable_vertex_cache; |     std::unordered_map<std::string, Shader> programmable_vertex_cache; | ||||||
|     std::unordered_map<PicaFixedGSConfig, Shader> fixed_geometry_shaders; |     std::unordered_map<Pica::Shader::Generator::PicaFixedGSConfig, Shader> fixed_geometry_shaders; | ||||||
|     std::unordered_map<PicaFSConfig, Shader> fragment_shaders; |     std::unordered_map<Pica::Shader::Generator::PicaFSConfig, Shader> fragment_shaders; | ||||||
|     Shader trivial_vertex_shader; |     Shader trivial_vertex_shader; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -28,6 +28,8 @@ MICROPROFILE_DEFINE(Vulkan_Drawing, "Vulkan", "Drawing", MP_RGB(128, 128, 192)); | ||||||
| using TriangleTopology = Pica::PipelineRegs::TriangleTopology; | using TriangleTopology = Pica::PipelineRegs::TriangleTopology; | ||||||
| using VideoCore::SurfaceType; | using VideoCore::SurfaceType; | ||||||
| 
 | 
 | ||||||
|  | using namespace Pica::Shader::Generator; | ||||||
|  | 
 | ||||||
| constexpr u64 STREAM_BUFFER_SIZE = 64 * 1024 * 1024; | constexpr u64 STREAM_BUFFER_SIZE = 64 * 1024 * 1024; | ||||||
| constexpr u64 UNIFORM_BUFFER_SIZE = 4 * 1024 * 1024; | constexpr u64 UNIFORM_BUFFER_SIZE = 4 * 1024 * 1024; | ||||||
| constexpr u64 TEXTURE_BUFFER_SIZE = 2 * 1024 * 1024; | constexpr u64 TEXTURE_BUFFER_SIZE = 2 * 1024 * 1024; | ||||||
|  | @ -76,10 +78,10 @@ RasterizerVulkan::RasterizerVulkan(Memory::MemorySystem& memory, | ||||||
|     vertex_buffers.fill(stream_buffer.Handle()); |     vertex_buffers.fill(stream_buffer.Handle()); | ||||||
| 
 | 
 | ||||||
|     uniform_buffer_alignment = instance.UniformMinAlignment(); |     uniform_buffer_alignment = instance.UniformMinAlignment(); | ||||||
|     uniform_size_aligned_vs = |     uniform_size_aligned_vs_pica = | ||||||
|         Common::AlignUp(sizeof(Pica::Shader::VSUniformData), uniform_buffer_alignment); |         Common::AlignUp(sizeof(VSPicaUniformData), uniform_buffer_alignment); | ||||||
|     uniform_size_aligned_fs = |     uniform_size_aligned_vs = Common::AlignUp(sizeof(VSUniformData), uniform_buffer_alignment); | ||||||
|         Common::AlignUp(sizeof(Pica::Shader::UniformData), uniform_buffer_alignment); |     uniform_size_aligned_fs = Common::AlignUp(sizeof(FSUniformData), uniform_buffer_alignment); | ||||||
| 
 | 
 | ||||||
|     // Define vertex layout for software shaders
 |     // Define vertex layout for software shaders
 | ||||||
|     MakeSoftwareVertexLayout(); |     MakeSoftwareVertexLayout(); | ||||||
|  | @ -107,11 +109,12 @@ RasterizerVulkan::RasterizerVulkan(Memory::MemorySystem& memory, | ||||||
| 
 | 
 | ||||||
|     // Since we don't have access to VK_EXT_descriptor_indexing we need to intiallize
 |     // Since we don't have access to VK_EXT_descriptor_indexing we need to intiallize
 | ||||||
|     // all descriptor sets even the ones we don't use.
 |     // all descriptor sets even the ones we don't use.
 | ||||||
|     pipeline_cache.BindBuffer(0, uniform_buffer.Handle(), 0, sizeof(Pica::Shader::VSUniformData)); |     pipeline_cache.BindBuffer(0, uniform_buffer.Handle(), 0, sizeof(VSPicaUniformData)); | ||||||
|     pipeline_cache.BindBuffer(1, uniform_buffer.Handle(), 0, sizeof(Pica::Shader::UniformData)); |     pipeline_cache.BindBuffer(1, uniform_buffer.Handle(), 0, sizeof(VSUniformData)); | ||||||
|     pipeline_cache.BindTexelBuffer(2, *texture_lf_view); |     pipeline_cache.BindBuffer(2, uniform_buffer.Handle(), 0, sizeof(FSUniformData)); | ||||||
|     pipeline_cache.BindTexelBuffer(3, *texture_rg_view); |     pipeline_cache.BindTexelBuffer(3, *texture_lf_view); | ||||||
|     pipeline_cache.BindTexelBuffer(4, *texture_rgba_view); |     pipeline_cache.BindTexelBuffer(4, *texture_rg_view); | ||||||
|  |     pipeline_cache.BindTexelBuffer(5, *texture_rgba_view); | ||||||
| 
 | 
 | ||||||
|     Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID); |     Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID); | ||||||
|     Surface& null_cube_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_CUBE_ID); |     Surface& null_cube_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_CUBE_ID); | ||||||
|  | @ -140,7 +143,6 @@ void RasterizerVulkan::LoadDiskResources(const std::atomic_bool& stop_loading, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerVulkan::SyncFixedState() { | void RasterizerVulkan::SyncFixedState() { | ||||||
|     SyncClipEnabled(); |  | ||||||
|     SyncCullMode(); |     SyncCullMode(); | ||||||
|     SyncBlendEnabled(); |     SyncBlendEnabled(); | ||||||
|     SyncBlendFuncs(); |     SyncBlendFuncs(); | ||||||
|  | @ -478,16 +480,16 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { | ||||||
| 
 | 
 | ||||||
|     // Update scissor uniforms
 |     // Update scissor uniforms
 | ||||||
|     const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor(); |     const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor(); | ||||||
|     if (uniform_block_data.data.scissor_x1 != scissor_x1 || |     if (fs_uniform_block_data.data.scissor_x1 != scissor_x1 || | ||||||
|         uniform_block_data.data.scissor_x2 != scissor_x2 || |         fs_uniform_block_data.data.scissor_x2 != scissor_x2 || | ||||||
|         uniform_block_data.data.scissor_y1 != scissor_y1 || |         fs_uniform_block_data.data.scissor_y1 != scissor_y1 || | ||||||
|         uniform_block_data.data.scissor_y2 != scissor_y2) { |         fs_uniform_block_data.data.scissor_y2 != scissor_y2) { | ||||||
| 
 | 
 | ||||||
|         uniform_block_data.data.scissor_x1 = scissor_x1; |         fs_uniform_block_data.data.scissor_x1 = scissor_x1; | ||||||
|         uniform_block_data.data.scissor_x2 = scissor_x2; |         fs_uniform_block_data.data.scissor_x2 = scissor_x2; | ||||||
|         uniform_block_data.data.scissor_y1 = scissor_y1; |         fs_uniform_block_data.data.scissor_y1 = scissor_y1; | ||||||
|         uniform_block_data.data.scissor_y2 = scissor_y2; |         fs_uniform_block_data.data.scissor_y2 = scissor_y2; | ||||||
|         uniform_block_data.dirty = true; |         fs_uniform_block_data.dirty = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sync and bind the texture surfaces
 |     // Sync and bind the texture surfaces
 | ||||||
|  | @ -670,11 +672,6 @@ void RasterizerVulkan::UnbindSpecial() { | ||||||
| 
 | 
 | ||||||
| void RasterizerVulkan::NotifyFixedFunctionPicaRegisterChanged(u32 id) { | void RasterizerVulkan::NotifyFixedFunctionPicaRegisterChanged(u32 id) { | ||||||
|     switch (id) { |     switch (id) { | ||||||
|     // Clipping plane
 |  | ||||||
|     case PICA_REG_INDEX(rasterizer.clip_enable): |  | ||||||
|         SyncClipEnabled(); |  | ||||||
|         break; |  | ||||||
| 
 |  | ||||||
|     // Culling
 |     // Culling
 | ||||||
|     case PICA_REG_INDEX(rasterizer.cull_mode): |     case PICA_REG_INDEX(rasterizer.cull_mode): | ||||||
|         SyncCullMode(); |         SyncCullMode(); | ||||||
|  | @ -831,14 +828,6 @@ void RasterizerVulkan::MakeSoftwareVertexLayout() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerVulkan::SyncClipEnabled() { |  | ||||||
|     bool clip_enabled = regs.rasterizer.clip_enable != 0; |  | ||||||
|     if (clip_enabled != uniform_block_data.data.enable_clip1) { |  | ||||||
|         uniform_block_data.data.enable_clip1 = clip_enabled; |  | ||||||
|         uniform_block_data.dirty = true; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void RasterizerVulkan::SyncCullMode() { | void RasterizerVulkan::SyncCullMode() { | ||||||
|     pipeline_info.rasterization.cull_mode.Assign(regs.rasterizer.cull_mode); |     pipeline_info.rasterization.cull_mode.Assign(regs.rasterizer.cull_mode); | ||||||
| } | } | ||||||
|  | @ -946,7 +935,7 @@ void RasterizerVulkan::SyncAndUploadLUTsLF() { | ||||||
|         sizeof(Common::Vec2f) * 256 * Pica::LightingRegs::NumLightingSampler + |         sizeof(Common::Vec2f) * 256 * Pica::LightingRegs::NumLightingSampler + | ||||||
|         sizeof(Common::Vec2f) * 128; // fog
 |         sizeof(Common::Vec2f) * 128; // fog
 | ||||||
| 
 | 
 | ||||||
|     if (!uniform_block_data.lighting_lut_dirty_any && !uniform_block_data.fog_lut_dirty) { |     if (!fs_uniform_block_data.lighting_lut_dirty_any && !fs_uniform_block_data.fog_lut_dirty) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -954,9 +943,9 @@ void RasterizerVulkan::SyncAndUploadLUTsLF() { | ||||||
|     auto [buffer, offset, invalidate] = texture_lf_buffer.Map(max_size, sizeof(Common::Vec4f)); |     auto [buffer, offset, invalidate] = texture_lf_buffer.Map(max_size, sizeof(Common::Vec4f)); | ||||||
| 
 | 
 | ||||||
|     // Sync the lighting luts
 |     // Sync the lighting luts
 | ||||||
|     if (uniform_block_data.lighting_lut_dirty_any || invalidate) { |     if (fs_uniform_block_data.lighting_lut_dirty_any || invalidate) { | ||||||
|         for (unsigned index = 0; index < uniform_block_data.lighting_lut_dirty.size(); index++) { |         for (unsigned index = 0; index < fs_uniform_block_data.lighting_lut_dirty.size(); index++) { | ||||||
|             if (uniform_block_data.lighting_lut_dirty[index] || invalidate) { |             if (fs_uniform_block_data.lighting_lut_dirty[index] || invalidate) { | ||||||
|                 std::array<Common::Vec2f, 256> new_data; |                 std::array<Common::Vec2f, 256> new_data; | ||||||
|                 const auto& source_lut = Pica::g_state.lighting.luts[index]; |                 const auto& source_lut = Pica::g_state.lighting.luts[index]; | ||||||
|                 std::transform(source_lut.begin(), source_lut.end(), new_data.begin(), |                 std::transform(source_lut.begin(), source_lut.end(), new_data.begin(), | ||||||
|  | @ -968,19 +957,19 @@ void RasterizerVulkan::SyncAndUploadLUTsLF() { | ||||||
|                     lighting_lut_data[index] = new_data; |                     lighting_lut_data[index] = new_data; | ||||||
|                     std::memcpy(buffer + bytes_used, new_data.data(), |                     std::memcpy(buffer + bytes_used, new_data.data(), | ||||||
|                                 new_data.size() * sizeof(Common::Vec2f)); |                                 new_data.size() * sizeof(Common::Vec2f)); | ||||||
|                     uniform_block_data.data.lighting_lut_offset[index / 4][index % 4] = |                     fs_uniform_block_data.data.lighting_lut_offset[index / 4][index % 4] = | ||||||
|                         static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f)); |                         static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f)); | ||||||
|                     uniform_block_data.dirty = true; |                     fs_uniform_block_data.dirty = true; | ||||||
|                     bytes_used += new_data.size() * sizeof(Common::Vec2f); |                     bytes_used += new_data.size() * sizeof(Common::Vec2f); | ||||||
|                 } |                 } | ||||||
|                 uniform_block_data.lighting_lut_dirty[index] = false; |                 fs_uniform_block_data.lighting_lut_dirty[index] = false; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         uniform_block_data.lighting_lut_dirty_any = false; |         fs_uniform_block_data.lighting_lut_dirty_any = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sync the fog lut
 |     // Sync the fog lut
 | ||||||
|     if (uniform_block_data.fog_lut_dirty || invalidate) { |     if (fs_uniform_block_data.fog_lut_dirty || invalidate) { | ||||||
|         std::array<Common::Vec2f, 128> new_data; |         std::array<Common::Vec2f, 128> new_data; | ||||||
| 
 | 
 | ||||||
|         std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(), |         std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(), | ||||||
|  | @ -992,12 +981,12 @@ void RasterizerVulkan::SyncAndUploadLUTsLF() { | ||||||
|             fog_lut_data = new_data; |             fog_lut_data = new_data; | ||||||
|             std::memcpy(buffer + bytes_used, new_data.data(), |             std::memcpy(buffer + bytes_used, new_data.data(), | ||||||
|                         new_data.size() * sizeof(Common::Vec2f)); |                         new_data.size() * sizeof(Common::Vec2f)); | ||||||
|             uniform_block_data.data.fog_lut_offset = |             fs_uniform_block_data.data.fog_lut_offset = | ||||||
|                 static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f)); |                 static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f)); | ||||||
|             uniform_block_data.dirty = true; |             fs_uniform_block_data.dirty = true; | ||||||
|             bytes_used += new_data.size() * sizeof(Common::Vec2f); |             bytes_used += new_data.size() * sizeof(Common::Vec2f); | ||||||
|         } |         } | ||||||
|         uniform_block_data.fog_lut_dirty = false; |         fs_uniform_block_data.fog_lut_dirty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     texture_lf_buffer.Commit(static_cast<u32>(bytes_used)); |     texture_lf_buffer.Commit(static_cast<u32>(bytes_used)); | ||||||
|  | @ -1010,10 +999,10 @@ void RasterizerVulkan::SyncAndUploadLUTs() { | ||||||
|         sizeof(Common::Vec4f) * 256 +     // proctex
 |         sizeof(Common::Vec4f) * 256 +     // proctex
 | ||||||
|         sizeof(Common::Vec4f) * 256;      // proctex diff
 |         sizeof(Common::Vec4f) * 256;      // proctex diff
 | ||||||
| 
 | 
 | ||||||
|     if (!uniform_block_data.proctex_noise_lut_dirty && |     if (!fs_uniform_block_data.proctex_noise_lut_dirty && | ||||||
|         !uniform_block_data.proctex_color_map_dirty && |         !fs_uniform_block_data.proctex_color_map_dirty && | ||||||
|         !uniform_block_data.proctex_alpha_map_dirty && !uniform_block_data.proctex_lut_dirty && |         !fs_uniform_block_data.proctex_alpha_map_dirty && | ||||||
|         !uniform_block_data.proctex_diff_lut_dirty) { |         !fs_uniform_block_data.proctex_lut_dirty && !fs_uniform_block_data.proctex_diff_lut_dirty) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1035,34 +1024,34 @@ void RasterizerVulkan::SyncAndUploadLUTs() { | ||||||
|                 std::memcpy(buffer + bytes_used, new_data.data(), |                 std::memcpy(buffer + bytes_used, new_data.data(), | ||||||
|                             new_data.size() * sizeof(Common::Vec2f)); |                             new_data.size() * sizeof(Common::Vec2f)); | ||||||
|                 lut_offset = static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f)); |                 lut_offset = static_cast<int>((offset + bytes_used) / sizeof(Common::Vec2f)); | ||||||
|                 uniform_block_data.dirty = true; |                 fs_uniform_block_data.dirty = true; | ||||||
|                 bytes_used += new_data.size() * sizeof(Common::Vec2f); |                 bytes_used += new_data.size() * sizeof(Common::Vec2f); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|     // Sync the proctex noise lut
 |     // Sync the proctex noise lut
 | ||||||
|     if (uniform_block_data.proctex_noise_lut_dirty || invalidate) { |     if (fs_uniform_block_data.proctex_noise_lut_dirty || invalidate) { | ||||||
|         sync_proctex_value_lut(proctex.noise_table, proctex_noise_lut_data, |         sync_proctex_value_lut(proctex.noise_table, proctex_noise_lut_data, | ||||||
|                                uniform_block_data.data.proctex_noise_lut_offset); |                                fs_uniform_block_data.data.proctex_noise_lut_offset); | ||||||
|         uniform_block_data.proctex_noise_lut_dirty = false; |         fs_uniform_block_data.proctex_noise_lut_dirty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sync the proctex color map
 |     // Sync the proctex color map
 | ||||||
|     if (uniform_block_data.proctex_color_map_dirty || invalidate) { |     if (fs_uniform_block_data.proctex_color_map_dirty || invalidate) { | ||||||
|         sync_proctex_value_lut(proctex.color_map_table, proctex_color_map_data, |         sync_proctex_value_lut(proctex.color_map_table, proctex_color_map_data, | ||||||
|                                uniform_block_data.data.proctex_color_map_offset); |                                fs_uniform_block_data.data.proctex_color_map_offset); | ||||||
|         uniform_block_data.proctex_color_map_dirty = false; |         fs_uniform_block_data.proctex_color_map_dirty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sync the proctex alpha map
 |     // Sync the proctex alpha map
 | ||||||
|     if (uniform_block_data.proctex_alpha_map_dirty || invalidate) { |     if (fs_uniform_block_data.proctex_alpha_map_dirty || invalidate) { | ||||||
|         sync_proctex_value_lut(proctex.alpha_map_table, proctex_alpha_map_data, |         sync_proctex_value_lut(proctex.alpha_map_table, proctex_alpha_map_data, | ||||||
|                                uniform_block_data.data.proctex_alpha_map_offset); |                                fs_uniform_block_data.data.proctex_alpha_map_offset); | ||||||
|         uniform_block_data.proctex_alpha_map_dirty = false; |         fs_uniform_block_data.proctex_alpha_map_dirty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sync the proctex lut
 |     // Sync the proctex lut
 | ||||||
|     if (uniform_block_data.proctex_lut_dirty || invalidate) { |     if (fs_uniform_block_data.proctex_lut_dirty || invalidate) { | ||||||
|         std::array<Common::Vec4f, 256> new_data; |         std::array<Common::Vec4f, 256> new_data; | ||||||
| 
 | 
 | ||||||
|         std::transform(proctex.color_table.begin(), proctex.color_table.end(), new_data.begin(), |         std::transform(proctex.color_table.begin(), proctex.color_table.end(), new_data.begin(), | ||||||
|  | @ -1075,16 +1064,16 @@ void RasterizerVulkan::SyncAndUploadLUTs() { | ||||||
|             proctex_lut_data = new_data; |             proctex_lut_data = new_data; | ||||||
|             std::memcpy(buffer + bytes_used, new_data.data(), |             std::memcpy(buffer + bytes_used, new_data.data(), | ||||||
|                         new_data.size() * sizeof(Common::Vec4f)); |                         new_data.size() * sizeof(Common::Vec4f)); | ||||||
|             uniform_block_data.data.proctex_lut_offset = |             fs_uniform_block_data.data.proctex_lut_offset = | ||||||
|                 static_cast<int>((offset + bytes_used) / sizeof(Common::Vec4f)); |                 static_cast<int>((offset + bytes_used) / sizeof(Common::Vec4f)); | ||||||
|             uniform_block_data.dirty = true; |             fs_uniform_block_data.dirty = true; | ||||||
|             bytes_used += new_data.size() * sizeof(Common::Vec4f); |             bytes_used += new_data.size() * sizeof(Common::Vec4f); | ||||||
|         } |         } | ||||||
|         uniform_block_data.proctex_lut_dirty = false; |         fs_uniform_block_data.proctex_lut_dirty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sync the proctex difference lut
 |     // Sync the proctex difference lut
 | ||||||
|     if (uniform_block_data.proctex_diff_lut_dirty || invalidate) { |     if (fs_uniform_block_data.proctex_diff_lut_dirty || invalidate) { | ||||||
|         std::array<Common::Vec4f, 256> new_data; |         std::array<Common::Vec4f, 256> new_data; | ||||||
| 
 | 
 | ||||||
|         std::transform(proctex.color_diff_table.begin(), proctex.color_diff_table.end(), |         std::transform(proctex.color_diff_table.begin(), proctex.color_diff_table.end(), | ||||||
|  | @ -1097,48 +1086,59 @@ void RasterizerVulkan::SyncAndUploadLUTs() { | ||||||
|             proctex_diff_lut_data = new_data; |             proctex_diff_lut_data = new_data; | ||||||
|             std::memcpy(buffer + bytes_used, new_data.data(), |             std::memcpy(buffer + bytes_used, new_data.data(), | ||||||
|                         new_data.size() * sizeof(Common::Vec4f)); |                         new_data.size() * sizeof(Common::Vec4f)); | ||||||
|             uniform_block_data.data.proctex_diff_lut_offset = |             fs_uniform_block_data.data.proctex_diff_lut_offset = | ||||||
|                 static_cast<int>((offset + bytes_used) / sizeof(Common::Vec4f)); |                 static_cast<int>((offset + bytes_used) / sizeof(Common::Vec4f)); | ||||||
|             uniform_block_data.dirty = true; |             fs_uniform_block_data.dirty = true; | ||||||
|             bytes_used += new_data.size() * sizeof(Common::Vec4f); |             bytes_used += new_data.size() * sizeof(Common::Vec4f); | ||||||
|         } |         } | ||||||
|         uniform_block_data.proctex_diff_lut_dirty = false; |         fs_uniform_block_data.proctex_diff_lut_dirty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     texture_buffer.Commit(static_cast<u32>(bytes_used)); |     texture_buffer.Commit(static_cast<u32>(bytes_used)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerVulkan::UploadUniforms(bool accelerate_draw) { | void RasterizerVulkan::UploadUniforms(bool accelerate_draw) { | ||||||
|     const bool sync_vs = accelerate_draw; |     const bool sync_vs_pica = accelerate_draw; | ||||||
|     const bool sync_fs = uniform_block_data.dirty; |     const bool sync_vs = vs_uniform_block_data.dirty; | ||||||
| 
 |     const bool sync_fs = fs_uniform_block_data.dirty; | ||||||
|     if (!sync_vs && !sync_fs) { |     if (!sync_vs_pica && !sync_vs && !sync_fs) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const u64 uniform_size = uniform_size_aligned_vs + uniform_size_aligned_fs; |     const u64 uniform_size = | ||||||
|  |         uniform_size_aligned_vs_pica + uniform_size_aligned_vs + uniform_size_aligned_fs; | ||||||
|     auto [uniforms, offset, invalidate] = |     auto [uniforms, offset, invalidate] = | ||||||
|         uniform_buffer.Map(uniform_size, uniform_buffer_alignment); |         uniform_buffer.Map(uniform_size, uniform_buffer_alignment); | ||||||
| 
 | 
 | ||||||
|     u32 used_bytes = 0; |     u32 used_bytes = 0; | ||||||
|     if (sync_vs) { |  | ||||||
|         Pica::Shader::VSUniformData vs_uniforms; |  | ||||||
|         vs_uniforms.uniforms.SetFromRegs(regs.vs, Pica::g_state.vs); |  | ||||||
|         std::memcpy(uniforms, &vs_uniforms, sizeof(vs_uniforms)); |  | ||||||
| 
 | 
 | ||||||
|         pipeline_cache.SetBufferOffset(0, offset); |     if (sync_vs || invalidate) { | ||||||
|  |         std::memcpy(uniforms + used_bytes, &vs_uniform_block_data.data, | ||||||
|  |                     sizeof(vs_uniform_block_data.data)); | ||||||
|  | 
 | ||||||
|  |         pipeline_cache.SetBufferOffset(1, offset + used_bytes); | ||||||
|  |         vs_uniform_block_data.dirty = false; | ||||||
|         used_bytes += static_cast<u32>(uniform_size_aligned_vs); |         used_bytes += static_cast<u32>(uniform_size_aligned_vs); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (sync_fs || invalidate) { |     if (sync_fs || invalidate) { | ||||||
|         std::memcpy(uniforms + used_bytes, &uniform_block_data.data, |         std::memcpy(uniforms + used_bytes, &fs_uniform_block_data.data, | ||||||
|                     sizeof(Pica::Shader::UniformData)); |                     sizeof(fs_uniform_block_data.data)); | ||||||
| 
 | 
 | ||||||
|         pipeline_cache.SetBufferOffset(1, offset + used_bytes); |         pipeline_cache.SetBufferOffset(2, offset + used_bytes); | ||||||
|         uniform_block_data.dirty = false; |         fs_uniform_block_data.dirty = false; | ||||||
|         used_bytes += static_cast<u32>(uniform_size_aligned_fs); |         used_bytes += static_cast<u32>(uniform_size_aligned_fs); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (sync_vs_pica) { | ||||||
|  |         VSPicaUniformData vs_uniforms; | ||||||
|  |         vs_uniforms.uniforms.SetFromRegs(regs.vs, Pica::g_state.vs); | ||||||
|  |         std::memcpy(uniforms + used_bytes, &vs_uniforms, sizeof(vs_uniforms)); | ||||||
|  | 
 | ||||||
|  |         pipeline_cache.SetBufferOffset(0, offset + used_bytes); | ||||||
|  |         used_bytes += static_cast<u32>(uniform_size_aligned_vs_pica); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     uniform_buffer.Commit(used_bytes); |     uniform_buffer.Commit(used_bytes); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -60,9 +60,6 @@ public: | ||||||
| private: | private: | ||||||
|     void NotifyFixedFunctionPicaRegisterChanged(u32 id) override; |     void NotifyFixedFunctionPicaRegisterChanged(u32 id) override; | ||||||
| 
 | 
 | ||||||
|     /// Syncs the clip enabled status to match the PICA register
 |  | ||||||
|     void SyncClipEnabled(); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the cull mode to match the PICA register
 |     /// Syncs the cull mode to match the PICA register
 | ||||||
|     void SyncCullMode(); |     void SyncCullMode(); | ||||||
| 
 | 
 | ||||||
|  | @ -163,6 +160,7 @@ private: | ||||||
|     vk::UniqueBufferView texture_rg_view; |     vk::UniqueBufferView texture_rg_view; | ||||||
|     vk::UniqueBufferView texture_rgba_view; |     vk::UniqueBufferView texture_rgba_view; | ||||||
|     u64 uniform_buffer_alignment; |     u64 uniform_buffer_alignment; | ||||||
|  |     u64 uniform_size_aligned_vs_pica; | ||||||
|     u64 uniform_size_aligned_vs; |     u64 uniform_size_aligned_vs; | ||||||
|     u64 uniform_size_aligned_fs; |     u64 uniform_size_aligned_fs; | ||||||
|     bool async_shaders{false}; |     bool async_shaders{false}; | ||||||
|  |  | ||||||
|  | @ -12,9 +12,9 @@ | ||||||
| #include <nihstro/shader_bytecode.h> | #include <nihstro/shader_bytecode.h> | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "video_core/renderer_opengl/gl_shader_decompiler.h" | #include "video_core/shader/generator/glsl_shader_decompiler.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenGL::ShaderDecompiler { | namespace Pica::Shader::Generator::GLSL { | ||||||
| 
 | 
 | ||||||
| using nihstro::DestRegister; | using nihstro::DestRegister; | ||||||
| using nihstro::Instruction; | using nihstro::Instruction; | ||||||
|  | @ -939,34 +939,20 @@ private: | ||||||
|     ShaderWriter shader; |     ShaderWriter shader; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| std::string GetCommonDeclarations() { | std::string DecompileProgram(const Pica::Shader::ProgramCode& program_code, | ||||||
|     return R"( |                              const Pica::Shader::SwizzleData& swizzle_data, u32 main_offset, | ||||||
| struct pica_uniforms { |                              const RegGetter& inputreg_getter, const RegGetter& outputreg_getter, | ||||||
|     bool b[16]; |                              bool sanitize_mul) { | ||||||
|     uvec4 i[4]; |  | ||||||
|     vec4 f[96]; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| bool exec_shader(); |  | ||||||
| 
 |  | ||||||
| )"; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| std::optional<ProgramResult> DecompileProgram(const Pica::Shader::ProgramCode& program_code, |  | ||||||
|                                               const Pica::Shader::SwizzleData& swizzle_data, |  | ||||||
|                                               u32 main_offset, const RegGetter& inputreg_getter, |  | ||||||
|                                               const RegGetter& outputreg_getter, |  | ||||||
|                                               bool sanitize_mul) { |  | ||||||
| 
 | 
 | ||||||
|     try { |     try { | ||||||
|         auto subroutines = ControlFlowAnalyzer(program_code, main_offset).MoveSubroutines(); |         auto subroutines = ControlFlowAnalyzer(program_code, main_offset).MoveSubroutines(); | ||||||
|         GLSLGenerator generator(subroutines, program_code, swizzle_data, main_offset, |         GLSLGenerator generator(subroutines, program_code, swizzle_data, main_offset, | ||||||
|                                 inputreg_getter, outputreg_getter, sanitize_mul); |                                 inputreg_getter, outputreg_getter, sanitize_mul); | ||||||
|         return {ProgramResult{generator.MoveShaderCode()}}; |         return generator.MoveShaderCode(); | ||||||
|     } catch (const DecompileFail& exception) { |     } catch (const DecompileFail& exception) { | ||||||
|         LOG_INFO(HW_GPU, "Shader decompilation failed: {}", exception.what()); |         LOG_INFO(HW_GPU, "Shader decompilation failed: {}", exception.what()); | ||||||
|         return std::nullopt; |         return ""; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace OpenGL::ShaderDecompiler
 | } // namespace Pica::Shader::Generator::GLSL
 | ||||||
							
								
								
									
										21
									
								
								src/video_core/shader/generator/glsl_shader_decompiler.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/video_core/shader/generator/glsl_shader_decompiler.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | // Copyright 2017 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <functional> | ||||||
|  | #include <string> | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "video_core/shader/shader.h" | ||||||
|  | 
 | ||||||
|  | namespace Pica::Shader::Generator::GLSL { | ||||||
|  | 
 | ||||||
|  | using RegGetter = std::function<std::string(u32)>; | ||||||
|  | 
 | ||||||
|  | std::string DecompileProgram(const Pica::Shader::ProgramCode& program_code, | ||||||
|  |                              const Pica::Shader::SwizzleData& swizzle_data, u32 main_offset, | ||||||
|  |                              const RegGetter& inputreg_getter, const RegGetter& outputreg_getter, | ||||||
|  |                              bool sanitize_mul); | ||||||
|  | 
 | ||||||
|  | } // namespace Pica::Shader::Generator::GLSL
 | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										57
									
								
								src/video_core/shader/generator/glsl_shader_gen.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/video_core/shader/generator/glsl_shader_gen.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,57 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "video_core/shader/generator/shader_gen.h" | ||||||
|  | #include "video_core/shader/shader.h" | ||||||
|  | 
 | ||||||
|  | // High precision may or may not be supported in GLES3. If it isn't, use medium precision instead.
 | ||||||
|  | static constexpr char fragment_shader_precision_OES[] = R"( | ||||||
|  | #if GL_ES | ||||||
|  | #ifdef GL_FRAGMENT_PRECISION_HIGH | ||||||
|  | precision highp int; | ||||||
|  | precision highp float; | ||||||
|  | precision highp samplerBuffer; | ||||||
|  | precision highp uimage2D; | ||||||
|  | #else | ||||||
|  | precision mediump int; | ||||||
|  | precision mediump float; | ||||||
|  | precision mediump samplerBuffer; | ||||||
|  | precision mediump uimage2D; | ||||||
|  | #endif // GL_FRAGMENT_PRECISION_HIGH
 | ||||||
|  | #endif | ||||||
|  | )"; | ||||||
|  | 
 | ||||||
|  | namespace Pica::Shader::Generator::GLSL { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generates the GLSL vertex shader program source code that accepts vertices from software shader | ||||||
|  |  * and directly passes them to the fragment shader. | ||||||
|  |  * @returns String of the shader source code | ||||||
|  |  */ | ||||||
|  | std::string GenerateTrivialVertexShader(bool use_clip_planes, bool separable_shader); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generates the GLSL vertex shader program source code for the given VS program | ||||||
|  |  * @returns String of the shader source code; empty on failure | ||||||
|  |  */ | ||||||
|  | std::string GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, const PicaVSConfig& config, | ||||||
|  |                                  bool separable_shader); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * 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, bool separable_shader); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * 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!) | ||||||
|  |  * @returns String of the shader source code | ||||||
|  |  */ | ||||||
|  | std::string GenerateFragmentShader(const PicaFSConfig& config, bool separable_shader); | ||||||
|  | 
 | ||||||
|  | } // namespace Pica::Shader::Generator::GLSL
 | ||||||
							
								
								
									
										281
									
								
								src/video_core/shader/generator/shader_gen.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								src/video_core/shader/generator/shader_gen.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,281 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "common/bit_set.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "video_core/shader/generator/shader_gen.h" | ||||||
|  | #include "video_core/video_core.h" | ||||||
|  | 
 | ||||||
|  | namespace Pica::Shader::Generator { | ||||||
|  | 
 | ||||||
|  | PicaFSConfig::PicaFSConfig(const Pica::Regs& regs, bool has_fragment_shader_interlock, | ||||||
|  |                            bool emulate_logic_op, bool emulate_custom_border_color, | ||||||
|  |                            bool emulate_blend_minmax_factor, bool use_custom_normal_map) { | ||||||
|  |     state.scissor_test_mode.Assign(regs.rasterizer.scissor_test.mode); | ||||||
|  | 
 | ||||||
|  |     state.depthmap_enable.Assign(regs.rasterizer.depthmap_enable); | ||||||
|  | 
 | ||||||
|  |     state.alpha_test_func.Assign(regs.framebuffer.output_merger.alpha_test.enable | ||||||
|  |                                      ? regs.framebuffer.output_merger.alpha_test.func.Value() | ||||||
|  |                                      : Pica::FramebufferRegs::CompareFunc::Always); | ||||||
|  | 
 | ||||||
|  |     state.texture0_type.Assign(regs.texturing.texture0.type); | ||||||
|  | 
 | ||||||
|  |     state.texture2_use_coord1.Assign(regs.texturing.main_config.texture2_use_coord1 != 0); | ||||||
|  | 
 | ||||||
|  |     const auto pica_textures = regs.texturing.GetTextures(); | ||||||
|  |     for (u32 tex_index = 0; tex_index < 3; tex_index++) { | ||||||
|  |         const auto config = pica_textures[tex_index].config; | ||||||
|  |         state.texture_border_color[tex_index].enable_s.Assign( | ||||||
|  |             emulate_custom_border_color && | ||||||
|  |             config.wrap_s == Pica::TexturingRegs::TextureConfig::WrapMode::ClampToBorder); | ||||||
|  |         state.texture_border_color[tex_index].enable_t.Assign( | ||||||
|  |             emulate_custom_border_color && | ||||||
|  |             config.wrap_t == Pica::TexturingRegs::TextureConfig::WrapMode::ClampToBorder); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Emulate logic op in the shader if not supported. This is mostly for mobile GPUs
 | ||||||
|  |     const bool needs_emulate_logic_op = | ||||||
|  |         emulate_logic_op && !regs.framebuffer.output_merger.alphablend_enable; | ||||||
|  | 
 | ||||||
|  |     state.emulate_logic_op.Assign(needs_emulate_logic_op); | ||||||
|  |     if (needs_emulate_logic_op) { | ||||||
|  |         state.logic_op.Assign(regs.framebuffer.output_merger.logic_op); | ||||||
|  |     } else { | ||||||
|  |         state.logic_op.Assign(Pica::FramebufferRegs::LogicOp::NoOp); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Copy relevant tev stages fields.
 | ||||||
|  |     // We don't sync const_color here because of the high variance, it is a
 | ||||||
|  |     // shader uniform instead.
 | ||||||
|  |     const auto& tev_stages = regs.texturing.GetTevStages(); | ||||||
|  |     DEBUG_ASSERT(state.tev_stages.size() == tev_stages.size()); | ||||||
|  |     for (std::size_t i = 0; i < tev_stages.size(); i++) { | ||||||
|  |         const auto& tev_stage = tev_stages[i]; | ||||||
|  |         state.tev_stages[i].sources_raw = tev_stage.sources_raw; | ||||||
|  |         state.tev_stages[i].modifiers_raw = tev_stage.modifiers_raw; | ||||||
|  |         state.tev_stages[i].ops_raw = tev_stage.ops_raw; | ||||||
|  |         state.tev_stages[i].scales_raw = tev_stage.scales_raw; | ||||||
|  |         if (tev_stage.color_op == Pica::TexturingRegs::TevStageConfig::Operation::Dot3_RGBA) { | ||||||
|  |             state.tev_stages[i].sources_raw &= 0xFFF; | ||||||
|  |             state.tev_stages[i].modifiers_raw &= 0xFFF; | ||||||
|  |             state.tev_stages[i].ops_raw &= 0xF; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     state.fog_mode.Assign(regs.texturing.fog_mode); | ||||||
|  |     state.fog_flip.Assign(regs.texturing.fog_flip != 0); | ||||||
|  | 
 | ||||||
|  |     state.combiner_buffer_input.Assign( | ||||||
|  |         regs.texturing.tev_combiner_buffer_input.update_mask_rgb.Value() | | ||||||
|  |         regs.texturing.tev_combiner_buffer_input.update_mask_a.Value() << 4); | ||||||
|  | 
 | ||||||
|  |     // Fragment lighting
 | ||||||
|  |     state.lighting.enable.Assign(!regs.lighting.disable); | ||||||
|  |     if (state.lighting.enable) { | ||||||
|  |         state.lighting.src_num.Assign(regs.lighting.max_light_index + 1); | ||||||
|  | 
 | ||||||
|  |         for (u32 light_index = 0; light_index < state.lighting.src_num; ++light_index) { | ||||||
|  |             const u32 num = regs.lighting.light_enable.GetNum(light_index); | ||||||
|  |             const auto& light = regs.lighting.light[num]; | ||||||
|  |             state.lighting.light[light_index].num.Assign(num); | ||||||
|  |             state.lighting.light[light_index].directional.Assign(light.config.directional != 0); | ||||||
|  |             state.lighting.light[light_index].two_sided_diffuse.Assign( | ||||||
|  |                 light.config.two_sided_diffuse != 0); | ||||||
|  |             state.lighting.light[light_index].geometric_factor_0.Assign( | ||||||
|  |                 light.config.geometric_factor_0 != 0); | ||||||
|  |             state.lighting.light[light_index].geometric_factor_1.Assign( | ||||||
|  |                 light.config.geometric_factor_1 != 0); | ||||||
|  |             state.lighting.light[light_index].dist_atten_enable.Assign( | ||||||
|  |                 !regs.lighting.IsDistAttenDisabled(num)); | ||||||
|  |             state.lighting.light[light_index].spot_atten_enable.Assign( | ||||||
|  |                 !regs.lighting.IsSpotAttenDisabled(num)); | ||||||
|  |             state.lighting.light[light_index].shadow_enable.Assign( | ||||||
|  |                 !regs.lighting.IsShadowDisabled(num)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         state.lighting.lut_d0.enable.Assign(regs.lighting.config1.disable_lut_d0 == 0); | ||||||
|  |         if (state.lighting.lut_d0.enable) { | ||||||
|  |             state.lighting.lut_d0.abs_input.Assign(regs.lighting.abs_lut_input.disable_d0 == 0); | ||||||
|  |             state.lighting.lut_d0.type.Assign(regs.lighting.lut_input.d0.Value()); | ||||||
|  |             state.lighting.lut_d0.scale = | ||||||
|  |                 regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         state.lighting.lut_d1.enable.Assign(regs.lighting.config1.disable_lut_d1 == 0); | ||||||
|  |         if (state.lighting.lut_d1.enable) { | ||||||
|  |             state.lighting.lut_d1.abs_input.Assign(regs.lighting.abs_lut_input.disable_d1 == 0); | ||||||
|  |             state.lighting.lut_d1.type.Assign(regs.lighting.lut_input.d1.Value()); | ||||||
|  |             state.lighting.lut_d1.scale = | ||||||
|  |                 regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // this is a dummy field due to lack of the corresponding register
 | ||||||
|  |         state.lighting.lut_sp.enable.Assign(1); | ||||||
|  |         state.lighting.lut_sp.abs_input.Assign(regs.lighting.abs_lut_input.disable_sp == 0); | ||||||
|  |         state.lighting.lut_sp.type.Assign(regs.lighting.lut_input.sp.Value()); | ||||||
|  |         state.lighting.lut_sp.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.sp); | ||||||
|  | 
 | ||||||
|  |         state.lighting.lut_fr.enable.Assign(regs.lighting.config1.disable_lut_fr == 0); | ||||||
|  |         if (state.lighting.lut_fr.enable) { | ||||||
|  |             state.lighting.lut_fr.abs_input.Assign(regs.lighting.abs_lut_input.disable_fr == 0); | ||||||
|  |             state.lighting.lut_fr.type.Assign(regs.lighting.lut_input.fr.Value()); | ||||||
|  |             state.lighting.lut_fr.scale = | ||||||
|  |                 regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         state.lighting.lut_rr.enable.Assign(regs.lighting.config1.disable_lut_rr == 0); | ||||||
|  |         if (state.lighting.lut_rr.enable) { | ||||||
|  |             state.lighting.lut_rr.abs_input.Assign(regs.lighting.abs_lut_input.disable_rr == 0); | ||||||
|  |             state.lighting.lut_rr.type.Assign(regs.lighting.lut_input.rr.Value()); | ||||||
|  |             state.lighting.lut_rr.scale = | ||||||
|  |                 regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         state.lighting.lut_rg.enable.Assign(regs.lighting.config1.disable_lut_rg == 0); | ||||||
|  |         if (state.lighting.lut_rg.enable) { | ||||||
|  |             state.lighting.lut_rg.abs_input.Assign(regs.lighting.abs_lut_input.disable_rg == 0); | ||||||
|  |             state.lighting.lut_rg.type.Assign(regs.lighting.lut_input.rg.Value()); | ||||||
|  |             state.lighting.lut_rg.scale = | ||||||
|  |                 regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         state.lighting.lut_rb.enable.Assign(regs.lighting.config1.disable_lut_rb == 0); | ||||||
|  |         if (state.lighting.lut_rb.enable) { | ||||||
|  |             state.lighting.lut_rb.abs_input.Assign(regs.lighting.abs_lut_input.disable_rb == 0); | ||||||
|  |             state.lighting.lut_rb.type.Assign(regs.lighting.lut_input.rb.Value()); | ||||||
|  |             state.lighting.lut_rb.scale = | ||||||
|  |                 regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         state.lighting.config.Assign(regs.lighting.config0.config); | ||||||
|  |         state.lighting.enable_primary_alpha.Assign(regs.lighting.config0.enable_primary_alpha); | ||||||
|  |         state.lighting.enable_secondary_alpha.Assign(regs.lighting.config0.enable_secondary_alpha); | ||||||
|  |         state.lighting.bump_mode.Assign(regs.lighting.config0.bump_mode); | ||||||
|  |         state.lighting.bump_selector.Assign(regs.lighting.config0.bump_selector); | ||||||
|  |         state.lighting.bump_renorm.Assign(regs.lighting.config0.disable_bump_renorm == 0); | ||||||
|  |         state.lighting.clamp_highlights.Assign(regs.lighting.config0.clamp_highlights != 0); | ||||||
|  | 
 | ||||||
|  |         state.lighting.enable_shadow.Assign(regs.lighting.config0.enable_shadow != 0); | ||||||
|  |         if (state.lighting.enable_shadow) { | ||||||
|  |             state.lighting.shadow_primary.Assign(regs.lighting.config0.shadow_primary != 0); | ||||||
|  |             state.lighting.shadow_secondary.Assign(regs.lighting.config0.shadow_secondary != 0); | ||||||
|  |             state.lighting.shadow_invert.Assign(regs.lighting.config0.shadow_invert != 0); | ||||||
|  |             state.lighting.shadow_alpha.Assign(regs.lighting.config0.shadow_alpha != 0); | ||||||
|  |             state.lighting.shadow_selector.Assign(regs.lighting.config0.shadow_selector); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     state.proctex.enable.Assign(regs.texturing.main_config.texture3_enable); | ||||||
|  |     if (state.proctex.enable) { | ||||||
|  |         state.proctex.coord.Assign(regs.texturing.main_config.texture3_coordinates); | ||||||
|  |         state.proctex.u_clamp.Assign(regs.texturing.proctex.u_clamp); | ||||||
|  |         state.proctex.v_clamp.Assign(regs.texturing.proctex.v_clamp); | ||||||
|  |         state.proctex.color_combiner.Assign(regs.texturing.proctex.color_combiner); | ||||||
|  |         state.proctex.alpha_combiner.Assign(regs.texturing.proctex.alpha_combiner); | ||||||
|  |         state.proctex.separate_alpha.Assign(regs.texturing.proctex.separate_alpha); | ||||||
|  |         state.proctex.noise_enable.Assign(regs.texturing.proctex.noise_enable); | ||||||
|  |         state.proctex.u_shift.Assign(regs.texturing.proctex.u_shift); | ||||||
|  |         state.proctex.v_shift.Assign(regs.texturing.proctex.v_shift); | ||||||
|  |         state.proctex.lut_width = regs.texturing.proctex_lut.width; | ||||||
|  |         state.proctex.lut_offset0 = regs.texturing.proctex_lut_offset.level0; | ||||||
|  |         state.proctex.lut_offset1 = regs.texturing.proctex_lut_offset.level1; | ||||||
|  |         state.proctex.lut_offset2 = regs.texturing.proctex_lut_offset.level2; | ||||||
|  |         state.proctex.lut_offset3 = regs.texturing.proctex_lut_offset.level3; | ||||||
|  |         state.proctex.lod_min = regs.texturing.proctex_lut.lod_min; | ||||||
|  |         state.proctex.lod_max = regs.texturing.proctex_lut.lod_max; | ||||||
|  |         state.proctex.lut_filter.Assign(regs.texturing.proctex_lut.filter); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto alpha_eq = regs.framebuffer.output_merger.alpha_blending.blend_equation_a.Value(); | ||||||
|  |     const auto rgb_eq = regs.framebuffer.output_merger.alpha_blending.blend_equation_rgb.Value(); | ||||||
|  |     if (emulate_blend_minmax_factor && regs.framebuffer.output_merger.alphablend_enable) { | ||||||
|  |         if (rgb_eq == Pica::FramebufferRegs::BlendEquation::Max || | ||||||
|  |             rgb_eq == Pica::FramebufferRegs::BlendEquation::Min) { | ||||||
|  |             state.rgb_blend.emulate_blending = true; | ||||||
|  |             state.rgb_blend.eq = rgb_eq; | ||||||
|  |             state.rgb_blend.src_factor = | ||||||
|  |                 regs.framebuffer.output_merger.alpha_blending.factor_source_rgb; | ||||||
|  |             state.rgb_blend.dst_factor = | ||||||
|  |                 regs.framebuffer.output_merger.alpha_blending.factor_dest_rgb; | ||||||
|  |         } | ||||||
|  |         if (alpha_eq == Pica::FramebufferRegs::BlendEquation::Max || | ||||||
|  |             alpha_eq == Pica::FramebufferRegs::BlendEquation::Min) { | ||||||
|  |             state.alpha_blend.emulate_blending = true; | ||||||
|  |             state.alpha_blend.eq = alpha_eq; | ||||||
|  |             state.alpha_blend.src_factor = | ||||||
|  |                 regs.framebuffer.output_merger.alpha_blending.factor_source_a; | ||||||
|  |             state.alpha_blend.dst_factor = | ||||||
|  |                 regs.framebuffer.output_merger.alpha_blending.factor_dest_a; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     state.shadow_rendering.Assign(regs.framebuffer.output_merger.fragment_operation_mode == | ||||||
|  |                                   Pica::FramebufferRegs::FragmentOperationMode::Shadow); | ||||||
|  |     state.shadow_texture_orthographic.Assign(regs.texturing.shadow.orthographic != 0); | ||||||
|  | 
 | ||||||
|  |     // We only need fragment shader interlock when shadow rendering.
 | ||||||
|  |     state.use_fragment_shader_interlock.Assign(state.shadow_rendering && | ||||||
|  |                                                has_fragment_shader_interlock); | ||||||
|  |     state.use_custom_normal_map.Assign(use_custom_normal_map); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PicaGSConfigState::Init(const Pica::Regs& regs, bool use_clip_planes_) { | ||||||
|  |     use_clip_planes = use_clip_planes_; | ||||||
|  | 
 | ||||||
|  |     vs_output_attributes = Common::BitSet<u32>(regs.vs.output_mask).Count(); | ||||||
|  |     gs_output_attributes = vs_output_attributes; | ||||||
|  | 
 | ||||||
|  |     semantic_maps.fill({16, 0}); | ||||||
|  |     for (u32 attrib = 0; attrib < regs.rasterizer.vs_output_total; ++attrib) { | ||||||
|  |         const std::array semantics{ | ||||||
|  |             regs.rasterizer.vs_output_attributes[attrib].map_x.Value(), | ||||||
|  |             regs.rasterizer.vs_output_attributes[attrib].map_y.Value(), | ||||||
|  |             regs.rasterizer.vs_output_attributes[attrib].map_z.Value(), | ||||||
|  |             regs.rasterizer.vs_output_attributes[attrib].map_w.Value(), | ||||||
|  |         }; | ||||||
|  |         for (u32 comp = 0; comp < 4; ++comp) { | ||||||
|  |             const auto semantic = semantics[comp]; | ||||||
|  |             if (static_cast<std::size_t>(semantic) < 24) { | ||||||
|  |                 semantic_maps[static_cast<std::size_t>(semantic)] = {attrib, comp}; | ||||||
|  |             } else if (semantic != Pica::RasterizerRegs::VSOutputAttributes::INVALID) { | ||||||
|  |                 LOG_ERROR(Render, "Invalid/unknown semantic id: {}", semantic); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PicaVSConfigState::Init(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup, | ||||||
|  |                              bool use_clip_planes_, bool use_geometry_shader_) { | ||||||
|  |     use_clip_planes = use_clip_planes_; | ||||||
|  |     use_geometry_shader = use_geometry_shader_; | ||||||
|  | 
 | ||||||
|  |     program_hash = setup.GetProgramCodeHash(); | ||||||
|  |     swizzle_hash = setup.GetSwizzleDataHash(); | ||||||
|  |     main_offset = regs.vs.main_offset; | ||||||
|  |     sanitize_mul = VideoCore::g_hw_shader_accurate_mul; | ||||||
|  | 
 | ||||||
|  |     num_outputs = 0; | ||||||
|  |     load_flags.fill(AttribLoadFlags::Float); | ||||||
|  |     output_map.fill(16); | ||||||
|  | 
 | ||||||
|  |     for (int reg : Common::BitSet<u32>(regs.vs.output_mask)) { | ||||||
|  |         output_map[reg] = num_outputs++; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!use_geometry_shader_) { | ||||||
|  |         gs_state.Init(regs, use_clip_planes_); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PicaVSConfig::PicaVSConfig(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup, | ||||||
|  |                            bool use_clip_planes_, bool use_geometry_shader_) { | ||||||
|  |     state.Init(regs, setup, use_clip_planes_, use_geometry_shader_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PicaFixedGSConfig::PicaFixedGSConfig(const Pica::Regs& regs, bool use_clip_planes_) { | ||||||
|  |     state.Init(regs, use_clip_planes_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Pica::Shader::Generator
 | ||||||
|  | @ -4,14 +4,17 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <optional> |  | ||||||
| #include "common/hash.h" | #include "common/hash.h" | ||||||
| #include "video_core/regs.h" | #include "video_core/regs.h" | ||||||
| #include "video_core/shader/shader.h" | #include "video_core/shader/shader.h" | ||||||
| 
 | 
 | ||||||
| namespace Vulkan { | namespace Pica::Shader::Generator { | ||||||
| 
 | 
 | ||||||
| class Instance; | enum ProgramType : u32 { | ||||||
|  |     VS = 0, | ||||||
|  |     GS = 2, | ||||||
|  |     FS = 1, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| enum Attributes { | enum Attributes { | ||||||
|     ATTRIBUTE_POSITION, |     ATTRIBUTE_POSITION, | ||||||
|  | @ -31,13 +34,13 @@ struct TevStageConfigRaw { | ||||||
|     u32 ops_raw; |     u32 ops_raw; | ||||||
|     u32 scales_raw; |     u32 scales_raw; | ||||||
|     explicit operator Pica::TexturingRegs::TevStageConfig() const noexcept { |     explicit operator Pica::TexturingRegs::TevStageConfig() const noexcept { | ||||||
|         Pica::TexturingRegs::TevStageConfig stage; |         return { | ||||||
|         stage.sources_raw = sources_raw; |             .sources_raw = sources_raw, | ||||||
|         stage.modifiers_raw = modifiers_raw; |             .modifiers_raw = modifiers_raw, | ||||||
|         stage.ops_raw = ops_raw; |             .ops_raw = ops_raw, | ||||||
|         stage.const_color = 0; |             .const_color = 0, | ||||||
|         stage.scales_raw = scales_raw; |             .scales_raw = scales_raw, | ||||||
|         return stage; |         }; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -56,6 +59,7 @@ struct PicaFSConfigState { | ||||||
|         BitField<27, 1, u32> shadow_rendering; |         BitField<27, 1, u32> shadow_rendering; | ||||||
|         BitField<28, 1, u32> shadow_texture_orthographic; |         BitField<28, 1, u32> shadow_texture_orthographic; | ||||||
|         BitField<29, 1, u32> use_fragment_shader_interlock; |         BitField<29, 1, u32> use_fragment_shader_interlock; | ||||||
|  |         BitField<30, 1, u32> use_custom_normal_map; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     union { |     union { | ||||||
|  | @ -127,24 +131,33 @@ struct PicaFSConfigState { | ||||||
|         u8 lod_min; |         u8 lod_min; | ||||||
|         u8 lod_max; |         u8 lod_max; | ||||||
|     } proctex; |     } proctex; | ||||||
|  | 
 | ||||||
|  |     struct { | ||||||
|  |         bool emulate_blending; | ||||||
|  |         Pica::FramebufferRegs::BlendEquation eq; | ||||||
|  |         Pica::FramebufferRegs::BlendFactor src_factor; | ||||||
|  |         Pica::FramebufferRegs::BlendFactor dst_factor; | ||||||
|  |     } rgb_blend, alpha_blend; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * This struct contains all state used to generate the GLSL fragment shader that emulates the |  * 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 |  * 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 |  * programs. The functions in glsl_shader_gen.cpp should retrieve state from this struct only, not | ||||||
|  * directly accessing Pica registers. This should reduce the risk of bugs in shader generation where |  * by directly accessing Pica registers. This should reduce the risk of bugs in shader generation | ||||||
|  * Pica state is not being captured in the shader cache key, thereby resulting in (what should be) |  * where Pica state is not being captured in the shader cache key, thereby resulting in (what should | ||||||
|  * two separate shaders sharing the same key. |  * be) two separate shaders sharing the same key. | ||||||
|  */ |  */ | ||||||
| struct PicaFSConfig : Common::HashableStruct<PicaFSConfigState> { | struct PicaFSConfig : Common::HashableStruct<PicaFSConfigState> { | ||||||
|     PicaFSConfig(const Pica::Regs& regs, const Instance& instance); |     PicaFSConfig(const Pica::Regs& regs, bool has_fragment_shader_interlock, bool emulate_logic_op, | ||||||
|  |                  bool emulate_custom_border_color, bool emulate_blend_minmax_factor, | ||||||
|  |                  bool use_custom_normal_map = false); | ||||||
| 
 | 
 | ||||||
|     bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { |     [[nodiscard]] bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { | ||||||
|         return (stage_index < 4) && (state.combiner_buffer_input & (1 << stage_index)); |         return (stage_index < 4) && (state.combiner_buffer_input & (1 << stage_index)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { |     [[nodiscard]] bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { | ||||||
|         return (stage_index < 4) && ((state.combiner_buffer_input >> 4) & (1 << stage_index)); |         return (stage_index < 4) && ((state.combiner_buffer_input >> 4) & (1 << stage_index)); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  | @ -158,12 +171,36 @@ enum class AttribLoadFlags { | ||||||
| DECLARE_ENUM_FLAG_OPERATORS(AttribLoadFlags) | DECLARE_ENUM_FLAG_OPERATORS(AttribLoadFlags) | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * This struct contains common information to identify a GL vertex/geometry shader generated from |  * This struct contains common information to identify a GLSL geometry shader generated from | ||||||
|  * PICA vertex/geometry shader. |  * PICA geometry shader. | ||||||
|  */ |  */ | ||||||
| struct PicaShaderConfigCommon { | struct PicaGSConfigState { | ||||||
|     void Init(const Pica::RasterizerRegs& rasterizer, const Pica::ShaderRegs& regs, |     void Init(const Pica::Regs& regs, bool use_clip_planes_); | ||||||
|               Pica::Shader::ShaderSetup& setup); | 
 | ||||||
|  |     bool use_clip_planes; | ||||||
|  | 
 | ||||||
|  |     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 common information to identify a GLSL vertex shader generated from | ||||||
|  |  * PICA vertex shader. | ||||||
|  |  */ | ||||||
|  | struct PicaVSConfigState { | ||||||
|  |     void Init(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup, bool use_clip_planes_, | ||||||
|  |               bool use_geometry_shader_); | ||||||
|  | 
 | ||||||
|  |     bool use_clip_planes; | ||||||
|  |     bool use_geometry_shader; | ||||||
| 
 | 
 | ||||||
|     u64 program_hash; |     u64 program_hash; | ||||||
|     u64 swizzle_hash; |     u64 swizzle_hash; | ||||||
|  | @ -177,103 +214,46 @@ struct PicaShaderConfigCommon { | ||||||
|     // output_map[output register index] -> output attribute index
 |     // output_map[output register index] -> output attribute index
 | ||||||
|     std::array<u32, 16> output_map; |     std::array<u32, 16> output_map; | ||||||
| 
 | 
 | ||||||
|     bool use_geometry_shader; |     PicaGSConfigState gs_state; | ||||||
|     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 |  * This struct contains information to identify a GL vertex shader generated from PICA vertex | ||||||
|  * shader. |  * shader. | ||||||
|  */ |  */ | ||||||
| struct PicaVSConfig : Common::HashableStruct<PicaShaderConfigCommon> { | struct PicaVSConfig : Common::HashableStruct<PicaVSConfigState> { | ||||||
|     explicit PicaVSConfig(const Pica::RasterizerRegs& rasterizer, const Pica::ShaderRegs& regs, |     explicit PicaVSConfig(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup, | ||||||
|                           Pica::Shader::ShaderSetup& setup, const Instance& instance); |                           bool use_clip_planes_, bool use_geometry_shader_); | ||||||
|     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 |  * This struct contains information to identify a GL geometry shader generated from PICA no-geometry | ||||||
|  * shader pipeline |  * shader pipeline | ||||||
|  */ |  */ | ||||||
| struct PicaFixedGSConfig : Common::HashableStruct<PicaGSConfigCommonRaw> { | struct PicaFixedGSConfig : Common::HashableStruct<PicaGSConfigState> { | ||||||
|     explicit PicaFixedGSConfig(const Pica::Regs& regs, const Instance& instance); |     explicit PicaFixedGSConfig(const Pica::Regs& regs, bool use_clip_planes_); | ||||||
|     bool use_clip_planes; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | } // namespace Pica::Shader::Generator
 | ||||||
|  * 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 { | namespace std { | ||||||
| template <> | template <> | ||||||
| struct hash<Vulkan::PicaFSConfig> { | struct hash<Pica::Shader::Generator::PicaFSConfig> { | ||||||
|     std::size_t operator()(const Vulkan::PicaFSConfig& k) const noexcept { |     std::size_t operator()(const Pica::Shader::Generator::PicaFSConfig& k) const noexcept { | ||||||
|         return k.Hash(); |         return k.Hash(); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <> | template <> | ||||||
| struct hash<Vulkan::PicaVSConfig> { | struct hash<Pica::Shader::Generator::PicaVSConfig> { | ||||||
|     std::size_t operator()(const Vulkan::PicaVSConfig& k) const noexcept { |     std::size_t operator()(const Pica::Shader::Generator::PicaVSConfig& k) const noexcept { | ||||||
|         return k.Hash(); |         return k.Hash(); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <> | template <> | ||||||
| struct hash<Vulkan::PicaFixedGSConfig> { | struct hash<Pica::Shader::Generator::PicaFixedGSConfig> { | ||||||
|     std::size_t operator()(const Vulkan::PicaFixedGSConfig& k) const noexcept { |     std::size_t operator()(const Pica::Shader::Generator::PicaFixedGSConfig& k) const noexcept { | ||||||
|         return k.Hash(); |         return k.Hash(); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
							
								
								
									
										26
									
								
								src/video_core/shader/generator/shader_uniforms.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/video_core/shader/generator/shader_uniforms.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include "video_core/shader/generator/shader_uniforms.h" | ||||||
|  | #include "video_core/shader/shader.h" | ||||||
|  | 
 | ||||||
|  | namespace Pica::Shader::Generator { | ||||||
|  | 
 | ||||||
|  | void PicaUniformsData::SetFromRegs(const Pica::ShaderRegs& regs, | ||||||
|  |                                    const Pica::Shader::ShaderSetup& setup) { | ||||||
|  |     std::transform(std::begin(setup.uniforms.b), std::end(setup.uniforms.b), std::begin(bools), | ||||||
|  |                    [](bool value) -> BoolAligned { return {value ? 1 : 0}; }); | ||||||
|  |     std::transform(std::begin(regs.int_uniforms), std::end(regs.int_uniforms), std::begin(i), | ||||||
|  |                    [](const auto& value) -> Common::Vec4u { | ||||||
|  |                        return {value.x.Value(), value.y.Value(), value.z.Value(), value.w.Value()}; | ||||||
|  |                    }); | ||||||
|  |     std::transform(std::begin(setup.uniforms.f), std::end(setup.uniforms.f), std::begin(f), | ||||||
|  |                    [](const auto& value) -> Common::Vec4f { | ||||||
|  |                        return {value.x.ToFloat32(), value.y.ToFloat32(), value.z.ToFloat32(), | ||||||
|  |                                value.w.ToFloat32()}; | ||||||
|  |                    }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Pica::Shader::Generator
 | ||||||
|  | @ -12,10 +12,10 @@ struct ShaderRegs; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| namespace Pica::Shader { | namespace Pica::Shader { | ||||||
| 
 |  | ||||||
| struct ShaderSetup; | struct ShaderSetup; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| enum class UniformBindings : u32 { Common, VS, GS }; | namespace Pica::Shader::Generator { | ||||||
| 
 | 
 | ||||||
| struct LightSrc { | struct LightSrc { | ||||||
|     alignas(16) Common::Vec3f specular_0; |     alignas(16) Common::Vec3f specular_0; | ||||||
|  | @ -34,7 +34,7 @@ struct LightSrc { | ||||||
|  *       the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. |  *       the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. | ||||||
|  *       Not following that rule will cause problems on some AMD drivers. |  *       Not following that rule will cause problems on some AMD drivers. | ||||||
|  */ |  */ | ||||||
| struct UniformData { | struct FSUniformData { | ||||||
|     int framebuffer_scale; |     int framebuffer_scale; | ||||||
|     int alphatest_ref; |     int alphatest_ref; | ||||||
|     float depth_scale; |     float depth_scale; | ||||||
|  | @ -53,7 +53,6 @@ struct UniformData { | ||||||
|     int proctex_diff_lut_offset; |     int proctex_diff_lut_offset; | ||||||
|     float proctex_bias; |     float proctex_bias; | ||||||
|     int shadow_texture_bias; |     int shadow_texture_bias; | ||||||
|     alignas(4) bool enable_clip1; |  | ||||||
|     alignas(16) Common::Vec4i lighting_lut_offset[LightingRegs::NumLightingSampler / 4]; |     alignas(16) Common::Vec4i lighting_lut_offset[LightingRegs::NumLightingSampler / 4]; | ||||||
|     alignas(16) Common::Vec3f fog_color; |     alignas(16) Common::Vec3f fog_color; | ||||||
|     alignas(8) Common::Vec2f proctex_noise_f; |     alignas(8) Common::Vec2f proctex_noise_f; | ||||||
|  | @ -65,13 +64,12 @@ struct UniformData { | ||||||
|     alignas(16) Common::Vec4f tev_combiner_buffer_color; |     alignas(16) Common::Vec4f tev_combiner_buffer_color; | ||||||
|     alignas(16) Common::Vec3f tex_lod_bias; |     alignas(16) Common::Vec3f tex_lod_bias; | ||||||
|     alignas(16) Common::Vec4f tex_border_color[3]; |     alignas(16) Common::Vec4f tex_border_color[3]; | ||||||
|     alignas(16) Common::Vec4f clip_coef; |  | ||||||
|     alignas(16) Common::Vec4f blend_color; |     alignas(16) Common::Vec4f blend_color; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static_assert(sizeof(UniformData) == 0x540, | static_assert(sizeof(FSUniformData) == 0x530, | ||||||
|               "The size of the UniformData does not match the structure in the shader"); |               "The size of the UniformData does not match the structure in the shader"); | ||||||
| static_assert(sizeof(UniformData) < 16384, | static_assert(sizeof(FSUniformData) < 16384, | ||||||
|               "UniformData structure must be less than 16kb as per the OpenGL spec"); |               "UniformData structure must be less than 16kb as per the OpenGL spec"); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -91,13 +89,20 @@ struct PicaUniformsData { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct VSUniformData { | struct VSUniformData { | ||||||
|     PicaUniformsData uniforms; |     bool enable_clip1; | ||||||
|  |     alignas(16) Common::Vec4f clip_coef; | ||||||
| }; | }; | ||||||
| static_assert(sizeof(VSUniformData) == 1856, | static_assert(sizeof(VSUniformData) == 32, | ||||||
|               "The size of the VSUniformData does not match the structure in the shader"); |               "The size of the VSUniformData does not match the structure in the shader"); | ||||||
| static_assert(sizeof(VSUniformData) < 16384, | static_assert(sizeof(VSUniformData) < 16384, | ||||||
|               "VSUniformData structure must be less than 16kb as per the OpenGL spec"); |               "VSUniformData structure must be less than 16kb as per the OpenGL spec"); | ||||||
| 
 | 
 | ||||||
| std::string BuildShaderUniformDefinitions(const std::string& extra_layout_parameters = ""); | struct VSPicaUniformData { | ||||||
|  |     alignas(16) PicaUniformsData uniforms; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(VSPicaUniformData) == 1856, | ||||||
|  |               "The size of the VSPicaUniformData does not match the structure in the shader"); | ||||||
|  | static_assert(sizeof(VSPicaUniformData) < 16384, | ||||||
|  |               "VSPicaUniformData structure must be less than 16kb as per the OpenGL spec"); | ||||||
| 
 | 
 | ||||||
| } // namespace Pica::Shader
 | } // namespace Pica::Shader::Generator
 | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/telemetry_session.h" | #include "core/telemetry_session.h" | ||||||
| #include "video_core/renderer_vulkan/vk_shader_gen_spv.h" | #include "video_core/shader/generator/spv_shader_gen.h" | ||||||
| 
 | 
 | ||||||
| using Pica::FramebufferRegs; | using Pica::FramebufferRegs; | ||||||
| using Pica::LightingRegs; | using Pica::LightingRegs; | ||||||
|  | @ -12,7 +12,7 @@ using Pica::RasterizerRegs; | ||||||
| using Pica::TexturingRegs; | using Pica::TexturingRegs; | ||||||
| using TevStageConfig = TexturingRegs::TevStageConfig; | using TevStageConfig = TexturingRegs::TevStageConfig; | ||||||
| 
 | 
 | ||||||
| namespace Vulkan { | namespace Pica::Shader::Generator::SPIRV { | ||||||
| 
 | 
 | ||||||
| constexpr u32 SPIRV_VERSION_1_3 = 0x00010300; | constexpr u32 SPIRV_VERSION_1_3 = 0x00010300; | ||||||
| 
 | 
 | ||||||
|  | @ -52,7 +52,7 @@ void FragmentModule::Generate() { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     combiner_buffer = ConstF32(0.f, 0.f, 0.f, 0.f); |     combiner_buffer = ConstF32(0.f, 0.f, 0.f, 0.f); | ||||||
|     next_combiner_buffer = GetShaderDataMember(vec_ids.Get(4), ConstS32(27)); |     next_combiner_buffer = GetShaderDataMember(vec_ids.Get(4), ConstS32(26)); | ||||||
|     last_tex_env_out = rounded_primary_color; |     last_tex_env_out = rounded_primary_color; | ||||||
| 
 | 
 | ||||||
|     // Write shader bytecode to emulate PICA TEV stages
 |     // Write shader bytecode to emulate PICA TEV stages
 | ||||||
|  | @ -192,7 +192,7 @@ void FragmentModule::WriteFog() { | ||||||
|     // Blend the fog
 |     // Blend the fog
 | ||||||
|     const Id tex_env_rgb{ |     const Id tex_env_rgb{ | ||||||
|         OpVectorShuffle(vec_ids.Get(3), last_tex_env_out, last_tex_env_out, 0, 1, 2)}; |         OpVectorShuffle(vec_ids.Get(3), last_tex_env_out, last_tex_env_out, 0, 1, 2)}; | ||||||
|     const Id fog_color{GetShaderDataMember(vec_ids.Get(3), ConstS32(20))}; |     const Id fog_color{GetShaderDataMember(vec_ids.Get(3), ConstS32(19))}; | ||||||
|     const Id fog_factor_rgb{ |     const Id fog_factor_rgb{ | ||||||
|         OpCompositeConstruct(vec_ids.Get(3), fog_factor, fog_factor, fog_factor)}; |         OpCompositeConstruct(vec_ids.Get(3), fog_factor, fog_factor, fog_factor)}; | ||||||
|     const Id fog_result{OpFMix(vec_ids.Get(3), fog_color, tex_env_rgb, fog_factor_rgb)}; |     const Id fog_result{OpFMix(vec_ids.Get(3), fog_color, tex_env_rgb, fog_factor_rgb)}; | ||||||
|  | @ -202,7 +202,7 @@ void FragmentModule::WriteFog() { | ||||||
| void FragmentModule::WriteGas() { | void FragmentModule::WriteGas() { | ||||||
|     // TODO: Implement me
 |     // TODO: Implement me
 | ||||||
|     telemetry.AddField(Common::Telemetry::FieldType::Session, "VideoCore_Pica_UseGasMode", true); |     telemetry.AddField(Common::Telemetry::FieldType::Session, "VideoCore_Pica_UseGasMode", true); | ||||||
|     LOG_CRITICAL(Render_Vulkan, "Unimplemented gas mode"); |     LOG_CRITICAL(Render, "Unimplemented gas mode"); | ||||||
|     OpKill(); |     OpKill(); | ||||||
|     OpFunctionEnd(); |     OpFunctionEnd(); | ||||||
| } | } | ||||||
|  | @ -380,7 +380,7 @@ void FragmentModule::WriteLighting() { | ||||||
|         const auto GetLightMember = [&](s32 member) -> Id { |         const auto GetLightMember = [&](s32 member) -> Id { | ||||||
|             const Id member_type = member < 6 ? vec_ids.Get(3) : f32_id; |             const Id member_type = member < 6 ? vec_ids.Get(3) : f32_id; | ||||||
|             const Id light_num{ConstS32(static_cast<s32>(lighting.light[light_index].num.Value()))}; |             const Id light_num{ConstS32(static_cast<s32>(lighting.light[light_index].num.Value()))}; | ||||||
|             return GetShaderDataMember(member_type, ConstS32(25), light_num, ConstS32(member)); |             return GetShaderDataMember(member_type, ConstS32(24), light_num, ConstS32(member)); | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         // Compute light vector (directional or positional)
 |         // Compute light vector (directional or positional)
 | ||||||
|  | @ -583,7 +583,7 @@ void FragmentModule::WriteLighting() { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Sum final lighting result
 |     // Sum final lighting result
 | ||||||
|     const Id lighting_global_ambient{GetShaderDataMember(vec_ids.Get(3), ConstS32(24))}; |     const Id lighting_global_ambient{GetShaderDataMember(vec_ids.Get(3), ConstS32(23))}; | ||||||
|     const Id lighting_global_ambient_rgba{ |     const Id lighting_global_ambient_rgba{ | ||||||
|         PadVectorF32(lighting_global_ambient, vec_ids.Get(4), 0.f)}; |         PadVectorF32(lighting_global_ambient, vec_ids.Get(4), 0.f)}; | ||||||
|     const Id zero_vec{ConstF32(0.f, 0.f, 0.f, 0.f)}; |     const Id zero_vec{ConstF32(0.f, 0.f, 0.f, 0.f)}; | ||||||
|  | @ -706,7 +706,7 @@ void FragmentModule::WriteAlphaTestCondition(FramebufferRegs::CompareFunc func) | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     default: |     default: | ||||||
|         LOG_CRITICAL(Render_Vulkan, "Unknown alpha test condition {}", func); |         LOG_CRITICAL(Render, "Unknown alpha test condition {}", func); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -791,7 +791,7 @@ Id FragmentModule::AppendProcTexShiftOffset(Id v, ProcTexShift mode, ProcTexClam | ||||||
|     case ProcTexShift::Even: |     case ProcTexShift::Even: | ||||||
|         return shift(true); |         return shift(true); | ||||||
|     default: |     default: | ||||||
|         LOG_CRITICAL(Render_Vulkan, "Unknown shift mode {}", mode); |         LOG_CRITICAL(Render, "Unknown shift mode {}", mode); | ||||||
|         return ConstF32(0.f); |         return ConstF32(0.f); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -819,7 +819,7 @@ Id FragmentModule::AppendProcTexClamp(Id var, ProcTexClamp mode) { | ||||||
|     case ProcTexClamp::Pulse: |     case ProcTexClamp::Pulse: | ||||||
|         return OpSelect(f32_id, OpFOrdGreaterThan(bool_id, var, ConstF32(0.5f)), one, zero); |         return OpSelect(f32_id, OpFOrdGreaterThan(bool_id, var, ConstF32(0.5f)), one, zero); | ||||||
|     default: |     default: | ||||||
|         LOG_CRITICAL(Render_Vulkan, "Unknown clamp mode {}", mode); |         LOG_CRITICAL(Render, "Unknown clamp mode {}", mode); | ||||||
|         return OpFMin(f32_id, var, one); |         return OpFMin(f32_id, var, one); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -851,7 +851,7 @@ Id FragmentModule::AppendProcTexCombineAndMap(ProcTexCombiner combiner, Id u, Id | ||||||
|             return OpFMin(f32_id, OpFMul(f32_id, r, ConstF32(0.5f)), ConstF32(1.f)); |             return OpFMin(f32_id, OpFMul(f32_id, r, ConstF32(0.5f)), ConstF32(1.f)); | ||||||
|         } |         } | ||||||
|         default: |         default: | ||||||
|             LOG_CRITICAL(Render_Vulkan, "Unknown combiner {}", combiner); |             LOG_CRITICAL(Render, "Unknown combiner {}", combiner); | ||||||
|             return ConstF32(0.f); |             return ConstF32(0.f); | ||||||
|         } |         } | ||||||
|     }(); |     }(); | ||||||
|  | @ -916,7 +916,7 @@ void FragmentModule::DefineTexSampler(u32 texture_unit) { | ||||||
| 
 | 
 | ||||||
|         AddLabel(border_label); |         AddLabel(border_label); | ||||||
|         const Id border_color{ |         const Id border_color{ | ||||||
|             GetShaderDataMember(vec_ids.Get(4), ConstS32(29), ConstU32(texture_unit))}; |             GetShaderDataMember(vec_ids.Get(4), ConstS32(28), ConstU32(texture_unit))}; | ||||||
|         OpReturnValue(border_color); |         OpReturnValue(border_color); | ||||||
| 
 | 
 | ||||||
|         AddLabel(not_border_label); |         AddLabel(not_border_label); | ||||||
|  | @ -937,7 +937,7 @@ void FragmentModule::DefineTexSampler(u32 texture_unit) { | ||||||
|         const Id dx_dy_max{ |         const Id dx_dy_max{ | ||||||
|             OpFMax(f32_id, OpCompositeExtract(f32_id, d, 0), OpCompositeExtract(f32_id, d, 1))}; |             OpFMax(f32_id, OpCompositeExtract(f32_id, d, 0), OpCompositeExtract(f32_id, d, 1))}; | ||||||
|         const Id lod{OpLog2(f32_id, dx_dy_max)}; |         const Id lod{OpLog2(f32_id, dx_dy_max)}; | ||||||
|         const Id lod_bias{GetShaderDataMember(f32_id, ConstS32(28), ConstU32(texture_unit))}; |         const Id lod_bias{GetShaderDataMember(f32_id, ConstS32(27), ConstU32(texture_unit))}; | ||||||
|         const Id biased_lod{OpFAdd(f32_id, lod, lod_bias)}; |         const Id biased_lod{OpFAdd(f32_id, lod, lod_bias)}; | ||||||
|         return OpImageSampleExplicitLod(vec_ids.Get(4), sampled_image, texcoord, |         return OpImageSampleExplicitLod(vec_ids.Get(4), sampled_image, texcoord, | ||||||
|                                         spv::ImageOperandsMask::Lod, biased_lod); |                                         spv::ImageOperandsMask::Lod, biased_lod); | ||||||
|  | @ -976,7 +976,7 @@ void FragmentModule::DefineTexSampler(u32 texture_unit) { | ||||||
|             // return "shadowTextureCube(texcoord0, texcoord0_w)";
 |             // return "shadowTextureCube(texcoord0, texcoord0_w)";
 | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             LOG_CRITICAL(Render_Vulkan, "Unhandled texture type {:x}", state.texture0_type.Value()); |             LOG_CRITICAL(Render, "Unhandled texture type {:x}", state.texture0_type.Value()); | ||||||
|             UNIMPLEMENTED(); |             UNIMPLEMENTED(); | ||||||
|             ret_val = zero_vec; |             ret_val = zero_vec; | ||||||
|             break; |             break; | ||||||
|  | @ -1012,7 +1012,7 @@ Id FragmentModule::ProcTexSampler() { | ||||||
|         const Id texcoord{OpLoad(vec_ids.Get(2), texcoord_id[config.state.proctex.coord.Value()])}; |         const Id texcoord{OpLoad(vec_ids.Get(2), texcoord_id[config.state.proctex.coord.Value()])}; | ||||||
|         uv = OpFAbs(vec_ids.Get(2), texcoord); |         uv = OpFAbs(vec_ids.Get(2), texcoord); | ||||||
|     } else { |     } else { | ||||||
|         LOG_CRITICAL(Render_Vulkan, "Unexpected proctex.coord >= 3"); |         LOG_CRITICAL(Render, "Unexpected proctex.coord >= 3"); | ||||||
|         uv = OpFAbs(vec_ids.Get(2), OpLoad(vec_ids.Get(2), texcoord_id[0])); |         uv = OpFAbs(vec_ids.Get(2), OpLoad(vec_ids.Get(2), texcoord_id[0])); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1047,7 +1047,7 @@ Id FragmentModule::ProcTexSampler() { | ||||||
| 
 | 
 | ||||||
|     // Generate noise
 |     // Generate noise
 | ||||||
|     if (config.state.proctex.noise_enable) { |     if (config.state.proctex.noise_enable) { | ||||||
|         const Id proctex_noise_a{GetShaderDataMember(vec_ids.Get(2), ConstS32(22))}; |         const Id proctex_noise_a{GetShaderDataMember(vec_ids.Get(2), ConstS32(21))}; | ||||||
|         const Id noise_coef{ProcTexNoiseCoef(uv)}; |         const Id noise_coef{ProcTexNoiseCoef(uv)}; | ||||||
|         uv = OpFAdd(vec_ids.Get(2), uv, |         uv = OpFAdd(vec_ids.Get(2), uv, | ||||||
|                     OpVectorTimesScalar(vec_ids.Get(2), proctex_noise_a, noise_coef)); |                     OpVectorTimesScalar(vec_ids.Get(2), proctex_noise_a, noise_coef)); | ||||||
|  | @ -1158,8 +1158,8 @@ Id FragmentModule::ProcTexNoiseCoef(Id x) { | ||||||
|         return OpFma(f32_id, OpConvertSToF(f32_id, v2), ConstF32(2.f / 15.f), ConstF32(-1.f)); |         return OpFma(f32_id, OpConvertSToF(f32_id, v2), ConstF32(2.f / 15.f), ConstF32(-1.f)); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const Id proctex_noise_f{GetShaderDataMember(vec_ids.Get(2), ConstS32(21))}; |     const Id proctex_noise_f{GetShaderDataMember(vec_ids.Get(2), ConstS32(20))}; | ||||||
|     const Id proctex_noise_p{GetShaderDataMember(vec_ids.Get(2), ConstS32(23))}; |     const Id proctex_noise_p{GetShaderDataMember(vec_ids.Get(2), ConstS32(22))}; | ||||||
|     const Id grid{OpFMul(vec_ids.Get(2), |     const Id grid{OpFMul(vec_ids.Get(2), | ||||||
|                          OpVectorTimesScalar(vec_ids.Get(2), proctex_noise_f, ConstF32(9.f)), |                          OpVectorTimesScalar(vec_ids.Get(2), proctex_noise_f, ConstF32(9.f)), | ||||||
|                          OpFAbs(vec_ids.Get(2), OpFAdd(vec_ids.Get(2), x, proctex_noise_p)))}; |                          OpFAbs(vec_ids.Get(2), OpFAdd(vec_ids.Get(2), x, proctex_noise_p)))}; | ||||||
|  | @ -1245,7 +1245,7 @@ Id FragmentModule::LookupLightingLUT(Id lut_index, Id index, Id delta) { | ||||||
| 
 | 
 | ||||||
|     const Id lut_index_x{OpShiftRightArithmetic(i32_id, lut_index, ConstS32(2))}; |     const Id lut_index_x{OpShiftRightArithmetic(i32_id, lut_index, ConstS32(2))}; | ||||||
|     const Id lut_index_y{OpBitwiseAnd(i32_id, lut_index, ConstS32(3))}; |     const Id lut_index_y{OpBitwiseAnd(i32_id, lut_index, ConstS32(3))}; | ||||||
|     const Id lut_offset{GetShaderDataMember(i32_id, ConstS32(19), lut_index_x, lut_index_y)}; |     const Id lut_offset{GetShaderDataMember(i32_id, ConstS32(18), lut_index_x, lut_index_y)}; | ||||||
|     const Id coord{OpIAdd(i32_id, lut_offset, index)}; |     const Id coord{OpIAdd(i32_id, lut_offset, index)}; | ||||||
|     const Id entry{ |     const Id entry{ | ||||||
|         OpImageFetch(vec_ids.Get(4), OpImage(image_buffer_id, texture_buffer_lut_lf), coord)}; |         OpImageFetch(vec_ids.Get(4), OpImage(image_buffer_id, texture_buffer_lut_lf), coord)}; | ||||||
|  | @ -1274,11 +1274,11 @@ Id FragmentModule::AppendSource(TevStageConfig::Source source, s32 index) { | ||||||
|     case Source::PreviousBuffer: |     case Source::PreviousBuffer: | ||||||
|         return combiner_buffer; |         return combiner_buffer; | ||||||
|     case Source::Constant: |     case Source::Constant: | ||||||
|         return GetShaderDataMember(vec_ids.Get(4), ConstS32(26), ConstS32(index)); |         return GetShaderDataMember(vec_ids.Get(4), ConstS32(25), ConstS32(index)); | ||||||
|     case Source::Previous: |     case Source::Previous: | ||||||
|         return last_tex_env_out; |         return last_tex_env_out; | ||||||
|     default: |     default: | ||||||
|         LOG_CRITICAL(Render_Vulkan, "Unknown source op {}", source); |         LOG_CRITICAL(Render, "Unknown source op {}", source); | ||||||
|         return ConstF32(0.f, 0.f, 0.f, 0.f); |         return ConstF32(0.f, 0.f, 0.f, 0.f); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1315,7 +1315,7 @@ Id FragmentModule::AppendColorModifier(TevStageConfig::ColorModifier modifier, | ||||||
|     case ColorModifier::OneMinusSourceAlpha: |     case ColorModifier::OneMinusSourceAlpha: | ||||||
|         return OpFSub(vec_ids.Get(3), one_vec, shuffle(3, 3, 3)); |         return OpFSub(vec_ids.Get(3), one_vec, shuffle(3, 3, 3)); | ||||||
|     default: |     default: | ||||||
|         LOG_CRITICAL(Render_Vulkan, "Unknown color modifier op {}", modifier); |         LOG_CRITICAL(Render, "Unknown color modifier op {}", modifier); | ||||||
|         return one_vec; |         return one_vec; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1346,7 +1346,7 @@ Id FragmentModule::AppendAlphaModifier(TevStageConfig::AlphaModifier modifier, | ||||||
|     case AlphaModifier::OneMinusSourceBlue: |     case AlphaModifier::OneMinusSourceBlue: | ||||||
|         return OpFSub(f32_id, one_f32, component(2)); |         return OpFSub(f32_id, one_f32, component(2)); | ||||||
|     default: |     default: | ||||||
|         LOG_CRITICAL(Render_Vulkan, "Unknown alpha modifier op {}", modifier); |         LOG_CRITICAL(Render, "Unknown alpha modifier op {}", modifier); | ||||||
|         return one_f32; |         return one_f32; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -1395,7 +1395,7 @@ Id FragmentModule::AppendColorCombiner(Pica::TexturingRegs::TevStageConfig::Oper | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         color = zero_vec; |         color = zero_vec; | ||||||
|         LOG_CRITICAL(Render_Vulkan, "Unknown color combiner operation: {}", operation); |         LOG_CRITICAL(Render, "Unknown color combiner operation: {}", operation); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1435,7 +1435,7 @@ Id FragmentModule::AppendAlphaCombiner(TevStageConfig::Operation operation) { | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         color = ConstF32(0.f); |         color = ConstF32(0.f); | ||||||
|         LOG_CRITICAL(Render_Vulkan, "Unknown alpha combiner operation: {}", operation); |         LOG_CRITICAL(Render, "Unknown alpha combiner operation: {}", operation); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1485,16 +1485,15 @@ void FragmentModule::DefineUniformStructs() { | ||||||
| 
 | 
 | ||||||
|     const Id shader_data_struct_id{ |     const Id shader_data_struct_id{ | ||||||
|         TypeStruct(i32_id, i32_id, f32_id, f32_id, f32_id, f32_id, i32_id, i32_id, i32_id, i32_id, |         TypeStruct(i32_id, i32_id, f32_id, f32_id, f32_id, f32_id, i32_id, i32_id, i32_id, i32_id, | ||||||
|                    i32_id, i32_id, i32_id, i32_id, i32_id, i32_id, f32_id, i32_id, u32_id, |                    i32_id, i32_id, i32_id, i32_id, i32_id, i32_id, f32_id, i32_id, | ||||||
|                    lighting_lut_array_id, vec_ids.Get(3), vec_ids.Get(2), vec_ids.Get(2), |                    lighting_lut_array_id, vec_ids.Get(3), vec_ids.Get(2), vec_ids.Get(2), | ||||||
|                    vec_ids.Get(2), vec_ids.Get(3), light_src_array_id, const_color_array_id, |                    vec_ids.Get(2), vec_ids.Get(3), light_src_array_id, const_color_array_id, | ||||||
|                    vec_ids.Get(4), vec_ids.Get(3), border_color_array_id, vec_ids.Get(4))}; |                    vec_ids.Get(4), vec_ids.Get(3), border_color_array_id, vec_ids.Get(4))}; | ||||||
| 
 | 
 | ||||||
|     constexpr std::array light_src_offsets{0u, 16u, 32u, 48u, 64u, 80u, 92u, 96u}; |     constexpr std::array light_src_offsets{0u, 16u, 32u, 48u, 64u, 80u, 92u, 96u}; | ||||||
|     constexpr std::array shader_data_offsets{0u,   4u,   8u,    12u,   16u,   20u,   24u,  28u, |     constexpr std::array shader_data_offsets{ | ||||||
|                                              32u,  36u,  40u,   44u,   48u,   52u,   56u,  60u, |         0u,  4u,  8u,  12u, 16u,  20u,  24u,  28u,  32u,  36u,  40u,   44u,   48u,   52u,   56u, | ||||||
|                                              64u,  68u,  72u,   80u,   176u,  192u,  200u, 208u, |         60u, 64u, 68u, 80u, 176u, 192u, 200u, 208u, 224u, 240u, 1136u, 1232u, 1248u, 1264u, 1312u}; | ||||||
|                                              224u, 240u, 1136u, 1232u, 1248u, 1264u, 1312u}; |  | ||||||
| 
 | 
 | ||||||
|     Decorate(lighting_lut_array_id, spv::Decoration::ArrayStride, 16u); |     Decorate(lighting_lut_array_id, spv::Decoration::ArrayStride, 16u); | ||||||
|     Decorate(light_src_array_id, spv::Decoration::ArrayStride, 112u); |     Decorate(light_src_array_id, spv::Decoration::ArrayStride, 112u); | ||||||
|  | @ -1511,7 +1510,7 @@ void FragmentModule::DefineUniformStructs() { | ||||||
|     shader_data_id = AddGlobalVariable( |     shader_data_id = AddGlobalVariable( | ||||||
|         TypePointer(spv::StorageClass::Uniform, shader_data_struct_id), spv::StorageClass::Uniform); |         TypePointer(spv::StorageClass::Uniform, shader_data_struct_id), spv::StorageClass::Uniform); | ||||||
|     Decorate(shader_data_id, spv::Decoration::DescriptorSet, 0); |     Decorate(shader_data_id, spv::Decoration::DescriptorSet, 0); | ||||||
|     Decorate(shader_data_id, spv::Decoration::Binding, 1); |     Decorate(shader_data_id, spv::Decoration::Binding, 2); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void FragmentModule::DefineInterface() { | void FragmentModule::DefineInterface() { | ||||||
|  | @ -1532,9 +1531,9 @@ void FragmentModule::DefineInterface() { | ||||||
|     image_r32_id = TypeImage(u32_id, spv::Dim::Dim2D, 0, 0, 0, 2, spv::ImageFormat::R32ui); |     image_r32_id = TypeImage(u32_id, spv::Dim::Dim2D, 0, 0, 0, 2, spv::ImageFormat::R32ui); | ||||||
|     sampler_id = TypeSampler(); |     sampler_id = TypeSampler(); | ||||||
| 
 | 
 | ||||||
|     texture_buffer_lut_lf_id = DefineUniformConst(TypeSampledImage(image_buffer_id), 0, 2); |     texture_buffer_lut_lf_id = DefineUniformConst(TypeSampledImage(image_buffer_id), 0, 3); | ||||||
|     texture_buffer_lut_rg_id = DefineUniformConst(TypeSampledImage(image_buffer_id), 0, 3); |     texture_buffer_lut_rg_id = DefineUniformConst(TypeSampledImage(image_buffer_id), 0, 4); | ||||||
|     texture_buffer_lut_rgba_id = DefineUniformConst(TypeSampledImage(image_buffer_id), 0, 4); |     texture_buffer_lut_rgba_id = DefineUniformConst(TypeSampledImage(image_buffer_id), 0, 5); | ||||||
|     tex0_id = DefineUniformConst(TypeSampledImage(image2d_id), 1, 0); |     tex0_id = DefineUniformConst(TypeSampledImage(image2d_id), 1, 0); | ||||||
|     tex1_id = DefineUniformConst(TypeSampledImage(image2d_id), 1, 1); |     tex1_id = DefineUniformConst(TypeSampledImage(image2d_id), 1, 1); | ||||||
|     tex2_id = DefineUniformConst(TypeSampledImage(image2d_id), 1, 2); |     tex2_id = DefineUniformConst(TypeSampledImage(image2d_id), 1, 2); | ||||||
|  | @ -1550,11 +1549,11 @@ void FragmentModule::DefineInterface() { | ||||||
|     Decorate(gl_frag_depth_id, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth); |     Decorate(gl_frag_depth_id, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::vector<u32> GenerateFragmentShaderSPV(const PicaFSConfig& config) { | std::vector<u32> GenerateFragmentShader(const PicaFSConfig& config) { | ||||||
|     auto& telemetry = Core::System::GetInstance().TelemetrySession(); |     auto& telemetry = Core::System::GetInstance().TelemetrySession(); | ||||||
|     FragmentModule module{telemetry, config}; |     FragmentModule module{telemetry, config}; | ||||||
|     module.Generate(); |     module.Generate(); | ||||||
|     return module.Assemble(); |     return module.Assemble(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace Vulkan
 | } // namespace Pica::Shader::Generator::SPIRV
 | ||||||
|  | @ -7,13 +7,13 @@ | ||||||
| #include <array> | #include <array> | ||||||
| #include <sirit/sirit.h> | #include <sirit/sirit.h> | ||||||
| 
 | 
 | ||||||
| #include "video_core/renderer_vulkan/vk_shader_gen.h" | #include "video_core/shader/generator/shader_gen.h" | ||||||
| 
 | 
 | ||||||
| namespace Core { | namespace Core { | ||||||
| class TelemetrySession; | class TelemetrySession; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| namespace Vulkan { | namespace Pica::Shader::Generator::SPIRV { | ||||||
| 
 | 
 | ||||||
| using Sirit::Id; | using Sirit::Id; | ||||||
| 
 | 
 | ||||||
|  | @ -41,7 +41,7 @@ public: | ||||||
|     void Generate(); |     void Generate(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     /// Undos the vulkan perspective transformation and applies the PICA one
 |     /// Undos the host perspective transformation and applies the PICA one
 | ||||||
|     void WriteDepth(); |     void WriteDepth(); | ||||||
| 
 | 
 | ||||||
|     /// Emits code to emulate the scissor rectangle
 |     /// Emits code to emulate the scissor rectangle
 | ||||||
|  | @ -289,6 +289,6 @@ private: | ||||||
|  * @param separable_shader generates shader that can be used for separate shader object |  * @param separable_shader generates shader that can be used for separate shader object | ||||||
|  * @returns String of the shader source code |  * @returns String of the shader source code | ||||||
|  */ |  */ | ||||||
| std::vector<u32> GenerateFragmentShaderSPV(const PicaFSConfig& config); | std::vector<u32> GenerateFragmentShader(const PicaFSConfig& config); | ||||||
| 
 | 
 | ||||||
| } // namespace Vulkan
 | } // namespace Pica::Shader::Generator::SPIRV
 | ||||||
|  | @ -1,80 +0,0 @@ | ||||||
| // Copyright 2023 Citra Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #include <algorithm> |  | ||||||
| #include "video_core/shader/shader.h" |  | ||||||
| #include "video_core/shader/shader_uniforms.h" |  | ||||||
| 
 |  | ||||||
| namespace Pica::Shader { |  | ||||||
| 
 |  | ||||||
| void PicaUniformsData::SetFromRegs(const Pica::ShaderRegs& regs, |  | ||||||
|                                    const Pica::Shader::ShaderSetup& setup) { |  | ||||||
|     std::transform(std::begin(setup.uniforms.b), std::end(setup.uniforms.b), std::begin(bools), |  | ||||||
|                    [](bool value) -> BoolAligned { return {value ? 1 : 0}; }); |  | ||||||
|     std::transform(std::begin(regs.int_uniforms), std::end(regs.int_uniforms), std::begin(i), |  | ||||||
|                    [](const auto& value) -> Common::Vec4u { |  | ||||||
|                        return {value.x.Value(), value.y.Value(), value.z.Value(), value.w.Value()}; |  | ||||||
|                    }); |  | ||||||
|     std::transform(std::begin(setup.uniforms.f), std::end(setup.uniforms.f), std::begin(f), |  | ||||||
|                    [](const auto& value) -> Common::Vec4f { |  | ||||||
|                        return {value.x.ToFloat32(), value.y.ToFloat32(), value.z.ToFloat32(), |  | ||||||
|                                value.w.ToFloat32()}; |  | ||||||
|                    }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| constexpr std::string_view UniformBlockDefFormat = R"( |  | ||||||
| #define NUM_TEV_STAGES 6 |  | ||||||
| #define NUM_LIGHTS 8 |  | ||||||
| #define NUM_LIGHTING_SAMPLERS 24 |  | ||||||
| struct LightSrc {{ |  | ||||||
|     vec3 specular_0; |  | ||||||
|     vec3 specular_1; |  | ||||||
|     vec3 diffuse; |  | ||||||
|     vec3 ambient; |  | ||||||
|     vec3 position; |  | ||||||
|     vec3 spot_direction; |  | ||||||
|     float dist_atten_bias; |  | ||||||
|     float dist_atten_scale; |  | ||||||
| }}; |  | ||||||
| layout ({}std140) uniform shader_data {{ |  | ||||||
|     int framebuffer_scale; |  | ||||||
|     int alphatest_ref; |  | ||||||
|     float depth_scale; |  | ||||||
|     float depth_offset; |  | ||||||
|     float shadow_bias_constant; |  | ||||||
|     float shadow_bias_linear; |  | ||||||
|     int scissor_x1; |  | ||||||
|     int scissor_y1; |  | ||||||
|     int scissor_x2; |  | ||||||
|     int scissor_y2; |  | ||||||
|     int fog_lut_offset; |  | ||||||
|     int proctex_noise_lut_offset; |  | ||||||
|     int proctex_color_map_offset; |  | ||||||
|     int proctex_alpha_map_offset; |  | ||||||
|     int proctex_lut_offset; |  | ||||||
|     int proctex_diff_lut_offset; |  | ||||||
|     float proctex_bias; |  | ||||||
|     int shadow_texture_bias; |  | ||||||
|     bool enable_clip1; |  | ||||||
|     ivec4 lighting_lut_offset[NUM_LIGHTING_SAMPLERS / 4]; |  | ||||||
|     vec3 fog_color; |  | ||||||
|     vec2 proctex_noise_f; |  | ||||||
|     vec2 proctex_noise_a; |  | ||||||
|     vec2 proctex_noise_p; |  | ||||||
|     vec3 lighting_global_ambient; |  | ||||||
|     LightSrc light_src[NUM_LIGHTS]; |  | ||||||
|     vec4 const_color[NUM_TEV_STAGES]; |  | ||||||
|     vec4 tev_combiner_buffer_color; |  | ||||||
|     vec3 tex_lod_bias; |  | ||||||
|     vec4 tex_border_color[3]; |  | ||||||
|     vec4 clip_coef; |  | ||||||
|     vec4 blend_color; |  | ||||||
| }}; |  | ||||||
| )"; |  | ||||||
| 
 |  | ||||||
| std::string BuildShaderUniformDefinitions(const std::string& extra_layout_parameters) { |  | ||||||
|     return fmt::format(UniformBlockDefFormat, extra_layout_parameters); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } // namespace Pica::Shader
 |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue