mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Prepare frontend for multiple graphics APIs (#6347)
* externals: Update dynarmic * settings: Introduce GraphicsAPI enum * For now it's OpenGL only but will be expanded upon later * citra_qt: Introduce backend agnostic context management * Mostly a direct port from yuzu * core: Simplify context acquire * settings: Add option to create debug contexts * renderer_opengl: Abstract initialization to Driver * This commit also updates glad and adds some useful extensions which we will use in part 2 * Rasterizer construction is moved to the specific renderer instead of RendererBase. Software rendering has been disable to achieve this but will be brought back in the next commit. * video_core: Remove Init/Shutdown methods from renderer * The constructor and destructor can do the same job * In addition move opengl function loading to Qt since SDL already does this. Also remove ErrorVideoCore which is never reached * citra_qt: Decouple software renderer from opengl part 1 * citra: Decouple software renderer from opengl part 2 * android: Decouple software renderer from opengl part 3 * swrasterizer: Decouple software renderer from opengl part 4 * This commit simply enforces the renderer naming conventions in the software renderer * video_core: Move RendererBase to VideoCore * video_core: De-globalize screenshot state * video_core: Pass system to the renderers * video_core: Commonize shader uniform data * video_core: Abstract backend agnostic rasterizer operations * bootmanager: Remove references to OpenGL for macOS OpenGL macOS headers definitions clash heavily with each other * citra_qt: Proper title for api settings * video_core: Reduce boost usage * bootmanager: Fix hide mouse option Remove event handlers from RenderWidget for events that are already handled by the parent GRenderWindow. Also enable mouse tracking on the RenderWidget. * android: Remove software from graphics api list * code: Address review comments * citra: Port per-game settings read * Having to update the default value for all backends is a pain so lets centralize it * android: Rename to OpenGLES --------- Co-authored-by: MerryMage <MerryMage@users.noreply.github.com> Co-authored-by: Vitor Kiguchi <vitor-kiguchi@hotmail.com>
This commit is contained in:
		
							parent
							
								
									9ef42040af
								
							
						
					
					
						commit
						b5d6f645bd
					
				
					 99 changed files with 3165 additions and 4501 deletions
				
			
		
							
								
								
									
										2
									
								
								externals/dynarmic
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								externals/dynarmic
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +1 @@ | ||||||
| Subproject commit 9af4b970d302389829448a30608c7cb4fce9b662 | Subproject commit b3a92ab54dadd26a0c2a87d2677b80249d2e1a5a | ||||||
							
								
								
									
										2
									
								
								externals/glad/Readme.md
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								externals/glad/Readme.md
									
										
									
									
										vendored
									
									
								
							|  | @ -1,5 +1,5 @@ | ||||||
| These files were generated by the [glad](https://github.com/Dav1dde/glad) OpenGL loader generator and have been checked in as-is. You can re-generate them using glad with the following command: | These files were generated by the [glad](https://github.com/Dav1dde/glad) OpenGL loader generator and have been checked in as-is. You can re-generate them using glad with the following command: | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| python -m glad --profile core --out-path glad/ --api "gl=3.3,gles2=3.2" --generator=c | python -m glad --profile core --out-path glad/ --api "gl=4.6,gles2=3.2" --generator=c | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
							
								
								
									
										1614
									
								
								externals/glad/include/glad/glad.h
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1614
									
								
								externals/glad/include/glad/glad.h
									
										
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										776
									
								
								externals/glad/src/glad.c
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										776
									
								
								externals/glad/src/glad.c
									
										
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -496,9 +496,6 @@ public final class NativeLibrary { | ||||||
|         final int ErrorLoader_ErrorEncrypted = 5; |         final int ErrorLoader_ErrorEncrypted = 5; | ||||||
|         final int ErrorLoader_ErrorInvalidFormat = 6; |         final int ErrorLoader_ErrorInvalidFormat = 6; | ||||||
|         final int ErrorSystemFiles = 7; |         final int ErrorSystemFiles = 7; | ||||||
|         final int ErrorVideoCore = 8; |  | ||||||
|         final int ErrorVideoCore_ErrorGenericDrivers = 9; |  | ||||||
|         final int ErrorVideoCore_ErrorBelowGL33 = 10; |  | ||||||
|         final int ShutdownRequested = 11; |         final int ShutdownRequested = 11; | ||||||
|         final int ErrorUnknown = 12; |         final int ErrorUnknown = 12; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -355,6 +355,7 @@ public final class SettingsFragmentPresenter { | ||||||
|         mView.getActivity().setTitle(R.string.preferences_graphics); |         mView.getActivity().setTitle(R.string.preferences_graphics); | ||||||
| 
 | 
 | ||||||
|         SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER); |         SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER); | ||||||
|  |         Setting graphicsApi = rendererSection.getSetting(SettingsFile.KEY_GRAPHICS_API); | ||||||
|         Setting resolutionFactor = rendererSection.getSetting(SettingsFile.KEY_RESOLUTION_FACTOR); |         Setting resolutionFactor = rendererSection.getSetting(SettingsFile.KEY_RESOLUTION_FACTOR); | ||||||
|         Setting filterMode = rendererSection.getSetting(SettingsFile.KEY_FILTER_MODE); |         Setting filterMode = rendererSection.getSetting(SettingsFile.KEY_FILTER_MODE); | ||||||
|         Setting shadersAccurateMul = rendererSection.getSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL); |         Setting shadersAccurateMul = rendererSection.getSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL); | ||||||
|  | @ -371,6 +372,7 @@ public final class SettingsFragmentPresenter { | ||||||
|         //Setting preloadTextures = utilitySection.getSetting(SettingsFile.KEY_PRELOAD_TEXTURES); |         //Setting preloadTextures = utilitySection.getSetting(SettingsFile.KEY_PRELOAD_TEXTURES); | ||||||
| 
 | 
 | ||||||
|         sl.add(new HeaderSetting(null, null, R.string.renderer, 0)); |         sl.add(new HeaderSetting(null, null, R.string.renderer, 0)); | ||||||
|  |         sl.add(new SingleChoiceSetting(SettingsFile.KEY_GRAPHICS_API, Settings.SECTION_RENDERER, R.string.graphics_api, 0, R.array.graphicsApiNames, R.array.graphicsApiValues, 0, graphicsApi)); | ||||||
|         sl.add(new SliderSetting(SettingsFile.KEY_RESOLUTION_FACTOR, Settings.SECTION_RENDERER, R.string.internal_resolution, R.string.internal_resolution_description, 1, 4, "x", 1, resolutionFactor)); |         sl.add(new SliderSetting(SettingsFile.KEY_RESOLUTION_FACTOR, Settings.SECTION_RENDERER, R.string.internal_resolution, R.string.internal_resolution_description, 1, 4, "x", 1, resolutionFactor)); | ||||||
|         sl.add(new CheckBoxSetting(SettingsFile.KEY_FILTER_MODE, Settings.SECTION_RENDERER, R.string.linear_filtering, R.string.linear_filtering_description, true, filterMode)); |         sl.add(new CheckBoxSetting(SettingsFile.KEY_FILTER_MODE, Settings.SECTION_RENDERER, R.string.linear_filtering, R.string.linear_filtering_description, true, filterMode)); | ||||||
|         sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL, Settings.SECTION_RENDERER, R.string.shaders_accurate_mul, R.string.shaders_accurate_mul_description, false, shadersAccurateMul)); |         sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL, Settings.SECTION_RENDERER, R.string.shaders_accurate_mul, R.string.shaders_accurate_mul_description, false, shadersAccurateMul)); | ||||||
|  | @ -409,14 +411,14 @@ public final class SettingsFragmentPresenter { | ||||||
|         SettingSection coreSection = mSettings.getSection(Settings.SECTION_CORE); |         SettingSection coreSection = mSettings.getSection(Settings.SECTION_CORE); | ||||||
|         SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER); |         SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER); | ||||||
|         Setting useCpuJit = coreSection.getSetting(SettingsFile.KEY_CPU_JIT); |         Setting useCpuJit = coreSection.getSetting(SettingsFile.KEY_CPU_JIT); | ||||||
|         Setting hardwareRenderer = rendererSection.getSetting(SettingsFile.KEY_HW_RENDERER); |  | ||||||
|         Setting hardwareShader = rendererSection.getSetting(SettingsFile.KEY_HW_SHADER); |         Setting hardwareShader = rendererSection.getSetting(SettingsFile.KEY_HW_SHADER); | ||||||
|         Setting vsyncEnable = rendererSection.getSetting(SettingsFile.KEY_USE_VSYNC); |         Setting vsyncEnable = rendererSection.getSetting(SettingsFile.KEY_USE_VSYNC); | ||||||
|  |         Setting rendererDebug = rendererSection.getSetting(SettingsFile.KEY_RENDERER_DEBUG); | ||||||
| 
 | 
 | ||||||
|         sl.add(new HeaderSetting(null, null, R.string.debug_warning, 0)); |         sl.add(new HeaderSetting(null, null, R.string.debug_warning, 0)); | ||||||
|         sl.add(new CheckBoxSetting(SettingsFile.KEY_CPU_JIT, Settings.SECTION_CORE, R.string.cpu_jit, R.string.cpu_jit_description, true, useCpuJit, true, mView)); |         sl.add(new CheckBoxSetting(SettingsFile.KEY_CPU_JIT, Settings.SECTION_CORE, R.string.cpu_jit, R.string.cpu_jit_description, true, useCpuJit, true, mView)); | ||||||
|         sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_RENDERER, Settings.SECTION_RENDERER, R.string.hw_renderer, R.string.hw_renderer_description, true, hardwareRenderer, true, mView)); |  | ||||||
|         sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_SHADER, Settings.SECTION_RENDERER, R.string.hw_shaders, R.string.hw_shaders_description, true, hardwareShader, true, mView)); |         sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_SHADER, Settings.SECTION_RENDERER, R.string.hw_shaders, R.string.hw_shaders_description, true, hardwareShader, true, mView)); | ||||||
|         sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_VSYNC, Settings.SECTION_RENDERER, R.string.vsync, R.string.vsync_description, true, vsyncEnable)); |         sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_VSYNC, Settings.SECTION_RENDERER, R.string.vsync, R.string.vsync_description, true, vsyncEnable)); | ||||||
|  |         sl.add(new CheckBoxSetting(SettingsFile.KEY_RENDERER_DEBUG, Settings.SECTION_RENDERER, R.string.renderer_debug, R.string.renderer_debug_description, false, rendererDebug)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -44,7 +44,8 @@ public final class SettingsFile { | ||||||
| 
 | 
 | ||||||
|     public static final String KEY_PREMIUM = "premium"; |     public static final String KEY_PREMIUM = "premium"; | ||||||
| 
 | 
 | ||||||
|     public static final String KEY_HW_RENDERER = "use_hw_renderer"; |     public static final String KEY_GRAPHICS_API = "graphics_api"; | ||||||
|  |     public static final String KEY_RENDERER_DEBUG = "renderer_debug"; | ||||||
|     public static final String KEY_HW_SHADER = "use_hw_shader"; |     public static final String KEY_HW_SHADER = "use_hw_shader"; | ||||||
|     public static final String KEY_SHADERS_ACCURATE_MUL = "shaders_accurate_mul"; |     public static final String KEY_SHADERS_ACCURATE_MUL = "shaders_accurate_mul"; | ||||||
|     public static final String KEY_USE_SHADER_JIT = "use_shader_jit"; |     public static final String KEY_USE_SHADER_JIT = "use_shader_jit"; | ||||||
|  |  | ||||||
|  | @ -82,6 +82,30 @@ void Config::UpdateCFG() { | ||||||
|     cfg->UpdateConfigNANDSavegame(); |     cfg->UpdateConfigNANDSavegame(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | template <> | ||||||
|  | void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) { | ||||||
|  |     std::string setting_value = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); | ||||||
|  |     if (setting_value.empty()) { | ||||||
|  |         setting_value = setting.GetDefault(); | ||||||
|  |     } | ||||||
|  |     setting = std::move(setting_value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) { | ||||||
|  |     setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <typename Type, bool ranged> | ||||||
|  | void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) { | ||||||
|  |     if constexpr (std::is_floating_point_v<Type>) { | ||||||
|  |         setting = sdl2_config->GetReal(group, setting.GetLabel(), setting.GetDefault()); | ||||||
|  |     } else { | ||||||
|  |         setting = static_cast<Type>(sdl2_config->GetInteger( | ||||||
|  |             group, setting.GetLabel(), static_cast<long>(setting.GetDefault()))); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void Config::ReadValues() { | void Config::ReadValues() { | ||||||
|     // Controls
 |     // Controls
 | ||||||
|     for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |     for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||||||
|  | @ -112,39 +136,32 @@ void Config::ReadValues() { | ||||||
|                                                  InputCommon::CemuhookUDP::DEFAULT_PORT)); |                                                  InputCommon::CemuhookUDP::DEFAULT_PORT)); | ||||||
| 
 | 
 | ||||||
|     // Core
 |     // Core
 | ||||||
|     Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); |     ReadSetting("Core", Settings::values.use_cpu_jit); | ||||||
|     Settings::values.cpu_clock_percentage = |     ReadSetting("Core", Settings::values.cpu_clock_percentage); | ||||||
|         static_cast<int>(sdl2_config->GetInteger("Core", "cpu_clock_percentage", 100)); |  | ||||||
| 
 | 
 | ||||||
|     // Premium
 |     // Premium
 | ||||||
|     Settings::values.texture_filter_name = |     ReadSetting("Premium", Settings::values.texture_filter_name); | ||||||
|         sdl2_config->GetString("Premium", "texture_filter_name", "none"); |  | ||||||
| 
 | 
 | ||||||
|     // Renderer
 |     // Renderer
 | ||||||
|     Settings::values.use_gles = sdl2_config->GetBoolean("Renderer", "use_gles", true); |     Settings::values.use_gles = sdl2_config->GetBoolean("Renderer", "use_gles", true); | ||||||
|     Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); |  | ||||||
|     Settings::values.use_hw_shader = sdl2_config->GetBoolean("Renderer", "use_hw_shader", true); |  | ||||||
|     Settings::values.shaders_accurate_mul = |     Settings::values.shaders_accurate_mul = | ||||||
|         sdl2_config->GetBoolean("Renderer", "shaders_accurate_mul", false); |         sdl2_config->GetBoolean("Renderer", "shaders_accurate_mul", false); | ||||||
|     Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true); |     ReadSetting("Renderer", Settings::values.graphics_api); | ||||||
|     Settings::values.resolution_factor = |     ReadSetting("Renderer", Settings::values.use_hw_shader); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Renderer", "resolution_factor", 1)); |     ReadSetting("Renderer", Settings::values.use_shader_jit); | ||||||
|     Settings::values.use_disk_shader_cache = |     ReadSetting("Renderer", Settings::values.resolution_factor); | ||||||
|         sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", true); |     ReadSetting("Renderer", Settings::values.use_disk_shader_cache); | ||||||
|     Settings::values.use_vsync_new = sdl2_config->GetBoolean("Renderer", "use_vsync_new", true); |     ReadSetting("Renderer", Settings::values.use_vsync_new); | ||||||
| 
 | 
 | ||||||
|     // Work around to map Android setting for enabling the frame limiter to the format Citra expects
 |     // Work around to map Android setting for enabling the frame limiter to the format Citra expects
 | ||||||
|     if (sdl2_config->GetBoolean("Renderer", "use_frame_limit", true)) { |     if (sdl2_config->GetBoolean("Renderer", "use_frame_limit", true)) { | ||||||
|         Settings::values.frame_limit = |         ReadSetting("Renderer", Settings::values.frame_limit); | ||||||
|             static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); |  | ||||||
|     } else { |     } else { | ||||||
|         Settings::values.frame_limit = 0; |         Settings::values.frame_limit = 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Settings::values.render_3d = static_cast<Settings::StereoRenderOption>( |     ReadSetting("Renderer", Settings::values.render_3d); | ||||||
|         sdl2_config->GetInteger("Renderer", "render_3d", 0)); |     ReadSetting("Renderer", Settings::values.factor_3d); | ||||||
|     Settings::values.factor_3d = |  | ||||||
|         static_cast<u8>(sdl2_config->GetInteger("Renderer", "factor_3d", 0)); |  | ||||||
|     std::string default_shader = "none (builtin)"; |     std::string default_shader = "none (builtin)"; | ||||||
|     if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) |     if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) | ||||||
|         default_shader = "dubois (builtin)"; |         default_shader = "dubois (builtin)"; | ||||||
|  | @ -152,70 +169,49 @@ void Config::ReadValues() { | ||||||
|         default_shader = "horizontal (builtin)"; |         default_shader = "horizontal (builtin)"; | ||||||
|     Settings::values.pp_shader_name = |     Settings::values.pp_shader_name = | ||||||
|         sdl2_config->GetString("Renderer", "pp_shader_name", default_shader); |         sdl2_config->GetString("Renderer", "pp_shader_name", default_shader); | ||||||
|     Settings::values.filter_mode = sdl2_config->GetBoolean("Renderer", "filter_mode", true); |     ReadSetting("Renderer", Settings::values.filter_mode); | ||||||
| 
 | 
 | ||||||
|     Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); |     ReadSetting("Renderer", Settings::values.bg_red); | ||||||
|     Settings::values.bg_green = |     ReadSetting("Renderer", Settings::values.bg_green); | ||||||
|         static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)); |     ReadSetting("Renderer", Settings::values.bg_blue); | ||||||
|     Settings::values.bg_blue = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)); |  | ||||||
| 
 | 
 | ||||||
|     // Layout
 |     // Layout
 | ||||||
|     Settings::values.layout_option = static_cast<Settings::LayoutOption>(sdl2_config->GetInteger( |     Settings::values.layout_option = static_cast<Settings::LayoutOption>(sdl2_config->GetInteger( | ||||||
|         "Layout", "layout_option", static_cast<int>(Settings::LayoutOption::MobileLandscape))); |         "Layout", "layout_option", static_cast<int>(Settings::LayoutOption::MobileLandscape))); | ||||||
|     Settings::values.custom_layout = sdl2_config->GetBoolean("Layout", "custom_layout", false); |     ReadSetting("Layout", Settings::values.custom_layout); | ||||||
|     Settings::values.custom_top_left = |     ReadSetting("Layout", Settings::values.custom_top_left); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_left", 0)); |     ReadSetting("Layout", Settings::values.custom_top_top); | ||||||
|     Settings::values.custom_top_top = |     ReadSetting("Layout", Settings::values.custom_top_right); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_top", 0)); |     ReadSetting("Layout", Settings::values.custom_top_bottom); | ||||||
|     Settings::values.custom_top_right = |     ReadSetting("Layout", Settings::values.custom_bottom_left); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_right", 400)); |     ReadSetting("Layout", Settings::values.custom_bottom_top); | ||||||
|     Settings::values.custom_top_bottom = |     ReadSetting("Layout", Settings::values.custom_bottom_right); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_bottom", 240)); |     ReadSetting("Layout", Settings::values.custom_bottom_bottom); | ||||||
|     Settings::values.custom_bottom_left = |     ReadSetting("Layout", Settings::values.cardboard_screen_size); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_left", 40)); |     ReadSetting("Layout", Settings::values.cardboard_x_shift); | ||||||
|     Settings::values.custom_bottom_top = |     ReadSetting("Layout", Settings::values.cardboard_y_shift); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_top", 240)); |  | ||||||
|     Settings::values.custom_bottom_right = |  | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_right", 360)); |  | ||||||
|     Settings::values.custom_bottom_bottom = |  | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_bottom", 480)); |  | ||||||
|     Settings::values.cardboard_screen_size = |  | ||||||
|         static_cast<int>(sdl2_config->GetInteger("Layout", "cardboard_screen_size", 85)); |  | ||||||
|     Settings::values.cardboard_x_shift = |  | ||||||
|         static_cast<int>(sdl2_config->GetInteger("Layout", "cardboard_x_shift", 0)); |  | ||||||
|     Settings::values.cardboard_y_shift = |  | ||||||
|         static_cast<int>(sdl2_config->GetInteger("Layout", "cardboard_y_shift", 0)); |  | ||||||
| 
 | 
 | ||||||
|     // Utility
 |     // Utility
 | ||||||
|     Settings::values.dump_textures = sdl2_config->GetBoolean("Utility", "dump_textures", false); |     ReadSetting("Utility", Settings::values.dump_textures); | ||||||
|     Settings::values.custom_textures = sdl2_config->GetBoolean("Utility", "custom_textures", false); |     ReadSetting("Utility", Settings::values.custom_textures); | ||||||
|     Settings::values.preload_textures = |     ReadSetting("Utility", Settings::values.preload_textures); | ||||||
|         sdl2_config->GetBoolean("Utility", "preload_textures", false); |  | ||||||
| 
 | 
 | ||||||
|     // Audio
 |     // Audio
 | ||||||
|     Settings::values.audio_emulation = |     ReadSetting("Audio", Settings::values.audio_emulation); | ||||||
|         static_cast<Settings::AudioEmulation>(sdl2_config->GetInteger( |     ReadSetting("Audio", Settings::values.sink_id); | ||||||
|             "Audio", "audio_emulation", static_cast<int>(Settings::AudioEmulation::HLE))); |     ReadSetting("Audio", Settings::values.enable_audio_stretching); | ||||||
|     Settings::values.sink_id = sdl2_config->GetString("Audio", "output_engine", "auto"); |     ReadSetting("Audio", Settings::values.audio_device_id); | ||||||
|     Settings::values.enable_audio_stretching = |     ReadSetting("Audio", Settings::values.volume); | ||||||
|         sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); |     ReadSetting("Audio", Settings::values.mic_input_device); | ||||||
|     Settings::values.audio_device_id = sdl2_config->GetString("Audio", "output_device", "auto"); |     ReadSetting("Audio", Settings::values.mic_input_type); | ||||||
|     Settings::values.volume = static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1)); |  | ||||||
|     Settings::values.mic_input_device = |  | ||||||
|         sdl2_config->GetString("Audio", "mic_input_device", "Default"); |  | ||||||
|     Settings::values.mic_input_type = |  | ||||||
|         static_cast<Settings::MicInputType>(sdl2_config->GetInteger("Audio", "mic_input_type", 1)); |  | ||||||
| 
 | 
 | ||||||
|     // Data Storage
 |     // Data Storage
 | ||||||
|     Settings::values.use_virtual_sd = |     ReadSetting("Data Storage", Settings::values.use_virtual_sd); | ||||||
|         sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); |  | ||||||
| 
 | 
 | ||||||
|     // System
 |     // System
 | ||||||
|     Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", true); |     ReadSetting("System", Settings::values.is_new_3ds); | ||||||
|     Settings::values.region_value = |     ReadSetting("System", Settings::values.region_value); | ||||||
|         sdl2_config->GetInteger("System", "region_value", Settings::REGION_VALUE_AUTO_SELECT); |     ReadSetting("System", Settings::values.init_clock); | ||||||
|     Settings::values.init_clock = |  | ||||||
|         static_cast<Settings::InitClock>(sdl2_config->GetInteger("System", "init_clock", 0)); |  | ||||||
|     { |     { | ||||||
|         std::tm t; |         std::tm t; | ||||||
|         t.tm_sec = 1; |         t.tm_sec = 1; | ||||||
|  | @ -236,10 +232,8 @@ void Config::ReadValues() { | ||||||
|                 std::chrono::system_clock::from_time_t(std::mktime(&t)).time_since_epoch()) |                 std::chrono::system_clock::from_time_t(std::mktime(&t)).time_since_epoch()) | ||||||
|                 .count(); |                 .count(); | ||||||
|     } |     } | ||||||
|     Settings::values.plugin_loader_enabled = |     ReadSetting("System", Settings::values.plugin_loader_enabled); | ||||||
|         sdl2_config->GetBoolean("System", "plugin_loader", false); |     ReadSetting("System", Settings::values.allow_plugin_loader); | ||||||
|     Settings::values.allow_plugin_loader = |  | ||||||
|         sdl2_config->GetBoolean("System", "allow_plugin_loader", true); |  | ||||||
| 
 | 
 | ||||||
|     // Camera
 |     // Camera
 | ||||||
|     using namespace Service::CAM; |     using namespace Service::CAM; | ||||||
|  | @ -263,14 +257,14 @@ void Config::ReadValues() { | ||||||
|         sdl2_config->GetInteger("Camera", "camera_outer_left_flip", 0); |         sdl2_config->GetInteger("Camera", "camera_outer_left_flip", 0); | ||||||
| 
 | 
 | ||||||
|     // Miscellaneous
 |     // Miscellaneous
 | ||||||
|     Settings::values.log_filter = sdl2_config->GetString("Miscellaneous", "log_filter", "*:Info"); |     ReadSetting("Miscellaneous", Settings::values.log_filter); | ||||||
| 
 | 
 | ||||||
|     // Debugging
 |     // Debugging
 | ||||||
|     Settings::values.record_frame_times = |     Settings::values.record_frame_times = | ||||||
|         sdl2_config->GetBoolean("Debugging", "record_frame_times", false); |         sdl2_config->GetBoolean("Debugging", "record_frame_times", false); | ||||||
|     Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); |     ReadSetting("Debugging", Settings::values.renderer_debug); | ||||||
|     Settings::values.gdbstub_port = |     ReadSetting("Debugging", Settings::values.use_gdbstub); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); |     ReadSetting("Debugging", Settings::values.gdbstub_port); | ||||||
| 
 | 
 | ||||||
|     for (const auto& service_module : Service::service_module_map) { |     for (const auto& service_module : Service::service_module_map) { | ||||||
|         bool use_lle = sdl2_config->GetBoolean("Debugging", "LLE\\" + service_module.name, false); |         bool use_lle = sdl2_config->GetBoolean("Debugging", "LLE\\" + service_module.name, false); | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
|  | #include "common/settings.h" | ||||||
| 
 | 
 | ||||||
| class INIReader; | class INIReader; | ||||||
| 
 | 
 | ||||||
|  | @ -23,4 +24,14 @@ public: | ||||||
|     ~Config(); |     ~Config(); | ||||||
| 
 | 
 | ||||||
|     void Reload(); |     void Reload(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     /**
 | ||||||
|  |      * Applies a value read from the sdl2_config to a Setting. | ||||||
|  |      * | ||||||
|  |      * @param group The name of the INI group | ||||||
|  |      * @param setting The yuzu setting to modify | ||||||
|  |      */ | ||||||
|  |     template <typename Type, bool ranged> | ||||||
|  |     void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -98,13 +98,9 @@ use_cpu_jit = | ||||||
| cpu_clock_percentage = | cpu_clock_percentage = | ||||||
| 
 | 
 | ||||||
| [Renderer] | [Renderer] | ||||||
| # Whether to render using GLES or OpenGL | # Whether to render using OpenGL | ||||||
| # 0: OpenGL, 1 (default): GLES | # 1: OpenGLES (default) | ||||||
| use_gles = | graphics_api = | ||||||
| 
 |  | ||||||
| # Whether to use software or hardware rendering. |  | ||||||
| # 0: Software, 1 (default): Hardware |  | ||||||
| use_hw_renderer = |  | ||||||
| 
 | 
 | ||||||
| # Whether to use hardware shaders to emulate 3DS shaders | # Whether to use hardware shaders to emulate 3DS shaders | ||||||
| # 0: Software, 1 (default): Hardware | # 0: Software, 1 (default): Hardware | ||||||
|  | @ -118,10 +114,6 @@ separable_shader = | ||||||
| # 0: Off (Default. Faster, but causes issues in some games) 1: On (Slower, but correct) | # 0: Off (Default. Faster, but causes issues in some games) 1: On (Slower, but correct) | ||||||
| shaders_accurate_mul = | shaders_accurate_mul = | ||||||
| 
 | 
 | ||||||
| # Enable asynchronous GPU emulation |  | ||||||
| # 0: Off (Slower, but more accurate) 1: On (Default. Faster, but may cause issues in some games) |  | ||||||
| use_asynchronous_gpu_emulation = |  | ||||||
| 
 |  | ||||||
| # Whether to use the Just-In-Time (JIT) compiler for shader emulation | # Whether to use the Just-In-Time (JIT) compiler for shader emulation | ||||||
| # 0: Interpreter (slow), 1 (default): JIT (fast) | # 0: Interpreter (slow), 1 (default): JIT (fast) | ||||||
| use_shader_jit = | use_shader_jit = | ||||||
|  | @ -325,9 +317,15 @@ log_filter = *:Info | ||||||
| [Debugging] | [Debugging] | ||||||
| # Record frame time data, can be found in the log directory. Boolean value | # Record frame time data, can be found in the log directory. Boolean value | ||||||
| record_frame_times = | record_frame_times = | ||||||
|  | 
 | ||||||
|  | # Whether to enable additional debugging information during emulation | ||||||
|  | # 0 (default): Off, 1: On | ||||||
|  | renderer_debug = | ||||||
|  | 
 | ||||||
| # Port for listening to GDB connections. | # Port for listening to GDB connections. | ||||||
| use_gdbstub=false | use_gdbstub=false | ||||||
| gdbstub_port=24689 | gdbstub_port=24689 | ||||||
|  | 
 | ||||||
| # To LLE a service module add "LLE\<module name>=true" | # To LLE a service module add "LLE\<module name>=true" | ||||||
| 
 | 
 | ||||||
| [WebService] | [WebService] | ||||||
|  |  | ||||||
|  | @ -25,7 +25,6 @@ | ||||||
| #include "core/frontend/applets/default_applets.h" | #include "core/frontend/applets/default_applets.h" | ||||||
| #include "core/frontend/camera/factory.h" | #include "core/frontend/camera/factory.h" | ||||||
| #include "core/frontend/mic.h" | #include "core/frontend/mic.h" | ||||||
| #include "core/frontend/scope_acquire_context.h" |  | ||||||
| #include "core/hle/service/am/am.h" | #include "core/hle/service/am/am.h" | ||||||
| #include "core/hle/service/nfc/nfc.h" | #include "core/hle/service/nfc/nfc.h" | ||||||
| #include "core/savestate.h" | #include "core/savestate.h" | ||||||
|  | @ -46,6 +45,7 @@ | ||||||
| #include "jni/ndk_motion.h" | #include "jni/ndk_motion.h" | ||||||
| #include "video_core/renderer_base.h" | #include "video_core/renderer_base.h" | ||||||
| #include "video_core/renderer_opengl/texture_filters/texture_filterer.h" | #include "video_core/renderer_opengl/texture_filters/texture_filterer.h" | ||||||
|  | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
| namespace { | namespace { | ||||||
| 
 | 
 | ||||||
|  | @ -149,7 +149,15 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { | ||||||
|         return Core::System::ResultStatus::ErrorLoader; |         return Core::System::ResultStatus::ErrorLoader; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     window = std::make_unique<EmuWindow_Android>(s_surf); |     const auto graphics_api = Settings::values.graphics_api.GetValue(); | ||||||
|  |     switch (graphics_api) { | ||||||
|  |     case Settings::GraphicsAPI::OpenGL: | ||||||
|  |         window = std::make_unique<EmuWindow_Android>(s_surf); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         LOG_CRITICAL(Frontend, "Unknown graphics API {}, using OpenGL", graphics_api); | ||||||
|  |         window = std::make_unique<EmuWindow_Android>(s_surf); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     Core::System& system{Core::System::GetInstance()}; |     Core::System& system{Core::System::GetInstance()}; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -58,8 +58,6 @@ | ||||||
|     <string name="linear_filtering_description">Aktiviert lineare Filterung, welche die Spieletexturen glättet.</string> |     <string name="linear_filtering_description">Aktiviert lineare Filterung, welche die Spieletexturen glättet.</string> | ||||||
|     <string name="texture_filter_name">Texturfilter</string> |     <string name="texture_filter_name">Texturfilter</string> | ||||||
|     <string name="texture_filter_description">Verbessert die Grafik von Spielen durch das Anwendung eines Texturfilters. Die unterstützten Filter sind Anime4K Ultrafast, Bicubic, ScaleForce und xBRZ freescale.</string> |     <string name="texture_filter_description">Verbessert die Grafik von Spielen durch das Anwendung eines Texturfilters. Die unterstützten Filter sind Anime4K Ultrafast, Bicubic, ScaleForce und xBRZ freescale.</string> | ||||||
|     <string name="hw_renderer">Aktiviere Hardware Renderer</string> |  | ||||||
|     <string name="hw_renderer_description">Benutzt Hardware, um die 3DS-Grafik zu emulieren. Wenn aktiviert, wird die Spieleleistung stark verbessert.</string> |  | ||||||
|     <string name="hw_shaders">Aktiviere Hardware Shader</string> |     <string name="hw_shaders">Aktiviere Hardware Shader</string> | ||||||
|     <string name="hw_shaders_description">Benutzt Hardware, um die 3DS-Shader zu emulieren. Wenn aktiviert, wird die Spieleleistung stark verbessert.</string> |     <string name="hw_shaders_description">Benutzt Hardware, um die 3DS-Shader zu emulieren. Wenn aktiviert, wird die Spieleleistung stark verbessert.</string> | ||||||
|     <string name="shaders_accurate_mul">Aktiviere genaue Shader-Multiplikation</string> |     <string name="shaders_accurate_mul">Aktiviere genaue Shader-Multiplikation</string> | ||||||
|  |  | ||||||
|  | @ -62,8 +62,6 @@ | ||||||
|     <string name="linear_filtering_description">Activa el filtro linear, que hace que los gráficos del juego se vean más suaves.</string> |     <string name="linear_filtering_description">Activa el filtro linear, que hace que los gráficos del juego se vean más suaves.</string> | ||||||
|     <string name="texture_filter_name">Filtro de Texturas</string> |     <string name="texture_filter_name">Filtro de Texturas</string> | ||||||
|     <string name="texture_filter_description">Mejora los gráficos visuales de los juegos al aplicar un filtro a las texturas. Los filtros soportados son Anime4K Ultrafast, Bicubic, ScaleForce, y xBRZ freescale.</string> |     <string name="texture_filter_description">Mejora los gráficos visuales de los juegos al aplicar un filtro a las texturas. Los filtros soportados son Anime4K Ultrafast, Bicubic, ScaleForce, y xBRZ freescale.</string> | ||||||
|     <string name="hw_renderer">Activar renderizador de hardware</string> |  | ||||||
|     <string name="hw_renderer_description">Usa el hardware para emular los gráficos de 3DS. Cuando se active, el rendimiento mejorará notablemente.</string> |  | ||||||
|     <string name="hw_shaders">Activar sombreador de hardware</string> |     <string name="hw_shaders">Activar sombreador de hardware</string> | ||||||
|     <string name="hw_shaders_description">Usa el hardware para emular los sombreadores de 3DS. Cuando se active, el rendimiento mejorará notablemente.</string> |     <string name="hw_shaders_description">Usa el hardware para emular los sombreadores de 3DS. Cuando se active, el rendimiento mejorará notablemente.</string> | ||||||
|     <string name="shaders_accurate_mul">Activar multiplicación precisa de sombreado</string> |     <string name="shaders_accurate_mul">Activar multiplicación precisa de sombreado</string> | ||||||
|  |  | ||||||
|  | @ -46,8 +46,6 @@ | ||||||
|     <!-- Graphics settings strings --> |     <!-- Graphics settings strings --> | ||||||
|     <string name="vsync">Aktivoi V-Sync</string> |     <string name="vsync">Aktivoi V-Sync</string> | ||||||
|     <string name="vsync_description">Synkronoi pelin virkistystaajuus laitteesi virkistystaajuuteen.</string> |     <string name="vsync_description">Synkronoi pelin virkistystaajuus laitteesi virkistystaajuuteen.</string> | ||||||
|     <string name="hw_renderer">Aktivoi Laitteistorenderöinti</string> |  | ||||||
|     <string name="hw_renderer_description">Käyttää laitteistoa emuloidakseen 3DS-grafiikoita. Kun tämä on päällä, pelien suorituskyky on huomattavasti parempi.</string> |  | ||||||
|     <string name="hw_shaders">Aktivoi Laitteistovarjostin</string> |     <string name="hw_shaders">Aktivoi Laitteistovarjostin</string> | ||||||
|     <string name="hw_shaders_description">Käyttää laitteistoa emuloidakseen 3DS:n varjostimia. Kun tämä on päällä, pelien suorituskyky on huomattavasti parempi.</string> |     <string name="hw_shaders_description">Käyttää laitteistoa emuloidakseen 3DS:n varjostimia. Kun tämä on päällä, pelien suorituskyky on huomattavasti parempi.</string> | ||||||
|     <string name="frame_limit_enable">Aktivoi nopeuden rajoitus</string> |     <string name="frame_limit_enable">Aktivoi nopeuden rajoitus</string> | ||||||
|  |  | ||||||
|  | @ -58,8 +58,6 @@ | ||||||
|     <string name="linear_filtering_description">Active le filtrage linéaire, qui améliorera le lissage graphique du jeu.</string> |     <string name="linear_filtering_description">Active le filtrage linéaire, qui améliorera le lissage graphique du jeu.</string> | ||||||
|     <string name="texture_filter_name">Filtrage des textures</string> |     <string name="texture_filter_name">Filtrage des textures</string> | ||||||
|     <string name="texture_filter_description">Améliore l\'aspect graphique des jeux en appliquant un filtre aux textures. Les filtres supportés sont Anime4K Ultrafast, Bicubic, ScaleForce, et xBRZ freescale.</string> |     <string name="texture_filter_description">Améliore l\'aspect graphique des jeux en appliquant un filtre aux textures. Les filtres supportés sont Anime4K Ultrafast, Bicubic, ScaleForce, et xBRZ freescale.</string> | ||||||
|     <string name="hw_renderer">Activer le rendu matériel</string> |  | ||||||
|     <string name="hw_renderer_description">Utilise le matériel pour émuler les graphismes de la 3DS. Lorsqu\'il est activé, la performance des jeux sera améliorée de manière significative.</string> |  | ||||||
|     <string name="hw_shaders">Activer le shader (nuanceur) matériel </string> |     <string name="hw_shaders">Activer le shader (nuanceur) matériel </string> | ||||||
|     <string name="hw_shaders_description">Utilise le matériel pour émuler les shaders de la 3DS. Lorsqu\'il est activé, la performance des jeux sera améliorée de manière significative.</string> |     <string name="hw_shaders_description">Utilise le matériel pour émuler les shaders de la 3DS. Lorsqu\'il est activé, la performance des jeux sera améliorée de manière significative.</string> | ||||||
|     <string name="shaders_accurate_mul">Activer la multiplication précise dans les shaders</string> |     <string name="shaders_accurate_mul">Activer la multiplication précise dans les shaders</string> | ||||||
|  |  | ||||||
|  | @ -58,8 +58,6 @@ | ||||||
|     <string name="linear_filtering_description">Abilita il filtro lineare, che fa sembrare più smussata la grafica dei giochi.</string> |     <string name="linear_filtering_description">Abilita il filtro lineare, che fa sembrare più smussata la grafica dei giochi.</string> | ||||||
|     <string name="texture_filter_name">Filtro Texture</string> |     <string name="texture_filter_name">Filtro Texture</string> | ||||||
|     <string name="texture_filter_description">Migliora la grafica dei giochi applicando un filtro alle texture. I filtri supportati sono Anime4k Ultrafast, Bicubic, ScaleForce, e xBRZ freescale.</string> |     <string name="texture_filter_description">Migliora la grafica dei giochi applicando un filtro alle texture. I filtri supportati sono Anime4k Ultrafast, Bicubic, ScaleForce, e xBRZ freescale.</string> | ||||||
|     <string name="hw_renderer">Abilita renderer hardware</string> |  | ||||||
|     <string name="hw_renderer_description">Utilizza l\'hardware per emulare la grafica del 3DS. Se abilitato, le prestazioni dei giochi miglioreranno significativamente.</string> |  | ||||||
|     <string name="hw_shaders">Abilita shader hardware</string> |     <string name="hw_shaders">Abilita shader hardware</string> | ||||||
|     <string name="hw_shaders_description">Utilizza l\'hardware per emulare gli shader del 3DS. Se abilitato, le prestazioni dei giochi miglioreranno significativamente.</string> |     <string name="hw_shaders_description">Utilizza l\'hardware per emulare gli shader del 3DS. Se abilitato, le prestazioni dei giochi miglioreranno significativamente.</string> | ||||||
|     <string name="shaders_accurate_mul">Abilita moltiplicazione shader accurata</string> |     <string name="shaders_accurate_mul">Abilita moltiplicazione shader accurata</string> | ||||||
|  |  | ||||||
|  | @ -40,8 +40,6 @@ | ||||||
|     <string name="linear_filtering">リニアフィルタリングを有効化</string> |     <string name="linear_filtering">リニアフィルタリングを有効化</string> | ||||||
|     <string name="linear_filtering_description">有効にすると、よりなめらかな画質が期待できます。</string> |     <string name="linear_filtering_description">有効にすると、よりなめらかな画質が期待できます。</string> | ||||||
|     <string name="texture_filter_name">テクスチャフィルタ</string> |     <string name="texture_filter_name">テクスチャフィルタ</string> | ||||||
|     <string name="hw_renderer">ハードウェアレンダラを有効にする</string> |  | ||||||
|     <string name="hw_renderer_description">グラフィックエミュレーションにハードウェアを使用します。有効にすると、パフォーマンスが大幅に向上します。</string> |  | ||||||
|     <string name="hw_shaders">ハードウェアシェーダを有効にする</string> |     <string name="hw_shaders">ハードウェアシェーダを有効にする</string> | ||||||
|     <string name="hw_shaders_description">シェーダエミュレーションにハードウェアを使用します。有効にすると、パフォーマンスが大幅に向上します。</string> |     <string name="hw_shaders_description">シェーダエミュレーションにハードウェアを使用します。有効にすると、パフォーマンスが大幅に向上します。</string> | ||||||
|     <string name="shaders_accurate_mul">正確なシェーダ乗算を有効にする</string> |     <string name="shaders_accurate_mul">正確なシェーダ乗算を有効にする</string> | ||||||
|  |  | ||||||
|  | @ -58,8 +58,6 @@ | ||||||
|     <string name="linear_filtering_description">게임 필터링이 매끄럽게 보이도록 선형 필터링을 활성화합니다.</string> |     <string name="linear_filtering_description">게임 필터링이 매끄럽게 보이도록 선형 필터링을 활성화합니다.</string> | ||||||
|     <string name="texture_filter_name">텍스처 필터</string> |     <string name="texture_filter_name">텍스처 필터</string> | ||||||
|     <string name="texture_filter_description">텍스처에 필터를 적용하여 게임의 시각적 효과를 향상시킵니다. 지원되는 필터는 Anime4K Ultrafast, Bicubic, ScaleForce 및 xBRZ 프리스케일입니다.</string> |     <string name="texture_filter_description">텍스처에 필터를 적용하여 게임의 시각적 효과를 향상시킵니다. 지원되는 필터는 Anime4K Ultrafast, Bicubic, ScaleForce 및 xBRZ 프리스케일입니다.</string> | ||||||
|     <string name="hw_renderer">하드웨어 렌더러 사용</string> |  | ||||||
|     <string name="hw_renderer_description">하드웨어를 사용하여 3DS 그래픽을 에뮬레이션합니다. 활성화하면 게임 성능이 크게 향상됩니다.</string> |  | ||||||
|     <string name="hw_shaders">하드웨어 쉐이더 사용</string> |     <string name="hw_shaders">하드웨어 쉐이더 사용</string> | ||||||
|     <string name="hw_shaders_description">하드웨어를 사용하여 3DS 쉐이더를 에뮬레이션합니다. 활성화하면 게임 성능이 크게 향상됩니다.</string> |     <string name="hw_shaders_description">하드웨어를 사용하여 3DS 쉐이더를 에뮬레이션합니다. 활성화하면 게임 성능이 크게 향상됩니다.</string> | ||||||
|     <string name="shaders_accurate_mul">정확한 쉐이더 곱셉 사용</string> |     <string name="shaders_accurate_mul">정확한 쉐이더 곱셉 사용</string> | ||||||
|  |  | ||||||
|  | @ -58,8 +58,6 @@ | ||||||
|     <string name="linear_filtering_description">Aktiverer lineær filtrering, noe som får spillvisualer til å vises jevnere.</string> |     <string name="linear_filtering_description">Aktiverer lineær filtrering, noe som får spillvisualer til å vises jevnere.</string> | ||||||
|     <string name="texture_filter_name">Tekstur Filter</string> |     <string name="texture_filter_name">Tekstur Filter</string> | ||||||
|     <string name="texture_filter_description">Forbedrer det visuelle i spill ved å bruke et filter på teksturer. De støttede filtrene er Anime4K Ultrafast, Bicubic, ScaleForce og xBRZ freescale.</string> |     <string name="texture_filter_description">Forbedrer det visuelle i spill ved å bruke et filter på teksturer. De støttede filtrene er Anime4K Ultrafast, Bicubic, ScaleForce og xBRZ freescale.</string> | ||||||
|     <string name="hw_renderer">Aktiver maskinvaregjengivelse</string> |  | ||||||
|     <string name="hw_renderer_description">Bruker maskinvare til å emulere 3DS grafikk. Når dette er aktivert, vil spillytelsen bli betydelig forbedret.</string> |  | ||||||
|     <string name="hw_shaders">Aktiver maskinvare shader</string> |     <string name="hw_shaders">Aktiver maskinvare shader</string> | ||||||
|     <string name="hw_shaders_description">Bruker maskinvare for å etterligne 3DS shaders. Når dette er aktivert, vil spillytelsen bli betydelig forbedret.</string> |     <string name="hw_shaders_description">Bruker maskinvare for å etterligne 3DS shaders. Når dette er aktivert, vil spillytelsen bli betydelig forbedret.</string> | ||||||
|     <string name="shaders_accurate_mul">Aktiver nøyaktig shader-multiplikasjon</string> |     <string name="shaders_accurate_mul">Aktiver nøyaktig shader-multiplikasjon</string> | ||||||
|  |  | ||||||
|  | @ -58,8 +58,6 @@ | ||||||
|     <string name="linear_filtering_description">Ativa a filtragem linear, que suaviza o visual do jogo.</string> |     <string name="linear_filtering_description">Ativa a filtragem linear, que suaviza o visual do jogo.</string> | ||||||
|     <string name="texture_filter_name">Filtro de texturas</string> |     <string name="texture_filter_name">Filtro de texturas</string> | ||||||
|     <string name="texture_filter_description">Aprimora o visual dos jogos ao aplicar filtros às texturas. Os filtros compatíveis são: Anime4K Ultrafast, Bicúbico, ScaleForce e xBRZ Freescale.</string> |     <string name="texture_filter_description">Aprimora o visual dos jogos ao aplicar filtros às texturas. Os filtros compatíveis são: Anime4K Ultrafast, Bicúbico, ScaleForce e xBRZ Freescale.</string> | ||||||
|     <string name="hw_renderer">Ativar renderizador por hardware</string> |  | ||||||
|     <string name="hw_renderer_description">Utiliza o hardware para emular os gráficos do 3DS. Quando ativado, o desempenho de jogo será consideravelmente melhorado.</string> |  | ||||||
|     <string name="hw_shaders">Ativar shaders via hardware</string> |     <string name="hw_shaders">Ativar shaders via hardware</string> | ||||||
|     <string name="hw_shaders_description">Utiliza o hardware para emular os shaders do 3DS. Quando ativado, o desempenho do jogo será consideravelmente melhorado.</string> |     <string name="hw_shaders_description">Utiliza o hardware para emular os shaders do 3DS. Quando ativado, o desempenho do jogo será consideravelmente melhorado.</string> | ||||||
|     <string name="shaders_accurate_mul">Ativar multiplicação precisa de shaders</string> |     <string name="shaders_accurate_mul">Ativar multiplicação precisa de shaders</string> | ||||||
|  |  | ||||||
|  | @ -58,8 +58,6 @@ | ||||||
|     <string name="linear_filtering_description">开启后,游戏视觉效果会更加平滑。</string> |     <string name="linear_filtering_description">开启后,游戏视觉效果会更加平滑。</string> | ||||||
|     <string name="texture_filter_name">纹理滤镜</string> |     <string name="texture_filter_name">纹理滤镜</string> | ||||||
|     <string name="texture_filter_description">通过对纹理使用滤镜来增强游戏的视觉效果。支持的滤镜有 Anime4K Ultrafast, Bicubic, ScaleForce 和 xBRZ freescale。</string> |     <string name="texture_filter_description">通过对纹理使用滤镜来增强游戏的视觉效果。支持的滤镜有 Anime4K Ultrafast, Bicubic, ScaleForce 和 xBRZ freescale。</string> | ||||||
|     <string name="hw_renderer">启用硬件渲染器</string> |  | ||||||
|     <string name="hw_renderer_description">使用硬件模拟 3DS 图形。启用后,游戏性能将显著提高。</string> |  | ||||||
|     <string name="hw_shaders">启用硬件着色器</string> |     <string name="hw_shaders">启用硬件着色器</string> | ||||||
|     <string name="hw_shaders_description">使用硬件模拟 3DS 着色器。启用后,游戏性能将显著提高。</string> |     <string name="hw_shaders_description">使用硬件模拟 3DS 着色器。启用后,游戏性能将显著提高。</string> | ||||||
|     <string name="shaders_accurate_mul">启用精确乘法运算</string> |     <string name="shaders_accurate_mul">启用精确乘法运算</string> | ||||||
|  |  | ||||||
|  | @ -171,4 +171,12 @@ | ||||||
|         <item>4</item> |         <item>4</item> | ||||||
|         <item>5</item> |         <item>5</item> | ||||||
|     </integer-array> |     </integer-array> | ||||||
|  | 
 | ||||||
|  |     <string-array name="graphicsApiNames"> | ||||||
|  |         <item>OpenGLES</item> | ||||||
|  |     </string-array> | ||||||
|  | 
 | ||||||
|  |     <integer-array name="graphicsApiValues"> | ||||||
|  |         <item>1</item> | ||||||
|  |     </integer-array> | ||||||
| </resources> | </resources> | ||||||
|  |  | ||||||
|  | @ -72,14 +72,15 @@ | ||||||
| 
 | 
 | ||||||
|     <!-- Graphics settings strings --> |     <!-- Graphics settings strings --> | ||||||
|     <string name="renderer">Renderer</string> |     <string name="renderer">Renderer</string> | ||||||
|  |     <string name="graphics_api">Graphics API</string> | ||||||
|  |     <string name="renderer_debug">Enable debug renderer</string> | ||||||
|  |     <string name="renderer_debug_description">Log additional graphics related debug information. When enabled, game performance will be significantly reduced</string> | ||||||
|     <string name="vsync">Enable V-Sync</string> |     <string name="vsync">Enable V-Sync</string> | ||||||
|     <string name="vsync_description">Synchronizes the game frame rate to the refresh rate of your device.</string> |     <string name="vsync_description">Synchronizes the game frame rate to the refresh rate of your device.</string> | ||||||
|     <string name="linear_filtering">Enable linear filtering</string> |     <string name="linear_filtering">Enable linear filtering</string> | ||||||
|     <string name="linear_filtering_description">Enables linear filtering, which causes game visuals to appear smoother.</string> |     <string name="linear_filtering_description">Enables linear filtering, which causes game visuals to appear smoother.</string> | ||||||
|     <string name="texture_filter_name">Texture Filter</string> |     <string name="texture_filter_name">Texture Filter</string> | ||||||
|     <string name="texture_filter_description">Enhances the visuals of games by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, and xBRZ freescale.</string> |     <string name="texture_filter_description">Enhances the visuals of games by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, and xBRZ freescale.</string> | ||||||
|     <string name="hw_renderer">Enable hardware renderer</string> |  | ||||||
|     <string name="hw_renderer_description">Uses hardware to emulate 3DS graphics. When enabled, game performance will be significantly improved.</string> |  | ||||||
|     <string name="hw_shaders">Enable hardware shader</string> |     <string name="hw_shaders">Enable hardware shader</string> | ||||||
|     <string name="hw_shaders_description">Uses hardware to emulate 3DS shaders. When enabled, game performance will be significantly improved.</string> |     <string name="hw_shaders_description">Uses hardware to emulate 3DS shaders. When enabled, game performance will be significantly improved.</string> | ||||||
|     <string name="shaders_accurate_mul">Enable accurate shader multiplication</string> |     <string name="shaders_accurate_mul">Enable accurate shader multiplication</string> | ||||||
|  |  | ||||||
|  | @ -8,6 +8,10 @@ add_executable(citra | ||||||
|     default_ini.h |     default_ini.h | ||||||
|     emu_window/emu_window_sdl2.cpp |     emu_window/emu_window_sdl2.cpp | ||||||
|     emu_window/emu_window_sdl2.h |     emu_window/emu_window_sdl2.h | ||||||
|  |     emu_window/emu_window_sdl2_gl.cpp | ||||||
|  |     emu_window/emu_window_sdl2_gl.h | ||||||
|  |     emu_window/emu_window_sdl2_sw.cpp | ||||||
|  |     emu_window/emu_window_sdl2_sw.h | ||||||
|     lodepng_image_interface.cpp |     lodepng_image_interface.cpp | ||||||
|     lodepng_image_interface.h |     lodepng_image_interface.h | ||||||
|     precompiled_headers.h |     precompiled_headers.h | ||||||
|  |  | ||||||
|  | @ -13,6 +13,8 @@ | ||||||
| 
 | 
 | ||||||
| #include "citra/config.h" | #include "citra/config.h" | ||||||
| #include "citra/emu_window/emu_window_sdl2.h" | #include "citra/emu_window/emu_window_sdl2.h" | ||||||
|  | #include "citra/emu_window/emu_window_sdl2_gl.h" | ||||||
|  | #include "citra/emu_window/emu_window_sdl2_sw.h" | ||||||
| #include "citra/lodepng_image_interface.h" | #include "citra/lodepng_image_interface.h" | ||||||
| #include "common/common_paths.h" | #include "common/common_paths.h" | ||||||
| #include "common/detached_tasks.h" | #include "common/detached_tasks.h" | ||||||
|  | @ -29,7 +31,6 @@ | ||||||
| #include "core/file_sys/cia_container.h" | #include "core/file_sys/cia_container.h" | ||||||
| #include "core/frontend/applets/default_applets.h" | #include "core/frontend/applets/default_applets.h" | ||||||
| #include "core/frontend/framebuffer_layout.h" | #include "core/frontend/framebuffer_layout.h" | ||||||
| #include "core/frontend/scope_acquire_context.h" |  | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
| #include "core/hle/service/am/am.h" | #include "core/hle/service/am/am.h" | ||||||
| #include "core/hle/service/cfg/cfg.h" | #include "core/hle/service/cfg/cfg.h" | ||||||
|  | @ -38,6 +39,7 @@ | ||||||
| #include "input_common/main.h" | #include "input_common/main.h" | ||||||
| #include "network/network.h" | #include "network/network.h" | ||||||
| #include "video_core/renderer_base.h" | #include "video_core/renderer_base.h" | ||||||
|  | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
| #undef _UNICODE | #undef _UNICODE | ||||||
| #include <getopt.h> | #include <getopt.h> | ||||||
|  | @ -362,13 +364,23 @@ int main(int argc, char** argv) { | ||||||
| 
 | 
 | ||||||
|     EmuWindow_SDL2::InitializeSDL2(); |     EmuWindow_SDL2::InitializeSDL2(); | ||||||
| 
 | 
 | ||||||
|     const auto emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen, false)}; |     const auto CreateEmuWindow = [](bool fullscreen, | ||||||
|     const bool use_secondary_window{Settings::values.layout_option.GetValue() == |                                     bool is_secondary) -> std::unique_ptr<EmuWindow_SDL2> { | ||||||
|                                     Settings::LayoutOption::SeparateWindows}; |         switch (Settings::values.graphics_api.GetValue()) { | ||||||
|     const auto secondary_window = |         case Settings::GraphicsAPI::OpenGL: | ||||||
|         use_secondary_window ? std::make_unique<EmuWindow_SDL2>(false, true) : nullptr; |             return std::make_unique<EmuWindow_SDL2_GL>(fullscreen, is_secondary); | ||||||
|  |         case Settings::GraphicsAPI::Software: | ||||||
|  |             return std::make_unique<EmuWindow_SDL2_SW>(fullscreen, is_secondary); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     Frontend::ScopeAcquireContext scope(*emu_window); |     const auto emu_window{CreateEmuWindow(fullscreen, false)}; | ||||||
|  |     const bool use_secondary_window{ | ||||||
|  |         Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows && | ||||||
|  |         Settings::values.graphics_api.GetValue() != Settings::GraphicsAPI::Software}; | ||||||
|  |     const auto secondary_window = use_secondary_window ? CreateEmuWindow(false, true) : nullptr; | ||||||
|  | 
 | ||||||
|  |     const auto scope = emu_window->Acquire(); | ||||||
| 
 | 
 | ||||||
|     LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, |     LOG_INFO(Frontend, "Citra Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, | ||||||
|              Common::g_scm_desc); |              Common::g_scm_desc); | ||||||
|  | @ -400,9 +412,6 @@ int main(int argc, char** argv) { | ||||||
|     case Core::System::ResultStatus::ErrorSystemMode: |     case Core::System::ResultStatus::ErrorSystemMode: | ||||||
|         LOG_CRITICAL(Frontend, "Failed to determine system mode!"); |         LOG_CRITICAL(Frontend, "Failed to determine system mode!"); | ||||||
|         return -1; |         return -1; | ||||||
|     case Core::System::ResultStatus::ErrorVideoCore: |  | ||||||
|         LOG_CRITICAL(Frontend, "VideoCore not initialized"); |  | ||||||
|         return -1; |  | ||||||
|     case Core::System::ResultStatus::Success: |     case Core::System::ResultStatus::Success: | ||||||
|         break; // Expected case
 |         break; // Expected case
 | ||||||
|     default: |     default: | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| #include <iomanip> | #include <iomanip> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  | #include <type_traits> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <SDL.h> | #include <SDL.h> | ||||||
| #include <inih/cpp/INIReader.h> | #include <inih/cpp/INIReader.h> | ||||||
|  | @ -71,6 +72,30 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> | ||||||
|     }, |     }, | ||||||
| }}; | }}; | ||||||
| 
 | 
 | ||||||
|  | template <> | ||||||
|  | void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) { | ||||||
|  |     std::string setting_value = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); | ||||||
|  |     if (setting_value.empty()) { | ||||||
|  |         setting_value = setting.GetDefault(); | ||||||
|  |     } | ||||||
|  |     setting = std::move(setting_value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) { | ||||||
|  |     setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <typename Type, bool ranged> | ||||||
|  | void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) { | ||||||
|  |     if constexpr (std::is_floating_point_v<Type>) { | ||||||
|  |         setting = sdl2_config->GetReal(group, setting.GetLabel(), setting.GetDefault()); | ||||||
|  |     } else { | ||||||
|  |         setting = static_cast<Type>(sdl2_config->GetInteger( | ||||||
|  |             group, setting.GetLabel(), static_cast<long>(setting.GetDefault()))); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void Config::ReadValues() { | void Config::ReadValues() { | ||||||
|     // Controls
 |     // Controls
 | ||||||
|     // TODO: add multiple input profile support
 |     // TODO: add multiple input profile support
 | ||||||
|  | @ -104,104 +129,71 @@ void Config::ReadValues() { | ||||||
|                                                  InputCommon::CemuhookUDP::DEFAULT_PORT)); |                                                  InputCommon::CemuhookUDP::DEFAULT_PORT)); | ||||||
| 
 | 
 | ||||||
|     // Core
 |     // Core
 | ||||||
|     Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); |     ReadSetting("Core", Settings::values.use_cpu_jit); | ||||||
|     Settings::values.cpu_clock_percentage = |     ReadSetting("Core", Settings::values.cpu_clock_percentage); | ||||||
|         sdl2_config->GetInteger("Core", "cpu_clock_percentage", 100); |  | ||||||
| 
 | 
 | ||||||
|     // Renderer
 |     // Renderer
 | ||||||
|     Settings::values.use_gles = sdl2_config->GetBoolean("Renderer", "use_gles", false); |     ReadSetting("Renderer", Settings::values.graphics_api); | ||||||
|     Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); |     ReadSetting("Renderer", Settings::values.use_gles); | ||||||
|     Settings::values.use_hw_shader = sdl2_config->GetBoolean("Renderer", "use_hw_shader", true); |     ReadSetting("Renderer", Settings::values.use_hw_shader); | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
|     // Separable shader is broken on macos with Intel GPU thanks to poor drivers.
 |     // Separable shader is broken on macos with Intel GPU thanks to poor drivers.
 | ||||||
|     // We still want to provide this option for test/development purposes, but disable it by
 |     // We still want to provide this option for test/development purposes, but disable it by
 | ||||||
|     // default.
 |     // default.
 | ||||||
|     Settings::values.separable_shader = |     ReadSetting("Renderer", Settings::values.separable_shader); | ||||||
|         sdl2_config->GetBoolean("Renderer", "separable_shader", false); |  | ||||||
| #endif | #endif | ||||||
|     Settings::values.shaders_accurate_mul = |     ReadSetting("Renderer", Settings::values.shaders_accurate_mul); | ||||||
|         sdl2_config->GetBoolean("Renderer", "shaders_accurate_mul", true); |     ReadSetting("Renderer", Settings::values.use_shader_jit); | ||||||
|     Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true); |     ReadSetting("Renderer", Settings::values.resolution_factor); | ||||||
|     Settings::values.resolution_factor = |     ReadSetting("Renderer", Settings::values.use_disk_shader_cache); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Renderer", "resolution_factor", 1)); |     ReadSetting("Renderer", Settings::values.frame_limit); | ||||||
|     Settings::values.use_disk_shader_cache = |     ReadSetting("Renderer", Settings::values.use_vsync_new); | ||||||
|         sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", true); |     ReadSetting("Renderer", Settings::values.texture_filter_name); | ||||||
|     Settings::values.frame_limit = |  | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); |  | ||||||
|     Settings::values.use_vsync_new = |  | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync_new", 1)); |  | ||||||
|     Settings::values.texture_filter_name = |  | ||||||
|         sdl2_config->GetString("Renderer", "texture_filter_name", "none"); |  | ||||||
| 
 | 
 | ||||||
|     Settings::values.mono_render_option = static_cast<Settings::MonoRenderOption>( |     ReadSetting("Renderer", Settings::values.mono_render_option); | ||||||
|         sdl2_config->GetInteger("Renderer", "mono_render_option", 0)); |     ReadSetting("Renderer", Settings::values.render_3d); | ||||||
|     Settings::values.render_3d = static_cast<Settings::StereoRenderOption>( |     ReadSetting("Renderer", Settings::values.factor_3d); | ||||||
|         sdl2_config->GetInteger("Renderer", "render_3d", 0)); |     ReadSetting("Renderer", Settings::values.pp_shader_name); | ||||||
|     Settings::values.factor_3d = |     ReadSetting("Renderer", Settings::values.anaglyph_shader_name); | ||||||
|         static_cast<u8>(sdl2_config->GetInteger("Renderer", "factor_3d", 0)); |     ReadSetting("Renderer", Settings::values.filter_mode); | ||||||
|     Settings::values.pp_shader_name = |  | ||||||
|         sdl2_config->GetString("Renderer", "pp_shader_name", "none (builtin)"); |  | ||||||
|     Settings::values.anaglyph_shader_name = |  | ||||||
|         sdl2_config->GetString("Renderer", "anaglyph_shader_name", "dubois (builtin)"); |  | ||||||
|     Settings::values.filter_mode = sdl2_config->GetBoolean("Renderer", "filter_mode", true); |  | ||||||
| 
 | 
 | ||||||
|     Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); |     ReadSetting("Renderer", Settings::values.bg_red); | ||||||
|     Settings::values.bg_green = |     ReadSetting("Renderer", Settings::values.bg_green); | ||||||
|         static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)); |     ReadSetting("Renderer", Settings::values.bg_blue); | ||||||
|     Settings::values.bg_blue = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)); |  | ||||||
| 
 | 
 | ||||||
|     // Layout
 |     // Layout
 | ||||||
|     Settings::values.layout_option = |     ReadSetting("Layout", Settings::values.layout_option); | ||||||
|         static_cast<Settings::LayoutOption>(sdl2_config->GetInteger("Layout", "layout_option", 0)); |     ReadSetting("Layout", Settings::values.swap_screen); | ||||||
|     Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false); |     ReadSetting("Layout", Settings::values.upright_screen); | ||||||
|     Settings::values.upright_screen = sdl2_config->GetBoolean("Layout", "upright_screen", false); |     ReadSetting("Layout", Settings::values.large_screen_proportion); | ||||||
|     Settings::values.large_screen_proportion = |     ReadSetting("Layout", Settings::values.custom_layout); | ||||||
|         sdl2_config->GetReal("Layout", "large_screen_proportion", 4.0); |     ReadSetting("Layout", Settings::values.custom_top_left); | ||||||
|     Settings::values.custom_layout = sdl2_config->GetBoolean("Layout", "custom_layout", false); |     ReadSetting("Layout", Settings::values.custom_top_top); | ||||||
|     Settings::values.custom_top_left = |     ReadSetting("Layout", Settings::values.custom_top_right); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_left", 0)); |     ReadSetting("Layout", Settings::values.custom_top_bottom); | ||||||
|     Settings::values.custom_top_top = |     ReadSetting("Layout", Settings::values.custom_bottom_left); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_top", 0)); |     ReadSetting("Layout", Settings::values.custom_bottom_top); | ||||||
|     Settings::values.custom_top_right = |     ReadSetting("Layout", Settings::values.custom_bottom_right); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_right", 400)); |     ReadSetting("Layout", Settings::values.custom_bottom_bottom); | ||||||
|     Settings::values.custom_top_bottom = |     ReadSetting("Layout", Settings::values.custom_second_layer_opacity); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_bottom", 240)); |  | ||||||
|     Settings::values.custom_bottom_left = |  | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_left", 40)); |  | ||||||
|     Settings::values.custom_bottom_top = |  | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_top", 240)); |  | ||||||
|     Settings::values.custom_bottom_right = |  | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_right", 360)); |  | ||||||
|     Settings::values.custom_bottom_bottom = |  | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_bottom_bottom", 480)); |  | ||||||
|     Settings::values.custom_second_layer_opacity = |  | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_second_layer_opacity", 100)); |  | ||||||
| 
 | 
 | ||||||
|     // Utility
 |     // Utility
 | ||||||
|     Settings::values.dump_textures = sdl2_config->GetBoolean("Utility", "dump_textures", false); |     ReadSetting("Utility", Settings::values.dump_textures); | ||||||
|     Settings::values.custom_textures = sdl2_config->GetBoolean("Utility", "custom_textures", false); |     ReadSetting("Utility", Settings::values.custom_textures); | ||||||
|     Settings::values.preload_textures = |     ReadSetting("Utility", Settings::values.preload_textures); | ||||||
|         sdl2_config->GetBoolean("Utility", "preload_textures", false); |  | ||||||
| 
 | 
 | ||||||
|     // Audio
 |     // Audio
 | ||||||
|     Settings::values.audio_emulation = static_cast<Settings::AudioEmulation>( |     ReadSetting("Audio", Settings::values.audio_emulation); | ||||||
|         sdl2_config->GetInteger("Audio", "audio_emulation", 0)); |     ReadSetting("Audio", Settings::values.sink_id); | ||||||
|     Settings::values.sink_id = sdl2_config->GetString("Audio", "output_engine", "auto"); |     ReadSetting("Audio", Settings::values.enable_audio_stretching); | ||||||
|     Settings::values.enable_audio_stretching = |     ReadSetting("Audio", Settings::values.audio_device_id); | ||||||
|         sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); |     ReadSetting("Audio", Settings::values.volume); | ||||||
|     Settings::values.audio_device_id = sdl2_config->GetString("Audio", "output_device", "auto"); |     ReadSetting("Audio", Settings::values.mic_input_device); | ||||||
|     Settings::values.volume = static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1)); |     ReadSetting("Audio", Settings::values.mic_input_type); | ||||||
|     Settings::values.mic_input_device = |  | ||||||
|         sdl2_config->GetString("Audio", "mic_input_device", Frontend::Mic::default_device_name); |  | ||||||
|     Settings::values.mic_input_type = |  | ||||||
|         static_cast<Settings::MicInputType>(sdl2_config->GetInteger("Audio", "mic_input_type", 0)); |  | ||||||
| 
 | 
 | ||||||
|     // Data Storage
 |     // Data Storage
 | ||||||
|     Settings::values.use_virtual_sd = |     ReadSetting("Data Storage", Settings::values.use_virtual_sd); | ||||||
|         sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); |     ReadSetting("Data Storage", Settings::values.use_custom_storage); | ||||||
| 
 |  | ||||||
|     Settings::values.use_custom_storage = |  | ||||||
|         sdl2_config->GetBoolean("Data Storage", "use_custom_storage", false); |  | ||||||
| 
 | 
 | ||||||
|     if (Settings::values.use_custom_storage) { |     if (Settings::values.use_custom_storage) { | ||||||
|         FileUtil::UpdateUserPath(FileUtil::UserPath::NANDDir, |         FileUtil::UpdateUserPath(FileUtil::UserPath::NANDDir, | ||||||
|  | @ -211,11 +203,9 @@ void Config::ReadValues() { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // System
 |     // System
 | ||||||
|     Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", true); |     ReadSetting("System", Settings::values.is_new_3ds); | ||||||
|     Settings::values.region_value = |     ReadSetting("System", Settings::values.region_value); | ||||||
|         sdl2_config->GetInteger("System", "region_value", Settings::REGION_VALUE_AUTO_SELECT); |     ReadSetting("System", Settings::values.init_clock); | ||||||
|     Settings::values.init_clock = |  | ||||||
|         static_cast<Settings::InitClock>(sdl2_config->GetInteger("System", "init_clock", 1)); |  | ||||||
|     { |     { | ||||||
|         std::tm t; |         std::tm t; | ||||||
|         t.tm_sec = 1; |         t.tm_sec = 1; | ||||||
|  | @ -236,6 +226,8 @@ void Config::ReadValues() { | ||||||
|                 std::chrono::system_clock::from_time_t(std::mktime(&t)).time_since_epoch()) |                 std::chrono::system_clock::from_time_t(std::mktime(&t)).time_since_epoch()) | ||||||
|                 .count(); |                 .count(); | ||||||
|     } |     } | ||||||
|  |     ReadSetting("System", Settings::values.plugin_loader_enabled); | ||||||
|  |     ReadSetting("System", Settings::values.allow_plugin_loader); | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         constexpr const char* default_init_time_offset = "0 00:00:00"; |         constexpr const char* default_init_time_offset = "0 00:00:00"; | ||||||
|  | @ -311,14 +303,14 @@ void Config::ReadValues() { | ||||||
|         sdl2_config->GetInteger("Camera", "camera_outer_left_flip", 0); |         sdl2_config->GetInteger("Camera", "camera_outer_left_flip", 0); | ||||||
| 
 | 
 | ||||||
|     // Miscellaneous
 |     // Miscellaneous
 | ||||||
|     Settings::values.log_filter = sdl2_config->GetString("Miscellaneous", "log_filter", "*:Info"); |     ReadSetting("Miscellaneous", Settings::values.log_filter); | ||||||
| 
 | 
 | ||||||
|     // Debugging
 |     // Debugging
 | ||||||
|     Settings::values.record_frame_times = |     Settings::values.record_frame_times = | ||||||
|         sdl2_config->GetBoolean("Debugging", "record_frame_times", false); |         sdl2_config->GetBoolean("Debugging", "record_frame_times", false); | ||||||
|     Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); |     ReadSetting("Debugging", Settings::values.renderer_debug); | ||||||
|     Settings::values.gdbstub_port = |     ReadSetting("Debugging", Settings::values.use_gdbstub); | ||||||
|         static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); |     ReadSetting("Debugging", Settings::values.gdbstub_port); | ||||||
| 
 | 
 | ||||||
|     for (const auto& service_module : Service::service_module_map) { |     for (const auto& service_module : Service::service_module_map) { | ||||||
|         bool use_lle = sdl2_config->GetBoolean("Debugging", "LLE\\" + service_module.name, false); |         bool use_lle = sdl2_config->GetBoolean("Debugging", "LLE\\" + service_module.name, false); | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
|  | #include "common/settings.h" | ||||||
| 
 | 
 | ||||||
| class INIReader; | class INIReader; | ||||||
| 
 | 
 | ||||||
|  | @ -21,4 +22,14 @@ public: | ||||||
|     ~Config(); |     ~Config(); | ||||||
| 
 | 
 | ||||||
|     void Reload(); |     void Reload(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     /**
 | ||||||
|  |      * Applies a value read from the sdl2_config to a Setting. | ||||||
|  |      * | ||||||
|  |      * @param group The name of the INI group | ||||||
|  |      * @param setting The yuzu setting to modify | ||||||
|  |      */ | ||||||
|  |     template <typename Type, bool ranged> | ||||||
|  |     void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -98,14 +98,14 @@ use_cpu_jit = | ||||||
| cpu_clock_percentage = | cpu_clock_percentage = | ||||||
| 
 | 
 | ||||||
| [Renderer] | [Renderer] | ||||||
|  | # Whether to render using OpenGL or Software | ||||||
|  | # 0: Software, 1: OpenGL (default) | ||||||
|  | graphics_api = | ||||||
|  | 
 | ||||||
| # Whether to render using GLES or OpenGL | # Whether to render using GLES or OpenGL | ||||||
| # 0 (default): OpenGL, 1: GLES | # 0 (default): OpenGL, 1: GLES | ||||||
| use_gles = | use_gles = | ||||||
| 
 | 
 | ||||||
| # Whether to use software or hardware rendering. |  | ||||||
| # 0: Software, 1 (default): Hardware |  | ||||||
| use_hw_renderer = |  | ||||||
| 
 |  | ||||||
| # Whether to use hardware shaders to emulate 3DS shaders | # Whether to use hardware shaders to emulate 3DS shaders | ||||||
| # 0: Software, 1 (default): Hardware | # 0: Software, 1 (default): Hardware | ||||||
| use_hw_shader = | use_hw_shader = | ||||||
|  | @ -328,9 +328,15 @@ log_filter = *:Info | ||||||
| [Debugging] | [Debugging] | ||||||
| # Record frame time data, can be found in the log directory. Boolean value | # Record frame time data, can be found in the log directory. Boolean value | ||||||
| record_frame_times = | record_frame_times = | ||||||
|  | 
 | ||||||
| # Port for listening to GDB connections. | # Port for listening to GDB connections. | ||||||
| use_gdbstub=false | use_gdbstub=false | ||||||
| gdbstub_port=24689 | gdbstub_port=24689 | ||||||
|  | 
 | ||||||
|  | # Whether to enable additional debugging information during emulation | ||||||
|  | # 0 (default): Off, 1: On | ||||||
|  | renderer_debug = | ||||||
|  | 
 | ||||||
| # To LLE a service module add "LLE\<module name>=true" | # To LLE a service module add "LLE\<module name>=true" | ||||||
| 
 | 
 | ||||||
| [WebService] | [WebService] | ||||||
|  |  | ||||||
|  | @ -7,40 +7,14 @@ | ||||||
| #include <string> | #include <string> | ||||||
| #define SDL_MAIN_HANDLED | #define SDL_MAIN_HANDLED | ||||||
| #include <SDL.h> | #include <SDL.h> | ||||||
| #include <fmt/format.h> |  | ||||||
| #include <glad/glad.h> |  | ||||||
| #include "citra/emu_window/emu_window_sdl2.h" | #include "citra/emu_window/emu_window_sdl2.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/scm_rev.h" | #include "common/scm_rev.h" | ||||||
| #include "common/settings.h" |  | ||||||
| #include "core/3ds.h" |  | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "input_common/keyboard.h" | #include "input_common/keyboard.h" | ||||||
| #include "input_common/main.h" | #include "input_common/main.h" | ||||||
| #include "input_common/motion_emu.h" | #include "input_common/motion_emu.h" | ||||||
| #include "input_common/sdl/sdl.h" |  | ||||||
| #include "network/network.h" | #include "network/network.h" | ||||||
| #include "video_core/renderer_base.h" |  | ||||||
| #include "video_core/video_core.h" |  | ||||||
| 
 |  | ||||||
| SharedContext_SDL2::SharedContext_SDL2() { |  | ||||||
|     window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, |  | ||||||
|                               SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); |  | ||||||
|     context = SDL_GL_CreateContext(window); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| SharedContext_SDL2::~SharedContext_SDL2() { |  | ||||||
|     SDL_GL_DeleteContext(context); |  | ||||||
|     SDL_DestroyWindow(window); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SharedContext_SDL2::MakeCurrent() { |  | ||||||
|     SDL_GL_MakeCurrent(window, context); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SharedContext_SDL2::DoneCurrent() { |  | ||||||
|     SDL_GL_MakeCurrent(window, nullptr); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | ||||||
|     TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); |     TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); | ||||||
|  | @ -135,78 +109,9 @@ void EmuWindow_SDL2::Fullscreen() { | ||||||
|     SDL_MaximizeWindow(render_window); |     SDL_MaximizeWindow(render_window); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen, bool is_secondary) : EmuWindow(is_secondary) { | EmuWindow_SDL2::EmuWindow_SDL2(bool is_secondary) : EmuWindow(is_secondary) {} | ||||||
|     // Initialize the window
 |  | ||||||
|     if (Settings::values.use_gles) { |  | ||||||
|         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); |  | ||||||
|         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); |  | ||||||
|         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); |  | ||||||
|     } else { |  | ||||||
|         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); |  | ||||||
|         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); |  | ||||||
|         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); |  | ||||||
|     SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); |  | ||||||
|     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); |  | ||||||
|     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); |  | ||||||
|     SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); |  | ||||||
|     // Enable context sharing for the shared context
 |  | ||||||
|     SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); |  | ||||||
|     // Enable vsync
 |  | ||||||
|     SDL_GL_SetSwapInterval(1); |  | ||||||
| 
 |  | ||||||
|     std::string window_title = fmt::format("Citra {} | {}-{}", Common::g_build_fullname, |  | ||||||
|                                            Common::g_scm_branch, Common::g_scm_desc); |  | ||||||
|     render_window = |  | ||||||
|         SDL_CreateWindow(window_title.c_str(), |  | ||||||
|                          SDL_WINDOWPOS_UNDEFINED, // x position
 |  | ||||||
|                          SDL_WINDOWPOS_UNDEFINED, // y position
 |  | ||||||
|                          Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight, |  | ||||||
|                          SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); |  | ||||||
| 
 |  | ||||||
|     if (render_window == nullptr) { |  | ||||||
|         LOG_CRITICAL(Frontend, "Failed to create SDL2 window: {}", SDL_GetError()); |  | ||||||
|         exit(1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     dummy_window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, |  | ||||||
|                                     SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); |  | ||||||
| 
 |  | ||||||
|     if (fullscreen) { |  | ||||||
|         Fullscreen(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     window_context = SDL_GL_CreateContext(render_window); |  | ||||||
|     core_context = CreateSharedContext(); |  | ||||||
|     last_saved_context = nullptr; |  | ||||||
| 
 |  | ||||||
|     if (window_context == nullptr) { |  | ||||||
|         LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: {}", SDL_GetError()); |  | ||||||
|         exit(1); |  | ||||||
|     } |  | ||||||
|     if (core_context == nullptr) { |  | ||||||
|         LOG_CRITICAL(Frontend, "Failed to create shared SDL2 GL context: {}", SDL_GetError()); |  | ||||||
|         exit(1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     render_window_id = SDL_GetWindowID(render_window); |  | ||||||
|     auto gl_load_func = Settings::values.use_gles ? gladLoadGLES2Loader : gladLoadGLLoader; |  | ||||||
| 
 |  | ||||||
|     if (!gl_load_func(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { |  | ||||||
|         LOG_CRITICAL(Frontend, "Failed to initialize GL functions: {}", SDL_GetError()); |  | ||||||
|         exit(1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     OnResize(); |  | ||||||
|     OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |  | ||||||
|     SDL_PumpEvents(); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| EmuWindow_SDL2::~EmuWindow_SDL2() { | EmuWindow_SDL2::~EmuWindow_SDL2() { | ||||||
|     core_context.reset(); |  | ||||||
|     SDL_GL_DeleteContext(window_context); |  | ||||||
|     SDL_Quit(); |     SDL_Quit(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -222,28 +127,6 @@ void EmuWindow_SDL2::InitializeSDL2() { | ||||||
|     SDL_SetMainReady(); |     SDL_SetMainReady(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const { |  | ||||||
|     return std::make_unique<SharedContext_SDL2>(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void EmuWindow_SDL2::SaveContext() { |  | ||||||
|     last_saved_context = SDL_GL_GetCurrentContext(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void EmuWindow_SDL2::RestoreContext() { |  | ||||||
|     SDL_GL_MakeCurrent(render_window, last_saved_context); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void EmuWindow_SDL2::Present() { |  | ||||||
|     SDL_GL_MakeCurrent(render_window, window_context); |  | ||||||
|     SDL_GL_SetSwapInterval(1); |  | ||||||
|     while (IsOpen()) { |  | ||||||
|         VideoCore::g_renderer->TryPresent(100, is_secondary); |  | ||||||
|         SDL_GL_SwapWindow(render_window); |  | ||||||
|     } |  | ||||||
|     SDL_GL_MakeCurrent(render_window, nullptr); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void EmuWindow_SDL2::PollEvents() { | void EmuWindow_SDL2::PollEvents() { | ||||||
|     SDL_Event event; |     SDL_Event event; | ||||||
|     std::vector<SDL_Event> other_window_events; |     std::vector<SDL_Event> other_window_events; | ||||||
|  | @ -312,14 +195,6 @@ void EmuWindow_SDL2::PollEvents() { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmuWindow_SDL2::MakeCurrent() { |  | ||||||
|     core_context->MakeCurrent(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void EmuWindow_SDL2::DoneCurrent() { |  | ||||||
|     core_context->DoneCurrent(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) { | void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) { | ||||||
|     SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); |     SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -10,56 +10,27 @@ | ||||||
| 
 | 
 | ||||||
| struct SDL_Window; | struct SDL_Window; | ||||||
| 
 | 
 | ||||||
| class SharedContext_SDL2 : public Frontend::GraphicsContext { |  | ||||||
| public: |  | ||||||
|     using SDL_GLContext = void*; |  | ||||||
| 
 |  | ||||||
|     SharedContext_SDL2(); |  | ||||||
| 
 |  | ||||||
|     ~SharedContext_SDL2() override; |  | ||||||
| 
 |  | ||||||
|     void MakeCurrent() override; |  | ||||||
| 
 |  | ||||||
|     void DoneCurrent() override; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     SDL_GLContext context; |  | ||||||
|     SDL_Window* window; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class EmuWindow_SDL2 : public Frontend::EmuWindow { | class EmuWindow_SDL2 : public Frontend::EmuWindow { | ||||||
| public: | public: | ||||||
|     explicit EmuWindow_SDL2(bool fullscreen, bool is_secondary); |     explicit EmuWindow_SDL2(bool is_secondary); | ||||||
|     ~EmuWindow_SDL2(); |     ~EmuWindow_SDL2(); | ||||||
| 
 | 
 | ||||||
|  |     /// Initializes SDL2
 | ||||||
|     static void InitializeSDL2(); |     static void InitializeSDL2(); | ||||||
| 
 | 
 | ||||||
|     void Present(); |     /// Presents the most recent frame from the video backend
 | ||||||
|  |     virtual void Present() {} | ||||||
| 
 | 
 | ||||||
|     /// Polls window events
 |     /// Polls window events
 | ||||||
|     void PollEvents() override; |     void PollEvents() override; | ||||||
| 
 | 
 | ||||||
|     /// Makes the graphics context current for the caller thread
 |  | ||||||
|     void MakeCurrent() override; |  | ||||||
| 
 |  | ||||||
|     /// Releases the GL context from the caller thread
 |  | ||||||
|     void DoneCurrent() override; |  | ||||||
| 
 |  | ||||||
|     /// Whether the window is still open, and a close request hasn't yet been sent
 |     /// Whether the window is still open, and a close request hasn't yet been sent
 | ||||||
|     bool IsOpen() const; |     bool IsOpen() const; | ||||||
| 
 | 
 | ||||||
|     /// Close the window.
 |     /// Close the window.
 | ||||||
|     void RequestClose(); |     void RequestClose(); | ||||||
| 
 | 
 | ||||||
|     /// Creates a new context that is shared with the current context
 | protected: | ||||||
|     std::unique_ptr<GraphicsContext> CreateSharedContext() const override; |  | ||||||
| 
 |  | ||||||
|     /// Saves the current context, for the purpose of e.g. creating new shared contexts
 |  | ||||||
|     void SaveContext() override; |  | ||||||
|     /// Restores the context previously saved
 |  | ||||||
|     void RestoreContext() override; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     /// Called by PollEvents when a key is pressed or released.
 |     /// Called by PollEvents when a key is pressed or released.
 | ||||||
|     void OnKeyEvent(int key, u8 state); |     void OnKeyEvent(int key, u8 state); | ||||||
| 
 | 
 | ||||||
|  | @ -105,17 +76,6 @@ private: | ||||||
|     /// Fake hidden window for the core context
 |     /// Fake hidden window for the core context
 | ||||||
|     SDL_Window* dummy_window; |     SDL_Window* dummy_window; | ||||||
| 
 | 
 | ||||||
|     using SDL_GLContext = void*; |  | ||||||
| 
 |  | ||||||
|     /// The OpenGL context associated with the window
 |  | ||||||
|     SDL_GLContext window_context; |  | ||||||
| 
 |  | ||||||
|     /// Used by SaveContext and RestoreContext
 |  | ||||||
|     SDL_GLContext last_saved_context; |  | ||||||
| 
 |  | ||||||
|     /// The OpenGL context associated with the core
 |  | ||||||
|     std::unique_ptr<Frontend::GraphicsContext> core_context; |  | ||||||
| 
 |  | ||||||
|     /// Keeps track of how often to update the title bar during gameplay
 |     /// Keeps track of how often to update the title bar during gameplay
 | ||||||
|     u32 last_time = 0; |     u32 last_time = 0; | ||||||
| }; | }; | ||||||
|  |  | ||||||
							
								
								
									
										152
									
								
								src/citra/emu_window/emu_window_sdl2_gl.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								src/citra/emu_window/emu_window_sdl2_gl.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,152 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <string> | ||||||
|  | #define SDL_MAIN_HANDLED | ||||||
|  | #include <SDL.h> | ||||||
|  | #include <glad/glad.h> | ||||||
|  | #include "citra/emu_window/emu_window_sdl2_gl.h" | ||||||
|  | #include "common/scm_rev.h" | ||||||
|  | #include "video_core/renderer_base.h" | ||||||
|  | #include "video_core/video_core.h" | ||||||
|  | 
 | ||||||
|  | class SDLGLContext : public Frontend::GraphicsContext { | ||||||
|  | public: | ||||||
|  |     using SDL_GLContext = void*; | ||||||
|  | 
 | ||||||
|  |     SDLGLContext() { | ||||||
|  |         window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, | ||||||
|  |                                   SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); | ||||||
|  |         context = SDL_GL_CreateContext(window); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ~SDLGLContext() override { | ||||||
|  |         SDL_GL_DeleteContext(context); | ||||||
|  |         SDL_DestroyWindow(window); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void MakeCurrent() override { | ||||||
|  |         SDL_GL_MakeCurrent(window, context); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void DoneCurrent() override { | ||||||
|  |         SDL_GL_MakeCurrent(window, nullptr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     SDL_Window* window; | ||||||
|  |     SDL_GLContext context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen, bool is_secondary) | ||||||
|  |     : EmuWindow_SDL2{is_secondary} { | ||||||
|  |     // Initialize the window
 | ||||||
|  |     if (Settings::values.use_gles) { | ||||||
|  |         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); | ||||||
|  |         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); | ||||||
|  |         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); | ||||||
|  |     } else { | ||||||
|  |         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); | ||||||
|  |         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); | ||||||
|  |         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | ||||||
|  |     SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); | ||||||
|  |     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); | ||||||
|  |     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | ||||||
|  |     SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | ||||||
|  |     // Enable context sharing for the shared context
 | ||||||
|  |     SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); | ||||||
|  |     // Enable vsync
 | ||||||
|  |     SDL_GL_SetSwapInterval(1); | ||||||
|  |     // Enable debug context
 | ||||||
|  |     if (Settings::values.renderer_debug) { | ||||||
|  |         SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::string window_title = fmt::format("Citra {} | {}-{}", Common::g_build_fullname, | ||||||
|  |                                            Common::g_scm_branch, Common::g_scm_desc); | ||||||
|  |     render_window = | ||||||
|  |         SDL_CreateWindow(window_title.c_str(), | ||||||
|  |                          SDL_WINDOWPOS_UNDEFINED, // x position
 | ||||||
|  |                          SDL_WINDOWPOS_UNDEFINED, // y position
 | ||||||
|  |                          Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight, | ||||||
|  |                          SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); | ||||||
|  | 
 | ||||||
|  |     if (render_window == nullptr) { | ||||||
|  |         LOG_CRITICAL(Frontend, "Failed to create SDL2 window: {}", SDL_GetError()); | ||||||
|  |         exit(1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     dummy_window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, | ||||||
|  |                                     SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); | ||||||
|  | 
 | ||||||
|  |     if (fullscreen) { | ||||||
|  |         Fullscreen(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     window_context = SDL_GL_CreateContext(render_window); | ||||||
|  |     core_context = CreateSharedContext(); | ||||||
|  |     last_saved_context = nullptr; | ||||||
|  | 
 | ||||||
|  |     if (window_context == nullptr) { | ||||||
|  |         LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: {}", SDL_GetError()); | ||||||
|  |         exit(1); | ||||||
|  |     } | ||||||
|  |     if (core_context == nullptr) { | ||||||
|  |         LOG_CRITICAL(Frontend, "Failed to create shared SDL2 GL context: {}", SDL_GetError()); | ||||||
|  |         exit(1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     render_window_id = SDL_GetWindowID(render_window); | ||||||
|  |     auto gl_load_func = Settings::values.use_gles ? gladLoadGLES2Loader : gladLoadGLLoader; | ||||||
|  | 
 | ||||||
|  |     if (!gl_load_func(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { | ||||||
|  |         LOG_CRITICAL(Frontend, "Failed to initialize GL functions: {}", SDL_GetError()); | ||||||
|  |         exit(1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     OnResize(); | ||||||
|  |     OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | ||||||
|  |     SDL_PumpEvents(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { | ||||||
|  |     core_context.reset(); | ||||||
|  |     SDL_DestroyWindow(render_window); | ||||||
|  |     SDL_GL_DeleteContext(window_context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::unique_ptr<Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { | ||||||
|  |     return std::make_unique<SDLGLContext>(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EmuWindow_SDL2_GL::MakeCurrent() { | ||||||
|  |     core_context->MakeCurrent(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EmuWindow_SDL2_GL::DoneCurrent() { | ||||||
|  |     core_context->DoneCurrent(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EmuWindow_SDL2_GL::SaveContext() { | ||||||
|  |     last_saved_context = SDL_GL_GetCurrentContext(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EmuWindow_SDL2_GL::RestoreContext() { | ||||||
|  |     SDL_GL_MakeCurrent(render_window, last_saved_context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EmuWindow_SDL2_GL::Present() { | ||||||
|  |     SDL_GL_MakeCurrent(render_window, window_context); | ||||||
|  |     SDL_GL_SetSwapInterval(1); | ||||||
|  |     while (IsOpen()) { | ||||||
|  |         VideoCore::g_renderer->TryPresent(100, is_secondary); | ||||||
|  |         SDL_GL_SwapWindow(render_window); | ||||||
|  |     } | ||||||
|  |     SDL_GL_MakeCurrent(render_window, nullptr); | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								src/citra/emu_window/emu_window_sdl2_gl.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/citra/emu_window/emu_window_sdl2_gl.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include "citra/emu_window/emu_window_sdl2.h" | ||||||
|  | 
 | ||||||
|  | struct SDL_Window; | ||||||
|  | 
 | ||||||
|  | class EmuWindow_SDL2_GL : public EmuWindow_SDL2 { | ||||||
|  | public: | ||||||
|  |     explicit EmuWindow_SDL2_GL(bool fullscreen, bool is_secondary); | ||||||
|  |     ~EmuWindow_SDL2_GL(); | ||||||
|  | 
 | ||||||
|  |     void Present() override; | ||||||
|  |     std::unique_ptr<GraphicsContext> CreateSharedContext() const override; | ||||||
|  |     void MakeCurrent() override; | ||||||
|  |     void DoneCurrent() override; | ||||||
|  |     void SaveContext() override; | ||||||
|  |     void RestoreContext() override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     using SDL_GLContext = void*; | ||||||
|  | 
 | ||||||
|  |     /// The OpenGL context associated with the window
 | ||||||
|  |     SDL_GLContext window_context; | ||||||
|  | 
 | ||||||
|  |     /// Used by SaveContext and RestoreContext
 | ||||||
|  |     SDL_GLContext last_saved_context; | ||||||
|  | 
 | ||||||
|  |     /// The OpenGL context associated with the core
 | ||||||
|  |     std::unique_ptr<Frontend::GraphicsContext> core_context; | ||||||
|  | }; | ||||||
							
								
								
									
										127
									
								
								src/citra/emu_window/emu_window_sdl2_sw.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/citra/emu_window/emu_window_sdl2_sw.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,127 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <string> | ||||||
|  | #define SDL_MAIN_HANDLED | ||||||
|  | #include <SDL.h> | ||||||
|  | #include <SDL_rect.h> | ||||||
|  | #include "citra/emu_window/emu_window_sdl2_sw.h" | ||||||
|  | #include "common/color.h" | ||||||
|  | #include "common/scm_rev.h" | ||||||
|  | #include "core/frontend/emu_window.h" | ||||||
|  | #include "core/hw/gpu.h" | ||||||
|  | #include "core/memory.h" | ||||||
|  | #include "video_core/video_core.h" | ||||||
|  | 
 | ||||||
|  | class DummyContext : public Frontend::GraphicsContext {}; | ||||||
|  | 
 | ||||||
|  | EmuWindow_SDL2_SW::EmuWindow_SDL2_SW(bool fullscreen, bool is_secondary) | ||||||
|  |     : EmuWindow_SDL2{is_secondary} { | ||||||
|  |     std::string window_title = fmt::format("Citra {} | {}-{}", Common::g_build_fullname, | ||||||
|  |                                            Common::g_scm_branch, Common::g_scm_desc); | ||||||
|  |     render_window = | ||||||
|  |         SDL_CreateWindow(window_title.c_str(), | ||||||
|  |                          SDL_WINDOWPOS_UNDEFINED, // x position
 | ||||||
|  |                          SDL_WINDOWPOS_UNDEFINED, // y position
 | ||||||
|  |                          Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight, | ||||||
|  |                          SDL_WINDOW_SHOWN); | ||||||
|  | 
 | ||||||
|  |     if (render_window == nullptr) { | ||||||
|  |         LOG_CRITICAL(Frontend, "Failed to create SDL2 window: {}", SDL_GetError()); | ||||||
|  |         exit(1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     window_surface = SDL_GetWindowSurface(render_window); | ||||||
|  |     renderer = SDL_CreateSoftwareRenderer(window_surface); | ||||||
|  | 
 | ||||||
|  |     if (fullscreen) { | ||||||
|  |         Fullscreen(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     render_window_id = SDL_GetWindowID(render_window); | ||||||
|  | 
 | ||||||
|  |     OnResize(); | ||||||
|  |     OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | ||||||
|  |     SDL_PumpEvents(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | EmuWindow_SDL2_SW::~EmuWindow_SDL2_SW() { | ||||||
|  |     SDL_DestroyRenderer(renderer); | ||||||
|  |     SDL_DestroyWindow(render_window); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::unique_ptr<Frontend::GraphicsContext> EmuWindow_SDL2_SW::CreateSharedContext() const { | ||||||
|  |     return std::make_unique<DummyContext>(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EmuWindow_SDL2_SW::Present() { | ||||||
|  |     const auto layout{Layout::DefaultFrameLayout( | ||||||
|  |         Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight, false, false)}; | ||||||
|  | 
 | ||||||
|  |     while (IsOpen()) { | ||||||
|  |         SDL_SetRenderDrawColor(renderer, Settings::values.bg_red.GetValue() * 255, | ||||||
|  |                                Settings::values.bg_green.GetValue() * 255, | ||||||
|  |                                Settings::values.bg_blue.GetValue() * 255, 0xFF); | ||||||
|  |         SDL_RenderClear(renderer); | ||||||
|  | 
 | ||||||
|  |         const auto draw_screen = [&](int fb_id) { | ||||||
|  |             const auto dst_rect = fb_id == 0 ? layout.top_screen : layout.bottom_screen; | ||||||
|  |             SDL_Rect sdl_rect{static_cast<int>(dst_rect.left), static_cast<int>(dst_rect.top), | ||||||
|  |                               static_cast<int>(dst_rect.GetWidth()), | ||||||
|  |                               static_cast<int>(dst_rect.GetHeight())}; | ||||||
|  |             SDL_Surface* screen = LoadFramebuffer(fb_id); | ||||||
|  |             SDL_BlitSurface(screen, nullptr, window_surface, &sdl_rect); | ||||||
|  |             SDL_FreeSurface(screen); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         draw_screen(0); | ||||||
|  |         draw_screen(1); | ||||||
|  | 
 | ||||||
|  |         SDL_RenderPresent(renderer); | ||||||
|  |         SDL_UpdateWindowSurface(render_window); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SDL_Surface* EmuWindow_SDL2_SW::LoadFramebuffer(int fb_id) { | ||||||
|  |     const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id]; | ||||||
|  |     const PAddr framebuffer_addr = | ||||||
|  |         framebuffer.active_fb == 0 ? framebuffer.address_left1 : framebuffer.address_left2; | ||||||
|  | 
 | ||||||
|  |     Memory::RasterizerFlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height); | ||||||
|  |     const u8* framebuffer_data = VideoCore::g_memory->GetPhysicalPointer(framebuffer_addr); | ||||||
|  | 
 | ||||||
|  |     const int width = framebuffer.height; | ||||||
|  |     const int height = framebuffer.width; | ||||||
|  |     const int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format); | ||||||
|  | 
 | ||||||
|  |     SDL_Surface* surface = | ||||||
|  |         SDL_CreateRGBSurfaceWithFormat(0, width, height, 0, SDL_PIXELFORMAT_ABGR8888); | ||||||
|  |     SDL_LockSurface(surface); | ||||||
|  |     for (int y = 0; y < height; y++) { | ||||||
|  |         for (int x = 0; x < width; x++) { | ||||||
|  |             const u8* pixel = framebuffer_data + (x * height + height - y) * bpp; | ||||||
|  |             const Common::Vec4 color = [&] { | ||||||
|  |                 switch (framebuffer.color_format) { | ||||||
|  |                 case GPU::Regs::PixelFormat::RGBA8: | ||||||
|  |                     return Common::Color::DecodeRGBA8(pixel); | ||||||
|  |                 case GPU::Regs::PixelFormat::RGB8: | ||||||
|  |                     return Common::Color::DecodeRGB8(pixel); | ||||||
|  |                 case GPU::Regs::PixelFormat::RGB565: | ||||||
|  |                     return Common::Color::DecodeRGB565(pixel); | ||||||
|  |                 case GPU::Regs::PixelFormat::RGB5A1: | ||||||
|  |                     return Common::Color::DecodeRGB5A1(pixel); | ||||||
|  |                 case GPU::Regs::PixelFormat::RGBA4: | ||||||
|  |                     return Common::Color::DecodeRGBA4(pixel); | ||||||
|  |                 } | ||||||
|  |             }(); | ||||||
|  | 
 | ||||||
|  |             u8* dst_pixel = reinterpret_cast<u8*>(surface->pixels) + (y * width + x) * 4; | ||||||
|  |             std::memcpy(dst_pixel, color.AsArray(), sizeof(color)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     SDL_UnlockSurface(surface); | ||||||
|  |     return surface; | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								src/citra/emu_window/emu_window_sdl2_sw.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/citra/emu_window/emu_window_sdl2_sw.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include "citra/emu_window/emu_window_sdl2.h" | ||||||
|  | 
 | ||||||
|  | struct SDL_Renderer; | ||||||
|  | struct SDL_Surface; | ||||||
|  | 
 | ||||||
|  | class EmuWindow_SDL2_SW : public EmuWindow_SDL2 { | ||||||
|  | public: | ||||||
|  |     explicit EmuWindow_SDL2_SW(bool fullscreen, bool is_secondary); | ||||||
|  |     ~EmuWindow_SDL2_SW(); | ||||||
|  | 
 | ||||||
|  |     void Present() override; | ||||||
|  |     std::unique_ptr<GraphicsContext> CreateSharedContext() const override; | ||||||
|  |     void MakeCurrent() override {} | ||||||
|  |     void DoneCurrent() override {} | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     /// Loads a framebuffer to an SDL surface
 | ||||||
|  |     SDL_Surface* LoadFramebuffer(int fb_id); | ||||||
|  | 
 | ||||||
|  |     /// The SDL software renderer
 | ||||||
|  |     SDL_Renderer* renderer; | ||||||
|  | 
 | ||||||
|  |     /// The window surface
 | ||||||
|  |     SDL_Surface* window_surface; | ||||||
|  | }; | ||||||
|  | @ -269,6 +269,10 @@ target_link_libraries(citra-qt PRIVATE audio_core common core input_common netwo | ||||||
| target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Widgets Qt5::Multimedia Qt5::Concurrent) | target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::Widgets Qt5::Multimedia Qt5::Concurrent) | ||||||
| target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | ||||||
| 
 | 
 | ||||||
|  | if (NOT WIN32) | ||||||
|  |     target_include_directories(citra-qt PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) | ||||||
|  | endif() | ||||||
|  | 
 | ||||||
| if (UNIX AND NOT APPLE) | if (UNIX AND NOT APPLE) | ||||||
|     target_link_libraries(citra-qt PRIVATE Qt5::DBus) |     target_link_libraries(citra-qt PRIVATE Qt5::DBus) | ||||||
| endif() | endif() | ||||||
|  | @ -325,6 +329,10 @@ if (MSVC) | ||||||
|     endif() |     endif() | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
|  | if (NOT APPLE) | ||||||
|  |     target_compile_definitions(citra-qt PRIVATE HAS_OPENGL) | ||||||
|  | endif() | ||||||
|  | 
 | ||||||
| if (CITRA_USE_PRECOMPILED_HEADERS) | if (CITRA_USE_PRECOMPILED_HEADERS) | ||||||
|     target_precompile_headers(citra-qt PRIVATE precompiled_headers.h) |     target_precompile_headers(citra-qt PRIVATE precompiled_headers.h) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  | @ -2,31 +2,45 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <glad/glad.h> | ||||||
|  | 
 | ||||||
| #include <QApplication> | #include <QApplication> | ||||||
| #include <QDragEnterEvent> | #include <QDragEnterEvent> | ||||||
| #include <QHBoxLayout> | #include <QHBoxLayout> | ||||||
| #include <QKeyEvent> | #include <QKeyEvent> | ||||||
| #include <QOffscreenSurface> | #include <QMessageBox> | ||||||
| #include <QOpenGLContext> | #include <QPainter> | ||||||
| #include <QOpenGLFunctions> |  | ||||||
| #include <QOpenGLFunctions_4_3_Core> |  | ||||||
| #include <fmt/format.h> | #include <fmt/format.h> | ||||||
| #include "citra_qt/bootmanager.h" | #include "citra_qt/bootmanager.h" | ||||||
| #include "citra_qt/main.h" | #include "citra_qt/main.h" | ||||||
|  | #include "common/color.h" | ||||||
| #include "common/microprofile.h" | #include "common/microprofile.h" | ||||||
| #include "common/scm_rev.h" | #include "common/scm_rev.h" | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
| #include "core/3ds.h" | #include "core/3ds.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/frontend/scope_acquire_context.h" | #include "core/frontend/framebuffer_layout.h" | ||||||
| #include "core/perf_stats.h" | #include "core/perf_stats.h" | ||||||
| #include "input_common/keyboard.h" | #include "input_common/keyboard.h" | ||||||
| #include "input_common/main.h" | #include "input_common/main.h" | ||||||
| #include "input_common/motion_emu.h" | #include "input_common/motion_emu.h" | ||||||
| #include "network/network.h" |  | ||||||
| #include "video_core/renderer_base.h" | #include "video_core/renderer_base.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
|  | #ifdef HAS_OPENGL | ||||||
|  | #include <QOffscreenSurface> | ||||||
|  | #include <QOpenGLContext> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if defined(__APPLE__) | ||||||
|  | #include <objc/message.h> | ||||||
|  | #include <objc/objc.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if !defined(WIN32) | ||||||
|  | #include <qpa/qplatformnativeinterface.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| EmuThread::EmuThread(Frontend::GraphicsContext& core_context) : core_context(core_context) {} | EmuThread::EmuThread(Frontend::GraphicsContext& core_context) : core_context(core_context) {} | ||||||
| 
 | 
 | ||||||
| EmuThread::~EmuThread() = default; | EmuThread::~EmuThread() = default; | ||||||
|  | @ -44,7 +58,7 @@ static GMainWindow* GetMainWindow() { | ||||||
| 
 | 
 | ||||||
| void EmuThread::run() { | void EmuThread::run() { | ||||||
|     MicroProfileOnThreadCreate("EmuThread"); |     MicroProfileOnThreadCreate("EmuThread"); | ||||||
|     Frontend::ScopeAcquireContext scope(core_context); |     const auto scope = core_context.Acquire(); | ||||||
| 
 | 
 | ||||||
|     emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |     emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | ||||||
| 
 | 
 | ||||||
|  | @ -55,6 +69,7 @@ void EmuThread::run() { | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|     emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); |     emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | ||||||
|  |     emit HideLoadingScreen(); | ||||||
| 
 | 
 | ||||||
|     core_context.MakeCurrent(); |     core_context.MakeCurrent(); | ||||||
| 
 | 
 | ||||||
|  | @ -113,90 +128,263 @@ void EmuThread::run() { | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| OpenGLWindow::OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context, | #ifdef HAS_OPENGL | ||||||
|                            bool is_secondary) | class OpenGLSharedContext : public Frontend::GraphicsContext { | ||||||
|     : QWindow(parent), context(std::make_unique<QOpenGLContext>(shared_context->parent())), | public: | ||||||
|       event_handler(event_handler), is_secondary{is_secondary} { |     /// Create the original context that should be shared from
 | ||||||
|  |     explicit OpenGLSharedContext() { | ||||||
|  |         QSurfaceFormat format; | ||||||
| 
 | 
 | ||||||
|     // disable vsync for any shared contexts
 |         format.setVersion(4, 3); | ||||||
|     auto format = shared_context->format(); |         format.setProfile(QSurfaceFormat::CoreProfile); | ||||||
|     format.setSwapInterval(Settings::values.use_vsync_new ? 1 : 0); |  | ||||||
|     this->setFormat(format); |  | ||||||
| 
 | 
 | ||||||
|     context->setShareContext(shared_context); |         if (Settings::values.renderer_debug) { | ||||||
|     context->setScreen(this->screen()); |             format.setOption(QSurfaceFormat::FormatOption::DebugContext); | ||||||
|     context->setFormat(format); |         } | ||||||
|     context->create(); |  | ||||||
| 
 | 
 | ||||||
|     setSurfaceType(QWindow::OpenGLSurface); |         // TODO: expose a setting for buffer value (ie default/single/double/triple)
 | ||||||
|  |         format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | ||||||
|  |         format.setSwapInterval(0); | ||||||
| 
 | 
 | ||||||
|     // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
 |         context = std::make_unique<QOpenGLContext>(); | ||||||
|     // WA_DontShowOnScreen, WA_DeleteOnClose
 |         context->setFormat(format); | ||||||
| } |         if (!context->create()) { | ||||||
|  |             LOG_ERROR(Frontend, "Unable to create main openGL context"); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
| OpenGLWindow::~OpenGLWindow() { |         offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr); | ||||||
|     context->doneCurrent(); |         offscreen_surface->setFormat(format); | ||||||
| } |         offscreen_surface->create(); | ||||||
| 
 |         surface = offscreen_surface.get(); | ||||||
| void OpenGLWindow::Present() { |  | ||||||
|     if (!isExposed()) |  | ||||||
|         return; |  | ||||||
| 
 |  | ||||||
|     context->makeCurrent(this); |  | ||||||
|     if (VideoCore::g_renderer) { |  | ||||||
|         VideoCore::g_renderer->TryPresent(100, is_secondary); |  | ||||||
|     } |     } | ||||||
|     context->swapBuffers(this); |  | ||||||
|     auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>(); |  | ||||||
|     f->glFinish(); |  | ||||||
|     QWindow::requestUpdate(); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| bool OpenGLWindow::event(QEvent* event) { |     /// Create the shared contexts for rendering and presentation
 | ||||||
|     switch (event->type()) { |     explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface) { | ||||||
|     case QEvent::UpdateRequest: | 
 | ||||||
|  |         // disable vsync for any shared contexts
 | ||||||
|  |         auto format = share_context->format(); | ||||||
|  |         format.setSwapInterval(main_surface ? Settings::values.use_vsync_new.GetValue() : 0); | ||||||
|  | 
 | ||||||
|  |         context = std::make_unique<QOpenGLContext>(); | ||||||
|  |         context->setShareContext(share_context); | ||||||
|  |         context->setFormat(format); | ||||||
|  |         if (!context->create()) { | ||||||
|  |             LOG_ERROR(Frontend, "Unable to create shared openGL context"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         surface = main_surface; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ~OpenGLSharedContext() { | ||||||
|  |         context->doneCurrent(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void SwapBuffers() override { | ||||||
|  |         context->swapBuffers(surface); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void MakeCurrent() override { | ||||||
|  |         // We can't track the current state of the underlying context in this wrapper class because
 | ||||||
|  |         // Qt may make the underlying context not current for one reason or another. In particular,
 | ||||||
|  |         // the WebBrowser uses GL, so it seems to conflict if we aren't careful.
 | ||||||
|  |         // Instead of always just making the context current (which does not have any caching to
 | ||||||
|  |         // check if the underlying context is already current) we can check for the current context
 | ||||||
|  |         // in the thread local data by calling `currentContext()` and checking if its ours.
 | ||||||
|  |         if (QOpenGLContext::currentContext() != context.get()) { | ||||||
|  |             context->makeCurrent(surface); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void DoneCurrent() override { | ||||||
|  |         context->doneCurrent(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     QOpenGLContext* GetShareContext() const { | ||||||
|  |         return context.get(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     // Avoid using Qt parent system here since we might move the QObjects to new threads
 | ||||||
|  |     // As a note, this means we should avoid using slots/signals with the objects too
 | ||||||
|  |     std::unique_ptr<QOpenGLContext> context; | ||||||
|  |     std::unique_ptr<QOffscreenSurface> offscreen_surface{}; | ||||||
|  |     QSurface* surface; | ||||||
|  | }; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | class DummyContext : public Frontend::GraphicsContext {}; | ||||||
|  | 
 | ||||||
|  | class RenderWidget : public QWidget { | ||||||
|  | public: | ||||||
|  |     RenderWidget(GRenderWindow* parent) : QWidget(parent) { | ||||||
|  |         setMouseTracking(true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     virtual ~RenderWidget() = default; | ||||||
|  | 
 | ||||||
|  |     virtual void Present() {} | ||||||
|  | 
 | ||||||
|  |     void paintEvent(QPaintEvent* event) override { | ||||||
|         Present(); |         Present(); | ||||||
|         return true; |         update(); | ||||||
|     case QEvent::MouseButtonPress: |  | ||||||
|     case QEvent::MouseButtonRelease: |  | ||||||
|     case QEvent::MouseButtonDblClick: |  | ||||||
|     case QEvent::MouseMove: |  | ||||||
|     case QEvent::KeyPress: |  | ||||||
|     case QEvent::KeyRelease: |  | ||||||
|     case QEvent::FocusIn: |  | ||||||
|     case QEvent::FocusOut: |  | ||||||
|     case QEvent::FocusAboutToChange: |  | ||||||
|     case QEvent::Enter: |  | ||||||
|     case QEvent::Leave: |  | ||||||
|     case QEvent::Wheel: |  | ||||||
|     case QEvent::TabletMove: |  | ||||||
|     case QEvent::TabletPress: |  | ||||||
|     case QEvent::TabletRelease: |  | ||||||
|     case QEvent::TabletEnterProximity: |  | ||||||
|     case QEvent::TabletLeaveProximity: |  | ||||||
|     case QEvent::TouchBegin: |  | ||||||
|     case QEvent::TouchUpdate: |  | ||||||
|     case QEvent::TouchEnd: |  | ||||||
|     case QEvent::InputMethodQuery: |  | ||||||
|     case QEvent::TouchCancel: |  | ||||||
|         return QCoreApplication::sendEvent(event_handler, event); |  | ||||||
|     case QEvent::Drop: |  | ||||||
|         GetMainWindow()->DropAction(static_cast<QDropEvent*>(event)); |  | ||||||
|         return true; |  | ||||||
|     case QEvent::DragEnter: |  | ||||||
|     case QEvent::DragMove: |  | ||||||
|         GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event)); |  | ||||||
|         return true; |  | ||||||
|     default: |  | ||||||
|         return QWindow::event(event); |  | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     std::pair<unsigned, unsigned> GetSize() const { | ||||||
|  |         return std::make_pair(width(), height()); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #ifdef HAS_OPENGL | ||||||
|  | class OpenGLRenderWidget : public RenderWidget { | ||||||
|  | public: | ||||||
|  |     explicit OpenGLRenderWidget(GRenderWindow* parent, bool is_secondary) | ||||||
|  |         : RenderWidget(parent), is_secondary(is_secondary) { | ||||||
|  |         setAttribute(Qt::WA_NativeWindow); | ||||||
|  |         setAttribute(Qt::WA_PaintOnScreen); | ||||||
|  |         windowHandle()->setSurfaceType(QWindow::OpenGLSurface); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void SetContext(std::unique_ptr<Frontend::GraphicsContext>&& context_) { | ||||||
|  |         context = std::move(context_); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void Present() override { | ||||||
|  |         if (!isVisible()) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         if (!Core::System::GetInstance().IsPoweredOn()) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         context->MakeCurrent(); | ||||||
|  |         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||||||
|  |         VideoCore::g_renderer->TryPresent(100, is_secondary); | ||||||
|  |         context->SwapBuffers(); | ||||||
|  |         glFinish(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     QPaintEngine* paintEngine() const override { | ||||||
|  |         return nullptr; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::unique_ptr<Frontend::GraphicsContext> context{}; | ||||||
|  |     bool is_secondary; | ||||||
|  | }; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | struct SoftwareRenderWidget : public RenderWidget { | ||||||
|  |     explicit SoftwareRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {} | ||||||
|  | 
 | ||||||
|  |     void Present() override { | ||||||
|  |         if (!isVisible()) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         if (!Core::System::GetInstance().IsPoweredOn()) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const auto layout{Layout::DefaultFrameLayout(width(), height(), false, false)}; | ||||||
|  |         QPainter painter(this); | ||||||
|  | 
 | ||||||
|  |         const auto draw_screen = [&](int fb_id) { | ||||||
|  |             const auto rect = fb_id == 0 ? layout.top_screen : layout.bottom_screen; | ||||||
|  |             const QImage screen = LoadFramebuffer(fb_id); | ||||||
|  |             painter.drawImage(rect.left, rect.top, screen); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         painter.fillRect(rect(), qRgb(Settings::values.bg_red.GetValue() * 255, | ||||||
|  |                                       Settings::values.bg_green.GetValue() * 255, | ||||||
|  |                                       Settings::values.bg_blue.GetValue() * 255)); | ||||||
|  |         draw_screen(0); | ||||||
|  |         draw_screen(1); | ||||||
|  | 
 | ||||||
|  |         painter.end(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     QImage LoadFramebuffer(int fb_id) { | ||||||
|  |         const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id]; | ||||||
|  |         const PAddr framebuffer_addr = | ||||||
|  |             framebuffer.active_fb == 0 ? framebuffer.address_left1 : framebuffer.address_left2; | ||||||
|  | 
 | ||||||
|  |         Memory::RasterizerFlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height); | ||||||
|  |         const u8* framebuffer_data = VideoCore::g_memory->GetPhysicalPointer(framebuffer_addr); | ||||||
|  | 
 | ||||||
|  |         const int width = framebuffer.height; | ||||||
|  |         const int height = framebuffer.width; | ||||||
|  |         const int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format); | ||||||
|  | 
 | ||||||
|  |         QImage image{width, height, QImage::Format_RGBA8888}; | ||||||
|  |         for (int y = 0; y < height; y++) { | ||||||
|  |             for (int x = 0; x < width; x++) { | ||||||
|  |                 const u8* pixel = framebuffer_data + (x * height + height - y) * bpp; | ||||||
|  |                 const Common::Vec4 color = [&] { | ||||||
|  |                     switch (framebuffer.color_format) { | ||||||
|  |                     case GPU::Regs::PixelFormat::RGBA8: | ||||||
|  |                         return Common::Color::DecodeRGBA8(pixel); | ||||||
|  |                     case GPU::Regs::PixelFormat::RGB8: | ||||||
|  |                         return Common::Color::DecodeRGB8(pixel); | ||||||
|  |                     case GPU::Regs::PixelFormat::RGB565: | ||||||
|  |                         return Common::Color::DecodeRGB565(pixel); | ||||||
|  |                     case GPU::Regs::PixelFormat::RGB5A1: | ||||||
|  |                         return Common::Color::DecodeRGB5A1(pixel); | ||||||
|  |                     case GPU::Regs::PixelFormat::RGBA4: | ||||||
|  |                         return Common::Color::DecodeRGBA4(pixel); | ||||||
|  |                     } | ||||||
|  |                 }(); | ||||||
|  | 
 | ||||||
|  |                 image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return image; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static Frontend::WindowSystemType GetWindowSystemType() { | ||||||
|  |     // Determine WSI type based on Qt platform.
 | ||||||
|  |     const QString platform_name = QGuiApplication::platformName(); | ||||||
|  |     if (platform_name == QStringLiteral("windows")) | ||||||
|  |         return Frontend::WindowSystemType::Windows; | ||||||
|  |     else if (platform_name == QStringLiteral("xcb")) | ||||||
|  |         return Frontend::WindowSystemType::X11; | ||||||
|  |     else if (platform_name == QStringLiteral("wayland")) | ||||||
|  |         return Frontend::WindowSystemType::Wayland; | ||||||
|  |     else if (platform_name == QStringLiteral("cocoa")) | ||||||
|  |         return Frontend::WindowSystemType::MacOS; | ||||||
|  | 
 | ||||||
|  |     LOG_CRITICAL(Frontend, "Unknown Qt platform!"); | ||||||
|  |     return Frontend::WindowSystemType::Windows; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void OpenGLWindow::exposeEvent(QExposeEvent* event) { | static Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) { | ||||||
|     QWindow::requestUpdate(); |     Frontend::EmuWindow::WindowSystemInfo wsi; | ||||||
|     QWindow::exposeEvent(event); |     wsi.type = GetWindowSystemType(); | ||||||
|  | 
 | ||||||
|  |     if (window) { | ||||||
|  | #if defined(WIN32) | ||||||
|  |         // Our Win32 Qt external doesn't have the private API.
 | ||||||
|  |         wsi.render_surface = reinterpret_cast<void*>(window->winId()); | ||||||
|  | #elif defined(__APPLE__) | ||||||
|  |         wsi.render_surface = reinterpret_cast<void* (*)(id, SEL)>(objc_msgSend)( | ||||||
|  |             reinterpret_cast<id>(window->winId()), sel_registerName("layer")); | ||||||
|  | #else | ||||||
|  |         QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); | ||||||
|  |         wsi.display_connection = pni->nativeResourceForWindow("display", window); | ||||||
|  |         if (wsi.type == Frontend::WindowSystemType::Wayland) | ||||||
|  |             wsi.render_surface = pni->nativeResourceForWindow("surface", window); | ||||||
|  |         else | ||||||
|  |             wsi.render_surface = reinterpret_cast<void*>(window->winId()); | ||||||
|  | #endif | ||||||
|  |         wsi.render_surface_scale = static_cast<float>(window->devicePixelRatio()); | ||||||
|  |     } else { | ||||||
|  |         wsi.render_surface = nullptr; | ||||||
|  |         wsi.render_surface_scale = 1.0f; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return wsi; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::shared_ptr<Frontend::GraphicsContext> GRenderWindow::main_context; | ||||||
|  | 
 | ||||||
| GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread, bool is_secondary_) | GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread, bool is_secondary_) | ||||||
|     : QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread) { |     : QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread) { | ||||||
| 
 | 
 | ||||||
|  | @ -218,11 +406,11 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread, bool is_se | ||||||
| GRenderWindow::~GRenderWindow() = default; | GRenderWindow::~GRenderWindow() = default; | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::MakeCurrent() { | void GRenderWindow::MakeCurrent() { | ||||||
|     core_context->MakeCurrent(); |     main_context->MakeCurrent(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::DoneCurrent() { | void GRenderWindow::DoneCurrent() { | ||||||
|     core_context->DoneCurrent(); |     main_context->DoneCurrent(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::PollEvents() { | void GRenderWindow::PollEvents() { | ||||||
|  | @ -295,8 +483,9 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::mousePressEvent(QMouseEvent* event) { | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | ||||||
|     if (event->source() == Qt::MouseEventSynthesizedBySystem) |     if (event->source() == Qt::MouseEventSynthesizedBySystem) { | ||||||
|         return; // touch input is handled in TouchBeginEvent
 |         return; // touch input is handled in TouchBeginEvent
 | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     auto pos = event->pos(); |     auto pos = event->pos(); | ||||||
|     if (event->button() == Qt::LeftButton) { |     if (event->button() == Qt::LeftButton) { | ||||||
|  | @ -309,8 +498,9 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | ||||||
|     if (event->source() == Qt::MouseEventSynthesizedBySystem) |     if (event->source() == Qt::MouseEventSynthesizedBySystem) { | ||||||
|         return; // touch input is handled in TouchUpdateEvent
 |         return; // touch input is handled in TouchUpdateEvent
 | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     auto pos = event->pos(); |     auto pos = event->pos(); | ||||||
|     const auto [x, y] = ScaleTouch(pos); |     const auto [x, y] = ScaleTouch(pos); | ||||||
|  | @ -320,8 +510,9 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | ||||||
|     if (event->source() == Qt::MouseEventSynthesizedBySystem) |     if (event->source() == Qt::MouseEventSynthesizedBySystem) { | ||||||
|         return; // touch input is handled in TouchEndEvent
 |         return; // touch input is handled in TouchEndEvent
 | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if (event->button() == Qt::LeftButton) |     if (event->button() == Qt::LeftButton) | ||||||
|         this->TouchReleased(); |         this->TouchReleased(); | ||||||
|  | @ -393,42 +584,61 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) { | ||||||
|     OnFramebufferSizeChanged(); |     OnFramebufferSizeChanged(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::InitRenderTarget() { | bool GRenderWindow::InitRenderTarget() { | ||||||
|     ReleaseRenderTarget(); |     { | ||||||
|  |         // Create a dummy render widget so that Qt
 | ||||||
|  |         // places the render window at the correct position.
 | ||||||
|  |         const RenderWidget dummy_widget{this}; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     first_frame = false; |     first_frame = false; | ||||||
| 
 | 
 | ||||||
|     GMainWindow* parent = GetMainWindow(); |     const auto graphics_api = Settings::values.graphics_api.GetValue(); | ||||||
|     QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; |     switch (graphics_api) { | ||||||
|     child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext(), |     case Settings::GraphicsAPI::Software: | ||||||
|                                     is_secondary); |         InitializeSoftware(); | ||||||
|     child_window->create(); |         break; | ||||||
|     child_widget = createWindowContainer(child_window, this); |     case Settings::GraphicsAPI::OpenGL: | ||||||
|  |         if (!InitializeOpenGL() || !LoadOpenGL()) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Update the Window System information with the new render target
 | ||||||
|  |     window_info = GetWindowSystemInfo(child_widget->windowHandle()); | ||||||
|  | 
 | ||||||
|     child_widget->resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); |     child_widget->resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); | ||||||
| 
 | 
 | ||||||
|     layout()->addWidget(child_widget); |     layout()->addWidget(child_widget); | ||||||
|  |     // Reset minimum required size to avoid resizing issues on the main window after restarting.
 | ||||||
|  |     setMinimumSize(1, 1); | ||||||
| 
 | 
 | ||||||
|     core_context = CreateSharedContext(); |  | ||||||
|     resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); |     resize(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight); | ||||||
|     OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |     OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | ||||||
|     OnFramebufferSizeChanged(); |     OnFramebufferSizeChanged(); | ||||||
|     BackupGeometry(); |     BackupGeometry(); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::ReleaseRenderTarget() { | void GRenderWindow::ReleaseRenderTarget() { | ||||||
|     if (child_widget) { |     if (child_widget) { | ||||||
|         layout()->removeWidget(child_widget); |         layout()->removeWidget(child_widget); | ||||||
|         delete child_widget; |         child_widget->deleteLater(); | ||||||
|         child_widget = nullptr; |         child_widget = nullptr; | ||||||
|     } |     } | ||||||
|  |     main_context.reset(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { | ||||||
|     if (res_scale == 0) |     if (res_scale == 0) { | ||||||
|         res_scale = VideoCore::GetResolutionScaleFactor(); |         res_scale = VideoCore::GetResolutionScaleFactor(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     const auto layout{Layout::FrameLayoutFromResolutionScale(res_scale, is_secondary)}; |     const auto layout{Layout::FrameLayoutFromResolutionScale(res_scale, is_secondary)}; | ||||||
|     screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); |     screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); | ||||||
|     VideoCore::RequestScreenshot( |     VideoCore::g_renderer->RequestScreenshot( | ||||||
|         screenshot_image.bits(), |         screenshot_image.bits(), | ||||||
|         [this, screenshot_path] { |         [this, screenshot_path] { | ||||||
|             const std::string std_screenshot_path = screenshot_path.toStdString(); |             const std::string std_screenshot_path = screenshot_path.toStdString(); | ||||||
|  | @ -445,6 +655,59 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal | ||||||
|     setMinimumSize(minimal_size.first, minimal_size.second); |     setMinimumSize(minimal_size.first, minimal_size.second); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool GRenderWindow::InitializeOpenGL() { | ||||||
|  | #ifdef HAS_OPENGL | ||||||
|  |     // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
 | ||||||
|  |     // WA_DontShowOnScreen, WA_DeleteOnClose
 | ||||||
|  |     auto child = new OpenGLRenderWidget(this, is_secondary); | ||||||
|  |     child_widget = child; | ||||||
|  |     child_widget->windowHandle()->create(); | ||||||
|  | 
 | ||||||
|  |     if (!main_context) { | ||||||
|  |         main_context = std::make_shared<OpenGLSharedContext>(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto child_context = CreateSharedContext(); | ||||||
|  |     child->SetContext(std::move(child_context)); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | #else | ||||||
|  |     QMessageBox::warning(this, tr("OpenGL not available!"), | ||||||
|  |                          tr("Citra has not been compiled with OpenGL support.")); | ||||||
|  |     return false; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GRenderWindow::InitializeSoftware() { | ||||||
|  |     child_widget = new SoftwareRenderWidget(this); | ||||||
|  |     main_context = std::make_unique<DummyContext>(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GRenderWindow::LoadOpenGL() { | ||||||
|  |     auto context = CreateSharedContext(); | ||||||
|  |     auto scope = context->Acquire(); | ||||||
|  |     if (!gladLoadGL()) { | ||||||
|  |         QMessageBox::warning( | ||||||
|  |             this, tr("Error while initializing OpenGL!"), | ||||||
|  |             tr("Your GPU may not support OpenGL, or you do not have the latest graphics driver.")); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const QString renderer = | ||||||
|  |         QString::fromUtf8(reinterpret_cast<const char*>(glGetString(GL_RENDERER))); | ||||||
|  | 
 | ||||||
|  |     if (!GLAD_GL_VERSION_4_3) { | ||||||
|  |         LOG_ERROR(Frontend, "GPU does not support OpenGL 4.3: {}", renderer.toStdString()); | ||||||
|  |         QMessageBox::warning(this, tr("Error while initializing OpenGL 4.3!"), | ||||||
|  |                              tr("Your GPU may not support OpenGL 4.3, or you do not have the " | ||||||
|  |                                 "latest graphics driver.<br><br>GL Renderer:<br>%1") | ||||||
|  |                                  .arg(renderer)); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { | ||||||
|     this->emu_thread = emu_thread; |     this->emu_thread = emu_thread; | ||||||
| } | } | ||||||
|  | @ -458,29 +721,15 @@ void GRenderWindow::showEvent(QShowEvent* event) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | std::unique_ptr<Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | ||||||
|     return std::make_unique<GLContext>(QOpenGLContext::globalShareContext()); | #ifdef HAS_OPENGL | ||||||
| } |     const auto graphics_api = Settings::values.graphics_api.GetValue(); | ||||||
| 
 |     if (graphics_api == Settings::GraphicsAPI::OpenGL) { | ||||||
| GLContext::GLContext(QOpenGLContext* shared_context) |         auto gl_context = static_cast<OpenGLSharedContext*>(main_context.get()); | ||||||
|     : context(std::make_unique<QOpenGLContext>(shared_context->parent())), |         // Bind the shared contexts to the main surface in case the backend wants to take over
 | ||||||
|       surface(std::make_unique<QOffscreenSurface>(nullptr)) { |         // presentation
 | ||||||
| 
 |         return std::make_unique<OpenGLSharedContext>(gl_context->GetShareContext(), | ||||||
|     // disable vsync for any shared contexts
 |                                                      child_widget->windowHandle()); | ||||||
|     auto format = shared_context->format(); |     } | ||||||
|     format.setSwapInterval(0); | #endif | ||||||
| 
 |     return std::make_unique<DummyContext>(); | ||||||
|     context->setShareContext(shared_context); |  | ||||||
|     context->setFormat(format); |  | ||||||
|     context->create(); |  | ||||||
|     surface->setParent(shared_context->parent()); |  | ||||||
|     surface->setFormat(format); |  | ||||||
|     surface->create(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void GLContext::MakeCurrent() { |  | ||||||
|     context->makeCurrent(surface.get()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void GLContext::DoneCurrent() { |  | ||||||
|     context->doneCurrent(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -27,19 +27,6 @@ namespace VideoCore { | ||||||
| enum class LoadCallbackStage; | enum class LoadCallbackStage; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class GLContext : public Frontend::GraphicsContext { |  | ||||||
| public: |  | ||||||
|     explicit GLContext(QOpenGLContext* shared_context); |  | ||||||
| 
 |  | ||||||
|     void MakeCurrent() override; |  | ||||||
| 
 |  | ||||||
|     void DoneCurrent() override; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     std::unique_ptr<QOpenGLContext> context; |  | ||||||
|     std::unique_ptr<QOffscreenSurface> surface; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class EmuThread final : public QThread { | class EmuThread final : public QThread { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
|  | @ -126,26 +113,6 @@ signals: | ||||||
|     void HideLoadingScreen(); |     void HideLoadingScreen(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class OpenGLWindow : public QWindow { |  | ||||||
|     Q_OBJECT |  | ||||||
| public: |  | ||||||
|     explicit OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context, |  | ||||||
|                           bool is_secondary = false); |  | ||||||
| 
 |  | ||||||
|     ~OpenGLWindow(); |  | ||||||
| 
 |  | ||||||
|     void Present(); |  | ||||||
| 
 |  | ||||||
| protected: |  | ||||||
|     bool event(QEvent* event) override; |  | ||||||
|     void exposeEvent(QExposeEvent* event) override; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     std::unique_ptr<QOpenGLContext> context; |  | ||||||
|     QWidget* event_handler; |  | ||||||
|     bool is_secondary; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class GRenderWindow : public QWidget, public Frontend::EmuWindow { | class GRenderWindow : public QWidget, public Frontend::EmuWindow { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
|  | @ -185,13 +152,15 @@ public: | ||||||
|         return has_focus; |         return has_focus; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void InitRenderTarget(); |     bool InitRenderTarget(); | ||||||
| 
 | 
 | ||||||
|     /// Destroy the previous run's child_widget which should also destroy the child_window
 |     /// Destroy the previous run's child_widget which should also destroy the child_window
 | ||||||
|     void ReleaseRenderTarget(); |     void ReleaseRenderTarget(); | ||||||
| 
 | 
 | ||||||
|     void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); |     void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); | ||||||
| 
 | 
 | ||||||
|  |     std::pair<u32, u32> ScaleTouch(const QPointF pos) const; | ||||||
|  | 
 | ||||||
| public slots: | public slots: | ||||||
| 
 | 
 | ||||||
|     void OnEmulationStarting(EmuThread* emu_thread); |     void OnEmulationStarting(EmuThread* emu_thread); | ||||||
|  | @ -211,29 +180,28 @@ signals: | ||||||
|     void MouseActivity(); |     void MouseActivity(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     std::pair<u32, u32> ScaleTouch(QPointF pos) const; |  | ||||||
|     void TouchBeginEvent(const QTouchEvent* event); |     void TouchBeginEvent(const QTouchEvent* event); | ||||||
|     void TouchUpdateEvent(const QTouchEvent* event); |     void TouchUpdateEvent(const QTouchEvent* event); | ||||||
|     void TouchEndEvent(); |     void TouchEndEvent(); | ||||||
| 
 | 
 | ||||||
|     void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; |     void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<GraphicsContext> core_context; |     bool InitializeOpenGL(); | ||||||
|  |     void InitializeSoftware(); | ||||||
|  |     bool LoadOpenGL(); | ||||||
| 
 | 
 | ||||||
|     QByteArray geometry; |  | ||||||
| 
 |  | ||||||
|     /// Native window handle that backs this presentation widget
 |  | ||||||
|     QWindow* child_window = nullptr; |  | ||||||
| 
 |  | ||||||
|     /// In order to embed the window into GRenderWindow, you need to use createWindowContainer to
 |  | ||||||
|     /// put the child_window into a widget then add it to the layout. This child_widget can be
 |  | ||||||
|     /// parented to GRenderWindow and use Qt's lifetime system
 |  | ||||||
|     QWidget* child_widget = nullptr; |     QWidget* child_widget = nullptr; | ||||||
| 
 | 
 | ||||||
|     EmuThread* emu_thread; |     EmuThread* emu_thread; | ||||||
| 
 | 
 | ||||||
|  |     /// Main context that will be shared with all other contexts that are requested.
 | ||||||
|  |     /// If this is used in a shared context setting, then this should not be used directly, but
 | ||||||
|  |     /// should instead be shared from
 | ||||||
|  |     static std::shared_ptr<Frontend::GraphicsContext> main_context; | ||||||
|  | 
 | ||||||
|     /// Temporary storage of the screenshot taken
 |     /// Temporary storage of the screenshot taken
 | ||||||
|     QImage screenshot_image; |     QImage screenshot_image; | ||||||
|  |     QByteArray geometry; | ||||||
|     bool first_frame = false; |     bool first_frame = false; | ||||||
|     bool has_focus = false; |     bool has_focus = false; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -482,6 +482,7 @@ void Config::ReadDebuggingValues() { | ||||||
|         qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); |         qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); | ||||||
|     ReadBasicSetting(Settings::values.use_gdbstub); |     ReadBasicSetting(Settings::values.use_gdbstub); | ||||||
|     ReadBasicSetting(Settings::values.gdbstub_port); |     ReadBasicSetting(Settings::values.gdbstub_port); | ||||||
|  |     ReadBasicSetting(Settings::values.renderer_debug); | ||||||
| 
 | 
 | ||||||
|     qt_config->beginGroup(QStringLiteral("LLE")); |     qt_config->beginGroup(QStringLiteral("LLE")); | ||||||
|     for (const auto& service_module : Service::service_module_map) { |     for (const auto& service_module : Service::service_module_map) { | ||||||
|  | @ -625,7 +626,7 @@ void Config::ReadPathValues() { | ||||||
| void Config::ReadRendererValues() { | void Config::ReadRendererValues() { | ||||||
|     qt_config->beginGroup(QStringLiteral("Renderer")); |     qt_config->beginGroup(QStringLiteral("Renderer")); | ||||||
| 
 | 
 | ||||||
|     ReadGlobalSetting(Settings::values.use_hw_renderer); |     ReadGlobalSetting(Settings::values.graphics_api); | ||||||
|     ReadGlobalSetting(Settings::values.use_hw_shader); |     ReadGlobalSetting(Settings::values.use_hw_shader); | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
|     // Hardware shader is broken on macos with Intel GPUs thanks to poor drivers.
 |     // Hardware shader is broken on macos with Intel GPUs thanks to poor drivers.
 | ||||||
|  | @ -992,6 +993,7 @@ void Config::SaveDebuggingValues() { | ||||||
|     qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); |     qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); | ||||||
|     WriteBasicSetting(Settings::values.use_gdbstub); |     WriteBasicSetting(Settings::values.use_gdbstub); | ||||||
|     WriteBasicSetting(Settings::values.gdbstub_port); |     WriteBasicSetting(Settings::values.gdbstub_port); | ||||||
|  |     WriteBasicSetting(Settings::values.renderer_debug); | ||||||
| 
 | 
 | ||||||
|     qt_config->beginGroup(QStringLiteral("LLE")); |     qt_config->beginGroup(QStringLiteral("LLE")); | ||||||
|     for (const auto& service_module : Settings::values.lle_modules) { |     for (const auto& service_module : Settings::values.lle_modules) { | ||||||
|  | @ -1103,7 +1105,7 @@ void Config::SavePathValues() { | ||||||
| void Config::SaveRendererValues() { | void Config::SaveRendererValues() { | ||||||
|     qt_config->beginGroup(QStringLiteral("Renderer")); |     qt_config->beginGroup(QStringLiteral("Renderer")); | ||||||
| 
 | 
 | ||||||
|     WriteGlobalSetting(Settings::values.use_hw_renderer); |     WriteGlobalSetting(Settings::values.graphics_api); | ||||||
|     WriteGlobalSetting(Settings::values.use_hw_shader); |     WriteGlobalSetting(Settings::values.use_hw_shader); | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
|     // Hardware shader is broken on macos thanks to poor drivers.
 |     // Hardware shader is broken on macos thanks to poor drivers.
 | ||||||
|  |  | ||||||
|  | @ -83,6 +83,21 @@ template <> | ||||||
| void SetPerGameSetting(QComboBox* combobox, | void SetPerGameSetting(QComboBox* combobox, | ||||||
|                        const Settings::SwitchableSetting<std::string>* setting); |                        const Settings::SwitchableSetting<std::string>* setting); | ||||||
| 
 | 
 | ||||||
|  | /// Given an index of a combobox setting extracts the setting taking into
 | ||||||
|  | /// account per-game status
 | ||||||
|  | template <typename Type, bool ranged> | ||||||
|  | Type GetComboboxSetting(int index, const Settings::SwitchableSetting<Type, ranged>* setting) { | ||||||
|  |     if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { | ||||||
|  |         return static_cast<Type>(index); | ||||||
|  |     } else if (!Settings::IsConfiguringGlobal()) { | ||||||
|  |         if (index == 0) { | ||||||
|  |             return setting->GetValue(); | ||||||
|  |         } else { | ||||||
|  |             return static_cast<Type>(index - ConfigurationShared::USE_GLOBAL_OFFSET); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// Given a Qt widget sets the background color to indicate whether the setting
 | /// Given a Qt widget sets the background color to indicate whether the setting
 | ||||||
| /// is per-game overriden (highlighted) or global (non-highlighted)
 | /// is per-game overriden (highlighted) or global (non-highlighted)
 | ||||||
| void SetHighlight(QWidget* widget, bool highlighted); | void SetHighlight(QWidget* widget, bool highlighted); | ||||||
|  |  | ||||||
|  | @ -37,6 +37,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) | ||||||
| 
 | 
 | ||||||
|     const bool is_powered_on = Core::System::GetInstance().IsPoweredOn(); |     const bool is_powered_on = Core::System::GetInstance().IsPoweredOn(); | ||||||
|     ui->toggle_cpu_jit->setEnabled(!is_powered_on); |     ui->toggle_cpu_jit->setEnabled(!is_powered_on); | ||||||
|  |     ui->toggle_renderer_debug->setEnabled(!is_powered_on); | ||||||
| 
 | 
 | ||||||
|     // Set a minimum width for the label to prevent the slider from changing size.
 |     // Set a minimum width for the label to prevent the slider from changing size.
 | ||||||
|     // This scales across DPIs. (This value should be enough for "xxx%")
 |     // This scales across DPIs. (This value should be enough for "xxx%")
 | ||||||
|  | @ -62,6 +63,7 @@ void ConfigureDebug::SetConfiguration() { | ||||||
|     ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); |     ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); | ||||||
|     ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue())); |     ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue())); | ||||||
|     ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue()); |     ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue()); | ||||||
|  |     ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue()); | ||||||
| 
 | 
 | ||||||
|     if (!Settings::IsConfiguringGlobal()) { |     if (!Settings::IsConfiguringGlobal()) { | ||||||
|         if (Settings::values.cpu_clock_percentage.UsingGlobal()) { |         if (Settings::values.cpu_clock_percentage.UsingGlobal()) { | ||||||
|  | @ -91,6 +93,7 @@ void ConfigureDebug::ApplyConfiguration() { | ||||||
|     filter.ParseFilterString(Settings::values.log_filter.GetValue()); |     filter.ParseFilterString(Settings::values.log_filter.GetValue()); | ||||||
|     Log::SetGlobalFilter(filter); |     Log::SetGlobalFilter(filter); | ||||||
|     Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); |     Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); | ||||||
|  |     Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked(); | ||||||
| 
 | 
 | ||||||
|     ConfigurationShared::ApplyPerGameSetting( |     ConfigurationShared::ApplyPerGameSetting( | ||||||
|         &Settings::values.cpu_clock_percentage, ui->clock_speed_combo, |         &Settings::values.cpu_clock_percentage, ui->clock_speed_combo, | ||||||
|  |  | ||||||
|  | @ -23,5 +23,6 @@ public: | ||||||
|     void SetConfiguration(); |     void SetConfiguration(); | ||||||
|     void SetupPerGameUI(); |     void SetupPerGameUI(); | ||||||
| 
 | 
 | ||||||
|  | private: | ||||||
|     std::unique_ptr<Ui::ConfigureDebug> ui; |     std::unique_ptr<Ui::ConfigureDebug> ui; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -6,8 +6,8 @@ | ||||||
|    <rect> |    <rect> | ||||||
|     <x>0</x> |     <x>0</x> | ||||||
|     <y>0</y> |     <y>0</y> | ||||||
|     <width>443</width> |     <width>523</width> | ||||||
|     <height>358</height> |     <height>447</height> | ||||||
|    </rect> |    </rect> | ||||||
|   </property> |   </property> | ||||||
|   <property name="windowTitle"> |   <property name="windowTitle"> | ||||||
|  | @ -112,12 +112,34 @@ | ||||||
|       <string>CPU</string> |       <string>CPU</string> | ||||||
|      </property> |      </property> | ||||||
|      <layout class="QGridLayout" name="gridLayout_2"> |      <layout class="QGridLayout" name="gridLayout_2"> | ||||||
|  |       <item row="2" column="0"> | ||||||
|  |        <widget class="QCheckBox" name="toggle_cpu_jit"> | ||||||
|  |         <property name="toolTip"> | ||||||
|  |          <string><html><head/><body><p>Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes</p></body></html></string> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string>Enable CPU JIT</string> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|       <item row="1" column="0"> |       <item row="1" column="0"> | ||||||
|        <widget class="QWidget" name="clock_speed_widget" native="true"> |        <widget class="QWidget" name="clock_speed_widget" native="true"> | ||||||
|         <layout class="QHBoxLayout" name="clock_speed_layout"> |         <layout class="QHBoxLayout" name="clock_speed_layout"> | ||||||
|          <property name="spacing"> |          <property name="spacing"> | ||||||
|           <number>7</number> |           <number>7</number> | ||||||
|          </property> |          </property> | ||||||
|  |          <property name="leftMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="topMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="rightMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="bottomMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|          <item> |          <item> | ||||||
|           <widget class="QComboBox" name="clock_speed_combo"> |           <widget class="QComboBox" name="clock_speed_combo"> | ||||||
|            <item> |            <item> | ||||||
|  | @ -180,13 +202,10 @@ | ||||||
|         </layout> |         </layout> | ||||||
|        </widget> |        </widget> | ||||||
|       </item> |       </item> | ||||||
|       <item row="2" column="0"> |       <item row="3" column="0"> | ||||||
|        <widget class="QCheckBox" name="toggle_cpu_jit"> |        <widget class="QCheckBox" name="toggle_renderer_debug"> | ||||||
|         <property name="toolTip"> |  | ||||||
|          <string><html><head/><body><p>Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes</p></body></html></string> |  | ||||||
|         </property> |  | ||||||
|         <property name="text"> |         <property name="text"> | ||||||
|          <string>Enable CPU JIT</string> |          <string>Enable debug renderer</string> | ||||||
|         </property> |         </property> | ||||||
|        </widget> |        </widget> | ||||||
|       </item> |       </item> | ||||||
|  |  | ||||||
|  | @ -22,7 +22,9 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent) | ||||||
| 
 | 
 | ||||||
|     ui->layout_group->setEnabled(!Settings::values.custom_layout); |     ui->layout_group->setEnabled(!Settings::values.custom_layout); | ||||||
| 
 | 
 | ||||||
|     ui->resolution_factor_combobox->setEnabled(Settings::values.use_hw_renderer.GetValue()); |     const auto graphics_api = Settings::values.graphics_api.GetValue(); | ||||||
|  |     const bool res_scale_enabled = graphics_api != Settings::GraphicsAPI::Software; | ||||||
|  |     ui->resolution_factor_combobox->setEnabled(res_scale_enabled); | ||||||
| 
 | 
 | ||||||
|     connect(ui->render_3d_combobox, |     connect(ui->render_3d_combobox, | ||||||
|             static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, |             static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | ||||||
|  |  | ||||||
|  | @ -16,22 +16,20 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) | ||||||
|     : QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) { |     : QWidget(parent), ui(std::make_unique<Ui::ConfigureGraphics>()) { | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
| 
 | 
 | ||||||
|     SetupPerGameUI(); |  | ||||||
|     SetConfiguration(); |  | ||||||
| 
 |  | ||||||
|     ui->hw_renderer_group->setEnabled(ui->hw_renderer_group->isEnabled() && |  | ||||||
|                                       ui->toggle_hw_renderer->isChecked()); |  | ||||||
|     ui->toggle_vsync_new->setEnabled(!Core::System::GetInstance().IsPoweredOn()); |     ui->toggle_vsync_new->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | ||||||
|  |     // Set the index to -1 to ensure the below lambda is called with setCurrentIndex
 | ||||||
|  |     ui->graphics_api_combo->setCurrentIndex(-1); | ||||||
| 
 | 
 | ||||||
|     connect(ui->toggle_hw_renderer, &QCheckBox::toggled, this, [this] { |     connect(ui->graphics_api_combo, qOverload<int>(&QComboBox::currentIndexChanged), this, | ||||||
|         const bool checked = ui->toggle_hw_renderer->isChecked(); |             [this](int index) { | ||||||
|         ui->hw_renderer_group->setEnabled(checked); |                 const auto graphics_api = | ||||||
|         ui->toggle_disk_shader_cache->setEnabled(checked && ui->toggle_hw_shader->isChecked()); |                     ConfigurationShared::GetComboboxSetting(index, &Settings::values.graphics_api); | ||||||
|     }); |                 const bool is_software = graphics_api == Settings::GraphicsAPI::Software; | ||||||
| 
 | 
 | ||||||
|     ui->hw_shader_group->setEnabled(ui->toggle_hw_shader->isChecked()); |                 ui->hw_renderer_group->setEnabled(!is_software); | ||||||
|     ui->toggle_disk_shader_cache->setEnabled(ui->toggle_hw_renderer->isChecked() && |                 ui->toggle_disk_shader_cache->setEnabled(!is_software && | ||||||
|                                              ui->toggle_hw_shader->isChecked()); |                                                          ui->toggle_hw_shader->isChecked()); | ||||||
|  |             }); | ||||||
| 
 | 
 | ||||||
|     connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] { |     connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] { | ||||||
|         const bool checked = ui->toggle_hw_shader->isChecked(); |         const bool checked = ui->toggle_hw_shader->isChecked(); | ||||||
|  | @ -60,12 +58,24 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) | ||||||
|     // TODO(B3N30): Hide this for macs with none Intel GPUs, too.
 |     // TODO(B3N30): Hide this for macs with none Intel GPUs, too.
 | ||||||
|     ui->toggle_separable_shader->setVisible(false); |     ui->toggle_separable_shader->setVisible(false); | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
|  |     SetupPerGameUI(); | ||||||
|  |     SetConfiguration(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ConfigureGraphics::~ConfigureGraphics() = default; | ConfigureGraphics::~ConfigureGraphics() = default; | ||||||
| 
 | 
 | ||||||
| void ConfigureGraphics::SetConfiguration() { | void ConfigureGraphics::SetConfiguration() { | ||||||
|     ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer.GetValue()); |     if (!Settings::IsConfiguringGlobal()) { | ||||||
|  |         ConfigurationShared::SetHighlight(ui->graphics_api_group, | ||||||
|  |                                           !Settings::values.graphics_api.UsingGlobal()); | ||||||
|  |         ConfigurationShared::SetPerGameSetting(ui->graphics_api_combo, | ||||||
|  |                                                &Settings::values.graphics_api); | ||||||
|  |     } else { | ||||||
|  |         ui->graphics_api_combo->setCurrentIndex( | ||||||
|  |             static_cast<int>(Settings::values.graphics_api.GetValue())); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue()); |     ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue()); | ||||||
|     ui->toggle_separable_shader->setChecked(Settings::values.separable_shader.GetValue()); |     ui->toggle_separable_shader->setChecked(Settings::values.separable_shader.GetValue()); | ||||||
|     ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue()); |     ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue()); | ||||||
|  | @ -78,8 +88,8 @@ void ConfigureGraphics::SetConfiguration() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ConfigureGraphics::ApplyConfiguration() { | void ConfigureGraphics::ApplyConfiguration() { | ||||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_renderer, |     ConfigurationShared::ApplyPerGameSetting(&Settings::values.graphics_api, | ||||||
|                                              ui->toggle_hw_renderer, use_hw_renderer); |                                              ui->graphics_api_combo); | ||||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_shader, ui->toggle_hw_shader, |     ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_shader, ui->toggle_hw_shader, | ||||||
|                                              use_hw_shader); |                                              use_hw_shader); | ||||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.separable_shader, |     ConfigurationShared::ApplyPerGameSetting(&Settings::values.separable_shader, | ||||||
|  | @ -103,7 +113,7 @@ void ConfigureGraphics::RetranslateUI() { | ||||||
| void ConfigureGraphics::SetupPerGameUI() { | void ConfigureGraphics::SetupPerGameUI() { | ||||||
|     // Block the global settings if a game is currently running that overrides them
 |     // Block the global settings if a game is currently running that overrides them
 | ||||||
|     if (Settings::IsConfiguringGlobal()) { |     if (Settings::IsConfiguringGlobal()) { | ||||||
|         ui->toggle_hw_renderer->setEnabled(Settings::values.use_hw_renderer.UsingGlobal()); |         ui->graphics_api_group->setEnabled(Settings::values.graphics_api.UsingGlobal()); | ||||||
|         ui->toggle_hw_shader->setEnabled(Settings::values.use_hw_shader.UsingGlobal()); |         ui->toggle_hw_shader->setEnabled(Settings::values.use_hw_shader.UsingGlobal()); | ||||||
|         ui->toggle_separable_shader->setEnabled(Settings::values.separable_shader.UsingGlobal()); |         ui->toggle_separable_shader->setEnabled(Settings::values.separable_shader.UsingGlobal()); | ||||||
|         ui->toggle_accurate_mul->setEnabled(Settings::values.shaders_accurate_mul.UsingGlobal()); |         ui->toggle_accurate_mul->setEnabled(Settings::values.shaders_accurate_mul.UsingGlobal()); | ||||||
|  | @ -115,8 +125,10 @@ void ConfigureGraphics::SetupPerGameUI() { | ||||||
| 
 | 
 | ||||||
|     ui->toggle_shader_jit->setVisible(false); |     ui->toggle_shader_jit->setVisible(false); | ||||||
| 
 | 
 | ||||||
|     ConfigurationShared::SetColoredTristate(ui->toggle_hw_renderer, |     ConfigurationShared::SetColoredComboBox( | ||||||
|                                             Settings::values.use_hw_renderer, use_hw_renderer); |         ui->graphics_api_combo, ui->graphics_api_group, | ||||||
|  |         static_cast<u32>(Settings::values.graphics_api.GetValue(true))); | ||||||
|  | 
 | ||||||
|     ConfigurationShared::SetColoredTristate(ui->toggle_hw_shader, Settings::values.use_hw_shader, |     ConfigurationShared::SetColoredTristate(ui->toggle_hw_shader, Settings::values.use_hw_shader, | ||||||
|                                             use_hw_shader); |                                             use_hw_shader); | ||||||
|     ConfigurationShared::SetColoredTristate(ui->toggle_separable_shader, |     ConfigurationShared::SetColoredTristate(ui->toggle_separable_shader, | ||||||
|  |  | ||||||
|  | @ -28,9 +28,9 @@ public: | ||||||
| 
 | 
 | ||||||
|     void UpdateBackgroundColorButton(const QColor& color); |     void UpdateBackgroundColorButton(const QColor& color); | ||||||
| 
 | 
 | ||||||
|  | private: | ||||||
|     void SetupPerGameUI(); |     void SetupPerGameUI(); | ||||||
| 
 | 
 | ||||||
|     ConfigurationShared::CheckState use_hw_renderer; |  | ||||||
|     ConfigurationShared::CheckState use_hw_shader; |     ConfigurationShared::CheckState use_hw_shader; | ||||||
|     ConfigurationShared::CheckState separable_shader; |     ConfigurationShared::CheckState separable_shader; | ||||||
|     ConfigurationShared::CheckState shaders_accurate_mul; |     ConfigurationShared::CheckState shaders_accurate_mul; | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ | ||||||
|     <x>0</x> |     <x>0</x> | ||||||
|     <y>0</y> |     <y>0</y> | ||||||
|     <width>400</width> |     <width>400</width> | ||||||
|     <height>430</height> |     <height>443</height> | ||||||
|    </rect> |    </rect> | ||||||
|   </property> |   </property> | ||||||
|   <property name="minimumSize"> |   <property name="minimumSize"> | ||||||
|  | @ -20,27 +20,65 @@ | ||||||
|    <string>Form</string> |    <string>Form</string> | ||||||
|   </property> |   </property> | ||||||
|   <layout class="QVBoxLayout" name="verticalLayout"> |   <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QGroupBox" name="apiBox"> | ||||||
|  |      <property name="title"> | ||||||
|  |       <string>API Settings</string> | ||||||
|  |      </property> | ||||||
|  |      <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QWidget" name="graphics_api_group" native="true"> | ||||||
|  |         <layout class="QHBoxLayout" name="graphics_api_group_2"> | ||||||
|  |          <property name="leftMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="topMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="rightMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="bottomMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <item> | ||||||
|  |           <widget class="QLabel" name="graphics_api_label"> | ||||||
|  |            <property name="text"> | ||||||
|  |             <string>Graphics API</string> | ||||||
|  |            </property> | ||||||
|  |           </widget> | ||||||
|  |          </item> | ||||||
|  |          <item> | ||||||
|  |           <widget class="QComboBox" name="graphics_api_combo"> | ||||||
|  |            <item> | ||||||
|  |             <property name="text"> | ||||||
|  |              <string>Software</string> | ||||||
|  |             </property> | ||||||
|  |            </item> | ||||||
|  |            <item> | ||||||
|  |             <property name="text"> | ||||||
|  |              <string>OpenGL</string> | ||||||
|  |             </property> | ||||||
|  |            </item> | ||||||
|  |           </widget> | ||||||
|  |          </item> | ||||||
|  |         </layout> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|    <item> |    <item> | ||||||
|     <widget class="QGroupBox" name="rendererBox"> |     <widget class="QGroupBox" name="rendererBox"> | ||||||
|      <property name="title"> |      <property name="title"> | ||||||
|       <string>Renderer</string> |       <string>Renderer</string> | ||||||
|      </property> |      </property> | ||||||
|      <layout class="QVBoxLayout" name="verticalLayout_6"> |      <layout class="QVBoxLayout" name="verticalLayout_6"> | ||||||
|       <item> |  | ||||||
|        <widget class="QCheckBox" name="toggle_hw_renderer"> |  | ||||||
|         <property name="toolTip"> |  | ||||||
|          <string><html><head/><body><p>Use OpenGL to accelerate rendering.</p><p>Disable to debug graphics-related problem.</p></body></html></string> |  | ||||||
|         </property> |  | ||||||
|         <property name="text"> |  | ||||||
|          <string>Enable Hardware Renderer</string> |  | ||||||
|         </property> |  | ||||||
|        </widget> |  | ||||||
|       </item> |  | ||||||
|       <item> |       <item> | ||||||
|        <widget class="QWidget" name="hw_renderer_group" native="true"> |        <widget class="QWidget" name="hw_renderer_group" native="true"> | ||||||
|         <layout class="QVBoxLayout" name="verticalLayout_5"> |         <layout class="QVBoxLayout" name="verticalLayout_5"> | ||||||
|          <property name="leftMargin"> |          <property name="leftMargin"> | ||||||
|           <number>16</number> |           <number>0</number> | ||||||
|          </property> |          </property> | ||||||
|          <property name="topMargin"> |          <property name="topMargin"> | ||||||
|           <number>0</number> |           <number>0</number> | ||||||
|  | @ -157,8 +195,6 @@ | ||||||
|   </layout> |   </layout> | ||||||
|  </widget> |  </widget> | ||||||
|  <tabstops> |  <tabstops> | ||||||
|   <tabstop>toggle_hw_renderer</tabstop> |  | ||||||
|   <tabstop>toggle_hw_shader</tabstop> |  | ||||||
|   <tabstop>toggle_separable_shader</tabstop> |   <tabstop>toggle_separable_shader</tabstop> | ||||||
|   <tabstop>toggle_accurate_mul</tabstop> |   <tabstop>toggle_accurate_mul</tabstop> | ||||||
|   <tabstop>toggle_shader_jit</tabstop> |   <tabstop>toggle_shader_jit</tabstop> | ||||||
|  |  | ||||||
|  | @ -12,7 +12,6 @@ | ||||||
| #include <QFutureWatcher> | #include <QFutureWatcher> | ||||||
| #include <QLabel> | #include <QLabel> | ||||||
| #include <QMessageBox> | #include <QMessageBox> | ||||||
| #include <QOpenGLFunctions_4_3_Core> |  | ||||||
| #include <QSysInfo> | #include <QSysInfo> | ||||||
| #include <QtConcurrent/QtConcurrentRun> | #include <QtConcurrent/QtConcurrentRun> | ||||||
| #include <QtGui> | #include <QtGui> | ||||||
|  | @ -88,7 +87,6 @@ | ||||||
| #include "core/file_sys/archive_extsavedata.h" | #include "core/file_sys/archive_extsavedata.h" | ||||||
| #include "core/file_sys/archive_source_sd_savedata.h" | #include "core/file_sys/archive_source_sd_savedata.h" | ||||||
| #include "core/frontend/applets/default_applets.h" | #include "core/frontend/applets/default_applets.h" | ||||||
| #include "core/frontend/scope_acquire_context.h" |  | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
| #include "core/hle/service/cfg/cfg.h" | #include "core/hle/service/cfg/cfg.h" | ||||||
| #include "core/hle/service/fs/archive.h" | #include "core/hle/service/fs/archive.h" | ||||||
|  | @ -1026,16 +1024,7 @@ bool GMainWindow::LoadROM(const QString& filename) { | ||||||
|     render_window->InitRenderTarget(); |     render_window->InitRenderTarget(); | ||||||
|     secondary_window->InitRenderTarget(); |     secondary_window->InitRenderTarget(); | ||||||
| 
 | 
 | ||||||
|     Frontend::ScopeAcquireContext scope(*render_window); |     const auto scope = render_window->Acquire(); | ||||||
| 
 |  | ||||||
|     const QString below_gl43_title = tr("OpenGL 4.3 Unsupported"); |  | ||||||
|     const QString below_gl43_message = tr("Your GPU may not support OpenGL 4.3, or you do not " |  | ||||||
|                                           "have the latest graphics driver."); |  | ||||||
| 
 |  | ||||||
|     if (!QOpenGLContext::globalShareContext()->versionFunctions<QOpenGLFunctions_4_3_Core>()) { |  | ||||||
|         QMessageBox::critical(this, below_gl43_title, below_gl43_message); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     Core::System& system{Core::System::GetInstance()}; |     Core::System& system{Core::System::GetInstance()}; | ||||||
| 
 | 
 | ||||||
|  | @ -1091,28 +1080,6 @@ bool GMainWindow::LoadROM(const QString& filename) { | ||||||
|                                   tr("GBA Virtual Console ROMs are not supported by Citra.")); |                                   tr("GBA Virtual Console ROMs are not supported by Citra.")); | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case Core::System::ResultStatus::ErrorVideoCore: |  | ||||||
|             QMessageBox::critical( |  | ||||||
|                 this, tr("Video Core Error"), |  | ||||||
|                 tr("An error has occurred. Please <a " |  | ||||||
|                    "href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>see " |  | ||||||
|                    "the " |  | ||||||
|                    "log</a> for more details. " |  | ||||||
|                    "Ensure that you have the latest graphics drivers for your GPU.")); |  | ||||||
|             break; |  | ||||||
| 
 |  | ||||||
|         case Core::System::ResultStatus::ErrorVideoCore_ErrorGenericDrivers: |  | ||||||
|             QMessageBox::critical( |  | ||||||
|                 this, tr("Video Core Error"), |  | ||||||
|                 tr("You are running default Windows drivers " |  | ||||||
|                    "for your GPU. You need to install the " |  | ||||||
|                    "proper drivers for your graphics card from the manufacturer's website.")); |  | ||||||
|             break; |  | ||||||
| 
 |  | ||||||
|         case Core::System::ResultStatus::ErrorVideoCore_ErrorBelowGL43: |  | ||||||
|             QMessageBox::critical(this, below_gl43_title, below_gl43_message); |  | ||||||
|             break; |  | ||||||
| 
 |  | ||||||
|         default: |         default: | ||||||
|             QMessageBox::critical( |             QMessageBox::critical( | ||||||
|                 this, tr("Error while loading ROM!"), |                 this, tr("Error while loading ROM!"), | ||||||
|  | @ -2786,14 +2753,6 @@ int main(int argc, char* argv[]) { | ||||||
|     QCoreApplication::setOrganizationName(QStringLiteral("Citra team")); |     QCoreApplication::setOrganizationName(QStringLiteral("Citra team")); | ||||||
|     QCoreApplication::setApplicationName(QStringLiteral("Citra")); |     QCoreApplication::setApplicationName(QStringLiteral("Citra")); | ||||||
| 
 | 
 | ||||||
|     QSurfaceFormat format; |  | ||||||
|     format.setVersion(4, 3); |  | ||||||
|     format.setProfile(QSurfaceFormat::CoreProfile); |  | ||||||
|     format.setSwapInterval(0); |  | ||||||
|     // TODO: expose a setting for buffer value (ie default/single/double/triple)
 |  | ||||||
|     format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); |  | ||||||
|     QSurfaceFormat::setDefaultFormat(format); |  | ||||||
| 
 |  | ||||||
|     SetHighDPIAttributes(); |     SetHighDPIAttributes(); | ||||||
| 
 | 
 | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
|  |  | ||||||
|  | @ -55,6 +55,60 @@ __declspec(dllimport) void __stdcall DebugBreak(void); | ||||||
| 
 | 
 | ||||||
| #endif // _MSC_VER
 | #endif // _MSC_VER
 | ||||||
| 
 | 
 | ||||||
|  | #define DECLARE_ENUM_FLAG_OPERATORS(type)                                                          \ | ||||||
|  |     [[nodiscard]] constexpr type operator|(type a, type b) noexcept {                              \ | ||||||
|  |         using T = std::underlying_type_t<type>;                                                    \ | ||||||
|  |         return static_cast<type>(static_cast<T>(a) | static_cast<T>(b));                           \ | ||||||
|  |     }                                                                                              \ | ||||||
|  |     [[nodiscard]] constexpr type operator&(type a, type b) noexcept {                              \ | ||||||
|  |         using T = std::underlying_type_t<type>;                                                    \ | ||||||
|  |         return static_cast<type>(static_cast<T>(a) & static_cast<T>(b));                           \ | ||||||
|  |     }                                                                                              \ | ||||||
|  |     [[nodiscard]] constexpr type operator^(type a, type b) noexcept {                              \ | ||||||
|  |         using T = std::underlying_type_t<type>;                                                    \ | ||||||
|  |         return static_cast<type>(static_cast<T>(a) ^ static_cast<T>(b));                           \ | ||||||
|  |     }                                                                                              \ | ||||||
|  |     [[nodiscard]] constexpr type operator<<(type a, type b) noexcept {                             \ | ||||||
|  |         using T = std::underlying_type_t<type>;                                                    \ | ||||||
|  |         return static_cast<type>(static_cast<T>(a) << static_cast<T>(b));                          \ | ||||||
|  |     }                                                                                              \ | ||||||
|  |     [[nodiscard]] constexpr type operator>>(type a, type b) noexcept {                             \ | ||||||
|  |         using T = std::underlying_type_t<type>;                                                    \ | ||||||
|  |         return static_cast<type>(static_cast<T>(a) >> static_cast<T>(b));                          \ | ||||||
|  |     }                                                                                              \ | ||||||
|  |     constexpr type& operator|=(type& a, type b) noexcept {                                         \ | ||||||
|  |         a = a | b;                                                                                 \ | ||||||
|  |         return a;                                                                                  \ | ||||||
|  |     }                                                                                              \ | ||||||
|  |     constexpr type& operator&=(type& a, type b) noexcept {                                         \ | ||||||
|  |         a = a & b;                                                                                 \ | ||||||
|  |         return a;                                                                                  \ | ||||||
|  |     }                                                                                              \ | ||||||
|  |     constexpr type& operator^=(type& a, type b) noexcept {                                         \ | ||||||
|  |         a = a ^ b;                                                                                 \ | ||||||
|  |         return a;                                                                                  \ | ||||||
|  |     }                                                                                              \ | ||||||
|  |     constexpr type& operator<<=(type& a, type b) noexcept {                                        \ | ||||||
|  |         a = a << b;                                                                                \ | ||||||
|  |         return a;                                                                                  \ | ||||||
|  |     }                                                                                              \ | ||||||
|  |     constexpr type& operator>>=(type& a, type b) noexcept {                                        \ | ||||||
|  |         a = a >> b;                                                                                \ | ||||||
|  |         return a;                                                                                  \ | ||||||
|  |     }                                                                                              \ | ||||||
|  |     [[nodiscard]] constexpr type operator~(type key) noexcept {                                    \ | ||||||
|  |         using T = std::underlying_type_t<type>;                                                    \ | ||||||
|  |         return static_cast<type>(~static_cast<T>(key));                                            \ | ||||||
|  |     }                                                                                              \ | ||||||
|  |     [[nodiscard]] constexpr bool True(type key) noexcept {                                         \ | ||||||
|  |         using T = std::underlying_type_t<type>;                                                    \ | ||||||
|  |         return static_cast<T>(key) != 0;                                                           \ | ||||||
|  |     }                                                                                              \ | ||||||
|  |     [[nodiscard]] constexpr bool False(type key) noexcept {                                        \ | ||||||
|  |         using T = std::underlying_type_t<type>;                                                    \ | ||||||
|  |         return static_cast<T>(key) == 0;                                                           \ | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| // Generic function to get last error message.
 | // Generic function to get last error message.
 | ||||||
| // Call directly after the command or use the error num.
 | // Call directly after the command or use the error num.
 | ||||||
| // This function might change the error code.
 | // This function might change the error code.
 | ||||||
|  |  | ||||||
|  | @ -20,6 +20,8 @@ | ||||||
| 
 | 
 | ||||||
| namespace Settings { | namespace Settings { | ||||||
| 
 | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
| std::string_view GetAudioEmulationName(AudioEmulation emulation) { | std::string_view GetAudioEmulationName(AudioEmulation emulation) { | ||||||
|     switch (emulation) { |     switch (emulation) { | ||||||
|     case AudioEmulation::HLE: |     case AudioEmulation::HLE: | ||||||
|  | @ -31,6 +33,17 @@ std::string_view GetAudioEmulationName(AudioEmulation emulation) { | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | std::string_view GetGraphicsAPIName(GraphicsAPI api) { | ||||||
|  |     switch (api) { | ||||||
|  |     case GraphicsAPI::Software: | ||||||
|  |         return "Software"; | ||||||
|  |     case GraphicsAPI::OpenGL: | ||||||
|  |         return "OpenGL"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // Anonymous namespace
 | ||||||
|  | 
 | ||||||
| Values values = {}; | Values values = {}; | ||||||
| static bool configuring_global = true; | static bool configuring_global = true; | ||||||
| 
 | 
 | ||||||
|  | @ -38,7 +51,6 @@ void Apply() { | ||||||
|     GDBStub::SetServerPort(values.gdbstub_port.GetValue()); |     GDBStub::SetServerPort(values.gdbstub_port.GetValue()); | ||||||
|     GDBStub::ToggleServer(values.use_gdbstub.GetValue()); |     GDBStub::ToggleServer(values.use_gdbstub.GetValue()); | ||||||
| 
 | 
 | ||||||
|     VideoCore::g_hw_renderer_enabled = values.use_hw_renderer.GetValue(); |  | ||||||
|     VideoCore::g_shader_jit_enabled = values.use_shader_jit.GetValue(); |     VideoCore::g_shader_jit_enabled = values.use_shader_jit.GetValue(); | ||||||
|     VideoCore::g_hw_shader_enabled = values.use_hw_shader.GetValue(); |     VideoCore::g_hw_shader_enabled = values.use_hw_shader.GetValue(); | ||||||
|     VideoCore::g_separable_shader_enabled = values.separable_shader.GetValue(); |     VideoCore::g_separable_shader_enabled = values.separable_shader.GetValue(); | ||||||
|  | @ -101,7 +113,7 @@ void LogSettings() { | ||||||
|     log_setting("Core_UseCpuJit", values.use_cpu_jit.GetValue()); |     log_setting("Core_UseCpuJit", values.use_cpu_jit.GetValue()); | ||||||
|     log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage.GetValue()); |     log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage.GetValue()); | ||||||
|     log_setting("Renderer_UseGLES", values.use_gles.GetValue()); |     log_setting("Renderer_UseGLES", values.use_gles.GetValue()); | ||||||
|     log_setting("Renderer_UseHwRenderer", values.use_hw_renderer.GetValue()); |     log_setting("Renderer_GraphicsAPI", GetGraphicsAPIName(values.graphics_api.GetValue())); | ||||||
|     log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue()); |     log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue()); | ||||||
|     log_setting("Renderer_SeparableShader", values.separable_shader.GetValue()); |     log_setting("Renderer_SeparableShader", values.separable_shader.GetValue()); | ||||||
|     log_setting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul.GetValue()); |     log_setting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul.GetValue()); | ||||||
|  | @ -186,7 +198,7 @@ void RestoreGlobalState(bool is_powered_on) { | ||||||
|     values.is_new_3ds.SetGlobal(true); |     values.is_new_3ds.SetGlobal(true); | ||||||
| 
 | 
 | ||||||
|     // Renderer
 |     // Renderer
 | ||||||
|     values.use_hw_renderer.SetGlobal(true); |     values.graphics_api.SetGlobal(true); | ||||||
|     values.use_hw_shader.SetGlobal(true); |     values.use_hw_shader.SetGlobal(true); | ||||||
|     values.separable_shader.SetGlobal(true); |     values.separable_shader.SetGlobal(true); | ||||||
|     values.use_disk_shader_cache.SetGlobal(true); |     values.use_disk_shader_cache.SetGlobal(true); | ||||||
|  |  | ||||||
|  | @ -15,6 +15,11 @@ | ||||||
| 
 | 
 | ||||||
| namespace Settings { | namespace Settings { | ||||||
| 
 | 
 | ||||||
|  | enum class GraphicsAPI { | ||||||
|  |     Software = 0, | ||||||
|  |     OpenGL = 1, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| enum class InitClock : u32 { | enum class InitClock : u32 { | ||||||
|     SystemTime = 0, |     SystemTime = 0, | ||||||
|     FixedTime = 1, |     FixedTime = 1, | ||||||
|  | @ -415,8 +420,9 @@ struct Values { | ||||||
|     Setting<bool> allow_plugin_loader{true, "allow_plugin_loader"}; |     Setting<bool> allow_plugin_loader{true, "allow_plugin_loader"}; | ||||||
| 
 | 
 | ||||||
|     // Renderer
 |     // Renderer
 | ||||||
|  |     SwitchableSetting<GraphicsAPI> graphics_api{GraphicsAPI::OpenGL, "graphics_api"}; | ||||||
|     Setting<bool> use_gles{false, "use_gles"}; |     Setting<bool> use_gles{false, "use_gles"}; | ||||||
|     SwitchableSetting<bool> use_hw_renderer{true, "use_hw_renderer"}; |     Setting<bool> renderer_debug{false, "renderer_debug"}; | ||||||
|     SwitchableSetting<bool> use_hw_shader{true, "use_hw_shader"}; |     SwitchableSetting<bool> use_hw_shader{true, "use_hw_shader"}; | ||||||
|     SwitchableSetting<bool> separable_shader{false, "use_separable_shader"}; |     SwitchableSetting<bool> separable_shader{false, "use_separable_shader"}; | ||||||
|     SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; |     SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; | ||||||
|  |  | ||||||
|  | @ -113,8 +113,6 @@ add_library(core STATIC | ||||||
|     frontend/input.h |     frontend/input.h | ||||||
|     frontend/mic.cpp |     frontend/mic.cpp | ||||||
|     frontend/mic.h |     frontend/mic.h | ||||||
|     frontend/scope_acquire_context.cpp |  | ||||||
|     frontend/scope_acquire_context.h |  | ||||||
|     gdbstub/gdbstub.cpp |     gdbstub/gdbstub.cpp | ||||||
|     gdbstub/gdbstub.h |     gdbstub/gdbstub.h | ||||||
|     hle/applets/applet.cpp |     hle/applets/applet.cpp | ||||||
|  |  | ||||||
|  | @ -431,17 +431,7 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, | ||||||
|     video_dumper = std::make_unique<VideoDumper::NullBackend>(); |     video_dumper = std::make_unique<VideoDumper::NullBackend>(); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|     VideoCore::ResultStatus result = VideoCore::Init(emu_window, secondary_window, *memory); |     VideoCore::Init(emu_window, secondary_window, *this); | ||||||
|     if (result != VideoCore::ResultStatus::Success) { |  | ||||||
|         switch (result) { |  | ||||||
|         case VideoCore::ResultStatus::ErrorGenericDrivers: |  | ||||||
|             return ResultStatus::ErrorVideoCore_ErrorGenericDrivers; |  | ||||||
|         case VideoCore::ResultStatus::ErrorBelowGL43: |  | ||||||
|             return ResultStatus::ErrorVideoCore_ErrorBelowGL43; |  | ||||||
|         default: |  | ||||||
|             return ResultStatus::ErrorVideoCore; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Core, "Initialized OK"); |     LOG_DEBUG(Core, "Initialized OK"); | ||||||
| 
 | 
 | ||||||
|  | @ -450,7 +440,7 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, | ||||||
|     return ResultStatus::Success; |     return ResultStatus::Success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RendererBase& System::Renderer() { | VideoCore::RendererBase& System::Renderer() { | ||||||
|     return *VideoCore::g_renderer; |     return *VideoCore::g_renderer; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -57,7 +57,9 @@ namespace VideoDumper { | ||||||
| class Backend; | class Backend; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | namespace VideoCore { | ||||||
| class RendererBase; | class RendererBase; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| namespace Core { | namespace Core { | ||||||
| 
 | 
 | ||||||
|  | @ -87,14 +89,9 @@ public: | ||||||
|         ErrorLoader_ErrorGbaTitle, ///< Error loading the specified application as it is GBA Virtual
 |         ErrorLoader_ErrorGbaTitle, ///< Error loading the specified application as it is GBA Virtual
 | ||||||
|                                    ///< Console
 |                                    ///< Console
 | ||||||
|         ErrorSystemFiles,          ///< Error in finding system files
 |         ErrorSystemFiles,          ///< Error in finding system files
 | ||||||
|         ErrorVideoCore,            ///< Error in the video core
 |         ErrorSavestate,            ///< Error saving or loading
 | ||||||
|         ErrorVideoCore_ErrorGenericDrivers, ///< Error in the video core due to the user having
 |         ShutdownRequested,         ///< Emulated program requested a system shutdown
 | ||||||
|                                             /// generic drivers installed
 |         ErrorUnknown               ///< Any other error
 | ||||||
|         ErrorVideoCore_ErrorBelowGL43,      ///< Error in the video core due to the user not having
 |  | ||||||
|                                             /// OpenGL 4.3 or higher
 |  | ||||||
|         ErrorSavestate,                     ///< Error saving or loading
 |  | ||||||
|         ShutdownRequested,                  ///< Emulated program requested a system shutdown
 |  | ||||||
|         ErrorUnknown                        ///< Any other error
 |  | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     ~System(); |     ~System(); | ||||||
|  | @ -210,7 +207,7 @@ public: | ||||||
|         return *dsp_core; |         return *dsp_core; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] RendererBase& Renderer(); |     [[nodiscard]] VideoCore::RendererBase& Renderer(); | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Gets a reference to the service manager. |      * Gets a reference to the service manager. | ||||||
|  |  | ||||||
|  | @ -14,6 +14,17 @@ | ||||||
| 
 | 
 | ||||||
| namespace Frontend { | namespace Frontend { | ||||||
| 
 | 
 | ||||||
|  | /// Information for the Graphics Backends signifying what type of screen pointer is in
 | ||||||
|  | /// WindowInformation
 | ||||||
|  | enum class WindowSystemType : u8 { | ||||||
|  |     Headless, | ||||||
|  |     Android, | ||||||
|  |     Windows, | ||||||
|  |     MacOS, | ||||||
|  |     X11, | ||||||
|  |     Wayland, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| struct Frame; | struct Frame; | ||||||
| /**
 | /**
 | ||||||
|  * For smooth Vsync rendering, we want to always present the latest frame that the core generates, |  * For smooth Vsync rendering, we want to always present the latest frame that the core generates, | ||||||
|  | @ -62,11 +73,33 @@ class GraphicsContext { | ||||||
| public: | public: | ||||||
|     virtual ~GraphicsContext(); |     virtual ~GraphicsContext(); | ||||||
| 
 | 
 | ||||||
|  |     /// Inform the driver to swap the front/back buffers and present the current image
 | ||||||
|  |     virtual void SwapBuffers(){}; | ||||||
|  | 
 | ||||||
|     /// Makes the graphics context current for the caller thread
 |     /// Makes the graphics context current for the caller thread
 | ||||||
|     virtual void MakeCurrent() = 0; |     virtual void MakeCurrent(){}; | ||||||
| 
 | 
 | ||||||
|     /// Releases (dunno if this is the "right" word) the context from the caller thread
 |     /// Releases (dunno if this is the "right" word) the context from the caller thread
 | ||||||
|     virtual void DoneCurrent() = 0; |     virtual void DoneCurrent(){}; | ||||||
|  | 
 | ||||||
|  |     class Scoped { | ||||||
|  |     public: | ||||||
|  |         explicit Scoped(GraphicsContext& context_) : context(context_) { | ||||||
|  |             context.MakeCurrent(); | ||||||
|  |         } | ||||||
|  |         ~Scoped() { | ||||||
|  |             context.DoneCurrent(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     private: | ||||||
|  |         GraphicsContext& context; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
 | ||||||
|  |     /// ends
 | ||||||
|  |     [[nodiscard]] Scoped Acquire() { | ||||||
|  |         return Scoped{*this}; | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -100,6 +133,23 @@ public: | ||||||
|             Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight}; |             Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight}; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     /// Data describing host window system information
 | ||||||
|  |     struct WindowSystemInfo { | ||||||
|  |         // Window system type. Determines which GL context or Vulkan WSI is used.
 | ||||||
|  |         WindowSystemType type = WindowSystemType::Headless; | ||||||
|  | 
 | ||||||
|  |         // Connection to a display server. This is used on X11 and Wayland platforms.
 | ||||||
|  |         void* display_connection = nullptr; | ||||||
|  | 
 | ||||||
|  |         // Render surface. This is a pointer to the native window handle, which depends
 | ||||||
|  |         // on the platform. e.g. HWND for Windows, Window for X11. If the surface is
 | ||||||
|  |         // set to nullptr, the video backend will run in headless mode.
 | ||||||
|  |         void* render_surface = nullptr; | ||||||
|  | 
 | ||||||
|  |         // Scale of the render surface. For hidpi systems, this will be >1.
 | ||||||
|  |         float render_surface_scale = 1.0f; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     /// Polls window events
 |     /// Polls window events
 | ||||||
|     virtual void PollEvents() = 0; |     virtual void PollEvents() = 0; | ||||||
| 
 | 
 | ||||||
|  | @ -163,6 +213,13 @@ public: | ||||||
|         config = val; |         config = val; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Returns system information about the drawing area. | ||||||
|  |      */ | ||||||
|  |     const WindowSystemInfo& GetWindowInfo() const { | ||||||
|  |         return window_info; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Gets the framebuffer layout (width, height, and screen regions) |      * Gets the framebuffer layout (width, height, and screen regions) | ||||||
|      * @note This method is thread-safe |      * @note This method is thread-safe | ||||||
|  | @ -211,6 +268,7 @@ protected: | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool is_secondary{}; |     bool is_secondary{}; | ||||||
|  |     WindowSystemInfo window_info; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     /**
 |     /**
 | ||||||
|  |  | ||||||
|  | @ -1,17 +0,0 @@ | ||||||
| // Copyright 2019 yuzu Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #include "core/frontend/emu_window.h" |  | ||||||
| #include "core/frontend/scope_acquire_context.h" |  | ||||||
| 
 |  | ||||||
| namespace Frontend { |  | ||||||
| 
 |  | ||||||
| ScopeAcquireContext::ScopeAcquireContext(Frontend::GraphicsContext& context) : context{context} { |  | ||||||
|     context.MakeCurrent(); |  | ||||||
| } |  | ||||||
| ScopeAcquireContext::~ScopeAcquireContext() { |  | ||||||
|     context.DoneCurrent(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } // namespace Frontend
 |  | ||||||
|  | @ -1,23 +0,0 @@ | ||||||
| // Copyright 2019 yuzu Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" |  | ||||||
| 
 |  | ||||||
| namespace Frontend { |  | ||||||
| 
 |  | ||||||
| class GraphicsContext; |  | ||||||
| 
 |  | ||||||
| /// Helper class to acquire/release window context within a given scope
 |  | ||||||
| class ScopeAcquireContext : NonCopyable { |  | ||||||
| public: |  | ||||||
|     explicit ScopeAcquireContext(Frontend::GraphicsContext& context); |  | ||||||
|     ~ScopeAcquireContext(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     Frontend::GraphicsContext& context; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| } // namespace Frontend
 |  | ||||||
|  | @ -133,8 +133,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { | ||||||
|              Settings::values.resolution_factor.GetValue()); |              Settings::values.resolution_factor.GetValue()); | ||||||
|     AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", |     AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", | ||||||
|              Settings::values.frame_limit.GetValue()); |              Settings::values.frame_limit.GetValue()); | ||||||
|     AddField(Telemetry::FieldType::UserConfig, "Renderer_UseHwRenderer", |     AddField(Telemetry::FieldType::UserConfig, "Renderer_Backend", | ||||||
|              Settings::values.use_hw_renderer.GetValue()); |              static_cast<int>(Settings::values.graphics_api.GetValue())); | ||||||
|     AddField(Telemetry::FieldType::UserConfig, "Renderer_UseHwShader", |     AddField(Telemetry::FieldType::UserConfig, "Renderer_UseHwShader", | ||||||
|              Settings::values.use_hw_shader.GetValue()); |              Settings::values.use_hw_shader.GetValue()); | ||||||
|     AddField(Telemetry::FieldType::UserConfig, "Renderer_ShadersAccurateMul", |     AddField(Telemetry::FieldType::UserConfig, "Renderer_ShadersAccurateMul", | ||||||
|  |  | ||||||
|  | @ -13,6 +13,8 @@ add_library(video_core STATIC | ||||||
|     precompiled_headers.h |     precompiled_headers.h | ||||||
|     primitive_assembly.cpp |     primitive_assembly.cpp | ||||||
|     primitive_assembly.h |     primitive_assembly.h | ||||||
|  |     rasterizer_accelerated.cpp | ||||||
|  |     rasterizer_accelerated.h | ||||||
|     rasterizer_interface.h |     rasterizer_interface.h | ||||||
|     regs.cpp |     regs.cpp | ||||||
|     regs.h |     regs.h | ||||||
|  | @ -39,6 +41,8 @@ add_library(video_core STATIC | ||||||
|     rasterizer_cache/texture_runtime.h |     rasterizer_cache/texture_runtime.h | ||||||
|     renderer_opengl/frame_dumper_opengl.cpp |     renderer_opengl/frame_dumper_opengl.cpp | ||||||
|     renderer_opengl/frame_dumper_opengl.h |     renderer_opengl/frame_dumper_opengl.h | ||||||
|  |     renderer_opengl/gl_driver.cpp | ||||||
|  |     renderer_opengl/gl_driver.h | ||||||
|     renderer_opengl/gl_rasterizer.cpp |     renderer_opengl/gl_rasterizer.cpp | ||||||
|     renderer_opengl/gl_rasterizer.h |     renderer_opengl/gl_rasterizer.h | ||||||
|     renderer_opengl/gl_resource_manager.cpp |     renderer_opengl/gl_resource_manager.cpp | ||||||
|  | @ -82,6 +86,22 @@ add_library(video_core STATIC | ||||||
|     #temporary, move these back in alphabetical order before merging |     #temporary, move these back in alphabetical order before merging | ||||||
|     renderer_opengl/gl_format_reinterpreter.cpp |     renderer_opengl/gl_format_reinterpreter.cpp | ||||||
|     renderer_opengl/gl_format_reinterpreter.h |     renderer_opengl/gl_format_reinterpreter.h | ||||||
|  |     renderer_software/rasterizer.cpp | ||||||
|  |     renderer_software/rasterizer.h | ||||||
|  |     renderer_software/renderer_software.cpp | ||||||
|  |     renderer_software/renderer_software.h | ||||||
|  |     renderer_software/sw_clipper.cpp | ||||||
|  |     renderer_software/sw_clipper.h | ||||||
|  |     renderer_software/sw_framebuffer.cpp | ||||||
|  |     renderer_software/sw_framebuffer.h | ||||||
|  |     renderer_software/sw_lighting.cpp | ||||||
|  |     renderer_software/sw_lighting.h | ||||||
|  |     renderer_software/sw_proctex.cpp | ||||||
|  |     renderer_software/sw_proctex.h | ||||||
|  |     renderer_software/sw_rasterizer.cpp | ||||||
|  |     renderer_software/sw_rasterizer.h | ||||||
|  |     renderer_software/sw_texturing.cpp | ||||||
|  |     renderer_software/sw_texturing.h | ||||||
|     shader/debug_data.h |     shader/debug_data.h | ||||||
|     shader/shader.cpp |     shader/shader.cpp | ||||||
|     shader/shader.h |     shader/shader.h | ||||||
|  | @ -91,20 +111,8 @@ 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 | ||||||
|     swrasterizer/clipper.cpp |     shader/shader_uniforms.cpp | ||||||
|     swrasterizer/clipper.h |     shader/shader_uniforms.h | ||||||
|     swrasterizer/framebuffer.cpp |  | ||||||
|     swrasterizer/framebuffer.h |  | ||||||
|     swrasterizer/lighting.cpp |  | ||||||
|     swrasterizer/lighting.h |  | ||||||
|     swrasterizer/proctex.cpp |  | ||||||
|     swrasterizer/proctex.h |  | ||||||
|     swrasterizer/rasterizer.cpp |  | ||||||
|     swrasterizer/rasterizer.h |  | ||||||
|     swrasterizer/swrasterizer.cpp |  | ||||||
|     swrasterizer/swrasterizer.h |  | ||||||
|     swrasterizer/texturing.cpp |  | ||||||
|     swrasterizer/texturing.h |  | ||||||
|     texture/etc1.cpp |     texture/etc1.cpp | ||||||
|     texture/etc1.h |     texture/etc1.h | ||||||
|     texture/texture_decode.cpp |     texture/texture_decode.cpp | ||||||
|  |  | ||||||
							
								
								
									
										832
									
								
								src/video_core/rasterizer_accelerated.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										832
									
								
								src/video_core/rasterizer_accelerated.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,832 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <limits> | ||||||
|  | #include "common/alignment.h" | ||||||
|  | #include "core/memory.h" | ||||||
|  | #include "video_core/pica_state.h" | ||||||
|  | #include "video_core/rasterizer_accelerated.h" | ||||||
|  | 
 | ||||||
|  | namespace VideoCore { | ||||||
|  | 
 | ||||||
|  | static Common::Vec4f ColorRGBA8(const u32 color) { | ||||||
|  |     const auto rgba = | ||||||
|  |         Common::Vec4u{color >> 0 & 0xFF, color >> 8 & 0xFF, color >> 16 & 0xFF, color >> 24 & 0xFF}; | ||||||
|  |     return rgba / 255.0f; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static Common::Vec3f LightColor(const Pica::LightingRegs::LightColor& color) { | ||||||
|  |     return Common::Vec3u{color.r, color.g, color.b} / 255.0f; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RasterizerAccelerated::HardwareVertex::HardwareVertex(const Pica::Shader::OutputVertex& v, | ||||||
|  |                                                       bool flip_quaternion) { | ||||||
|  |     position[0] = v.pos.x.ToFloat32(); | ||||||
|  |     position[1] = v.pos.y.ToFloat32(); | ||||||
|  |     position[2] = v.pos.z.ToFloat32(); | ||||||
|  |     position[3] = v.pos.w.ToFloat32(); | ||||||
|  |     color[0] = v.color.x.ToFloat32(); | ||||||
|  |     color[1] = v.color.y.ToFloat32(); | ||||||
|  |     color[2] = v.color.z.ToFloat32(); | ||||||
|  |     color[3] = v.color.w.ToFloat32(); | ||||||
|  |     tex_coord0[0] = v.tc0.x.ToFloat32(); | ||||||
|  |     tex_coord0[1] = v.tc0.y.ToFloat32(); | ||||||
|  |     tex_coord1[0] = v.tc1.x.ToFloat32(); | ||||||
|  |     tex_coord1[1] = v.tc1.y.ToFloat32(); | ||||||
|  |     tex_coord2[0] = v.tc2.x.ToFloat32(); | ||||||
|  |     tex_coord2[1] = v.tc2.y.ToFloat32(); | ||||||
|  |     tex_coord0_w = v.tc0_w.ToFloat32(); | ||||||
|  |     normquat[0] = v.quat.x.ToFloat32(); | ||||||
|  |     normquat[1] = v.quat.y.ToFloat32(); | ||||||
|  |     normquat[2] = v.quat.z.ToFloat32(); | ||||||
|  |     normquat[3] = v.quat.w.ToFloat32(); | ||||||
|  |     view[0] = v.view.x.ToFloat32(); | ||||||
|  |     view[1] = v.view.y.ToFloat32(); | ||||||
|  |     view[2] = v.view.z.ToFloat32(); | ||||||
|  | 
 | ||||||
|  |     if (flip_quaternion) { | ||||||
|  |         normquat = -normquat; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RasterizerAccelerated::RasterizerAccelerated(Memory::MemorySystem& memory_) | ||||||
|  |     : memory{memory_}, regs{Pica::g_state.regs} { | ||||||
|  |     uniform_block_data.lighting_lut_dirty.fill(true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * This is a helper function to resolve an issue when interpolating opposite quaternions. See below | ||||||
|  |  * for a detailed description of this issue (yuriks): | ||||||
|  |  * | ||||||
|  |  * For any rotation, there are two quaternions Q, and -Q, that represent the same rotation. If you | ||||||
|  |  * interpolate two quaternions that are opposite, instead of going from one rotation to another | ||||||
|  |  * using the shortest path, you'll go around the longest path. You can test if two quaternions are | ||||||
|  |  * opposite by checking if Dot(Q1, Q2) < 0. In that case, you can flip either of them, therefore | ||||||
|  |  * making Dot(Q1, -Q2) positive. | ||||||
|  |  * | ||||||
|  |  * This solution corrects this issue per-vertex before passing the quaternions to OpenGL. This is | ||||||
|  |  * correct for most cases but can still rotate around the long way sometimes. An implementation | ||||||
|  |  * which did `lerp(lerp(Q1, Q2), Q3)` (with proper weighting), applying the dot product check | ||||||
|  |  * between each step would work for those cases at the cost of being more complex to implement. | ||||||
|  |  * | ||||||
|  |  * Fortunately however, the 3DS hardware happens to also use this exact same logic to work around | ||||||
|  |  * these issues, making this basic implementation actually more accurate to the hardware. | ||||||
|  |  */ | ||||||
|  | static bool AreQuaternionsOpposite(Common::Vec4<Pica::float24> qa, Common::Vec4<Pica::float24> qb) { | ||||||
|  |     Common::Vec4f a{qa.x.ToFloat32(), qa.y.ToFloat32(), qa.z.ToFloat32(), qa.w.ToFloat32()}; | ||||||
|  |     Common::Vec4f b{qb.x.ToFloat32(), qb.y.ToFloat32(), qb.z.ToFloat32(), qb.w.ToFloat32()}; | ||||||
|  | 
 | ||||||
|  |     return (Common::Dot(a, b) < 0.f); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::AddTriangle(const Pica::Shader::OutputVertex& v0, | ||||||
|  |                                         const Pica::Shader::OutputVertex& v1, | ||||||
|  |                                         const Pica::Shader::OutputVertex& v2) { | ||||||
|  |     vertex_batch.emplace_back(v0, false); | ||||||
|  |     vertex_batch.emplace_back(v1, AreQuaternionsOpposite(v0.quat, v1.quat)); | ||||||
|  |     vertex_batch.emplace_back(v2, AreQuaternionsOpposite(v0.quat, v2.quat)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RasterizerAccelerated::VertexArrayInfo RasterizerAccelerated::AnalyzeVertexArray( | ||||||
|  |     bool is_indexed, u32 stride_alignment) { | ||||||
|  |     const auto& vertex_attributes = regs.pipeline.vertex_attributes; | ||||||
|  | 
 | ||||||
|  |     u32 vertex_min; | ||||||
|  |     u32 vertex_max; | ||||||
|  |     if (is_indexed) { | ||||||
|  |         const auto& index_info = regs.pipeline.index_array; | ||||||
|  |         const PAddr address = vertex_attributes.GetPhysicalBaseAddress() + index_info.offset; | ||||||
|  |         const u8* index_address_8 = memory.GetPhysicalPointer(address); | ||||||
|  |         const u16* index_address_16 = reinterpret_cast<const u16*>(index_address_8); | ||||||
|  |         const bool index_u16 = index_info.format != 0; | ||||||
|  | 
 | ||||||
|  |         vertex_min = 0xFFFF; | ||||||
|  |         vertex_max = 0; | ||||||
|  |         const u32 size = regs.pipeline.num_vertices * (index_u16 ? 2 : 1); | ||||||
|  |         FlushRegion(address, size); | ||||||
|  |         for (u32 index = 0; index < regs.pipeline.num_vertices; ++index) { | ||||||
|  |             const u32 vertex = index_u16 ? index_address_16[index] : index_address_8[index]; | ||||||
|  |             vertex_min = std::min(vertex_min, vertex); | ||||||
|  |             vertex_max = std::max(vertex_max, vertex); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         vertex_min = regs.pipeline.vertex_offset; | ||||||
|  |         vertex_max = regs.pipeline.vertex_offset + regs.pipeline.num_vertices - 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const u32 vertex_num = vertex_max - vertex_min + 1; | ||||||
|  |     u32 vs_input_size = 0; | ||||||
|  |     for (const auto& loader : vertex_attributes.attribute_loaders) { | ||||||
|  |         if (loader.component_count != 0) { | ||||||
|  |             const u32 aligned_stride = | ||||||
|  |                 Common::AlignUp(static_cast<u32>(loader.byte_count), stride_alignment); | ||||||
|  |             vs_input_size += Common::AlignUp(aligned_stride * vertex_num, 4); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return {vertex_min, vertex_max, vs_input_size}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncEntireState() { | ||||||
|  |     // Sync renderer-specific fixed-function state
 | ||||||
|  |     SyncFixedState(); | ||||||
|  | 
 | ||||||
|  |     // Sync uniforms
 | ||||||
|  |     SyncClipCoef(); | ||||||
|  |     SyncDepthScale(); | ||||||
|  |     SyncDepthOffset(); | ||||||
|  |     SyncAlphaTest(); | ||||||
|  |     SyncCombinerColor(); | ||||||
|  |     auto& tev_stages = regs.texturing.GetTevStages(); | ||||||
|  |     for (std::size_t index = 0; index < tev_stages.size(); ++index) { | ||||||
|  |         SyncTevConstColor(index, tev_stages[index]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SyncGlobalAmbient(); | ||||||
|  |     for (unsigned light_index = 0; light_index < 8; light_index++) { | ||||||
|  |         SyncLightSpecular0(light_index); | ||||||
|  |         SyncLightSpecular1(light_index); | ||||||
|  |         SyncLightDiffuse(light_index); | ||||||
|  |         SyncLightAmbient(light_index); | ||||||
|  |         SyncLightPosition(light_index); | ||||||
|  |         SyncLightDistanceAttenuationBias(light_index); | ||||||
|  |         SyncLightDistanceAttenuationScale(light_index); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SyncFogColor(); | ||||||
|  |     SyncProcTexNoise(); | ||||||
|  |     SyncProcTexBias(); | ||||||
|  |     SyncShadowBias(); | ||||||
|  |     SyncShadowTextureBias(); | ||||||
|  | 
 | ||||||
|  |     for (unsigned tex_index = 0; tex_index < 3; tex_index++) { | ||||||
|  |         SyncTextureLodBias(tex_index); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) { | ||||||
|  |     switch (id) { | ||||||
|  |     // Depth modifiers
 | ||||||
|  |     case PICA_REG_INDEX(rasterizer.viewport_depth_range): | ||||||
|  |         SyncDepthScale(); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(rasterizer.viewport_depth_near_plane): | ||||||
|  |         SyncDepthOffset(); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Depth buffering
 | ||||||
|  |     case PICA_REG_INDEX(rasterizer.depthmap_enable): | ||||||
|  |         shader_dirty = true; | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Shadow texture
 | ||||||
|  |     case PICA_REG_INDEX(texturing.shadow): | ||||||
|  |         SyncShadowTextureBias(); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fog state
 | ||||||
|  |     case PICA_REG_INDEX(texturing.fog_color): | ||||||
|  |         SyncFogColor(); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(texturing.fog_lut_data[0]): | ||||||
|  |     case PICA_REG_INDEX(texturing.fog_lut_data[1]): | ||||||
|  |     case PICA_REG_INDEX(texturing.fog_lut_data[2]): | ||||||
|  |     case PICA_REG_INDEX(texturing.fog_lut_data[3]): | ||||||
|  |     case PICA_REG_INDEX(texturing.fog_lut_data[4]): | ||||||
|  |     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[7]): | ||||||
|  |         uniform_block_data.fog_lut_dirty = true; | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // ProcTex state
 | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex): | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_lut): | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_lut_offset): | ||||||
|  |         SyncProcTexBias(); | ||||||
|  |         shader_dirty = true; | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_noise_u): | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_noise_v): | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_noise_frequency): | ||||||
|  |         SyncProcTexNoise(); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_lut_data[0]): | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_lut_data[1]): | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_lut_data[2]): | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_lut_data[3]): | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_lut_data[4]): | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_lut_data[5]): | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_lut_data[6]): | ||||||
|  |     case PICA_REG_INDEX(texturing.proctex_lut_data[7]): | ||||||
|  |         using Pica::TexturingRegs; | ||||||
|  |         switch (regs.texturing.proctex_lut_config.ref_table.Value()) { | ||||||
|  |         case TexturingRegs::ProcTexLutTable::Noise: | ||||||
|  |             uniform_block_data.proctex_noise_lut_dirty = true; | ||||||
|  |             break; | ||||||
|  |         case TexturingRegs::ProcTexLutTable::ColorMap: | ||||||
|  |             uniform_block_data.proctex_color_map_dirty = true; | ||||||
|  |             break; | ||||||
|  |         case TexturingRegs::ProcTexLutTable::AlphaMap: | ||||||
|  |             uniform_block_data.proctex_alpha_map_dirty = true; | ||||||
|  |             break; | ||||||
|  |         case TexturingRegs::ProcTexLutTable::Color: | ||||||
|  |             uniform_block_data.proctex_lut_dirty = true; | ||||||
|  |             break; | ||||||
|  |         case TexturingRegs::ProcTexLutTable::ColorDiff: | ||||||
|  |             uniform_block_data.proctex_diff_lut_dirty = true; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Alpha test
 | ||||||
|  |     case PICA_REG_INDEX(framebuffer.output_merger.alpha_test): | ||||||
|  |         SyncAlphaTest(); | ||||||
|  |         shader_dirty = true; | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case PICA_REG_INDEX(framebuffer.shadow): | ||||||
|  |         SyncShadowBias(); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Scissor test
 | ||||||
|  |     case PICA_REG_INDEX(rasterizer.scissor_test.mode): | ||||||
|  |         shader_dirty = true; | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case PICA_REG_INDEX(texturing.main_config): | ||||||
|  |         shader_dirty = true; | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Texture 0 type
 | ||||||
|  |     case PICA_REG_INDEX(texturing.texture0.type): | ||||||
|  |         shader_dirty = true; | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // TEV stages
 | ||||||
|  |     // (This also syncs fog_mode and fog_flip which are part of tev_combiner_buffer_input)
 | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage0.color_source1): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage0.color_modifier1): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage0.color_op): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage0.color_scale): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage1.color_source1): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage1.color_modifier1): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage1.color_op): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage1.color_scale): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage2.color_source1): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage2.color_modifier1): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage2.color_op): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage2.color_scale): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage3.color_source1): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage3.color_modifier1): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage3.color_op): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage3.color_scale): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage4.color_source1): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage4.color_modifier1): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage4.color_op): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage4.color_scale): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage5.color_source1): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage5.color_modifier1): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage5.color_op): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage5.color_scale): | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_combiner_buffer_input): | ||||||
|  |         shader_dirty = true; | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage0.const_r): | ||||||
|  |         SyncTevConstColor(0, regs.texturing.tev_stage0); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage1.const_r): | ||||||
|  |         SyncTevConstColor(1, regs.texturing.tev_stage1); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage2.const_r): | ||||||
|  |         SyncTevConstColor(2, regs.texturing.tev_stage2); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage3.const_r): | ||||||
|  |         SyncTevConstColor(3, regs.texturing.tev_stage3); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage4.const_r): | ||||||
|  |         SyncTevConstColor(4, regs.texturing.tev_stage4); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_stage5.const_r): | ||||||
|  |         SyncTevConstColor(5, regs.texturing.tev_stage5); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // TEV combiner buffer color
 | ||||||
|  |     case PICA_REG_INDEX(texturing.tev_combiner_buffer_color): | ||||||
|  |         SyncCombinerColor(); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fragment lighting switches
 | ||||||
|  |     case PICA_REG_INDEX(lighting.disable): | ||||||
|  |     case PICA_REG_INDEX(lighting.max_light_index): | ||||||
|  |     case PICA_REG_INDEX(lighting.config0): | ||||||
|  |     case PICA_REG_INDEX(lighting.config1): | ||||||
|  |     case PICA_REG_INDEX(lighting.abs_lut_input): | ||||||
|  |     case PICA_REG_INDEX(lighting.lut_input): | ||||||
|  |     case PICA_REG_INDEX(lighting.lut_scale): | ||||||
|  |     case PICA_REG_INDEX(lighting.light_enable): | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fragment lighting specular 0 color
 | ||||||
|  |     case PICA_REG_INDEX(lighting.light[0].specular_0): | ||||||
|  |         SyncLightSpecular0(0); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[1].specular_0): | ||||||
|  |         SyncLightSpecular0(1); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[2].specular_0): | ||||||
|  |         SyncLightSpecular0(2); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[3].specular_0): | ||||||
|  |         SyncLightSpecular0(3); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[4].specular_0): | ||||||
|  |         SyncLightSpecular0(4); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[5].specular_0): | ||||||
|  |         SyncLightSpecular0(5); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[6].specular_0): | ||||||
|  |         SyncLightSpecular0(6); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[7].specular_0): | ||||||
|  |         SyncLightSpecular0(7); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fragment lighting specular 1 color
 | ||||||
|  |     case PICA_REG_INDEX(lighting.light[0].specular_1): | ||||||
|  |         SyncLightSpecular1(0); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[1].specular_1): | ||||||
|  |         SyncLightSpecular1(1); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[2].specular_1): | ||||||
|  |         SyncLightSpecular1(2); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[3].specular_1): | ||||||
|  |         SyncLightSpecular1(3); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[4].specular_1): | ||||||
|  |         SyncLightSpecular1(4); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[5].specular_1): | ||||||
|  |         SyncLightSpecular1(5); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[6].specular_1): | ||||||
|  |         SyncLightSpecular1(6); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[7].specular_1): | ||||||
|  |         SyncLightSpecular1(7); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fragment lighting diffuse color
 | ||||||
|  |     case PICA_REG_INDEX(lighting.light[0].diffuse): | ||||||
|  |         SyncLightDiffuse(0); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[1].diffuse): | ||||||
|  |         SyncLightDiffuse(1); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[2].diffuse): | ||||||
|  |         SyncLightDiffuse(2); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[3].diffuse): | ||||||
|  |         SyncLightDiffuse(3); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[4].diffuse): | ||||||
|  |         SyncLightDiffuse(4); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[5].diffuse): | ||||||
|  |         SyncLightDiffuse(5); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[6].diffuse): | ||||||
|  |         SyncLightDiffuse(6); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[7].diffuse): | ||||||
|  |         SyncLightDiffuse(7); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fragment lighting ambient color
 | ||||||
|  |     case PICA_REG_INDEX(lighting.light[0].ambient): | ||||||
|  |         SyncLightAmbient(0); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[1].ambient): | ||||||
|  |         SyncLightAmbient(1); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[2].ambient): | ||||||
|  |         SyncLightAmbient(2); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[3].ambient): | ||||||
|  |         SyncLightAmbient(3); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[4].ambient): | ||||||
|  |         SyncLightAmbient(4); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[5].ambient): | ||||||
|  |         SyncLightAmbient(5); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[6].ambient): | ||||||
|  |         SyncLightAmbient(6); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[7].ambient): | ||||||
|  |         SyncLightAmbient(7); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fragment lighting position
 | ||||||
|  |     case PICA_REG_INDEX(lighting.light[0].x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[0].z): | ||||||
|  |         SyncLightPosition(0); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[1].x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[1].z): | ||||||
|  |         SyncLightPosition(1); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[2].x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[2].z): | ||||||
|  |         SyncLightPosition(2); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[3].x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[3].z): | ||||||
|  |         SyncLightPosition(3); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[4].x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[4].z): | ||||||
|  |         SyncLightPosition(4); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[5].x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[5].z): | ||||||
|  |         SyncLightPosition(5); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[6].x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[6].z): | ||||||
|  |         SyncLightPosition(6); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[7].x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[7].z): | ||||||
|  |         SyncLightPosition(7); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fragment spot lighting direction
 | ||||||
|  |     case PICA_REG_INDEX(lighting.light[0].spot_x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[0].spot_z): | ||||||
|  |         SyncLightSpotDirection(0); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[1].spot_x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[1].spot_z): | ||||||
|  |         SyncLightSpotDirection(1); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[2].spot_x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[2].spot_z): | ||||||
|  |         SyncLightSpotDirection(2); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[3].spot_x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[3].spot_z): | ||||||
|  |         SyncLightSpotDirection(3); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[4].spot_x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[4].spot_z): | ||||||
|  |         SyncLightSpotDirection(4); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[5].spot_x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[5].spot_z): | ||||||
|  |         SyncLightSpotDirection(5); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[6].spot_x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[6].spot_z): | ||||||
|  |         SyncLightSpotDirection(6); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[7].spot_x): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[7].spot_z): | ||||||
|  |         SyncLightSpotDirection(7); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fragment lighting light source config
 | ||||||
|  |     case PICA_REG_INDEX(lighting.light[0].config): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[1].config): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[2].config): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[3].config): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[4].config): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[5].config): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[6].config): | ||||||
|  |     case PICA_REG_INDEX(lighting.light[7].config): | ||||||
|  |         shader_dirty = true; | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fragment lighting distance attenuation bias
 | ||||||
|  |     case PICA_REG_INDEX(lighting.light[0].dist_atten_bias): | ||||||
|  |         SyncLightDistanceAttenuationBias(0); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[1].dist_atten_bias): | ||||||
|  |         SyncLightDistanceAttenuationBias(1); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[2].dist_atten_bias): | ||||||
|  |         SyncLightDistanceAttenuationBias(2); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[3].dist_atten_bias): | ||||||
|  |         SyncLightDistanceAttenuationBias(3); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[4].dist_atten_bias): | ||||||
|  |         SyncLightDistanceAttenuationBias(4); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[5].dist_atten_bias): | ||||||
|  |         SyncLightDistanceAttenuationBias(5); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[6].dist_atten_bias): | ||||||
|  |         SyncLightDistanceAttenuationBias(6); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[7].dist_atten_bias): | ||||||
|  |         SyncLightDistanceAttenuationBias(7); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fragment lighting distance attenuation scale
 | ||||||
|  |     case PICA_REG_INDEX(lighting.light[0].dist_atten_scale): | ||||||
|  |         SyncLightDistanceAttenuationScale(0); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[1].dist_atten_scale): | ||||||
|  |         SyncLightDistanceAttenuationScale(1); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[2].dist_atten_scale): | ||||||
|  |         SyncLightDistanceAttenuationScale(2); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[3].dist_atten_scale): | ||||||
|  |         SyncLightDistanceAttenuationScale(3); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[4].dist_atten_scale): | ||||||
|  |         SyncLightDistanceAttenuationScale(4); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[5].dist_atten_scale): | ||||||
|  |         SyncLightDistanceAttenuationScale(5); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[6].dist_atten_scale): | ||||||
|  |         SyncLightDistanceAttenuationScale(6); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(lighting.light[7].dist_atten_scale): | ||||||
|  |         SyncLightDistanceAttenuationScale(7); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fragment lighting global ambient color (emission + ambient * ambient)
 | ||||||
|  |     case PICA_REG_INDEX(lighting.global_ambient): | ||||||
|  |         SyncGlobalAmbient(); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Fragment lighting lookup tables
 | ||||||
|  |     case PICA_REG_INDEX(lighting.lut_data[0]): | ||||||
|  |     case PICA_REG_INDEX(lighting.lut_data[1]): | ||||||
|  |     case PICA_REG_INDEX(lighting.lut_data[2]): | ||||||
|  |     case PICA_REG_INDEX(lighting.lut_data[3]): | ||||||
|  |     case PICA_REG_INDEX(lighting.lut_data[4]): | ||||||
|  |     case PICA_REG_INDEX(lighting.lut_data[5]): | ||||||
|  |     case PICA_REG_INDEX(lighting.lut_data[6]): | ||||||
|  |     case PICA_REG_INDEX(lighting.lut_data[7]): { | ||||||
|  |         const auto& lut_config = regs.lighting.lut_config; | ||||||
|  |         uniform_block_data.lighting_lut_dirty[lut_config.type] = true; | ||||||
|  |         uniform_block_data.lighting_lut_dirty_any = true; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Texture LOD biases
 | ||||||
|  |     case PICA_REG_INDEX(texturing.texture0.lod.bias): | ||||||
|  |         SyncTextureLodBias(0); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(texturing.texture1.lod.bias): | ||||||
|  |         SyncTextureLodBias(1); | ||||||
|  |         break; | ||||||
|  |     case PICA_REG_INDEX(texturing.texture2.lod.bias): | ||||||
|  |         SyncTextureLodBias(2); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     // Clipping plane
 | ||||||
|  |     case PICA_REG_INDEX(rasterizer.clip_coef[0]): | ||||||
|  |     case PICA_REG_INDEX(rasterizer.clip_coef[1]): | ||||||
|  |     case PICA_REG_INDEX(rasterizer.clip_coef[2]): | ||||||
|  |     case PICA_REG_INDEX(rasterizer.clip_coef[3]): | ||||||
|  |         SyncClipCoef(); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         // Forward registers that map to fixed function API features to the video backend
 | ||||||
|  |         NotifyFixedFunctionPicaRegisterChanged(id); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncDepthScale() { | ||||||
|  |     float depth_scale = Pica::float24::FromRaw(regs.rasterizer.viewport_depth_range).ToFloat32(); | ||||||
|  | 
 | ||||||
|  |     if (depth_scale != uniform_block_data.data.depth_scale) { | ||||||
|  |         uniform_block_data.data.depth_scale = depth_scale; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncDepthOffset() { | ||||||
|  |     float depth_offset = | ||||||
|  |         Pica::float24::FromRaw(regs.rasterizer.viewport_depth_near_plane).ToFloat32(); | ||||||
|  | 
 | ||||||
|  |     if (depth_offset != uniform_block_data.data.depth_offset) { | ||||||
|  |         uniform_block_data.data.depth_offset = depth_offset; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncFogColor() { | ||||||
|  |     const auto& fog_color_regs = regs.texturing.fog_color; | ||||||
|  |     const Common::Vec3f fog_color = { | ||||||
|  |         fog_color_regs.r.Value() / 255.0f, | ||||||
|  |         fog_color_regs.g.Value() / 255.0f, | ||||||
|  |         fog_color_regs.b.Value() / 255.0f, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if (fog_color != uniform_block_data.data.fog_color) { | ||||||
|  |         uniform_block_data.data.fog_color = fog_color; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncProcTexNoise() { | ||||||
|  |     const Common::Vec2f proctex_noise_f = { | ||||||
|  |         Pica::float16::FromRaw(regs.texturing.proctex_noise_frequency.u).ToFloat32(), | ||||||
|  |         Pica::float16::FromRaw(regs.texturing.proctex_noise_frequency.v).ToFloat32(), | ||||||
|  |     }; | ||||||
|  |     const Common::Vec2f proctex_noise_a = { | ||||||
|  |         regs.texturing.proctex_noise_u.amplitude / 4095.0f, | ||||||
|  |         regs.texturing.proctex_noise_v.amplitude / 4095.0f, | ||||||
|  |     }; | ||||||
|  |     const Common::Vec2f proctex_noise_p = { | ||||||
|  |         Pica::float16::FromRaw(regs.texturing.proctex_noise_u.phase).ToFloat32(), | ||||||
|  |         Pica::float16::FromRaw(regs.texturing.proctex_noise_v.phase).ToFloat32(), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if (proctex_noise_f != uniform_block_data.data.proctex_noise_f || | ||||||
|  |         proctex_noise_a != uniform_block_data.data.proctex_noise_a || | ||||||
|  |         proctex_noise_p != uniform_block_data.data.proctex_noise_p) { | ||||||
|  |         uniform_block_data.data.proctex_noise_f = proctex_noise_f; | ||||||
|  |         uniform_block_data.data.proctex_noise_a = proctex_noise_a; | ||||||
|  |         uniform_block_data.data.proctex_noise_p = proctex_noise_p; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncProcTexBias() { | ||||||
|  |     const auto proctex_bias = Pica::float16::FromRaw(regs.texturing.proctex.bias_low | | ||||||
|  |                                                      (regs.texturing.proctex_lut.bias_high << 8)) | ||||||
|  |                                   .ToFloat32(); | ||||||
|  |     if (proctex_bias != uniform_block_data.data.proctex_bias) { | ||||||
|  |         uniform_block_data.data.proctex_bias = proctex_bias; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncAlphaTest() { | ||||||
|  |     if (regs.framebuffer.output_merger.alpha_test.ref != uniform_block_data.data.alphatest_ref) { | ||||||
|  |         uniform_block_data.data.alphatest_ref = regs.framebuffer.output_merger.alpha_test.ref; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncCombinerColor() { | ||||||
|  |     auto combiner_color = ColorRGBA8(regs.texturing.tev_combiner_buffer_color.raw); | ||||||
|  |     if (combiner_color != uniform_block_data.data.tev_combiner_buffer_color) { | ||||||
|  |         uniform_block_data.data.tev_combiner_buffer_color = combiner_color; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncTevConstColor( | ||||||
|  |     std::size_t stage_index, const Pica::TexturingRegs::TevStageConfig& tev_stage) { | ||||||
|  |     const auto const_color = ColorRGBA8(tev_stage.const_color); | ||||||
|  | 
 | ||||||
|  |     if (const_color == uniform_block_data.data.const_color[stage_index]) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     uniform_block_data.data.const_color[stage_index] = const_color; | ||||||
|  |     uniform_block_data.dirty = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncGlobalAmbient() { | ||||||
|  |     auto color = LightColor(regs.lighting.global_ambient); | ||||||
|  |     if (color != uniform_block_data.data.lighting_global_ambient) { | ||||||
|  |         uniform_block_data.data.lighting_global_ambient = color; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncLightSpecular0(int light_index) { | ||||||
|  |     auto color = LightColor(regs.lighting.light[light_index].specular_0); | ||||||
|  |     if (color != uniform_block_data.data.light_src[light_index].specular_0) { | ||||||
|  |         uniform_block_data.data.light_src[light_index].specular_0 = color; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncLightSpecular1(int light_index) { | ||||||
|  |     auto color = LightColor(regs.lighting.light[light_index].specular_1); | ||||||
|  |     if (color != uniform_block_data.data.light_src[light_index].specular_1) { | ||||||
|  |         uniform_block_data.data.light_src[light_index].specular_1 = color; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncLightDiffuse(int light_index) { | ||||||
|  |     auto color = LightColor(regs.lighting.light[light_index].diffuse); | ||||||
|  |     if (color != uniform_block_data.data.light_src[light_index].diffuse) { | ||||||
|  |         uniform_block_data.data.light_src[light_index].diffuse = color; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncLightAmbient(int light_index) { | ||||||
|  |     auto color = LightColor(regs.lighting.light[light_index].ambient); | ||||||
|  |     if (color != uniform_block_data.data.light_src[light_index].ambient) { | ||||||
|  |         uniform_block_data.data.light_src[light_index].ambient = color; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncLightPosition(int light_index) { | ||||||
|  |     const Common::Vec3f position = { | ||||||
|  |         Pica::float16::FromRaw(regs.lighting.light[light_index].x).ToFloat32(), | ||||||
|  |         Pica::float16::FromRaw(regs.lighting.light[light_index].y).ToFloat32(), | ||||||
|  |         Pica::float16::FromRaw(regs.lighting.light[light_index].z).ToFloat32(), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if (position != uniform_block_data.data.light_src[light_index].position) { | ||||||
|  |         uniform_block_data.data.light_src[light_index].position = position; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncLightSpotDirection(int light_index) { | ||||||
|  |     const auto& light = regs.lighting.light[light_index]; | ||||||
|  |     const auto spot_direction = | ||||||
|  |         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) { | ||||||
|  |         uniform_block_data.data.light_src[light_index].spot_direction = spot_direction; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncLightDistanceAttenuationBias(int light_index) { | ||||||
|  |     float dist_atten_bias = | ||||||
|  |         Pica::float20::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) { | ||||||
|  |         uniform_block_data.data.light_src[light_index].dist_atten_bias = dist_atten_bias; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncLightDistanceAttenuationScale(int light_index) { | ||||||
|  |     float dist_atten_scale = | ||||||
|  |         Pica::float20::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) { | ||||||
|  |         uniform_block_data.data.light_src[light_index].dist_atten_scale = dist_atten_scale; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncShadowBias() { | ||||||
|  |     const auto& shadow = regs.framebuffer.shadow; | ||||||
|  |     float constant = Pica::float16::FromRaw(shadow.constant).ToFloat32(); | ||||||
|  |     float linear = Pica::float16::FromRaw(shadow.linear).ToFloat32(); | ||||||
|  | 
 | ||||||
|  |     if (constant != uniform_block_data.data.shadow_bias_constant || | ||||||
|  |         linear != uniform_block_data.data.shadow_bias_linear) { | ||||||
|  |         uniform_block_data.data.shadow_bias_constant = constant; | ||||||
|  |         uniform_block_data.data.shadow_bias_linear = linear; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncShadowTextureBias() { | ||||||
|  |     int bias = regs.texturing.shadow.bias << 1; | ||||||
|  |     if (bias != uniform_block_data.data.shadow_texture_bias) { | ||||||
|  |         uniform_block_data.data.shadow_texture_bias = bias; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncTextureLodBias(int tex_index) { | ||||||
|  |     const auto pica_textures = regs.texturing.GetTextures(); | ||||||
|  |     const float bias = pica_textures[tex_index].config.lod.bias / 256.0f; | ||||||
|  |     if (bias != uniform_block_data.data.tex_lod_bias[tex_index]) { | ||||||
|  |         uniform_block_data.data.tex_lod_bias[tex_index] = bias; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerAccelerated::SyncClipCoef() { | ||||||
|  |     const auto raw_clip_coef = regs.rasterizer.GetClipCoef(); | ||||||
|  |     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()}; | ||||||
|  |     if (new_clip_coef != uniform_block_data.data.clip_coef) { | ||||||
|  |         uniform_block_data.data.clip_coef = new_clip_coef; | ||||||
|  |         uniform_block_data.dirty = true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace VideoCore
 | ||||||
							
								
								
									
										159
									
								
								src/video_core/rasterizer_accelerated.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								src/video_core/rasterizer_accelerated.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,159 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "common/vector_math.h" | ||||||
|  | #include "video_core/rasterizer_interface.h" | ||||||
|  | #include "video_core/regs_texturing.h" | ||||||
|  | #include "video_core/shader/shader_uniforms.h" | ||||||
|  | 
 | ||||||
|  | namespace Memory { | ||||||
|  | class MemorySystem; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace Pica { | ||||||
|  | struct Regs; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace VideoCore { | ||||||
|  | 
 | ||||||
|  | class RasterizerAccelerated : public RasterizerInterface { | ||||||
|  | public: | ||||||
|  |     RasterizerAccelerated(Memory::MemorySystem& memory); | ||||||
|  |     virtual ~RasterizerAccelerated() = default; | ||||||
|  | 
 | ||||||
|  |     void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1, | ||||||
|  |                      const Pica::Shader::OutputVertex& v2) override; | ||||||
|  |     void NotifyPicaRegisterChanged(u32 id) override; | ||||||
|  |     void SyncEntireState() override; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     /// Sync fixed-function pipeline state
 | ||||||
|  |     virtual void SyncFixedState() = 0; | ||||||
|  | 
 | ||||||
|  |     /// Notifies that a fixed function PICA register changed to the video backend
 | ||||||
|  |     virtual void NotifyFixedFunctionPicaRegisterChanged(u32 id) = 0; | ||||||
|  | 
 | ||||||
|  |     /// Syncs the depth scale to match the PICA register
 | ||||||
|  |     void SyncDepthScale(); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the depth offset to match the PICA register
 | ||||||
|  |     void SyncDepthOffset(); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the fog states to match the PICA register
 | ||||||
|  |     void SyncFogColor(); | ||||||
|  | 
 | ||||||
|  |     /// Sync the procedural texture noise configuration to match the PICA register
 | ||||||
|  |     void SyncProcTexNoise(); | ||||||
|  | 
 | ||||||
|  |     /// Sync the procedural texture bias configuration to match the PICA register
 | ||||||
|  |     void SyncProcTexBias(); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the alpha test states to match the PICA register
 | ||||||
|  |     void SyncAlphaTest(); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the TEV combiner color buffer to match the PICA register
 | ||||||
|  |     void SyncCombinerColor(); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the TEV constant color to match the PICA register
 | ||||||
|  |     void SyncTevConstColor(std::size_t tev_index, | ||||||
|  |                            const Pica::TexturingRegs::TevStageConfig& tev_stage); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the lighting global ambient color to match the PICA register
 | ||||||
|  |     void SyncGlobalAmbient(); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the specified light's specular 0 color to match the PICA register
 | ||||||
|  |     void SyncLightSpecular0(int light_index); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the specified light's specular 1 color to match the PICA register
 | ||||||
|  |     void SyncLightSpecular1(int light_index); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the specified light's diffuse color to match the PICA register
 | ||||||
|  |     void SyncLightDiffuse(int light_index); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the specified light's ambient color to match the PICA register
 | ||||||
|  |     void SyncLightAmbient(int light_index); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the specified light's position to match the PICA register
 | ||||||
|  |     void SyncLightPosition(int light_index); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the specified spot light direcition to match the PICA register
 | ||||||
|  |     void SyncLightSpotDirection(int light_index); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the specified light's distance attenuation bias to match the PICA register
 | ||||||
|  |     void SyncLightDistanceAttenuationBias(int light_index); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the specified light's distance attenuation scale to match the PICA register
 | ||||||
|  |     void SyncLightDistanceAttenuationScale(int light_index); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the shadow rendering bias to match the PICA register
 | ||||||
|  |     void SyncShadowBias(); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the shadow texture bias to match the PICA register
 | ||||||
|  |     void SyncShadowTextureBias(); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the texture LOD bias to match the PICA register
 | ||||||
|  |     void SyncTextureLodBias(int tex_index); | ||||||
|  | 
 | ||||||
|  |     /// Syncs the clip coefficients to match the PICA register
 | ||||||
|  |     void SyncClipCoef(); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     /// Structure that keeps tracks of the uniform state
 | ||||||
|  |     struct UniformBlockData { | ||||||
|  |         Pica::Shader::UniformData data{}; | ||||||
|  |         std::array<bool, Pica::LightingRegs::NumLightingSampler> lighting_lut_dirty{}; | ||||||
|  |         bool lighting_lut_dirty_any = true; | ||||||
|  |         bool fog_lut_dirty = true; | ||||||
|  |         bool proctex_noise_lut_dirty = true; | ||||||
|  |         bool proctex_color_map_dirty = true; | ||||||
|  |         bool proctex_alpha_map_dirty = true; | ||||||
|  |         bool proctex_lut_dirty = true; | ||||||
|  |         bool proctex_diff_lut_dirty = true; | ||||||
|  |         bool dirty = true; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /// Structure that the hardware rendered vertices are composed of
 | ||||||
|  |     struct HardwareVertex { | ||||||
|  |         HardwareVertex() = default; | ||||||
|  |         HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion); | ||||||
|  | 
 | ||||||
|  |         Common::Vec4f position; | ||||||
|  |         Common::Vec4f color; | ||||||
|  |         Common::Vec2f tex_coord0; | ||||||
|  |         Common::Vec2f tex_coord1; | ||||||
|  |         Common::Vec2f tex_coord2; | ||||||
|  |         float tex_coord0_w; | ||||||
|  |         Common::Vec4f normquat; | ||||||
|  |         Common::Vec3f view; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     struct VertexArrayInfo { | ||||||
|  |         u32 vs_input_index_min; | ||||||
|  |         u32 vs_input_index_max; | ||||||
|  |         u32 vs_input_size; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /// Retrieve the range and the size of the input vertex
 | ||||||
|  |     VertexArrayInfo AnalyzeVertexArray(bool is_indexed, u32 stride_alignment = 1); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     Memory::MemorySystem& memory; | ||||||
|  |     Pica::Regs& regs; | ||||||
|  | 
 | ||||||
|  |     std::vector<HardwareVertex> vertex_batch; | ||||||
|  |     bool shader_dirty = true; | ||||||
|  | 
 | ||||||
|  |     UniformBlockData uniform_block_data{}; | ||||||
|  |     std::array<std::array<Common::Vec2f, 256>, Pica::LightingRegs::NumLightingSampler> | ||||||
|  |         lighting_lut_data{}; | ||||||
|  |     std::array<Common::Vec2f, 128> fog_lut_data{}; | ||||||
|  |     std::array<Common::Vec2f, 128> proctex_noise_lut_data{}; | ||||||
|  |     std::array<Common::Vec2f, 128> proctex_color_map_data{}; | ||||||
|  |     std::array<Common::Vec2f, 128> proctex_alpha_map_data{}; | ||||||
|  |     std::array<Common::Vec4f, 256> proctex_lut_data{}; | ||||||
|  |     std::array<Common::Vec4f, 256> proctex_diff_lut_data{}; | ||||||
|  | }; | ||||||
|  | } // namespace VideoCore
 | ||||||
|  | @ -2,15 +2,17 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include "core/core.h" | ||||||
| #include "core/frontend/emu_window.h" | #include "core/frontend/emu_window.h" | ||||||
|  | #include "core/tracer/recorder.h" | ||||||
|  | #include "video_core/debug_utils/debug_utils.h" | ||||||
| #include "video_core/renderer_base.h" | #include "video_core/renderer_base.h" | ||||||
| #include "video_core/renderer_opengl/gl_rasterizer.h" |  | ||||||
| #include "video_core/swrasterizer/swrasterizer.h" |  | ||||||
| #include "video_core/video_core.h" |  | ||||||
| 
 | 
 | ||||||
| RendererBase::RendererBase(Frontend::EmuWindow& window, Frontend::EmuWindow* secondary_window_) | namespace VideoCore { | ||||||
|     : render_window{window}, secondary_window{secondary_window_} {} | 
 | ||||||
|  | RendererBase::RendererBase(Core::System& system_, Frontend::EmuWindow& window, | ||||||
|  |                            Frontend::EmuWindow* secondary_window_) | ||||||
|  |     : system{system_}, render_window{window}, secondary_window{secondary_window_} {} | ||||||
| 
 | 
 | ||||||
| RendererBase::~RendererBase() = default; | RendererBase::~RendererBase() = default; | ||||||
| 
 | 
 | ||||||
|  | @ -25,19 +27,35 @@ void RendererBase::UpdateCurrentFramebufferLayout(bool is_portrait_mode) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RendererBase::RefreshRasterizerSetting() { | void RendererBase::EndFrame() { | ||||||
|     bool hw_renderer_enabled = VideoCore::g_hw_renderer_enabled; |     current_frame++; | ||||||
|     if (rasterizer == nullptr || opengl_rasterizer_active != hw_renderer_enabled) { |  | ||||||
|         opengl_rasterizer_active = hw_renderer_enabled; |  | ||||||
| 
 | 
 | ||||||
|         if (hw_renderer_enabled) { |     system.perf_stats->EndSystemFrame(); | ||||||
|             rasterizer = std::make_unique<OpenGL::RasterizerOpenGL>(render_window); | 
 | ||||||
|         } else { |     render_window.PollEvents(); | ||||||
|             rasterizer = std::make_unique<VideoCore::SWRasterizer>(); | 
 | ||||||
|         } |     system.frame_limiter.DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs()); | ||||||
|  |     system.perf_stats->BeginSystemFrame(); | ||||||
|  | 
 | ||||||
|  |     if (Pica::g_debug_context && Pica::g_debug_context->recorder) { | ||||||
|  |         Pica::g_debug_context->recorder->FrameFinished(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RendererBase::Sync() { | bool RendererBase::IsScreenshotPending() const { | ||||||
|     rasterizer->SyncEntireState(); |     return renderer_settings.screenshot_requested; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void RendererBase::RequestScreenshot(void* data, std::function<void()> callback, | ||||||
|  |                                      const Layout::FramebufferLayout& layout) { | ||||||
|  |     if (renderer_settings.screenshot_requested) { | ||||||
|  |         LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     renderer_settings.screenshot_bits = data; | ||||||
|  |     renderer_settings.screenshot_complete_callback = callback; | ||||||
|  |     renderer_settings.screenshot_framebuffer_layout = layout; | ||||||
|  |     renderer_settings.screenshot_requested = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace VideoCore
 | ||||||
|  |  | ||||||
|  | @ -4,25 +4,36 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <memory> |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "core/frontend/framebuffer_layout.h" | ||||||
| #include "video_core/rasterizer_interface.h" | #include "video_core/rasterizer_interface.h" | ||||||
| #include "video_core/video_core.h" |  | ||||||
| 
 | 
 | ||||||
| namespace Frontend { | namespace Frontend { | ||||||
| class EmuWindow; | class EmuWindow; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | namespace Core { | ||||||
|  | class System; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace VideoCore { | ||||||
|  | 
 | ||||||
|  | struct RendererSettings { | ||||||
|  |     // Screenshot
 | ||||||
|  |     std::atomic_bool screenshot_requested{false}; | ||||||
|  |     void* screenshot_bits{}; | ||||||
|  |     std::function<void()> screenshot_complete_callback; | ||||||
|  |     Layout::FramebufferLayout screenshot_framebuffer_layout; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| class RendererBase : NonCopyable { | class RendererBase : NonCopyable { | ||||||
| public: | public: | ||||||
|     explicit RendererBase(Frontend::EmuWindow& window, Frontend::EmuWindow* secondary_window); |     explicit RendererBase(Core::System& system, Frontend::EmuWindow& window, | ||||||
|  |                           Frontend::EmuWindow* secondary_window); | ||||||
|     virtual ~RendererBase(); |     virtual ~RendererBase(); | ||||||
| 
 | 
 | ||||||
|     /// Initialize the renderer
 |     /// Returns the rasterizer owned by the renderer
 | ||||||
|     virtual VideoCore::ResultStatus Init() = 0; |     virtual VideoCore::RasterizerInterface* Rasterizer() const = 0; | ||||||
| 
 |  | ||||||
|     /// Shutdown the renderer
 |  | ||||||
|     virtual void ShutDown() = 0; |  | ||||||
| 
 | 
 | ||||||
|     /// Finalize rendering the guest frame and draw into the presentation texture
 |     /// Finalize rendering the guest frame and draw into the presentation texture
 | ||||||
|     virtual void SwapBuffers() = 0; |     virtual void SwapBuffers() = 0; | ||||||
|  | @ -35,27 +46,29 @@ public: | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Prepares for video dumping (e.g. create necessary buffers, etc)
 |     /// Prepares for video dumping (e.g. create necessary buffers, etc)
 | ||||||
|     virtual void PrepareVideoDumping() = 0; |     virtual void PrepareVideoDumping() {} | ||||||
| 
 | 
 | ||||||
|     /// Cleans up after video dumping is ended
 |     /// Cleans up after video dumping is ended
 | ||||||
|     virtual void CleanupVideoDumping() = 0; |     virtual void CleanupVideoDumping() {} | ||||||
|  | 
 | ||||||
|  |     /// Synchronizes fixed function renderer state
 | ||||||
|  |     virtual void Sync() {} | ||||||
| 
 | 
 | ||||||
|     /// Updates the framebuffer layout of the contained render window handle.
 |     /// Updates the framebuffer layout of the contained render window handle.
 | ||||||
|     void UpdateCurrentFramebufferLayout(bool is_portrait_mode = {}); |     void UpdateCurrentFramebufferLayout(bool is_portrait_mode = {}); | ||||||
| 
 | 
 | ||||||
|  |     /// Ends the current frame
 | ||||||
|  |     void EndFrame(); | ||||||
|  | 
 | ||||||
|     // Getter/setter functions:
 |     // Getter/setter functions:
 | ||||||
|     // ------------------------
 |     // ------------------------
 | ||||||
| 
 | 
 | ||||||
|     f32 GetCurrentFPS() const { |     f32 GetCurrentFPS() const { | ||||||
|         return m_current_fps; |         return current_fps; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     int GetCurrentFrame() const { |     int GetCurrentFrame() const { | ||||||
|         return m_current_frame; |         return current_frame; | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     VideoCore::RasterizerInterface* Rasterizer() const { |  | ||||||
|         return rasterizer.get(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Frontend::EmuWindow& GetRenderWindow() { |     Frontend::EmuWindow& GetRenderWindow() { | ||||||
|  | @ -66,16 +79,28 @@ public: | ||||||
|         return render_window; |         return render_window; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void RefreshRasterizerSetting(); |     [[nodiscard]] RendererSettings& Settings() { | ||||||
|     void Sync(); |         return renderer_settings; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     [[nodiscard]] const RendererSettings& Settings() const { | ||||||
|  |         return renderer_settings; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns true if a screenshot is being processed
 | ||||||
|  |     [[nodiscard]] bool IsScreenshotPending() const; | ||||||
|  | 
 | ||||||
|  |     /// Request a screenshot of the next frame
 | ||||||
|  |     void RequestScreenshot(void* data, std::function<void()> callback, | ||||||
|  |                            const Layout::FramebufferLayout& layout); | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|  |     Core::System& system; | ||||||
|  |     RendererSettings renderer_settings; | ||||||
|     Frontend::EmuWindow& render_window;    ///< Reference to the render window handle.
 |     Frontend::EmuWindow& render_window;    ///< Reference to the render window handle.
 | ||||||
|     Frontend::EmuWindow* secondary_window; ///< Reference to the secondary render window handle.
 |     Frontend::EmuWindow* secondary_window; ///< Reference to the secondary render window handle.
 | ||||||
|     std::unique_ptr<VideoCore::RasterizerInterface> rasterizer; |     f32 current_fps = 0.0f;                ///< Current framerate, should be set by the renderer
 | ||||||
|     f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
 |     int current_frame = 0;                 ///< Current frame, should be set by the renderer
 | ||||||
|     int m_current_frame = 0;  ///< Current frame, should be set by the renderer
 |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     bool opengl_rasterizer_active = false; |  | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | } // namespace VideoCore
 | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <glad/glad.h> | #include <glad/glad.h> | ||||||
| #include "core/frontend/emu_window.h" | #include "core/frontend/emu_window.h" | ||||||
| #include "core/frontend/scope_acquire_context.h" |  | ||||||
| #include "video_core/renderer_opengl/frame_dumper_opengl.h" | #include "video_core/renderer_opengl/frame_dumper_opengl.h" | ||||||
| #include "video_core/renderer_opengl/renderer_opengl.h" | #include "video_core/renderer_opengl/renderer_opengl.h" | ||||||
| 
 | 
 | ||||||
|  | @ -39,7 +38,7 @@ void FrameDumperOpenGL::StopDumping() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void FrameDumperOpenGL::PresentLoop() { | void FrameDumperOpenGL::PresentLoop() { | ||||||
|     Frontend::ScopeAcquireContext scope{*context}; |     const auto scope = context->Acquire(); | ||||||
|     InitializeOpenGLObjects(); |     InitializeOpenGLObjects(); | ||||||
| 
 | 
 | ||||||
|     const auto& layout = GetLayout(); |     const auto& layout = GetLayout(); | ||||||
|  |  | ||||||
							
								
								
									
										156
									
								
								src/video_core/renderer_opengl/gl_driver.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								src/video_core/renderer_opengl/gl_driver.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,156 @@ | ||||||
|  | // Copyright 2022 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <glad/glad.h> | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/settings.h" | ||||||
|  | #include "core/telemetry_session.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_driver.h" | ||||||
|  | 
 | ||||||
|  | namespace OpenGL { | ||||||
|  | 
 | ||||||
|  | DECLARE_ENUM_FLAG_OPERATORS(DriverBug); | ||||||
|  | 
 | ||||||
|  | inline std::string_view GetSource(GLenum source) { | ||||||
|  | #define RET(s)                                                                                     \ | ||||||
|  |     case GL_DEBUG_SOURCE_##s:                                                                      \ | ||||||
|  |         return #s | ||||||
|  |     switch (source) { | ||||||
|  |         RET(API); | ||||||
|  |         RET(WINDOW_SYSTEM); | ||||||
|  |         RET(SHADER_COMPILER); | ||||||
|  |         RET(THIRD_PARTY); | ||||||
|  |         RET(APPLICATION); | ||||||
|  |         RET(OTHER); | ||||||
|  |     default: | ||||||
|  |         UNREACHABLE(); | ||||||
|  |     } | ||||||
|  | #undef RET | ||||||
|  | 
 | ||||||
|  |     return std::string_view{}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline std::string_view GetType(GLenum type) { | ||||||
|  | #define RET(t)                                                                                     \ | ||||||
|  |     case GL_DEBUG_TYPE_##t:                                                                        \ | ||||||
|  |         return #t | ||||||
|  |     switch (type) { | ||||||
|  |         RET(ERROR); | ||||||
|  |         RET(DEPRECATED_BEHAVIOR); | ||||||
|  |         RET(UNDEFINED_BEHAVIOR); | ||||||
|  |         RET(PORTABILITY); | ||||||
|  |         RET(PERFORMANCE); | ||||||
|  |         RET(OTHER); | ||||||
|  |         RET(MARKER); | ||||||
|  |     default: | ||||||
|  |         UNREACHABLE(); | ||||||
|  |     } | ||||||
|  | #undef RET | ||||||
|  | 
 | ||||||
|  |     return std::string_view{}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, | ||||||
|  |                                   GLsizei length, const GLchar* message, const void* user_param) { | ||||||
|  |     Log::Level level = Log::Level::Info; | ||||||
|  |     switch (severity) { | ||||||
|  |     case GL_DEBUG_SEVERITY_HIGH: | ||||||
|  |         level = Log::Level::Critical; | ||||||
|  |         break; | ||||||
|  |     case GL_DEBUG_SEVERITY_MEDIUM: | ||||||
|  |         level = Log::Level::Warning; | ||||||
|  |         break; | ||||||
|  |     case GL_DEBUG_SEVERITY_NOTIFICATION: | ||||||
|  |     case GL_DEBUG_SEVERITY_LOW: | ||||||
|  |         level = Log::Level::Debug; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     LOG_GENERIC(Log::Class::Render_OpenGL, level, "{} {} {}: {}", GetSource(source), GetType(type), | ||||||
|  |                 id, message); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Driver::Driver(Core::TelemetrySession& telemetry_session_) : telemetry_session{telemetry_session_} { | ||||||
|  |     const bool enable_debug = Settings::values.renderer_debug.GetValue(); | ||||||
|  |     if (enable_debug) { | ||||||
|  |         glEnable(GL_DEBUG_OUTPUT); | ||||||
|  |         glDebugMessageCallback(DebugHandler, nullptr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ReportDriverInfo(); | ||||||
|  |     DeduceVendor(); | ||||||
|  |     CheckExtensionSupport(); | ||||||
|  |     FindBugs(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Driver::~Driver() = default; | ||||||
|  | 
 | ||||||
|  | bool Driver::HasBug(DriverBug bug) const { | ||||||
|  |     return True(bugs & bug); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Driver::ReportDriverInfo() { | ||||||
|  |     // Report the context version and the vendor string
 | ||||||
|  |     gl_version = std::string_view{reinterpret_cast<const char*>(glGetString(GL_VERSION))}; | ||||||
|  |     gpu_vendor = std::string_view{reinterpret_cast<const char*>(glGetString(GL_VENDOR))}; | ||||||
|  |     gpu_model = std::string_view{reinterpret_cast<const char*>(glGetString(GL_RENDERER))}; | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version); | ||||||
|  |     LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor); | ||||||
|  |     LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model); | ||||||
|  | 
 | ||||||
|  |     // Add the information to the telemetry system
 | ||||||
|  |     constexpr auto user_system = Common::Telemetry::FieldType::UserSystem; | ||||||
|  |     telemetry_session.AddField(user_system, "GPU_Vendor", std::string{gpu_vendor}); | ||||||
|  |     telemetry_session.AddField(user_system, "GPU_Model", std::string{gpu_model}); | ||||||
|  |     telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string{gl_version}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Driver::DeduceVendor() { | ||||||
|  |     if (gpu_vendor.find("NVIDIA") != gpu_vendor.npos) { | ||||||
|  |         vendor = Vendor::Nvidia; | ||||||
|  |     } else if ((gpu_vendor.find("ATI") != gpu_vendor.npos) || | ||||||
|  |                (gpu_vendor.find("AMD") != gpu_vendor.npos) || | ||||||
|  |                (gpu_vendor.find("Advanced Micro Devices") != gpu_vendor.npos)) { | ||||||
|  |         vendor = Vendor::AMD; | ||||||
|  |     } else if (gpu_vendor.find("Intel") != gpu_vendor.npos) { | ||||||
|  |         vendor = Vendor::Intel; | ||||||
|  |     } else if (gpu_vendor.find("ARM") != gpu_vendor.npos) { | ||||||
|  |         vendor = Vendor::ARM; | ||||||
|  |     } else if (gpu_vendor.find("Qualcomm") != gpu_vendor.npos) { | ||||||
|  |         vendor = Vendor::Qualcomm; | ||||||
|  |     } else if (gpu_vendor.find("Samsung") != gpu_vendor.npos) { | ||||||
|  |         vendor = Vendor::Samsung; | ||||||
|  |     } else if (gpu_vendor.find("GDI Generic") != gpu_vendor.npos) { | ||||||
|  |         vendor = Vendor::Generic; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Driver::CheckExtensionSupport() { | ||||||
|  |     ext_buffer_storage = GLAD_GL_EXT_buffer_storage; | ||||||
|  |     arb_buffer_storage = GLAD_GL_ARB_buffer_storage; | ||||||
|  |     arb_clear_texture = GLAD_GL_ARB_clear_texture; | ||||||
|  |     arb_get_texture_sub_image = GLAD_GL_ARB_get_texture_sub_image; | ||||||
|  |     ext_clip_cull_distance = GLAD_GL_EXT_clip_cull_distance; | ||||||
|  |     is_suitable = GLAD_GL_VERSION_4_3 || GLAD_GL_ES_VERSION_3_1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Driver::FindBugs() { | ||||||
|  | #ifdef __unix__ | ||||||
|  |     const bool is_linux = true; | ||||||
|  | #else | ||||||
|  |     const bool is_linux = false; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     // TODO: Check if these have been fixed in the newer driver
 | ||||||
|  |     if (vendor == Vendor::AMD) { | ||||||
|  |         bugs |= DriverBug::ShaderStageChangeFreeze | DriverBug::VertexArrayOutOfBound; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (vendor == Vendor::AMD || (vendor == Vendor::Intel && !is_linux)) { | ||||||
|  |         bugs |= DriverBug::BrokenTextureView; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace OpenGL
 | ||||||
							
								
								
									
										114
									
								
								src/video_core/renderer_opengl/gl_driver.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								src/video_core/renderer_opengl/gl_driver.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,114 @@ | ||||||
|  | // Copyright 2022 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <string_view> | ||||||
|  | #include "common/common_types.h" | ||||||
|  | 
 | ||||||
|  | namespace Core { | ||||||
|  | class TelemetrySession; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace OpenGL { | ||||||
|  | 
 | ||||||
|  | enum class Vendor { | ||||||
|  |     Unknown = 0, | ||||||
|  |     AMD = 1, | ||||||
|  |     Nvidia = 2, | ||||||
|  |     Intel = 3, | ||||||
|  |     ARM = 4, | ||||||
|  |     Qualcomm = 5, | ||||||
|  |     Samsung = 6, | ||||||
|  |     Generic = 7, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class DriverBug { | ||||||
|  |     // AMD drivers sometimes freezes when one shader stage is changed but not the others.
 | ||||||
|  |     ShaderStageChangeFreeze = 1 << 0, | ||||||
|  |     // On AMD drivers there is a strange crash in indexed drawing. The crash happens when the buffer
 | ||||||
|  |     // read position is near the end and is an out-of-bound access to the vertex buffer. This is
 | ||||||
|  |     // probably a bug in the driver and is related to the usage of vec3<byte> attributes in the
 | ||||||
|  |     // vertex array. Doubling the allocation size for the vertex buffer seems to avoid the crash.
 | ||||||
|  |     VertexArrayOutOfBound = 1 << 1, | ||||||
|  |     // On AMD and Intel drivers on Windows glTextureView produces incorrect results
 | ||||||
|  |     BrokenTextureView = 1 << 2, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Utility class that loads the OpenGL function pointers and reports | ||||||
|  |  * information about the graphics device and driver used | ||||||
|  |  */ | ||||||
|  | class Driver { | ||||||
|  | public: | ||||||
|  |     Driver(Core::TelemetrySession& telemetry_session); | ||||||
|  |     ~Driver(); | ||||||
|  | 
 | ||||||
|  |     /// Returns true of the driver has a particular bug stated in the DriverBug enum
 | ||||||
|  |     bool HasBug(DriverBug bug) const; | ||||||
|  | 
 | ||||||
|  |     /// Returns the vendor of the currently selected physical device
 | ||||||
|  |     Vendor GetVendor() const { | ||||||
|  |         return vendor; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns the gpu vendor string returned by the driver
 | ||||||
|  |     std::string_view GetVendorString() const { | ||||||
|  |         return gpu_vendor; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns true if the implementation is suitable for emulation
 | ||||||
|  |     bool IsSuitable() const { | ||||||
|  |         return is_suitable; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns true if the implementation supports ARB_buffer_storage
 | ||||||
|  |     bool HasArbBufferStorage() const { | ||||||
|  |         return arb_buffer_storage; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns true if the implementation supports EXT_buffer_storage
 | ||||||
|  |     bool HasExtBufferStorage() const { | ||||||
|  |         return ext_buffer_storage; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns true if the implementation supports ARB_clear_texture
 | ||||||
|  |     bool HasArbClearTexture() const { | ||||||
|  |         return arb_clear_texture; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns true if the implementation supports ARB_get_texture_sub_image
 | ||||||
|  |     bool HasArbGetTextureSubImage() const { | ||||||
|  |         return arb_get_texture_sub_image; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns true if the implementation supports EXT_clip_cull_distance
 | ||||||
|  |     bool HasExtClipCullDistance() const { | ||||||
|  |         return ext_clip_cull_distance; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void ReportDriverInfo(); | ||||||
|  |     void DeduceVendor(); | ||||||
|  |     void CheckExtensionSupport(); | ||||||
|  |     void FindBugs(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     Core::TelemetrySession& telemetry_session; | ||||||
|  |     Vendor vendor = Vendor::Unknown; | ||||||
|  |     DriverBug bugs{}; | ||||||
|  |     bool is_suitable{}; | ||||||
|  | 
 | ||||||
|  |     bool ext_buffer_storage{}; | ||||||
|  |     bool arb_buffer_storage{}; | ||||||
|  |     bool arb_clear_texture{}; | ||||||
|  |     bool arb_get_texture_sub_image{}; | ||||||
|  |     bool ext_clip_cull_distance{}; | ||||||
|  | 
 | ||||||
|  |     std::string_view gl_version{}; | ||||||
|  |     std::string_view gpu_vendor{}; | ||||||
|  |     std::string_view gpu_model{}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace OpenGL
 | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -3,12 +3,11 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| #include "common/vector_math.h" | 
 | ||||||
| #include "core/hw/gpu.h" | #include "core/hw/gpu.h" | ||||||
| #include "video_core/pica_types.h" | #include "video_core/rasterizer_accelerated.h" | ||||||
| #include "video_core/rasterizer_cache/rasterizer_cache.h" | #include "video_core/rasterizer_cache/rasterizer_cache.h" | ||||||
| #include "video_core/rasterizer_interface.h" | #include "video_core/rasterizer_interface.h" | ||||||
| #include "video_core/regs_lighting.h" |  | ||||||
| #include "video_core/regs_texturing.h" | #include "video_core/regs_texturing.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" | ||||||
|  | @ -20,20 +19,20 @@ class EmuWindow; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
|  | 
 | ||||||
|  | class Driver; | ||||||
| class ShaderProgramManager; | class ShaderProgramManager; | ||||||
| 
 | 
 | ||||||
| class RasterizerOpenGL : public VideoCore::RasterizerInterface { | class RasterizerOpenGL : public VideoCore::RasterizerAccelerated { | ||||||
| public: | public: | ||||||
|     explicit RasterizerOpenGL(Frontend::EmuWindow& emu_window); |     explicit RasterizerOpenGL(Memory::MemorySystem& memory, Frontend::EmuWindow& emu_window, | ||||||
|  |                               Driver& driver); | ||||||
|     ~RasterizerOpenGL() override; |     ~RasterizerOpenGL() override; | ||||||
| 
 | 
 | ||||||
|     void LoadDiskResources(const std::atomic_bool& stop_loading, |     void LoadDiskResources(const std::atomic_bool& stop_loading, | ||||||
|                            const VideoCore::DiskResourceLoadCallback& callback) override; |                            const VideoCore::DiskResourceLoadCallback& callback) override; | ||||||
| 
 | 
 | ||||||
|     void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1, |  | ||||||
|                      const Pica::Shader::OutputVertex& v2) override; |  | ||||||
|     void DrawTriangles() override; |     void DrawTriangles() override; | ||||||
|     void NotifyPicaRegisterChanged(u32 id) override; |  | ||||||
|     void FlushAll() override; |     void FlushAll() override; | ||||||
|     void FlushRegion(PAddr addr, u32 size) override; |     void FlushRegion(PAddr addr, u32 size) override; | ||||||
|     void InvalidateRegion(PAddr addr, u32 size) override; |     void InvalidateRegion(PAddr addr, u32 size) override; | ||||||
|  | @ -46,10 +45,10 @@ public: | ||||||
|                            u32 pixel_stride, ScreenInfo& screen_info) override; |                            u32 pixel_stride, ScreenInfo& screen_info) override; | ||||||
|     bool AccelerateDrawBatch(bool is_indexed) override; |     bool AccelerateDrawBatch(bool is_indexed) override; | ||||||
| 
 | 
 | ||||||
|     /// Syncs entire status to match PICA registers
 |  | ||||||
|     void SyncEntireState() override; |  | ||||||
| 
 |  | ||||||
| private: | private: | ||||||
|  |     void SyncFixedState() override; | ||||||
|  |     void NotifyFixedFunctionPicaRegisterChanged(u32 id) override; | ||||||
|  | 
 | ||||||
|     struct SamplerInfo { |     struct SamplerInfo { | ||||||
|         using TextureConfig = Pica::TexturingRegs::TextureConfig; |         using TextureConfig = Pica::TexturingRegs::TextureConfig; | ||||||
| 
 | 
 | ||||||
|  | @ -76,66 +75,15 @@ private: | ||||||
|         bool supress_mipmap_for_cube = false; |         bool supress_mipmap_for_cube = false; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     /// Structure that the hardware rendered vertices are composed of
 |  | ||||||
|     struct HardwareVertex { |  | ||||||
|         HardwareVertex() = default; |  | ||||||
|         HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) { |  | ||||||
|             position[0] = v.pos.x.ToFloat32(); |  | ||||||
|             position[1] = v.pos.y.ToFloat32(); |  | ||||||
|             position[2] = v.pos.z.ToFloat32(); |  | ||||||
|             position[3] = v.pos.w.ToFloat32(); |  | ||||||
|             color[0] = v.color.x.ToFloat32(); |  | ||||||
|             color[1] = v.color.y.ToFloat32(); |  | ||||||
|             color[2] = v.color.z.ToFloat32(); |  | ||||||
|             color[3] = v.color.w.ToFloat32(); |  | ||||||
|             tex_coord0[0] = v.tc0.x.ToFloat32(); |  | ||||||
|             tex_coord0[1] = v.tc0.y.ToFloat32(); |  | ||||||
|             tex_coord1[0] = v.tc1.x.ToFloat32(); |  | ||||||
|             tex_coord1[1] = v.tc1.y.ToFloat32(); |  | ||||||
|             tex_coord2[0] = v.tc2.x.ToFloat32(); |  | ||||||
|             tex_coord2[1] = v.tc2.y.ToFloat32(); |  | ||||||
|             tex_coord0_w = v.tc0_w.ToFloat32(); |  | ||||||
|             normquat[0] = v.quat.x.ToFloat32(); |  | ||||||
|             normquat[1] = v.quat.y.ToFloat32(); |  | ||||||
|             normquat[2] = v.quat.z.ToFloat32(); |  | ||||||
|             normquat[3] = v.quat.w.ToFloat32(); |  | ||||||
|             view[0] = v.view.x.ToFloat32(); |  | ||||||
|             view[1] = v.view.y.ToFloat32(); |  | ||||||
|             view[2] = v.view.z.ToFloat32(); |  | ||||||
| 
 |  | ||||||
|             if (flip_quaternion) { |  | ||||||
|                 normquat = -normquat; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         Common::Vec4f position; |  | ||||||
|         Common::Vec4f color; |  | ||||||
|         Common::Vec2f tex_coord0; |  | ||||||
|         Common::Vec2f tex_coord1; |  | ||||||
|         Common::Vec2f tex_coord2; |  | ||||||
|         float tex_coord0_w; |  | ||||||
|         Common::Vec4f normquat; |  | ||||||
|         Common::Vec3f view; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     /// Syncs the clip enabled status to match the PICA register
 |     /// Syncs the clip enabled status to match the PICA register
 | ||||||
|     void SyncClipEnabled(); |     void SyncClipEnabled(); | ||||||
| 
 | 
 | ||||||
|     /// Syncs the clip coefficients to match the PICA register
 |  | ||||||
|     void SyncClipCoef(); |  | ||||||
| 
 |  | ||||||
|     /// Sets the OpenGL shader in accordance with the current PICA register state
 |     /// Sets the OpenGL shader in accordance with the current PICA register state
 | ||||||
|     void SetShader(); |     void SetShader(); | ||||||
| 
 | 
 | ||||||
|     /// Syncs the cull mode to match the PICA register
 |     /// Syncs the cull mode to match the PICA register
 | ||||||
|     void SyncCullMode(); |     void SyncCullMode(); | ||||||
| 
 | 
 | ||||||
|     /// Syncs the depth scale to match the PICA register
 |  | ||||||
|     void SyncDepthScale(); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the depth offset to match the PICA register
 |  | ||||||
|     void SyncDepthOffset(); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the blend enabled status to match the PICA register
 |     /// Syncs the blend enabled status to match the PICA register
 | ||||||
|     void SyncBlendEnabled(); |     void SyncBlendEnabled(); | ||||||
| 
 | 
 | ||||||
|  | @ -145,18 +93,6 @@ private: | ||||||
|     /// Syncs the blend color to match the PICA register
 |     /// Syncs the blend color to match the PICA register
 | ||||||
|     void SyncBlendColor(); |     void SyncBlendColor(); | ||||||
| 
 | 
 | ||||||
|     /// Syncs the fog states to match the PICA register
 |  | ||||||
|     void SyncFogColor(); |  | ||||||
| 
 |  | ||||||
|     /// Sync the procedural texture noise configuration to match the PICA register
 |  | ||||||
|     void SyncProcTexNoise(); |  | ||||||
| 
 |  | ||||||
|     /// Sync the procedural texture bias configuration to match the PICA register
 |  | ||||||
|     void SyncProcTexBias(); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the alpha test states to match the PICA register
 |  | ||||||
|     void SyncAlphaTest(); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the logic op states to match the PICA register
 |     /// Syncs the logic op states to match the PICA register
 | ||||||
|     void SyncLogicOp(); |     void SyncLogicOp(); | ||||||
| 
 | 
 | ||||||
|  | @ -175,46 +111,6 @@ private: | ||||||
|     /// Syncs the depth test states to match the PICA register
 |     /// Syncs the depth test states to match the PICA register
 | ||||||
|     void SyncDepthTest(); |     void SyncDepthTest(); | ||||||
| 
 | 
 | ||||||
|     /// Syncs the TEV combiner color buffer to match the PICA register
 |  | ||||||
|     void SyncCombinerColor(); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the TEV constant color to match the PICA register
 |  | ||||||
|     void SyncTevConstColor(std::size_t tev_index, |  | ||||||
|                            const Pica::TexturingRegs::TevStageConfig& tev_stage); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the lighting global ambient color to match the PICA register
 |  | ||||||
|     void SyncGlobalAmbient(); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the specified light's specular 0 color to match the PICA register
 |  | ||||||
|     void SyncLightSpecular0(int light_index); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the specified light's specular 1 color to match the PICA register
 |  | ||||||
|     void SyncLightSpecular1(int light_index); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the specified light's diffuse color to match the PICA register
 |  | ||||||
|     void SyncLightDiffuse(int light_index); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the specified light's ambient color to match the PICA register
 |  | ||||||
|     void SyncLightAmbient(int light_index); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the specified light's position to match the PICA register
 |  | ||||||
|     void SyncLightPosition(int light_index); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the specified spot light direcition to match the PICA register
 |  | ||||||
|     void SyncLightSpotDirection(int light_index); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the specified light's distance attenuation bias to match the PICA register
 |  | ||||||
|     void SyncLightDistanceAttenuationBias(int light_index); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the specified light's distance attenuation scale to match the PICA register
 |  | ||||||
|     void SyncLightDistanceAttenuationScale(int light_index); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the shadow rendering bias to match the PICA register
 |  | ||||||
|     void SyncShadowBias(); |  | ||||||
| 
 |  | ||||||
|     /// Syncs the shadow texture bias to match the PICA register
 |  | ||||||
|     void SyncShadowTextureBias(); |  | ||||||
| 
 |  | ||||||
|     /// Syncs and uploads the lighting, fog and proctex LUTs
 |     /// Syncs and uploads the lighting, fog and proctex LUTs
 | ||||||
|     void SyncAndUploadLUTs(); |     void SyncAndUploadLUTs(); | ||||||
|     void SyncAndUploadLUTsLF(); |     void SyncAndUploadLUTsLF(); | ||||||
|  | @ -228,15 +124,6 @@ private: | ||||||
|     /// Internal implementation for AccelerateDrawBatch
 |     /// Internal implementation for AccelerateDrawBatch
 | ||||||
|     bool AccelerateDrawBatchInternal(bool is_indexed); |     bool AccelerateDrawBatchInternal(bool is_indexed); | ||||||
| 
 | 
 | ||||||
|     struct VertexArrayInfo { |  | ||||||
|         u32 vs_input_index_min; |  | ||||||
|         u32 vs_input_index_max; |  | ||||||
|         u32 vs_input_size; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     /// Retrieve the range and the size of the input vertex
 |  | ||||||
|     VertexArrayInfo AnalyzeVertexArray(bool is_indexed); |  | ||||||
| 
 |  | ||||||
|     /// Setup vertex array for AccelerateDrawBatch
 |     /// Setup vertex array for AccelerateDrawBatch
 | ||||||
|     void SetupVertexArray(u8* array_ptr, GLintptr buffer_offset, GLuint vs_input_index_min, |     void SetupVertexArray(u8* array_ptr, GLintptr buffer_offset, GLuint vs_input_index_min, | ||||||
|                           GLuint vs_input_index_max); |                           GLuint vs_input_index_max); | ||||||
|  | @ -247,38 +134,13 @@ private: | ||||||
|     /// Setup geometry shader for AccelerateDrawBatch
 |     /// Setup geometry shader for AccelerateDrawBatch
 | ||||||
|     bool SetupGeometryShader(); |     bool SetupGeometryShader(); | ||||||
| 
 | 
 | ||||||
|     bool is_amd; | private: | ||||||
| 
 |     Driver& driver; | ||||||
|     OpenGLState state; |     OpenGLState state; | ||||||
|     GLuint default_texture; |     GLuint default_texture; | ||||||
| 
 |  | ||||||
|     RasterizerCacheOpenGL res_cache; |     RasterizerCacheOpenGL res_cache; | ||||||
| 
 |  | ||||||
|     std::vector<HardwareVertex> vertex_batch; |  | ||||||
| 
 |  | ||||||
|     bool shader_dirty = true; |  | ||||||
| 
 |  | ||||||
|     struct { |  | ||||||
|         UniformData data; |  | ||||||
|         std::array<bool, Pica::LightingRegs::NumLightingSampler> lighting_lut_dirty; |  | ||||||
|         bool lighting_lut_dirty_any; |  | ||||||
|         bool fog_lut_dirty; |  | ||||||
|         bool proctex_noise_lut_dirty; |  | ||||||
|         bool proctex_color_map_dirty; |  | ||||||
|         bool proctex_alpha_map_dirty; |  | ||||||
|         bool proctex_lut_dirty; |  | ||||||
|         bool proctex_diff_lut_dirty; |  | ||||||
|         bool dirty; |  | ||||||
|     } uniform_block_data = {}; |  | ||||||
| 
 |  | ||||||
|     std::unique_ptr<ShaderProgramManager> shader_program_manager; |     std::unique_ptr<ShaderProgramManager> shader_program_manager; | ||||||
| 
 | 
 | ||||||
|     // They shall be big enough for about one frame.
 |  | ||||||
|     static constexpr std::size_t VERTEX_BUFFER_SIZE = 16 * 1024 * 1024; |  | ||||||
|     static constexpr std::size_t INDEX_BUFFER_SIZE = 1 * 1024 * 1024; |  | ||||||
|     static constexpr std::size_t UNIFORM_BUFFER_SIZE = 2 * 1024 * 1024; |  | ||||||
|     static constexpr std::size_t TEXTURE_BUFFER_SIZE = 1 * 1024 * 1024; |  | ||||||
| 
 |  | ||||||
|     OGLVertexArray sw_vao; // VAO for software shader draw
 |     OGLVertexArray sw_vao; // VAO for software shader draw
 | ||||||
|     OGLVertexArray hw_vao; // VAO for hardware shader / accelerate draw
 |     OGLVertexArray hw_vao; // VAO for hardware shader / accelerate draw
 | ||||||
|     std::array<bool, 16> hw_vao_enabled_attributes{}; |     std::array<bool, 16> hw_vao_enabled_attributes{}; | ||||||
|  | @ -299,15 +161,6 @@ private: | ||||||
|     OGLTexture texture_buffer_lut_lf; |     OGLTexture texture_buffer_lut_lf; | ||||||
|     OGLTexture texture_buffer_lut_rg; |     OGLTexture texture_buffer_lut_rg; | ||||||
|     OGLTexture texture_buffer_lut_rgba; |     OGLTexture texture_buffer_lut_rgba; | ||||||
| 
 |  | ||||||
|     std::array<std::array<Common::Vec2f, 256>, Pica::LightingRegs::NumLightingSampler> |  | ||||||
|         lighting_lut_data{}; |  | ||||||
|     std::array<Common::Vec2f, 128> fog_lut_data{}; |  | ||||||
|     std::array<Common::Vec2f, 128> proctex_noise_lut_data{}; |  | ||||||
|     std::array<Common::Vec2f, 128> proctex_color_map_data{}; |  | ||||||
|     std::array<Common::Vec2f, 128> proctex_alpha_map_data{}; |  | ||||||
|     std::array<Common::Vec4f, 256> proctex_lut_data{}; |  | ||||||
|     std::array<Common::Vec4f, 256> proctex_diff_lut_data{}; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace OpenGL
 | } // namespace OpenGL
 | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
| #include "video_core/renderer_opengl/gl_shader_gen.h" | #include "video_core/renderer_opengl/gl_shader_gen.h" | ||||||
| #include "video_core/renderer_opengl/gl_shader_util.h" | #include "video_core/renderer_opengl/gl_shader_util.h" | ||||||
| #include "video_core/renderer_opengl/gl_vars.h" | #include "video_core/renderer_opengl/gl_vars.h" | ||||||
|  | #include "video_core/shader/shader_uniforms.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
| using Pica::FramebufferRegs; | using Pica::FramebufferRegs; | ||||||
|  | @ -23,53 +24,7 @@ using VSOutputAttributes = RasterizerRegs::VSOutputAttributes; | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
| constexpr std::string_view UniformBlockDef = R"( | const std::string UniformBlockDef = Pica::Shader::BuildShaderUniformDefinitions(); | ||||||
| #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; |  | ||||||
|     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; |  | ||||||
|     vec4 clip_coef; |  | ||||||
| }; |  | ||||||
| )"; |  | ||||||
| 
 | 
 | ||||||
| static std::string GetVertexInterfaceDeclaration(bool is_output, bool separable_shader) { | static std::string GetVertexInterfaceDeclaration(bool is_output, bool separable_shader) { | ||||||
|     std::string out; |     std::string out; | ||||||
|  |  | ||||||
|  | @ -6,13 +6,13 @@ | ||||||
| #include <set> | #include <set> | ||||||
| #include <thread> | #include <thread> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <boost/variant.hpp> | #include <variant> | ||||||
| #include "core/frontend/scope_acquire_context.h" | #include "video_core/renderer_opengl/gl_driver.h" | ||||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||||
| #include "video_core/renderer_opengl/gl_shader_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/renderer_opengl/gl_vars.h" | #include "video_core/shader/shader_uniforms.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
|  | @ -85,7 +85,8 @@ static std::tuple<PicaVSConfig, Pica::Shader::ShaderSetup> BuildVSConfigFromRaw( | ||||||
|     return {PicaVSConfig{raw.GetRawShaderConfig().vs, setup}, setup}; |     return {PicaVSConfig{raw.GetRawShaderConfig().vs, setup}, setup}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void SetShaderUniformBlockBinding(GLuint shader, const char* name, UniformBindings binding, | static void SetShaderUniformBlockBinding(GLuint shader, const char* name, | ||||||
|  |                                          Pica::Shader::UniformBindings binding, | ||||||
|                                          std::size_t expected_size) { |                                          std::size_t expected_size) { | ||||||
|     const GLuint ub_index = glGetUniformBlockIndex(shader, name); |     const GLuint ub_index = glGetUniformBlockIndex(shader, name); | ||||||
|     if (ub_index == GL_INVALID_INDEX) { |     if (ub_index == GL_INVALID_INDEX) { | ||||||
|  | @ -100,9 +101,10 @@ static void SetShaderUniformBlockBinding(GLuint shader, const char* name, Unifor | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void SetShaderUniformBlockBindings(GLuint shader) { | static void SetShaderUniformBlockBindings(GLuint shader) { | ||||||
|     SetShaderUniformBlockBinding(shader, "shader_data", UniformBindings::Common, |     SetShaderUniformBlockBinding(shader, "shader_data", Pica::Shader::UniformBindings::Common, | ||||||
|                                  sizeof(UniformData)); |                                  sizeof(Pica::Shader::UniformData)); | ||||||
|     SetShaderUniformBlockBinding(shader, "vs_config", UniformBindings::VS, sizeof(VSUniformData)); |     SetShaderUniformBlockBinding(shader, "vs_config", Pica::Shader::UniformBindings::VS, | ||||||
|  |                                  sizeof(Pica::Shader::VSUniformData)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void SetShaderSamplerBinding(GLuint shader, const char* name, | static void SetShaderSamplerBinding(GLuint shader, const char* name, | ||||||
|  | @ -148,21 +150,6 @@ static void SetShaderSamplerBindings(GLuint shader) { | ||||||
|     cur_state.Apply(); |     cur_state.Apply(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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 ? GL_TRUE : GL_FALSE}; }); |  | ||||||
|     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()}; |  | ||||||
|                    }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * An object representing a shader program staging. It can be either a shader object or a program |  * An object representing a shader program staging. It can be either a shader object or a program | ||||||
|  * object, depending on whether separable program is used. |  * object, depending on whether separable program is used. | ||||||
|  | @ -178,12 +165,12 @@ public: | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void Create(const char* source, GLenum type) { |     void Create(const char* source, GLenum type) { | ||||||
|         if (shader_or_program.which() == 0) { |         if (shader_or_program.index() == 0) { | ||||||
|             boost::get<OGLShader>(shader_or_program).Create(source, type); |             std::get<OGLShader>(shader_or_program).Create(source, type); | ||||||
|         } else { |         } else { | ||||||
|             OGLShader shader; |             OGLShader shader; | ||||||
|             shader.Create(source, type); |             shader.Create(source, type); | ||||||
|             OGLProgram& program = boost::get<OGLProgram>(shader_or_program); |             OGLProgram& program = std::get<OGLProgram>(shader_or_program); | ||||||
|             program.Create(true, {shader.handle}); |             program.Create(true, {shader.handle}); | ||||||
|             SetShaderUniformBlockBindings(program.handle); |             SetShaderUniformBlockBindings(program.handle); | ||||||
| 
 | 
 | ||||||
|  | @ -194,10 +181,10 @@ public: | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     GLuint GetHandle() const { |     GLuint GetHandle() const { | ||||||
|         if (shader_or_program.which() == 0) { |         if (shader_or_program.index() == 0) { | ||||||
|             return boost::get<OGLShader>(shader_or_program).handle; |             return std::get<OGLShader>(shader_or_program).handle; | ||||||
|         } else { |         } else { | ||||||
|             return boost::get<OGLProgram>(shader_or_program).handle; |             return std::get<OGLProgram>(shader_or_program).handle; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -208,7 +195,7 @@ public: | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     boost::variant<OGLShader, OGLProgram> shader_or_program; |     std::variant<OGLShader, OGLProgram> shader_or_program; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class TrivialVertexShader { | class TrivialVertexShader { | ||||||
|  | @ -329,8 +316,8 @@ using FragmentShaders = ShaderCache<PicaFSConfig, &GenerateFragmentShader, GL_FR | ||||||
| 
 | 
 | ||||||
| class ShaderProgramManager::Impl { | class ShaderProgramManager::Impl { | ||||||
| public: | public: | ||||||
|     explicit Impl(bool separable, bool is_amd) |     explicit Impl(bool separable) | ||||||
|         : is_amd(is_amd), separable(separable), programmable_vertex_shaders(separable), |         : separable(separable), programmable_vertex_shaders(separable), | ||||||
|           trivial_vertex_shader(separable), fixed_geometry_shaders(separable), |           trivial_vertex_shader(separable), fixed_geometry_shaders(separable), | ||||||
|           fragment_shaders(separable), disk_cache(separable) { |           fragment_shaders(separable), disk_cache(separable) { | ||||||
|         if (separable) |         if (separable) | ||||||
|  | @ -363,7 +350,6 @@ public: | ||||||
|     static_assert(offsetof(ShaderTuple, fs_hash) == sizeof(std::size_t) * 2, |     static_assert(offsetof(ShaderTuple, fs_hash) == sizeof(std::size_t) * 2, | ||||||
|                   "ShaderTuple layout changed!"); |                   "ShaderTuple layout changed!"); | ||||||
| 
 | 
 | ||||||
|     bool is_amd; |  | ||||||
|     bool separable; |     bool separable; | ||||||
| 
 | 
 | ||||||
|     ShaderTuple current; |     ShaderTuple current; | ||||||
|  | @ -379,9 +365,9 @@ public: | ||||||
|     ShaderDiskCache disk_cache; |     ShaderDiskCache disk_cache; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, bool separable, | ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, const Driver& driver_, | ||||||
|                                            bool is_amd) |                                            bool separable) | ||||||
|     : impl(std::make_unique<Impl>(separable, is_amd)), emu_window{emu_window_} {} |     : impl(std::make_unique<Impl>(separable)), emu_window{emu_window_}, driver{driver_} {} | ||||||
| 
 | 
 | ||||||
| ShaderProgramManager::~ShaderProgramManager() = default; | ShaderProgramManager::~ShaderProgramManager() = default; | ||||||
| 
 | 
 | ||||||
|  | @ -443,10 +429,7 @@ void ShaderProgramManager::UseFragmentShader(const Pica::Regs& regs) { | ||||||
| 
 | 
 | ||||||
| void ShaderProgramManager::ApplyTo(OpenGLState& state) { | void ShaderProgramManager::ApplyTo(OpenGLState& state) { | ||||||
|     if (impl->separable) { |     if (impl->separable) { | ||||||
|         if (impl->is_amd) { |         if (driver.HasBug(DriverBug::ShaderStageChangeFreeze)) { | ||||||
|             // Without this reseting, AMD sometimes freezes when one stage is changed but not
 |  | ||||||
|             // for the others. On the other hand, including this reset seems to introduce memory
 |  | ||||||
|             // leak in Intel Graphics.
 |  | ||||||
|             glUseProgramStages( |             glUseProgramStages( | ||||||
|                 impl->pipeline.handle, |                 impl->pipeline.handle, | ||||||
|                 GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, 0); |                 GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, 0); | ||||||
|  | @ -641,7 +624,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, | ||||||
|     std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex
 |     std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex
 | ||||||
|     const auto LoadRawSepareble = [&](Frontend::GraphicsContext* context, std::size_t begin, |     const auto LoadRawSepareble = [&](Frontend::GraphicsContext* context, std::size_t begin, | ||||||
|                                       std::size_t end) { |                                       std::size_t end) { | ||||||
|         Frontend::ScopeAcquireContext scope(*context); |         const auto scope = context->Acquire(); | ||||||
|         for (std::size_t i = begin; i < end; ++i) { |         for (std::size_t i = begin; i < end; ++i) { | ||||||
|             if (stop_loading || compilation_failed) { |             if (stop_loading || compilation_failed) { | ||||||
|                 return; |                 return; | ||||||
|  |  | ||||||
|  | @ -5,13 +5,7 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| #include "common/vector_math.h" |  | ||||||
| #include "video_core/rasterizer_interface.h" | #include "video_core/rasterizer_interface.h" | ||||||
| #include "video_core/regs_lighting.h" |  | ||||||
| 
 |  | ||||||
| namespace Core { |  | ||||||
| class System; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| namespace Frontend { | namespace Frontend { | ||||||
| class EmuWindow; | class EmuWindow; | ||||||
|  | @ -19,8 +13,7 @@ class EmuWindow; | ||||||
| 
 | 
 | ||||||
| namespace Pica { | namespace Pica { | ||||||
| struct Regs; | struct Regs; | ||||||
| struct ShaderRegs; | } | ||||||
| } // namespace Pica
 |  | ||||||
| 
 | 
 | ||||||
| namespace Pica::Shader { | namespace Pica::Shader { | ||||||
| struct ShaderSetup; | struct ShaderSetup; | ||||||
|  | @ -28,87 +21,13 @@ struct ShaderSetup; | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
| enum class UniformBindings : u32 { Common, VS, GS }; | class Driver; | ||||||
| 
 |  | ||||||
| struct LightSrc { |  | ||||||
|     alignas(16) Common::Vec3f specular_0; |  | ||||||
|     alignas(16) Common::Vec3f specular_1; |  | ||||||
|     alignas(16) Common::Vec3f diffuse; |  | ||||||
|     alignas(16) Common::Vec3f ambient; |  | ||||||
|     alignas(16) Common::Vec3f position; |  | ||||||
|     alignas(16) Common::Vec3f spot_direction; // negated
 |  | ||||||
|     float dist_atten_bias; |  | ||||||
|     float dist_atten_scale; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned
 |  | ||||||
| // NOTE: Always keep a vec4 at the end. The GL spec is not clear wether the alignment at
 |  | ||||||
| //       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.
 |  | ||||||
| struct UniformData { |  | ||||||
|     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; |  | ||||||
|     alignas(16) Common::Vec4i lighting_lut_offset[Pica::LightingRegs::NumLightingSampler / 4]; |  | ||||||
|     alignas(16) Common::Vec3f fog_color; |  | ||||||
|     alignas(8) Common::Vec2f proctex_noise_f; |  | ||||||
|     alignas(8) Common::Vec2f proctex_noise_a; |  | ||||||
|     alignas(8) Common::Vec2f proctex_noise_p; |  | ||||||
|     alignas(16) Common::Vec3f lighting_global_ambient; |  | ||||||
|     LightSrc light_src[8]; |  | ||||||
|     alignas(16) Common::Vec4f const_color[6]; // A vec4 color for each of the six tev stages
 |  | ||||||
|     alignas(16) Common::Vec4f tev_combiner_buffer_color; |  | ||||||
|     alignas(16) Common::Vec4f clip_coef; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static_assert(sizeof(UniformData) == 0x4F0, |  | ||||||
|               "The size of the UniformData does not match the structure in the shader"); |  | ||||||
| static_assert(sizeof(UniformData) < 16384, |  | ||||||
|               "UniformData structure must be less than 16kb as per the OpenGL spec"); |  | ||||||
| 
 |  | ||||||
| /// Uniform struct for the Uniform Buffer Object that contains PICA vertex/geometry shader uniforms.
 |  | ||||||
| // NOTE: the same rule from UniformData also applies here.
 |  | ||||||
| struct PicaUniformsData { |  | ||||||
|     void SetFromRegs(const Pica::ShaderRegs& regs, const Pica::Shader::ShaderSetup& setup); |  | ||||||
| 
 |  | ||||||
|     struct BoolAligned { |  | ||||||
|         alignas(16) int b; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     std::array<BoolAligned, 16> bools; |  | ||||||
|     alignas(16) std::array<Common::Vec4u, 4> i; |  | ||||||
|     alignas(16) std::array<Common::Vec4f, 96> f; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct VSUniformData { |  | ||||||
|     PicaUniformsData uniforms; |  | ||||||
| }; |  | ||||||
| static_assert(sizeof(VSUniformData) == 1856, |  | ||||||
|               "The size of the VSUniformData does not match the structure in the shader"); |  | ||||||
| static_assert(sizeof(VSUniformData) < 16384, |  | ||||||
|               "VSUniformData structure must be less than 16kb as per the OpenGL spec"); |  | ||||||
| 
 |  | ||||||
| class OpenGLState; | class OpenGLState; | ||||||
| 
 | 
 | ||||||
| /// 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: | ||||||
|     ShaderProgramManager(Frontend::EmuWindow& emu_window_, bool separable, bool is_amd); |     ShaderProgramManager(Frontend::EmuWindow& emu_window, const Driver& driver, bool separable); | ||||||
|     ~ShaderProgramManager(); |     ~ShaderProgramManager(); | ||||||
| 
 | 
 | ||||||
|     void LoadDiskCache(const std::atomic_bool& stop_loading, |     void LoadDiskCache(const std::atomic_bool& stop_loading, | ||||||
|  | @ -131,5 +50,6 @@ private: | ||||||
|     std::unique_ptr<Impl> impl; |     std::unique_ptr<Impl> impl; | ||||||
| 
 | 
 | ||||||
|     Frontend::EmuWindow& emu_window; |     Frontend::EmuWindow& emu_window; | ||||||
|  |     const Driver& driver; | ||||||
| }; | }; | ||||||
| } // namespace OpenGL
 | } // namespace OpenGL
 | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| #include "common/alignment.h" | #include "common/alignment.h" | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/microprofile.h" | #include "common/microprofile.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_driver.h" | ||||||
| #include "video_core/renderer_opengl/gl_stream_buffer.h" | #include "video_core/renderer_opengl/gl_stream_buffer.h" | ||||||
| 
 | 
 | ||||||
| MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", | MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", | ||||||
|  | @ -12,19 +13,14 @@ MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
| OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool array_buffer_for_amd, | OGLStreamBuffer::OGLStreamBuffer(Driver& driver, GLenum target, GLsizeiptr size, | ||||||
|                                  bool prefer_coherent) |                                  bool prefer_coherent) | ||||||
|     : gl_target(target), buffer_size(size) { |     : gl_target(target), buffer_size(size) { | ||||||
|     gl_buffer.Create(); |     gl_buffer.Create(); | ||||||
|     glBindBuffer(gl_target, gl_buffer.handle); |     glBindBuffer(gl_target, gl_buffer.handle); | ||||||
| 
 | 
 | ||||||
|     GLsizeiptr allocate_size = size; |     GLsizeiptr allocate_size = size; | ||||||
|     if (array_buffer_for_amd) { |     if (driver.HasBug(DriverBug::VertexArrayOutOfBound) && target == GL_ARRAY_BUFFER) { | ||||||
|         // On AMD GPU there is a strange crash in indexed drawing. The crash happens when the buffer
 |  | ||||||
|         // read position is near the end and is an out-of-bound access to the vertex buffer. This is
 |  | ||||||
|         // probably a bug in the driver and is related to the usage of vec3<byte> attributes in the
 |  | ||||||
|         // vertex array. Doubling the allocation size for the vertex buffer seems to avoid the
 |  | ||||||
|         // crash.
 |  | ||||||
|         allocate_size *= 2; |         allocate_size *= 2; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,14 +3,17 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
|  | 
 | ||||||
| #include <tuple> | #include <tuple> | ||||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenGL { | namespace OpenGL { | ||||||
| 
 | 
 | ||||||
|  | class Driver; | ||||||
|  | 
 | ||||||
| class OGLStreamBuffer : private NonCopyable { | class OGLStreamBuffer : private NonCopyable { | ||||||
| public: | public: | ||||||
|     explicit OGLStreamBuffer(GLenum target, GLsizeiptr size, bool array_buffer_for_amd, |     explicit OGLStreamBuffer(Driver& driver, GLenum target, GLsizeiptr size, | ||||||
|                              bool prefer_coherent = false); |                              bool prefer_coherent = false); | ||||||
|     ~OGLStreamBuffer(); |     ~OGLStreamBuffer(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,8 +13,6 @@ | ||||||
| #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 "core/tracer/recorder.h" |  | ||||||
| #include "video_core/debug_utils/debug_utils.h" |  | ||||||
| #include "video_core/rasterizer_interface.h" | #include "video_core/rasterizer_interface.h" | ||||||
| #include "video_core/renderer_opengl/gl_shader_util.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" | ||||||
|  | @ -352,14 +350,17 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons | ||||||
|     return matrix; |     return matrix; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RendererOpenGL::RendererOpenGL(Frontend::EmuWindow& window, Frontend::EmuWindow* secondary_window) | RendererOpenGL::RendererOpenGL(Core::System& system, Frontend::EmuWindow& window, | ||||||
|     : RendererBase{window, secondary_window}, |                                Frontend::EmuWindow* secondary_window) | ||||||
|       frame_dumper(Core::System::GetInstance().VideoDumper(), window) { |     : VideoCore::RendererBase{system, window, secondary_window}, driver{system.TelemetrySession()}, | ||||||
|  |       frame_dumper{system.VideoDumper(), window} { | ||||||
|     window.mailbox = std::make_unique<OGLTextureMailbox>(); |     window.mailbox = std::make_unique<OGLTextureMailbox>(); | ||||||
|     if (secondary_window) { |     if (secondary_window) { | ||||||
|         secondary_window->mailbox = std::make_unique<OGLTextureMailbox>(); |         secondary_window->mailbox = std::make_unique<OGLTextureMailbox>(); | ||||||
|     } |     } | ||||||
|     frame_dumper.mailbox = std::make_unique<OGLVideoDumpingMailbox>(); |     frame_dumper.mailbox = std::make_unique<OGLVideoDumpingMailbox>(); | ||||||
|  |     InitOpenGLObjects(); | ||||||
|  |     rasterizer = std::make_unique<RasterizerOpenGL>(system.Memory(), render_window, driver); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RendererOpenGL::~RendererOpenGL() = default; | RendererOpenGL::~RendererOpenGL() = default; | ||||||
|  | @ -374,7 +375,6 @@ void RendererOpenGL::SwapBuffers() { | ||||||
|     state.Apply(); |     state.Apply(); | ||||||
| 
 | 
 | ||||||
|     PrepareRendertarget(); |     PrepareRendertarget(); | ||||||
| 
 |  | ||||||
|     RenderScreenshot(); |     RenderScreenshot(); | ||||||
| 
 | 
 | ||||||
|     const auto& main_layout = render_window.GetFramebufferLayout(); |     const auto& main_layout = render_window.GetFramebufferLayout(); | ||||||
|  | @ -396,26 +396,12 @@ void RendererOpenGL::SwapBuffers() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     m_current_frame++; |     EndFrame(); | ||||||
| 
 |  | ||||||
|     Core::System::GetInstance().perf_stats->EndSystemFrame(); |  | ||||||
| 
 |  | ||||||
|     render_window.PollEvents(); |  | ||||||
| 
 |  | ||||||
|     Core::System::GetInstance().frame_limiter.DoFrameLimiting( |  | ||||||
|         Core::System::GetInstance().CoreTiming().GetGlobalTimeUs()); |  | ||||||
|     Core::System::GetInstance().perf_stats->BeginSystemFrame(); |  | ||||||
| 
 |  | ||||||
|     prev_state.Apply(); |     prev_state.Apply(); | ||||||
|     RefreshRasterizerSetting(); |  | ||||||
| 
 |  | ||||||
|     if (Pica::g_debug_context && Pica::g_debug_context->recorder) { |  | ||||||
|         Pica::g_debug_context->recorder->FrameFinished(); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RendererOpenGL::RenderScreenshot() { | void RendererOpenGL::RenderScreenshot() { | ||||||
|     if (VideoCore::g_renderer_screenshot_requested) { |     if (renderer_settings.screenshot_requested.exchange(false)) { | ||||||
|         // Draw this frame to the screenshot framebuffer
 |         // Draw this frame to the screenshot framebuffer
 | ||||||
|         screenshot_framebuffer.Create(); |         screenshot_framebuffer.Create(); | ||||||
|         GLuint old_read_fb = state.draw.read_framebuffer; |         GLuint old_read_fb = state.draw.read_framebuffer; | ||||||
|  | @ -423,7 +409,7 @@ void RendererOpenGL::RenderScreenshot() { | ||||||
|         state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle; |         state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle; | ||||||
|         state.Apply(); |         state.Apply(); | ||||||
| 
 | 
 | ||||||
|         Layout::FramebufferLayout layout{VideoCore::g_screenshot_framebuffer_layout}; |         const auto layout{renderer_settings.screenshot_framebuffer_layout}; | ||||||
| 
 | 
 | ||||||
|         GLuint renderbuffer; |         GLuint renderbuffer; | ||||||
|         glGenRenderbuffers(1, &renderbuffer); |         glGenRenderbuffers(1, &renderbuffer); | ||||||
|  | @ -435,7 +421,7 @@ void RendererOpenGL::RenderScreenshot() { | ||||||
|         DrawScreens(layout, false); |         DrawScreens(layout, false); | ||||||
| 
 | 
 | ||||||
|         glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, |         glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, | ||||||
|                      VideoCore::g_screenshot_bits); |                      renderer_settings.screenshot_bits); | ||||||
| 
 | 
 | ||||||
|         screenshot_framebuffer.Release(); |         screenshot_framebuffer.Release(); | ||||||
|         state.draw.read_framebuffer = old_read_fb; |         state.draw.read_framebuffer = old_read_fb; | ||||||
|  | @ -443,8 +429,7 @@ void RendererOpenGL::RenderScreenshot() { | ||||||
|         state.Apply(); |         state.Apply(); | ||||||
|         glDeleteRenderbuffers(1, &renderbuffer); |         glDeleteRenderbuffers(1, &renderbuffer); | ||||||
| 
 | 
 | ||||||
|         VideoCore::g_screenshot_complete_callback(); |         renderer_settings.screenshot_complete_callback(); | ||||||
|         VideoCore::g_renderer_screenshot_requested = false; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1226,109 +1211,8 @@ void RendererOpenGL::CleanupVideoDumping() { | ||||||
|     mailbox->free_cv.notify_one(); |     mailbox->free_cv.notify_one(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const char* GetSource(GLenum source) { | void RendererOpenGL::Sync() { | ||||||
| #define RET(s)                                                                                     \ |     rasterizer->SyncEntireState(); | ||||||
|     case GL_DEBUG_SOURCE_##s:                                                                      \ |  | ||||||
|         return #s |  | ||||||
|     switch (source) { |  | ||||||
|         RET(API); |  | ||||||
|         RET(WINDOW_SYSTEM); |  | ||||||
|         RET(SHADER_COMPILER); |  | ||||||
|         RET(THIRD_PARTY); |  | ||||||
|         RET(APPLICATION); |  | ||||||
|         RET(OTHER); |  | ||||||
|     default: |  | ||||||
|         UNREACHABLE(); |  | ||||||
|     } |  | ||||||
| #undef RET |  | ||||||
| 
 |  | ||||||
|     return ""; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const char* GetType(GLenum type) { |  | ||||||
| #define RET(t)                                                                                     \ |  | ||||||
|     case GL_DEBUG_TYPE_##t:                                                                        \ |  | ||||||
|         return #t |  | ||||||
|     switch (type) { |  | ||||||
|         RET(ERROR); |  | ||||||
|         RET(DEPRECATED_BEHAVIOR); |  | ||||||
|         RET(UNDEFINED_BEHAVIOR); |  | ||||||
|         RET(PORTABILITY); |  | ||||||
|         RET(PERFORMANCE); |  | ||||||
|         RET(OTHER); |  | ||||||
|         RET(MARKER); |  | ||||||
|     default: |  | ||||||
|         UNREACHABLE(); |  | ||||||
|     } |  | ||||||
| #undef RET |  | ||||||
| 
 |  | ||||||
|     return ""; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, |  | ||||||
|                                   GLsizei length, const GLchar* message, const void* user_param) { |  | ||||||
|     Log::Level level; |  | ||||||
|     switch (severity) { |  | ||||||
|     case GL_DEBUG_SEVERITY_HIGH: |  | ||||||
|         level = Log::Level::Critical; |  | ||||||
|         break; |  | ||||||
|     case GL_DEBUG_SEVERITY_MEDIUM: |  | ||||||
|         level = Log::Level::Warning; |  | ||||||
|         break; |  | ||||||
|     case GL_DEBUG_SEVERITY_NOTIFICATION: |  | ||||||
|     case GL_DEBUG_SEVERITY_LOW: |  | ||||||
|         level = Log::Level::Debug; |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     LOG_GENERIC(Log::Class::Render_OpenGL, level, "{} {} {}: {}", GetSource(source), GetType(type), |  | ||||||
|                 id, message); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Initialize the renderer
 |  | ||||||
| VideoCore::ResultStatus RendererOpenGL::Init() { |  | ||||||
| #ifndef ANDROID |  | ||||||
|     if (!gladLoadGL()) { |  | ||||||
|         return VideoCore::ResultStatus::ErrorBelowGL43; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Qualcomm has some spammy info messages that are marked as errors but not important
 |  | ||||||
|     // https://developer.qualcomm.com/comment/11845
 |  | ||||||
|     if (GLAD_GL_KHR_debug) { |  | ||||||
|         glEnable(GL_DEBUG_OUTPUT); |  | ||||||
|         glDebugMessageCallback(DebugHandler, nullptr); |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|     const std::string_view gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))}; |  | ||||||
|     const std::string_view gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))}; |  | ||||||
|     const std::string_view gpu_model{reinterpret_cast<char const*>(glGetString(GL_RENDERER))}; |  | ||||||
| 
 |  | ||||||
|     LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version); |  | ||||||
|     LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor); |  | ||||||
|     LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model); |  | ||||||
| 
 |  | ||||||
|     auto& telemetry_session = Core::System::GetInstance().TelemetrySession(); |  | ||||||
|     constexpr auto user_system = Common::Telemetry::FieldType::UserSystem; |  | ||||||
|     telemetry_session.AddField(user_system, "GPU_Vendor", std::string(gpu_vendor)); |  | ||||||
|     telemetry_session.AddField(user_system, "GPU_Model", std::string(gpu_model)); |  | ||||||
|     telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); |  | ||||||
| 
 |  | ||||||
|     if (gpu_vendor == "GDI Generic") { |  | ||||||
|         return VideoCore::ResultStatus::ErrorGenericDrivers; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (!(GLAD_GL_VERSION_4_3 || GLAD_GL_ES_VERSION_3_1)) { |  | ||||||
|         return VideoCore::ResultStatus::ErrorBelowGL43; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     InitOpenGLObjects(); |  | ||||||
| 
 |  | ||||||
|     RefreshRasterizerSetting(); |  | ||||||
| 
 |  | ||||||
|     return VideoCore::ResultStatus::Success; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Shutdown the renderer
 |  | ||||||
| void RendererOpenGL::ShutDown() {} |  | ||||||
| 
 |  | ||||||
| } // namespace OpenGL
 | } // namespace OpenGL
 | ||||||
|  |  | ||||||
|  | @ -8,6 +8,8 @@ | ||||||
| #include "core/hw/gpu.h" | #include "core/hw/gpu.h" | ||||||
| #include "video_core/renderer_base.h" | #include "video_core/renderer_base.h" | ||||||
| #include "video_core/renderer_opengl/frame_dumper_opengl.h" | #include "video_core/renderer_opengl/frame_dumper_opengl.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_driver.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||||
| #include "video_core/renderer_opengl/gl_state.h" | #include "video_core/renderer_opengl/gl_state.h" | ||||||
| 
 | 
 | ||||||
|  | @ -15,6 +17,10 @@ namespace Layout { | ||||||
| struct FramebufferLayout; | struct FramebufferLayout; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | namespace Core { | ||||||
|  | class System; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| namespace Frontend { | namespace Frontend { | ||||||
| 
 | 
 | ||||||
| struct Frame { | struct Frame { | ||||||
|  | @ -48,35 +54,21 @@ struct ScreenInfo { | ||||||
|     TextureInfo texture; |     TextureInfo texture; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct PresentationTexture { | class RendererOpenGL : public VideoCore::RendererBase { | ||||||
|     u32 width = 0; |  | ||||||
|     u32 height = 0; |  | ||||||
|     OGLTexture texture; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class RendererOpenGL : public RendererBase { |  | ||||||
| public: | public: | ||||||
|     explicit RendererOpenGL(Frontend::EmuWindow& window, Frontend::EmuWindow* secondary_window); |     explicit RendererOpenGL(Core::System& system, Frontend::EmuWindow& window, | ||||||
|  |                             Frontend::EmuWindow* secondary_window); | ||||||
|     ~RendererOpenGL() override; |     ~RendererOpenGL() override; | ||||||
| 
 | 
 | ||||||
|     /// Initialize the renderer
 |     [[nodiscard]] VideoCore::RasterizerInterface* Rasterizer() const override { | ||||||
|     VideoCore::ResultStatus Init() override; |         return rasterizer.get(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     /// Shutdown the renderer
 |  | ||||||
|     void ShutDown() override; |  | ||||||
| 
 |  | ||||||
|     /// Finalizes rendering the guest frame
 |  | ||||||
|     void SwapBuffers() override; |     void SwapBuffers() override; | ||||||
| 
 |  | ||||||
|     /// Draws the latest frame from texture mailbox to the currently bound draw framebuffer in this
 |  | ||||||
|     /// context
 |  | ||||||
|     void TryPresent(int timeout_ms, bool is_secondary) override; |     void TryPresent(int timeout_ms, bool is_secondary) override; | ||||||
| 
 |  | ||||||
|     /// Prepares for video dumping (e.g. create necessary buffers, etc)
 |  | ||||||
|     void PrepareVideoDumping() override; |     void PrepareVideoDumping() override; | ||||||
| 
 |  | ||||||
|     /// Cleans up after video dumping is ended
 |  | ||||||
|     void CleanupVideoDumping() override; |     void CleanupVideoDumping() override; | ||||||
|  |     void Sync() override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     void InitOpenGLObjects(); |     void InitOpenGLObjects(); | ||||||
|  | @ -111,7 +103,10 @@ private: | ||||||
|     // Fills active OpenGL texture with the given RGB color.
 |     // Fills active OpenGL texture with the given RGB color.
 | ||||||
|     void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture); |     void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture); | ||||||
| 
 | 
 | ||||||
|  | private: | ||||||
|  |     Driver driver; | ||||||
|     OpenGLState state; |     OpenGLState state; | ||||||
|  |     std::unique_ptr<RasterizerOpenGL> rasterizer; | ||||||
| 
 | 
 | ||||||
|     // OpenGL object IDs
 |     // OpenGL object IDs
 | ||||||
|     OGLVertexArray vertex_array; |     OGLVertexArray vertex_array; | ||||||
|  |  | ||||||
|  | @ -22,12 +22,12 @@ | ||||||
| #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/regs_texturing.h" | #include "video_core/regs_texturing.h" | ||||||
|  | #include "video_core/renderer_software/rasterizer.h" | ||||||
|  | #include "video_core/renderer_software/sw_framebuffer.h" | ||||||
|  | #include "video_core/renderer_software/sw_lighting.h" | ||||||
|  | #include "video_core/renderer_software/sw_proctex.h" | ||||||
|  | #include "video_core/renderer_software/sw_texturing.h" | ||||||
| #include "video_core/shader/shader.h" | #include "video_core/shader/shader.h" | ||||||
| #include "video_core/swrasterizer/framebuffer.h" |  | ||||||
| #include "video_core/swrasterizer/lighting.h" |  | ||||||
| #include "video_core/swrasterizer/proctex.h" |  | ||||||
| #include "video_core/swrasterizer/rasterizer.h" |  | ||||||
| #include "video_core/swrasterizer/texturing.h" |  | ||||||
| #include "video_core/texture/texture_decode.h" | #include "video_core/texture/texture_decode.h" | ||||||
| #include "video_core/utils.h" | #include "video_core/utils.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
							
								
								
									
										19
									
								
								src/video_core/renderer_software/renderer_software.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/video_core/renderer_software/renderer_software.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "video_core/renderer_software/renderer_software.h" | ||||||
|  | 
 | ||||||
|  | namespace VideoCore { | ||||||
|  | 
 | ||||||
|  | RendererSoftware::RendererSoftware(Core::System& system, Frontend::EmuWindow& window) | ||||||
|  |     : VideoCore::RendererBase{system, window, nullptr}, | ||||||
|  |       rasterizer{std::make_unique<RasterizerSoftware>()} {} | ||||||
|  | 
 | ||||||
|  | RendererSoftware::~RendererSoftware() = default; | ||||||
|  | 
 | ||||||
|  | void RendererSoftware::SwapBuffers() { | ||||||
|  |     EndFrame(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace VideoCore
 | ||||||
							
								
								
									
										33
									
								
								src/video_core/renderer_software/renderer_software.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/video_core/renderer_software/renderer_software.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "video_core/renderer_base.h" | ||||||
|  | #include "video_core/renderer_software/sw_rasterizer.h" | ||||||
|  | 
 | ||||||
|  | namespace Core { | ||||||
|  | class System; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace VideoCore { | ||||||
|  | 
 | ||||||
|  | class RendererSoftware : public VideoCore::RendererBase { | ||||||
|  | public: | ||||||
|  |     explicit RendererSoftware(Core::System& system, Frontend::EmuWindow& window); | ||||||
|  |     ~RendererSoftware() override; | ||||||
|  | 
 | ||||||
|  |     [[nodiscard]] VideoCore::RasterizerInterface* Rasterizer() const override { | ||||||
|  |         return rasterizer.get(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void SwapBuffers() override; | ||||||
|  |     void TryPresent(int timeout_ms, bool is_secondary) override {} | ||||||
|  |     void Sync() override {} | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::unique_ptr<RasterizerSoftware> rasterizer; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace VideoCore
 | ||||||
|  | @ -12,9 +12,9 @@ | ||||||
| #include "common/vector_math.h" | #include "common/vector_math.h" | ||||||
| #include "video_core/pica_state.h" | #include "video_core/pica_state.h" | ||||||
| #include "video_core/pica_types.h" | #include "video_core/pica_types.h" | ||||||
|  | #include "video_core/renderer_software/rasterizer.h" | ||||||
|  | #include "video_core/renderer_software/sw_clipper.h" | ||||||
| #include "video_core/shader/shader.h" | #include "video_core/shader/shader.h" | ||||||
| #include "video_core/swrasterizer/clipper.h" |  | ||||||
| #include "video_core/swrasterizer/rasterizer.h" |  | ||||||
| 
 | 
 | ||||||
| using Pica::Rasterizer::Vertex; | using Pica::Rasterizer::Vertex; | ||||||
| 
 | 
 | ||||||
|  | @ -12,7 +12,7 @@ | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| #include "video_core/pica_state.h" | #include "video_core/pica_state.h" | ||||||
| #include "video_core/regs_framebuffer.h" | #include "video_core/regs_framebuffer.h" | ||||||
| #include "video_core/swrasterizer/framebuffer.h" | #include "video_core/renderer_software/sw_framebuffer.h" | ||||||
| #include "video_core/utils.h" | #include "video_core/utils.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include "video_core/swrasterizer/lighting.h" | #include "video_core/renderer_software/sw_lighting.h" | ||||||
| 
 | 
 | ||||||
| namespace Pica { | namespace Pica { | ||||||
| 
 | 
 | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| #include <array> | #include <array> | ||||||
| #include <cmath> | #include <cmath> | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| #include "video_core/swrasterizer/proctex.h" | #include "video_core/renderer_software/sw_proctex.h" | ||||||
| 
 | 
 | ||||||
| namespace Pica::Rasterizer { | namespace Pica::Rasterizer { | ||||||
| 
 | 
 | ||||||
							
								
								
									
										16
									
								
								src/video_core/renderer_software/sw_rasterizer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/video_core/renderer_software/sw_rasterizer.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | // Copyright 2015 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "video_core/renderer_software/sw_clipper.h" | ||||||
|  | #include "video_core/renderer_software/sw_rasterizer.h" | ||||||
|  | 
 | ||||||
|  | namespace VideoCore { | ||||||
|  | 
 | ||||||
|  | void RasterizerSoftware::AddTriangle(const Pica::Shader::OutputVertex& v0, | ||||||
|  |                                      const Pica::Shader::OutputVertex& v1, | ||||||
|  |                                      const Pica::Shader::OutputVertex& v2) { | ||||||
|  |     Pica::Clipper::ProcessTriangle(v0, v1, v2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace VideoCore
 | ||||||
|  | @ -13,7 +13,7 @@ struct OutputVertex; | ||||||
| 
 | 
 | ||||||
| namespace VideoCore { | namespace VideoCore { | ||||||
| 
 | 
 | ||||||
| class SWRasterizer : public RasterizerInterface { | class RasterizerSoftware : public RasterizerInterface { | ||||||
|     void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1, |     void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1, | ||||||
|                      const Pica::Shader::OutputVertex& v2) override; |                      const Pica::Shader::OutputVertex& v2) override; | ||||||
|     void DrawTriangles() override {} |     void DrawTriangles() override {} | ||||||
|  | @ -7,7 +7,7 @@ | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/vector_math.h" | #include "common/vector_math.h" | ||||||
| #include "video_core/regs_texturing.h" | #include "video_core/regs_texturing.h" | ||||||
| #include "video_core/swrasterizer/texturing.h" | #include "video_core/renderer_software/sw_texturing.h" | ||||||
| 
 | 
 | ||||||
| namespace Pica::Rasterizer { | namespace Pica::Rasterizer { | ||||||
| 
 | 
 | ||||||
|  | @ -86,7 +86,7 @@ void UnitState::LoadInput(const ShaderRegs& config, const AttributeBuffer& input | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void CopyRegistersToOutput(const Common::Vec4<float24>* regs, u32 mask, | static void CopyRegistersToOutput(std::span<Common::Vec4<float24>, 16> regs, u32 mask, | ||||||
|                                   AttributeBuffer& buffer) { |                                   AttributeBuffer& buffer) { | ||||||
|     int output_i = 0; |     int output_i = 0; | ||||||
|     for (int reg : Common::BitSet<u32>(mask)) { |     for (int reg : Common::BitSet<u32>(mask)) { | ||||||
|  | @ -108,7 +108,7 @@ GSEmitter::~GSEmitter() { | ||||||
|     delete handlers; |     delete handlers; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GSEmitter::Emit(Common::Vec4<float24> (&output_regs)[16]) { | void GSEmitter::Emit(std::span<Common::Vec4<float24>, 16> output_regs) { | ||||||
|     ASSERT(vertex_id < 3); |     ASSERT(vertex_id < 3); | ||||||
|     // TODO: This should be merged with UnitState::WriteOutput somehow
 |     // TODO: This should be merged with UnitState::WriteOutput somehow
 | ||||||
|     CopyRegistersToOutput(output_regs, output_mask, buffer[vertex_id]); |     CopyRegistersToOutput(output_regs, output_mask, buffer[vertex_id]); | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ | ||||||
| #include <array> | #include <array> | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <functional> | #include <functional> | ||||||
|  | #include <span> | ||||||
| #include <type_traits> | #include <type_traits> | ||||||
| #include <boost/serialization/access.hpp> | #include <boost/serialization/access.hpp> | ||||||
| #include <boost/serialization/array.hpp> | #include <boost/serialization/array.hpp> | ||||||
|  | @ -113,7 +114,7 @@ struct GSEmitter { | ||||||
| 
 | 
 | ||||||
|     GSEmitter(); |     GSEmitter(); | ||||||
|     ~GSEmitter(); |     ~GSEmitter(); | ||||||
|     void Emit(Common::Vec4<float24> (&output_regs)[16]); |     void Emit(std::span<Common::Vec4<float24>, 16> output_regs); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     friend class boost::serialization::access; |     friend class boost::serialization::access; | ||||||
|  | @ -140,9 +141,9 @@ struct UnitState { | ||||||
|     struct Registers { |     struct Registers { | ||||||
|         // The registers are accessed by the shader JIT using SSE instructions, and are therefore
 |         // The registers are accessed by the shader JIT using SSE instructions, and are therefore
 | ||||||
|         // required to be 16-byte aligned.
 |         // required to be 16-byte aligned.
 | ||||||
|         alignas(16) Common::Vec4<float24> input[16]; |         alignas(16) std::array<Common::Vec4<float24>, 16> input; | ||||||
|         alignas(16) Common::Vec4<float24> temporary[16]; |         alignas(16) std::array<Common::Vec4<float24>, 16> temporary; | ||||||
|         alignas(16) Common::Vec4<float24> output[16]; |         alignas(16) std::array<Common::Vec4<float24>, 16> output; | ||||||
| 
 | 
 | ||||||
|     private: |     private: | ||||||
|         friend class boost::serialization::access; |         friend class boost::serialization::access; | ||||||
|  |  | ||||||
|  | @ -7,7 +7,6 @@ | ||||||
| #include <cmath> | #include <cmath> | ||||||
| #include <numeric> | #include <numeric> | ||||||
| #include <boost/container/static_vector.hpp> | #include <boost/container/static_vector.hpp> | ||||||
| #include <boost/range/algorithm/fill.hpp> |  | ||||||
| #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" | ||||||
|  | @ -688,7 +687,7 @@ DebugData<true> InterpreterEngine::ProduceDebugInfo(const ShaderSetup& setup, | ||||||
|     DebugData<true> debug_data; |     DebugData<true> debug_data; | ||||||
| 
 | 
 | ||||||
|     // Setup input register table
 |     // Setup input register table
 | ||||||
|     boost::fill(state.registers.input, Common::Vec4<float24>::AssignToAll(float24::Zero())); |     state.registers.input.fill(Common::Vec4<float24>::AssignToAll(float24::Zero())); | ||||||
|     state.LoadInput(config, input); |     state.LoadInput(config, input); | ||||||
|     RunInterpreter(setup, state, debug_data, setup.engine_data.entry_point); |     RunInterpreter(setup, state, debug_data, setup.engine_data.entry_point); | ||||||
|     return debug_data; |     return debug_data; | ||||||
|  |  | ||||||
							
								
								
									
										78
									
								
								src/video_core/shader/shader_uniforms.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/video_core/shader/shader_uniforms.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | ||||||
|  | // 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 clip_coef; | ||||||
|  | }}; | ||||||
|  | )"; | ||||||
|  | 
 | ||||||
|  | std::string BuildShaderUniformDefinitions(const std::string& extra_layout_parameters) { | ||||||
|  |     return fmt::format(UniformBlockDefFormat, extra_layout_parameters); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Pica::Shader
 | ||||||
							
								
								
									
										101
									
								
								src/video_core/shader/shader_uniforms.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/video_core/shader/shader_uniforms.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,101 @@ | ||||||
|  | // Copyright 2023 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "common/vector_math.h" | ||||||
|  | #include "video_core/regs_lighting.h" | ||||||
|  | 
 | ||||||
|  | namespace Pica { | ||||||
|  | struct ShaderRegs; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace Pica::Shader { | ||||||
|  | 
 | ||||||
|  | struct ShaderSetup; | ||||||
|  | 
 | ||||||
|  | enum class UniformBindings : u32 { Common, VS, GS }; | ||||||
|  | 
 | ||||||
|  | struct LightSrc { | ||||||
|  |     alignas(16) Common::Vec3f specular_0; | ||||||
|  |     alignas(16) Common::Vec3f specular_1; | ||||||
|  |     alignas(16) Common::Vec3f diffuse; | ||||||
|  |     alignas(16) Common::Vec3f ambient; | ||||||
|  |     alignas(16) Common::Vec3f position; | ||||||
|  |     alignas(16) Common::Vec3f spot_direction; // negated
 | ||||||
|  |     float dist_atten_bias; | ||||||
|  |     float dist_atten_scale; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned | ||||||
|  |  * NOTE: Always keep a vec4 at the end. The GL spec is not clear wether the alignment at | ||||||
|  |  *       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. | ||||||
|  |  */ | ||||||
|  | struct UniformData { | ||||||
|  |     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; | ||||||
|  |     alignas(4) bool enable_clip1; | ||||||
|  |     alignas(16) Common::Vec4i lighting_lut_offset[LightingRegs::NumLightingSampler / 4]; | ||||||
|  |     alignas(16) Common::Vec3f fog_color; | ||||||
|  |     alignas(8) Common::Vec2f proctex_noise_f; | ||||||
|  |     alignas(8) Common::Vec2f proctex_noise_a; | ||||||
|  |     alignas(8) Common::Vec2f proctex_noise_p; | ||||||
|  |     alignas(16) Common::Vec3f lighting_global_ambient; | ||||||
|  |     LightSrc light_src[8]; | ||||||
|  |     alignas(16) Common::Vec4f const_color[6]; // A vec4 color for each of the six tev stages
 | ||||||
|  |     alignas(16) Common::Vec4f tev_combiner_buffer_color; | ||||||
|  |     alignas(16) Common::Vec3f tex_lod_bias; | ||||||
|  |     alignas(16) Common::Vec4f clip_coef; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static_assert(sizeof(UniformData) == 0x500, | ||||||
|  |               "The size of the UniformData does not match the structure in the shader"); | ||||||
|  | static_assert(sizeof(UniformData) < 16384, | ||||||
|  |               "UniformData structure must be less than 16kb as per the OpenGL spec"); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Uniform struct for the Uniform Buffer Object that contains PICA vertex/geometry shader uniforms. | ||||||
|  |  * NOTE: the same rule from UniformData also applies here. | ||||||
|  |  */ | ||||||
|  | struct PicaUniformsData { | ||||||
|  |     void SetFromRegs(const ShaderRegs& regs, const ShaderSetup& setup); | ||||||
|  | 
 | ||||||
|  |     struct BoolAligned { | ||||||
|  |         alignas(16) int b; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     std::array<BoolAligned, 16> bools; | ||||||
|  |     alignas(16) std::array<Common::Vec4u, 4> i; | ||||||
|  |     alignas(16) std::array<Common::Vec4f, 96> f; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct VSUniformData { | ||||||
|  |     PicaUniformsData uniforms; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(VSUniformData) == 1856, | ||||||
|  |               "The size of the VSUniformData does not match the structure in the shader"); | ||||||
|  | static_assert(sizeof(VSUniformData) < 16384, | ||||||
|  |               "VSUniformData structure must be less than 16kb as per the OpenGL spec"); | ||||||
|  | 
 | ||||||
|  | std::string BuildShaderUniformDefinitions(const std::string& extra_layout_parameters = ""); | ||||||
|  | 
 | ||||||
|  | } // namespace Pica::Shader
 | ||||||
|  | @ -1,16 +0,0 @@ | ||||||
| // Copyright 2015 Citra Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #include "video_core/swrasterizer/clipper.h" |  | ||||||
| #include "video_core/swrasterizer/swrasterizer.h" |  | ||||||
| 
 |  | ||||||
| namespace VideoCore { |  | ||||||
| 
 |  | ||||||
| void SWRasterizer::AddTriangle(const Pica::Shader::OutputVertex& v0, |  | ||||||
|                                const Pica::Shader::OutputVertex& v1, |  | ||||||
|                                const Pica::Shader::OutputVertex& v2) { |  | ||||||
|     Pica::Clipper::ProcessTriangle(v0, v1, v2); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } // namespace VideoCore
 |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <boost/range/algorithm/fill.hpp> |  | ||||||
| #include "common/alignment.h" | #include "common/alignment.h" | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/bit_field.h" | #include "common/bit_field.h" | ||||||
|  | @ -23,7 +22,7 @@ void VertexLoader::Setup(const PipelineRegs& regs) { | ||||||
|     const auto& attribute_config = regs.vertex_attributes; |     const auto& attribute_config = regs.vertex_attributes; | ||||||
|     num_total_attributes = attribute_config.GetNumTotalAttributes(); |     num_total_attributes = attribute_config.GetNumTotalAttributes(); | ||||||
| 
 | 
 | ||||||
|     boost::fill(vertex_attribute_sources, 0xdeadbeef); |     vertex_attribute_sources.fill(0xdeadbeef); | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < 16; i++) { |     for (int i = 0; i < 16; i++) { | ||||||
|         vertex_attribute_is_default[i] = attribute_config.IsDefaultAttribute(i); |         vertex_attribute_is_default[i] = attribute_config.IsDefaultAttribute(i); | ||||||
|  |  | ||||||
|  | @ -6,11 +6,13 @@ | ||||||
| #include "common/archives.h" | #include "common/archives.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
|  | #include "core/core.h" | ||||||
| #include "video_core/pica.h" | #include "video_core/pica.h" | ||||||
| #include "video_core/pica_state.h" | #include "video_core/pica_state.h" | ||||||
| #include "video_core/renderer_base.h" | #include "video_core/renderer_base.h" | ||||||
| #include "video_core/renderer_opengl/gl_vars.h" | #include "video_core/renderer_opengl/gl_vars.h" | ||||||
| #include "video_core/renderer_opengl/renderer_opengl.h" | #include "video_core/renderer_opengl/renderer_opengl.h" | ||||||
|  | #include "video_core/renderer_software/renderer_software.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | @ -18,9 +20,8 @@ | ||||||
| 
 | 
 | ||||||
| namespace VideoCore { | namespace VideoCore { | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
 | std::unique_ptr<RendererBase> g_renderer{}; ///< Renderer plugin
 | ||||||
| 
 | 
 | ||||||
| std::atomic<bool> g_hw_renderer_enabled; |  | ||||||
| std::atomic<bool> g_shader_jit_enabled; | std::atomic<bool> g_shader_jit_enabled; | ||||||
| std::atomic<bool> g_hw_shader_enabled; | std::atomic<bool> g_hw_shader_enabled; | ||||||
| std::atomic<bool> g_separable_shader_enabled; | std::atomic<bool> g_separable_shader_enabled; | ||||||
|  | @ -30,65 +31,49 @@ std::atomic<bool> g_renderer_bg_color_update_requested; | ||||||
| std::atomic<bool> g_renderer_sampler_update_requested; | std::atomic<bool> g_renderer_sampler_update_requested; | ||||||
| std::atomic<bool> g_renderer_shader_update_requested; | std::atomic<bool> g_renderer_shader_update_requested; | ||||||
| std::atomic<bool> g_texture_filter_update_requested; | std::atomic<bool> g_texture_filter_update_requested; | ||||||
| // Screenshot
 |  | ||||||
| std::atomic<bool> g_renderer_screenshot_requested; |  | ||||||
| void* g_screenshot_bits; |  | ||||||
| std::function<void()> g_screenshot_complete_callback; |  | ||||||
| Layout::FramebufferLayout g_screenshot_framebuffer_layout; |  | ||||||
| 
 | 
 | ||||||
| Memory::MemorySystem* g_memory; | Memory::MemorySystem* g_memory; | ||||||
| 
 | 
 | ||||||
| /// Initialize the video core
 | /// Initialize the video core
 | ||||||
| ResultStatus Init(Frontend::EmuWindow& emu_window, Frontend::EmuWindow* secondary_window, | void Init(Frontend::EmuWindow& emu_window, Frontend::EmuWindow* secondary_window, | ||||||
|                   Memory::MemorySystem& memory) { |           Core::System& system) { | ||||||
|     g_memory = &memory; |     g_memory = &system.Memory(); | ||||||
|     Pica::Init(); |     Pica::Init(); | ||||||
| 
 | 
 | ||||||
|  |     const Settings::GraphicsAPI graphics_api = Settings::values.graphics_api.GetValue(); | ||||||
|     OpenGL::GLES = Settings::values.use_gles.GetValue(); |     OpenGL::GLES = Settings::values.use_gles.GetValue(); | ||||||
| 
 | 
 | ||||||
|     g_renderer = std::make_unique<OpenGL::RendererOpenGL>(emu_window, secondary_window); |     switch (graphics_api) { | ||||||
|     ResultStatus result = g_renderer->Init(); |     case Settings::GraphicsAPI::Software: | ||||||
| 
 |         g_renderer = std::make_unique<VideoCore::RendererSoftware>(system, emu_window); | ||||||
|     if (result != ResultStatus::Success) { |         break; | ||||||
|         LOG_ERROR(Render, "initialization failed !"); |     case Settings::GraphicsAPI::OpenGL: | ||||||
|     } else { |         g_renderer = std::make_unique<OpenGL::RendererOpenGL>(system, emu_window, secondary_window); | ||||||
|         LOG_DEBUG(Render, "initialized OK"); |         break; | ||||||
|  |     default: | ||||||
|  |         LOG_CRITICAL(Render, "Unknown graphics API {}, using OpenGL", graphics_api); | ||||||
|  |         g_renderer = std::make_unique<OpenGL::RendererOpenGL>(system, emu_window, secondary_window); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Shutdown the video core
 | /// Shutdown the video core
 | ||||||
| void Shutdown() { | void Shutdown() { | ||||||
|     Pica::Shutdown(); |     Pica::Shutdown(); | ||||||
| 
 |  | ||||||
|     g_renderer->ShutDown(); |  | ||||||
|     g_renderer.reset(); |     g_renderer.reset(); | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Render, "shutdown OK"); |     LOG_DEBUG(Render, "shutdown OK"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RequestScreenshot(void* data, std::function<void()> callback, |  | ||||||
|                        const Layout::FramebufferLayout& layout) { |  | ||||||
|     if (g_renderer_screenshot_requested) { |  | ||||||
|         LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     g_screenshot_bits = data; |  | ||||||
|     g_screenshot_complete_callback = std::move(callback); |  | ||||||
|     g_screenshot_framebuffer_layout = layout; |  | ||||||
|     g_renderer_screenshot_requested = true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| u16 GetResolutionScaleFactor() { | u16 GetResolutionScaleFactor() { | ||||||
|     if (g_hw_renderer_enabled) { |     const auto graphics_api = Settings::values.graphics_api.GetValue(); | ||||||
|         return Settings::values.resolution_factor.GetValue() |     if (graphics_api == Settings::GraphicsAPI::Software) { | ||||||
|                    ? Settings::values.resolution_factor.GetValue() |  | ||||||
|                    : g_renderer->GetRenderWindow().GetFramebufferLayout().GetScalingRatio(); |  | ||||||
|     } else { |  | ||||||
|         // Software renderer always render at native resolution
 |         // Software renderer always render at native resolution
 | ||||||
|         return 1; |         return 1; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     return Settings::values.resolution_factor.GetValue() | ||||||
|  |                ? Settings::values.resolution_factor.GetValue() | ||||||
|  |                : g_renderer->GetRenderWindow().GetFramebufferLayout().GetScalingRatio(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <class Archive> | template <class Archive> | ||||||
|  |  | ||||||
|  | @ -14,7 +14,9 @@ namespace Frontend { | ||||||
| class EmuWindow; | class EmuWindow; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class RendererBase; | namespace Core { | ||||||
|  | class System; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| namespace Memory { | namespace Memory { | ||||||
| class MemorySystem; | class MemorySystem; | ||||||
|  | @ -25,11 +27,12 @@ class MemorySystem; | ||||||
| 
 | 
 | ||||||
| namespace VideoCore { | namespace VideoCore { | ||||||
| 
 | 
 | ||||||
|  | class RendererBase; | ||||||
|  | 
 | ||||||
| extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
 | extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
 | ||||||
| 
 | 
 | ||||||
| // TODO: Wrap these in a user settings struct along with any other graphics settings (often set from
 | // TODO: Wrap these in a user settings struct along with any other graphics settings (often set from
 | ||||||
| // qt ui)
 | // qt ui)
 | ||||||
| extern std::atomic<bool> g_hw_renderer_enabled; |  | ||||||
| extern std::atomic<bool> g_shader_jit_enabled; | extern std::atomic<bool> g_shader_jit_enabled; | ||||||
| extern std::atomic<bool> g_hw_shader_enabled; | extern std::atomic<bool> g_hw_shader_enabled; | ||||||
| extern std::atomic<bool> g_separable_shader_enabled; | extern std::atomic<bool> g_separable_shader_enabled; | ||||||
|  | @ -39,31 +42,16 @@ extern std::atomic<bool> g_renderer_bg_color_update_requested; | ||||||
| extern std::atomic<bool> g_renderer_sampler_update_requested; | extern std::atomic<bool> g_renderer_sampler_update_requested; | ||||||
| extern std::atomic<bool> g_renderer_shader_update_requested; | extern std::atomic<bool> g_renderer_shader_update_requested; | ||||||
| extern std::atomic<bool> g_texture_filter_update_requested; | extern std::atomic<bool> g_texture_filter_update_requested; | ||||||
| // Screenshot
 |  | ||||||
| extern std::atomic<bool> g_renderer_screenshot_requested; |  | ||||||
| extern void* g_screenshot_bits; |  | ||||||
| extern std::function<void()> g_screenshot_complete_callback; |  | ||||||
| extern Layout::FramebufferLayout g_screenshot_framebuffer_layout; |  | ||||||
| 
 | 
 | ||||||
| extern Memory::MemorySystem* g_memory; | extern Memory::MemorySystem* g_memory; | ||||||
| 
 | 
 | ||||||
| enum class ResultStatus { |  | ||||||
|     Success, |  | ||||||
|     ErrorGenericDrivers, |  | ||||||
|     ErrorBelowGL43, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /// Initialize the video core
 | /// Initialize the video core
 | ||||||
| ResultStatus Init(Frontend::EmuWindow& emu_window, Frontend::EmuWindow* secondary_window, | void Init(Frontend::EmuWindow& emu_window, Frontend::EmuWindow* secondary_window, | ||||||
|                   Memory::MemorySystem& memory); |           Core::System& system); | ||||||
| 
 | 
 | ||||||
| /// Shutdown the video core
 | /// Shutdown the video core
 | ||||||
| void Shutdown(); | void Shutdown(); | ||||||
| 
 | 
 | ||||||
| /// Request a screenshot of the next frame
 |  | ||||||
| void RequestScreenshot(void* data, std::function<void()> callback, |  | ||||||
|                        const Layout::FramebufferLayout& layout); |  | ||||||
| 
 |  | ||||||
| u16 GetResolutionScaleFactor(); | u16 GetResolutionScaleFactor(); | ||||||
| 
 | 
 | ||||||
| template <class Archive> | template <class Archive> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue