summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt4
-rw-r--r--src/android/app/src/main/jni/applets/software_keyboard.cpp12
-rw-r--r--src/android/app/src/main/jni/applets/software_keyboard.h4
-rw-r--r--src/android/app/src/main/jni/native.cpp45
-rw-r--r--src/android/app/src/main/jni/native.h5
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/range_mutex.h93
-rw-r--r--src/common/settings.h12
-rw-r--r--src/core/CMakeLists.txt113
-rw-r--r--src/core/core.cpp72
-rw-r--r--src/core/core.h25
-rw-r--r--src/core/device_memory_manager.h18
-rw-r--r--src/core/device_memory_manager.inc63
-rw-r--r--src/core/frontend/applets/general.cpp (renamed from src/core/frontend/applets/general_frontend.cpp)2
-rw-r--r--src/core/frontend/applets/general.h (renamed from src/core/frontend/applets/general_frontend.h)0
-rw-r--r--src/core/frontend/applets/profile_select.h8
-rw-r--r--src/core/frontend/applets/software_keyboard.cpp12
-rw-r--r--src/core/frontend/applets/software_keyboard.h22
-rw-r--r--src/core/frontend/applets/web_browser.cpp4
-rw-r--r--src/core/frontend/applets/web_browser.h4
-rw-r--r--src/core/hle/kernel/kernel.cpp30
-rw-r--r--src/core/hle/service/am/am.cpp2685
-rw-r--r--src/core/hle/service/am/am.h454
-rw-r--r--src/core/hle/service/am/am_results.h16
-rw-r--r--src/core/hle/service/am/am_types.h178
-rw-r--r--src/core/hle/service/am/applet.cpp27
-rw-r--r--src/core/hle/service/am/applet.h133
-rw-r--r--src/core/hle/service/am/applet_ae.cpp320
-rw-r--r--src/core/hle/service/am/applet_ae.h10
-rw-r--r--src/core/hle/service/am/applet_common_functions.cpp63
-rw-r--r--src/core/hle/service/am/applet_common_functions.h24
-rw-r--r--src/core/hle/service/am/applet_data_broker.cpp67
-rw-r--r--src/core/hle/service/am/applet_data_broker.h80
-rw-r--r--src/core/hle/service/am/applet_manager.cpp361
-rw-r--r--src/core/hle/service/am/applet_manager.h59
-rw-r--r--src/core/hle/service/am/applet_message_queue.cpp73
-rw-r--r--src/core/hle/service/am/applet_message_queue.h76
-rw-r--r--src/core/hle/service/am/applet_oe.cpp129
-rw-r--r--src/core/hle/service/am/applet_oe.h10
-rw-r--r--src/core/hle/service/am/applets/applets.cpp338
-rw-r--r--src/core/hle/service/am/applets/applets.h289
-rw-r--r--src/core/hle/service/am/application_creator.cpp25
-rw-r--r--src/core/hle/service/am/application_creator.h16
-rw-r--r--src/core/hle/service/am/application_functions.cpp594
-rw-r--r--src/core/hle/service/am/application_functions.h58
-rw-r--r--src/core/hle/service/am/application_proxy.cpp115
-rw-r--r--src/core/hle/service/am/application_proxy.h33
-rw-r--r--src/core/hle/service/am/audio_controller.cpp91
-rw-r--r--src/core/hle/service/am/audio_controller.h36
-rw-r--r--src/core/hle/service/am/common_state_getter.cpp314
-rw-r--r--src/core/hle/service/am/common_state_getter.h77
-rw-r--r--src/core/hle/service/am/debug_functions.cpp44
-rw-r--r--src/core/hle/service/am/debug_functions.h16
-rw-r--r--src/core/hle/service/am/display_controller.cpp135
-rw-r--r--src/core/hle/service/am/display_controller.h30
-rw-r--r--src/core/hle/service/am/frontend/applet_cabinet.cpp (renamed from src/core/hle/service/am/applets/applet_cabinet.cpp)29
-rw-r--r--src/core/hle/service/am/frontend/applet_cabinet.h (renamed from src/core/hle/service/am/applets/applet_cabinet.h)13
-rw-r--r--src/core/hle/service/am/frontend/applet_controller.cpp (renamed from src/core/hle/service/am/applets/applet_controller.cpp)31
-rw-r--r--src/core/hle/service/am/frontend/applet_controller.h (renamed from src/core/hle/service/am/applets/applet_controller.h)13
-rw-r--r--src/core/hle/service/am/frontend/applet_error.cpp (renamed from src/core/hle/service/am/applets/applet_error.cpp)23
-rw-r--r--src/core/hle/service/am/frontend/applet_error.h (renamed from src/core/hle/service/am/applets/applet_error.h)14
-rw-r--r--src/core/hle/service/am/frontend/applet_general.cpp (renamed from src/core/hle/service/am/applets/applet_general_backend.cpp)90
-rw-r--r--src/core/hle/service/am/frontend/applet_general.h (renamed from src/core/hle/service/am/applets/applet_general_backend.h)27
-rw-r--r--src/core/hle/service/am/frontend/applet_mii_edit.cpp (renamed from src/core/hle/service/am/applets/applet_mii_edit.cpp)27
-rw-r--r--src/core/hle/service/am/frontend/applet_mii_edit.h (renamed from src/core/hle/service/am/applets/applet_mii_edit.h)15
-rw-r--r--src/core/hle/service/am/frontend/applet_mii_edit_types.h (renamed from src/core/hle/service/am/applets/applet_mii_edit_types.h)4
-rw-r--r--src/core/hle/service/am/frontend/applet_profile_select.cpp (renamed from src/core/hle/service/am/applets/applet_profile_select.cpp)28
-rw-r--r--src/core/hle/service/am/frontend/applet_profile_select.h (renamed from src/core/hle/service/am/applets/applet_profile_select.h)13
-rw-r--r--src/core/hle/service/am/frontend/applet_software_keyboard.cpp (renamed from src/core/hle/service/am/applets/applet_software_keyboard.cpp)68
-rw-r--r--src/core/hle/service/am/frontend/applet_software_keyboard.h (renamed from src/core/hle/service/am/applets/applet_software_keyboard.h)15
-rw-r--r--src/core/hle/service/am/frontend/applet_software_keyboard_types.h (renamed from src/core/hle/service/am/applets/applet_software_keyboard_types.h)4
-rw-r--r--src/core/hle/service/am/frontend/applet_web_browser.cpp (renamed from src/core/hle/service/am/applets/applet_web_browser.cpp)24
-rw-r--r--src/core/hle/service/am/frontend/applet_web_browser.h (renamed from src/core/hle/service/am/applets/applet_web_browser.h)17
-rw-r--r--src/core/hle/service/am/frontend/applet_web_browser_types.h (renamed from src/core/hle/service/am/applets/applet_web_browser_types.h)4
-rw-r--r--src/core/hle/service/am/frontend/applets.cpp240
-rw-r--r--src/core/hle/service/am/frontend/applets.h146
-rw-r--r--src/core/hle/service/am/global_state_controller.cpp34
-rw-r--r--src/core/hle/service/am/global_state_controller.h16
-rw-r--r--src/core/hle/service/am/hid_registration.cpp35
-rw-r--r--src/core/hle/service/am/hid_registration.h32
-rw-r--r--src/core/hle/service/am/home_menu_functions.cpp57
-rw-r--r--src/core/hle/service/am/home_menu_functions.h25
-rw-r--r--src/core/hle/service/am/library_applet_accessor.cpp202
-rw-r--r--src/core/hle/service/am/library_applet_accessor.h43
-rw-r--r--src/core/hle/service/am/library_applet_creator.cpp271
-rw-r--r--src/core/hle/service/am/library_applet_creator.h26
-rw-r--r--src/core/hle/service/am/library_applet_proxy.cpp143
-rw-r--r--src/core/hle/service/am/library_applet_proxy.h36
-rw-r--r--src/core/hle/service/am/library_applet_self_accessor.cpp338
-rw-r--r--src/core/hle/service/am/library_applet_self_accessor.h44
-rw-r--r--src/core/hle/service/am/library_applet_storage.cpp140
-rw-r--r--src/core/hle/service/am/library_applet_storage.h36
-rw-r--r--src/core/hle/service/am/lock_accessor.cpp71
-rw-r--r--src/core/hle/service/am/lock_accessor.h28
-rw-r--r--src/core/hle/service/am/managed_layer_holder.cpp59
-rw-r--r--src/core/hle/service/am/managed_layer_holder.h32
-rw-r--r--src/core/hle/service/am/process.cpp138
-rw-r--r--src/core/hle/service/am/process.h50
-rw-r--r--src/core/hle/service/am/process_winding_controller.cpp56
-rw-r--r--src/core/hle/service/am/process_winding_controller.h24
-rw-r--r--src/core/hle/service/am/self_controller.cpp456
-rw-r--r--src/core/hle/service/am/self_controller.h58
-rw-r--r--src/core/hle/service/am/storage.cpp59
-rw-r--r--src/core/hle/service/am/storage.h31
-rw-r--r--src/core/hle/service/am/storage_accessor.cpp90
-rw-r--r--src/core/hle/service/am/storage_accessor.h37
-rw-r--r--src/core/hle/service/am/system_applet_proxy.cpp136
-rw-r--r--src/core/hle/service/am/system_applet_proxy.h36
-rw-r--r--src/core/hle/service/am/system_buffer_manager.cpp69
-rw-r--r--src/core/hle/service/am/system_buffer_manager.h51
-rw-r--r--src/core/hle/service/am/window_controller.cpp86
-rw-r--r--src/core/hle/service/am/window_controller.h27
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp2
-rw-r--r--src/core/hle/service/caps/caps_a.cpp156
-rw-r--r--src/core/hle/service/caps/caps_a.h34
-rw-r--r--src/core/hle/service/caps/caps_c.cpp16
-rw-r--r--src/core/hle/service/caps/caps_c.h5
-rw-r--r--src/core/hle/service/caps/caps_manager.cpp61
-rw-r--r--src/core/hle/service/caps/caps_manager.h16
-rw-r--r--src/core/hle/service/caps/caps_ss.cpp82
-rw-r--r--src/core/hle/service/caps/caps_ss.h18
-rw-r--r--src/core/hle/service/caps/caps_su.cpp87
-rw-r--r--src/core/hle/service/caps/caps_su.h18
-rw-r--r--src/core/hle/service/caps/caps_types.h32
-rw-r--r--src/core/hle/service/caps/caps_u.cpp110
-rw-r--r--src/core/hle/service/caps/caps_u.h16
-rw-r--r--src/core/hle/service/event.cpp31
-rw-r--r--src/core/hle/service/event.h31
-rw-r--r--src/core/hle/service/filesystem/fsp/fsp_srv.cpp48
-rw-r--r--src/core/hle/service/glue/time/time_zone.cpp27
-rw-r--r--src/core/hle/service/glue/time/time_zone.h6
-rw-r--r--src/core/hle/service/hid/hid.cpp5
-rw-r--r--src/core/hle/service/hid/hid_debug_server.cpp196
-rw-r--r--src/core/hle/service/hid/hid_debug_server.h15
-rw-r--r--src/core/hle/service/hid/hid_server.cpp24
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp64
-rw-r--r--src/core/hle/service/hid/hid_system_server.h3
-rw-r--r--src/core/hle/service/nifm/nifm.cpp12
-rw-r--r--src/core/hle/service/ns/ns.cpp67
-rw-r--r--src/core/hle/service/ns/ns.h15
-rw-r--r--src/core/hle/service/nvdrv/core/container.cpp4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp35
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h10
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp2
-rw-r--r--src/core/hle/service/nvnflinger/hardware_composer.cpp215
-rw-r--r--src/core/hle/service/nvnflinger/hardware_composer.h59
-rw-r--r--src/core/hle/service/nvnflinger/hwc_layer.h27
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp52
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h5
-rw-r--r--src/core/hle/service/psc/time/common.h24
-rw-r--r--src/core/hle/service/psc/time/service_manager.cpp14
-rw-r--r--src/core/hle/service/psc/time/time_zone.cpp41
-rw-r--r--src/core/hle/service/psc/time/time_zone.h12
-rw-r--r--src/core/hle/service/psc/time/time_zone_service.cpp20
-rw-r--r--src/core/hle/service/psc/time/time_zone_service.h4
-rw-r--r--src/core/hle/service/set/setting_formats/system_settings.h4
-rw-r--r--src/core/hle/service/set/system_settings_server.cpp39
-rw-r--r--src/core/hle/service/set/system_settings_server.h4
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp8
-rw-r--r--src/core/hle/service/vi/display/vi_display.h15
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp2
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h9
-rw-r--r--src/core/hle/service/vi/vi.cpp13
-rw-r--r--src/core/internal_network/network.cpp32
-rw-r--r--src/core/internal_network/sockets.h3
-rw-r--r--src/frontend_common/config.cpp8
-rw-r--r--src/hid_core/CMakeLists.txt7
-rw-r--r--src/hid_core/hid_result.h8
-rw-r--r--src/hid_core/hid_types.h28
-rw-r--r--src/hid_core/resource_manager.cpp61
-rw-r--r--src/hid_core/resource_manager.h89
-rw-r--r--src/hid_core/resources/applet_resource.cpp6
-rw-r--r--src/hid_core/resources/applet_resource.h8
-rw-r--r--src/hid_core/resources/npad/npad.cpp3
-rw-r--r--src/hid_core/resources/npad/npad.h4
-rw-r--r--src/hid_core/resources/npad/npad_resource.cpp6
-rw-r--r--src/hid_core/resources/touch_screen/gesture.cpp367
-rw-r--r--src/hid_core/resources/touch_screen/gesture.h87
-rw-r--r--src/hid_core/resources/touch_screen/gesture_handler.cpp260
-rw-r--r--src/hid_core/resources/touch_screen/gesture_handler.h55
-rw-r--r--src/hid_core/resources/touch_screen/gesture_types.h77
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen.cpp209
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen.h71
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen_driver.cpp114
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen_driver.h47
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen_resource.cpp579
-rw-r--r--src/hid_core/resources/touch_screen/touch_screen_resource.h126
-rw-r--r--src/hid_core/resources/touch_screen/touch_types.h61
-rw-r--r--src/video_core/CMakeLists.txt39
-rw-r--r--src/video_core/engines/maxwell_dma.h1
-rw-r--r--src/video_core/engines/sw_blitter/blitter.cpp37
-rw-r--r--src/video_core/framebuffer_config.cpp55
-rw-r--r--src/video_core/framebuffer_config.h6
-rw-r--r--src/video_core/gpu.cpp29
-rw-r--r--src/video_core/gpu.h4
-rw-r--r--src/video_core/gpu_thread.cpp6
-rw-r--r--src/video_core/gpu_thread.h15
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt11
-rw-r--r--src/video_core/host_shaders/fidelityfx_fsr.frag (renamed from src/video_core/host_shaders/fidelityfx_fsr.comp)33
-rw-r--r--src/video_core/host_shaders/fxaa.vert4
-rw-r--r--src/video_core/host_shaders/opengl_present_scaleforce.frag12
-rw-r--r--src/video_core/host_shaders/present_bicubic.frag12
-rw-r--r--src/video_core/host_shaders/present_gaussian.frag12
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr.vert13
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag (renamed from src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.comp)2
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag (renamed from src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.comp)2
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag (renamed from src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.comp)2
-rw-r--r--src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag (renamed from src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.comp)2
-rw-r--r--src/video_core/host_shaders/vulkan_present.frag2
-rw-r--r--src/video_core/host_shaders/vulkan_present.vert33
-rw-r--r--src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag1
-rw-r--r--src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag2
-rw-r--r--src/video_core/rasterizer_interface.h6
-rw-r--r--src/video_core/renderer_base.h2
-rw-r--r--src/video_core/renderer_null/null_rasterizer.cpp4
-rw-r--r--src/video_core/renderer_null/null_rasterizer.h2
-rw-r--r--src/video_core/renderer_null/renderer_null.cpp4
-rw-r--r--src/video_core/renderer_null/renderer_null.h2
-rw-r--r--src/video_core/renderer_opengl/gl_blit_screen.cpp96
-rw-r--r--src/video_core/renderer_opengl/gl_blit_screen.h71
-rw-r--r--src/video_core/renderer_opengl/gl_fsr.cpp101
-rw-r--r--src/video_core/renderer_opengl/gl_fsr.h43
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp36
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h14
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h2
-rw-r--r--src/video_core/renderer_opengl/present/filters.cpp39
-rw-r--r--src/video_core/renderer_opengl/present/filters.h17
-rw-r--r--src/video_core/renderer_opengl/present/fsr.cpp98
-rw-r--r--src/video_core/renderer_opengl/present/fsr.h39
-rw-r--r--src/video_core/renderer_opengl/present/fxaa.cpp41
-rw-r--r--src/video_core/renderer_opengl/present/fxaa.h27
-rw-r--r--src/video_core/renderer_opengl/present/layer.cpp215
-rw-r--r--src/video_core/renderer_opengl/present/layer.h80
-rw-r--r--src/video_core/renderer_opengl/present/present_uniforms.h43
-rw-r--r--src/video_core/renderer_opengl/present/smaa.cpp102
-rw-r--r--src/video_core/renderer_opengl/present/smaa.h35
-rw-r--r--src/video_core/renderer_opengl/present/util.h43
-rw-r--r--src/video_core/renderer_opengl/present/window_adapt_pass.cpp103
-rw-r--r--src/video_core/renderer_opengl/present/window_adapt_pass.h47
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp568
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h93
-rw-r--r--src/video_core/renderer_vulkan/present/anti_alias_pass.h25
-rw-r--r--src/video_core/renderer_vulkan/present/filters.cpp56
-rw-r--r--src/video_core/renderer_vulkan/present/filters.h18
-rw-r--r--src/video_core/renderer_vulkan/present/fsr.cpp226
-rw-r--r--src/video_core/renderer_vulkan/present/fsr.h69
-rw-r--r--src/video_core/renderer_vulkan/present/fxaa.cpp148
-rw-r--r--src/video_core/renderer_vulkan/present/fxaa.h63
-rw-r--r--src/video_core/renderer_vulkan/present/layer.cpp336
-rw-r--r--src/video_core/renderer_vulkan/present/layer.h92
-rw-r--r--src/video_core/renderer_vulkan/present/present_push_constants.h34
-rw-r--r--src/video_core/renderer_vulkan/present/smaa.cpp277
-rw-r--r--src/video_core/renderer_vulkan/present/smaa.h (renamed from src/video_core/renderer_vulkan/vk_smaa.h)9
-rw-r--r--src/video_core/renderer_vulkan/present/util.cpp (renamed from src/video_core/renderer_vulkan/vk_smaa.cpp)470
-rw-r--r--src/video_core/renderer_vulkan/present/util.h56
-rw-r--r--src/video_core/renderer_vulkan/present/window_adapt_pass.cpp137
-rw-r--r--src/video_core/renderer_vulkan/present/window_adapt_pass.h58
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp177
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h9
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp1536
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h144
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.cpp420
-rw-r--r--src/video_core/renderer_vulkan/vk_fsr.h52
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp33
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h14
-rw-r--r--src/video_core/texture_cache/texture_cache.h96
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h8
-rw-r--r--src/yuzu/applets/qt_profile_select.cpp4
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp10
-rw-r--r--src/yuzu/applets/qt_software_keyboard.h17
-rw-r--r--src/yuzu/applets/qt_web_browser.cpp12
-rw-r--r--src/yuzu/applets/qt_web_browser.h10
-rw-r--r--src/yuzu/configuration/configure_audio.cpp117
-rw-r--r--src/yuzu/configuration/configure_audio.h7
-rw-r--r--src/yuzu/configuration/configure_input.cpp18
-rw-r--r--src/yuzu/game_list.cpp12
-rw-r--r--src/yuzu/game_list.h3
-rw-r--r--src/yuzu/main.cpp168
-rw-r--r--src/yuzu/main.h38
-rw-r--r--src/yuzu_cmd/yuzu.cpp6
282 files changed, 13315 insertions, 9557 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index 53137b2e2..6ebb46af7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -261,7 +261,7 @@ object NativeLibrary {
261 /** 261 /**
262 * Begins emulation. 262 * Begins emulation.
263 */ 263 */
264 external fun run(path: String?, programIndex: Int = 0) 264 external fun run(path: String?, programIndex: Int, frontendInitiated: Boolean)
265 265
266 // Surface Handling 266 // Surface Handling
267 external fun surfaceChanged(surf: Surface?) 267 external fun surfaceChanged(surf: Surface?)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 1f591ced1..937b8faf1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -927,7 +927,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
927 emulationThread.join() 927 emulationThread.join()
928 emulationThread = Thread({ 928 emulationThread = Thread({
929 Log.debug("[EmulationFragment] Starting emulation thread.") 929 Log.debug("[EmulationFragment] Starting emulation thread.")
930 NativeLibrary.run(gamePath, programIndex) 930 NativeLibrary.run(gamePath, programIndex, false)
931 }, "NativeEmulation") 931 }, "NativeEmulation")
932 emulationThread.start() 932 emulationThread.start()
933 } 933 }
@@ -981,7 +981,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
981 State.STOPPED -> { 981 State.STOPPED -> {
982 emulationThread = Thread({ 982 emulationThread = Thread({
983 Log.debug("[EmulationFragment] Starting emulation thread.") 983 Log.debug("[EmulationFragment] Starting emulation thread.")
984 NativeLibrary.run(gamePath, programIndex) 984 NativeLibrary.run(gamePath, programIndex, true)
985 }, "NativeEmulation") 985 }, "NativeEmulation")
986 emulationThread.start() 986 emulationThread.start()
987 } 987 }
diff --git a/src/android/app/src/main/jni/applets/software_keyboard.cpp b/src/android/app/src/main/jni/applets/software_keyboard.cpp
index 74e040478..9943483e8 100644
--- a/src/android/app/src/main/jni/applets/software_keyboard.cpp
+++ b/src/android/app/src/main/jni/applets/software_keyboard.cpp
@@ -82,7 +82,7 @@ AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobj
82 const jstring string = reinterpret_cast<jstring>(env->GetObjectField( 82 const jstring string = reinterpret_cast<jstring>(env->GetObjectField(
83 object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;"))); 83 object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;")));
84 return ResultData{GetJString(env, string), 84 return ResultData{GetJString(env, string),
85 static_cast<Service::AM::Applets::SwkbdResult>(env->GetIntField( 85 static_cast<Service::AM::Frontend::SwkbdResult>(env->GetIntField(
86 object, env->GetFieldID(s_keyboard_data_class, "result", "I")))}; 86 object, env->GetFieldID(s_keyboard_data_class, "result", "I")))};
87} 87}
88 88
@@ -149,7 +149,7 @@ void AndroidKeyboard::ShowNormalKeyboard() const {
149} 149}
150 150
151void AndroidKeyboard::ShowTextCheckDialog( 151void AndroidKeyboard::ShowTextCheckDialog(
152 Service::AM::Applets::SwkbdTextCheckResult text_check_result, 152 Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
153 std::u16string text_check_message) const { 153 std::u16string text_check_message) const {
154 LOG_WARNING(Frontend, "(STUBBED) called, backend requested to show the text check dialog."); 154 LOG_WARNING(Frontend, "(STUBBED) called, backend requested to show the text check dialog.");
155} 155}
@@ -204,7 +204,7 @@ void AndroidKeyboard::InlineTextChanged(
204 "\ncursor_position={}", 204 "\ncursor_position={}",
205 Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position); 205 Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position);
206 206
207 submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, 207 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString,
208 text_parameters.input_text, text_parameters.cursor_position); 208 text_parameters.input_text, text_parameters.cursor_position);
209} 209}
210 210
@@ -219,7 +219,7 @@ void AndroidKeyboard::SubmitInlineKeyboardText(std::u16string submitted_text) {
219 219
220 m_current_text += submitted_text; 220 m_current_text += submitted_text;
221 221
222 submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, m_current_text, 222 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
223 m_current_text.size()); 223 m_current_text.size());
224} 224}
225 225
@@ -236,12 +236,12 @@ void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
236 case KEYCODE_BACK: 236 case KEYCODE_BACK:
237 case KEYCODE_ENTER: 237 case KEYCODE_ENTER:
238 m_is_inline_active = false; 238 m_is_inline_active = false;
239 submit_inline_callback(Service::AM::Applets::SwkbdReplyType::DecidedEnter, m_current_text, 239 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::DecidedEnter, m_current_text,
240 static_cast<s32>(m_current_text.size())); 240 static_cast<s32>(m_current_text.size()));
241 break; 241 break;
242 case KEYCODE_DEL: 242 case KEYCODE_DEL:
243 m_current_text.pop_back(); 243 m_current_text.pop_back();
244 submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, m_current_text, 244 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
245 m_current_text.size()); 245 m_current_text.size());
246 break; 246 break;
247 } 247 }
diff --git a/src/android/app/src/main/jni/applets/software_keyboard.h b/src/android/app/src/main/jni/applets/software_keyboard.h
index b2fb59b68..2affc01f6 100644
--- a/src/android/app/src/main/jni/applets/software_keyboard.h
+++ b/src/android/app/src/main/jni/applets/software_keyboard.h
@@ -24,7 +24,7 @@ public:
24 24
25 void ShowNormalKeyboard() const override; 25 void ShowNormalKeyboard() const override;
26 26
27 void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, 27 void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
28 std::u16string text_check_message) const override; 28 std::u16string text_check_message) const override;
29 29
30 void ShowInlineKeyboard( 30 void ShowInlineKeyboard(
@@ -45,7 +45,7 @@ private:
45 static ResultData CreateFromFrontend(jobject object); 45 static ResultData CreateFromFrontend(jobject object);
46 46
47 std::string text; 47 std::string text;
48 Service::AM::Applets::SwkbdResult result{}; 48 Service::AM::Frontend::SwkbdResult result{};
49 }; 49 };
50 50
51 void SubmitNormalText(const ResultData& result) const; 51 void SubmitNormalText(const ResultData& result) const;
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 64627db88..654510129 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -42,14 +42,15 @@
42#include "core/frontend/applets/cabinet.h" 42#include "core/frontend/applets/cabinet.h"
43#include "core/frontend/applets/controller.h" 43#include "core/frontend/applets/controller.h"
44#include "core/frontend/applets/error.h" 44#include "core/frontend/applets/error.h"
45#include "core/frontend/applets/general_frontend.h" 45#include "core/frontend/applets/general.h"
46#include "core/frontend/applets/mii_edit.h" 46#include "core/frontend/applets/mii_edit.h"
47#include "core/frontend/applets/profile_select.h" 47#include "core/frontend/applets/profile_select.h"
48#include "core/frontend/applets/software_keyboard.h" 48#include "core/frontend/applets/software_keyboard.h"
49#include "core/frontend/applets/web_browser.h" 49#include "core/frontend/applets/web_browser.h"
50#include "core/hle/service/am/applet_ae.h" 50#include "core/hle/service/am/applet_ae.h"
51#include "core/hle/service/am/applet_manager.h"
51#include "core/hle/service/am/applet_oe.h" 52#include "core/hle/service/am/applet_oe.h"
52#include "core/hle/service/am/applets/applets.h" 53#include "core/hle/service/am/frontend/applets.h"
53#include "core/hle/service/filesystem/filesystem.h" 54#include "core/hle/service/filesystem/filesystem.h"
54#include "core/loader/loader.h" 55#include "core/loader/loader.h"
55#include "frontend_common/config.h" 56#include "frontend_common/config.h"
@@ -211,8 +212,15 @@ void EmulationSession::InitializeSystem(bool reload) {
211 m_system.GetFileSystemController().CreateFactories(*m_vfs); 212 m_system.GetFileSystemController().CreateFactories(*m_vfs);
212} 213}
213 214
215void EmulationSession::SetAppletId(int applet_id) {
216 m_applet_id = applet_id;
217 m_system.GetFrontendAppletHolder().SetCurrentAppletId(
218 static_cast<Service::AM::AppletId>(m_applet_id));
219}
220
214Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath, 221Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath,
215 const std::size_t program_index) { 222 const std::size_t program_index,
223 const bool frontend_initiated) {
216 std::scoped_lock lock(m_mutex); 224 std::scoped_lock lock(m_mutex);
217 225
218 // Create the render window. 226 // Create the render window.
@@ -226,7 +234,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
226 m_system.ApplySettings(); 234 m_system.ApplySettings();
227 Settings::LogSettings(); 235 Settings::LogSettings();
228 m_system.HIDCore().ReloadInputDevices(); 236 m_system.HIDCore().ReloadInputDevices();
229 m_system.SetAppletFrontendSet({ 237 m_system.SetFrontendAppletSet({
230 nullptr, // Amiibo Settings 238 nullptr, // Amiibo Settings
231 nullptr, // Controller Selector 239 nullptr, // Controller Selector
232 nullptr, // Error Display 240 nullptr, // Error Display
@@ -242,8 +250,13 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
242 ConfigureFilesystemProvider(filepath); 250 ConfigureFilesystemProvider(filepath);
243 251
244 // Load the ROM. 252 // Load the ROM.
245 m_load_result = 253 Service::AM::FrontendAppletParameters params{
246 m_system.Load(EmulationSession::GetInstance().Window(), filepath, 0, program_index); 254 .applet_id = static_cast<Service::AM::AppletId>(m_applet_id),
255 .launch_type = frontend_initiated ? Service::AM::LaunchType::FrontendInitiated
256 : Service::AM::LaunchType::ApplicationInitiated,
257 .program_index = static_cast<s32>(program_index),
258 };
259 m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params);
247 if (m_load_result != Core::SystemResultStatus::Success) { 260 if (m_load_result != Core::SystemResultStatus::Success) {
248 return m_load_result; 261 return m_load_result;
249 } 262 }
@@ -339,6 +352,9 @@ void EmulationSession::RunEmulation() {
339 } 352 }
340 } 353 }
341 } 354 }
355
356 // Reset current applet ID.
357 m_applet_id = static_cast<int>(Service::AM::AppletId::Application);
342} 358}
343 359
344bool EmulationSession::IsHandheldOnly() { 360bool EmulationSession::IsHandheldOnly() {
@@ -434,7 +450,8 @@ u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
434} 450}
435 451
436static Core::SystemResultStatus RunEmulation(const std::string& filepath, 452static Core::SystemResultStatus RunEmulation(const std::string& filepath,
437 const size_t program_index = 0) { 453 const size_t program_index,
454 const bool frontend_initiated) {
438 MicroProfileOnThreadCreate("EmuThread"); 455 MicroProfileOnThreadCreate("EmuThread");
439 SCOPE_EXIT({ MicroProfileShutdown(); }); 456 SCOPE_EXIT({ MicroProfileShutdown(); });
440 457
@@ -447,7 +464,8 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
447 464
448 SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); }); 465 SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
449 466
450 jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index); 467 jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath, program_index,
468 frontend_initiated);
451 if (result != Core::SystemResultStatus::Success) { 469 if (result != Core::SystemResultStatus::Success) {
452 return result; 470 return result;
453 } 471 }
@@ -744,10 +762,12 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj
744} 762}
745 763
746void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path, 764void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path,
747 jint j_program_index) { 765 jint j_program_index,
766 jboolean j_frontend_initiated) {
748 const std::string path = GetJString(env, j_path); 767 const std::string path = GetJString(env, j_path);
749 768
750 const Core::SystemResultStatus result{RunEmulation(path, j_program_index)}; 769 const Core::SystemResultStatus result{
770 RunEmulation(path, j_program_index, j_frontend_initiated)};
751 if (result != Core::SystemResultStatus::Success) { 771 if (result != Core::SystemResultStatus::Success) {
752 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), 772 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
753 IDCache::GetExitEmulationActivity(), static_cast<int>(result)); 773 IDCache::GetExitEmulationActivity(), static_cast<int>(result));
@@ -809,13 +829,12 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getAppletLaunchPath(JNIEnv* env, j
809 829
810void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz, 830void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz,
811 jint jappletId) { 831 jint jappletId) {
812 EmulationSession::GetInstance().System().GetAppletManager().SetCurrentAppletId( 832 EmulationSession::GetInstance().SetAppletId(jappletId);
813 static_cast<Service::AM::Applets::AppletId>(jappletId));
814} 833}
815 834
816void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCabinetMode(JNIEnv* env, jclass clazz, 835void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCabinetMode(JNIEnv* env, jclass clazz,
817 jint jcabinetMode) { 836 jint jcabinetMode) {
818 EmulationSession::GetInstance().System().GetAppletManager().SetCabinetMode( 837 EmulationSession::GetInstance().System().GetFrontendAppletHolder().SetCabinetMode(
819 static_cast<Service::NFP::CabinetMode>(jcabinetMode)); 838 static_cast<Service::NFP::CabinetMode>(jcabinetMode));
820} 839}
821 840
diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h
index bfe3fccca..e49d4e015 100644
--- a/src/android/app/src/main/jni/native.h
+++ b/src/android/app/src/main/jni/native.h
@@ -45,8 +45,10 @@ public:
45 const Core::PerfStatsResults& PerfStats(); 45 const Core::PerfStatsResults& PerfStats();
46 void ConfigureFilesystemProvider(const std::string& filepath); 46 void ConfigureFilesystemProvider(const std::string& filepath);
47 void InitializeSystem(bool reload); 47 void InitializeSystem(bool reload);
48 void SetAppletId(int applet_id);
48 Core::SystemResultStatus InitializeEmulation(const std::string& filepath, 49 Core::SystemResultStatus InitializeEmulation(const std::string& filepath,
49 const std::size_t program_index = 0); 50 const std::size_t program_index,
51 const bool frontend_initiated);
50 52
51 bool IsHandheldOnly(); 53 bool IsHandheldOnly();
52 void SetDeviceType([[maybe_unused]] int index, int type); 54 void SetDeviceType([[maybe_unused]] int index, int type);
@@ -79,6 +81,7 @@ private:
79 std::atomic<bool> m_is_paused = false; 81 std::atomic<bool> m_is_paused = false;
80 SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; 82 SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
81 std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; 83 std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
84 int m_applet_id{1};
82 85
83 // GPU driver parameters 86 // GPU driver parameters
84 std::shared_ptr<Common::DynamicLibrary> m_vulkan_library; 87 std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index e30fea268..85926fc8f 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -106,6 +106,7 @@ add_library(common STATIC
106 precompiled_headers.h 106 precompiled_headers.h
107 quaternion.h 107 quaternion.h
108 range_map.h 108 range_map.h
109 range_mutex.h
109 reader_writer_queue.h 110 reader_writer_queue.h
110 ring_buffer.h 111 ring_buffer.h
111 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp 112 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
diff --git a/src/common/range_mutex.h b/src/common/range_mutex.h
new file mode 100644
index 000000000..d6c949811
--- /dev/null
+++ b/src/common/range_mutex.h
@@ -0,0 +1,93 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <condition_variable>
7#include <mutex>
8
9#include "common/intrusive_list.h"
10
11namespace Common {
12
13class ScopedRangeLock;
14
15class RangeMutex {
16public:
17 explicit RangeMutex() = default;
18 ~RangeMutex() = default;
19
20private:
21 friend class ScopedRangeLock;
22
23 void Lock(ScopedRangeLock& l);
24 void Unlock(ScopedRangeLock& l);
25 bool HasIntersectionLocked(ScopedRangeLock& l);
26
27private:
28 std::mutex m_mutex;
29 std::condition_variable m_cv;
30
31 using LockList = Common::IntrusiveListBaseTraits<ScopedRangeLock>::ListType;
32 LockList m_list;
33};
34
35class ScopedRangeLock : public Common::IntrusiveListBaseNode<ScopedRangeLock> {
36public:
37 explicit ScopedRangeLock(RangeMutex& mutex, u64 address, u64 size)
38 : m_mutex(mutex), m_address(address), m_size(size) {
39 if (m_size > 0) {
40 m_mutex.Lock(*this);
41 }
42 }
43 ~ScopedRangeLock() {
44 if (m_size > 0) {
45 m_mutex.Unlock(*this);
46 }
47 }
48
49 u64 GetAddress() const {
50 return m_address;
51 }
52
53 u64 GetSize() const {
54 return m_size;
55 }
56
57private:
58 RangeMutex& m_mutex;
59 const u64 m_address{};
60 const u64 m_size{};
61};
62
63inline void RangeMutex::Lock(ScopedRangeLock& l) {
64 std::unique_lock lk{m_mutex};
65 m_cv.wait(lk, [&] { return !HasIntersectionLocked(l); });
66 m_list.push_back(l);
67}
68
69inline void RangeMutex::Unlock(ScopedRangeLock& l) {
70 {
71 std::scoped_lock lk{m_mutex};
72 m_list.erase(m_list.iterator_to(l));
73 }
74 m_cv.notify_all();
75}
76
77inline bool RangeMutex::HasIntersectionLocked(ScopedRangeLock& l) {
78 const auto cur_begin = l.GetAddress();
79 const auto cur_last = l.GetAddress() + l.GetSize() - 1;
80
81 for (const auto& other : m_list) {
82 const auto other_begin = other.GetAddress();
83 const auto other_last = other.GetAddress() + other.GetSize() - 1;
84
85 if (cur_begin <= other_last && other_begin <= cur_last) {
86 return true;
87 }
88 }
89
90 return false;
91}
92
93} // namespace Common
diff --git a/src/common/settings.h b/src/common/settings.h
index 16749ab68..f1b1add56 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -134,12 +134,12 @@ struct Values {
134 Linkage linkage{}; 134 Linkage linkage{};
135 135
136 // Audio 136 // Audio
137 Setting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", Category::Audio, 137 SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
138 Specialization::RuntimeList}; 138 Category::Audio, Specialization::RuntimeList};
139 Setting<std::string> audio_output_device_id{linkage, "auto", "output_device", Category::Audio, 139 SwitchableSetting<std::string> audio_output_device_id{
140 Specialization::RuntimeList}; 140 linkage, "auto", "output_device", Category::Audio, Specialization::RuntimeList};
141 Setting<std::string> audio_input_device_id{linkage, "auto", "input_device", Category::Audio, 141 SwitchableSetting<std::string> audio_input_device_id{
142 Specialization::RuntimeList}; 142 linkage, "auto", "input_device", Category::Audio, Specialization::RuntimeList};
143 SwitchableSetting<AudioMode, true> sound_index{ 143 SwitchableSetting<AudioMode, true> sound_index{
144 linkage, AudioMode::Stereo, AudioMode::Mono, AudioMode::Surround, 144 linkage, AudioMode::Stereo, AudioMode::Mono, AudioMode::Surround,
145 "sound_index", Category::SystemAudio, Specialization::Default, true, 145 "sound_index", Category::SystemAudio, Specialization::Default, true,
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ea6b2c285..eb8f643a2 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -176,8 +176,8 @@ add_library(core STATIC
176 frontend/applets/controller.h 176 frontend/applets/controller.h
177 frontend/applets/error.cpp 177 frontend/applets/error.cpp
178 frontend/applets/error.h 178 frontend/applets/error.h
179 frontend/applets/general_frontend.cpp 179 frontend/applets/general.cpp
180 frontend/applets/general_frontend.h 180 frontend/applets/general.h
181 frontend/applets/mii_edit.cpp 181 frontend/applets/mii_edit.cpp
182 frontend/applets/mii_edit.h 182 frontend/applets/mii_edit.h
183 frontend/applets/profile_select.cpp 183 frontend/applets/profile_select.cpp
@@ -390,39 +390,101 @@ add_library(core STATIC
390 hle/service/acc/errors.h 390 hle/service/acc/errors.h
391 hle/service/acc/profile_manager.cpp 391 hle/service/acc/profile_manager.cpp
392 hle/service/acc/profile_manager.h 392 hle/service/acc/profile_manager.h
393 hle/service/am/frontend/applet_cabinet.cpp
394 hle/service/am/frontend/applet_cabinet.h
395 hle/service/am/frontend/applet_controller.cpp
396 hle/service/am/frontend/applet_controller.h
397 hle/service/am/frontend/applet_error.cpp
398 hle/service/am/frontend/applet_error.h
399 hle/service/am/frontend/applet_general.cpp
400 hle/service/am/frontend/applet_general.h
401 hle/service/am/frontend/applet_mii_edit.cpp
402 hle/service/am/frontend/applet_mii_edit.h
403 hle/service/am/frontend/applet_mii_edit_types.h
404 hle/service/am/frontend/applet_profile_select.cpp
405 hle/service/am/frontend/applet_profile_select.h
406 hle/service/am/frontend/applet_software_keyboard.cpp
407 hle/service/am/frontend/applet_software_keyboard.h
408 hle/service/am/frontend/applet_software_keyboard_types.h
409 hle/service/am/frontend/applet_web_browser.cpp
410 hle/service/am/frontend/applet_web_browser.h
411 hle/service/am/frontend/applet_web_browser_types.h
412 hle/service/am/frontend/applets.cpp
413 hle/service/am/frontend/applets.h
393 hle/service/am/am.cpp 414 hle/service/am/am.cpp
394 hle/service/am/am.h 415 hle/service/am/am.h
416 hle/service/am/am_results.h
417 hle/service/am/am_types.h
418 hle/service/am/applet.cpp
419 hle/service/am/applet.h
395 hle/service/am/applet_ae.cpp 420 hle/service/am/applet_ae.cpp
396 hle/service/am/applet_ae.h 421 hle/service/am/applet_ae.h
422 hle/service/am/applet_manager.cpp
423 hle/service/am/applet_data_broker.cpp
424 hle/service/am/applet_data_broker.h
425 hle/service/am/applet_manager.h
397 hle/service/am/applet_oe.cpp 426 hle/service/am/applet_oe.cpp
398 hle/service/am/applet_oe.h 427 hle/service/am/applet_oe.h
399 hle/service/am/applets/applet_cabinet.cpp 428 hle/service/am/applet_common_functions.cpp
400 hle/service/am/applets/applet_cabinet.h 429 hle/service/am/applet_common_functions.h
401 hle/service/am/applets/applet_controller.cpp 430 hle/service/am/applet_message_queue.cpp
402 hle/service/am/applets/applet_controller.h 431 hle/service/am/applet_message_queue.h
403 hle/service/am/applets/applet_error.cpp 432 hle/service/am/application_creator.cpp
404 hle/service/am/applets/applet_error.h 433 hle/service/am/application_creator.h
405 hle/service/am/applets/applet_general_backend.cpp 434 hle/service/am/application_functions.cpp
406 hle/service/am/applets/applet_general_backend.h 435 hle/service/am/application_functions.h
407 hle/service/am/applets/applet_mii_edit.cpp 436 hle/service/am/application_proxy.cpp
408 hle/service/am/applets/applet_mii_edit.h 437 hle/service/am/application_proxy.h
409 hle/service/am/applets/applet_mii_edit_types.h 438 hle/service/am/audio_controller.cpp
410 hle/service/am/applets/applet_profile_select.cpp 439 hle/service/am/audio_controller.h
411 hle/service/am/applets/applet_profile_select.h 440 hle/service/am/common_state_getter.cpp
412 hle/service/am/applets/applet_software_keyboard.cpp 441 hle/service/am/common_state_getter.h
413 hle/service/am/applets/applet_software_keyboard.h 442 hle/service/am/debug_functions.cpp
414 hle/service/am/applets/applet_software_keyboard_types.h 443 hle/service/am/debug_functions.h
415 hle/service/am/applets/applet_web_browser.cpp 444 hle/service/am/display_controller.cpp
416 hle/service/am/applets/applet_web_browser.h 445 hle/service/am/display_controller.h
417 hle/service/am/applets/applet_web_browser_types.h 446 hle/service/am/global_state_controller.cpp
418 hle/service/am/applets/applets.cpp 447 hle/service/am/global_state_controller.h
419 hle/service/am/applets/applets.h 448 hle/service/am/hid_registration.cpp
449 hle/service/am/hid_registration.h
450 hle/service/am/home_menu_functions.cpp
451 hle/service/am/home_menu_functions.h
420 hle/service/am/idle.cpp 452 hle/service/am/idle.cpp
421 hle/service/am/idle.h 453 hle/service/am/idle.h
454 hle/service/am/library_applet_accessor.cpp
455 hle/service/am/library_applet_accessor.h
456 hle/service/am/library_applet_creator.cpp
457 hle/service/am/library_applet_creator.h
458 hle/service/am/library_applet_proxy.cpp
459 hle/service/am/library_applet_proxy.h
460 hle/service/am/library_applet_self_accessor.cpp
461 hle/service/am/library_applet_self_accessor.h
462 hle/service/am/library_applet_storage.cpp
463 hle/service/am/library_applet_storage.h
464 hle/service/am/lock_accessor.cpp
465 hle/service/am/lock_accessor.h
466 hle/service/am/managed_layer_holder.cpp
467 hle/service/am/managed_layer_holder.h
422 hle/service/am/omm.cpp 468 hle/service/am/omm.cpp
423 hle/service/am/omm.h 469 hle/service/am/omm.h
470 hle/service/am/process_winding_controller.cpp
471 hle/service/am/process_winding_controller.h
472 hle/service/am/process.cpp
473 hle/service/am/process.h
474 hle/service/am/self_controller.cpp
475 hle/service/am/self_controller.h
476 hle/service/am/system_applet_proxy.cpp
477 hle/service/am/system_applet_proxy.h
478 hle/service/am/system_buffer_manager.cpp
479 hle/service/am/system_buffer_manager.h
424 hle/service/am/spsm.cpp 480 hle/service/am/spsm.cpp
425 hle/service/am/spsm.h 481 hle/service/am/spsm.h
482 hle/service/am/storage_accessor.cpp
483 hle/service/am/storage_accessor.h
484 hle/service/am/storage.cpp
485 hle/service/am/storage.h
486 hle/service/am/window_controller.cpp
487 hle/service/am/window_controller.h
426 hle/service/aoc/aoc_u.cpp 488 hle/service/aoc/aoc_u.cpp
427 hle/service/aoc/aoc_u.h 489 hle/service/aoc/aoc_u.h
428 hle/service/apm/apm.cpp 490 hle/service/apm/apm.cpp
@@ -486,6 +548,8 @@ add_library(core STATIC
486 hle/service/es/es.h 548 hle/service/es/es.h
487 hle/service/eupld/eupld.cpp 549 hle/service/eupld/eupld.cpp
488 hle/service/eupld/eupld.h 550 hle/service/eupld/eupld.h
551 hle/service/event.cpp
552 hle/service/event.h
489 hle/service/fatal/fatal.cpp 553 hle/service/fatal/fatal.cpp
490 hle/service/fatal/fatal.h 554 hle/service/fatal/fatal.h
491 hle/service/fatal/fatal_p.cpp 555 hle/service/fatal/fatal_p.cpp
@@ -711,6 +775,9 @@ add_library(core STATIC
711 hle/service/nvnflinger/graphic_buffer_producer.h 775 hle/service/nvnflinger/graphic_buffer_producer.h
712 hle/service/nvnflinger/hos_binder_driver_server.cpp 776 hle/service/nvnflinger/hos_binder_driver_server.cpp
713 hle/service/nvnflinger/hos_binder_driver_server.h 777 hle/service/nvnflinger/hos_binder_driver_server.h
778 hle/service/nvnflinger/hardware_composer.cpp
779 hle/service/nvnflinger/hardware_composer.h
780 hle/service/nvnflinger/hwc_layer.h
714 hle/service/nvnflinger/nvnflinger.cpp 781 hle/service/nvnflinger/nvnflinger.cpp
715 hle/service/nvnflinger/nvnflinger.h 782 hle/service/nvnflinger/nvnflinger.h
716 hle/service/nvnflinger/parcel.h 783 hle/service/nvnflinger/parcel.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 11bf8d2f6..435ef6793 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -36,7 +36,8 @@
36#include "core/hle/kernel/kernel.h" 36#include "core/hle/kernel/kernel.h"
37#include "core/hle/kernel/physical_core.h" 37#include "core/hle/kernel/physical_core.h"
38#include "core/hle/service/acc/profile_manager.h" 38#include "core/hle/service/acc/profile_manager.h"
39#include "core/hle/service/am/applets/applets.h" 39#include "core/hle/service/am/applet_manager.h"
40#include "core/hle/service/am/frontend/applets.h"
40#include "core/hle/service/apm/apm_controller.h" 41#include "core/hle/service/apm/apm_controller.h"
41#include "core/hle/service/filesystem/filesystem.h" 42#include "core/hle/service/filesystem/filesystem.h"
42#include "core/hle/service/glue/glue_manager.h" 43#include "core/hle/service/glue/glue_manager.h"
@@ -135,8 +136,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
135 136
136struct System::Impl { 137struct System::Impl {
137 explicit Impl(System& system) 138 explicit Impl(System& system)
138 : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, 139 : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system},
139 cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{} {} 140 reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
140 141
141 void Initialize(System& system) { 142 void Initialize(System& system) {
142 device_memory = std::make_unique<Core::DeviceMemory>(); 143 device_memory = std::make_unique<Core::DeviceMemory>();
@@ -157,7 +158,7 @@ struct System::Impl {
157 } 158 }
158 159
159 // Create default implementations of applets if one is not provided. 160 // Create default implementations of applets if one is not provided.
160 applet_manager.SetDefaultAppletsIfMissing(); 161 frontend_applets.SetDefaultAppletsIfMissing();
161 162
162 is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); 163 is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
163 164
@@ -330,16 +331,27 @@ struct System::Impl {
330 } 331 }
331 332
332 SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, 333 SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
333 const std::string& filepath, u64 program_id, 334 const std::string& filepath,
334 std::size_t program_index) { 335 Service::AM::FrontendAppletParameters& params) {
335 app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath), 336 app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath),
336 program_id, program_index); 337 params.program_id, params.program_index);
337 338
338 if (!app_loader) { 339 if (!app_loader) {
339 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 340 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
340 return SystemResultStatus::ErrorGetLoader; 341 return SystemResultStatus::ErrorGetLoader;
341 } 342 }
342 343
344 if (app_loader->ReadProgramId(params.program_id) != Loader::ResultStatus::Success) {
345 LOG_ERROR(Core, "Failed to find title id for ROM!");
346 }
347
348 std::string name = "Unknown program";
349 if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) {
350 LOG_ERROR(Core, "Failed to read title for ROM!");
351 }
352
353 LOG_INFO(Core, "Loading {} ({})", name, params.program_id);
354
343 InitializeKernel(system); 355 InitializeKernel(system);
344 356
345 // Create the application process. 357 // Create the application process.
@@ -373,9 +385,14 @@ struct System::Impl {
373 cheat_engine->Initialize(); 385 cheat_engine->Initialize();
374 } 386 }
375 387
388 // Register with applet manager.
389 applet_manager.CreateAndInsertByFrontendAppletParameters(main_process->GetProcessId(),
390 params);
391
376 // All threads are started, begin main process execution, now that we're in the clear. 392 // All threads are started, begin main process execution, now that we're in the clear.
377 main_process->Run(load_parameters->main_thread_priority, 393 main_process->Run(load_parameters->main_thread_priority,
378 load_parameters->main_thread_stack_size); 394 load_parameters->main_thread_stack_size);
395 main_process->Close();
379 396
380 if (Settings::values.gamecard_inserted) { 397 if (Settings::values.gamecard_inserted) {
381 if (Settings::values.gamecard_current_game) { 398 if (Settings::values.gamecard_current_game) {
@@ -386,21 +403,13 @@ struct System::Impl {
386 } 403 }
387 } 404 }
388 405
389 if (app_loader->ReadProgramId(program_id) != Loader::ResultStatus::Success) { 406 perf_stats = std::make_unique<PerfStats>(params.program_id);
390 LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", load_result);
391 }
392 perf_stats = std::make_unique<PerfStats>(program_id);
393 // Reset counters and set time origin to current frame 407 // Reset counters and set time origin to current frame
394 GetAndResetPerfStats(); 408 GetAndResetPerfStats();
395 perf_stats->BeginSystemFrame(); 409 perf_stats->BeginSystemFrame();
396 410
397 std::string name = "Unknown Game";
398 if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) {
399 LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result);
400 }
401
402 std::string title_version; 411 std::string title_version;
403 const FileSys::PatchManager pm(program_id, system.GetFileSystemController(), 412 const FileSys::PatchManager pm(params.program_id, system.GetFileSystemController(),
404 system.GetContentProvider()); 413 system.GetContentProvider());
405 const auto metadata = pm.GetControlMetadata(); 414 const auto metadata = pm.GetControlMetadata();
406 if (metadata.first != nullptr) { 415 if (metadata.first != nullptr) {
@@ -409,14 +418,15 @@ struct System::Impl {
409 if (auto room_member = room_network.GetRoomMember().lock()) { 418 if (auto room_member = room_network.GetRoomMember().lock()) {
410 Network::GameInfo game_info; 419 Network::GameInfo game_info;
411 game_info.name = name; 420 game_info.name = name;
412 game_info.id = program_id; 421 game_info.id = params.program_id;
413 game_info.version = title_version; 422 game_info.version = title_version;
414 room_member->SendGameInfo(game_info); 423 room_member->SendGameInfo(game_info);
415 } 424 }
416 425
417 // Workarounds: 426 // Workarounds:
418 // Activate this in Super Smash Brothers Ultimate, it only affects AMD cards using AMDVLK 427 // Activate this in Super Smash Brothers Ultimate, it only affects AMD cards using AMDVLK
419 Settings::values.renderer_amdvlk_depth_bias_workaround = program_id == 0x1006A800016E000ULL; 428 Settings::values.renderer_amdvlk_depth_bias_workaround =
429 params.program_id == 0x1006A800016E000ULL;
420 430
421 status = SystemResultStatus::Success; 431 status = SystemResultStatus::Success;
422 return status; 432 return status;
@@ -455,6 +465,7 @@ struct System::Impl {
455 } 465 }
456 kernel.CloseServices(); 466 kernel.CloseServices();
457 kernel.ShutdownCores(); 467 kernel.ShutdownCores();
468 applet_manager.Reset();
458 services.reset(); 469 services.reset();
459 service_manager.reset(); 470 service_manager.reset();
460 fs_controller.Reset(); 471 fs_controller.Reset();
@@ -566,8 +577,9 @@ struct System::Impl {
566 577
567 std::unique_ptr<Tools::RenderdocAPI> renderdoc_api; 578 std::unique_ptr<Tools::RenderdocAPI> renderdoc_api;
568 579
569 /// Frontend applets 580 /// Applets
570 Service::AM::Applets::AppletManager applet_manager; 581 Service::AM::AppletManager applet_manager;
582 Service::AM::Frontend::FrontendAppletHolder frontend_applets;
571 583
572 /// APM (Performance) services 584 /// APM (Performance) services
573 Service::APM::Controller apm_controller{core_timing}; 585 Service::APM::Controller apm_controller{core_timing};
@@ -680,8 +692,8 @@ void System::InitializeDebugger() {
680} 692}
681 693
682SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath, 694SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
683 u64 program_id, std::size_t program_index) { 695 Service::AM::FrontendAppletParameters& params) {
684 return impl->Load(*this, emu_window, filepath, program_id, program_index); 696 return impl->Load(*this, emu_window, filepath, params);
685} 697}
686 698
687bool System::IsPoweredOn() const { 699bool System::IsPoweredOn() const {
@@ -871,19 +883,19 @@ void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
871 impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size); 883 impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size);
872} 884}
873 885
874void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) { 886void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
875 impl->applet_manager.SetAppletFrontendSet(std::move(set)); 887 impl->frontend_applets.SetFrontendAppletSet(std::move(set));
876} 888}
877 889
878void System::SetDefaultAppletFrontendSet() { 890Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() {
879 impl->applet_manager.SetDefaultAppletFrontendSet(); 891 return impl->frontend_applets;
880} 892}
881 893
882Service::AM::Applets::AppletManager& System::GetAppletManager() { 894const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const {
883 return impl->applet_manager; 895 return impl->frontend_applets;
884} 896}
885 897
886const Service::AM::Applets::AppletManager& System::GetAppletManager() const { 898Service::AM::AppletManager& System::GetAppletManager() {
887 return impl->applet_manager; 899 return impl->applet_manager;
888} 900}
889 901
diff --git a/src/core/core.h b/src/core/core.h
index d8862e9ce..90826bd3a 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -50,10 +50,15 @@ namespace Account {
50class ProfileManager; 50class ProfileManager;
51} // namespace Account 51} // namespace Account
52 52
53namespace AM::Applets { 53namespace AM {
54struct AppletFrontendSet; 54struct FrontendAppletParameters;
55class AppletManager; 55class AppletManager;
56} // namespace AM::Applets 56} // namespace AM
57
58namespace AM::Frontend {
59struct FrontendAppletSet;
60class FrontendAppletHolder;
61} // namespace AM::Frontend
57 62
58namespace APM { 63namespace APM {
59class Controller; 64class Controller;
@@ -203,8 +208,8 @@ public:
203 * @returns SystemResultStatus code, indicating if the operation succeeded. 208 * @returns SystemResultStatus code, indicating if the operation succeeded.
204 */ 209 */
205 [[nodiscard]] SystemResultStatus Load(Frontend::EmuWindow& emu_window, 210 [[nodiscard]] SystemResultStatus Load(Frontend::EmuWindow& emu_window,
206 const std::string& filepath, u64 program_id = 0, 211 const std::string& filepath,
207 std::size_t program_index = 0); 212 Service::AM::FrontendAppletParameters& params);
208 213
209 /** 214 /**
210 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an 215 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an
@@ -344,11 +349,13 @@ public:
344 const std::array<u8, 0x20>& build_id, u64 main_region_begin, 349 const std::array<u8, 0x20>& build_id, u64 main_region_begin,
345 u64 main_region_size); 350 u64 main_region_size);
346 351
347 void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set); 352 void SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set);
348 void SetDefaultAppletFrontendSet(); 353
354 [[nodiscard]] Service::AM::Frontend::FrontendAppletHolder& GetFrontendAppletHolder();
355 [[nodiscard]] const Service::AM::Frontend::FrontendAppletHolder& GetFrontendAppletHolder()
356 const;
349 357
350 [[nodiscard]] Service::AM::Applets::AppletManager& GetAppletManager(); 358 [[nodiscard]] Service::AM::AppletManager& GetAppletManager();
351 [[nodiscard]] const Service::AM::Applets::AppletManager& GetAppletManager() const;
352 359
353 void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider); 360 void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
354 361
diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h
index ffeed46cc..0568a821b 100644
--- a/src/core/device_memory_manager.h
+++ b/src/core/device_memory_manager.h
@@ -5,11 +5,13 @@
5 5
6#include <array> 6#include <array>
7#include <atomic> 7#include <atomic>
8#include <bit>
8#include <deque> 9#include <deque>
9#include <memory> 10#include <memory>
10#include <mutex> 11#include <mutex>
11 12
12#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/range_mutex.h"
13#include "common/scratch_buffer.h" 15#include "common/scratch_buffer.h"
14#include "common/virtual_buffer.h" 16#include "common/virtual_buffer.h"
15 17
@@ -180,31 +182,35 @@ private:
180 } 182 }
181 183
182 Common::VirtualBuffer<VAddr> cpu_backing_address; 184 Common::VirtualBuffer<VAddr> cpu_backing_address;
183 static constexpr size_t subentries = 8 / sizeof(u8); 185 using CounterType = u8;
186 using CounterAtomicType = std::atomic_uint8_t;
187 static constexpr size_t subentries = 8 / sizeof(CounterType);
184 static constexpr size_t subentries_mask = subentries - 1; 188 static constexpr size_t subentries_mask = subentries - 1;
189 static constexpr size_t subentries_shift =
190 std::countr_zero(sizeof(u64)) - std::countr_zero(sizeof(CounterType));
185 class CounterEntry final { 191 class CounterEntry final {
186 public: 192 public:
187 CounterEntry() = default; 193 CounterEntry() = default;
188 194
189 std::atomic_uint8_t& Count(std::size_t page) { 195 CounterAtomicType& Count(std::size_t page) {
190 return values[page & subentries_mask]; 196 return values[page & subentries_mask];
191 } 197 }
192 198
193 const std::atomic_uint8_t& Count(std::size_t page) const { 199 const CounterAtomicType& Count(std::size_t page) const {
194 return values[page & subentries_mask]; 200 return values[page & subentries_mask];
195 } 201 }
196 202
197 private: 203 private:
198 std::array<std::atomic_uint8_t, subentries> values{}; 204 std::array<CounterAtomicType, subentries> values{};
199 }; 205 };
200 static_assert(sizeof(CounterEntry) == subentries * sizeof(u8), 206 static_assert(sizeof(CounterEntry) == subentries * sizeof(CounterType),
201 "CounterEntry should be 8 bytes!"); 207 "CounterEntry should be 8 bytes!");
202 208
203 static constexpr size_t num_counter_entries = 209 static constexpr size_t num_counter_entries =
204 (1ULL << (device_virtual_bits - page_bits)) / subentries; 210 (1ULL << (device_virtual_bits - page_bits)) / subentries;
205 using CachedPages = std::array<CounterEntry, num_counter_entries>; 211 using CachedPages = std::array<CounterEntry, num_counter_entries>;
206 std::unique_ptr<CachedPages> cached_pages; 212 std::unique_ptr<CachedPages> cached_pages;
207 std::mutex counter_guard; 213 Common::RangeMutex counter_guard;
208 std::mutex mapping_guard; 214 std::mutex mapping_guard;
209}; 215};
210 216
diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc
index eab8a2731..b026f4220 100644
--- a/src/core/device_memory_manager.inc
+++ b/src/core/device_memory_manager.inc
@@ -213,8 +213,8 @@ void DeviceMemoryManager<Traits>::Free(DAddr start, size_t size) {
213} 213}
214 214
215template <typename Traits> 215template <typename Traits>
216void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size_t size, 216void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size_t size, Asid asid,
217 Asid asid, bool track) { 217 bool track) {
218 Core::Memory::Memory* process_memory = registered_processes[asid.id]; 218 Core::Memory::Memory* process_memory = registered_processes[asid.id];
219 size_t start_page_d = address >> Memory::YUZU_PAGEBITS; 219 size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
220 size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; 220 size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
@@ -508,12 +508,7 @@ void DeviceMemoryManager<Traits>::UnregisterProcess(Asid asid) {
508 508
509template <typename Traits> 509template <typename Traits>
510void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) { 510void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) {
511 std::unique_lock<std::mutex> lk(counter_guard, std::defer_lock); 511 Common::ScopedRangeLock lk(counter_guard, addr, size);
512 const auto Lock = [&] {
513 if (!lk) {
514 lk.lock();
515 }
516 };
517 u64 uncache_begin = 0; 512 u64 uncache_begin = 0;
518 u64 cache_begin = 0; 513 u64 cache_begin = 0;
519 u64 uncache_bytes = 0; 514 u64 uncache_bytes = 0;
@@ -524,22 +519,36 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
524 const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE); 519 const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
525 size_t page = addr >> Memory::YUZU_PAGEBITS; 520 size_t page = addr >> Memory::YUZU_PAGEBITS;
526 auto [asid, base_vaddress] = ExtractCPUBacking(page); 521 auto [asid, base_vaddress] = ExtractCPUBacking(page);
527 size_t vpage = base_vaddress >> Memory::YUZU_PAGEBITS;
528 auto* memory_device_inter = registered_processes[asid.id]; 522 auto* memory_device_inter = registered_processes[asid.id];
523 const auto release_pending = [&] {
524 if (uncache_bytes > 0) {
525 MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
526 uncache_bytes, false);
527 uncache_bytes = 0;
528 }
529 if (cache_bytes > 0) {
530 MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
531 cache_bytes, true);
532 cache_bytes = 0;
533 }
534 };
529 for (; page != page_end; ++page) { 535 for (; page != page_end; ++page) {
530 std::atomic_uint8_t& count = cached_pages->at(page >> 3).Count(page); 536 CounterAtomicType& count = cached_pages->at(page >> subentries_shift).Count(page);
537 auto [asid_2, vpage] = ExtractCPUBacking(page);
538 vpage >>= Memory::YUZU_PAGEBITS;
531 539
532 if (delta > 0) { 540 if (vpage == 0) [[unlikely]] {
533 ASSERT_MSG(count.load(std::memory_order::relaxed) < std::numeric_limits<u8>::max(), 541 release_pending();
534 "Count may overflow!"); 542 continue;
535 } else if (delta < 0) { 543 }
536 ASSERT_MSG(count.load(std::memory_order::relaxed) > 0, "Count may underflow!"); 544
537 } else { 545 if (asid.id != asid_2.id) [[unlikely]] {
538 ASSERT_MSG(false, "Delta must be non-zero!"); 546 release_pending();
547 memory_device_inter = registered_processes[asid_2.id];
539 } 548 }
540 549
541 // Adds or subtracts 1, as count is a unsigned 8-bit value 550 // Adds or subtracts 1, as count is a unsigned 8-bit value
542 count.fetch_add(static_cast<u8>(delta), std::memory_order_release); 551 count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release);
543 552
544 // Assume delta is either -1 or 1 553 // Assume delta is either -1 or 1
545 if (count.load(std::memory_order::relaxed) == 0) { 554 if (count.load(std::memory_order::relaxed) == 0) {
@@ -548,7 +557,6 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
548 } 557 }
549 uncache_bytes += Memory::YUZU_PAGESIZE; 558 uncache_bytes += Memory::YUZU_PAGESIZE;
550 } else if (uncache_bytes > 0) { 559 } else if (uncache_bytes > 0) {
551 Lock();
552 MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, 560 MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
553 uncache_bytes, false); 561 uncache_bytes, false);
554 uncache_bytes = 0; 562 uncache_bytes = 0;
@@ -559,23 +567,12 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
559 } 567 }
560 cache_bytes += Memory::YUZU_PAGESIZE; 568 cache_bytes += Memory::YUZU_PAGESIZE;
561 } else if (cache_bytes > 0) { 569 } else if (cache_bytes > 0) {
562 Lock(); 570 MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
563 MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes, 571 cache_bytes, true);
564 true);
565 cache_bytes = 0; 572 cache_bytes = 0;
566 } 573 }
567 vpage++;
568 }
569 if (uncache_bytes > 0) {
570 Lock();
571 MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes,
572 false);
573 }
574 if (cache_bytes > 0) {
575 Lock();
576 MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
577 true);
578 } 574 }
575 release_pending();
579} 576}
580 577
581} // namespace Core 578} // namespace Core
diff --git a/src/core/frontend/applets/general_frontend.cpp b/src/core/frontend/applets/general.cpp
index b4b213a31..4c299ee9c 100644
--- a/src/core/frontend/applets/general_frontend.cpp
+++ b/src/core/frontend/applets/general.cpp
@@ -2,7 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/logging/log.h" 4#include "common/logging/log.h"
5#include "core/frontend/applets/general_frontend.h" 5#include "core/frontend/applets/general.h"
6 6
7namespace Core::Frontend { 7namespace Core::Frontend {
8 8
diff --git a/src/core/frontend/applets/general_frontend.h b/src/core/frontend/applets/general.h
index 319838ac7..319838ac7 100644
--- a/src/core/frontend/applets/general_frontend.h
+++ b/src/core/frontend/applets/general.h
diff --git a/src/core/frontend/applets/profile_select.h b/src/core/frontend/applets/profile_select.h
index 92e2737ea..880b69ad6 100644
--- a/src/core/frontend/applets/profile_select.h
+++ b/src/core/frontend/applets/profile_select.h
@@ -8,15 +8,15 @@
8 8
9#include "common/uuid.h" 9#include "common/uuid.h"
10#include "core/frontend/applets/applet.h" 10#include "core/frontend/applets/applet.h"
11#include "core/hle/service/am/applets/applet_profile_select.h" 11#include "core/hle/service/am/frontend/applet_profile_select.h"
12 12
13namespace Core::Frontend { 13namespace Core::Frontend {
14 14
15struct ProfileSelectParameters { 15struct ProfileSelectParameters {
16 Service::AM::Applets::UiMode mode; 16 Service::AM::Frontend::UiMode mode;
17 std::array<Common::UUID, 8> invalid_uid_list; 17 std::array<Common::UUID, 8> invalid_uid_list;
18 Service::AM::Applets::UiSettingsDisplayOptions display_options; 18 Service::AM::Frontend::UiSettingsDisplayOptions display_options;
19 Service::AM::Applets::UserSelectionPurpose purpose; 19 Service::AM::Frontend::UserSelectionPurpose purpose;
20}; 20};
21 21
22class ProfileSelectApplet : public Applet { 22class ProfileSelectApplet : public Applet {
diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp
index 7655d215b..d00da8ac9 100644
--- a/src/core/frontend/applets/software_keyboard.cpp
+++ b/src/core/frontend/applets/software_keyboard.cpp
@@ -69,7 +69,7 @@ void DefaultSoftwareKeyboardApplet::ShowNormalKeyboard() const {
69} 69}
70 70
71void DefaultSoftwareKeyboardApplet::ShowTextCheckDialog( 71void DefaultSoftwareKeyboardApplet::ShowTextCheckDialog(
72 Service::AM::Applets::SwkbdTextCheckResult text_check_result, 72 Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
73 std::u16string text_check_message) const { 73 std::u16string text_check_message) const {
74 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to show the text check dialog."); 74 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to show the text check dialog.");
75} 75}
@@ -118,7 +118,7 @@ void DefaultSoftwareKeyboardApplet::InlineTextChanged(InlineTextParameters text_
118 "\ncursor_position={}", 118 "\ncursor_position={}",
119 Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position); 119 Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position);
120 120
121 submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, 121 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString,
122 text_parameters.input_text, text_parameters.cursor_position); 122 text_parameters.input_text, text_parameters.cursor_position);
123} 123}
124 124
@@ -127,22 +127,22 @@ void DefaultSoftwareKeyboardApplet::ExitKeyboard() const {
127} 127}
128 128
129void DefaultSoftwareKeyboardApplet::SubmitNormalText(std::u16string text) const { 129void DefaultSoftwareKeyboardApplet::SubmitNormalText(std::u16string text) const {
130 submit_normal_callback(Service::AM::Applets::SwkbdResult::Ok, text, true); 130 submit_normal_callback(Service::AM::Frontend::SwkbdResult::Ok, text, true);
131} 131}
132 132
133void DefaultSoftwareKeyboardApplet::SubmitInlineText(std::u16string_view text) const { 133void DefaultSoftwareKeyboardApplet::SubmitInlineText(std::u16string_view text) const {
134 std::this_thread::sleep_for(std::chrono::milliseconds(500)); 134 std::this_thread::sleep_for(std::chrono::milliseconds(500));
135 135
136 for (std::size_t index = 0; index < text.size(); ++index) { 136 for (std::size_t index = 0; index < text.size(); ++index) {
137 submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, 137 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString,
138 std::u16string(text.data(), text.data() + index + 1), 138 std::u16string(text.data(), text.data() + index + 1),
139 static_cast<s32>(index) + 1); 139 static_cast<s32>(index) + 1);
140 140
141 std::this_thread::sleep_for(std::chrono::milliseconds(250)); 141 std::this_thread::sleep_for(std::chrono::milliseconds(250));
142 } 142 }
143 143
144 submit_inline_callback(Service::AM::Applets::SwkbdReplyType::DecidedEnter, std::u16string(text), 144 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::DecidedEnter,
145 static_cast<s32>(text.size())); 145 std::u16string(text), static_cast<s32>(text.size()));
146} 146}
147 147
148} // namespace Core::Frontend 148} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h
index 8ed96da24..a32a98e4c 100644
--- a/src/core/frontend/applets/software_keyboard.h
+++ b/src/core/frontend/applets/software_keyboard.h
@@ -8,7 +8,7 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9 9
10#include "core/frontend/applets/applet.h" 10#include "core/frontend/applets/applet.h"
11#include "core/hle/service/am/applets/applet_software_keyboard_types.h" 11#include "core/hle/service/am/frontend/applet_software_keyboard_types.h"
12 12
13namespace Core::Frontend { 13namespace Core::Frontend {
14 14
@@ -23,10 +23,10 @@ struct KeyboardInitializeParameters {
23 u32 max_text_length; 23 u32 max_text_length;
24 u32 min_text_length; 24 u32 min_text_length;
25 s32 initial_cursor_position; 25 s32 initial_cursor_position;
26 Service::AM::Applets::SwkbdType type; 26 Service::AM::Frontend::SwkbdType type;
27 Service::AM::Applets::SwkbdPasswordMode password_mode; 27 Service::AM::Frontend::SwkbdPasswordMode password_mode;
28 Service::AM::Applets::SwkbdTextDrawType text_draw_type; 28 Service::AM::Frontend::SwkbdTextDrawType text_draw_type;
29 Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags; 29 Service::AM::Frontend::SwkbdKeyDisableFlags key_disable_flags;
30 bool use_blur_background; 30 bool use_blur_background;
31 bool enable_backspace_button; 31 bool enable_backspace_button;
32 bool enable_return_button; 32 bool enable_return_button;
@@ -40,8 +40,8 @@ struct InlineAppearParameters {
40 f32 key_top_scale_y; 40 f32 key_top_scale_y;
41 f32 key_top_translate_x; 41 f32 key_top_translate_x;
42 f32 key_top_translate_y; 42 f32 key_top_translate_y;
43 Service::AM::Applets::SwkbdType type; 43 Service::AM::Frontend::SwkbdType type;
44 Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags; 44 Service::AM::Frontend::SwkbdKeyDisableFlags key_disable_flags;
45 bool key_top_as_floating; 45 bool key_top_as_floating;
46 bool enable_backspace_button; 46 bool enable_backspace_button;
47 bool enable_return_button; 47 bool enable_return_button;
@@ -56,9 +56,9 @@ struct InlineTextParameters {
56class SoftwareKeyboardApplet : public Applet { 56class SoftwareKeyboardApplet : public Applet {
57public: 57public:
58 using SubmitInlineCallback = 58 using SubmitInlineCallback =
59 std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>; 59 std::function<void(Service::AM::Frontend::SwkbdReplyType, std::u16string, s32)>;
60 using SubmitNormalCallback = 60 using SubmitNormalCallback =
61 std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>; 61 std::function<void(Service::AM::Frontend::SwkbdResult, std::u16string, bool)>;
62 62
63 virtual ~SoftwareKeyboardApplet(); 63 virtual ~SoftwareKeyboardApplet();
64 64
@@ -69,7 +69,7 @@ public:
69 69
70 virtual void ShowNormalKeyboard() const = 0; 70 virtual void ShowNormalKeyboard() const = 0;
71 71
72 virtual void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, 72 virtual void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
73 std::u16string text_check_message) const = 0; 73 std::u16string text_check_message) const = 0;
74 74
75 virtual void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const = 0; 75 virtual void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const = 0;
@@ -93,7 +93,7 @@ public:
93 93
94 void ShowNormalKeyboard() const override; 94 void ShowNormalKeyboard() const override;
95 95
96 void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, 96 void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
97 std::u16string text_check_message) const override; 97 std::u16string text_check_message) const override;
98 98
99 void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const override; 99 void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const override;
diff --git a/src/core/frontend/applets/web_browser.cpp b/src/core/frontend/applets/web_browser.cpp
index 6e703ef06..eca8d6d98 100644
--- a/src/core/frontend/applets/web_browser.cpp
+++ b/src/core/frontend/applets/web_browser.cpp
@@ -18,7 +18,7 @@ void DefaultWebBrowserApplet::OpenLocalWebPage(const std::string& local_url,
18 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open local web page at {}", 18 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open local web page at {}",
19 local_url); 19 local_url);
20 20
21 callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/"); 21 callback(Service::AM::Frontend::WebExitReason::WindowClosed, "http://localhost/");
22} 22}
23 23
24void DefaultWebBrowserApplet::OpenExternalWebPage(const std::string& external_url, 24void DefaultWebBrowserApplet::OpenExternalWebPage(const std::string& external_url,
@@ -26,7 +26,7 @@ void DefaultWebBrowserApplet::OpenExternalWebPage(const std::string& external_ur
26 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open external web page at {}", 26 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open external web page at {}",
27 external_url); 27 external_url);
28 28
29 callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/"); 29 callback(Service::AM::Frontend::WebExitReason::WindowClosed, "http://localhost/");
30} 30}
31 31
32} // namespace Core::Frontend 32} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h
index 178bbdd3f..b70856a22 100644
--- a/src/core/frontend/applets/web_browser.h
+++ b/src/core/frontend/applets/web_browser.h
@@ -6,7 +6,7 @@
6#include <functional> 6#include <functional>
7 7
8#include "core/frontend/applets/applet.h" 8#include "core/frontend/applets/applet.h"
9#include "core/hle/service/am/applets/applet_web_browser_types.h" 9#include "core/hle/service/am/frontend/applet_web_browser_types.h"
10 10
11namespace Core::Frontend { 11namespace Core::Frontend {
12 12
@@ -14,7 +14,7 @@ class WebBrowserApplet : public Applet {
14public: 14public:
15 using ExtractROMFSCallback = std::function<void()>; 15 using ExtractROMFSCallback = std::function<void()>;
16 using OpenWebPageCallback = 16 using OpenWebPageCallback =
17 std::function<void(Service::AM::Applets::WebExitReason, std::string)>; 17 std::function<void(Service::AM::Frontend::WebExitReason, std::string)>;
18 18
19 virtual ~WebBrowserApplet(); 19 virtual ~WebBrowserApplet();
20 20
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index f3683cdcc..34b25be66 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -97,8 +97,14 @@ struct KernelCore::Impl {
97 RegisterHostThread(nullptr); 97 RegisterHostThread(nullptr);
98 } 98 }
99 99
100 void TerminateApplicationProcess() { 100 void TerminateAllProcesses() {
101 application_process.load()->Terminate(); 101 std::scoped_lock lk{process_list_lock};
102 for (auto& process : process_list) {
103 process->Terminate();
104 process->Close();
105 process = nullptr;
106 }
107 process_list.clear();
102 } 108 }
103 109
104 void Shutdown() { 110 void Shutdown() {
@@ -107,18 +113,9 @@ struct KernelCore::Impl {
107 113
108 CloseServices(); 114 CloseServices();
109 115
110 auto* old_process = application_process.exchange(nullptr); 116 if (application_process) {
111 if (old_process) { 117 application_process->Close();
112 old_process->Close(); 118 application_process = nullptr;
113 }
114
115 {
116 std::scoped_lock lk{process_list_lock};
117 for (auto* const process : process_list) {
118 process->Terminate();
119 process->Close();
120 }
121 process_list.clear();
122 } 119 }
123 120
124 next_object_id = 0; 121 next_object_id = 0;
@@ -354,6 +351,7 @@ struct KernelCore::Impl {
354 351
355 void MakeApplicationProcess(KProcess* process) { 352 void MakeApplicationProcess(KProcess* process) {
356 application_process = process; 353 application_process = process;
354 application_process->Open();
357 } 355 }
358 356
359 static inline thread_local u8 host_thread_id = UINT8_MAX; 357 static inline thread_local u8 host_thread_id = UINT8_MAX;
@@ -779,7 +777,7 @@ struct KernelCore::Impl {
779 // Lists all processes that exist in the current session. 777 // Lists all processes that exist in the current session.
780 std::mutex process_list_lock; 778 std::mutex process_list_lock;
781 std::vector<KProcess*> process_list; 779 std::vector<KProcess*> process_list;
782 std::atomic<KProcess*> application_process{}; 780 KProcess* application_process{};
783 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; 781 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
784 std::unique_ptr<Kernel::KHardwareTimer> hardware_timer; 782 std::unique_ptr<Kernel::KHardwareTimer> hardware_timer;
785 783
@@ -1243,7 +1241,7 @@ void KernelCore::SuspendApplication(bool suspended) {
1243} 1241}
1244 1242
1245void KernelCore::ShutdownCores() { 1243void KernelCore::ShutdownCores() {
1246 impl->TerminateApplicationProcess(); 1244 impl->TerminateAllProcesses();
1247 1245
1248 KScopedSchedulerLock lk{*this}; 1246 KScopedSchedulerLock lk{*this};
1249 1247
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 38f67adcd..8f90eba34 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1,2704 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm>
5#include <array>
6#include <cinttypes>
7#include <cstring>
8#include "common/settings.h"
9#include "common/settings_enums.h"
10#include "core/core.h"
11#include "core/core_timing.h"
12#include "core/file_sys/control_metadata.h"
13#include "core/file_sys/patch_manager.h"
14#include "core/file_sys/registered_cache.h"
15#include "core/file_sys/savedata_factory.h"
16#include "core/hle/kernel/k_event.h"
17#include "core/hle/kernel/k_transfer_memory.h"
18#include "core/hle/result.h"
19#include "core/hle/service/acc/profile_manager.h"
20#include "core/hle/service/am/am.h" 4#include "core/hle/service/am/am.h"
21#include "core/hle/service/am/applet_ae.h" 5#include "core/hle/service/am/applet_ae.h"
22#include "core/hle/service/am/applet_oe.h" 6#include "core/hle/service/am/applet_oe.h"
23#include "core/hle/service/am/applets/applet_cabinet.h"
24#include "core/hle/service/am/applets/applet_controller.h"
25#include "core/hle/service/am/applets/applet_mii_edit_types.h"
26#include "core/hle/service/am/applets/applet_profile_select.h"
27#include "core/hle/service/am/applets/applet_software_keyboard_types.h"
28#include "core/hle/service/am/applets/applet_web_browser.h"
29#include "core/hle/service/am/applets/applets.h"
30#include "core/hle/service/am/idle.h" 7#include "core/hle/service/am/idle.h"
31#include "core/hle/service/am/omm.h" 8#include "core/hle/service/am/omm.h"
32#include "core/hle/service/am/spsm.h" 9#include "core/hle/service/am/spsm.h"
33#include "core/hle/service/apm/apm_controller.h"
34#include "core/hle/service/apm/apm_interface.h"
35#include "core/hle/service/bcat/backend/backend.h"
36#include "core/hle/service/caps/caps_su.h"
37#include "core/hle/service/caps/caps_types.h"
38#include "core/hle/service/filesystem/filesystem.h"
39#include "core/hle/service/filesystem/save_data_controller.h"
40#include "core/hle/service/ipc_helpers.h"
41#include "core/hle/service/ns/ns.h"
42#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
43#include "core/hle/service/nvnflinger/nvnflinger.h"
44#include "core/hle/service/pm/pm.h"
45#include "core/hle/service/server_manager.h" 10#include "core/hle/service/server_manager.h"
46#include "core/hle/service/sm/sm.h"
47#include "core/hle/service/vi/vi.h"
48#include "core/hle/service/vi/vi_results.h"
49#include "core/memory.h"
50#include "hid_core/hid_types.h"
51#include "hid_core/resources/npad/npad.h"
52 11
53namespace Service::AM { 12namespace Service::AM {
54 13
55constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
56constexpr Result ResultNoMessages{ErrorModule::AM, 3};
57constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
58
59enum class LaunchParameterKind : u32 {
60 UserChannel = 1,
61 AccountPreselectedUser = 2,
62};
63
64constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
65
66struct LaunchParameterAccountPreselectedUser {
67 u32_le magic;
68 u32_le is_account_selected;
69 Common::UUID current_user;
70 INSERT_PADDING_BYTES(0x70);
71};
72static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88);
73
74IWindowController::IWindowController(Core::System& system_)
75 : ServiceFramework{system_, "IWindowController"} {
76 // clang-format off
77 static const FunctionInfo functions[] = {
78 {0, nullptr, "CreateWindow"},
79 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
80 {2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"},
81 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
82 {11, nullptr, "ReleaseForegroundRights"},
83 {12, nullptr, "RejectToChangeIntoBackground"},
84 {20, nullptr, "SetAppletWindowVisibility"},
85 {21, nullptr, "SetAppletGpuTimeSlice"},
86 };
87 // clang-format on
88
89 RegisterHandlers(functions);
90}
91
92IWindowController::~IWindowController() = default;
93
94void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) {
95 const u64 process_id = system.ApplicationProcess()->GetProcessId();
96
97 LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id);
98
99 IPC::ResponseBuilder rb{ctx, 4};
100 rb.Push(ResultSuccess);
101 rb.Push<u64>(process_id);
102}
103
104void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) {
105 const u64 process_id = 0;
106
107 LOG_WARNING(Service_AM, "(STUBBED) called");
108
109 IPC::ResponseBuilder rb{ctx, 4};
110 rb.Push(ResultSuccess);
111 rb.Push<u64>(process_id);
112}
113
114void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) {
115 LOG_WARNING(Service_AM, "(STUBBED) called");
116 IPC::ResponseBuilder rb{ctx, 2};
117 rb.Push(ResultSuccess);
118}
119
120IAudioController::IAudioController(Core::System& system_)
121 : ServiceFramework{system_, "IAudioController"} {
122 // clang-format off
123 static const FunctionInfo functions[] = {
124 {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
125 {1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"},
126 {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"},
127 {3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"},
128 {4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"},
129 };
130 // clang-format on
131
132 RegisterHandlers(functions);
133}
134
135IAudioController::~IAudioController() = default;
136
137void IAudioController::SetExpectedMasterVolume(HLERequestContext& ctx) {
138 IPC::RequestParser rp{ctx};
139 const float main_applet_volume_tmp = rp.Pop<float>();
140 const float library_applet_volume_tmp = rp.Pop<float>();
141
142 LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}",
143 main_applet_volume_tmp, library_applet_volume_tmp);
144
145 // Ensure the volume values remain within the 0-100% range
146 main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
147 library_applet_volume =
148 std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
149
150 IPC::ResponseBuilder rb{ctx, 2};
151 rb.Push(ResultSuccess);
152}
153
154void IAudioController::GetMainAppletExpectedMasterVolume(HLERequestContext& ctx) {
155 LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume);
156 IPC::ResponseBuilder rb{ctx, 3};
157 rb.Push(ResultSuccess);
158 rb.Push(main_applet_volume);
159}
160
161void IAudioController::GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx) {
162 LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume);
163 IPC::ResponseBuilder rb{ctx, 3};
164 rb.Push(ResultSuccess);
165 rb.Push(library_applet_volume);
166}
167
168void IAudioController::ChangeMainAppletMasterVolume(HLERequestContext& ctx) {
169 struct Parameters {
170 float volume;
171 s64 fade_time_ns;
172 };
173 static_assert(sizeof(Parameters) == 16);
174
175 IPC::RequestParser rp{ctx};
176 const auto parameters = rp.PopRaw<Parameters>();
177
178 LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume,
179 parameters.fade_time_ns);
180
181 main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume);
182 fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns};
183
184 IPC::ResponseBuilder rb{ctx, 2};
185 rb.Push(ResultSuccess);
186}
187
188void IAudioController::SetTransparentAudioRate(HLERequestContext& ctx) {
189 IPC::RequestParser rp{ctx};
190 const float transparent_volume_rate_tmp = rp.Pop<float>();
191
192 LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp);
193
194 // Clamp volume range to 0-100%.
195 transparent_volume_rate =
196 std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume);
197
198 IPC::ResponseBuilder rb{ctx, 2};
199 rb.Push(ResultSuccess);
200}
201
202IDisplayController::IDisplayController(Core::System& system_)
203 : ServiceFramework{system_, "IDisplayController"} {
204 // clang-format off
205 static const FunctionInfo functions[] = {
206 {0, nullptr, "GetLastForegroundCaptureImage"},
207 {1, nullptr, "UpdateLastForegroundCaptureImage"},
208 {2, nullptr, "GetLastApplicationCaptureImage"},
209 {3, nullptr, "GetCallerAppletCaptureImage"},
210 {4, nullptr, "UpdateCallerAppletCaptureImage"},
211 {5, nullptr, "GetLastForegroundCaptureImageEx"},
212 {6, nullptr, "GetLastApplicationCaptureImageEx"},
213 {7, &IDisplayController::GetCallerAppletCaptureImageEx, "GetCallerAppletCaptureImageEx"},
214 {8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"},
215 {9, nullptr, "CopyBetweenCaptureBuffers"},
216 {10, nullptr, "AcquireLastApplicationCaptureBuffer"},
217 {11, nullptr, "ReleaseLastApplicationCaptureBuffer"},
218 {12, nullptr, "AcquireLastForegroundCaptureBuffer"},
219 {13, nullptr, "ReleaseLastForegroundCaptureBuffer"},
220 {14, nullptr, "AcquireCallerAppletCaptureBuffer"},
221 {15, nullptr, "ReleaseCallerAppletCaptureBuffer"},
222 {16, nullptr, "AcquireLastApplicationCaptureBufferEx"},
223 {17, nullptr, "AcquireLastForegroundCaptureBufferEx"},
224 {18, nullptr, "AcquireCallerAppletCaptureBufferEx"},
225 {20, nullptr, "ClearCaptureBuffer"},
226 {21, nullptr, "ClearAppletTransitionBuffer"},
227 {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"},
228 {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"},
229 {24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"},
230 {25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"},
231 {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"},
232 {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"},
233 {28, nullptr, "TakeScreenShotOfOwnLayerEx"},
234 };
235 // clang-format on
236
237 RegisterHandlers(functions);
238}
239
240IDisplayController::~IDisplayController() = default;
241
242void IDisplayController::GetCallerAppletCaptureImageEx(HLERequestContext& ctx) {
243 LOG_WARNING(Service_AM, "(STUBBED) called");
244
245 IPC::ResponseBuilder rb{ctx, 4};
246 rb.Push(ResultSuccess);
247 rb.Push(1u);
248 rb.Push(0);
249}
250
251void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
252 LOG_WARNING(Service_AM, "(STUBBED) called");
253
254 IPC::ResponseBuilder rb{ctx, 2};
255 rb.Push(ResultSuccess);
256}
257
258void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
259 LOG_WARNING(Service_AM, "(STUBBED) called");
260
261 IPC::ResponseBuilder rb{ctx, 4};
262 rb.Push(ResultSuccess);
263 rb.Push(1U);
264 rb.Push(0);
265}
266
267void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
268 LOG_WARNING(Service_AM, "(STUBBED) called");
269
270 IPC::ResponseBuilder rb{ctx, 2};
271 rb.Push(ResultSuccess);
272}
273
274void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
275 LOG_WARNING(Service_AM, "(STUBBED) called");
276
277 IPC::ResponseBuilder rb{ctx, 4};
278 rb.Push(ResultSuccess);
279 rb.Push(1U);
280 rb.Push(0);
281}
282
283void IDisplayController::ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
284 LOG_WARNING(Service_AM, "(STUBBED) called");
285
286 IPC::ResponseBuilder rb{ctx, 2};
287 rb.Push(ResultSuccess);
288}
289
290IDebugFunctions::IDebugFunctions(Core::System& system_)
291 : ServiceFramework{system_, "IDebugFunctions"} {
292 // clang-format off
293 static const FunctionInfo functions[] = {
294 {0, nullptr, "NotifyMessageToHomeMenuForDebug"},
295 {1, nullptr, "OpenMainApplication"},
296 {10, nullptr, "PerformSystemButtonPressing"},
297 {20, nullptr, "InvalidateTransitionLayer"},
298 {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
299 {31, nullptr, "RequestLaunchApplicationByApplicationLaunchInfoForDebug"},
300 {40, nullptr, "GetAppletResourceUsageInfo"},
301 {50, nullptr, "AddSystemProgramIdAndAppletIdForDebug"},
302 {51, nullptr, "AddOperationConfirmedLibraryAppletIdForDebug"},
303 {100, nullptr, "SetCpuBoostModeForApplet"},
304 {101, nullptr, "CancelCpuBoostModeForApplet"},
305 {110, nullptr, "PushToAppletBoundChannelForDebug"},
306 {111, nullptr, "TryPopFromAppletBoundChannelForDebug"},
307 {120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"},
308 {121, nullptr, "AlarmSettingNotificationDisableAppEventReserve"},
309 {122, nullptr, "AlarmSettingNotificationPushAppEventNotify"},
310 {130, nullptr, "FriendInvitationSetApplicationParameter"},
311 {131, nullptr, "FriendInvitationClearApplicationParameter"},
312 {132, nullptr, "FriendInvitationPushApplicationParameter"},
313 {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"},
314 {200, nullptr, "CreateFloatingLibraryAppletAccepterForDebug"},
315 {300, nullptr, "TerminateAllRunningApplicationsForDebug"},
316 {900, nullptr, "GetGrcProcessLaunchedSystemEvent"},
317 };
318 // clang-format on
319
320 RegisterHandlers(functions);
321}
322
323IDebugFunctions::~IDebugFunctions() = default;
324
325ISelfController::ISelfController(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_)
326 : ServiceFramework{system_, "ISelfController"}, nvnflinger{nvnflinger_},
327 service_context{system, "ISelfController"} {
328 // clang-format off
329 static const FunctionInfo functions[] = {
330 {0, &ISelfController::Exit, "Exit"},
331 {1, &ISelfController::LockExit, "LockExit"},
332 {2, &ISelfController::UnlockExit, "UnlockExit"},
333 {3, &ISelfController::EnterFatalSection, "EnterFatalSection"},
334 {4, &ISelfController::LeaveFatalSection, "LeaveFatalSection"},
335 {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
336 {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
337 {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
338 {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"},
339 {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
340 {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
341 {15, nullptr, "SetScreenShotAppletIdentityInfo"},
342 {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"},
343 {17, nullptr, "SetControllerFirmwareUpdateSection"},
344 {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
345 {19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"},
346 {20, nullptr, "SetDesirableKeyboardLayout"},
347 {21, nullptr, "GetScreenShotProgramId"},
348 {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
349 {41, &ISelfController::IsSystemBufferSharingEnabled, "IsSystemBufferSharingEnabled"},
350 {42, &ISelfController::GetSystemSharedLayerHandle, "GetSystemSharedLayerHandle"},
351 {43, &ISelfController::GetSystemSharedBufferHandle, "GetSystemSharedBufferHandle"},
352 {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
353 {45, nullptr, "SetManagedDisplayLayerSeparationMode"},
354 {46, nullptr, "SetRecordingLayerCompositionEnabled"},
355 {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
356 {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
357 {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
358 {61, nullptr, "SetMediaPlaybackState"},
359 {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
360 {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
361 {64, nullptr, "SetInputDetectionSourceSet"},
362 {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"},
363 {66, nullptr, "GetCurrentIlluminance"},
364 {67, nullptr, "IsIlluminanceAvailable"},
365 {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"},
366 {69, &ISelfController::IsAutoSleepDisabled, "IsAutoSleepDisabled"},
367 {70, nullptr, "ReportMultimediaError"},
368 {71, nullptr, "GetCurrentIlluminanceEx"},
369 {72, nullptr, "SetInputDetectionPolicy"},
370 {80, nullptr, "SetWirelessPriorityMode"},
371 {90, &ISelfController::GetAccumulatedSuspendedTickValue, "GetAccumulatedSuspendedTickValue"},
372 {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
373 {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
374 {110, nullptr, "SetApplicationAlbumUserData"},
375 {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"},
376 {130, &ISelfController::SetRecordVolumeMuted, "SetRecordVolumeMuted"},
377 {1000, nullptr, "GetDebugStorageChannel"},
378 };
379 // clang-format on
380
381 RegisterHandlers(functions);
382
383 launchable_event = service_context.CreateEvent("ISelfController:LaunchableEvent");
384
385 // This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is
386 // called. Yuzu can just create it unconditionally, since it doesn't need to support multiple
387 // ISelfControllers. The event is signaled on creation, and on transition from suspended -> not
388 // suspended if the event has previously been created by a call to
389 // GetAccumulatedSuspendedTickChangedEvent.
390
391 accumulated_suspended_tick_changed_event =
392 service_context.CreateEvent("ISelfController:AccumulatedSuspendedTickChangedEvent");
393 accumulated_suspended_tick_changed_event->Signal();
394}
395
396ISelfController::~ISelfController() {
397 service_context.CloseEvent(launchable_event);
398 service_context.CloseEvent(accumulated_suspended_tick_changed_event);
399}
400
401void ISelfController::Exit(HLERequestContext& ctx) {
402 LOG_DEBUG(Service_AM, "called");
403
404 IPC::ResponseBuilder rb{ctx, 2};
405 rb.Push(ResultSuccess);
406
407 system.Exit();
408}
409
410void ISelfController::LockExit(HLERequestContext& ctx) {
411 LOG_DEBUG(Service_AM, "called");
412
413 system.SetExitLocked(true);
414
415 IPC::ResponseBuilder rb{ctx, 2};
416 rb.Push(ResultSuccess);
417}
418
419void ISelfController::UnlockExit(HLERequestContext& ctx) {
420 LOG_DEBUG(Service_AM, "called");
421
422 system.SetExitLocked(false);
423
424 IPC::ResponseBuilder rb{ctx, 2};
425 rb.Push(ResultSuccess);
426
427 if (system.GetExitRequested()) {
428 system.Exit();
429 }
430}
431
432void ISelfController::EnterFatalSection(HLERequestContext& ctx) {
433 ++num_fatal_sections_entered;
434 LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", num_fatal_sections_entered);
435
436 IPC::ResponseBuilder rb{ctx, 2};
437 rb.Push(ResultSuccess);
438}
439
440void ISelfController::LeaveFatalSection(HLERequestContext& ctx) {
441 LOG_DEBUG(Service_AM, "called.");
442
443 // Entry and exit of fatal sections must be balanced.
444 if (num_fatal_sections_entered == 0) {
445 IPC::ResponseBuilder rb{ctx, 2};
446 rb.Push(Result{ErrorModule::AM, 512});
447 return;
448 }
449
450 --num_fatal_sections_entered;
451
452 IPC::ResponseBuilder rb{ctx, 2};
453 rb.Push(ResultSuccess);
454}
455
456void ISelfController::GetLibraryAppletLaunchableEvent(HLERequestContext& ctx) {
457 LOG_WARNING(Service_AM, "(STUBBED) called");
458
459 launchable_event->Signal();
460
461 IPC::ResponseBuilder rb{ctx, 2, 1};
462 rb.Push(ResultSuccess);
463 rb.PushCopyObjects(launchable_event->GetReadableEvent());
464}
465
466void ISelfController::SetScreenShotPermission(HLERequestContext& ctx) {
467 IPC::RequestParser rp{ctx};
468 const auto permission = rp.PopEnum<ScreenshotPermission>();
469 LOG_DEBUG(Service_AM, "called, permission={}", permission);
470
471 screenshot_permission = permission;
472
473 IPC::ResponseBuilder rb{ctx, 2};
474 rb.Push(ResultSuccess);
475}
476
477void ISelfController::SetOperationModeChangedNotification(HLERequestContext& ctx) {
478 IPC::RequestParser rp{ctx};
479
480 bool flag = rp.Pop<bool>();
481 LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
482
483 IPC::ResponseBuilder rb{ctx, 2};
484 rb.Push(ResultSuccess);
485}
486
487void ISelfController::SetPerformanceModeChangedNotification(HLERequestContext& ctx) {
488 IPC::RequestParser rp{ctx};
489
490 bool flag = rp.Pop<bool>();
491 LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
492
493 IPC::ResponseBuilder rb{ctx, 2};
494 rb.Push(ResultSuccess);
495}
496
497void ISelfController::SetFocusHandlingMode(HLERequestContext& ctx) {
498 // Takes 3 input u8s with each field located immediately after the previous
499 // u8, these are bool flags. No output.
500 IPC::RequestParser rp{ctx};
501
502 struct FocusHandlingModeParams {
503 u8 unknown0;
504 u8 unknown1;
505 u8 unknown2;
506 };
507 const auto flags = rp.PopRaw<FocusHandlingModeParams>();
508
509 LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}",
510 flags.unknown0, flags.unknown1, flags.unknown2);
511
512 IPC::ResponseBuilder rb{ctx, 2};
513 rb.Push(ResultSuccess);
514}
515
516void ISelfController::SetRestartMessageEnabled(HLERequestContext& ctx) {
517 LOG_WARNING(Service_AM, "(STUBBED) called");
518
519 IPC::ResponseBuilder rb{ctx, 2};
520 rb.Push(ResultSuccess);
521}
522
523void ISelfController::SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx) {
524 // Takes 3 input u8s with each field located immediately after the previous
525 // u8, these are bool flags. No output.
526 IPC::RequestParser rp{ctx};
527
528 bool enabled = rp.Pop<bool>();
529 LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled);
530
531 IPC::ResponseBuilder rb{ctx, 2};
532 rb.Push(ResultSuccess);
533}
534
535void ISelfController::SetAlbumImageOrientation(HLERequestContext& ctx) {
536 LOG_WARNING(Service_AM, "(STUBBED) called");
537
538 IPC::ResponseBuilder rb{ctx, 2};
539 rb.Push(ResultSuccess);
540}
541
542void ISelfController::CreateManagedDisplayLayer(HLERequestContext& ctx) {
543 LOG_WARNING(Service_AM, "(STUBBED) called");
544
545 // TODO(Subv): Find out how AM determines the display to use, for now just
546 // create the layer in the Default display.
547 const auto display_id = nvnflinger.OpenDisplay("Default");
548 const auto layer_id = nvnflinger.CreateLayer(*display_id);
549
550 IPC::ResponseBuilder rb{ctx, 4};
551 rb.Push(ResultSuccess);
552 rb.Push(*layer_id);
553}
554
555void ISelfController::IsSystemBufferSharingEnabled(HLERequestContext& ctx) {
556 LOG_WARNING(Service_AM, "(STUBBED) called");
557
558 IPC::ResponseBuilder rb{ctx, 2};
559 rb.Push(this->EnsureBufferSharingEnabled());
560}
561
562void ISelfController::GetSystemSharedLayerHandle(HLERequestContext& ctx) {
563 LOG_WARNING(Service_AM, "(STUBBED) called");
564
565 IPC::ResponseBuilder rb{ctx, 6};
566 rb.Push(this->EnsureBufferSharingEnabled());
567 rb.Push<s64>(system_shared_buffer_id);
568 rb.Push<s64>(system_shared_layer_id);
569}
570
571void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) {
572 LOG_WARNING(Service_AM, "(STUBBED) called");
573
574 IPC::ResponseBuilder rb{ctx, 4};
575 rb.Push(this->EnsureBufferSharingEnabled());
576 rb.Push<s64>(system_shared_buffer_id);
577}
578
579Result ISelfController::EnsureBufferSharingEnabled() {
580 if (buffer_sharing_enabled) {
581 return ResultSuccess;
582 }
583
584 if (system.GetAppletManager().GetCurrentAppletId() <= Applets::AppletId::Application) {
585 return VI::ResultOperationFailed;
586 }
587
588 const auto display_id = nvnflinger.OpenDisplay("Default");
589 const auto result = nvnflinger.GetSystemBufferManager().Initialize(
590 &system_shared_buffer_id, &system_shared_layer_id, *display_id);
591
592 if (result.IsSuccess()) {
593 buffer_sharing_enabled = true;
594 }
595
596 return result;
597}
598
599void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) {
600 LOG_WARNING(Service_AM, "(STUBBED) called");
601
602 // TODO(Subv): Find out how AM determines the display to use, for now just
603 // create the layer in the Default display.
604 // This calls nn::vi::CreateRecordingLayer() which creates another layer.
605 // Currently we do not support more than 1 layer per display, output 1 layer id for now.
606 // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
607 // side effects.
608 // TODO: Support multiple layers
609 const auto display_id = nvnflinger.OpenDisplay("Default");
610 const auto layer_id = nvnflinger.CreateLayer(*display_id);
611
612 IPC::ResponseBuilder rb{ctx, 4};
613 rb.Push(ResultSuccess);
614 rb.Push(*layer_id);
615}
616
617void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) {
618 LOG_WARNING(Service_AM, "(STUBBED) called");
619
620 IPC::ResponseBuilder rb{ctx, 2};
621 rb.Push(ResultSuccess);
622}
623
624void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
625 LOG_WARNING(Service_AM, "(STUBBED) called");
626
627 IPC::ResponseBuilder rb{ctx, 2};
628 rb.Push(ResultSuccess);
629}
630
631void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
632 IPC::RequestParser rp{ctx};
633 idle_time_detection_extension = rp.Pop<u32>();
634 LOG_DEBUG(Service_AM, "(STUBBED) called idle_time_detection_extension={}",
635 idle_time_detection_extension);
636
637 IPC::ResponseBuilder rb{ctx, 2};
638 rb.Push(ResultSuccess);
639}
640
641void ISelfController::GetIdleTimeDetectionExtension(HLERequestContext& ctx) {
642 LOG_WARNING(Service_AM, "(STUBBED) called");
643
644 IPC::ResponseBuilder rb{ctx, 3};
645 rb.Push(ResultSuccess);
646 rb.Push<u32>(idle_time_detection_extension);
647}
648
649void ISelfController::ReportUserIsActive(HLERequestContext& ctx) {
650 LOG_WARNING(Service_AM, "(STUBBED) called");
651
652 IPC::ResponseBuilder rb{ctx, 2};
653 rb.Push(ResultSuccess);
654}
655
656void ISelfController::SetAutoSleepDisabled(HLERequestContext& ctx) {
657 IPC::RequestParser rp{ctx};
658 is_auto_sleep_disabled = rp.Pop<bool>();
659
660 // On the system itself, if the previous state of is_auto_sleep_disabled
661 // differed from the current value passed in, it'd signify the internal
662 // window manager to update (and also increment some statistics like update counts)
663 //
664 // It'd also indicate this change to an idle handling context.
665 //
666 // However, given we're emulating this behavior, most of this can be ignored
667 // and it's sufficient to simply set the member variable for querying via
668 // IsAutoSleepDisabled().
669
670 LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", is_auto_sleep_disabled);
671
672 IPC::ResponseBuilder rb{ctx, 2};
673 rb.Push(ResultSuccess);
674}
675
676void ISelfController::IsAutoSleepDisabled(HLERequestContext& ctx) {
677 LOG_DEBUG(Service_AM, "called.");
678
679 IPC::ResponseBuilder rb{ctx, 3};
680 rb.Push(ResultSuccess);
681 rb.Push(is_auto_sleep_disabled);
682}
683
684void ISelfController::GetAccumulatedSuspendedTickValue(HLERequestContext& ctx) {
685 LOG_DEBUG(Service_AM, "called.");
686
687 // This command returns the total number of system ticks since ISelfController creation
688 // where the game was suspended. Since Yuzu doesn't implement game suspension, this command
689 // can just always return 0 ticks.
690 IPC::ResponseBuilder rb{ctx, 4};
691 rb.Push(ResultSuccess);
692 rb.Push<u64>(0);
693}
694
695void ISelfController::GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx) {
696 LOG_DEBUG(Service_AM, "called.");
697
698 IPC::ResponseBuilder rb{ctx, 2, 1};
699 rb.Push(ResultSuccess);
700 rb.PushCopyObjects(accumulated_suspended_tick_changed_event->GetReadableEvent());
701}
702
703void ISelfController::SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx) {
704 IPC::RequestParser rp{ctx};
705
706 // This service call sets an internal flag whether a notification is shown when an image is
707 // captured. Currently we do not support capturing images via the capture button, so this can be
708 // stubbed for now.
709 const bool album_image_taken_notification_enabled = rp.Pop<bool>();
710
711 LOG_WARNING(Service_AM, "(STUBBED) called. album_image_taken_notification_enabled={}",
712 album_image_taken_notification_enabled);
713
714 IPC::ResponseBuilder rb{ctx, 2};
715 rb.Push(ResultSuccess);
716}
717
718void ISelfController::SaveCurrentScreenshot(HLERequestContext& ctx) {
719 IPC::RequestParser rp{ctx};
720
721 const auto report_option = rp.PopEnum<Capture::AlbumReportOption>();
722
723 LOG_INFO(Service_AM, "called, report_option={}", report_option);
724
725 const auto screenshot_service =
726 system.ServiceManager().GetService<Service::Capture::IScreenShotApplicationService>(
727 "caps:su");
728
729 if (screenshot_service) {
730 screenshot_service->CaptureAndSaveScreenshot(report_option);
731 }
732
733 IPC::ResponseBuilder rb{ctx, 2};
734 rb.Push(ResultSuccess);
735}
736
737void ISelfController::SetRecordVolumeMuted(HLERequestContext& ctx) {
738 IPC::RequestParser rp{ctx};
739
740 const auto is_record_volume_muted = rp.Pop<bool>();
741
742 LOG_WARNING(Service_AM, "(STUBBED) called. is_record_volume_muted={}", is_record_volume_muted);
743
744 IPC::ResponseBuilder rb{ctx, 2};
745 rb.Push(ResultSuccess);
746}
747
748AppletMessageQueue::AppletMessageQueue(Core::System& system)
749 : service_context{system, "AppletMessageQueue"} {
750 on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived");
751 on_operation_mode_changed = service_context.CreateEvent("AMMessageQueue:OperationModeChanged");
752}
753
754AppletMessageQueue::~AppletMessageQueue() {
755 service_context.CloseEvent(on_new_message);
756 service_context.CloseEvent(on_operation_mode_changed);
757}
758
759Kernel::KReadableEvent& AppletMessageQueue::GetMessageReceiveEvent() {
760 return on_new_message->GetReadableEvent();
761}
762
763Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() {
764 return on_operation_mode_changed->GetReadableEvent();
765}
766
767void AppletMessageQueue::PushMessage(AppletMessage msg) {
768 messages.push(msg);
769 on_new_message->Signal();
770}
771
772AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
773 if (messages.empty()) {
774 on_new_message->Clear();
775 return AppletMessage::None;
776 }
777 auto msg = messages.front();
778 messages.pop();
779 if (messages.empty()) {
780 on_new_message->Clear();
781 }
782 return msg;
783}
784
785std::size_t AppletMessageQueue::GetMessageCount() const {
786 return messages.size();
787}
788
789void AppletMessageQueue::RequestExit() {
790 PushMessage(AppletMessage::Exit);
791}
792
793void AppletMessageQueue::RequestResume() {
794 PushMessage(AppletMessage::Resume);
795}
796
797void AppletMessageQueue::FocusStateChanged() {
798 PushMessage(AppletMessage::FocusStateChanged);
799}
800
801void AppletMessageQueue::OperationModeChanged() {
802 PushMessage(AppletMessage::OperationModeChanged);
803 PushMessage(AppletMessage::PerformanceModeChanged);
804 on_operation_mode_changed->Signal();
805}
806
807ILockAccessor::ILockAccessor(Core::System& system_)
808 : ServiceFramework{system_, "ILockAccessor"}, service_context{system_, "ILockAccessor"} {
809 // clang-format off
810 static const FunctionInfo functions[] = {
811 {1, &ILockAccessor::TryLock, "TryLock"},
812 {2, &ILockAccessor::Unlock, "Unlock"},
813 {3, &ILockAccessor::GetEvent, "GetEvent"},
814 {4,&ILockAccessor::IsLocked, "IsLocked"},
815 };
816 // clang-format on
817
818 RegisterHandlers(functions);
819
820 lock_event = service_context.CreateEvent("ILockAccessor::LockEvent");
821}
822
823ILockAccessor::~ILockAccessor() {
824 service_context.CloseEvent(lock_event);
825};
826
827void ILockAccessor::TryLock(HLERequestContext& ctx) {
828 IPC::RequestParser rp{ctx};
829 const auto return_handle = rp.Pop<bool>();
830
831 LOG_WARNING(Service_AM, "(STUBBED) called, return_handle={}", return_handle);
832
833 // TODO: When return_handle is true this function should return the lock handle
834
835 is_locked = true;
836
837 IPC::ResponseBuilder rb{ctx, 3};
838 rb.Push(ResultSuccess);
839 rb.Push<u8>(is_locked);
840}
841
842void ILockAccessor::Unlock(HLERequestContext& ctx) {
843 LOG_INFO(Service_AM, "called");
844
845 is_locked = false;
846
847 IPC::ResponseBuilder rb{ctx, 2};
848 rb.Push(ResultSuccess);
849}
850
851void ILockAccessor::GetEvent(HLERequestContext& ctx) {
852 LOG_INFO(Service_AM, "called");
853
854 lock_event->Signal();
855
856 IPC::ResponseBuilder rb{ctx, 2, 1};
857 rb.Push(ResultSuccess);
858 rb.PushCopyObjects(lock_event->GetReadableEvent());
859}
860
861void ILockAccessor::IsLocked(HLERequestContext& ctx) {
862 LOG_INFO(Service_AM, "called");
863
864 IPC::ResponseBuilder rb{ctx, 2};
865 rb.Push(ResultSuccess);
866 rb.Push<u8>(is_locked);
867}
868
869ICommonStateGetter::ICommonStateGetter(Core::System& system_,
870 std::shared_ptr<AppletMessageQueue> msg_queue_)
871 : ServiceFramework{system_, "ICommonStateGetter"}, msg_queue{std::move(msg_queue_)},
872 service_context{system_, "ICommonStateGetter"} {
873 // clang-format off
874 static const FunctionInfo functions[] = {
875 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
876 {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
877 {2, nullptr, "GetThisAppletKind"},
878 {3, nullptr, "AllowToEnterSleep"},
879 {4, nullptr, "DisallowToEnterSleep"},
880 {5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"},
881 {6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"},
882 {7, nullptr, "GetCradleStatus"},
883 {8, &ICommonStateGetter::GetBootMode, "GetBootMode"},
884 {9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"},
885 {10, &ICommonStateGetter::RequestToAcquireSleepLock, "RequestToAcquireSleepLock"},
886 {11, nullptr, "ReleaseSleepLock"},
887 {12, nullptr, "ReleaseSleepLockTransiently"},
888 {13, &ICommonStateGetter::GetAcquiredSleepLockEvent, "GetAcquiredSleepLockEvent"},
889 {14, nullptr, "GetWakeupCount"},
890 {20, nullptr, "PushToGeneralChannel"},
891 {30, nullptr, "GetHomeButtonReaderLockAccessor"},
892 {31, &ICommonStateGetter::GetReaderLockAccessorEx, "GetReaderLockAccessorEx"},
893 {32, nullptr, "GetWriterLockAccessorEx"},
894 {40, nullptr, "GetCradleFwVersion"},
895 {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
896 {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
897 {52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
898 {53, &ICommonStateGetter::BeginVrModeEx, "BeginVrModeEx"},
899 {54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"},
900 {55, nullptr, "IsInControllerFirmwareUpdateSection"},
901 {59, nullptr, "SetVrPositionForDebug"},
902 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
903 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
904 {62, nullptr, "GetHdcpAuthenticationState"},
905 {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
906 {64, nullptr, "SetTvPowerStateMatchingMode"},
907 {65, nullptr, "GetApplicationIdByContentActionName"},
908 {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"},
909 {67, nullptr, "CancelCpuBoostMode"},
910 {68, &ICommonStateGetter::GetBuiltInDisplayType, "GetBuiltInDisplayType"},
911 {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"},
912 {90, nullptr, "SetPerformanceConfigurationChangedNotification"},
913 {91, nullptr, "GetCurrentPerformanceConfiguration"},
914 {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
915 {110, nullptr, "OpenMyGpuErrorHandler"},
916 {120, nullptr, "GetAppletLaunchedHistory"},
917 {200, nullptr, "GetOperationModeSystemInfo"},
918 {300, &ICommonStateGetter::GetSettingsPlatformRegion, "GetSettingsPlatformRegion"},
919 {400, nullptr, "ActivateMigrationService"},
920 {401, nullptr, "DeactivateMigrationService"},
921 {500, nullptr, "DisableSleepTillShutdown"},
922 {501, nullptr, "SuppressDisablingSleepTemporarily"},
923 {502, nullptr, "IsSleepEnabled"},
924 {503, nullptr, "IsDisablingSleepSuppressed"},
925 {900, &ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"},
926 };
927 // clang-format on
928
929 RegisterHandlers(functions);
930
931 sleep_lock_event = service_context.CreateEvent("ICommonStateGetter::SleepLockEvent");
932
933 // Configure applets to be in foreground state
934 msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
935 msg_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
936}
937
938ICommonStateGetter::~ICommonStateGetter() {
939 service_context.CloseEvent(sleep_lock_event);
940};
941
942void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) {
943 LOG_DEBUG(Service_AM, "called");
944
945 IPC::ResponseBuilder rb{ctx, 3};
946 rb.Push(ResultSuccess);
947 rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode
948}
949
950void ICommonStateGetter::GetEventHandle(HLERequestContext& ctx) {
951 LOG_DEBUG(Service_AM, "called");
952
953 IPC::ResponseBuilder rb{ctx, 2, 1};
954 rb.Push(ResultSuccess);
955 rb.PushCopyObjects(msg_queue->GetMessageReceiveEvent());
956}
957
958void ICommonStateGetter::ReceiveMessage(HLERequestContext& ctx) {
959 LOG_DEBUG(Service_AM, "called");
960
961 const auto message = msg_queue->PopMessage();
962 IPC::ResponseBuilder rb{ctx, 3};
963
964 if (message == AppletMessageQueue::AppletMessage::None) {
965 LOG_ERROR(Service_AM, "Message queue is empty");
966 rb.Push(AM::ResultNoMessages);
967 rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
968 return;
969 }
970
971 rb.Push(ResultSuccess);
972 rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
973}
974
975void ICommonStateGetter::GetCurrentFocusState(HLERequestContext& ctx) {
976 LOG_DEBUG(Service_AM, "(STUBBED) called");
977
978 IPC::ResponseBuilder rb{ctx, 3};
979 rb.Push(ResultSuccess);
980 rb.Push(static_cast<u8>(FocusState::InFocus));
981}
982
983void ICommonStateGetter::RequestToAcquireSleepLock(HLERequestContext& ctx) {
984 LOG_WARNING(Service_AM, "(STUBBED) called");
985
986 // Sleep lock is acquired immediately.
987 sleep_lock_event->Signal();
988
989 IPC::ResponseBuilder rb{ctx, 2};
990 rb.Push(ResultSuccess);
991}
992
993void ICommonStateGetter::GetReaderLockAccessorEx(HLERequestContext& ctx) {
994 IPC::RequestParser rp{ctx};
995 const auto unknown = rp.Pop<u32>();
996
997 LOG_INFO(Service_AM, "called, unknown={}", unknown);
998
999 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1000
1001 rb.Push(ResultSuccess);
1002 rb.PushIpcInterface<ILockAccessor>(system);
1003}
1004
1005void ICommonStateGetter::GetAcquiredSleepLockEvent(HLERequestContext& ctx) {
1006 LOG_WARNING(Service_AM, "called");
1007
1008 IPC::ResponseBuilder rb{ctx, 2, 1};
1009 rb.Push(ResultSuccess);
1010 rb.PushCopyObjects(sleep_lock_event->GetReadableEvent());
1011}
1012
1013void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) {
1014 LOG_DEBUG(Service_AM, "called");
1015
1016 IPC::ResponseBuilder rb{ctx, 3};
1017 rb.Push(ResultSuccess);
1018 rb.Push(vr_mode_state);
1019}
1020
1021void ICommonStateGetter::SetVrModeEnabled(HLERequestContext& ctx) {
1022 IPC::RequestParser rp{ctx};
1023 vr_mode_state = rp.Pop<bool>();
1024
1025 LOG_WARNING(Service_AM, "VR Mode is {}", vr_mode_state ? "on" : "off");
1026
1027 IPC::ResponseBuilder rb{ctx, 2};
1028 rb.Push(ResultSuccess);
1029}
1030
1031void ICommonStateGetter::SetLcdBacklighOffEnabled(HLERequestContext& ctx) {
1032 IPC::RequestParser rp{ctx};
1033 const auto is_lcd_backlight_off_enabled = rp.Pop<bool>();
1034
1035 LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}",
1036 is_lcd_backlight_off_enabled);
1037
1038 IPC::ResponseBuilder rb{ctx, 2};
1039 rb.Push(ResultSuccess);
1040}
1041
1042void ICommonStateGetter::BeginVrModeEx(HLERequestContext& ctx) {
1043 LOG_WARNING(Service_AM, "(STUBBED) called");
1044
1045 IPC::ResponseBuilder rb{ctx, 2};
1046 rb.Push(ResultSuccess);
1047}
1048
1049void ICommonStateGetter::EndVrModeEx(HLERequestContext& ctx) {
1050 LOG_WARNING(Service_AM, "(STUBBED) called");
1051
1052 IPC::ResponseBuilder rb{ctx, 2};
1053 rb.Push(ResultSuccess);
1054}
1055
1056void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx) {
1057 LOG_DEBUG(Service_AM, "called");
1058
1059 IPC::ResponseBuilder rb{ctx, 2, 1};
1060 rb.Push(ResultSuccess);
1061 rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent());
1062}
1063
1064void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) {
1065 LOG_DEBUG(Service_AM, "called");
1066
1067 IPC::ResponseBuilder rb{ctx, 4};
1068 rb.Push(ResultSuccess);
1069
1070 if (Settings::IsDockedMode()) {
1071 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
1072 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
1073 } else {
1074 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth));
1075 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight));
1076 }
1077}
1078
1079void ICommonStateGetter::SetCpuBoostMode(HLERequestContext& ctx) {
1080 LOG_DEBUG(Service_AM, "called, forwarding to APM:SYS");
1081
1082 const auto& sm = system.ServiceManager();
1083 const auto apm_sys = sm.GetService<APM::APM_Sys>("apm:sys");
1084 ASSERT(apm_sys != nullptr);
1085
1086 apm_sys->SetCpuBoostMode(ctx);
1087}
1088
1089void ICommonStateGetter::GetBuiltInDisplayType(HLERequestContext& ctx) {
1090 LOG_WARNING(Service_AM, "(STUBBED) called");
1091
1092 IPC::ResponseBuilder rb{ctx, 3};
1093 rb.Push(ResultSuccess);
1094 rb.Push(0);
1095}
1096
1097void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx) {
1098 IPC::RequestParser rp{ctx};
1099 const auto system_button{rp.PopEnum<SystemButtonType>()};
1100
1101 LOG_WARNING(Service_AM, "(STUBBED) called, system_button={}", system_button);
1102
1103 IPC::ResponseBuilder rb{ctx, 2};
1104 rb.Push(ResultSuccess);
1105}
1106
1107void ICommonStateGetter::GetSettingsPlatformRegion(HLERequestContext& ctx) {
1108 LOG_WARNING(Service_AM, "(STUBBED) called");
1109
1110 IPC::ResponseBuilder rb{ctx, 3};
1111 rb.Push(ResultSuccess);
1112 rb.PushEnum(SysPlatformRegion::Global);
1113}
1114
1115void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(
1116 HLERequestContext& ctx) {
1117 LOG_WARNING(Service_AM, "(STUBBED) called");
1118
1119 IPC::ResponseBuilder rb{ctx, 2};
1120 rb.Push(ResultSuccess);
1121}
1122
1123IStorageImpl::~IStorageImpl() = default;
1124
1125class StorageDataImpl final : public IStorageImpl {
1126public:
1127 explicit StorageDataImpl(std::vector<u8>&& buffer_) : buffer{std::move(buffer_)} {}
1128
1129 std::vector<u8>& GetData() override {
1130 return buffer;
1131 }
1132
1133 const std::vector<u8>& GetData() const override {
1134 return buffer;
1135 }
1136
1137 std::size_t GetSize() const override {
1138 return buffer.size();
1139 }
1140
1141private:
1142 std::vector<u8> buffer;
1143};
1144
1145IStorage::IStorage(Core::System& system_, std::vector<u8>&& buffer)
1146 : ServiceFramework{system_, "IStorage"}, impl{std::make_shared<StorageDataImpl>(
1147 std::move(buffer))} {
1148 Register();
1149}
1150
1151void IStorage::Register() {
1152 // clang-format off
1153 static const FunctionInfo functions[] = {
1154 {0, &IStorage::Open, "Open"},
1155 {1, nullptr, "OpenTransferStorage"},
1156 };
1157 // clang-format on
1158
1159 RegisterHandlers(functions);
1160}
1161
1162IStorage::~IStorage() = default;
1163
1164void IStorage::Open(HLERequestContext& ctx) {
1165 LOG_DEBUG(Service_AM, "called");
1166
1167 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1168
1169 rb.Push(ResultSuccess);
1170 rb.PushIpcInterface<IStorageAccessor>(system, *this);
1171}
1172
1173void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) {
1174 const bool use_docked_mode{Settings::IsDockedMode()};
1175 LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
1176
1177 IPC::ResponseBuilder rb{ctx, 3};
1178 rb.Push(ResultSuccess);
1179 rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
1180}
1181
1182void ICommonStateGetter::GetPerformanceMode(HLERequestContext& ctx) {
1183 LOG_DEBUG(Service_AM, "called");
1184
1185 IPC::ResponseBuilder rb{ctx, 3};
1186 rb.Push(ResultSuccess);
1187 rb.PushEnum(system.GetAPMController().GetCurrentPerformanceMode());
1188}
1189
1190class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
1191public:
1192 explicit ILibraryAppletAccessor(Core::System& system_, std::shared_ptr<Applets::Applet> applet_)
1193 : ServiceFramework{system_, "ILibraryAppletAccessor"}, applet{std::move(applet_)} {
1194 // clang-format off
1195 static const FunctionInfo functions[] = {
1196 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
1197 {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"},
1198 {10, &ILibraryAppletAccessor::Start, "Start"},
1199 {20, &ILibraryAppletAccessor::RequestExit, "RequestExit"},
1200 {25, nullptr, "Terminate"},
1201 {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
1202 {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
1203 {60, &ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero, "PresetLibraryAppletGpuTimeSliceZero"},
1204 {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
1205 {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
1206 {102, nullptr, "PushExtraStorage"},
1207 {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
1208 {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
1209 {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"},
1210 {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
1211 {110, nullptr, "NeedsToExitProcess"},
1212 {120, nullptr, "GetLibraryAppletInfo"},
1213 {150, nullptr, "RequestForAppletToGetForeground"},
1214 {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"},
1215 };
1216 // clang-format on
1217
1218 RegisterHandlers(functions);
1219 }
1220
1221private:
1222 void GetAppletStateChangedEvent(HLERequestContext& ctx) {
1223 LOG_DEBUG(Service_AM, "called");
1224
1225 IPC::ResponseBuilder rb{ctx, 2, 1};
1226 rb.Push(ResultSuccess);
1227 rb.PushCopyObjects(applet->GetBroker().GetStateChangedEvent());
1228 }
1229
1230 void IsCompleted(HLERequestContext& ctx) {
1231 LOG_DEBUG(Service_AM, "called");
1232
1233 IPC::ResponseBuilder rb{ctx, 3};
1234 rb.Push(ResultSuccess);
1235 rb.Push<u32>(applet->TransactionComplete());
1236 }
1237
1238 void GetResult(HLERequestContext& ctx) {
1239 LOG_DEBUG(Service_AM, "called");
1240
1241 IPC::ResponseBuilder rb{ctx, 2};
1242 rb.Push(applet->GetStatus());
1243 }
1244
1245 void PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx) {
1246 LOG_WARNING(Service_AM, "(STUBBED) called");
1247
1248 IPC::ResponseBuilder rb{ctx, 2};
1249 rb.Push(ResultSuccess);
1250 }
1251
1252 void Start(HLERequestContext& ctx) {
1253 LOG_DEBUG(Service_AM, "called");
1254
1255 ASSERT(applet != nullptr);
1256
1257 applet->Initialize();
1258 applet->Execute();
1259
1260 IPC::ResponseBuilder rb{ctx, 2};
1261 rb.Push(ResultSuccess);
1262 }
1263
1264 void RequestExit(HLERequestContext& ctx) {
1265 LOG_DEBUG(Service_AM, "called");
1266
1267 ASSERT(applet != nullptr);
1268
1269 IPC::ResponseBuilder rb{ctx, 2};
1270 rb.Push(applet->RequestExit());
1271 }
1272
1273 void PushInData(HLERequestContext& ctx) {
1274 LOG_DEBUG(Service_AM, "called");
1275
1276 IPC::RequestParser rp{ctx};
1277 applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>().lock());
1278
1279 IPC::ResponseBuilder rb{ctx, 2};
1280 rb.Push(ResultSuccess);
1281 }
1282
1283 void PopOutData(HLERequestContext& ctx) {
1284 LOG_DEBUG(Service_AM, "called");
1285
1286 auto storage = applet->GetBroker().PopNormalDataToGame();
1287 if (storage == nullptr) {
1288 LOG_DEBUG(Service_AM,
1289 "storage is a nullptr. There is no data in the current normal channel");
1290 IPC::ResponseBuilder rb{ctx, 2};
1291 rb.Push(AM::ResultNoDataInChannel);
1292 return;
1293 }
1294
1295 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1296 rb.Push(ResultSuccess);
1297 rb.PushIpcInterface<IStorage>(std::move(storage));
1298 }
1299
1300 void PushInteractiveInData(HLERequestContext& ctx) {
1301 LOG_DEBUG(Service_AM, "called");
1302
1303 IPC::RequestParser rp{ctx};
1304 applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>().lock());
1305
1306 ASSERT(applet->IsInitialized());
1307 applet->ExecuteInteractive();
1308 applet->Execute();
1309
1310 IPC::ResponseBuilder rb{ctx, 2};
1311 rb.Push(ResultSuccess);
1312 }
1313
1314 void PopInteractiveOutData(HLERequestContext& ctx) {
1315 LOG_DEBUG(Service_AM, "called");
1316
1317 auto storage = applet->GetBroker().PopInteractiveDataToGame();
1318 if (storage == nullptr) {
1319 LOG_DEBUG(Service_AM,
1320 "storage is a nullptr. There is no data in the current interactive channel");
1321 IPC::ResponseBuilder rb{ctx, 2};
1322 rb.Push(AM::ResultNoDataInChannel);
1323 return;
1324 }
1325
1326 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1327 rb.Push(ResultSuccess);
1328 rb.PushIpcInterface<IStorage>(std::move(storage));
1329 }
1330
1331 void GetPopOutDataEvent(HLERequestContext& ctx) {
1332 LOG_DEBUG(Service_AM, "called");
1333
1334 IPC::ResponseBuilder rb{ctx, 2, 1};
1335 rb.Push(ResultSuccess);
1336 rb.PushCopyObjects(applet->GetBroker().GetNormalDataEvent());
1337 }
1338
1339 void GetPopInteractiveOutDataEvent(HLERequestContext& ctx) {
1340 LOG_DEBUG(Service_AM, "called");
1341
1342 IPC::ResponseBuilder rb{ctx, 2, 1};
1343 rb.Push(ResultSuccess);
1344 rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
1345 }
1346
1347 void GetIndirectLayerConsumerHandle(HLERequestContext& ctx) {
1348 LOG_WARNING(Service_AM, "(STUBBED) called");
1349
1350 // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is
1351 // actually used anywhere
1352 constexpr u64 handle = 0xdeadbeef;
1353
1354 IPC::ResponseBuilder rb{ctx, 4};
1355 rb.Push(ResultSuccess);
1356 rb.Push(handle);
1357 }
1358
1359 std::shared_ptr<Applets::Applet> applet;
1360};
1361
1362IStorageAccessor::IStorageAccessor(Core::System& system_, IStorage& backing_)
1363 : ServiceFramework{system_, "IStorageAccessor"}, backing{backing_} {
1364 // clang-format off
1365 static const FunctionInfo functions[] = {
1366 {0, &IStorageAccessor::GetSize, "GetSize"},
1367 {10, &IStorageAccessor::Write, "Write"},
1368 {11, &IStorageAccessor::Read, "Read"},
1369 };
1370 // clang-format on
1371
1372 RegisterHandlers(functions);
1373}
1374
1375IStorageAccessor::~IStorageAccessor() = default;
1376
1377void IStorageAccessor::GetSize(HLERequestContext& ctx) {
1378 LOG_DEBUG(Service_AM, "called");
1379
1380 IPC::ResponseBuilder rb{ctx, 4};
1381
1382 rb.Push(ResultSuccess);
1383 rb.Push(static_cast<u64>(backing.GetSize()));
1384}
1385
1386void IStorageAccessor::Write(HLERequestContext& ctx) {
1387 IPC::RequestParser rp{ctx};
1388
1389 const u64 offset{rp.Pop<u64>()};
1390 const auto data{ctx.ReadBuffer()};
1391 const std::size_t size{std::min<u64>(data.size(), backing.GetSize() - offset)};
1392
1393 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
1394
1395 if (offset > backing.GetSize()) {
1396 LOG_ERROR(Service_AM,
1397 "offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}",
1398 backing.GetSize(), size, offset);
1399
1400 IPC::ResponseBuilder rb{ctx, 2};
1401 rb.Push(AM::ResultInvalidOffset);
1402 return;
1403 }
1404
1405 std::memcpy(backing.GetData().data() + offset, data.data(), size);
1406
1407 IPC::ResponseBuilder rb{ctx, 2};
1408 rb.Push(ResultSuccess);
1409}
1410
1411void IStorageAccessor::Read(HLERequestContext& ctx) {
1412 IPC::RequestParser rp{ctx};
1413
1414 const u64 offset{rp.Pop<u64>()};
1415 const std::size_t size{std::min<u64>(ctx.GetWriteBufferSize(), backing.GetSize() - offset)};
1416
1417 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
1418
1419 if (offset > backing.GetSize()) {
1420 LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}",
1421 backing.GetSize(), size, offset);
1422
1423 IPC::ResponseBuilder rb{ctx, 2};
1424 rb.Push(AM::ResultInvalidOffset);
1425 return;
1426 }
1427
1428 ctx.WriteBuffer(backing.GetData().data() + offset, size);
1429
1430 IPC::ResponseBuilder rb{ctx, 2};
1431 rb.Push(ResultSuccess);
1432}
1433
1434ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_)
1435 : ServiceFramework{system_, "ILibraryAppletCreator"} {
1436 static const FunctionInfo functions[] = {
1437 {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
1438 {1, nullptr, "TerminateAllLibraryApplets"},
1439 {2, nullptr, "AreAnyLibraryAppletsLeft"},
1440 {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
1441 {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
1442 {12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"},
1443 };
1444 RegisterHandlers(functions);
1445}
1446
1447ILibraryAppletCreator::~ILibraryAppletCreator() = default;
1448
1449void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) {
1450 IPC::RequestParser rp{ctx};
1451
1452 const auto applet_id = rp.PopRaw<Applets::AppletId>();
1453 const auto applet_mode = rp.PopRaw<Applets::LibraryAppletMode>();
1454
1455 LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
1456 applet_mode);
1457
1458 const auto& applet_manager{system.GetAppletManager()};
1459 const auto applet = applet_manager.GetApplet(applet_id, applet_mode);
1460
1461 if (applet == nullptr) {
1462 LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
1463
1464 IPC::ResponseBuilder rb{ctx, 2};
1465 rb.Push(ResultUnknown);
1466 return;
1467 }
1468
1469 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1470
1471 rb.Push(ResultSuccess);
1472 rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet);
1473}
1474
1475void ILibraryAppletCreator::CreateStorage(HLERequestContext& ctx) {
1476 IPC::RequestParser rp{ctx};
1477
1478 const s64 size{rp.Pop<s64>()};
1479
1480 LOG_DEBUG(Service_AM, "called, size={}", size);
1481
1482 if (size <= 0) {
1483 LOG_ERROR(Service_AM, "size is less than or equal to 0");
1484 IPC::ResponseBuilder rb{ctx, 2};
1485 rb.Push(ResultUnknown);
1486 return;
1487 }
1488
1489 std::vector<u8> buffer(size);
1490
1491 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1492 rb.Push(ResultSuccess);
1493 rb.PushIpcInterface<IStorage>(system, std::move(buffer));
1494}
1495
1496void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx) {
1497 IPC::RequestParser rp{ctx};
1498
1499 struct Parameters {
1500 u8 permissions;
1501 s64 size;
1502 };
1503
1504 const auto parameters{rp.PopRaw<Parameters>()};
1505 const auto handle{ctx.GetCopyHandle(0)};
1506
1507 LOG_DEBUG(Service_AM, "called, permissions={}, size={}, handle={:08X}", parameters.permissions,
1508 parameters.size, handle);
1509
1510 if (parameters.size <= 0) {
1511 LOG_ERROR(Service_AM, "size is less than or equal to 0");
1512 IPC::ResponseBuilder rb{ctx, 2};
1513 rb.Push(ResultUnknown);
1514 return;
1515 }
1516
1517 auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
1518
1519 if (transfer_mem.IsNull()) {
1520 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
1521 IPC::ResponseBuilder rb{ctx, 2};
1522 rb.Push(ResultUnknown);
1523 return;
1524 }
1525
1526 std::vector<u8> memory(transfer_mem->GetSize());
1527 ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
1528
1529 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1530 rb.Push(ResultSuccess);
1531 rb.PushIpcInterface<IStorage>(system, std::move(memory));
1532}
1533
1534void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
1535 IPC::RequestParser rp{ctx};
1536
1537 const s64 size{rp.Pop<s64>()};
1538 const auto handle{ctx.GetCopyHandle(0)};
1539
1540 LOG_DEBUG(Service_AM, "called, size={}, handle={:08X}", size, handle);
1541
1542 if (size <= 0) {
1543 LOG_ERROR(Service_AM, "size is less than or equal to 0");
1544 IPC::ResponseBuilder rb{ctx, 2};
1545 rb.Push(ResultUnknown);
1546 return;
1547 }
1548
1549 auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
1550
1551 if (transfer_mem.IsNull()) {
1552 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
1553 IPC::ResponseBuilder rb{ctx, 2};
1554 rb.Push(ResultUnknown);
1555 return;
1556 }
1557
1558 std::vector<u8> memory(transfer_mem->GetSize());
1559 ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
1560
1561 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1562 rb.Push(ResultSuccess);
1563 rb.PushIpcInterface<IStorage>(system, std::move(memory));
1564}
1565
1566ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
1567 : ServiceFramework{system_, "ILibraryAppletSelfAccessor"} {
1568 // clang-format off
1569 static const FunctionInfo functions[] = {
1570 {0, &ILibraryAppletSelfAccessor::PopInData, "PopInData"},
1571 {1, &ILibraryAppletSelfAccessor::PushOutData, "PushOutData"},
1572 {2, nullptr, "PopInteractiveInData"},
1573 {3, nullptr, "PushInteractiveOutData"},
1574 {5, nullptr, "GetPopInDataEvent"},
1575 {6, nullptr, "GetPopInteractiveInDataEvent"},
1576 {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"},
1577 {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"},
1578 {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"},
1579 {13, nullptr, "CanUseApplicationCore"},
1580 {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"},
1581 {15, nullptr, "GetMainAppletApplicationControlProperty"},
1582 {16, nullptr, "GetMainAppletStorageId"},
1583 {17, nullptr, "GetCallerAppletIdentityInfoStack"},
1584 {18, nullptr, "GetNextReturnDestinationAppletIdentityInfo"},
1585 {19, &ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout, "GetDesirableKeyboardLayout"},
1586 {20, nullptr, "PopExtraStorage"},
1587 {25, nullptr, "GetPopExtraStorageEvent"},
1588 {30, nullptr, "UnpopInData"},
1589 {31, nullptr, "UnpopExtraStorage"},
1590 {40, nullptr, "GetIndirectLayerProducerHandle"},
1591 {50, nullptr, "ReportVisibleError"},
1592 {51, nullptr, "ReportVisibleErrorWithErrorContext"},
1593 {60, nullptr, "GetMainAppletApplicationDesiredLanguage"},
1594 {70, nullptr, "GetCurrentApplicationId"},
1595 {80, nullptr, "RequestExitToSelf"},
1596 {90, nullptr, "CreateApplicationAndPushAndRequestToLaunch"},
1597 {100, nullptr, "CreateGameMovieTrimmer"},
1598 {101, nullptr, "ReserveResourceForMovieOperation"},
1599 {102, nullptr, "UnreserveResourceForMovieOperation"},
1600 {110, &ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers, "GetMainAppletAvailableUsers"},
1601 {120, nullptr, "GetLaunchStorageInfoForDebug"},
1602 {130, nullptr, "GetGpuErrorDetectedSystemEvent"},
1603 {140, nullptr, "SetApplicationMemoryReservation"},
1604 {150, &ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually, "ShouldSetGpuTimeSliceManually"},
1605 };
1606 // clang-format on
1607 RegisterHandlers(functions);
1608
1609 switch (system.GetAppletManager().GetCurrentAppletId()) {
1610 case Applets::AppletId::Cabinet:
1611 PushInShowCabinetData();
1612 break;
1613 case Applets::AppletId::MiiEdit:
1614 PushInShowMiiEditData();
1615 break;
1616 case Applets::AppletId::PhotoViewer:
1617 PushInShowAlbum();
1618 break;
1619 case Applets::AppletId::SoftwareKeyboard:
1620 PushInShowSoftwareKeyboard();
1621 break;
1622 case Applets::AppletId::Controller:
1623 PushInShowController();
1624 break;
1625 default:
1626 break;
1627 }
1628}
1629
1630ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default;
1631void ILibraryAppletSelfAccessor::PopInData(HLERequestContext& ctx) {
1632 LOG_INFO(Service_AM, "called");
1633
1634 if (queue_data.empty()) {
1635 IPC::ResponseBuilder rb{ctx, 2};
1636 rb.Push(ResultNoDataInChannel);
1637 return;
1638 }
1639
1640 auto data = queue_data.front();
1641 queue_data.pop_front();
1642
1643 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1644 rb.Push(ResultSuccess);
1645 rb.PushIpcInterface<IStorage>(system, std::move(data));
1646}
1647
1648void ILibraryAppletSelfAccessor::PushOutData(HLERequestContext& ctx) {
1649 LOG_WARNING(Service_AM, "(STUBBED) called");
1650
1651 IPC::ResponseBuilder rb{ctx, 2};
1652 rb.Push(ResultSuccess);
1653}
1654
1655void ILibraryAppletSelfAccessor::ExitProcessAndReturn(HLERequestContext& ctx) {
1656 LOG_WARNING(Service_AM, "(STUBBED) called");
1657
1658 system.Exit();
1659
1660 IPC::ResponseBuilder rb{ctx, 2};
1661 rb.Push(ResultSuccess);
1662}
1663
1664void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
1665 struct LibraryAppletInfo {
1666 Applets::AppletId applet_id;
1667 Applets::LibraryAppletMode library_applet_mode;
1668 };
1669
1670 LOG_WARNING(Service_AM, "(STUBBED) called");
1671
1672 const LibraryAppletInfo applet_info{
1673 .applet_id = system.GetAppletManager().GetCurrentAppletId(),
1674 .library_applet_mode = Applets::LibraryAppletMode::AllForeground,
1675 };
1676
1677 IPC::ResponseBuilder rb{ctx, 4};
1678 rb.Push(ResultSuccess);
1679 rb.PushRaw(applet_info);
1680}
1681
1682void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) {
1683 struct AppletIdentityInfo {
1684 Applets::AppletId applet_id;
1685 INSERT_PADDING_BYTES(0x4);
1686 u64 application_id;
1687 };
1688 static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
1689
1690 LOG_WARNING(Service_AM, "(STUBBED) called");
1691
1692 const AppletIdentityInfo applet_info{
1693 .applet_id = Applets::AppletId::QLaunch,
1694 .application_id = 0x0100000000001000ull,
1695 };
1696
1697 IPC::ResponseBuilder rb{ctx, 6};
1698 rb.Push(ResultSuccess);
1699 rb.PushRaw(applet_info);
1700}
1701
1702void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
1703 struct AppletIdentityInfo {
1704 Applets::AppletId applet_id;
1705 INSERT_PADDING_BYTES(0x4);
1706 u64 application_id;
1707 };
1708 static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
1709 LOG_WARNING(Service_AM, "(STUBBED) called");
1710
1711 const AppletIdentityInfo applet_info{
1712 .applet_id = Applets::AppletId::QLaunch,
1713 .application_id = 0x0100000000001000ull,
1714 };
1715
1716 IPC::ResponseBuilder rb{ctx, 6};
1717 rb.Push(ResultSuccess);
1718 rb.PushRaw(applet_info);
1719}
1720
1721void ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout(HLERequestContext& ctx) {
1722 LOG_WARNING(Service_AM, "(STUBBED) called");
1723
1724 IPC::ResponseBuilder rb{ctx, 3};
1725 rb.Push(ResultSuccess);
1726 rb.Push<u32>(0);
1727}
1728
1729void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) {
1730 const Service::Account::ProfileManager manager{};
1731 bool is_empty{true};
1732 s32 user_count{-1};
1733
1734 LOG_INFO(Service_AM, "called");
1735
1736 if (manager.GetUserCount() > 0) {
1737 is_empty = false;
1738 user_count = static_cast<s32>(manager.GetUserCount());
1739 ctx.WriteBuffer(manager.GetAllUsers());
1740 }
1741
1742 IPC::ResponseBuilder rb{ctx, 4};
1743 rb.Push(ResultSuccess);
1744 rb.Push<u8>(is_empty);
1745 rb.Push(user_count);
1746}
1747
1748void ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually(HLERequestContext& ctx) {
1749 LOG_WARNING(Service_AM, "(STUBBED) called");
1750
1751 IPC::ResponseBuilder rb{ctx, 2};
1752 rb.Push(ResultSuccess);
1753 rb.Push<u8>(0);
1754}
1755
1756void ILibraryAppletSelfAccessor::PushInShowAlbum() {
1757 const Applets::CommonArguments arguments{
1758 .arguments_version = Applets::CommonArgumentVersion::Version3,
1759 .size = Applets::CommonArgumentSize::Version3,
1760 .library_version = 1,
1761 .theme_color = Applets::ThemeColor::BasicBlack,
1762 .play_startup_sound = true,
1763 .system_tick = system.CoreTiming().GetClockTicks(),
1764 };
1765
1766 std::vector<u8> argument_data(sizeof(arguments));
1767 std::vector<u8> settings_data{2};
1768 std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
1769 queue_data.emplace_back(std::move(argument_data));
1770 queue_data.emplace_back(std::move(settings_data));
1771}
1772
1773void ILibraryAppletSelfAccessor::PushInShowController() {
1774 const Applets::CommonArguments common_args = {
1775 .arguments_version = Applets::CommonArgumentVersion::Version3,
1776 .size = Applets::CommonArgumentSize::Version3,
1777 .library_version = static_cast<u32>(Applets::ControllerAppletVersion::Version8),
1778 .theme_color = Applets::ThemeColor::BasicBlack,
1779 .play_startup_sound = true,
1780 .system_tick = system.CoreTiming().GetClockTicks(),
1781 };
1782
1783 Applets::ControllerSupportArgNew user_args = {
1784 .header = {.player_count_min = 1,
1785 .player_count_max = 4,
1786 .enable_take_over_connection = true,
1787 .enable_left_justify = false,
1788 .enable_permit_joy_dual = true,
1789 .enable_single_mode = false,
1790 .enable_identification_color = false},
1791 .identification_colors = {},
1792 .enable_explain_text = false,
1793 .explain_text = {},
1794 };
1795
1796 Applets::ControllerSupportArgPrivate private_args = {
1797 .arg_private_size = sizeof(Applets::ControllerSupportArgPrivate),
1798 .arg_size = sizeof(Applets::ControllerSupportArgNew),
1799 .is_home_menu = true,
1800 .flag_1 = true,
1801 .mode = Applets::ControllerSupportMode::ShowControllerSupport,
1802 .caller = Applets::ControllerSupportCaller::
1803 Application, // switchbrew: Always zero except with
1804 // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem,
1805 // which sets this to the input param
1806 .style_set = Core::HID::NpadStyleSet::None,
1807 .joy_hold_type = 0,
1808 };
1809 std::vector<u8> common_args_data(sizeof(common_args));
1810 std::vector<u8> private_args_data(sizeof(private_args));
1811 std::vector<u8> user_args_data(sizeof(user_args));
1812
1813 std::memcpy(common_args_data.data(), &common_args, sizeof(common_args));
1814 std::memcpy(private_args_data.data(), &private_args, sizeof(private_args));
1815 std::memcpy(user_args_data.data(), &user_args, sizeof(user_args));
1816
1817 queue_data.emplace_back(std::move(common_args_data));
1818 queue_data.emplace_back(std::move(private_args_data));
1819 queue_data.emplace_back(std::move(user_args_data));
1820}
1821
1822void ILibraryAppletSelfAccessor::PushInShowCabinetData() {
1823 const Applets::CommonArguments arguments{
1824 .arguments_version = Applets::CommonArgumentVersion::Version3,
1825 .size = Applets::CommonArgumentSize::Version3,
1826 .library_version = static_cast<u32>(Applets::CabinetAppletVersion::Version1),
1827 .theme_color = Applets::ThemeColor::BasicBlack,
1828 .play_startup_sound = true,
1829 .system_tick = system.CoreTiming().GetClockTicks(),
1830 };
1831
1832 const Applets::StartParamForAmiiboSettings amiibo_settings{
1833 .param_1 = 0,
1834 .applet_mode = system.GetAppletManager().GetCabinetMode(),
1835 .flags = Applets::CabinetFlags::None,
1836 .amiibo_settings_1 = 0,
1837 .device_handle = 0,
1838 .tag_info{},
1839 .register_info{},
1840 .amiibo_settings_3{},
1841 };
1842
1843 std::vector<u8> argument_data(sizeof(arguments));
1844 std::vector<u8> settings_data(sizeof(amiibo_settings));
1845 std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
1846 std::memcpy(settings_data.data(), &amiibo_settings, sizeof(amiibo_settings));
1847 queue_data.emplace_back(std::move(argument_data));
1848 queue_data.emplace_back(std::move(settings_data));
1849}
1850
1851void ILibraryAppletSelfAccessor::PushInShowMiiEditData() {
1852 struct MiiEditV3 {
1853 Applets::MiiEditAppletInputCommon common;
1854 Applets::MiiEditAppletInputV3 input;
1855 };
1856 static_assert(sizeof(MiiEditV3) == 0x100, "MiiEditV3 has incorrect size.");
1857
1858 MiiEditV3 mii_arguments{
1859 .common =
1860 {
1861 .version = Applets::MiiEditAppletVersion::Version3,
1862 .applet_mode = Applets::MiiEditAppletMode::ShowMiiEdit,
1863 },
1864 .input{},
1865 };
1866
1867 std::vector<u8> argument_data(sizeof(mii_arguments));
1868 std::memcpy(argument_data.data(), &mii_arguments, sizeof(mii_arguments));
1869
1870 queue_data.emplace_back(std::move(argument_data));
1871}
1872
1873void ILibraryAppletSelfAccessor::PushInShowSoftwareKeyboard() {
1874 const Applets::CommonArguments arguments{
1875 .arguments_version = Applets::CommonArgumentVersion::Version3,
1876 .size = Applets::CommonArgumentSize::Version3,
1877 .library_version = static_cast<u32>(Applets::SwkbdAppletVersion::Version524301),
1878 .theme_color = Applets::ThemeColor::BasicBlack,
1879 .play_startup_sound = true,
1880 .system_tick = system.CoreTiming().GetClockTicks(),
1881 };
1882
1883 std::vector<char16_t> initial_string(0);
1884
1885 const Applets::SwkbdConfigCommon swkbd_config{
1886 .type = Applets::SwkbdType::Qwerty,
1887 .ok_text{},
1888 .left_optional_symbol_key{},
1889 .right_optional_symbol_key{},
1890 .use_prediction = false,
1891 .key_disable_flags{},
1892 .initial_cursor_position = Applets::SwkbdInitialCursorPosition::Start,
1893 .header_text{},
1894 .sub_text{},
1895 .guide_text{},
1896 .max_text_length = 500,
1897 .min_text_length = 0,
1898 .password_mode = Applets::SwkbdPasswordMode::Disabled,
1899 .text_draw_type = Applets::SwkbdTextDrawType::Box,
1900 .enable_return_button = true,
1901 .use_utf8 = false,
1902 .use_blur_background = true,
1903 .initial_string_offset{},
1904 .initial_string_length = static_cast<u32>(initial_string.size()),
1905 .user_dictionary_offset{},
1906 .user_dictionary_entries{},
1907 .use_text_check = false,
1908 };
1909
1910 Applets::SwkbdConfigNew swkbd_config_new{};
1911
1912 std::vector<u8> argument_data(sizeof(arguments));
1913 std::vector<u8> swkbd_data(sizeof(swkbd_config) + sizeof(swkbd_config_new));
1914 std::vector<u8> work_buffer(swkbd_config.initial_string_length * sizeof(char16_t));
1915
1916 std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
1917 std::memcpy(swkbd_data.data(), &swkbd_config, sizeof(swkbd_config));
1918 std::memcpy(swkbd_data.data() + sizeof(swkbd_config), &swkbd_config_new,
1919 sizeof(Applets::SwkbdConfigNew));
1920 std::memcpy(work_buffer.data(), initial_string.data(),
1921 swkbd_config.initial_string_length * sizeof(char16_t));
1922
1923 queue_data.emplace_back(std::move(argument_data));
1924 queue_data.emplace_back(std::move(swkbd_data));
1925 queue_data.emplace_back(std::move(work_buffer));
1926}
1927
1928IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_)
1929 : ServiceFramework{system_, "IAppletCommonFunctions"} {
1930 // clang-format off
1931 static const FunctionInfo functions[] = {
1932 {0, nullptr, "SetTerminateResult"},
1933 {10, nullptr, "ReadThemeStorage"},
1934 {11, nullptr, "WriteThemeStorage"},
1935 {20, nullptr, "PushToAppletBoundChannel"},
1936 {21, nullptr, "TryPopFromAppletBoundChannel"},
1937 {40, nullptr, "GetDisplayLogicalResolution"},
1938 {42, nullptr, "SetDisplayMagnification"},
1939 {50, nullptr, "SetHomeButtonDoubleClickEnabled"},
1940 {51, nullptr, "GetHomeButtonDoubleClickEnabled"},
1941 {52, nullptr, "IsHomeButtonShortPressedBlocked"},
1942 {60, nullptr, "IsVrModeCurtainRequired"},
1943 {61, nullptr, "IsSleepRequiredByHighTemperature"},
1944 {62, nullptr, "IsSleepRequiredByLowBattery"},
1945 {70, &IAppletCommonFunctions::SetCpuBoostRequestPriority, "SetCpuBoostRequestPriority"},
1946 {80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"},
1947 {81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"},
1948 {90, nullptr, "OpenNamedChannelAsParent"},
1949 {91, nullptr, "OpenNamedChannelAsChild"},
1950 {100, nullptr, "SetApplicationCoreUsageMode"},
1951 };
1952 // clang-format on
1953
1954 RegisterHandlers(functions);
1955}
1956
1957IAppletCommonFunctions::~IAppletCommonFunctions() = default;
1958
1959void IAppletCommonFunctions::SetCpuBoostRequestPriority(HLERequestContext& ctx) {
1960 LOG_WARNING(Service_AM, "(STUBBED) called");
1961
1962 IPC::ResponseBuilder rb{ctx, 2};
1963 rb.Push(ResultSuccess);
1964}
1965
1966IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1967 : ServiceFramework{system_, "IApplicationFunctions"}, service_context{system,
1968 "IApplicationFunctions"} {
1969 // clang-format off
1970 static const FunctionInfo functions[] = {
1971 {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
1972 {10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
1973 {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
1974 {12, nullptr, "CreateApplicationAndRequestToStart"},
1975 {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
1976 {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"},
1977 {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"},
1978 {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
1979 {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
1980 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
1981 {23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
1982 {24, nullptr, "GetLaunchStorageInfoForDebug"},
1983 {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
1984 {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
1985 {27, &IApplicationFunctions::CreateCacheStorage, "CreateCacheStorage"},
1986 {28, &IApplicationFunctions::GetSaveDataSizeMax, "GetSaveDataSizeMax"},
1987 {29, nullptr, "GetCacheStorageMax"},
1988 {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
1989 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
1990 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
1991 {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
1992 {34, nullptr, "SelectApplicationLicense"},
1993 {35, nullptr, "GetDeviceSaveDataSizeMax"},
1994 {36, nullptr, "GetLimitedApplicationLicense"},
1995 {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
1996 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
1997 {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
1998 {60, nullptr, "SetMediaPlaybackStateForApplication"},
1999 {65, &IApplicationFunctions::IsGamePlayRecordingSupported, "IsGamePlayRecordingSupported"},
2000 {66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"},
2001 {67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"},
2002 {68, nullptr, "RequestFlushGamePlayingMovieForDebug"},
2003 {70, nullptr, "RequestToShutdown"},
2004 {71, nullptr, "RequestToReboot"},
2005 {72, nullptr, "RequestToSleep"},
2006 {80, nullptr, "ExitAndRequestToShowThanksMessage"},
2007 {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
2008 {100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"},
2009 {101, &IApplicationFunctions::SetApplicationCopyrightImage, "SetApplicationCopyrightImage"},
2010 {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"},
2011 {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"},
2012 {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"},
2013 {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"},
2014 {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"},
2015 {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"},
2016 {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
2017 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
2018 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
2019 {131, nullptr, "SetDelayTimeToAbortOnGpuError"},
2020 {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
2021 {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"},
2022 {150, &IApplicationFunctions::GetNotificationStorageChannelEvent, "GetNotificationStorageChannelEvent"},
2023 {151, nullptr, "TryPopFromNotificationStorageChannel"},
2024 {160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"},
2025 {170, nullptr, "SetHdcpAuthenticationActivated"},
2026 {180, nullptr, "GetLaunchRequiredVersion"},
2027 {181, nullptr, "UpgradeLaunchRequiredVersion"},
2028 {190, nullptr, "SendServerMaintenanceOverlayNotification"},
2029 {200, nullptr, "GetLastApplicationExitReason"},
2030 {500, nullptr, "StartContinuousRecordingFlushForDebug"},
2031 {1000, nullptr, "CreateMovieMaker"},
2032 {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
2033 };
2034 // clang-format on
2035
2036 RegisterHandlers(functions);
2037
2038 gpu_error_detected_event =
2039 service_context.CreateEvent("IApplicationFunctions:GpuErrorDetectedSystemEvent");
2040 friend_invitation_storage_channel_event =
2041 service_context.CreateEvent("IApplicationFunctions:FriendInvitationStorageChannelEvent");
2042 notification_storage_channel_event =
2043 service_context.CreateEvent("IApplicationFunctions:NotificationStorageChannelEvent");
2044 health_warning_disappeared_system_event =
2045 service_context.CreateEvent("IApplicationFunctions:HealthWarningDisappearedSystemEvent");
2046}
2047
2048IApplicationFunctions::~IApplicationFunctions() {
2049 service_context.CloseEvent(gpu_error_detected_event);
2050 service_context.CloseEvent(friend_invitation_storage_channel_event);
2051 service_context.CloseEvent(notification_storage_channel_event);
2052 service_context.CloseEvent(health_warning_disappeared_system_event);
2053}
2054
2055void IApplicationFunctions::EnableApplicationCrashReport(HLERequestContext& ctx) {
2056 LOG_WARNING(Service_AM, "(STUBBED) called");
2057
2058 IPC::ResponseBuilder rb{ctx, 2};
2059 rb.Push(ResultSuccess);
2060}
2061
2062void IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx) {
2063 LOG_WARNING(Service_AM, "(STUBBED) called");
2064
2065 IPC::ResponseBuilder rb{ctx, 2};
2066 rb.Push(ResultSuccess);
2067}
2068
2069void IApplicationFunctions::SetApplicationCopyrightImage(HLERequestContext& ctx) {
2070 LOG_WARNING(Service_AM, "(STUBBED) called");
2071
2072 IPC::ResponseBuilder rb{ctx, 2};
2073 rb.Push(ResultSuccess);
2074}
2075
2076void IApplicationFunctions::SetApplicationCopyrightVisibility(HLERequestContext& ctx) {
2077 IPC::RequestParser rp{ctx};
2078 const auto is_visible = rp.Pop<bool>();
2079
2080 LOG_WARNING(Service_AM, "(STUBBED) called, is_visible={}", is_visible);
2081
2082 IPC::ResponseBuilder rb{ctx, 2};
2083 rb.Push(ResultSuccess);
2084}
2085
2086void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) {
2087 LOG_WARNING(Service_AM, "(STUBBED) called");
2088
2089 IPC::ResponseBuilder rb{ctx, 2};
2090 rb.Push(ResultSuccess);
2091}
2092
2093void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) {
2094 LOG_WARNING(Service_AM, "(STUBBED) called");
2095
2096 IPC::ResponseBuilder rb{ctx, 2};
2097 rb.Push(ResultSuccess);
2098}
2099
2100void IApplicationFunctions::BeginBlockingHomeButton(HLERequestContext& ctx) {
2101 LOG_WARNING(Service_AM, "(STUBBED) called");
2102
2103 IPC::ResponseBuilder rb{ctx, 2};
2104 rb.Push(ResultSuccess);
2105}
2106
2107void IApplicationFunctions::EndBlockingHomeButton(HLERequestContext& ctx) {
2108 LOG_WARNING(Service_AM, "(STUBBED) called");
2109
2110 IPC::ResponseBuilder rb{ctx, 2};
2111 rb.Push(ResultSuccess);
2112}
2113
2114void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) {
2115 IPC::RequestParser rp{ctx};
2116 const auto kind = rp.PopEnum<LaunchParameterKind>();
2117
2118 LOG_INFO(Service_AM, "called, kind={:08X}", kind);
2119
2120 if (kind == LaunchParameterKind::UserChannel) {
2121 auto channel = system.GetUserChannel();
2122 if (channel.empty()) {
2123 LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!");
2124 IPC::ResponseBuilder rb{ctx, 2};
2125 rb.Push(AM::ResultNoDataInChannel);
2126 return;
2127 }
2128
2129 auto data = channel.back();
2130 channel.pop_back();
2131
2132 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
2133 rb.Push(ResultSuccess);
2134 rb.PushIpcInterface<IStorage>(system, std::move(data));
2135 } else if (kind == LaunchParameterKind::AccountPreselectedUser &&
2136 !launch_popped_account_preselect) {
2137 // TODO: Verify this is hw-accurate
2138 LaunchParameterAccountPreselectedUser params{};
2139
2140 params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC;
2141 params.is_account_selected = 1;
2142
2143 Account::ProfileManager profile_manager{};
2144 const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user));
2145 ASSERT(uuid.has_value() && uuid->IsValid());
2146 params.current_user = *uuid;
2147
2148 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
2149 rb.Push(ResultSuccess);
2150
2151 std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
2152 std::memcpy(buffer.data(), &params, buffer.size());
2153
2154 rb.PushIpcInterface<IStorage>(system, std::move(buffer));
2155 launch_popped_account_preselect = true;
2156 } else {
2157 LOG_ERROR(Service_AM, "Unknown launch parameter kind.");
2158 IPC::ResponseBuilder rb{ctx, 2};
2159 rb.Push(AM::ResultNoDataInChannel);
2160 }
2161}
2162
2163void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) {
2164 LOG_WARNING(Service_AM, "(STUBBED) called");
2165
2166 IPC::ResponseBuilder rb{ctx, 2};
2167 rb.Push(ResultSuccess);
2168}
2169
2170void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) {
2171 IPC::RequestParser rp{ctx};
2172 u128 user_id = rp.PopRaw<u128>();
2173
2174 LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
2175
2176 FileSys::SaveDataAttribute attribute{};
2177 attribute.title_id = system.GetApplicationProcessProgramID();
2178 attribute.user_id = user_id;
2179 attribute.type = FileSys::SaveDataType::SaveData;
2180
2181 FileSys::VirtualDir save_data{};
2182 const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
2183 &save_data, FileSys::SaveDataSpaceId::NandUser, attribute);
2184
2185 IPC::ResponseBuilder rb{ctx, 4};
2186 rb.Push(res);
2187 rb.Push<u64>(0);
2188}
2189
2190void IApplicationFunctions::SetTerminateResult(HLERequestContext& ctx) {
2191 // Takes an input u32 Result, no output.
2192 // For example, in some cases official apps use this with error 0x2A2 then
2193 // uses svcBreak.
2194
2195 IPC::RequestParser rp{ctx};
2196 u32 result = rp.Pop<u32>();
2197 LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result);
2198
2199 IPC::ResponseBuilder rb{ctx, 2};
2200 rb.Push(ResultSuccess);
2201}
2202
2203void IApplicationFunctions::GetDisplayVersion(HLERequestContext& ctx) {
2204 LOG_DEBUG(Service_AM, "called");
2205
2206 std::array<u8, 0x10> version_string{};
2207
2208 const auto res = [this] {
2209 const auto title_id = system.GetApplicationProcessProgramID();
2210
2211 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
2212 system.GetContentProvider()};
2213 auto metadata = pm.GetControlMetadata();
2214 if (metadata.first != nullptr) {
2215 return metadata;
2216 }
2217
2218 const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
2219 system.GetFileSystemController(),
2220 system.GetContentProvider()};
2221 return pm_update.GetControlMetadata();
2222 }();
2223
2224 if (res.first != nullptr) {
2225 const auto& version = res.first->GetVersionString();
2226 std::copy(version.begin(), version.end(), version_string.begin());
2227 } else {
2228 static constexpr char default_version[]{"1.0.0"};
2229 std::memcpy(version_string.data(), default_version, sizeof(default_version));
2230 }
2231
2232 IPC::ResponseBuilder rb{ctx, 6};
2233 rb.Push(ResultSuccess);
2234 rb.PushRaw(version_string);
2235}
2236
2237void IApplicationFunctions::GetDesiredLanguage(HLERequestContext& ctx) {
2238 // TODO(bunnei): This should be configurable
2239 LOG_DEBUG(Service_AM, "called");
2240
2241 // Get supported languages from NACP, if possible
2242 // Default to 0 (all languages supported)
2243 u32 supported_languages = 0;
2244
2245 const auto res = [this] {
2246 const auto title_id = system.GetApplicationProcessProgramID();
2247
2248 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
2249 system.GetContentProvider()};
2250 auto metadata = pm.GetControlMetadata();
2251 if (metadata.first != nullptr) {
2252 return metadata;
2253 }
2254
2255 const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
2256 system.GetFileSystemController(),
2257 system.GetContentProvider()};
2258 return pm_update.GetControlMetadata();
2259 }();
2260
2261 if (res.first != nullptr) {
2262 supported_languages = res.first->GetSupportedLanguages();
2263 }
2264
2265 // Call IApplicationManagerInterface implementation.
2266 auto& service_manager = system.ServiceManager();
2267 auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2");
2268 auto app_man = ns_am2->GetApplicationManagerInterface();
2269
2270 // Get desired application language
2271 u8 desired_language{};
2272 const auto res_lang =
2273 app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages);
2274 if (res_lang != ResultSuccess) {
2275 IPC::ResponseBuilder rb{ctx, 2};
2276 rb.Push(res_lang);
2277 return;
2278 }
2279
2280 // Convert to settings language code.
2281 u64 language_code{};
2282 const auto res_code =
2283 app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language);
2284 if (res_code != ResultSuccess) {
2285 IPC::ResponseBuilder rb{ctx, 2};
2286 rb.Push(res_code);
2287 return;
2288 }
2289
2290 LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code);
2291
2292 IPC::ResponseBuilder rb{ctx, 4};
2293 rb.Push(ResultSuccess);
2294 rb.Push(language_code);
2295}
2296
2297void IApplicationFunctions::IsGamePlayRecordingSupported(HLERequestContext& ctx) {
2298 LOG_WARNING(Service_AM, "(STUBBED) called");
2299
2300 constexpr bool gameplay_recording_supported = false;
2301
2302 IPC::ResponseBuilder rb{ctx, 3};
2303 rb.Push(ResultSuccess);
2304 rb.Push(gameplay_recording_supported);
2305}
2306
2307void IApplicationFunctions::InitializeGamePlayRecording(HLERequestContext& ctx) {
2308 LOG_WARNING(Service_AM, "(STUBBED) called");
2309
2310 IPC::ResponseBuilder rb{ctx, 2};
2311 rb.Push(ResultSuccess);
2312}
2313
2314void IApplicationFunctions::SetGamePlayRecordingState(HLERequestContext& ctx) {
2315 LOG_WARNING(Service_AM, "(STUBBED) called");
2316
2317 IPC::ResponseBuilder rb{ctx, 2};
2318 rb.Push(ResultSuccess);
2319}
2320
2321void IApplicationFunctions::NotifyRunning(HLERequestContext& ctx) {
2322 LOG_WARNING(Service_AM, "(STUBBED) called");
2323
2324 IPC::ResponseBuilder rb{ctx, 3};
2325 rb.Push(ResultSuccess);
2326 rb.Push<u8>(0); // Unknown, seems to be ignored by official processes
2327}
2328
2329void IApplicationFunctions::GetPseudoDeviceId(HLERequestContext& ctx) {
2330 LOG_WARNING(Service_AM, "(STUBBED) called");
2331
2332 IPC::ResponseBuilder rb{ctx, 6};
2333 rb.Push(ResultSuccess);
2334
2335 // Returns a 128-bit UUID
2336 rb.Push<u64>(0);
2337 rb.Push<u64>(0);
2338}
2339
2340void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) {
2341 struct Parameters {
2342 FileSys::SaveDataType type;
2343 u128 user_id;
2344 u64 new_normal_size;
2345 u64 new_journal_size;
2346 };
2347 static_assert(sizeof(Parameters) == 40);
2348
2349 IPC::RequestParser rp{ctx};
2350 const auto [type, user_id, new_normal_size, new_journal_size] = rp.PopRaw<Parameters>();
2351
2352 LOG_DEBUG(Service_AM,
2353 "called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, "
2354 "new_journal={:016X}",
2355 static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
2356
2357 system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
2358 type, system.GetApplicationProcessProgramID(), user_id,
2359 {new_normal_size, new_journal_size});
2360
2361 IPC::ResponseBuilder rb{ctx, 4};
2362 rb.Push(ResultSuccess);
2363
2364 // The following value is used upon failure to help the system recover.
2365 // Since we always succeed, this should be 0.
2366 rb.Push<u64>(0);
2367}
2368
2369void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) {
2370 struct Parameters {
2371 FileSys::SaveDataType type;
2372 u128 user_id;
2373 };
2374 static_assert(sizeof(Parameters) == 24);
2375
2376 IPC::RequestParser rp{ctx};
2377 const auto [type, user_id] = rp.PopRaw<Parameters>();
2378
2379 LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1],
2380 user_id[0]);
2381
2382 const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
2383 type, system.GetApplicationProcessProgramID(), user_id);
2384
2385 IPC::ResponseBuilder rb{ctx, 6};
2386 rb.Push(ResultSuccess);
2387 rb.Push(size.normal);
2388 rb.Push(size.journal);
2389}
2390
2391void IApplicationFunctions::CreateCacheStorage(HLERequestContext& ctx) {
2392 struct InputParameters {
2393 u16 index;
2394 s64 size;
2395 s64 journal_size;
2396 };
2397 static_assert(sizeof(InputParameters) == 24);
2398
2399 struct OutputParameters {
2400 u32 storage_target;
2401 u64 required_size;
2402 };
2403 static_assert(sizeof(OutputParameters) == 16);
2404
2405 IPC::RequestParser rp{ctx};
2406 const auto params = rp.PopRaw<InputParameters>();
2407
2408 LOG_WARNING(Service_AM, "(STUBBED) called with index={}, size={:#x}, journal_size={:#x}",
2409 params.index, params.size, params.journal_size);
2410
2411 const OutputParameters resp{
2412 .storage_target = 1,
2413 .required_size = 0,
2414 };
2415
2416 IPC::ResponseBuilder rb{ctx, 6};
2417 rb.Push(ResultSuccess);
2418 rb.PushRaw(resp);
2419}
2420
2421void IApplicationFunctions::GetSaveDataSizeMax(HLERequestContext& ctx) {
2422 LOG_WARNING(Service_AM, "(STUBBED) called");
2423
2424 constexpr u64 size_max_normal = 0xFFFFFFF;
2425 constexpr u64 size_max_journal = 0xFFFFFFF;
2426
2427 IPC::ResponseBuilder rb{ctx, 6};
2428 rb.Push(ResultSuccess);
2429 rb.Push(size_max_normal);
2430 rb.Push(size_max_journal);
2431}
2432
2433void IApplicationFunctions::QueryApplicationPlayStatistics(HLERequestContext& ctx) {
2434 LOG_WARNING(Service_AM, "(STUBBED) called");
2435
2436 IPC::ResponseBuilder rb{ctx, 3};
2437 rb.Push(ResultSuccess);
2438 rb.Push<u32>(0);
2439}
2440
2441void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx) {
2442 LOG_WARNING(Service_AM, "(STUBBED) called");
2443
2444 IPC::ResponseBuilder rb{ctx, 3};
2445 rb.Push(ResultSuccess);
2446 rb.Push<u32>(0);
2447}
2448
2449void IApplicationFunctions::ExecuteProgram(HLERequestContext& ctx) {
2450 LOG_WARNING(Service_AM, "(STUBBED) called");
2451
2452 IPC::RequestParser rp{ctx};
2453 [[maybe_unused]] const auto unk_1 = rp.Pop<u32>();
2454 [[maybe_unused]] const auto unk_2 = rp.Pop<u32>();
2455 const auto program_index = rp.Pop<u64>();
2456
2457 IPC::ResponseBuilder rb{ctx, 2};
2458 rb.Push(ResultSuccess);
2459
2460 system.ExecuteProgram(program_index);
2461}
2462
2463void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) {
2464 LOG_DEBUG(Service_AM, "called");
2465
2466 system.GetUserChannel().clear();
2467
2468 IPC::ResponseBuilder rb{ctx, 2};
2469 rb.Push(ResultSuccess);
2470}
2471
2472void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) {
2473 LOG_DEBUG(Service_AM, "called");
2474
2475 IPC::RequestParser rp{ctx};
2476 const auto storage = rp.PopIpcInterface<IStorage>().lock();
2477 if (storage) {
2478 system.GetUserChannel().push_back(storage->GetData());
2479 }
2480
2481 IPC::ResponseBuilder rb{ctx, 2};
2482 rb.Push(ResultSuccess);
2483}
2484
2485void IApplicationFunctions::GetPreviousProgramIndex(HLERequestContext& ctx) {
2486 LOG_WARNING(Service_AM, "(STUBBED) called");
2487
2488 IPC::ResponseBuilder rb{ctx, 3};
2489 rb.Push(ResultSuccess);
2490 rb.Push<s32>(previous_program_index);
2491}
2492
2493void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx) {
2494 LOG_WARNING(Service_AM, "(STUBBED) called");
2495
2496 IPC::ResponseBuilder rb{ctx, 2, 1};
2497 rb.Push(ResultSuccess);
2498 rb.PushCopyObjects(gpu_error_detected_event->GetReadableEvent());
2499}
2500
2501void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx) {
2502 LOG_DEBUG(Service_AM, "called");
2503
2504 IPC::ResponseBuilder rb{ctx, 2, 1};
2505 rb.Push(ResultSuccess);
2506 rb.PushCopyObjects(friend_invitation_storage_channel_event->GetReadableEvent());
2507}
2508
2509void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx) {
2510 LOG_DEBUG(Service_AM, "(STUBBED) called");
2511
2512 IPC::ResponseBuilder rb{ctx, 2};
2513 rb.Push(AM::ResultNoDataInChannel);
2514}
2515
2516void IApplicationFunctions::GetNotificationStorageChannelEvent(HLERequestContext& ctx) {
2517 LOG_DEBUG(Service_AM, "called");
2518
2519 IPC::ResponseBuilder rb{ctx, 2, 1};
2520 rb.Push(ResultSuccess);
2521 rb.PushCopyObjects(notification_storage_channel_event->GetReadableEvent());
2522}
2523
2524void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx) {
2525 LOG_DEBUG(Service_AM, "called");
2526
2527 IPC::ResponseBuilder rb{ctx, 2, 1};
2528 rb.Push(ResultSuccess);
2529 rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent());
2530}
2531
2532void IApplicationFunctions::PrepareForJit(HLERequestContext& ctx) {
2533 LOG_WARNING(Service_AM, "(STUBBED) called");
2534
2535 IPC::ResponseBuilder rb{ctx, 2};
2536 rb.Push(ResultSuccess);
2537}
2538
2539void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { 14void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) {
2540 auto message_queue = std::make_shared<AppletMessageQueue>(system);
2541 auto server_manager = std::make_unique<ServerManager>(system); 15 auto server_manager = std::make_unique<ServerManager>(system);
2542 16
2543 server_manager->RegisterNamedService( 17 server_manager->RegisterNamedService("appletAE",
2544 "appletAE", std::make_shared<AppletAE>(nvnflinger, message_queue, system)); 18 std::make_shared<AppletAE>(nvnflinger, system));
2545 server_manager->RegisterNamedService( 19 server_manager->RegisterNamedService("appletOE",
2546 "appletOE", std::make_shared<AppletOE>(nvnflinger, message_queue, system)); 20 std::make_shared<AppletOE>(nvnflinger, system));
2547 server_manager->RegisterNamedService("idle:sys", std::make_shared<IdleSys>(system)); 21 server_manager->RegisterNamedService("idle:sys", std::make_shared<IdleSys>(system));
2548 server_manager->RegisterNamedService("omm", std::make_shared<OMM>(system)); 22 server_manager->RegisterNamedService("omm", std::make_shared<OMM>(system));
2549 server_manager->RegisterNamedService("spsm", std::make_shared<SPSM>(system)); 23 server_manager->RegisterNamedService("spsm", std::make_shared<SPSM>(system));
2550 ServerManager::RunServer(std::move(server_manager)); 24 ServerManager::RunServer(std::move(server_manager));
2551} 25}
2552 26
2553IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_)
2554 : ServiceFramework{system_, "IHomeMenuFunctions"}, service_context{system,
2555 "IHomeMenuFunctions"} {
2556 // clang-format off
2557 static const FunctionInfo functions[] = {
2558 {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
2559 {11, nullptr, "LockForeground"},
2560 {12, nullptr, "UnlockForeground"},
2561 {20, nullptr, "PopFromGeneralChannel"},
2562 {21, &IHomeMenuFunctions::GetPopFromGeneralChannelEvent, "GetPopFromGeneralChannelEvent"},
2563 {30, nullptr, "GetHomeButtonWriterLockAccessor"},
2564 {31, nullptr, "GetWriterLockAccessorEx"},
2565 {40, nullptr, "IsSleepEnabled"},
2566 {41, nullptr, "IsRebootEnabled"},
2567 {50, nullptr, "LaunchSystemApplet"},
2568 {51, nullptr, "LaunchStarter"},
2569 {100, nullptr, "PopRequestLaunchApplicationForDebug"},
2570 {110, nullptr, "IsForceTerminateApplicationDisabledForDebug"},
2571 {200, nullptr, "LaunchDevMenu"},
2572 {1000, nullptr, "SetLastApplicationExitReason"},
2573 };
2574 // clang-format on
2575
2576 RegisterHandlers(functions);
2577
2578 pop_from_general_channel_event =
2579 service_context.CreateEvent("IHomeMenuFunctions:PopFromGeneralChannelEvent");
2580}
2581
2582IHomeMenuFunctions::~IHomeMenuFunctions() {
2583 service_context.CloseEvent(pop_from_general_channel_event);
2584}
2585
2586void IHomeMenuFunctions::RequestToGetForeground(HLERequestContext& ctx) {
2587 LOG_WARNING(Service_AM, "(STUBBED) called");
2588
2589 IPC::ResponseBuilder rb{ctx, 2};
2590 rb.Push(ResultSuccess);
2591}
2592
2593void IHomeMenuFunctions::GetPopFromGeneralChannelEvent(HLERequestContext& ctx) {
2594 LOG_WARNING(Service_AM, "(STUBBED) called");
2595
2596 IPC::ResponseBuilder rb{ctx, 2, 1};
2597 rb.Push(ResultSuccess);
2598 rb.PushCopyObjects(pop_from_general_channel_event->GetReadableEvent());
2599}
2600
2601IGlobalStateController::IGlobalStateController(Core::System& system_)
2602 : ServiceFramework{system_, "IGlobalStateController"} {
2603 // clang-format off
2604 static const FunctionInfo functions[] = {
2605 {0, nullptr, "RequestToEnterSleep"},
2606 {1, nullptr, "EnterSleep"},
2607 {2, nullptr, "StartSleepSequence"},
2608 {3, nullptr, "StartShutdownSequence"},
2609 {4, nullptr, "StartRebootSequence"},
2610 {9, nullptr, "IsAutoPowerDownRequested"},
2611 {10, nullptr, "LoadAndApplyIdlePolicySettings"},
2612 {11, nullptr, "NotifyCecSettingsChanged"},
2613 {12, nullptr, "SetDefaultHomeButtonLongPressTime"},
2614 {13, nullptr, "UpdateDefaultDisplayResolution"},
2615 {14, nullptr, "ShouldSleepOnBoot"},
2616 {15, nullptr, "GetHdcpAuthenticationFailedEvent"},
2617 {30, nullptr, "OpenCradleFirmwareUpdater"},
2618 };
2619 // clang-format on
2620
2621 RegisterHandlers(functions);
2622}
2623
2624IGlobalStateController::~IGlobalStateController() = default;
2625
2626IApplicationCreator::IApplicationCreator(Core::System& system_)
2627 : ServiceFramework{system_, "IApplicationCreator"} {
2628 // clang-format off
2629 static const FunctionInfo functions[] = {
2630 {0, nullptr, "CreateApplication"},
2631 {1, nullptr, "PopLaunchRequestedApplication"},
2632 {10, nullptr, "CreateSystemApplication"},
2633 {100, nullptr, "PopFloatingApplicationForDevelopment"},
2634 };
2635 // clang-format on
2636
2637 RegisterHandlers(functions);
2638}
2639
2640IApplicationCreator::~IApplicationCreator() = default;
2641
2642IProcessWindingController::IProcessWindingController(Core::System& system_)
2643 : ServiceFramework{system_, "IProcessWindingController"} {
2644 // clang-format off
2645 static const FunctionInfo functions[] = {
2646 {0, &IProcessWindingController::GetLaunchReason, "GetLaunchReason"},
2647 {11, &IProcessWindingController::OpenCallingLibraryApplet, "OpenCallingLibraryApplet"},
2648 {21, nullptr, "PushContext"},
2649 {22, nullptr, "PopContext"},
2650 {23, nullptr, "CancelWindingReservation"},
2651 {30, nullptr, "WindAndDoReserved"},
2652 {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
2653 {41, nullptr, "ReserveToStartAndWait"},
2654 };
2655 // clang-format on
2656
2657 RegisterHandlers(functions);
2658}
2659
2660IProcessWindingController::~IProcessWindingController() = default;
2661
2662void IProcessWindingController::GetLaunchReason(HLERequestContext& ctx) {
2663 LOG_WARNING(Service_AM, "(STUBBED) called");
2664
2665 struct AppletProcessLaunchReason {
2666 u8 flag;
2667 INSERT_PADDING_BYTES(3);
2668 };
2669 static_assert(sizeof(AppletProcessLaunchReason) == 0x4,
2670 "AppletProcessLaunchReason is an invalid size");
2671
2672 AppletProcessLaunchReason reason{
2673 .flag = 0,
2674 };
2675
2676 IPC::ResponseBuilder rb{ctx, 3};
2677 rb.Push(ResultSuccess);
2678 rb.PushRaw(reason);
2679}
2680
2681void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) {
2682 const auto applet_id = system.GetAppletManager().GetCurrentAppletId();
2683 const auto applet_mode = Applets::LibraryAppletMode::AllForeground;
2684
2685 LOG_WARNING(Service_AM, "(STUBBED) called with applet_id={:08X}, applet_mode={:08X}", applet_id,
2686 applet_mode);
2687
2688 const auto& applet_manager{system.GetAppletManager()};
2689 const auto applet = applet_manager.GetApplet(applet_id, applet_mode);
2690
2691 if (applet == nullptr) {
2692 LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
2693
2694 IPC::ResponseBuilder rb{ctx, 2};
2695 rb.Push(ResultUnknown);
2696 return;
2697 }
2698
2699 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
2700 rb.Push(ResultSuccess);
2701 rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet);
2702}
2703
2704} // namespace Service::AM 27} // namespace Service::AM
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 905a71b9f..4a2d797bd 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -1,20 +1,11 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include <chrono> 6namespace Core {
7#include <memory> 7class System;
8#include <queue> 8}
9
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/service.h"
12
13namespace Kernel {
14class KernelCore;
15class KReadableEvent;
16class KTransferMemory;
17} // namespace Kernel
18 9
19namespace Service::Nvnflinger { 10namespace Service::Nvnflinger {
20class Nvnflinger; 11class Nvnflinger;
@@ -22,443 +13,6 @@ class Nvnflinger;
22 13
23namespace Service::AM { 14namespace Service::AM {
24 15
25class AppletMessageQueue {
26public:
27 // This is nn::am::AppletMessage
28 enum class AppletMessage : u32 {
29 None = 0,
30 ChangeIntoForeground = 1,
31 ChangeIntoBackground = 2,
32 Exit = 4,
33 ApplicationExited = 6,
34 FocusStateChanged = 15,
35 Resume = 16,
36 DetectShortPressingHomeButton = 20,
37 DetectLongPressingHomeButton = 21,
38 DetectShortPressingPowerButton = 22,
39 DetectMiddlePressingPowerButton = 23,
40 DetectLongPressingPowerButton = 24,
41 RequestToPrepareSleep = 25,
42 FinishedSleepSequence = 26,
43 SleepRequiredByHighTemperature = 27,
44 SleepRequiredByLowBattery = 28,
45 AutoPowerDown = 29,
46 OperationModeChanged = 30,
47 PerformanceModeChanged = 31,
48 DetectReceivingCecSystemStandby = 32,
49 SdCardRemoved = 33,
50 LaunchApplicationRequested = 50,
51 RequestToDisplay = 51,
52 ShowApplicationLogo = 55,
53 HideApplicationLogo = 56,
54 ForceHideApplicationLogo = 57,
55 FloatingApplicationDetected = 60,
56 DetectShortPressingCaptureButton = 90,
57 AlbumScreenShotTaken = 92,
58 AlbumRecordingSaved = 93,
59 };
60
61 explicit AppletMessageQueue(Core::System& system);
62 ~AppletMessageQueue();
63
64 Kernel::KReadableEvent& GetMessageReceiveEvent();
65 Kernel::KReadableEvent& GetOperationModeChangedEvent();
66 void PushMessage(AppletMessage msg);
67 AppletMessage PopMessage();
68 std::size_t GetMessageCount() const;
69 void RequestExit();
70 void RequestResume();
71 void FocusStateChanged();
72 void OperationModeChanged();
73
74private:
75 KernelHelpers::ServiceContext service_context;
76
77 Kernel::KEvent* on_new_message;
78 Kernel::KEvent* on_operation_mode_changed;
79
80 std::queue<AppletMessage> messages;
81};
82
83class IWindowController final : public ServiceFramework<IWindowController> {
84public:
85 explicit IWindowController(Core::System& system_);
86 ~IWindowController() override;
87
88private:
89 void GetAppletResourceUserId(HLERequestContext& ctx);
90 void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx);
91 void AcquireForegroundRights(HLERequestContext& ctx);
92};
93
94class IAudioController final : public ServiceFramework<IAudioController> {
95public:
96 explicit IAudioController(Core::System& system_);
97 ~IAudioController() override;
98
99private:
100 void SetExpectedMasterVolume(HLERequestContext& ctx);
101 void GetMainAppletExpectedMasterVolume(HLERequestContext& ctx);
102 void GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx);
103 void ChangeMainAppletMasterVolume(HLERequestContext& ctx);
104 void SetTransparentAudioRate(HLERequestContext& ctx);
105
106 static constexpr float min_allowed_volume = 0.0f;
107 static constexpr float max_allowed_volume = 1.0f;
108
109 float main_applet_volume{0.25f};
110 float library_applet_volume{max_allowed_volume};
111 float transparent_volume_rate{min_allowed_volume};
112
113 // Volume transition fade time in nanoseconds.
114 // e.g. If the main applet volume was 0% and was changed to 50%
115 // with a fade of 50ns, then over the course of 50ns,
116 // the volume will gradually fade up to 50%
117 std::chrono::nanoseconds fade_time_ns{0};
118};
119
120class IDisplayController final : public ServiceFramework<IDisplayController> {
121public:
122 explicit IDisplayController(Core::System& system_);
123 ~IDisplayController() override;
124
125private:
126 void GetCallerAppletCaptureImageEx(HLERequestContext& ctx);
127 void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
128 void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
129 void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
130 void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
131 void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
132};
133
134class IDebugFunctions final : public ServiceFramework<IDebugFunctions> {
135public:
136 explicit IDebugFunctions(Core::System& system_);
137 ~IDebugFunctions() override;
138};
139
140class ISelfController final : public ServiceFramework<ISelfController> {
141public:
142 explicit ISelfController(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_);
143 ~ISelfController() override;
144
145private:
146 void Exit(HLERequestContext& ctx);
147 void LockExit(HLERequestContext& ctx);
148 void UnlockExit(HLERequestContext& ctx);
149 void EnterFatalSection(HLERequestContext& ctx);
150 void LeaveFatalSection(HLERequestContext& ctx);
151 void GetLibraryAppletLaunchableEvent(HLERequestContext& ctx);
152 void SetScreenShotPermission(HLERequestContext& ctx);
153 void SetOperationModeChangedNotification(HLERequestContext& ctx);
154 void SetPerformanceModeChangedNotification(HLERequestContext& ctx);
155 void SetFocusHandlingMode(HLERequestContext& ctx);
156 void SetRestartMessageEnabled(HLERequestContext& ctx);
157 void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx);
158 void SetAlbumImageOrientation(HLERequestContext& ctx);
159 void IsSystemBufferSharingEnabled(HLERequestContext& ctx);
160 void GetSystemSharedBufferHandle(HLERequestContext& ctx);
161 void GetSystemSharedLayerHandle(HLERequestContext& ctx);
162 void CreateManagedDisplayLayer(HLERequestContext& ctx);
163 void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
164 void SetHandlesRequestToDisplay(HLERequestContext& ctx);
165 void ApproveToDisplay(HLERequestContext& ctx);
166 void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
167 void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
168 void ReportUserIsActive(HLERequestContext& ctx);
169 void SetAutoSleepDisabled(HLERequestContext& ctx);
170 void IsAutoSleepDisabled(HLERequestContext& ctx);
171 void GetAccumulatedSuspendedTickValue(HLERequestContext& ctx);
172 void GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx);
173 void SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx);
174 void SaveCurrentScreenshot(HLERequestContext& ctx);
175 void SetRecordVolumeMuted(HLERequestContext& ctx);
176
177 Result EnsureBufferSharingEnabled();
178
179 enum class ScreenshotPermission : u32 {
180 Inherit = 0,
181 Enable = 1,
182 Disable = 2,
183 };
184
185 Nvnflinger::Nvnflinger& nvnflinger;
186
187 KernelHelpers::ServiceContext service_context;
188
189 Kernel::KEvent* launchable_event;
190 Kernel::KEvent* accumulated_suspended_tick_changed_event;
191
192 u32 idle_time_detection_extension = 0;
193 u64 num_fatal_sections_entered = 0;
194 u64 system_shared_buffer_id = 0;
195 u64 system_shared_layer_id = 0;
196 bool is_auto_sleep_disabled = false;
197 bool buffer_sharing_enabled = false;
198 ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
199};
200
201class ILockAccessor final : public ServiceFramework<ILockAccessor> {
202public:
203 explicit ILockAccessor(Core::System& system_);
204 ~ILockAccessor() override;
205
206private:
207 void TryLock(HLERequestContext& ctx);
208 void Unlock(HLERequestContext& ctx);
209 void GetEvent(HLERequestContext& ctx);
210 void IsLocked(HLERequestContext& ctx);
211
212 bool is_locked{};
213
214 Kernel::KEvent* lock_event;
215 KernelHelpers::ServiceContext service_context;
216};
217
218class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
219public:
220 explicit ICommonStateGetter(Core::System& system_,
221 std::shared_ptr<AppletMessageQueue> msg_queue_);
222 ~ICommonStateGetter() override;
223
224private:
225 // This is nn::oe::FocusState
226 enum class FocusState : u8 {
227 InFocus = 1,
228 NotInFocus = 2,
229 Background = 3,
230 };
231
232 // This is nn::oe::OperationMode
233 enum class OperationMode : u8 {
234 Handheld = 0,
235 Docked = 1,
236 };
237
238 // This is nn::am::service::SystemButtonType
239 enum class SystemButtonType {
240 None,
241 HomeButtonShortPressing,
242 HomeButtonLongPressing,
243 PowerButtonShortPressing,
244 PowerButtonLongPressing,
245 ShutdownSystem,
246 CaptureButtonShortPressing,
247 CaptureButtonLongPressing,
248 };
249
250 enum class SysPlatformRegion : s32 {
251 Global = 1,
252 Terra = 2,
253 };
254
255 void GetEventHandle(HLERequestContext& ctx);
256 void ReceiveMessage(HLERequestContext& ctx);
257 void GetCurrentFocusState(HLERequestContext& ctx);
258 void RequestToAcquireSleepLock(HLERequestContext& ctx);
259 void GetAcquiredSleepLockEvent(HLERequestContext& ctx);
260 void GetReaderLockAccessorEx(HLERequestContext& ctx);
261 void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx);
262 void GetOperationMode(HLERequestContext& ctx);
263 void GetPerformanceMode(HLERequestContext& ctx);
264 void GetBootMode(HLERequestContext& ctx);
265 void IsVrModeEnabled(HLERequestContext& ctx);
266 void SetVrModeEnabled(HLERequestContext& ctx);
267 void SetLcdBacklighOffEnabled(HLERequestContext& ctx);
268 void BeginVrModeEx(HLERequestContext& ctx);
269 void EndVrModeEx(HLERequestContext& ctx);
270 void GetDefaultDisplayResolution(HLERequestContext& ctx);
271 void SetCpuBoostMode(HLERequestContext& ctx);
272 void GetBuiltInDisplayType(HLERequestContext& ctx);
273 void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx);
274 void GetSettingsPlatformRegion(HLERequestContext& ctx);
275 void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx);
276
277 std::shared_ptr<AppletMessageQueue> msg_queue;
278 bool vr_mode_state{};
279 Kernel::KEvent* sleep_lock_event;
280 KernelHelpers::ServiceContext service_context;
281};
282
283class IStorageImpl {
284public:
285 virtual ~IStorageImpl();
286 virtual std::vector<u8>& GetData() = 0;
287 virtual const std::vector<u8>& GetData() const = 0;
288 virtual std::size_t GetSize() const = 0;
289};
290
291class IStorage final : public ServiceFramework<IStorage> {
292public:
293 explicit IStorage(Core::System& system_, std::vector<u8>&& buffer);
294 ~IStorage() override;
295
296 std::vector<u8>& GetData() {
297 return impl->GetData();
298 }
299
300 const std::vector<u8>& GetData() const {
301 return impl->GetData();
302 }
303
304 std::size_t GetSize() const {
305 return impl->GetSize();
306 }
307
308private:
309 void Register();
310 void Open(HLERequestContext& ctx);
311
312 std::shared_ptr<IStorageImpl> impl;
313};
314
315class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
316public:
317 explicit IStorageAccessor(Core::System& system_, IStorage& backing_);
318 ~IStorageAccessor() override;
319
320private:
321 void GetSize(HLERequestContext& ctx);
322 void Write(HLERequestContext& ctx);
323 void Read(HLERequestContext& ctx);
324
325 IStorage& backing;
326};
327
328class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
329public:
330 explicit ILibraryAppletCreator(Core::System& system_);
331 ~ILibraryAppletCreator() override;
332
333private:
334 void CreateLibraryApplet(HLERequestContext& ctx);
335 void CreateStorage(HLERequestContext& ctx);
336 void CreateTransferMemoryStorage(HLERequestContext& ctx);
337 void CreateHandleStorage(HLERequestContext& ctx);
338};
339
340class ILibraryAppletSelfAccessor final : public ServiceFramework<ILibraryAppletSelfAccessor> {
341public:
342 explicit ILibraryAppletSelfAccessor(Core::System& system_);
343 ~ILibraryAppletSelfAccessor() override;
344
345private:
346 void PopInData(HLERequestContext& ctx);
347 void PushOutData(HLERequestContext& ctx);
348 void GetLibraryAppletInfo(HLERequestContext& ctx);
349 void GetMainAppletIdentityInfo(HLERequestContext& ctx);
350 void ExitProcessAndReturn(HLERequestContext& ctx);
351 void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
352 void GetDesirableKeyboardLayout(HLERequestContext& ctx);
353 void GetMainAppletAvailableUsers(HLERequestContext& ctx);
354 void ShouldSetGpuTimeSliceManually(HLERequestContext& ctx);
355
356 void PushInShowAlbum();
357 void PushInShowCabinetData();
358 void PushInShowMiiEditData();
359 void PushInShowSoftwareKeyboard();
360 void PushInShowController();
361
362 std::deque<std::vector<u8>> queue_data;
363};
364
365class IAppletCommonFunctions final : public ServiceFramework<IAppletCommonFunctions> {
366public:
367 explicit IAppletCommonFunctions(Core::System& system_);
368 ~IAppletCommonFunctions() override;
369
370private:
371 void SetCpuBoostRequestPriority(HLERequestContext& ctx);
372};
373
374class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
375public:
376 explicit IApplicationFunctions(Core::System& system_);
377 ~IApplicationFunctions() override;
378
379private:
380 void PopLaunchParameter(HLERequestContext& ctx);
381 void CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx);
382 void EnsureSaveData(HLERequestContext& ctx);
383 void SetTerminateResult(HLERequestContext& ctx);
384 void GetDisplayVersion(HLERequestContext& ctx);
385 void GetDesiredLanguage(HLERequestContext& ctx);
386 void IsGamePlayRecordingSupported(HLERequestContext& ctx);
387 void InitializeGamePlayRecording(HLERequestContext& ctx);
388 void SetGamePlayRecordingState(HLERequestContext& ctx);
389 void NotifyRunning(HLERequestContext& ctx);
390 void GetPseudoDeviceId(HLERequestContext& ctx);
391 void ExtendSaveData(HLERequestContext& ctx);
392 void GetSaveDataSize(HLERequestContext& ctx);
393 void CreateCacheStorage(HLERequestContext& ctx);
394 void GetSaveDataSizeMax(HLERequestContext& ctx);
395 void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
396 void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
397 void BeginBlockingHomeButton(HLERequestContext& ctx);
398 void EndBlockingHomeButton(HLERequestContext& ctx);
399 void EnableApplicationCrashReport(HLERequestContext& ctx);
400 void InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx);
401 void SetApplicationCopyrightImage(HLERequestContext& ctx);
402 void SetApplicationCopyrightVisibility(HLERequestContext& ctx);
403 void QueryApplicationPlayStatistics(HLERequestContext& ctx);
404 void QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx);
405 void ExecuteProgram(HLERequestContext& ctx);
406 void ClearUserChannel(HLERequestContext& ctx);
407 void UnpopToUserChannel(HLERequestContext& ctx);
408 void GetPreviousProgramIndex(HLERequestContext& ctx);
409 void GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx);
410 void GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx);
411 void TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx);
412 void GetNotificationStorageChannelEvent(HLERequestContext& ctx);
413 void GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx);
414 void PrepareForJit(HLERequestContext& ctx);
415
416 KernelHelpers::ServiceContext service_context;
417
418 bool launch_popped_account_preselect = false;
419 s32 previous_program_index{-1};
420 Kernel::KEvent* gpu_error_detected_event;
421 Kernel::KEvent* friend_invitation_storage_channel_event;
422 Kernel::KEvent* notification_storage_channel_event;
423 Kernel::KEvent* health_warning_disappeared_system_event;
424};
425
426class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
427public:
428 explicit IHomeMenuFunctions(Core::System& system_);
429 ~IHomeMenuFunctions() override;
430
431private:
432 void RequestToGetForeground(HLERequestContext& ctx);
433 void GetPopFromGeneralChannelEvent(HLERequestContext& ctx);
434
435 KernelHelpers::ServiceContext service_context;
436
437 Kernel::KEvent* pop_from_general_channel_event;
438};
439
440class IGlobalStateController final : public ServiceFramework<IGlobalStateController> {
441public:
442 explicit IGlobalStateController(Core::System& system_);
443 ~IGlobalStateController() override;
444};
445
446class IApplicationCreator final : public ServiceFramework<IApplicationCreator> {
447public:
448 explicit IApplicationCreator(Core::System& system_);
449 ~IApplicationCreator() override;
450};
451
452class IProcessWindingController final : public ServiceFramework<IProcessWindingController> {
453public:
454 explicit IProcessWindingController(Core::System& system_);
455 ~IProcessWindingController() override;
456
457private:
458 void GetLaunchReason(HLERequestContext& ctx);
459 void OpenCallingLibraryApplet(HLERequestContext& ctx);
460};
461
462void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system); 16void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system);
463 17
464} // namespace Service::AM 18} // namespace Service::AM
diff --git a/src/core/hle/service/am/am_results.h b/src/core/hle/service/am/am_results.h
new file mode 100644
index 000000000..a2afc9eec
--- /dev/null
+++ b/src/core/hle/service/am/am_results.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::AM {
9
10constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
11constexpr Result ResultNoMessages{ErrorModule::AM, 3};
12constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
13constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511};
14constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512};
15
16} // namespace Service::AM
diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h
new file mode 100644
index 000000000..a2b852b12
--- /dev/null
+++ b/src/core/hle/service/am/am_types.h
@@ -0,0 +1,178 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace Service::AM {
10
11namespace Frontend {
12class FrontendApplet;
13}
14
15enum class AppletType {
16 Application,
17 LibraryApplet,
18 SystemApplet,
19};
20
21enum class GameplayRecordingState : u32 {
22 Disabled,
23 Enabled,
24};
25
26// This is nn::oe::FocusState
27enum class FocusState : u8 {
28 InFocus = 1,
29 NotInFocus = 2,
30 Background = 3,
31};
32
33// This is nn::oe::OperationMode
34enum class OperationMode : u8 {
35 Handheld = 0,
36 Docked = 1,
37};
38
39// This is nn::am::service::SystemButtonType
40enum class SystemButtonType {
41 None,
42 HomeButtonShortPressing,
43 HomeButtonLongPressing,
44 PowerButtonShortPressing,
45 PowerButtonLongPressing,
46 ShutdownSystem,
47 CaptureButtonShortPressing,
48 CaptureButtonLongPressing,
49};
50
51enum class SysPlatformRegion : s32 {
52 Global = 1,
53 Terra = 2,
54};
55
56struct AppletProcessLaunchReason {
57 u8 flag;
58 INSERT_PADDING_BYTES(3);
59};
60static_assert(sizeof(AppletProcessLaunchReason) == 0x4,
61 "AppletProcessLaunchReason is an invalid size");
62
63enum class ScreenshotPermission : u32 {
64 Inherit = 0,
65 Enable = 1,
66 Disable = 2,
67};
68
69struct FocusHandlingMode {
70 bool unknown0;
71 bool unknown1;
72 bool unknown2;
73 bool unknown3;
74};
75
76enum class IdleTimeDetectionExtension : u32 {
77 Disabled = 0,
78 Extended = 1,
79 ExtendedUnsafe = 2,
80};
81
82enum class AppletId : u32 {
83 None = 0x00,
84 Application = 0x01,
85 OverlayDisplay = 0x02,
86 QLaunch = 0x03,
87 Starter = 0x04,
88 Auth = 0x0A,
89 Cabinet = 0x0B,
90 Controller = 0x0C,
91 DataErase = 0x0D,
92 Error = 0x0E,
93 NetConnect = 0x0F,
94 ProfileSelect = 0x10,
95 SoftwareKeyboard = 0x11,
96 MiiEdit = 0x12,
97 Web = 0x13,
98 Shop = 0x14,
99 PhotoViewer = 0x15,
100 Settings = 0x16,
101 OfflineWeb = 0x17,
102 LoginShare = 0x18,
103 WebAuth = 0x19,
104 MyPage = 0x1A,
105};
106
107enum class AppletProgramId : u64 {
108 QLaunch = 0x0100000000001000ull,
109 Auth = 0x0100000000001001ull,
110 Cabinet = 0x0100000000001002ull,
111 Controller = 0x0100000000001003ull,
112 DataErase = 0x0100000000001004ull,
113 Error = 0x0100000000001005ull,
114 NetConnect = 0x0100000000001006ull,
115 ProfileSelect = 0x0100000000001007ull,
116 SoftwareKeyboard = 0x0100000000001008ull,
117 MiiEdit = 0x0100000000001009ull,
118 Web = 0x010000000000100Aull,
119 Shop = 0x010000000000100Bull,
120 OverlayDisplay = 0x010000000000100Cull,
121 PhotoViewer = 0x010000000000100Dull,
122 Settings = 0x010000000000100Eull,
123 OfflineWeb = 0x010000000000100Full,
124 LoginShare = 0x0100000000001010ull,
125 WebAuth = 0x0100000000001011ull,
126 Starter = 0x0100000000001012ull,
127 MyPage = 0x0100000000001013ull,
128 MaxProgramId = 0x0100000000001FFFull,
129};
130
131enum class LibraryAppletMode : u32 {
132 AllForeground = 0,
133 Background = 1,
134 NoUI = 2,
135 BackgroundIndirectDisplay = 3,
136 AllForegroundInitiallyHidden = 4,
137};
138
139enum class CommonArgumentVersion : u32 {
140 Version0,
141 Version1,
142 Version2,
143 Version3,
144};
145
146enum class CommonArgumentSize : u32 {
147 Version3 = 0x20,
148};
149
150enum class ThemeColor : u32 {
151 BasicWhite = 0,
152 BasicBlack = 3,
153};
154
155struct CommonArguments {
156 CommonArgumentVersion arguments_version;
157 CommonArgumentSize size;
158 u32 library_version;
159 ThemeColor theme_color;
160 bool play_startup_sound;
161 u64 system_tick;
162};
163static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
164
165struct AppletIdentityInfo {
166 AppletId applet_id;
167 INSERT_PADDING_BYTES(0x4);
168 u64 application_id;
169};
170static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
171
172using AppletResourceUserId = u64;
173using ProgramId = u64;
174
175struct Applet;
176class AppletDataBroker;
177
178} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet.cpp b/src/core/hle/service/am/applet.cpp
new file mode 100644
index 000000000..5b9056c12
--- /dev/null
+++ b/src/core/hle/service/am/applet.cpp
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5
6#include "core/core.h"
7#include "core/hle/service/am/am_results.h"
8#include "core/hle/service/am/applet.h"
9#include "core/hle/service/am/applet_manager.h"
10
11namespace Service::AM {
12
13Applet::Applet(Core::System& system, std::unique_ptr<Process> process_)
14 : context(system, "Applet"), message_queue(system), process(std::move(process_)),
15 hid_registration(system, *process), gpu_error_detected_event(context),
16 friend_invitation_storage_channel_event(context), notification_storage_channel_event(context),
17 health_warning_disappeared_system_event(context), acquired_sleep_lock_event(context),
18 pop_from_general_channel_event(context), library_applet_launchable_event(context),
19 accumulated_suspended_tick_changed_event(context), sleep_lock_event(context) {
20
21 aruid = process->GetProcessId();
22 program_id = process->GetProgramId();
23}
24
25Applet::~Applet() = default;
26
27} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h
new file mode 100644
index 000000000..bce6f9050
--- /dev/null
+++ b/src/core/hle/service/am/applet.h
@@ -0,0 +1,133 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <list>
7#include <mutex>
8
9#include "common/math_util.h"
10#include "core/hle/service/apm/apm_controller.h"
11#include "core/hle/service/caps/caps_types.h"
12#include "core/hle/service/event.h"
13#include "core/hle/service/kernel_helpers.h"
14#include "core/hle/service/service.h"
15
16#include "core/hle/service/am/am_types.h"
17#include "core/hle/service/am/applet_message_queue.h"
18#include "core/hle/service/am/hid_registration.h"
19#include "core/hle/service/am/managed_layer_holder.h"
20#include "core/hle/service/am/process.h"
21#include "core/hle/service/am/storage.h"
22#include "core/hle/service/am/system_buffer_manager.h"
23
24namespace Service::AM {
25
26struct Applet {
27 explicit Applet(Core::System& system, std::unique_ptr<Process> process_);
28 ~Applet();
29
30 // Lock
31 std::mutex lock{};
32
33 // Event creation helper
34 KernelHelpers::ServiceContext context;
35
36 // Applet message queue
37 AppletMessageQueue message_queue;
38
39 // Process
40 std::unique_ptr<Process> process;
41
42 // Creation state
43 AppletId applet_id{};
44 AppletResourceUserId aruid{};
45 AppletProcessLaunchReason launch_reason{};
46 AppletType type{};
47 ProgramId program_id{};
48 LibraryAppletMode library_applet_mode{};
49 s32 previous_program_index{-1};
50 ScreenshotPermission previous_screenshot_permission{ScreenshotPermission::Enable};
51
52 // TODO: some fields above can be AppletIdentityInfo
53 AppletIdentityInfo screen_shot_identity;
54
55 // hid state
56 HidRegistration hid_registration;
57
58 // vi state
59 SystemBufferManager system_buffer_manager{};
60 ManagedLayerHolder managed_layer_holder{};
61
62 // Applet common functions
63 Result terminate_result{};
64 s32 display_logical_width{};
65 s32 display_logical_height{};
66 Common::Rectangle<f32> display_magnification{0, 0, 1, 1};
67 bool home_button_double_click_enabled{};
68 bool home_button_short_pressed_blocked{};
69 bool home_button_long_pressed_blocked{};
70 bool vr_mode_curtain_required{};
71 bool sleep_required_by_high_temperature{};
72 bool sleep_required_by_low_battery{};
73 s32 cpu_boost_request_priority{-1};
74 bool handling_capture_button_short_pressed_message_enabled_for_applet{};
75 bool handling_capture_button_long_pressed_message_enabled_for_applet{};
76 u32 application_core_usage_mode{};
77
78 // Application functions
79 bool gameplay_recording_supported{};
80 GameplayRecordingState gameplay_recording_state{GameplayRecordingState::Disabled};
81 bool jit_service_launched{};
82 bool is_running{};
83 bool application_crash_report_enabled{};
84
85 // Common state
86 FocusState focus_state{};
87 bool sleep_lock_enabled{};
88 bool vr_mode_enabled{};
89 bool lcd_backlight_off_enabled{};
90 APM::CpuBoostMode boost_mode{};
91 bool request_exit_to_library_applet_at_execute_next_program_enabled{};
92
93 // Channels
94 std::deque<std::vector<u8>> user_channel_launch_parameter{};
95 std::deque<std::vector<u8>> preselected_user_launch_parameter{};
96
97 // Caller applet
98 std::weak_ptr<Applet> caller_applet{};
99 std::shared_ptr<AppletDataBroker> caller_applet_broker{};
100
101 // Self state
102 bool exit_locked{};
103 s32 fatal_section_count{};
104 bool operation_mode_changed_notification_enabled{true};
105 bool performance_mode_changed_notification_enabled{true};
106 FocusHandlingMode focus_handling_mode{};
107 bool restart_message_enabled{};
108 bool out_of_focus_suspension_enabled{true};
109 Capture::AlbumImageOrientation album_image_orientation{};
110 bool handles_request_to_display{};
111 ScreenshotPermission screenshot_permission{};
112 IdleTimeDetectionExtension idle_time_detection_extension{};
113 bool auto_sleep_disabled{};
114 u64 suspended_ticks{};
115 bool album_image_taken_notification_enabled{};
116 bool record_volume_muted{};
117
118 // Events
119 Event gpu_error_detected_event;
120 Event friend_invitation_storage_channel_event;
121 Event notification_storage_channel_event;
122 Event health_warning_disappeared_system_event;
123 Event acquired_sleep_lock_event;
124 Event pop_from_general_channel_event;
125 Event library_applet_launchable_event;
126 Event accumulated_suspended_tick_changed_event;
127 Event sleep_lock_event;
128
129 // Frontend state
130 std::shared_ptr<Frontend::FrontendApplet> frontend{};
131};
132
133} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index e30e6478a..1b715dea6 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -1,311 +1,73 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/hle/service/am/am.h"
7#include "core/hle/service/am/applet_ae.h" 4#include "core/hle/service/am/applet_ae.h"
5#include "core/hle/service/am/applet_manager.h"
6#include "core/hle/service/am/library_applet_proxy.h"
7#include "core/hle/service/am/system_applet_proxy.h"
8#include "core/hle/service/ipc_helpers.h" 8#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/nvnflinger/nvnflinger.h"
10 9
11namespace Service::AM { 10namespace Service::AM {
12 11
13class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { 12AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_)
14public: 13 : ServiceFramework{system_, "appletAE"}, nvnflinger{nvnflinger_} {
15 explicit ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, 14 // clang-format off
16 std::shared_ptr<AppletMessageQueue> msg_queue_, 15 static const FunctionInfo functions[] = {
17 Core::System& system_) 16 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
18 : ServiceFramework{system_, "ILibraryAppletProxy"}, 17 {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"},
19 nvnflinger{nvnflinger_}, msg_queue{std::move(msg_queue_)} { 18 {201, &AppletAE::OpenLibraryAppletProxy, "OpenLibraryAppletProxy"},
20 // clang-format off 19 {300, nullptr, "OpenOverlayAppletProxy"},
21 static const FunctionInfo functions[] = { 20 {350, nullptr, "OpenSystemApplicationProxy"},
22 {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 21 {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
23 {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, 22 {410, nullptr, "GetSystemAppletControllerForDebug"},
24 {2, &ILibraryAppletProxy::GetWindowController, "GetWindowController"}, 23 {1000, nullptr, "GetDebugFunctions"},
25 {3, &ILibraryAppletProxy::GetAudioController, "GetAudioController"}, 24 };
26 {4, &ILibraryAppletProxy::GetDisplayController, "GetDisplayController"}, 25 // clang-format on
27 {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"},
28 {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
29 {20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"},
30 {21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"},
31 {22, &ILibraryAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
32 {23, &ILibraryAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
33 {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
34 };
35 // clang-format on
36
37 RegisterHandlers(functions);
38 }
39
40private:
41 void GetCommonStateGetter(HLERequestContext& ctx) {
42 LOG_DEBUG(Service_AM, "called");
43
44 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
45 rb.Push(ResultSuccess);
46 rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue);
47 }
48
49 void GetSelfController(HLERequestContext& ctx) {
50 LOG_DEBUG(Service_AM, "called");
51
52 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
53 rb.Push(ResultSuccess);
54 rb.PushIpcInterface<ISelfController>(system, nvnflinger);
55 }
56
57 void GetWindowController(HLERequestContext& ctx) {
58 LOG_DEBUG(Service_AM, "called");
59
60 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
61 rb.Push(ResultSuccess);
62 rb.PushIpcInterface<IWindowController>(system);
63 }
64
65 void GetAudioController(HLERequestContext& ctx) {
66 LOG_DEBUG(Service_AM, "called");
67
68 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
69 rb.Push(ResultSuccess);
70 rb.PushIpcInterface<IAudioController>(system);
71 }
72
73 void GetDisplayController(HLERequestContext& ctx) {
74 LOG_DEBUG(Service_AM, "called");
75
76 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
77 rb.Push(ResultSuccess);
78 rb.PushIpcInterface<IDisplayController>(system);
79 }
80
81 void GetProcessWindingController(HLERequestContext& ctx) {
82 LOG_DEBUG(Service_AM, "called");
83
84 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
85 rb.Push(ResultSuccess);
86 rb.PushIpcInterface<IProcessWindingController>(system);
87 }
88
89 void GetLibraryAppletCreator(HLERequestContext& ctx) {
90 LOG_DEBUG(Service_AM, "called");
91
92 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
93 rb.Push(ResultSuccess);
94 rb.PushIpcInterface<ILibraryAppletCreator>(system);
95 }
96
97 void OpenLibraryAppletSelfAccessor(HLERequestContext& ctx) {
98 LOG_DEBUG(Service_AM, "called");
99
100 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
101 rb.Push(ResultSuccess);
102 rb.PushIpcInterface<ILibraryAppletSelfAccessor>(system);
103 }
104
105 void GetAppletCommonFunctions(HLERequestContext& ctx) {
106 LOG_DEBUG(Service_AM, "called");
107
108 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
109 rb.Push(ResultSuccess);
110 rb.PushIpcInterface<IAppletCommonFunctions>(system);
111 }
112
113 void GetHomeMenuFunctions(HLERequestContext& ctx) {
114 LOG_DEBUG(Service_AM, "called");
115
116 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
117 rb.Push(ResultSuccess);
118 rb.PushIpcInterface<IHomeMenuFunctions>(system);
119 }
120
121 void GetGlobalStateController(HLERequestContext& ctx) {
122 LOG_DEBUG(Service_AM, "called");
123
124 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
125 rb.Push(ResultSuccess);
126 rb.PushIpcInterface<IGlobalStateController>(system);
127 }
128
129 void GetDebugFunctions(HLERequestContext& ctx) {
130 LOG_DEBUG(Service_AM, "called");
131
132 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
133 rb.Push(ResultSuccess);
134 rb.PushIpcInterface<IDebugFunctions>(system);
135 }
136
137 Nvnflinger::Nvnflinger& nvnflinger;
138 std::shared_ptr<AppletMessageQueue> msg_queue;
139};
140
141class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
142public:
143 explicit ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
144 std::shared_ptr<AppletMessageQueue> msg_queue_,
145 Core::System& system_)
146 : ServiceFramework{system_, "ISystemAppletProxy"},
147 nvnflinger{nvnflinger_}, msg_queue{std::move(msg_queue_)} {
148 // clang-format off
149 static const FunctionInfo functions[] = {
150 {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
151 {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
152 {2, &ISystemAppletProxy::GetWindowController, "GetWindowController"},
153 {3, &ISystemAppletProxy::GetAudioController, "GetAudioController"},
154 {4, &ISystemAppletProxy::GetDisplayController, "GetDisplayController"},
155 {10, nullptr, "GetProcessWindingController"},
156 {11, &ISystemAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
157 {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
158 {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
159 {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"},
160 {23, &ISystemAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"},
161 {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
162 };
163 // clang-format on
164
165 RegisterHandlers(functions);
166 }
167
168private:
169 void GetCommonStateGetter(HLERequestContext& ctx) {
170 LOG_DEBUG(Service_AM, "called");
171
172 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
173 rb.Push(ResultSuccess);
174 rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue);
175 }
176
177 void GetSelfController(HLERequestContext& ctx) {
178 LOG_DEBUG(Service_AM, "called");
179
180 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
181 rb.Push(ResultSuccess);
182 rb.PushIpcInterface<ISelfController>(system, nvnflinger);
183 }
184
185 void GetWindowController(HLERequestContext& ctx) {
186 LOG_DEBUG(Service_AM, "called");
187
188 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
189 rb.Push(ResultSuccess);
190 rb.PushIpcInterface<IWindowController>(system);
191 }
192
193 void GetAudioController(HLERequestContext& ctx) {
194 LOG_DEBUG(Service_AM, "called");
195
196 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
197 rb.Push(ResultSuccess);
198 rb.PushIpcInterface<IAudioController>(system);
199 }
200
201 void GetDisplayController(HLERequestContext& ctx) {
202 LOG_DEBUG(Service_AM, "called");
203
204 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
205 rb.Push(ResultSuccess);
206 rb.PushIpcInterface<IDisplayController>(system);
207 }
208 26
209 void GetLibraryAppletCreator(HLERequestContext& ctx) { 27 RegisterHandlers(functions);
210 LOG_DEBUG(Service_AM, "called"); 28}
211 29
212 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 30AppletAE::~AppletAE() = default;
213 rb.Push(ResultSuccess);
214 rb.PushIpcInterface<ILibraryAppletCreator>(system);
215 }
216 31
217 void GetHomeMenuFunctions(HLERequestContext& ctx) { 32void AppletAE::OpenSystemAppletProxy(HLERequestContext& ctx) {
218 LOG_DEBUG(Service_AM, "called"); 33 LOG_DEBUG(Service_AM, "called");
219 34
35 if (const auto applet = GetAppletFromContext(ctx)) {
220 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 36 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
221 rb.Push(ResultSuccess); 37 rb.Push(ResultSuccess);
222 rb.PushIpcInterface<IHomeMenuFunctions>(system); 38 rb.PushIpcInterface<ISystemAppletProxy>(nvnflinger, applet, system);
223 } 39 } else {
224 40 UNIMPLEMENTED();
225 void GetGlobalStateController(HLERequestContext& ctx) {
226 LOG_DEBUG(Service_AM, "called");
227 41
228 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 42 IPC::ResponseBuilder rb{ctx, 2};
229 rb.Push(ResultSuccess); 43 rb.Push(ResultUnknown);
230 rb.PushIpcInterface<IGlobalStateController>(system);
231 }
232
233 void GetApplicationCreator(HLERequestContext& ctx) {
234 LOG_DEBUG(Service_AM, "called");
235
236 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
237 rb.Push(ResultSuccess);
238 rb.PushIpcInterface<IApplicationCreator>(system);
239 } 44 }
45}
240 46
241 void GetAppletCommonFunctions(HLERequestContext& ctx) { 47void AppletAE::OpenLibraryAppletProxy(HLERequestContext& ctx) {
242 LOG_DEBUG(Service_AM, "called"); 48 LOG_DEBUG(Service_AM, "called");
243 49
50 if (const auto applet = GetAppletFromContext(ctx)) {
244 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 51 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
245 rb.Push(ResultSuccess); 52 rb.Push(ResultSuccess);
246 rb.PushIpcInterface<IAppletCommonFunctions>(system); 53 rb.PushIpcInterface<ILibraryAppletProxy>(nvnflinger, applet, system);
247 } 54 } else {
248 55 UNIMPLEMENTED();
249 void GetDebugFunctions(HLERequestContext& ctx) {
250 LOG_DEBUG(Service_AM, "called");
251 56
252 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 57 IPC::ResponseBuilder rb{ctx, 2};
253 rb.Push(ResultSuccess); 58 rb.Push(ResultUnknown);
254 rb.PushIpcInterface<IDebugFunctions>(system);
255 } 59 }
256
257 Nvnflinger::Nvnflinger& nvnflinger;
258 std::shared_ptr<AppletMessageQueue> msg_queue;
259};
260
261void AppletAE::OpenSystemAppletProxy(HLERequestContext& ctx) {
262 LOG_DEBUG(Service_AM, "called");
263
264 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
265 rb.Push(ResultSuccess);
266 rb.PushIpcInterface<ISystemAppletProxy>(nvnflinger, msg_queue, system);
267}
268
269void AppletAE::OpenLibraryAppletProxy(HLERequestContext& ctx) {
270 LOG_DEBUG(Service_AM, "called");
271
272 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
273 rb.Push(ResultSuccess);
274 rb.PushIpcInterface<ILibraryAppletProxy>(nvnflinger, msg_queue, system);
275} 60}
276 61
277void AppletAE::OpenLibraryAppletProxyOld(HLERequestContext& ctx) { 62void AppletAE::OpenLibraryAppletProxyOld(HLERequestContext& ctx) {
278 LOG_DEBUG(Service_AM, "called"); 63 LOG_DEBUG(Service_AM, "called");
279 64
280 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 65 return OpenLibraryAppletProxy(ctx);
281 rb.Push(ResultSuccess);
282 rb.PushIpcInterface<ILibraryAppletProxy>(nvnflinger, msg_queue, system);
283} 66}
284 67
285AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, 68std::shared_ptr<Applet> AppletAE::GetAppletFromContext(HLERequestContext& ctx) {
286 std::shared_ptr<AppletMessageQueue> msg_queue_, Core::System& system_) 69 const auto aruid = ctx.GetPID();
287 : ServiceFramework{system_, "appletAE"}, nvnflinger{nvnflinger_}, msg_queue{ 70 return system.GetAppletManager().GetByAppletResourceUserId(aruid);
288 std::move(msg_queue_)} {
289 // clang-format off
290 static const FunctionInfo functions[] = {
291 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
292 {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"},
293 {201, &AppletAE::OpenLibraryAppletProxy, "OpenLibraryAppletProxy"},
294 {300, nullptr, "OpenOverlayAppletProxy"},
295 {350, nullptr, "OpenSystemApplicationProxy"},
296 {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
297 {410, nullptr, "GetSystemAppletControllerForDebug"},
298 {1000, nullptr, "GetDebugFunctions"},
299 };
300 // clang-format on
301
302 RegisterHandlers(functions);
303}
304
305AppletAE::~AppletAE() = default;
306
307const std::shared_ptr<AppletMessageQueue>& AppletAE::GetMessageQueue() const {
308 return msg_queue;
309} 71}
310 72
311} // namespace Service::AM 73} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 538ce2903..3d7961fa1 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -18,23 +18,21 @@ class Nvnflinger;
18 18
19namespace AM { 19namespace AM {
20 20
21class AppletMessageQueue; 21struct Applet;
22 22
23class AppletAE final : public ServiceFramework<AppletAE> { 23class AppletAE final : public ServiceFramework<AppletAE> {
24public: 24public:
25 explicit AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, 25 explicit AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_);
26 std::shared_ptr<AppletMessageQueue> msg_queue_, Core::System& system_);
27 ~AppletAE() override; 26 ~AppletAE() override;
28 27
29 const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
30
31private: 28private:
32 void OpenSystemAppletProxy(HLERequestContext& ctx); 29 void OpenSystemAppletProxy(HLERequestContext& ctx);
33 void OpenLibraryAppletProxy(HLERequestContext& ctx); 30 void OpenLibraryAppletProxy(HLERequestContext& ctx);
34 void OpenLibraryAppletProxyOld(HLERequestContext& ctx); 31 void OpenLibraryAppletProxyOld(HLERequestContext& ctx);
35 32
33 std::shared_ptr<Applet> GetAppletFromContext(HLERequestContext& ctx);
34
36 Nvnflinger::Nvnflinger& nvnflinger; 35 Nvnflinger::Nvnflinger& nvnflinger;
37 std::shared_ptr<AppletMessageQueue> msg_queue;
38}; 36};
39 37
40} // namespace AM 38} // namespace AM
diff --git a/src/core/hle/service/am/applet_common_functions.cpp b/src/core/hle/service/am/applet_common_functions.cpp
new file mode 100644
index 000000000..130614ae5
--- /dev/null
+++ b/src/core/hle/service/am/applet_common_functions.cpp
@@ -0,0 +1,63 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/applet.h"
5#include "core/hle/service/am/applet_common_functions.h"
6#include "core/hle/service/ipc_helpers.h"
7
8namespace Service::AM {
9
10IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,
11 std::shared_ptr<Applet> applet_)
12 : ServiceFramework{system_, "IAppletCommonFunctions"}, applet{std::move(applet_)} {
13 // clang-format off
14 static const FunctionInfo functions[] = {
15 {0, nullptr, "SetTerminateResult"},
16 {10, nullptr, "ReadThemeStorage"},
17 {11, nullptr, "WriteThemeStorage"},
18 {20, nullptr, "PushToAppletBoundChannel"},
19 {21, nullptr, "TryPopFromAppletBoundChannel"},
20 {40, nullptr, "GetDisplayLogicalResolution"},
21 {42, nullptr, "SetDisplayMagnification"},
22 {50, nullptr, "SetHomeButtonDoubleClickEnabled"},
23 {51, nullptr, "GetHomeButtonDoubleClickEnabled"},
24 {52, nullptr, "IsHomeButtonShortPressedBlocked"},
25 {60, nullptr, "IsVrModeCurtainRequired"},
26 {61, nullptr, "IsSleepRequiredByHighTemperature"},
27 {62, nullptr, "IsSleepRequiredByLowBattery"},
28 {70, &IAppletCommonFunctions::SetCpuBoostRequestPriority, "SetCpuBoostRequestPriority"},
29 {80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"},
30 {81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"},
31 {90, nullptr, "OpenNamedChannelAsParent"},
32 {91, nullptr, "OpenNamedChannelAsChild"},
33 {100, nullptr, "SetApplicationCoreUsageMode"},
34 {300, &IAppletCommonFunctions::GetCurrentApplicationId, "GetCurrentApplicationId"},
35 };
36 // clang-format on
37
38 RegisterHandlers(functions);
39}
40
41IAppletCommonFunctions::~IAppletCommonFunctions() = default;
42
43void IAppletCommonFunctions::SetCpuBoostRequestPriority(HLERequestContext& ctx) {
44 LOG_WARNING(Service_AM, "(STUBBED) called");
45
46 IPC::RequestParser rp{ctx};
47
48 std::scoped_lock lk{applet->lock};
49 applet->cpu_boost_request_priority = rp.Pop<s32>();
50
51 IPC::ResponseBuilder rb{ctx, 2};
52 rb.Push(ResultSuccess);
53}
54
55void IAppletCommonFunctions::GetCurrentApplicationId(HLERequestContext& ctx) {
56 LOG_WARNING(Service_AM, "(STUBBED) called");
57
58 IPC::ResponseBuilder rb{ctx, 4};
59 rb.Push(ResultSuccess);
60 rb.Push<u64>(system.GetApplicationProcessProgramID() & ~0xFFFULL);
61}
62
63} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_common_functions.h b/src/core/hle/service/am/applet_common_functions.h
new file mode 100644
index 000000000..b86adf5cb
--- /dev/null
+++ b/src/core/hle/service/am/applet_common_functions.h
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10struct Applet;
11
12class IAppletCommonFunctions final : public ServiceFramework<IAppletCommonFunctions> {
13public:
14 explicit IAppletCommonFunctions(Core::System& system_, std::shared_ptr<Applet> applet_);
15 ~IAppletCommonFunctions() override;
16
17private:
18 void SetCpuBoostRequestPriority(HLERequestContext& ctx);
19 void GetCurrentApplicationId(HLERequestContext& ctx);
20
21 const std::shared_ptr<Applet> applet;
22};
23
24} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_data_broker.cpp b/src/core/hle/service/am/applet_data_broker.cpp
new file mode 100644
index 000000000..4d58c4db5
--- /dev/null
+++ b/src/core/hle/service/am/applet_data_broker.cpp
@@ -0,0 +1,67 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5
6#include "core/core.h"
7#include "core/hle/service/am/am_results.h"
8#include "core/hle/service/am/applet_data_broker.h"
9#include "core/hle/service/am/applet_manager.h"
10
11namespace Service::AM {
12
13AppletStorageChannel::AppletStorageChannel(KernelHelpers::ServiceContext& context)
14 : m_event(context) {}
15AppletStorageChannel::~AppletStorageChannel() = default;
16
17void AppletStorageChannel::Push(std::shared_ptr<IStorage> storage) {
18 std::scoped_lock lk{m_lock};
19
20 m_data.emplace_back(std::move(storage));
21 m_event.Signal();
22}
23
24Result AppletStorageChannel::Pop(std::shared_ptr<IStorage>* out_storage) {
25 std::scoped_lock lk{m_lock};
26
27 SCOPE_EXIT({
28 if (m_data.empty()) {
29 m_event.Clear();
30 }
31 });
32
33 R_UNLESS(!m_data.empty(), AM::ResultNoDataInChannel);
34
35 *out_storage = std::move(m_data.front());
36 m_data.pop_front();
37
38 R_SUCCEED();
39}
40
41Kernel::KReadableEvent* AppletStorageChannel::GetEvent() {
42 return m_event.GetHandle();
43}
44
45AppletDataBroker::AppletDataBroker(Core::System& system_)
46 : system(system_), context(system_, "AppletDataBroker"), in_data(context),
47 interactive_in_data(context), out_data(context), interactive_out_data(context),
48 state_changed_event(context), is_completed(false) {}
49
50AppletDataBroker::~AppletDataBroker() = default;
51
52void AppletDataBroker::SignalCompletion() {
53 {
54 std::scoped_lock lk{lock};
55
56 if (is_completed) {
57 return;
58 }
59
60 is_completed = true;
61 state_changed_event.Signal();
62 }
63
64 system.GetAppletManager().FocusStateChanged();
65}
66
67} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_data_broker.h b/src/core/hle/service/am/applet_data_broker.h
new file mode 100644
index 000000000..12326fd04
--- /dev/null
+++ b/src/core/hle/service/am/applet_data_broker.h
@@ -0,0 +1,80 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <deque>
7#include <memory>
8#include <mutex>
9
10#include "core/hle/service/event.h"
11#include "core/hle/service/kernel_helpers.h"
12
13union Result;
14
15namespace Service::AM {
16
17struct Applet;
18class IStorage;
19
20class AppletStorageChannel {
21public:
22 explicit AppletStorageChannel(KernelHelpers::ServiceContext& ctx);
23 ~AppletStorageChannel();
24
25 void Push(std::shared_ptr<IStorage> storage);
26 Result Pop(std::shared_ptr<IStorage>* out_storage);
27 Kernel::KReadableEvent* GetEvent();
28
29private:
30 std::mutex m_lock{};
31 std::deque<std::shared_ptr<IStorage>> m_data{};
32 Event m_event;
33};
34
35class AppletDataBroker {
36public:
37 explicit AppletDataBroker(Core::System& system_);
38 ~AppletDataBroker();
39
40 AppletStorageChannel& GetInData() {
41 return in_data;
42 }
43
44 AppletStorageChannel& GetInteractiveInData() {
45 return interactive_in_data;
46 }
47
48 AppletStorageChannel& GetOutData() {
49 return out_data;
50 }
51
52 AppletStorageChannel& GetInteractiveOutData() {
53 return interactive_out_data;
54 }
55
56 Event& GetStateChangedEvent() {
57 return state_changed_event;
58 }
59
60 bool IsCompleted() const {
61 return is_completed;
62 }
63
64 void SignalCompletion();
65
66private:
67 Core::System& system;
68 KernelHelpers::ServiceContext context;
69
70 AppletStorageChannel in_data;
71 AppletStorageChannel interactive_in_data;
72 AppletStorageChannel out_data;
73 AppletStorageChannel interactive_out_data;
74 Event state_changed_event;
75
76 std::mutex lock;
77 bool is_completed;
78};
79
80} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp
new file mode 100644
index 000000000..52200d5b2
--- /dev/null
+++ b/src/core/hle/service/am/applet_manager.cpp
@@ -0,0 +1,361 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "common/uuid.h"
6#include "core/core.h"
7#include "core/core_timing.h"
8#include "core/hle/service/acc/profile_manager.h"
9#include "core/hle/service/am/applet_data_broker.h"
10#include "core/hle/service/am/applet_manager.h"
11#include "core/hle/service/am/frontend/applet_cabinet.h"
12#include "core/hle/service/am/frontend/applet_controller.h"
13#include "core/hle/service/am/frontend/applet_mii_edit_types.h"
14#include "core/hle/service/am/frontend/applet_software_keyboard_types.h"
15#include "hid_core/hid_types.h"
16
17namespace Service::AM {
18
19namespace {
20
21constexpr u32 LaunchParameterAccountPreselectedUserMagic = 0xC79497CA;
22
23struct LaunchParameterAccountPreselectedUser {
24 u32 magic;
25 u32 is_account_selected;
26 Common::UUID current_user;
27 INSERT_PADDING_BYTES(0x70);
28};
29static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88);
30
31AppletStorageChannel& InitializeFakeCallerApplet(Core::System& system,
32 std::shared_ptr<Applet>& applet) {
33 applet->caller_applet_broker = std::make_shared<AppletDataBroker>(system);
34 return applet->caller_applet_broker->GetInData();
35}
36
37void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) {
38 const CommonArguments arguments{
39 .arguments_version = CommonArgumentVersion::Version3,
40 .size = CommonArgumentSize::Version3,
41 .library_version = 1,
42 .theme_color = ThemeColor::BasicBlack,
43 .play_startup_sound = true,
44 .system_tick = system.CoreTiming().GetClockTicks(),
45 };
46
47 std::vector<u8> argument_data(sizeof(arguments));
48 std::vector<u8> settings_data{2};
49 std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
50 channel.Push(std::make_shared<IStorage>(system, std::move(argument_data)));
51 channel.Push(std::make_shared<IStorage>(system, std::move(settings_data)));
52}
53
54void PushInShowController(Core::System& system, AppletStorageChannel& channel) {
55 const CommonArguments common_args = {
56 .arguments_version = CommonArgumentVersion::Version3,
57 .size = CommonArgumentSize::Version3,
58 .library_version = static_cast<u32>(Frontend::ControllerAppletVersion::Version8),
59 .theme_color = ThemeColor::BasicBlack,
60 .play_startup_sound = true,
61 .system_tick = system.CoreTiming().GetClockTicks(),
62 };
63
64 Frontend::ControllerSupportArgNew user_args = {
65 .header = {.player_count_min = 1,
66 .player_count_max = 4,
67 .enable_take_over_connection = true,
68 .enable_left_justify = false,
69 .enable_permit_joy_dual = true,
70 .enable_single_mode = false,
71 .enable_identification_color = false},
72 .identification_colors = {},
73 .enable_explain_text = false,
74 .explain_text = {},
75 };
76
77 Frontend::ControllerSupportArgPrivate private_args = {
78 .arg_private_size = sizeof(Frontend::ControllerSupportArgPrivate),
79 .arg_size = sizeof(Frontend::ControllerSupportArgNew),
80 .is_home_menu = true,
81 .flag_1 = true,
82 .mode = Frontend::ControllerSupportMode::ShowControllerSupport,
83 .caller = Frontend::ControllerSupportCaller::
84 Application, // switchbrew: Always zero except with
85 // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem,
86 // which sets this to the input param
87 .style_set = Core::HID::NpadStyleSet::None,
88 .joy_hold_type = 0,
89 };
90 std::vector<u8> common_args_data(sizeof(common_args));
91 std::vector<u8> private_args_data(sizeof(private_args));
92 std::vector<u8> user_args_data(sizeof(user_args));
93
94 std::memcpy(common_args_data.data(), &common_args, sizeof(common_args));
95 std::memcpy(private_args_data.data(), &private_args, sizeof(private_args));
96 std::memcpy(user_args_data.data(), &user_args, sizeof(user_args));
97
98 channel.Push(std::make_shared<IStorage>(system, std::move(common_args_data)));
99 channel.Push(std::make_shared<IStorage>(system, std::move(private_args_data)));
100 channel.Push(std::make_shared<IStorage>(system, std::move(user_args_data)));
101}
102
103void PushInShowCabinetData(Core::System& system, AppletStorageChannel& channel) {
104 const CommonArguments arguments{
105 .arguments_version = CommonArgumentVersion::Version3,
106 .size = CommonArgumentSize::Version3,
107 .library_version = static_cast<u32>(Frontend::CabinetAppletVersion::Version1),
108 .theme_color = ThemeColor::BasicBlack,
109 .play_startup_sound = true,
110 .system_tick = system.CoreTiming().GetClockTicks(),
111 };
112
113 const Frontend::StartParamForAmiiboSettings amiibo_settings{
114 .param_1 = 0,
115 .applet_mode = system.GetFrontendAppletHolder().GetCabinetMode(),
116 .flags = Frontend::CabinetFlags::None,
117 .amiibo_settings_1 = 0,
118 .device_handle = 0,
119 .tag_info{},
120 .register_info{},
121 .amiibo_settings_3{},
122 };
123
124 std::vector<u8> argument_data(sizeof(arguments));
125 std::vector<u8> settings_data(sizeof(amiibo_settings));
126 std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
127 std::memcpy(settings_data.data(), &amiibo_settings, sizeof(amiibo_settings));
128 channel.Push(std::make_shared<IStorage>(system, std::move(argument_data)));
129 channel.Push(std::make_shared<IStorage>(system, std::move(settings_data)));
130}
131
132void PushInShowMiiEditData(Core::System& system, AppletStorageChannel& channel) {
133 struct MiiEditV3 {
134 Frontend::MiiEditAppletInputCommon common;
135 Frontend::MiiEditAppletInputV3 input;
136 };
137 static_assert(sizeof(MiiEditV3) == 0x100, "MiiEditV3 has incorrect size.");
138
139 MiiEditV3 mii_arguments{
140 .common =
141 {
142 .version = Frontend::MiiEditAppletVersion::Version3,
143 .applet_mode = Frontend::MiiEditAppletMode::ShowMiiEdit,
144 },
145 .input{},
146 };
147
148 std::vector<u8> argument_data(sizeof(mii_arguments));
149 std::memcpy(argument_data.data(), &mii_arguments, sizeof(mii_arguments));
150
151 channel.Push(std::make_shared<IStorage>(system, std::move(argument_data)));
152}
153
154void PushInShowSoftwareKeyboard(Core::System& system, AppletStorageChannel& channel) {
155 const CommonArguments arguments{
156 .arguments_version = CommonArgumentVersion::Version3,
157 .size = CommonArgumentSize::Version3,
158 .library_version = static_cast<u32>(Frontend::SwkbdAppletVersion::Version524301),
159 .theme_color = ThemeColor::BasicBlack,
160 .play_startup_sound = true,
161 .system_tick = system.CoreTiming().GetClockTicks(),
162 };
163
164 std::vector<char16_t> initial_string(0);
165
166 const Frontend::SwkbdConfigCommon swkbd_config{
167 .type = Frontend::SwkbdType::Qwerty,
168 .ok_text{},
169 .left_optional_symbol_key{},
170 .right_optional_symbol_key{},
171 .use_prediction = false,
172 .key_disable_flags{},
173 .initial_cursor_position = Frontend::SwkbdInitialCursorPosition::Start,
174 .header_text{},
175 .sub_text{},
176 .guide_text{},
177 .max_text_length = 500,
178 .min_text_length = 0,
179 .password_mode = Frontend::SwkbdPasswordMode::Disabled,
180 .text_draw_type = Frontend::SwkbdTextDrawType::Box,
181 .enable_return_button = true,
182 .use_utf8 = false,
183 .use_blur_background = true,
184 .initial_string_offset{},
185 .initial_string_length = static_cast<u32>(initial_string.size()),
186 .user_dictionary_offset{},
187 .user_dictionary_entries{},
188 .use_text_check = false,
189 };
190
191 Frontend::SwkbdConfigNew swkbd_config_new{};
192
193 std::vector<u8> argument_data(sizeof(arguments));
194 std::vector<u8> swkbd_data(sizeof(swkbd_config) + sizeof(swkbd_config_new));
195 std::vector<u8> work_buffer(swkbd_config.initial_string_length * sizeof(char16_t));
196
197 std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
198 std::memcpy(swkbd_data.data(), &swkbd_config, sizeof(swkbd_config));
199 std::memcpy(swkbd_data.data() + sizeof(swkbd_config), &swkbd_config_new,
200 sizeof(Frontend::SwkbdConfigNew));
201 std::memcpy(work_buffer.data(), initial_string.data(),
202 swkbd_config.initial_string_length * sizeof(char16_t));
203
204 channel.Push(std::make_shared<IStorage>(system, std::move(argument_data)));
205 channel.Push(std::make_shared<IStorage>(system, std::move(swkbd_data)));
206 channel.Push(std::make_shared<IStorage>(system, std::move(work_buffer)));
207}
208
209} // namespace
210
211AppletManager::AppletManager(Core::System& system) : m_system(system) {}
212AppletManager::~AppletManager() {
213 this->Reset();
214}
215
216void AppletManager::InsertApplet(std::shared_ptr<Applet> applet) {
217 std::scoped_lock lk{m_lock};
218
219 m_applets.emplace(applet->aruid, std::move(applet));
220}
221
222void AppletManager::TerminateAndRemoveApplet(AppletResourceUserId aruid) {
223 std::shared_ptr<Applet> applet;
224 bool should_stop = false;
225 {
226 std::scoped_lock lk{m_lock};
227
228 const auto it = m_applets.find(aruid);
229 if (it == m_applets.end()) {
230 return;
231 }
232
233 applet = it->second;
234 m_applets.erase(it);
235
236 should_stop = m_applets.empty();
237 }
238
239 // Terminate process.
240 applet->process->Terminate();
241
242 // If there were no applets left, stop emulation.
243 if (should_stop) {
244 m_system.Exit();
245 }
246}
247
248void AppletManager::CreateAndInsertByFrontendAppletParameters(
249 AppletResourceUserId aruid, const FrontendAppletParameters& params) {
250 // TODO: this should be run inside AM so that the events will have a parent process
251 // TODO: have am create the guest process
252 auto applet = std::make_shared<Applet>(m_system, std::make_unique<Process>(m_system));
253
254 applet->aruid = aruid;
255 applet->program_id = params.program_id;
256 applet->applet_id = params.applet_id;
257 applet->type = params.applet_type;
258 applet->previous_program_index = params.previous_program_index;
259
260 // Push UserChannel data from previous application
261 if (params.launch_type == LaunchType::ApplicationInitiated) {
262 applet->user_channel_launch_parameter.swap(m_system.GetUserChannel());
263 }
264
265 // TODO: Read whether we need a preselected user from NACP?
266 // TODO: This can be done quite easily from loader
267 {
268 LaunchParameterAccountPreselectedUser lp{};
269
270 lp.magic = LaunchParameterAccountPreselectedUserMagic;
271 lp.is_account_selected = 1;
272
273 Account::ProfileManager profile_manager{};
274 const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user));
275 ASSERT(uuid.has_value() && uuid->IsValid());
276 lp.current_user = *uuid;
277
278 std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
279 std::memcpy(buffer.data(), &lp, buffer.size());
280
281 applet->preselected_user_launch_parameter.push_back(std::move(buffer));
282 }
283
284 // Starting from frontend, some applets require input data.
285 switch (applet->applet_id) {
286 case AppletId::Cabinet:
287 PushInShowCabinetData(m_system, InitializeFakeCallerApplet(m_system, applet));
288 break;
289 case AppletId::MiiEdit:
290 PushInShowMiiEditData(m_system, InitializeFakeCallerApplet(m_system, applet));
291 break;
292 case AppletId::PhotoViewer:
293 PushInShowAlbum(m_system, InitializeFakeCallerApplet(m_system, applet));
294 break;
295 case AppletId::SoftwareKeyboard:
296 PushInShowSoftwareKeyboard(m_system, InitializeFakeCallerApplet(m_system, applet));
297 break;
298 case AppletId::Controller:
299 PushInShowController(m_system, InitializeFakeCallerApplet(m_system, applet));
300 break;
301 default:
302 break;
303 }
304
305 // Applet was started by frontend, so it is foreground.
306 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
307 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
308 applet->focus_state = FocusState::InFocus;
309
310 this->InsertApplet(std::move(applet));
311}
312
313std::shared_ptr<Applet> AppletManager::GetByAppletResourceUserId(AppletResourceUserId aruid) const {
314 std::scoped_lock lk{m_lock};
315
316 if (const auto it = m_applets.find(aruid); it != m_applets.end()) {
317 return it->second;
318 }
319
320 return {};
321}
322
323void AppletManager::Reset() {
324 std::scoped_lock lk{m_lock};
325
326 m_applets.clear();
327}
328
329void AppletManager::RequestExit() {
330 std::scoped_lock lk{m_lock};
331
332 for (const auto& [aruid, applet] : m_applets) {
333 applet->message_queue.RequestExit();
334 }
335}
336
337void AppletManager::RequestResume() {
338 std::scoped_lock lk{m_lock};
339
340 for (const auto& [aruid, applet] : m_applets) {
341 applet->message_queue.RequestResume();
342 }
343}
344
345void AppletManager::OperationModeChanged() {
346 std::scoped_lock lk{m_lock};
347
348 for (const auto& [aruid, applet] : m_applets) {
349 applet->message_queue.OperationModeChanged();
350 }
351}
352
353void AppletManager::FocusStateChanged() {
354 std::scoped_lock lk{m_lock};
355
356 for (const auto& [aruid, applet] : m_applets) {
357 applet->message_queue.FocusStateChanged();
358 }
359}
360
361} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_manager.h b/src/core/hle/service/am/applet_manager.h
new file mode 100644
index 000000000..4875de309
--- /dev/null
+++ b/src/core/hle/service/am/applet_manager.h
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <map>
7#include <mutex>
8
9#include "core/hle/service/am/applet.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::AM {
16
17enum class LaunchType {
18 FrontendInitiated,
19 ApplicationInitiated,
20};
21
22struct FrontendAppletParameters {
23 ProgramId program_id{};
24 AppletId applet_id{};
25 AppletType applet_type{};
26 LaunchType launch_type{};
27 s32 program_index{};
28 s32 previous_program_index{-1};
29};
30
31class AppletManager {
32public:
33 explicit AppletManager(Core::System& system);
34 ~AppletManager();
35
36 void InsertApplet(std::shared_ptr<Applet> applet);
37 void TerminateAndRemoveApplet(AppletResourceUserId aruid);
38
39 void CreateAndInsertByFrontendAppletParameters(AppletResourceUserId aruid,
40 const FrontendAppletParameters& params);
41 std::shared_ptr<Applet> GetByAppletResourceUserId(AppletResourceUserId aruid) const;
42
43 void Reset();
44
45 void RequestExit();
46 void RequestResume();
47 void OperationModeChanged();
48 void FocusStateChanged();
49
50private:
51 Core::System& m_system;
52
53 mutable std::mutex m_lock{};
54 std::map<AppletResourceUserId, std::shared_ptr<Applet>> m_applets{};
55
56 // AudioController state goes here
57};
58
59} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_message_queue.cpp b/src/core/hle/service/am/applet_message_queue.cpp
new file mode 100644
index 000000000..5ed996b70
--- /dev/null
+++ b/src/core/hle/service/am/applet_message_queue.cpp
@@ -0,0 +1,73 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/applet_message_queue.h"
5#include "core/hle/service/ipc_helpers.h"
6
7namespace Service::AM {
8
9AppletMessageQueue::AppletMessageQueue(Core::System& system)
10 : service_context{system, "AppletMessageQueue"} {
11 on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived");
12 on_operation_mode_changed = service_context.CreateEvent("AMMessageQueue:OperationModeChanged");
13}
14
15AppletMessageQueue::~AppletMessageQueue() {
16 service_context.CloseEvent(on_new_message);
17 service_context.CloseEvent(on_operation_mode_changed);
18}
19
20Kernel::KReadableEvent& AppletMessageQueue::GetMessageReceiveEvent() {
21 return on_new_message->GetReadableEvent();
22}
23
24Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() {
25 return on_operation_mode_changed->GetReadableEvent();
26}
27
28void AppletMessageQueue::PushMessage(AppletMessage msg) {
29 {
30 std::scoped_lock lk{lock};
31 messages.push(msg);
32 }
33 on_new_message->Signal();
34}
35
36AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
37 std::scoped_lock lk{lock};
38 if (messages.empty()) {
39 on_new_message->Clear();
40 return AppletMessage::None;
41 }
42 auto msg = messages.front();
43 messages.pop();
44 if (messages.empty()) {
45 on_new_message->Clear();
46 }
47 return msg;
48}
49
50std::size_t AppletMessageQueue::GetMessageCount() const {
51 std::scoped_lock lk{lock};
52 return messages.size();
53}
54
55void AppletMessageQueue::RequestExit() {
56 PushMessage(AppletMessage::Exit);
57}
58
59void AppletMessageQueue::RequestResume() {
60 PushMessage(AppletMessage::Resume);
61}
62
63void AppletMessageQueue::FocusStateChanged() {
64 PushMessage(AppletMessage::FocusStateChanged);
65}
66
67void AppletMessageQueue::OperationModeChanged() {
68 PushMessage(AppletMessage::OperationModeChanged);
69 PushMessage(AppletMessage::PerformanceModeChanged);
70 on_operation_mode_changed->Signal();
71}
72
73} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_message_queue.h b/src/core/hle/service/am/applet_message_queue.h
new file mode 100644
index 000000000..5cb236d47
--- /dev/null
+++ b/src/core/hle/service/am/applet_message_queue.h
@@ -0,0 +1,76 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <queue>
7
8#include "core/hle/service/kernel_helpers.h"
9#include "core/hle/service/service.h"
10
11namespace Kernel {
12class KReadableEvent;
13} // namespace Kernel
14
15namespace Service::AM {
16
17class AppletMessageQueue {
18public:
19 // This is nn::am::AppletMessage
20 enum class AppletMessage : u32 {
21 None = 0,
22 ChangeIntoForeground = 1,
23 ChangeIntoBackground = 2,
24 Exit = 4,
25 ApplicationExited = 6,
26 FocusStateChanged = 15,
27 Resume = 16,
28 DetectShortPressingHomeButton = 20,
29 DetectLongPressingHomeButton = 21,
30 DetectShortPressingPowerButton = 22,
31 DetectMiddlePressingPowerButton = 23,
32 DetectLongPressingPowerButton = 24,
33 RequestToPrepareSleep = 25,
34 FinishedSleepSequence = 26,
35 SleepRequiredByHighTemperature = 27,
36 SleepRequiredByLowBattery = 28,
37 AutoPowerDown = 29,
38 OperationModeChanged = 30,
39 PerformanceModeChanged = 31,
40 DetectReceivingCecSystemStandby = 32,
41 SdCardRemoved = 33,
42 LaunchApplicationRequested = 50,
43 RequestToDisplay = 51,
44 ShowApplicationLogo = 55,
45 HideApplicationLogo = 56,
46 ForceHideApplicationLogo = 57,
47 FloatingApplicationDetected = 60,
48 DetectShortPressingCaptureButton = 90,
49 AlbumScreenShotTaken = 92,
50 AlbumRecordingSaved = 93,
51 };
52
53 explicit AppletMessageQueue(Core::System& system);
54 ~AppletMessageQueue();
55
56 Kernel::KReadableEvent& GetMessageReceiveEvent();
57 Kernel::KReadableEvent& GetOperationModeChangedEvent();
58 void PushMessage(AppletMessage msg);
59 AppletMessage PopMessage();
60 std::size_t GetMessageCount() const;
61 void RequestExit();
62 void RequestResume();
63 void FocusStateChanged();
64 void OperationModeChanged();
65
66private:
67 KernelHelpers::ServiceContext service_context;
68
69 Kernel::KEvent* on_new_message;
70 Kernel::KEvent* on_operation_mode_changed;
71
72 mutable std::mutex lock;
73 std::queue<AppletMessage> messages;
74};
75
76} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index d6c565d85..56bafd162 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -1,129 +1,42 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/logging/log.h"
5#include "core/hle/service/am/am.h" 4#include "core/hle/service/am/am.h"
5#include "core/hle/service/am/applet_manager.h"
6#include "core/hle/service/am/applet_oe.h" 6#include "core/hle/service/am/applet_oe.h"
7#include "core/hle/service/am/application_proxy.h"
7#include "core/hle/service/ipc_helpers.h" 8#include "core/hle/service/ipc_helpers.h"
8#include "core/hle/service/nvnflinger/nvnflinger.h"
9 9
10namespace Service::AM { 10namespace Service::AM {
11 11
12class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { 12AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_)
13public: 13 : ServiceFramework{system_, "appletOE"}, nvnflinger{nvnflinger_} {
14 explicit IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_, 14 static const FunctionInfo functions[] = {
15 std::shared_ptr<AppletMessageQueue> msg_queue_, 15 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
16 Core::System& system_) 16 };
17 : ServiceFramework{system_, "IApplicationProxy"}, 17 RegisterHandlers(functions);
18 nvnflinger{nvnflinger_}, msg_queue{std::move(msg_queue_)} { 18}
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
22 {1, &IApplicationProxy::GetSelfController, "GetSelfController"},
23 {2, &IApplicationProxy::GetWindowController, "GetWindowController"},
24 {3, &IApplicationProxy::GetAudioController, "GetAudioController"},
25 {4, &IApplicationProxy::GetDisplayController, "GetDisplayController"},
26 {10, nullptr, "GetProcessWindingController"},
27 {11, &IApplicationProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
28 {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"},
29 {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"},
30 };
31 // clang-format on
32
33 RegisterHandlers(functions);
34 }
35
36private:
37 void GetAudioController(HLERequestContext& ctx) {
38 LOG_DEBUG(Service_AM, "called");
39
40 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
41 rb.Push(ResultSuccess);
42 rb.PushIpcInterface<IAudioController>(system);
43 }
44
45 void GetDisplayController(HLERequestContext& ctx) {
46 LOG_DEBUG(Service_AM, "called");
47
48 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
49 rb.Push(ResultSuccess);
50 rb.PushIpcInterface<IDisplayController>(system);
51 }
52
53 void GetDebugFunctions(HLERequestContext& ctx) {
54 LOG_DEBUG(Service_AM, "called");
55
56 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
57 rb.Push(ResultSuccess);
58 rb.PushIpcInterface<IDebugFunctions>(system);
59 }
60
61 void GetWindowController(HLERequestContext& ctx) {
62 LOG_DEBUG(Service_AM, "called");
63
64 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
65 rb.Push(ResultSuccess);
66 rb.PushIpcInterface<IWindowController>(system);
67 }
68
69 void GetSelfController(HLERequestContext& ctx) {
70 LOG_DEBUG(Service_AM, "called");
71
72 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
73 rb.Push(ResultSuccess);
74 rb.PushIpcInterface<ISelfController>(system, nvnflinger);
75 }
76
77 void GetCommonStateGetter(HLERequestContext& ctx) {
78 LOG_DEBUG(Service_AM, "called");
79 19
80 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 20AppletOE::~AppletOE() = default;
81 rb.Push(ResultSuccess);
82 rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue);
83 }
84 21
85 void GetLibraryAppletCreator(HLERequestContext& ctx) { 22void AppletOE::OpenApplicationProxy(HLERequestContext& ctx) {
86 LOG_DEBUG(Service_AM, "called"); 23 LOG_DEBUG(Service_AM, "called");
87 24
25 if (const auto applet = GetAppletFromContext(ctx)) {
88 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 26 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
89 rb.Push(ResultSuccess); 27 rb.Push(ResultSuccess);
90 rb.PushIpcInterface<ILibraryAppletCreator>(system); 28 rb.PushIpcInterface<IApplicationProxy>(nvnflinger, applet, system);
91 } 29 } else {
92 30 UNIMPLEMENTED();
93 void GetApplicationFunctions(HLERequestContext& ctx) {
94 LOG_DEBUG(Service_AM, "called");
95 31
96 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 32 IPC::ResponseBuilder rb{ctx, 2};
97 rb.Push(ResultSuccess); 33 rb.Push(ResultUnknown);
98 rb.PushIpcInterface<IApplicationFunctions>(system);
99 } 34 }
100
101 Nvnflinger::Nvnflinger& nvnflinger;
102 std::shared_ptr<AppletMessageQueue> msg_queue;
103};
104
105void AppletOE::OpenApplicationProxy(HLERequestContext& ctx) {
106 LOG_DEBUG(Service_AM, "called");
107
108 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
109 rb.Push(ResultSuccess);
110 rb.PushIpcInterface<IApplicationProxy>(nvnflinger, msg_queue, system);
111}
112
113AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_,
114 std::shared_ptr<AppletMessageQueue> msg_queue_, Core::System& system_)
115 : ServiceFramework{system_, "appletOE"}, nvnflinger{nvnflinger_}, msg_queue{
116 std::move(msg_queue_)} {
117 static const FunctionInfo functions[] = {
118 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
119 };
120 RegisterHandlers(functions);
121} 35}
122 36
123AppletOE::~AppletOE() = default; 37std::shared_ptr<Applet> AppletOE::GetAppletFromContext(HLERequestContext& ctx) {
124 38 const auto aruid = ctx.GetPID();
125const std::shared_ptr<AppletMessageQueue>& AppletOE::GetMessageQueue() const { 39 return system.GetAppletManager().GetByAppletResourceUserId(aruid);
126 return msg_queue;
127} 40}
128 41
129} // namespace Service::AM 42} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 39eccc4ab..f2ba1c924 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -18,21 +18,19 @@ class Nvnflinger;
18 18
19namespace AM { 19namespace AM {
20 20
21class AppletMessageQueue; 21struct Applet;
22 22
23class AppletOE final : public ServiceFramework<AppletOE> { 23class AppletOE final : public ServiceFramework<AppletOE> {
24public: 24public:
25 explicit AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, 25 explicit AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_);
26 std::shared_ptr<AppletMessageQueue> msg_queue_, Core::System& system_);
27 ~AppletOE() override; 26 ~AppletOE() override;
28 27
29 const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
30
31private: 28private:
32 void OpenApplicationProxy(HLERequestContext& ctx); 29 void OpenApplicationProxy(HLERequestContext& ctx);
33 30
31 std::shared_ptr<Applet> GetAppletFromContext(HLERequestContext& ctx);
32
34 Nvnflinger::Nvnflinger& nvnflinger; 33 Nvnflinger::Nvnflinger& nvnflinger;
35 std::shared_ptr<AppletMessageQueue> msg_queue;
36}; 34};
37 35
38} // namespace AM 36} // namespace AM
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
deleted file mode 100644
index 89d5434af..000000000
--- a/src/core/hle/service/am/applets/applets.cpp
+++ /dev/null
@@ -1,338 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstring>
5
6#include "common/assert.h"
7#include "core/core.h"
8#include "core/frontend/applets/cabinet.h"
9#include "core/frontend/applets/controller.h"
10#include "core/frontend/applets/error.h"
11#include "core/frontend/applets/general_frontend.h"
12#include "core/frontend/applets/mii_edit.h"
13#include "core/frontend/applets/profile_select.h"
14#include "core/frontend/applets/software_keyboard.h"
15#include "core/frontend/applets/web_browser.h"
16#include "core/hle/kernel/k_event.h"
17#include "core/hle/service/am/am.h"
18#include "core/hle/service/am/applet_ae.h"
19#include "core/hle/service/am/applet_oe.h"
20#include "core/hle/service/am/applets/applet_cabinet.h"
21#include "core/hle/service/am/applets/applet_controller.h"
22#include "core/hle/service/am/applets/applet_error.h"
23#include "core/hle/service/am/applets/applet_general_backend.h"
24#include "core/hle/service/am/applets/applet_mii_edit.h"
25#include "core/hle/service/am/applets/applet_profile_select.h"
26#include "core/hle/service/am/applets/applet_software_keyboard.h"
27#include "core/hle/service/am/applets/applet_web_browser.h"
28#include "core/hle/service/am/applets/applets.h"
29#include "core/hle/service/sm/sm.h"
30
31namespace Service::AM::Applets {
32
33AppletDataBroker::AppletDataBroker(Core::System& system_, LibraryAppletMode applet_mode_)
34 : system{system_}, applet_mode{applet_mode_}, service_context{system,
35 "ILibraryAppletAccessor"} {
36 state_changed_event = service_context.CreateEvent("ILibraryAppletAccessor:StateChangedEvent");
37 pop_out_data_event = service_context.CreateEvent("ILibraryAppletAccessor:PopDataOutEvent");
38 pop_interactive_out_data_event =
39 service_context.CreateEvent("ILibraryAppletAccessor:PopInteractiveDataOutEvent");
40}
41
42AppletDataBroker::~AppletDataBroker() {
43 service_context.CloseEvent(state_changed_event);
44 service_context.CloseEvent(pop_out_data_event);
45 service_context.CloseEvent(pop_interactive_out_data_event);
46}
47
48AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() const {
49 std::vector<std::vector<u8>> out_normal;
50
51 for (const auto& storage : in_channel) {
52 out_normal.push_back(storage->GetData());
53 }
54
55 std::vector<std::vector<u8>> out_interactive;
56
57 for (const auto& storage : in_interactive_channel) {
58 out_interactive.push_back(storage->GetData());
59 }
60
61 return {std::move(out_normal), std::move(out_interactive)};
62}
63
64std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
65 if (out_channel.empty())
66 return nullptr;
67
68 auto out = std::move(out_channel.front());
69 out_channel.pop_front();
70 pop_out_data_event->Clear();
71 return out;
72}
73
74std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
75 if (in_channel.empty())
76 return nullptr;
77
78 auto out = std::move(in_channel.front());
79 in_channel.pop_front();
80 return out;
81}
82
83std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
84 if (out_interactive_channel.empty())
85 return nullptr;
86
87 auto out = std::move(out_interactive_channel.front());
88 out_interactive_channel.pop_front();
89 pop_interactive_out_data_event->Clear();
90 return out;
91}
92
93std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
94 if (in_interactive_channel.empty())
95 return nullptr;
96
97 auto out = std::move(in_interactive_channel.front());
98 in_interactive_channel.pop_front();
99 return out;
100}
101
102void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr<IStorage>&& storage) {
103 in_channel.emplace_back(std::move(storage));
104}
105
106void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage) {
107 out_channel.emplace_back(std::move(storage));
108 pop_out_data_event->Signal();
109}
110
111void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage) {
112 in_interactive_channel.emplace_back(std::move(storage));
113}
114
115void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage) {
116 out_interactive_channel.emplace_back(std::move(storage));
117 pop_interactive_out_data_event->Signal();
118}
119
120void AppletDataBroker::SignalStateChanged() {
121 state_changed_event->Signal();
122
123 switch (applet_mode) {
124 case LibraryAppletMode::AllForeground:
125 case LibraryAppletMode::AllForegroundInitiallyHidden: {
126 auto applet_oe = system.ServiceManager().GetService<AppletOE>("appletOE");
127 auto applet_ae = system.ServiceManager().GetService<AppletAE>("appletAE");
128
129 if (applet_oe) {
130 applet_oe->GetMessageQueue()->FocusStateChanged();
131 break;
132 }
133
134 if (applet_ae) {
135 applet_ae->GetMessageQueue()->FocusStateChanged();
136 break;
137 }
138 break;
139 }
140 default:
141 break;
142 }
143}
144
145Kernel::KReadableEvent& AppletDataBroker::GetNormalDataEvent() {
146 return pop_out_data_event->GetReadableEvent();
147}
148
149Kernel::KReadableEvent& AppletDataBroker::GetInteractiveDataEvent() {
150 return pop_interactive_out_data_event->GetReadableEvent();
151}
152
153Kernel::KReadableEvent& AppletDataBroker::GetStateChangedEvent() {
154 return state_changed_event->GetReadableEvent();
155}
156
157Applet::Applet(Core::System& system_, LibraryAppletMode applet_mode_)
158 : broker{system_, applet_mode_}, applet_mode{applet_mode_} {}
159
160Applet::~Applet() = default;
161
162void Applet::Initialize() {
163 const auto common = broker.PopNormalDataToApplet();
164 ASSERT(common != nullptr);
165
166 const auto common_data = common->GetData();
167
168 ASSERT(common_data.size() >= sizeof(CommonArguments));
169 std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments));
170
171 initialized = true;
172}
173
174AppletFrontendSet::AppletFrontendSet() = default;
175
176AppletFrontendSet::AppletFrontendSet(CabinetApplet cabinet_applet,
177 ControllerApplet controller_applet, ErrorApplet error_applet,
178 MiiEdit mii_edit_,
179 ParentalControlsApplet parental_controls_applet,
180 PhotoViewer photo_viewer_, ProfileSelect profile_select_,
181 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
182 : cabinet{std::move(cabinet_applet)}, controller{std::move(controller_applet)},
183 error{std::move(error_applet)}, mii_edit{std::move(mii_edit_)},
184 parental_controls{std::move(parental_controls_applet)},
185 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
186 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
187
188AppletFrontendSet::~AppletFrontendSet() = default;
189
190AppletFrontendSet::AppletFrontendSet(AppletFrontendSet&&) noexcept = default;
191
192AppletFrontendSet& AppletFrontendSet::operator=(AppletFrontendSet&&) noexcept = default;
193
194AppletManager::AppletManager(Core::System& system_) : system{system_} {}
195
196AppletManager::~AppletManager() = default;
197
198const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
199 return frontend;
200}
201
202NFP::CabinetMode AppletManager::GetCabinetMode() const {
203 return cabinet_mode;
204}
205
206AppletId AppletManager::GetCurrentAppletId() const {
207 return current_applet_id;
208}
209
210void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
211 if (set.cabinet != nullptr) {
212 frontend.cabinet = std::move(set.cabinet);
213 }
214
215 if (set.controller != nullptr) {
216 frontend.controller = std::move(set.controller);
217 }
218
219 if (set.error != nullptr) {
220 frontend.error = std::move(set.error);
221 }
222
223 if (set.mii_edit != nullptr) {
224 frontend.mii_edit = std::move(set.mii_edit);
225 }
226
227 if (set.parental_controls != nullptr) {
228 frontend.parental_controls = std::move(set.parental_controls);
229 }
230
231 if (set.photo_viewer != nullptr) {
232 frontend.photo_viewer = std::move(set.photo_viewer);
233 }
234
235 if (set.profile_select != nullptr) {
236 frontend.profile_select = std::move(set.profile_select);
237 }
238
239 if (set.software_keyboard != nullptr) {
240 frontend.software_keyboard = std::move(set.software_keyboard);
241 }
242
243 if (set.web_browser != nullptr) {
244 frontend.web_browser = std::move(set.web_browser);
245 }
246}
247
248void AppletManager::SetCabinetMode(NFP::CabinetMode mode) {
249 cabinet_mode = mode;
250}
251
252void AppletManager::SetCurrentAppletId(AppletId applet_id) {
253 current_applet_id = applet_id;
254}
255
256void AppletManager::SetDefaultAppletFrontendSet() {
257 ClearAll();
258 SetDefaultAppletsIfMissing();
259}
260
261void AppletManager::SetDefaultAppletsIfMissing() {
262 if (frontend.cabinet == nullptr) {
263 frontend.cabinet = std::make_unique<Core::Frontend::DefaultCabinetApplet>();
264 }
265
266 if (frontend.controller == nullptr) {
267 frontend.controller =
268 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
269 }
270
271 if (frontend.error == nullptr) {
272 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
273 }
274
275 if (frontend.mii_edit == nullptr) {
276 frontend.mii_edit = std::make_unique<Core::Frontend::DefaultMiiEditApplet>();
277 }
278
279 if (frontend.parental_controls == nullptr) {
280 frontend.parental_controls =
281 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
282 }
283
284 if (frontend.photo_viewer == nullptr) {
285 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
286 }
287
288 if (frontend.profile_select == nullptr) {
289 frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
290 }
291
292 if (frontend.software_keyboard == nullptr) {
293 frontend.software_keyboard =
294 std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
295 }
296
297 if (frontend.web_browser == nullptr) {
298 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
299 }
300}
301
302void AppletManager::ClearAll() {
303 frontend = {};
304}
305
306std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode mode) const {
307 switch (id) {
308 case AppletId::Auth:
309 return std::make_shared<Auth>(system, mode, *frontend.parental_controls);
310 case AppletId::Cabinet:
311 return std::make_shared<Cabinet>(system, mode, *frontend.cabinet);
312 case AppletId::Controller:
313 return std::make_shared<Controller>(system, mode, *frontend.controller);
314 case AppletId::Error:
315 return std::make_shared<Error>(system, mode, *frontend.error);
316 case AppletId::ProfileSelect:
317 return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select);
318 case AppletId::SoftwareKeyboard:
319 return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard);
320 case AppletId::MiiEdit:
321 return std::make_shared<MiiEdit>(system, mode, *frontend.mii_edit);
322 case AppletId::Web:
323 case AppletId::Shop:
324 case AppletId::OfflineWeb:
325 case AppletId::LoginShare:
326 case AppletId::WebAuth:
327 return std::make_shared<WebBrowser>(system, mode, *frontend.web_browser);
328 case AppletId::PhotoViewer:
329 return std::make_shared<PhotoViewer>(system, mode, *frontend.photo_viewer);
330 default:
331 UNIMPLEMENTED_MSG(
332 "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
333 static_cast<u8>(id));
334 return std::make_shared<StubApplet>(system, id, mode);
335 }
336}
337
338} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
deleted file mode 100644
index 0bf2598b7..000000000
--- a/src/core/hle/service/am/applets/applets.h
+++ /dev/null
@@ -1,289 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <queue>
8
9#include "common/swap.h"
10#include "core/hle/service/kernel_helpers.h"
11
12union Result;
13
14namespace Core {
15class System;
16}
17
18namespace Core::Frontend {
19class CabinetApplet;
20class ControllerApplet;
21class ECommerceApplet;
22class ErrorApplet;
23class MiiEditApplet;
24class ParentalControlsApplet;
25class PhotoViewerApplet;
26class ProfileSelectApplet;
27class SoftwareKeyboardApplet;
28class WebBrowserApplet;
29} // namespace Core::Frontend
30
31namespace Kernel {
32class KernelCore;
33class KEvent;
34class KReadableEvent;
35} // namespace Kernel
36
37namespace Service::NFP {
38enum class CabinetMode : u8;
39} // namespace Service::NFP
40
41namespace Service::AM {
42
43class IStorage;
44
45namespace Applets {
46
47enum class AppletId : u32 {
48 None = 0x00,
49 Application = 0x01,
50 OverlayDisplay = 0x02,
51 QLaunch = 0x03,
52 Starter = 0x04,
53 Auth = 0x0A,
54 Cabinet = 0x0B,
55 Controller = 0x0C,
56 DataErase = 0x0D,
57 Error = 0x0E,
58 NetConnect = 0x0F,
59 ProfileSelect = 0x10,
60 SoftwareKeyboard = 0x11,
61 MiiEdit = 0x12,
62 Web = 0x13,
63 Shop = 0x14,
64 PhotoViewer = 0x15,
65 Settings = 0x16,
66 OfflineWeb = 0x17,
67 LoginShare = 0x18,
68 WebAuth = 0x19,
69 MyPage = 0x1A,
70};
71
72enum class AppletProgramId : u64 {
73 QLaunch = 0x0100000000001000ull,
74 Auth = 0x0100000000001001ull,
75 Cabinet = 0x0100000000001002ull,
76 Controller = 0x0100000000001003ull,
77 DataErase = 0x0100000000001004ull,
78 Error = 0x0100000000001005ull,
79 NetConnect = 0x0100000000001006ull,
80 ProfileSelect = 0x0100000000001007ull,
81 SoftwareKeyboard = 0x0100000000001008ull,
82 MiiEdit = 0x0100000000001009ull,
83 Web = 0x010000000000100Aull,
84 Shop = 0x010000000000100Bull,
85 OverlayDisplay = 0x010000000000100Cull,
86 PhotoViewer = 0x010000000000100Dull,
87 Settings = 0x010000000000100Eull,
88 OfflineWeb = 0x010000000000100Full,
89 LoginShare = 0x0100000000001010ull,
90 WebAuth = 0x0100000000001011ull,
91 Starter = 0x0100000000001012ull,
92 MyPage = 0x0100000000001013ull,
93 MaxProgramId = 0x0100000000001FFFull,
94};
95
96enum class LibraryAppletMode : u32 {
97 AllForeground = 0,
98 Background = 1,
99 NoUI = 2,
100 BackgroundIndirectDisplay = 3,
101 AllForegroundInitiallyHidden = 4,
102};
103
104enum class CommonArgumentVersion : u32 {
105 Version0,
106 Version1,
107 Version2,
108 Version3,
109};
110
111enum class CommonArgumentSize : u32 {
112 Version3 = 0x20,
113};
114
115enum class ThemeColor : u32 {
116 BasicWhite = 0,
117 BasicBlack = 3,
118};
119
120struct CommonArguments {
121 CommonArgumentVersion arguments_version;
122 CommonArgumentSize size;
123 u32 library_version;
124 ThemeColor theme_color;
125 bool play_startup_sound;
126 u64_le system_tick;
127};
128static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
129
130class AppletDataBroker final {
131public:
132 explicit AppletDataBroker(Core::System& system_, LibraryAppletMode applet_mode_);
133 ~AppletDataBroker();
134
135 struct RawChannelData {
136 std::vector<std::vector<u8>> normal;
137 std::vector<std::vector<u8>> interactive;
138 };
139
140 // Retrieves but does not pop the data sent to applet.
141 RawChannelData PeekDataToAppletForDebug() const;
142
143 std::shared_ptr<IStorage> PopNormalDataToGame();
144 std::shared_ptr<IStorage> PopNormalDataToApplet();
145
146 std::shared_ptr<IStorage> PopInteractiveDataToGame();
147 std::shared_ptr<IStorage> PopInteractiveDataToApplet();
148
149 void PushNormalDataFromGame(std::shared_ptr<IStorage>&& storage);
150 void PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage);
151
152 void PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage);
153 void PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage);
154
155 void SignalStateChanged();
156
157 Kernel::KReadableEvent& GetNormalDataEvent();
158 Kernel::KReadableEvent& GetInteractiveDataEvent();
159 Kernel::KReadableEvent& GetStateChangedEvent();
160
161private:
162 Core::System& system;
163 LibraryAppletMode applet_mode;
164
165 KernelHelpers::ServiceContext service_context;
166
167 // Queues are named from applet's perspective
168
169 // PopNormalDataToApplet and PushNormalDataFromGame
170 std::deque<std::shared_ptr<IStorage>> in_channel;
171
172 // PopNormalDataToGame and PushNormalDataFromApplet
173 std::deque<std::shared_ptr<IStorage>> out_channel;
174
175 // PopInteractiveDataToApplet and PushInteractiveDataFromGame
176 std::deque<std::shared_ptr<IStorage>> in_interactive_channel;
177
178 // PopInteractiveDataToGame and PushInteractiveDataFromApplet
179 std::deque<std::shared_ptr<IStorage>> out_interactive_channel;
180
181 Kernel::KEvent* state_changed_event;
182
183 // Signaled on PushNormalDataFromApplet
184 Kernel::KEvent* pop_out_data_event;
185
186 // Signaled on PushInteractiveDataFromApplet
187 Kernel::KEvent* pop_interactive_out_data_event;
188};
189
190class Applet {
191public:
192 explicit Applet(Core::System& system_, LibraryAppletMode applet_mode_);
193 virtual ~Applet();
194
195 virtual void Initialize();
196
197 virtual bool TransactionComplete() const = 0;
198 virtual Result GetStatus() const = 0;
199 virtual void ExecuteInteractive() = 0;
200 virtual void Execute() = 0;
201 virtual Result RequestExit() = 0;
202
203 AppletDataBroker& GetBroker() {
204 return broker;
205 }
206
207 const AppletDataBroker& GetBroker() const {
208 return broker;
209 }
210
211 LibraryAppletMode GetLibraryAppletMode() const {
212 return applet_mode;
213 }
214
215 bool IsInitialized() const {
216 return initialized;
217 }
218
219protected:
220 CommonArguments common_args{};
221 AppletDataBroker broker;
222 LibraryAppletMode applet_mode;
223 bool initialized = false;
224};
225
226struct AppletFrontendSet {
227 using CabinetApplet = std::unique_ptr<Core::Frontend::CabinetApplet>;
228 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
229 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
230 using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
231 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
232 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
233 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
234 using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
235 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
236
237 AppletFrontendSet();
238 AppletFrontendSet(CabinetApplet cabinet_applet, ControllerApplet controller_applet,
239 ErrorApplet error_applet, MiiEdit mii_edit_,
240 ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
241 ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
242 WebBrowser web_browser_);
243 ~AppletFrontendSet();
244
245 AppletFrontendSet(const AppletFrontendSet&) = delete;
246 AppletFrontendSet& operator=(const AppletFrontendSet&) = delete;
247
248 AppletFrontendSet(AppletFrontendSet&&) noexcept;
249 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
250
251 CabinetApplet cabinet;
252 ControllerApplet controller;
253 ErrorApplet error;
254 MiiEdit mii_edit;
255 ParentalControlsApplet parental_controls;
256 PhotoViewer photo_viewer;
257 ProfileSelect profile_select;
258 SoftwareKeyboard software_keyboard;
259 WebBrowser web_browser;
260};
261
262class AppletManager {
263public:
264 explicit AppletManager(Core::System& system_);
265 ~AppletManager();
266
267 const AppletFrontendSet& GetAppletFrontendSet() const;
268 NFP::CabinetMode GetCabinetMode() const;
269 AppletId GetCurrentAppletId() const;
270
271 void SetAppletFrontendSet(AppletFrontendSet set);
272 void SetCabinetMode(NFP::CabinetMode mode);
273 void SetCurrentAppletId(AppletId applet_id);
274 void SetDefaultAppletFrontendSet();
275 void SetDefaultAppletsIfMissing();
276 void ClearAll();
277
278 std::shared_ptr<Applet> GetApplet(AppletId id, LibraryAppletMode mode) const;
279
280private:
281 AppletId current_applet_id{};
282 NFP::CabinetMode cabinet_mode{};
283
284 AppletFrontendSet frontend;
285 Core::System& system;
286};
287
288} // namespace Applets
289} // namespace Service::AM
diff --git a/src/core/hle/service/am/application_creator.cpp b/src/core/hle/service/am/application_creator.cpp
new file mode 100644
index 000000000..79ea045a3
--- /dev/null
+++ b/src/core/hle/service/am/application_creator.cpp
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/application_creator.h"
5#include "core/hle/service/ipc_helpers.h"
6
7namespace Service::AM {
8
9IApplicationCreator::IApplicationCreator(Core::System& system_)
10 : ServiceFramework{system_, "IApplicationCreator"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, nullptr, "CreateApplication"},
14 {1, nullptr, "PopLaunchRequestedApplication"},
15 {10, nullptr, "CreateSystemApplication"},
16 {100, nullptr, "PopFloatingApplicationForDevelopment"},
17 };
18 // clang-format on
19
20 RegisterHandlers(functions);
21}
22
23IApplicationCreator::~IApplicationCreator() = default;
24
25} // namespace Service::AM
diff --git a/src/core/hle/service/am/application_creator.h b/src/core/hle/service/am/application_creator.h
new file mode 100644
index 000000000..375a3c476
--- /dev/null
+++ b/src/core/hle/service/am/application_creator.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10class IApplicationCreator final : public ServiceFramework<IApplicationCreator> {
11public:
12 explicit IApplicationCreator(Core::System& system_);
13 ~IApplicationCreator() override;
14};
15
16} // namespace Service::AM
diff --git a/src/core/hle/service/am/application_functions.cpp b/src/core/hle/service/am/application_functions.cpp
new file mode 100644
index 000000000..51c5be2d1
--- /dev/null
+++ b/src/core/hle/service/am/application_functions.cpp
@@ -0,0 +1,594 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "common/uuid.h"
6#include "core/file_sys/control_metadata.h"
7#include "core/file_sys/patch_manager.h"
8#include "core/file_sys/registered_cache.h"
9#include "core/file_sys/savedata_factory.h"
10#include "core/hle/service/acc/profile_manager.h"
11#include "core/hle/service/am/am_results.h"
12#include "core/hle/service/am/applet.h"
13#include "core/hle/service/am/application_functions.h"
14#include "core/hle/service/am/storage.h"
15#include "core/hle/service/filesystem/filesystem.h"
16#include "core/hle/service/filesystem/save_data_controller.h"
17#include "core/hle/service/ipc_helpers.h"
18#include "core/hle/service/ns/ns.h"
19#include "core/hle/service/sm/sm.h"
20
21namespace Service::AM {
22
23enum class LaunchParameterKind : u32 {
24 UserChannel = 1,
25 AccountPreselectedUser = 2,
26};
27
28IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet_)
29 : ServiceFramework{system_, "IApplicationFunctions"}, applet{std::move(applet_)} {
30 // clang-format off
31 static const FunctionInfo functions[] = {
32 {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
33 {10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
34 {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
35 {12, nullptr, "CreateApplicationAndRequestToStart"},
36 {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
37 {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"},
38 {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"},
39 {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
40 {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
41 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
42 {23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
43 {24, nullptr, "GetLaunchStorageInfoForDebug"},
44 {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
45 {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
46 {27, &IApplicationFunctions::CreateCacheStorage, "CreateCacheStorage"},
47 {28, &IApplicationFunctions::GetSaveDataSizeMax, "GetSaveDataSizeMax"},
48 {29, nullptr, "GetCacheStorageMax"},
49 {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
50 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
51 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
52 {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
53 {34, nullptr, "SelectApplicationLicense"},
54 {35, nullptr, "GetDeviceSaveDataSizeMax"},
55 {36, nullptr, "GetLimitedApplicationLicense"},
56 {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
57 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
58 {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
59 {60, nullptr, "SetMediaPlaybackStateForApplication"},
60 {65, &IApplicationFunctions::IsGamePlayRecordingSupported, "IsGamePlayRecordingSupported"},
61 {66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"},
62 {67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"},
63 {68, nullptr, "RequestFlushGamePlayingMovieForDebug"},
64 {70, nullptr, "RequestToShutdown"},
65 {71, nullptr, "RequestToReboot"},
66 {72, nullptr, "RequestToSleep"},
67 {80, nullptr, "ExitAndRequestToShowThanksMessage"},
68 {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
69 {100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"},
70 {101, &IApplicationFunctions::SetApplicationCopyrightImage, "SetApplicationCopyrightImage"},
71 {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"},
72 {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"},
73 {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"},
74 {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"},
75 {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"},
76 {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"},
77 {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
78 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
79 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
80 {131, nullptr, "SetDelayTimeToAbortOnGpuError"},
81 {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
82 {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"},
83 {150, &IApplicationFunctions::GetNotificationStorageChannelEvent, "GetNotificationStorageChannelEvent"},
84 {151, nullptr, "TryPopFromNotificationStorageChannel"},
85 {160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"},
86 {170, nullptr, "SetHdcpAuthenticationActivated"},
87 {180, nullptr, "GetLaunchRequiredVersion"},
88 {181, nullptr, "UpgradeLaunchRequiredVersion"},
89 {190, nullptr, "SendServerMaintenanceOverlayNotification"},
90 {200, nullptr, "GetLastApplicationExitReason"},
91 {500, nullptr, "StartContinuousRecordingFlushForDebug"},
92 {1000, nullptr, "CreateMovieMaker"},
93 {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
94 };
95 // clang-format on
96
97 RegisterHandlers(functions);
98}
99
100IApplicationFunctions::~IApplicationFunctions() = default;
101
102void IApplicationFunctions::EnableApplicationCrashReport(HLERequestContext& ctx) {
103 LOG_WARNING(Service_AM, "(STUBBED) called");
104
105 std::scoped_lock lk{applet->lock};
106 applet->application_crash_report_enabled = true;
107
108 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(ResultSuccess);
110}
111
112void IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx) {
113 LOG_WARNING(Service_AM, "(STUBBED) called");
114
115 IPC::ResponseBuilder rb{ctx, 2};
116 rb.Push(ResultSuccess);
117}
118
119void IApplicationFunctions::SetApplicationCopyrightImage(HLERequestContext& ctx) {
120 LOG_WARNING(Service_AM, "(STUBBED) called");
121
122 IPC::ResponseBuilder rb{ctx, 2};
123 rb.Push(ResultSuccess);
124}
125
126void IApplicationFunctions::SetApplicationCopyrightVisibility(HLERequestContext& ctx) {
127 IPC::RequestParser rp{ctx};
128 const auto is_visible = rp.Pop<bool>();
129
130 LOG_WARNING(Service_AM, "(STUBBED) called, is_visible={}", is_visible);
131
132 IPC::ResponseBuilder rb{ctx, 2};
133 rb.Push(ResultSuccess);
134}
135
136void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) {
137 LOG_WARNING(Service_AM, "(STUBBED) called");
138
139 std::scoped_lock lk{applet->lock};
140 applet->home_button_long_pressed_blocked = true;
141 applet->home_button_short_pressed_blocked = true;
142
143 IPC::ResponseBuilder rb{ctx, 2};
144 rb.Push(ResultSuccess);
145}
146
147void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) {
148 LOG_WARNING(Service_AM, "(STUBBED) called");
149
150 std::scoped_lock lk{applet->lock};
151 applet->home_button_long_pressed_blocked = false;
152 applet->home_button_short_pressed_blocked = false;
153
154 IPC::ResponseBuilder rb{ctx, 2};
155 rb.Push(ResultSuccess);
156}
157
158void IApplicationFunctions::BeginBlockingHomeButton(HLERequestContext& ctx) {
159 LOG_WARNING(Service_AM, "(STUBBED) called");
160
161 std::scoped_lock lk{applet->lock};
162 applet->home_button_long_pressed_blocked = true;
163 applet->home_button_short_pressed_blocked = true;
164 applet->home_button_double_click_enabled = true;
165
166 IPC::ResponseBuilder rb{ctx, 2};
167 rb.Push(ResultSuccess);
168}
169
170void IApplicationFunctions::EndBlockingHomeButton(HLERequestContext& ctx) {
171 LOG_WARNING(Service_AM, "(STUBBED) called");
172
173 std::scoped_lock lk{applet->lock};
174 applet->home_button_long_pressed_blocked = false;
175 applet->home_button_short_pressed_blocked = false;
176 applet->home_button_double_click_enabled = false;
177
178 IPC::ResponseBuilder rb{ctx, 2};
179 rb.Push(ResultSuccess);
180}
181
182void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) {
183 IPC::RequestParser rp{ctx};
184 const auto kind = rp.PopEnum<LaunchParameterKind>();
185
186 LOG_INFO(Service_AM, "called, kind={:08X}", kind);
187
188 std::scoped_lock lk{applet->lock};
189
190 auto& channel = kind == LaunchParameterKind::UserChannel
191 ? applet->user_channel_launch_parameter
192 : applet->preselected_user_launch_parameter;
193
194 if (channel.empty()) {
195 LOG_WARNING(Service_AM, "Attempted to pop parameter {} but none was found!", kind);
196 IPC::ResponseBuilder rb{ctx, 2};
197 rb.Push(AM::ResultNoDataInChannel);
198 return;
199 }
200
201 auto data = channel.back();
202 channel.pop_back();
203
204 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
205 rb.Push(ResultSuccess);
206 rb.PushIpcInterface<IStorage>(system, std::move(data));
207}
208
209void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) {
210 LOG_WARNING(Service_AM, "(STUBBED) called");
211
212 IPC::ResponseBuilder rb{ctx, 2};
213 rb.Push(ResultSuccess);
214}
215
216void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) {
217 IPC::RequestParser rp{ctx};
218 u128 user_id = rp.PopRaw<u128>();
219
220 LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
221
222 FileSys::SaveDataAttribute attribute{};
223 attribute.title_id = applet->program_id;
224 attribute.user_id = user_id;
225 attribute.type = FileSys::SaveDataType::SaveData;
226
227 FileSys::VirtualDir save_data{};
228 const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
229 &save_data, FileSys::SaveDataSpaceId::NandUser, attribute);
230
231 IPC::ResponseBuilder rb{ctx, 4};
232 rb.Push(res);
233 rb.Push<u64>(0);
234}
235
236void IApplicationFunctions::SetTerminateResult(HLERequestContext& ctx) {
237 // Takes an input u32 Result, no output.
238 // For example, in some cases official apps use this with error 0x2A2 then
239 // uses svcBreak.
240
241 IPC::RequestParser rp{ctx};
242 u32 result = rp.Pop<u32>();
243 LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result);
244
245 std::scoped_lock lk{applet->lock};
246 applet->terminate_result = Result(result);
247
248 IPC::ResponseBuilder rb{ctx, 2};
249 rb.Push(ResultSuccess);
250}
251
252void IApplicationFunctions::GetDisplayVersion(HLERequestContext& ctx) {
253 LOG_DEBUG(Service_AM, "called");
254
255 std::array<u8, 0x10> version_string{};
256
257 const auto res = [this] {
258 const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(),
259 system.GetContentProvider()};
260 auto metadata = pm.GetControlMetadata();
261 if (metadata.first != nullptr) {
262 return metadata;
263 }
264
265 const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id),
266 system.GetFileSystemController(),
267 system.GetContentProvider()};
268 return pm_update.GetControlMetadata();
269 }();
270
271 if (res.first != nullptr) {
272 const auto& version = res.first->GetVersionString();
273 std::copy(version.begin(), version.end(), version_string.begin());
274 } else {
275 static constexpr char default_version[]{"1.0.0"};
276 std::memcpy(version_string.data(), default_version, sizeof(default_version));
277 }
278
279 IPC::ResponseBuilder rb{ctx, 6};
280 rb.Push(ResultSuccess);
281 rb.PushRaw(version_string);
282}
283
284void IApplicationFunctions::GetDesiredLanguage(HLERequestContext& ctx) {
285 // TODO(bunnei): This should be configurable
286 LOG_DEBUG(Service_AM, "called");
287
288 // Get supported languages from NACP, if possible
289 // Default to 0 (all languages supported)
290 u32 supported_languages = 0;
291
292 const auto res = [this] {
293 const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(),
294 system.GetContentProvider()};
295 auto metadata = pm.GetControlMetadata();
296 if (metadata.first != nullptr) {
297 return metadata;
298 }
299
300 const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id),
301 system.GetFileSystemController(),
302 system.GetContentProvider()};
303 return pm_update.GetControlMetadata();
304 }();
305
306 if (res.first != nullptr) {
307 supported_languages = res.first->GetSupportedLanguages();
308 }
309
310 // Call IApplicationManagerInterface implementation.
311 auto& service_manager = system.ServiceManager();
312 auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2");
313 auto app_man = ns_am2->GetApplicationManagerInterface();
314
315 // Get desired application language
316 u8 desired_language{};
317 const auto res_lang =
318 app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages);
319 if (res_lang != ResultSuccess) {
320 IPC::ResponseBuilder rb{ctx, 2};
321 rb.Push(res_lang);
322 return;
323 }
324
325 // Convert to settings language code.
326 u64 language_code{};
327 const auto res_code =
328 app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language);
329 if (res_code != ResultSuccess) {
330 IPC::ResponseBuilder rb{ctx, 2};
331 rb.Push(res_code);
332 return;
333 }
334
335 LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code);
336
337 IPC::ResponseBuilder rb{ctx, 4};
338 rb.Push(ResultSuccess);
339 rb.Push(language_code);
340}
341
342void IApplicationFunctions::IsGamePlayRecordingSupported(HLERequestContext& ctx) {
343 LOG_WARNING(Service_AM, "(STUBBED) called");
344
345 IPC::ResponseBuilder rb{ctx, 3};
346 rb.Push(ResultSuccess);
347 rb.Push(applet->gameplay_recording_supported);
348}
349
350void IApplicationFunctions::InitializeGamePlayRecording(HLERequestContext& ctx) {
351 LOG_WARNING(Service_AM, "(STUBBED) called");
352
353 IPC::ResponseBuilder rb{ctx, 2};
354 rb.Push(ResultSuccess);
355}
356
357void IApplicationFunctions::SetGamePlayRecordingState(HLERequestContext& ctx) {
358 LOG_WARNING(Service_AM, "(STUBBED) called");
359
360 IPC::RequestParser rp{ctx};
361
362 std::scoped_lock lk{applet->lock};
363 applet->gameplay_recording_state = rp.PopRaw<GameplayRecordingState>();
364
365 IPC::ResponseBuilder rb{ctx, 2};
366 rb.Push(ResultSuccess);
367}
368
369void IApplicationFunctions::NotifyRunning(HLERequestContext& ctx) {
370 LOG_WARNING(Service_AM, "(STUBBED) called");
371
372 std::scoped_lock lk{applet->lock};
373 applet->is_running = true;
374
375 IPC::ResponseBuilder rb{ctx, 3};
376 rb.Push(ResultSuccess);
377 rb.Push<u8>(0); // Unknown, seems to be ignored by official processes
378}
379
380void IApplicationFunctions::GetPseudoDeviceId(HLERequestContext& ctx) {
381 LOG_WARNING(Service_AM, "(STUBBED) called");
382
383 IPC::ResponseBuilder rb{ctx, 6};
384 rb.Push(ResultSuccess);
385
386 // Returns a 128-bit UUID
387 rb.Push<u64>(0);
388 rb.Push<u64>(0);
389}
390
391void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) {
392 struct Parameters {
393 FileSys::SaveDataType type;
394 u128 user_id;
395 u64 new_normal_size;
396 u64 new_journal_size;
397 };
398 static_assert(sizeof(Parameters) == 40);
399
400 IPC::RequestParser rp{ctx};
401 const auto [type, user_id, new_normal_size, new_journal_size] = rp.PopRaw<Parameters>();
402
403 LOG_DEBUG(Service_AM,
404 "called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, "
405 "new_journal={:016X}",
406 static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
407
408 system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
409 type, applet->program_id, user_id, {new_normal_size, new_journal_size});
410
411 IPC::ResponseBuilder rb{ctx, 4};
412 rb.Push(ResultSuccess);
413
414 // The following value is used upon failure to help the system recover.
415 // Since we always succeed, this should be 0.
416 rb.Push<u64>(0);
417}
418
419void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) {
420 struct Parameters {
421 FileSys::SaveDataType type;
422 u128 user_id;
423 };
424 static_assert(sizeof(Parameters) == 24);
425
426 IPC::RequestParser rp{ctx};
427 const auto [type, user_id] = rp.PopRaw<Parameters>();
428
429 LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1],
430 user_id[0]);
431
432 const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
433 type, applet->program_id, user_id);
434
435 IPC::ResponseBuilder rb{ctx, 6};
436 rb.Push(ResultSuccess);
437 rb.Push(size.normal);
438 rb.Push(size.journal);
439}
440
441void IApplicationFunctions::CreateCacheStorage(HLERequestContext& ctx) {
442 struct InputParameters {
443 u16 index;
444 s64 size;
445 s64 journal_size;
446 };
447 static_assert(sizeof(InputParameters) == 24);
448
449 struct OutputParameters {
450 u32 storage_target;
451 u64 required_size;
452 };
453 static_assert(sizeof(OutputParameters) == 16);
454
455 IPC::RequestParser rp{ctx};
456 const auto params = rp.PopRaw<InputParameters>();
457
458 LOG_WARNING(Service_AM, "(STUBBED) called with index={}, size={:#x}, journal_size={:#x}",
459 params.index, params.size, params.journal_size);
460
461 const OutputParameters resp{
462 .storage_target = 1,
463 .required_size = 0,
464 };
465
466 IPC::ResponseBuilder rb{ctx, 6};
467 rb.Push(ResultSuccess);
468 rb.PushRaw(resp);
469}
470
471void IApplicationFunctions::GetSaveDataSizeMax(HLERequestContext& ctx) {
472 LOG_WARNING(Service_AM, "(STUBBED) called");
473
474 constexpr u64 size_max_normal = 0xFFFFFFF;
475 constexpr u64 size_max_journal = 0xFFFFFFF;
476
477 IPC::ResponseBuilder rb{ctx, 6};
478 rb.Push(ResultSuccess);
479 rb.Push(size_max_normal);
480 rb.Push(size_max_journal);
481}
482
483void IApplicationFunctions::QueryApplicationPlayStatistics(HLERequestContext& ctx) {
484 LOG_WARNING(Service_AM, "(STUBBED) called");
485
486 IPC::ResponseBuilder rb{ctx, 3};
487 rb.Push(ResultSuccess);
488 rb.Push<u32>(0);
489}
490
491void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx) {
492 LOG_WARNING(Service_AM, "(STUBBED) called");
493
494 IPC::ResponseBuilder rb{ctx, 3};
495 rb.Push(ResultSuccess);
496 rb.Push<u32>(0);
497}
498
499void IApplicationFunctions::ExecuteProgram(HLERequestContext& ctx) {
500 LOG_WARNING(Service_AM, "(STUBBED) called");
501
502 IPC::RequestParser rp{ctx};
503 [[maybe_unused]] const auto unk_1 = rp.Pop<u32>();
504 [[maybe_unused]] const auto unk_2 = rp.Pop<u32>();
505 const auto program_index = rp.Pop<u64>();
506
507 IPC::ResponseBuilder rb{ctx, 2};
508 rb.Push(ResultSuccess);
509
510 // Swap user channel ownership into the system so that it will be preserved
511 system.GetUserChannel().swap(applet->user_channel_launch_parameter);
512 system.ExecuteProgram(program_index);
513}
514
515void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) {
516 LOG_DEBUG(Service_AM, "called");
517
518 applet->user_channel_launch_parameter.clear();
519
520 IPC::ResponseBuilder rb{ctx, 2};
521 rb.Push(ResultSuccess);
522}
523
524void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) {
525 LOG_DEBUG(Service_AM, "called");
526
527 IPC::RequestParser rp{ctx};
528 const auto storage = rp.PopIpcInterface<IStorage>().lock();
529 if (storage) {
530 applet->user_channel_launch_parameter.push_back(storage->GetData());
531 }
532
533 IPC::ResponseBuilder rb{ctx, 2};
534 rb.Push(ResultSuccess);
535}
536
537void IApplicationFunctions::GetPreviousProgramIndex(HLERequestContext& ctx) {
538 LOG_WARNING(Service_AM, "(STUBBED) called");
539
540 IPC::ResponseBuilder rb{ctx, 3};
541 rb.Push(ResultSuccess);
542 rb.Push<s32>(applet->previous_program_index);
543}
544
545void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx) {
546 LOG_WARNING(Service_AM, "(STUBBED) called");
547
548 IPC::ResponseBuilder rb{ctx, 2, 1};
549 rb.Push(ResultSuccess);
550 rb.PushCopyObjects(applet->gpu_error_detected_event.GetHandle());
551}
552
553void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx) {
554 LOG_DEBUG(Service_AM, "called");
555
556 IPC::ResponseBuilder rb{ctx, 2, 1};
557 rb.Push(ResultSuccess);
558 rb.PushCopyObjects(applet->friend_invitation_storage_channel_event.GetHandle());
559}
560
561void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx) {
562 LOG_DEBUG(Service_AM, "(STUBBED) called");
563
564 IPC::ResponseBuilder rb{ctx, 2};
565 rb.Push(AM::ResultNoDataInChannel);
566}
567
568void IApplicationFunctions::GetNotificationStorageChannelEvent(HLERequestContext& ctx) {
569 LOG_DEBUG(Service_AM, "called");
570
571 IPC::ResponseBuilder rb{ctx, 2, 1};
572 rb.Push(ResultSuccess);
573 rb.PushCopyObjects(applet->notification_storage_channel_event.GetHandle());
574}
575
576void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx) {
577 LOG_DEBUG(Service_AM, "called");
578
579 IPC::ResponseBuilder rb{ctx, 2, 1};
580 rb.Push(ResultSuccess);
581 rb.PushCopyObjects(applet->health_warning_disappeared_system_event.GetHandle());
582}
583
584void IApplicationFunctions::PrepareForJit(HLERequestContext& ctx) {
585 LOG_WARNING(Service_AM, "(STUBBED) called");
586
587 std::scoped_lock lk{applet->lock};
588 applet->jit_service_launched = true;
589
590 IPC::ResponseBuilder rb{ctx, 2};
591 rb.Push(ResultSuccess);
592}
593
594} // namespace Service::AM
diff --git a/src/core/hle/service/am/application_functions.h b/src/core/hle/service/am/application_functions.h
new file mode 100644
index 000000000..55eb21d39
--- /dev/null
+++ b/src/core/hle/service/am/application_functions.h
@@ -0,0 +1,58 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h"
8
9namespace Service::AM {
10
11struct Applet;
12
13class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
14public:
15 explicit IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet_);
16 ~IApplicationFunctions() override;
17
18private:
19 void PopLaunchParameter(HLERequestContext& ctx);
20 void CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx);
21 void EnsureSaveData(HLERequestContext& ctx);
22 void SetTerminateResult(HLERequestContext& ctx);
23 void GetDisplayVersion(HLERequestContext& ctx);
24 void GetDesiredLanguage(HLERequestContext& ctx);
25 void IsGamePlayRecordingSupported(HLERequestContext& ctx);
26 void InitializeGamePlayRecording(HLERequestContext& ctx);
27 void SetGamePlayRecordingState(HLERequestContext& ctx);
28 void NotifyRunning(HLERequestContext& ctx);
29 void GetPseudoDeviceId(HLERequestContext& ctx);
30 void ExtendSaveData(HLERequestContext& ctx);
31 void GetSaveDataSize(HLERequestContext& ctx);
32 void CreateCacheStorage(HLERequestContext& ctx);
33 void GetSaveDataSizeMax(HLERequestContext& ctx);
34 void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
35 void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
36 void BeginBlockingHomeButton(HLERequestContext& ctx);
37 void EndBlockingHomeButton(HLERequestContext& ctx);
38 void EnableApplicationCrashReport(HLERequestContext& ctx);
39 void InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx);
40 void SetApplicationCopyrightImage(HLERequestContext& ctx);
41 void SetApplicationCopyrightVisibility(HLERequestContext& ctx);
42 void QueryApplicationPlayStatistics(HLERequestContext& ctx);
43 void QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx);
44 void ExecuteProgram(HLERequestContext& ctx);
45 void ClearUserChannel(HLERequestContext& ctx);
46 void UnpopToUserChannel(HLERequestContext& ctx);
47 void GetPreviousProgramIndex(HLERequestContext& ctx);
48 void GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx);
49 void GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx);
50 void TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx);
51 void GetNotificationStorageChannelEvent(HLERequestContext& ctx);
52 void GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx);
53 void PrepareForJit(HLERequestContext& ctx);
54
55 const std::shared_ptr<Applet> applet;
56};
57
58} // namespace Service::AM
diff --git a/src/core/hle/service/am/application_proxy.cpp b/src/core/hle/service/am/application_proxy.cpp
new file mode 100644
index 000000000..a6fd6d37f
--- /dev/null
+++ b/src/core/hle/service/am/application_proxy.cpp
@@ -0,0 +1,115 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/applet_common_functions.h"
5#include "core/hle/service/am/application_functions.h"
6#include "core/hle/service/am/application_proxy.h"
7#include "core/hle/service/am/audio_controller.h"
8#include "core/hle/service/am/common_state_getter.h"
9#include "core/hle/service/am/debug_functions.h"
10#include "core/hle/service/am/display_controller.h"
11#include "core/hle/service/am/library_applet_creator.h"
12#include "core/hle/service/am/library_applet_self_accessor.h"
13#include "core/hle/service/am/process_winding_controller.h"
14#include "core/hle/service/am/self_controller.h"
15#include "core/hle/service/am/window_controller.h"
16#include "core/hle/service/ipc_helpers.h"
17
18namespace Service::AM {
19
20IApplicationProxy::IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_,
21 std::shared_ptr<Applet> applet_, Core::System& system_)
22 : ServiceFramework{system_, "IApplicationProxy"}, nvnflinger{nvnflinger_}, applet{std::move(
23 applet_)} {
24 // clang-format off
25 static const FunctionInfo functions[] = {
26 {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
27 {1, &IApplicationProxy::GetSelfController, "GetSelfController"},
28 {2, &IApplicationProxy::GetWindowController, "GetWindowController"},
29 {3, &IApplicationProxy::GetAudioController, "GetAudioController"},
30 {4, &IApplicationProxy::GetDisplayController, "GetDisplayController"},
31 {10, &IApplicationProxy::GetProcessWindingController, "GetProcessWindingController"},
32 {11, &IApplicationProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
33 {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"},
34 {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"},
35 };
36 // clang-format on
37
38 RegisterHandlers(functions);
39}
40
41IApplicationProxy::~IApplicationProxy() = default;
42
43void IApplicationProxy::GetAudioController(HLERequestContext& ctx) {
44 LOG_DEBUG(Service_AM, "called");
45
46 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
47 rb.Push(ResultSuccess);
48 rb.PushIpcInterface<IAudioController>(system);
49}
50
51void IApplicationProxy::GetDisplayController(HLERequestContext& ctx) {
52 LOG_DEBUG(Service_AM, "called");
53
54 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
55 rb.Push(ResultSuccess);
56 rb.PushIpcInterface<IDisplayController>(system, applet);
57}
58
59void IApplicationProxy::GetProcessWindingController(HLERequestContext& ctx) {
60 LOG_DEBUG(Service_AM, "called");
61
62 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
63 rb.Push(ResultSuccess);
64 rb.PushIpcInterface<IProcessWindingController>(system, applet);
65}
66
67void IApplicationProxy::GetDebugFunctions(HLERequestContext& ctx) {
68 LOG_DEBUG(Service_AM, "called");
69
70 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
71 rb.Push(ResultSuccess);
72 rb.PushIpcInterface<IDebugFunctions>(system);
73}
74
75void IApplicationProxy::GetWindowController(HLERequestContext& ctx) {
76 LOG_DEBUG(Service_AM, "called");
77
78 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
79 rb.Push(ResultSuccess);
80 rb.PushIpcInterface<IWindowController>(system, applet);
81}
82
83void IApplicationProxy::GetSelfController(HLERequestContext& ctx) {
84 LOG_DEBUG(Service_AM, "called");
85
86 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
87 rb.Push(ResultSuccess);
88 rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger);
89}
90
91void IApplicationProxy::GetCommonStateGetter(HLERequestContext& ctx) {
92 LOG_DEBUG(Service_AM, "called");
93
94 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
95 rb.Push(ResultSuccess);
96 rb.PushIpcInterface<ICommonStateGetter>(system, applet);
97}
98
99void IApplicationProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
100 LOG_DEBUG(Service_AM, "called");
101
102 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
103 rb.Push(ResultSuccess);
104 rb.PushIpcInterface<ILibraryAppletCreator>(system, applet);
105}
106
107void IApplicationProxy::GetApplicationFunctions(HLERequestContext& ctx) {
108 LOG_DEBUG(Service_AM, "called");
109
110 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
111 rb.Push(ResultSuccess);
112 rb.PushIpcInterface<IApplicationFunctions>(system, applet);
113}
114
115} // namespace Service::AM
diff --git a/src/core/hle/service/am/application_proxy.h b/src/core/hle/service/am/application_proxy.h
new file mode 100644
index 000000000..eb98b095c
--- /dev/null
+++ b/src/core/hle/service/am/application_proxy.h
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10struct Applet;
11
12class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
13public:
14 explicit IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_,
15 std::shared_ptr<Applet> msg_queue_, Core::System& system_);
16 ~IApplicationProxy();
17
18private:
19 void GetAudioController(HLERequestContext& ctx);
20 void GetDisplayController(HLERequestContext& ctx);
21 void GetProcessWindingController(HLERequestContext& ctx);
22 void GetDebugFunctions(HLERequestContext& ctx);
23 void GetWindowController(HLERequestContext& ctx);
24 void GetSelfController(HLERequestContext& ctx);
25 void GetCommonStateGetter(HLERequestContext& ctx);
26 void GetLibraryAppletCreator(HLERequestContext& ctx);
27 void GetApplicationFunctions(HLERequestContext& ctx);
28
29 Nvnflinger::Nvnflinger& nvnflinger;
30 std::shared_ptr<Applet> applet;
31};
32
33} // namespace Service::AM
diff --git a/src/core/hle/service/am/audio_controller.cpp b/src/core/hle/service/am/audio_controller.cpp
new file mode 100644
index 000000000..ae75db174
--- /dev/null
+++ b/src/core/hle/service/am/audio_controller.cpp
@@ -0,0 +1,91 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/audio_controller.h"
5#include "core/hle/service/ipc_helpers.h"
6
7namespace Service::AM {
8
9IAudioController::IAudioController(Core::System& system_)
10 : ServiceFramework{system_, "IAudioController"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
14 {1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"},
15 {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"},
16 {3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"},
17 {4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"},
18 };
19 // clang-format on
20
21 RegisterHandlers(functions);
22}
23
24IAudioController::~IAudioController() = default;
25
26void IAudioController::SetExpectedMasterVolume(HLERequestContext& ctx) {
27 IPC::RequestParser rp{ctx};
28 const float main_applet_volume_tmp = rp.Pop<float>();
29 const float library_applet_volume_tmp = rp.Pop<float>();
30
31 LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}",
32 main_applet_volume_tmp, library_applet_volume_tmp);
33
34 // Ensure the volume values remain within the 0-100% range
35 main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
36 library_applet_volume =
37 std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
38
39 IPC::ResponseBuilder rb{ctx, 2};
40 rb.Push(ResultSuccess);
41}
42
43void IAudioController::GetMainAppletExpectedMasterVolume(HLERequestContext& ctx) {
44 LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume);
45 IPC::ResponseBuilder rb{ctx, 3};
46 rb.Push(ResultSuccess);
47 rb.Push(main_applet_volume);
48}
49
50void IAudioController::GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx) {
51 LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume);
52 IPC::ResponseBuilder rb{ctx, 3};
53 rb.Push(ResultSuccess);
54 rb.Push(library_applet_volume);
55}
56
57void IAudioController::ChangeMainAppletMasterVolume(HLERequestContext& ctx) {
58 struct Parameters {
59 float volume;
60 s64 fade_time_ns;
61 };
62 static_assert(sizeof(Parameters) == 16);
63
64 IPC::RequestParser rp{ctx};
65 const auto parameters = rp.PopRaw<Parameters>();
66
67 LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume,
68 parameters.fade_time_ns);
69
70 main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume);
71 fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns};
72
73 IPC::ResponseBuilder rb{ctx, 2};
74 rb.Push(ResultSuccess);
75}
76
77void IAudioController::SetTransparentAudioRate(HLERequestContext& ctx) {
78 IPC::RequestParser rp{ctx};
79 const float transparent_volume_rate_tmp = rp.Pop<float>();
80
81 LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp);
82
83 // Clamp volume range to 0-100%.
84 transparent_volume_rate =
85 std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume);
86
87 IPC::ResponseBuilder rb{ctx, 2};
88 rb.Push(ResultSuccess);
89}
90
91} // namespace Service::AM
diff --git a/src/core/hle/service/am/audio_controller.h b/src/core/hle/service/am/audio_controller.h
new file mode 100644
index 000000000..a47e3bad8
--- /dev/null
+++ b/src/core/hle/service/am/audio_controller.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10class IAudioController final : public ServiceFramework<IAudioController> {
11public:
12 explicit IAudioController(Core::System& system_);
13 ~IAudioController() override;
14
15private:
16 void SetExpectedMasterVolume(HLERequestContext& ctx);
17 void GetMainAppletExpectedMasterVolume(HLERequestContext& ctx);
18 void GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx);
19 void ChangeMainAppletMasterVolume(HLERequestContext& ctx);
20 void SetTransparentAudioRate(HLERequestContext& ctx);
21
22 static constexpr float min_allowed_volume = 0.0f;
23 static constexpr float max_allowed_volume = 1.0f;
24
25 float main_applet_volume{0.25f};
26 float library_applet_volume{max_allowed_volume};
27 float transparent_volume_rate{min_allowed_volume};
28
29 // Volume transition fade time in nanoseconds.
30 // e.g. If the main applet volume was 0% and was changed to 50%
31 // with a fade of 50ns, then over the course of 50ns,
32 // the volume will gradually fade up to 50%
33 std::chrono::nanoseconds fade_time_ns{0};
34};
35
36} // namespace Service::AM
diff --git a/src/core/hle/service/am/common_state_getter.cpp b/src/core/hle/service/am/common_state_getter.cpp
new file mode 100644
index 000000000..937ac0beb
--- /dev/null
+++ b/src/core/hle/service/am/common_state_getter.cpp
@@ -0,0 +1,314 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "core/hle/service/am/am_results.h"
6#include "core/hle/service/am/applet.h"
7#include "core/hle/service/am/common_state_getter.h"
8#include "core/hle/service/am/lock_accessor.h"
9#include "core/hle/service/apm/apm_controller.h"
10#include "core/hle/service/apm/apm_interface.h"
11#include "core/hle/service/ipc_helpers.h"
12#include "core/hle/service/pm/pm.h"
13#include "core/hle/service/sm/sm.h"
14#include "core/hle/service/vi/vi.h"
15
16namespace Service::AM {
17
18ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet_)
19 : ServiceFramework{system_, "ICommonStateGetter"}, applet{std::move(applet_)} {
20 // clang-format off
21 static const FunctionInfo functions[] = {
22 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
23 {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
24 {2, nullptr, "GetThisAppletKind"},
25 {3, nullptr, "AllowToEnterSleep"},
26 {4, nullptr, "DisallowToEnterSleep"},
27 {5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"},
28 {6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"},
29 {7, nullptr, "GetCradleStatus"},
30 {8, &ICommonStateGetter::GetBootMode, "GetBootMode"},
31 {9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"},
32 {10, &ICommonStateGetter::RequestToAcquireSleepLock, "RequestToAcquireSleepLock"},
33 {11, nullptr, "ReleaseSleepLock"},
34 {12, nullptr, "ReleaseSleepLockTransiently"},
35 {13, &ICommonStateGetter::GetAcquiredSleepLockEvent, "GetAcquiredSleepLockEvent"},
36 {14, nullptr, "GetWakeupCount"},
37 {20, nullptr, "PushToGeneralChannel"},
38 {30, nullptr, "GetHomeButtonReaderLockAccessor"},
39 {31, &ICommonStateGetter::GetReaderLockAccessorEx, "GetReaderLockAccessorEx"},
40 {32, nullptr, "GetWriterLockAccessorEx"},
41 {40, nullptr, "GetCradleFwVersion"},
42 {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
43 {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
44 {52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
45 {53, &ICommonStateGetter::BeginVrModeEx, "BeginVrModeEx"},
46 {54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"},
47 {55, nullptr, "IsInControllerFirmwareUpdateSection"},
48 {59, nullptr, "SetVrPositionForDebug"},
49 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
50 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
51 {62, nullptr, "GetHdcpAuthenticationState"},
52 {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
53 {64, nullptr, "SetTvPowerStateMatchingMode"},
54 {65, nullptr, "GetApplicationIdByContentActionName"},
55 {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"},
56 {67, nullptr, "CancelCpuBoostMode"},
57 {68, &ICommonStateGetter::GetBuiltInDisplayType, "GetBuiltInDisplayType"},
58 {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"},
59 {90, nullptr, "SetPerformanceConfigurationChangedNotification"},
60 {91, nullptr, "GetCurrentPerformanceConfiguration"},
61 {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
62 {110, nullptr, "OpenMyGpuErrorHandler"},
63 {120, &ICommonStateGetter::GetAppletLaunchedHistory, "GetAppletLaunchedHistory"},
64 {200, nullptr, "GetOperationModeSystemInfo"},
65 {300, &ICommonStateGetter::GetSettingsPlatformRegion, "GetSettingsPlatformRegion"},
66 {400, nullptr, "ActivateMigrationService"},
67 {401, nullptr, "DeactivateMigrationService"},
68 {500, nullptr, "DisableSleepTillShutdown"},
69 {501, nullptr, "SuppressDisablingSleepTemporarily"},
70 {502, nullptr, "IsSleepEnabled"},
71 {503, nullptr, "IsDisablingSleepSuppressed"},
72 {900, &ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"},
73 };
74 // clang-format on
75
76 RegisterHandlers(functions);
77}
78
79ICommonStateGetter::~ICommonStateGetter() = default;
80
81void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) {
82 LOG_DEBUG(Service_AM, "called");
83
84 IPC::ResponseBuilder rb{ctx, 3};
85 rb.Push(ResultSuccess);
86 rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode
87}
88
89void ICommonStateGetter::GetEventHandle(HLERequestContext& ctx) {
90 LOG_DEBUG(Service_AM, "(STUBBED) called");
91
92 IPC::ResponseBuilder rb{ctx, 2, 1};
93 rb.Push(ResultSuccess);
94 rb.PushCopyObjects(applet->message_queue.GetMessageReceiveEvent());
95}
96
97void ICommonStateGetter::ReceiveMessage(HLERequestContext& ctx) {
98 LOG_DEBUG(Service_AM, "called");
99
100 const auto message = applet->message_queue.PopMessage();
101 IPC::ResponseBuilder rb{ctx, 3};
102
103 if (message == AppletMessageQueue::AppletMessage::None) {
104 LOG_ERROR(Service_AM, "Message queue is empty");
105 rb.Push(AM::ResultNoMessages);
106 rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
107 return;
108 }
109
110 rb.Push(ResultSuccess);
111 rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
112}
113
114void ICommonStateGetter::GetCurrentFocusState(HLERequestContext& ctx) {
115 LOG_DEBUG(Service_AM, "(STUBBED) called");
116
117 std::scoped_lock lk{applet->lock};
118
119 IPC::ResponseBuilder rb{ctx, 3};
120 rb.Push(ResultSuccess);
121 rb.Push(static_cast<u8>(applet->focus_state));
122}
123
124void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) {
125 const bool use_docked_mode{Settings::IsDockedMode()};
126 LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
127
128 IPC::ResponseBuilder rb{ctx, 3};
129 rb.Push(ResultSuccess);
130 rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
131}
132
133void ICommonStateGetter::GetPerformanceMode(HLERequestContext& ctx) {
134 LOG_DEBUG(Service_AM, "called");
135
136 IPC::ResponseBuilder rb{ctx, 3};
137 rb.Push(ResultSuccess);
138 rb.PushEnum(system.GetAPMController().GetCurrentPerformanceMode());
139}
140
141void ICommonStateGetter::RequestToAcquireSleepLock(HLERequestContext& ctx) {
142 LOG_WARNING(Service_AM, "(STUBBED) called");
143
144 // Sleep lock is acquired immediately.
145 applet->sleep_lock_event.Signal();
146
147 IPC::ResponseBuilder rb{ctx, 2};
148 rb.Push(ResultSuccess);
149}
150
151void ICommonStateGetter::GetReaderLockAccessorEx(HLERequestContext& ctx) {
152 IPC::RequestParser rp{ctx};
153 const auto unknown = rp.Pop<u32>();
154
155 LOG_INFO(Service_AM, "called, unknown={}", unknown);
156
157 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
158
159 rb.Push(ResultSuccess);
160 rb.PushIpcInterface<ILockAccessor>(system);
161}
162
163void ICommonStateGetter::GetAcquiredSleepLockEvent(HLERequestContext& ctx) {
164 LOG_WARNING(Service_AM, "called");
165
166 IPC::ResponseBuilder rb{ctx, 2, 1};
167 rb.Push(ResultSuccess);
168 rb.PushCopyObjects(applet->sleep_lock_event.GetHandle());
169}
170
171void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) {
172 LOG_DEBUG(Service_AM, "called");
173
174 std::scoped_lock lk{applet->lock};
175
176 IPC::ResponseBuilder rb{ctx, 3};
177 rb.Push(ResultSuccess);
178 rb.Push(applet->vr_mode_enabled);
179}
180
181void ICommonStateGetter::SetVrModeEnabled(HLERequestContext& ctx) {
182 IPC::RequestParser rp{ctx};
183
184 std::scoped_lock lk{applet->lock};
185 applet->vr_mode_enabled = rp.Pop<bool>();
186 LOG_WARNING(Service_AM, "VR Mode is {}", applet->vr_mode_enabled ? "on" : "off");
187
188 IPC::ResponseBuilder rb{ctx, 2};
189 rb.Push(ResultSuccess);
190}
191
192void ICommonStateGetter::SetLcdBacklighOffEnabled(HLERequestContext& ctx) {
193 IPC::RequestParser rp{ctx};
194 const auto is_lcd_backlight_off_enabled = rp.Pop<bool>();
195
196 LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}",
197 is_lcd_backlight_off_enabled);
198
199 IPC::ResponseBuilder rb{ctx, 2};
200 rb.Push(ResultSuccess);
201}
202
203void ICommonStateGetter::BeginVrModeEx(HLERequestContext& ctx) {
204 LOG_WARNING(Service_AM, "(STUBBED) called");
205
206 std::scoped_lock lk{applet->lock};
207 applet->vr_mode_enabled = true;
208
209 IPC::ResponseBuilder rb{ctx, 2};
210 rb.Push(ResultSuccess);
211}
212
213void ICommonStateGetter::EndVrModeEx(HLERequestContext& ctx) {
214 LOG_WARNING(Service_AM, "(STUBBED) called");
215
216 std::scoped_lock lk{applet->lock};
217 applet->vr_mode_enabled = false;
218
219 IPC::ResponseBuilder rb{ctx, 2};
220 rb.Push(ResultSuccess);
221}
222
223void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx) {
224 LOG_DEBUG(Service_AM, "called");
225
226 IPC::ResponseBuilder rb{ctx, 2, 1};
227 rb.Push(ResultSuccess);
228 rb.PushCopyObjects(applet->message_queue.GetOperationModeChangedEvent());
229}
230
231void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) {
232 LOG_DEBUG(Service_AM, "called");
233
234 IPC::ResponseBuilder rb{ctx, 4};
235 rb.Push(ResultSuccess);
236
237 if (Settings::IsDockedMode()) {
238 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
239 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
240 } else {
241 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth));
242 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight));
243 }
244}
245
246void ICommonStateGetter::SetCpuBoostMode(HLERequestContext& ctx) {
247 LOG_DEBUG(Service_AM, "called, forwarding to APM:SYS");
248
249 const auto& sm = system.ServiceManager();
250 const auto apm_sys = sm.GetService<APM::APM_Sys>("apm:sys");
251 ASSERT(apm_sys != nullptr);
252
253 apm_sys->SetCpuBoostMode(ctx);
254}
255
256void ICommonStateGetter::GetBuiltInDisplayType(HLERequestContext& ctx) {
257 LOG_WARNING(Service_AM, "(STUBBED) called");
258
259 IPC::ResponseBuilder rb{ctx, 3};
260 rb.Push(ResultSuccess);
261 rb.Push(0);
262}
263
264void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx) {
265 IPC::RequestParser rp{ctx};
266 const auto system_button{rp.PopEnum<SystemButtonType>()};
267
268 LOG_WARNING(Service_AM, "(STUBBED) called, system_button={}", system_button);
269
270 IPC::ResponseBuilder rb{ctx, 2};
271 rb.Push(ResultSuccess);
272}
273
274void ICommonStateGetter::GetAppletLaunchedHistory(HLERequestContext& ctx) {
275 LOG_WARNING(Service_AM, "(STUBBED) called");
276
277 std::shared_ptr<Applet> current_applet = applet;
278 std::vector<AppletId> result;
279
280 const size_t count = ctx.GetWriteBufferNumElements<AppletId>();
281 size_t i;
282
283 for (i = 0; i < count && current_applet != nullptr; i++) {
284 result.push_back(current_applet->applet_id);
285 current_applet = current_applet->caller_applet.lock();
286 }
287
288 ctx.WriteBuffer(result);
289
290 IPC::ResponseBuilder rb{ctx, 3};
291 rb.Push(ResultSuccess);
292 rb.Push(static_cast<u32>(i));
293}
294
295void ICommonStateGetter::GetSettingsPlatformRegion(HLERequestContext& ctx) {
296 LOG_WARNING(Service_AM, "(STUBBED) called");
297
298 IPC::ResponseBuilder rb{ctx, 3};
299 rb.Push(ResultSuccess);
300 rb.PushEnum(SysPlatformRegion::Global);
301}
302
303void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(
304 HLERequestContext& ctx) {
305 LOG_WARNING(Service_AM, "(STUBBED) called");
306
307 std::scoped_lock lk{applet->lock};
308 applet->request_exit_to_library_applet_at_execute_next_program_enabled = true;
309
310 IPC::ResponseBuilder rb{ctx, 2};
311 rb.Push(ResultSuccess);
312}
313
314} // namespace Service::AM
diff --git a/src/core/hle/service/am/common_state_getter.h b/src/core/hle/service/am/common_state_getter.h
new file mode 100644
index 000000000..bf652790c
--- /dev/null
+++ b/src/core/hle/service/am/common_state_getter.h
@@ -0,0 +1,77 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h"
8
9#include "core/hle/service/am/applet_message_queue.h"
10
11namespace Service::AM {
12
13struct Applet;
14
15class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
16public:
17 explicit ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet_);
18 ~ICommonStateGetter() override;
19
20private:
21 // This is nn::oe::FocusState
22 enum class FocusState : u8 {
23 InFocus = 1,
24 NotInFocus = 2,
25 Background = 3,
26 };
27
28 // This is nn::oe::OperationMode
29 enum class OperationMode : u8 {
30 Handheld = 0,
31 Docked = 1,
32 };
33
34 // This is nn::am::service::SystemButtonType
35 enum class SystemButtonType {
36 None,
37 HomeButtonShortPressing,
38 HomeButtonLongPressing,
39 PowerButtonShortPressing,
40 PowerButtonLongPressing,
41 ShutdownSystem,
42 CaptureButtonShortPressing,
43 CaptureButtonLongPressing,
44 };
45
46 enum class SysPlatformRegion : s32 {
47 Global = 1,
48 Terra = 2,
49 };
50
51 void GetEventHandle(HLERequestContext& ctx);
52 void ReceiveMessage(HLERequestContext& ctx);
53 void GetCurrentFocusState(HLERequestContext& ctx);
54 void RequestToAcquireSleepLock(HLERequestContext& ctx);
55 void GetAcquiredSleepLockEvent(HLERequestContext& ctx);
56 void GetReaderLockAccessorEx(HLERequestContext& ctx);
57 void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx);
58 void GetOperationMode(HLERequestContext& ctx);
59 void GetPerformanceMode(HLERequestContext& ctx);
60 void GetBootMode(HLERequestContext& ctx);
61 void IsVrModeEnabled(HLERequestContext& ctx);
62 void SetVrModeEnabled(HLERequestContext& ctx);
63 void SetLcdBacklighOffEnabled(HLERequestContext& ctx);
64 void BeginVrModeEx(HLERequestContext& ctx);
65 void EndVrModeEx(HLERequestContext& ctx);
66 void GetDefaultDisplayResolution(HLERequestContext& ctx);
67 void SetCpuBoostMode(HLERequestContext& ctx);
68 void GetBuiltInDisplayType(HLERequestContext& ctx);
69 void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx);
70 void GetAppletLaunchedHistory(HLERequestContext& ctx);
71 void GetSettingsPlatformRegion(HLERequestContext& ctx);
72 void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx);
73
74 const std::shared_ptr<Applet> applet;
75};
76
77} // namespace Service::AM
diff --git a/src/core/hle/service/am/debug_functions.cpp b/src/core/hle/service/am/debug_functions.cpp
new file mode 100644
index 000000000..f80b970f2
--- /dev/null
+++ b/src/core/hle/service/am/debug_functions.cpp
@@ -0,0 +1,44 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/debug_functions.h"
5#include "core/hle/service/ipc_helpers.h"
6
7namespace Service::AM {
8
9IDebugFunctions::IDebugFunctions(Core::System& system_)
10 : ServiceFramework{system_, "IDebugFunctions"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, nullptr, "NotifyMessageToHomeMenuForDebug"},
14 {1, nullptr, "OpenMainApplication"},
15 {10, nullptr, "PerformSystemButtonPressing"},
16 {20, nullptr, "InvalidateTransitionLayer"},
17 {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
18 {31, nullptr, "RequestLaunchApplicationByApplicationLaunchInfoForDebug"},
19 {40, nullptr, "GetAppletResourceUsageInfo"},
20 {50, nullptr, "AddSystemProgramIdAndAppletIdForDebug"},
21 {51, nullptr, "AddOperationConfirmedLibraryAppletIdForDebug"},
22 {100, nullptr, "SetCpuBoostModeForApplet"},
23 {101, nullptr, "CancelCpuBoostModeForApplet"},
24 {110, nullptr, "PushToAppletBoundChannelForDebug"},
25 {111, nullptr, "TryPopFromAppletBoundChannelForDebug"},
26 {120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"},
27 {121, nullptr, "AlarmSettingNotificationDisableAppEventReserve"},
28 {122, nullptr, "AlarmSettingNotificationPushAppEventNotify"},
29 {130, nullptr, "FriendInvitationSetApplicationParameter"},
30 {131, nullptr, "FriendInvitationClearApplicationParameter"},
31 {132, nullptr, "FriendInvitationPushApplicationParameter"},
32 {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"},
33 {200, nullptr, "CreateFloatingLibraryAppletAccepterForDebug"},
34 {300, nullptr, "TerminateAllRunningApplicationsForDebug"},
35 {900, nullptr, "GetGrcProcessLaunchedSystemEvent"},
36 };
37 // clang-format on
38
39 RegisterHandlers(functions);
40}
41
42IDebugFunctions::~IDebugFunctions() = default;
43
44} // namespace Service::AM
diff --git a/src/core/hle/service/am/debug_functions.h b/src/core/hle/service/am/debug_functions.h
new file mode 100644
index 000000000..d55968743
--- /dev/null
+++ b/src/core/hle/service/am/debug_functions.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10class IDebugFunctions final : public ServiceFramework<IDebugFunctions> {
11public:
12 explicit IDebugFunctions(Core::System& system_);
13 ~IDebugFunctions() override;
14};
15
16} // namespace Service::AM
diff --git a/src/core/hle/service/am/display_controller.cpp b/src/core/hle/service/am/display_controller.cpp
new file mode 100644
index 000000000..4d6858348
--- /dev/null
+++ b/src/core/hle/service/am/display_controller.cpp
@@ -0,0 +1,135 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/applet.h"
5#include "core/hle/service/am/display_controller.h"
6#include "core/hle/service/ipc_helpers.h"
7
8namespace Service::AM {
9
10namespace {
11struct OutputParameters {
12 bool was_written;
13 s32 fbshare_layer_index;
14};
15
16static_assert(sizeof(OutputParameters) == 8, "OutputParameters has wrong size");
17} // namespace
18
19IDisplayController::IDisplayController(Core::System& system_, std::shared_ptr<Applet> applet_)
20 : ServiceFramework{system_, "IDisplayController"}, applet(std::move(applet_)) {
21 // clang-format off
22 static const FunctionInfo functions[] = {
23 {0, nullptr, "GetLastForegroundCaptureImage"},
24 {1, nullptr, "UpdateLastForegroundCaptureImage"},
25 {2, nullptr, "GetLastApplicationCaptureImage"},
26 {3, nullptr, "GetCallerAppletCaptureImage"},
27 {4, nullptr, "UpdateCallerAppletCaptureImage"},
28 {5, nullptr, "GetLastForegroundCaptureImageEx"},
29 {6, nullptr, "GetLastApplicationCaptureImageEx"},
30 {7, &IDisplayController::GetCallerAppletCaptureImageEx, "GetCallerAppletCaptureImageEx"},
31 {8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"},
32 {9, nullptr, "CopyBetweenCaptureBuffers"},
33 {10, nullptr, "AcquireLastApplicationCaptureBuffer"},
34 {11, nullptr, "ReleaseLastApplicationCaptureBuffer"},
35 {12, nullptr, "AcquireLastForegroundCaptureBuffer"},
36 {13, nullptr, "ReleaseLastForegroundCaptureBuffer"},
37 {14, nullptr, "AcquireCallerAppletCaptureBuffer"},
38 {15, nullptr, "ReleaseCallerAppletCaptureBuffer"},
39 {16, nullptr, "AcquireLastApplicationCaptureBufferEx"},
40 {17, nullptr, "AcquireLastForegroundCaptureBufferEx"},
41 {18, nullptr, "AcquireCallerAppletCaptureBufferEx"},
42 {20, nullptr, "ClearCaptureBuffer"},
43 {21, nullptr, "ClearAppletTransitionBuffer"},
44 {22, &IDisplayController::AcquireLastApplicationCaptureSharedBuffer, "AcquireLastApplicationCaptureSharedBuffer"},
45 {23, &IDisplayController::ReleaseLastApplicationCaptureSharedBuffer, "ReleaseLastApplicationCaptureSharedBuffer"},
46 {24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"},
47 {25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"},
48 {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"},
49 {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"},
50 {28, nullptr, "TakeScreenShotOfOwnLayerEx"},
51 };
52 // clang-format on
53
54 RegisterHandlers(functions);
55}
56
57IDisplayController::~IDisplayController() = default;
58
59void IDisplayController::GetCallerAppletCaptureImageEx(HLERequestContext& ctx) {
60 LOG_WARNING(Service_AM, "(STUBBED) called");
61
62 OutputParameters params{};
63 const auto res = applet->system_buffer_manager.WriteAppletCaptureBuffer(
64 &params.was_written, &params.fbshare_layer_index);
65
66 IPC::ResponseBuilder rb{ctx, 4};
67 rb.Push(res);
68 rb.PushRaw(params);
69}
70
71void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
72 LOG_WARNING(Service_AM, "(STUBBED) called");
73
74 IPC::ResponseBuilder rb{ctx, 2};
75 rb.Push(ResultSuccess);
76}
77
78void IDisplayController::AcquireLastApplicationCaptureSharedBuffer(HLERequestContext& ctx) {
79 LOG_WARNING(Service_AM, "(STUBBED) called");
80
81 OutputParameters params{};
82 const auto res = applet->system_buffer_manager.WriteAppletCaptureBuffer(
83 &params.was_written, &params.fbshare_layer_index);
84
85 IPC::ResponseBuilder rb{ctx, 4};
86 rb.Push(res);
87 rb.PushRaw(params);
88}
89
90void IDisplayController::ReleaseLastApplicationCaptureSharedBuffer(HLERequestContext& ctx) {
91 LOG_WARNING(Service_AM, "(STUBBED) called");
92
93 IPC::ResponseBuilder rb{ctx, 2};
94 rb.Push(ResultSuccess);
95}
96
97void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
98 LOG_WARNING(Service_AM, "(STUBBED) called");
99
100 OutputParameters params{};
101 const auto res = applet->system_buffer_manager.WriteAppletCaptureBuffer(
102 &params.was_written, &params.fbshare_layer_index);
103
104 IPC::ResponseBuilder rb{ctx, 4};
105 rb.Push(res);
106 rb.PushRaw(params);
107}
108
109void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
110 LOG_WARNING(Service_AM, "(STUBBED) called");
111
112 IPC::ResponseBuilder rb{ctx, 2};
113 rb.Push(ResultSuccess);
114}
115
116void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
117 LOG_WARNING(Service_AM, "(STUBBED) called");
118
119 OutputParameters params{};
120 const auto res = applet->system_buffer_manager.WriteAppletCaptureBuffer(
121 &params.was_written, &params.fbshare_layer_index);
122
123 IPC::ResponseBuilder rb{ctx, 4};
124 rb.Push(res);
125 rb.PushRaw(params);
126}
127
128void IDisplayController::ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
129 LOG_WARNING(Service_AM, "(STUBBED) called");
130
131 IPC::ResponseBuilder rb{ctx, 2};
132 rb.Push(ResultSuccess);
133}
134
135} // namespace Service::AM
diff --git a/src/core/hle/service/am/display_controller.h b/src/core/hle/service/am/display_controller.h
new file mode 100644
index 000000000..75172580c
--- /dev/null
+++ b/src/core/hle/service/am/display_controller.h
@@ -0,0 +1,30 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10struct Applet;
11
12class IDisplayController final : public ServiceFramework<IDisplayController> {
13public:
14 explicit IDisplayController(Core::System& system_, std::shared_ptr<Applet> applet_);
15 ~IDisplayController() override;
16
17private:
18 void GetCallerAppletCaptureImageEx(HLERequestContext& ctx);
19 void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
20 void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
21 void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
22 void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
23 void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
24 void AcquireLastApplicationCaptureSharedBuffer(HLERequestContext& ctx);
25 void ReleaseLastApplicationCaptureSharedBuffer(HLERequestContext& ctx);
26
27 const std::shared_ptr<Applet> applet;
28};
29
30} // namespace Service::AM
diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/frontend/applet_cabinet.cpp
index c2ff444a6..0862c81b6 100644
--- a/src/core/hle/service/am/applets/applet_cabinet.cpp
+++ b/src/core/hle/service/am/frontend/applet_cabinet.cpp
@@ -8,16 +8,17 @@
8#include "core/hle/kernel/k_event.h" 8#include "core/hle/kernel/k_event.h"
9#include "core/hle/kernel/k_readable_event.h" 9#include "core/hle/kernel/k_readable_event.h"
10#include "core/hle/service/am/am.h" 10#include "core/hle/service/am/am.h"
11#include "core/hle/service/am/applets/applet_cabinet.h" 11#include "core/hle/service/am/frontend/applet_cabinet.h"
12#include "core/hle/service/am/storage.h"
12#include "core/hle/service/mii/mii_manager.h" 13#include "core/hle/service/mii/mii_manager.h"
13#include "core/hle/service/nfc/common/device.h" 14#include "core/hle/service/nfc/common/device.h"
14#include "hid_core/hid_core.h" 15#include "hid_core/hid_core.h"
15 16
16namespace Service::AM::Applets { 17namespace Service::AM::Frontend {
17 18
18Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_, 19Cabinet::Cabinet(Core::System& system_, std::shared_ptr<Applet> applet_,
19 const Core::Frontend::CabinetApplet& frontend_) 20 LibraryAppletMode applet_mode_, const Core::Frontend::CabinetApplet& frontend_)
20 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_}, service_context{ 21 : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_}, service_context{
21 system_, 22 system_,
22 "CabinetApplet"} { 23 "CabinetApplet"} {
23 24
@@ -30,7 +31,7 @@ Cabinet::~Cabinet() {
30}; 31};
31 32
32void Cabinet::Initialize() { 33void Cabinet::Initialize() {
33 Applet::Initialize(); 34 FrontendApplet::Initialize();
34 35
35 LOG_INFO(Service_HID, "Initializing Cabinet Applet."); 36 LOG_INFO(Service_HID, "Initializing Cabinet Applet.");
36 37
@@ -41,7 +42,7 @@ void Cabinet::Initialize() {
41 common_args.play_startup_sound, common_args.size, common_args.system_tick, 42 common_args.play_startup_sound, common_args.size, common_args.system_tick,
42 common_args.theme_color); 43 common_args.theme_color);
43 44
44 const auto storage = broker.PopNormalDataToApplet(); 45 std::shared_ptr<IStorage> storage = PopInData();
45 ASSERT(storage != nullptr); 46 ASSERT(storage != nullptr);
46 47
47 const auto applet_input_data = storage->GetData(); 48 const auto applet_input_data = storage->GetData();
@@ -51,10 +52,6 @@ void Cabinet::Initialize() {
51 sizeof(StartParamForAmiiboSettings)); 52 sizeof(StartParamForAmiiboSettings));
52} 53}
53 54
54bool Cabinet::TransactionComplete() const {
55 return is_complete;
56}
57
58Result Cabinet::GetStatus() const { 55Result Cabinet::GetStatus() const {
59 return ResultSuccess; 56 return ResultSuccess;
60} 57}
@@ -160,8 +157,8 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
160 157
161 is_complete = true; 158 is_complete = true;
162 159
163 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); 160 PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
164 broker.SignalStateChanged(); 161 Exit();
165} 162}
166 163
167void Cabinet::Cancel() { 164void Cabinet::Cancel() {
@@ -175,8 +172,8 @@ void Cabinet::Cancel() {
175 172
176 is_complete = true; 173 is_complete = true;
177 174
178 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); 175 PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
179 broker.SignalStateChanged(); 176 Exit();
180} 177}
181 178
182Result Cabinet::RequestExit() { 179Result Cabinet::RequestExit() {
@@ -184,4 +181,4 @@ Result Cabinet::RequestExit() {
184 R_SUCCEED(); 181 R_SUCCEED();
185} 182}
186 183
187} // namespace Service::AM::Applets 184} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_cabinet.h b/src/core/hle/service/am/frontend/applet_cabinet.h
index f498796f7..3a211ed37 100644
--- a/src/core/hle/service/am/applets/applet_cabinet.h
+++ b/src/core/hle/service/am/frontend/applet_cabinet.h
@@ -6,7 +6,7 @@
6#include <array> 6#include <array>
7 7
8#include "core/hle/result.h" 8#include "core/hle/result.h"
9#include "core/hle/service/am/applets/applets.h" 9#include "core/hle/service/am/frontend/applets.h"
10#include "core/hle/service/kernel_helpers.h" 10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/nfp/nfp_types.h" 11#include "core/hle/service/nfp/nfp_types.h"
12 12
@@ -23,7 +23,7 @@ namespace Service::NFC {
23class NfcDevice; 23class NfcDevice;
24} 24}
25 25
26namespace Service::AM::Applets { 26namespace Service::AM::Frontend {
27 27
28enum class CabinetAppletVersion : u32 { 28enum class CabinetAppletVersion : u32 {
29 Version1 = 0x1, 29 Version1 = 0x1,
@@ -84,15 +84,15 @@ static_assert(sizeof(ReturnValueForAmiiboSettings) == 0x188,
84 "ReturnValueForAmiiboSettings is an invalid size"); 84 "ReturnValueForAmiiboSettings is an invalid size");
85#pragma pack(pop) 85#pragma pack(pop)
86 86
87class Cabinet final : public Applet { 87class Cabinet final : public FrontendApplet {
88public: 88public:
89 explicit Cabinet(Core::System& system_, LibraryAppletMode applet_mode_, 89 explicit Cabinet(Core::System& system_, std::shared_ptr<Applet> applet_,
90 LibraryAppletMode applet_mode_,
90 const Core::Frontend::CabinetApplet& frontend_); 91 const Core::Frontend::CabinetApplet& frontend_);
91 ~Cabinet() override; 92 ~Cabinet() override;
92 93
93 void Initialize() override; 94 void Initialize() override;
94 95
95 bool TransactionComplete() const override;
96 Result GetStatus() const override; 96 Result GetStatus() const override;
97 void ExecuteInteractive() override; 97 void ExecuteInteractive() override;
98 void Execute() override; 98 void Execute() override;
@@ -102,7 +102,6 @@ public:
102 102
103private: 103private:
104 const Core::Frontend::CabinetApplet& frontend; 104 const Core::Frontend::CabinetApplet& frontend;
105 Core::System& system;
106 105
107 bool is_complete{false}; 106 bool is_complete{false};
108 std::shared_ptr<Service::NFC::NfcDevice> nfp_device; 107 std::shared_ptr<Service::NFC::NfcDevice> nfp_device;
@@ -111,4 +110,4 @@ private:
111 StartParamForAmiiboSettings applet_input_common{}; 110 StartParamForAmiiboSettings applet_input_common{};
112}; 111};
113 112
114} // namespace Service::AM::Applets 113} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/frontend/applet_controller.cpp
index 0e4d9cc39..bd3e49fc4 100644
--- a/src/core/hle/service/am/applets/applet_controller.cpp
+++ b/src/core/hle/service/am/frontend/applet_controller.cpp
@@ -11,13 +11,14 @@
11#include "core/frontend/applets/controller.h" 11#include "core/frontend/applets/controller.h"
12#include "core/hle/result.h" 12#include "core/hle/result.h"
13#include "core/hle/service/am/am.h" 13#include "core/hle/service/am/am.h"
14#include "core/hle/service/am/applets/applet_controller.h" 14#include "core/hle/service/am/frontend/applet_controller.h"
15#include "core/hle/service/am/storage.h"
15#include "hid_core/frontend/emulated_controller.h" 16#include "hid_core/frontend/emulated_controller.h"
16#include "hid_core/hid_core.h" 17#include "hid_core/hid_core.h"
17#include "hid_core/hid_types.h" 18#include "hid_core/hid_types.h"
18#include "hid_core/resources/npad/npad.h" 19#include "hid_core/resources/npad/npad.h"
19 20
20namespace Service::AM::Applets { 21namespace Service::AM::Frontend {
21 22
22[[maybe_unused]] constexpr Result ResultControllerSupportCanceled{ErrorModule::HID, 3101}; 23[[maybe_unused]] constexpr Result ResultControllerSupportCanceled{ErrorModule::HID, 3101};
23[[maybe_unused]] constexpr Result ResultControllerSupportNotSupportedNpadStyle{ErrorModule::HID, 24[[maybe_unused]] constexpr Result ResultControllerSupportNotSupportedNpadStyle{ErrorModule::HID,
@@ -46,14 +47,15 @@ static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
46 }; 47 };
47} 48}
48 49
49Controller::Controller(Core::System& system_, LibraryAppletMode applet_mode_, 50Controller::Controller(Core::System& system_, std::shared_ptr<Applet> applet_,
51 LibraryAppletMode applet_mode_,
50 const Core::Frontend::ControllerApplet& frontend_) 52 const Core::Frontend::ControllerApplet& frontend_)
51 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} 53 : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}
52 54
53Controller::~Controller() = default; 55Controller::~Controller() = default;
54 56
55void Controller::Initialize() { 57void Controller::Initialize() {
56 Applet::Initialize(); 58 FrontendApplet::Initialize();
57 59
58 LOG_INFO(Service_HID, "Initializing Controller Applet."); 60 LOG_INFO(Service_HID, "Initializing Controller Applet.");
59 61
@@ -66,7 +68,7 @@ void Controller::Initialize() {
66 68
67 controller_applet_version = ControllerAppletVersion{common_args.library_version}; 69 controller_applet_version = ControllerAppletVersion{common_args.library_version};
68 70
69 const auto private_arg_storage = broker.PopNormalDataToApplet(); 71 const std::shared_ptr<IStorage> private_arg_storage = PopInData();
70 ASSERT(private_arg_storage != nullptr); 72 ASSERT(private_arg_storage != nullptr);
71 73
72 const auto& private_arg = private_arg_storage->GetData(); 74 const auto& private_arg = private_arg_storage->GetData();
@@ -116,7 +118,7 @@ void Controller::Initialize() {
116 switch (controller_private_arg.mode) { 118 switch (controller_private_arg.mode) {
117 case ControllerSupportMode::ShowControllerSupport: 119 case ControllerSupportMode::ShowControllerSupport:
118 case ControllerSupportMode::ShowControllerStrapGuide: { 120 case ControllerSupportMode::ShowControllerStrapGuide: {
119 const auto user_arg_storage = broker.PopNormalDataToApplet(); 121 const std::shared_ptr<IStorage> user_arg_storage = PopInData();
120 ASSERT(user_arg_storage != nullptr); 122 ASSERT(user_arg_storage != nullptr);
121 123
122 const auto& user_arg = user_arg_storage->GetData(); 124 const auto& user_arg = user_arg_storage->GetData();
@@ -142,7 +144,7 @@ void Controller::Initialize() {
142 break; 144 break;
143 } 145 }
144 case ControllerSupportMode::ShowControllerFirmwareUpdate: { 146 case ControllerSupportMode::ShowControllerFirmwareUpdate: {
145 const auto update_arg_storage = broker.PopNormalDataToApplet(); 147 const std::shared_ptr<IStorage> update_arg_storage = PopInData();
146 ASSERT(update_arg_storage != nullptr); 148 ASSERT(update_arg_storage != nullptr);
147 149
148 const auto& update_arg = update_arg_storage->GetData(); 150 const auto& update_arg = update_arg_storage->GetData();
@@ -152,7 +154,7 @@ void Controller::Initialize() {
152 break; 154 break;
153 } 155 }
154 case ControllerSupportMode::ShowControllerKeyRemappingForSystem: { 156 case ControllerSupportMode::ShowControllerKeyRemappingForSystem: {
155 const auto remapping_arg_storage = broker.PopNormalDataToApplet(); 157 const std::shared_ptr<IStorage> remapping_arg_storage = PopInData();
156 ASSERT(remapping_arg_storage != nullptr); 158 ASSERT(remapping_arg_storage != nullptr);
157 159
158 const auto& remapping_arg = remapping_arg_storage->GetData(); 160 const auto& remapping_arg = remapping_arg_storage->GetData();
@@ -168,10 +170,6 @@ void Controller::Initialize() {
168 } 170 }
169} 171}
170 172
171bool Controller::TransactionComplete() const {
172 return complete;
173}
174
175Result Controller::GetStatus() const { 173Result Controller::GetStatus() const {
176 return status; 174 return status;
177} 175}
@@ -260,8 +258,9 @@ void Controller::ConfigurationComplete(bool is_success) {
260 complete = true; 258 complete = true;
261 out_data = std::vector<u8>(sizeof(ControllerSupportResultInfo)); 259 out_data = std::vector<u8>(sizeof(ControllerSupportResultInfo));
262 std::memcpy(out_data.data(), &result_info, out_data.size()); 260 std::memcpy(out_data.data(), &result_info, out_data.size());
263 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); 261
264 broker.SignalStateChanged(); 262 PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
263 Exit();
265} 264}
266 265
267Result Controller::RequestExit() { 266Result Controller::RequestExit() {
@@ -269,4 +268,4 @@ Result Controller::RequestExit() {
269 R_SUCCEED(); 268 R_SUCCEED();
270} 269}
271 270
272} // namespace Service::AM::Applets 271} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/frontend/applet_controller.h
index 9f839f3d7..2f219429c 100644
--- a/src/core/hle/service/am/applets/applet_controller.h
+++ b/src/core/hle/service/am/frontend/applet_controller.h
@@ -9,7 +9,7 @@
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
12#include "core/hle/service/am/applets/applets.h" 12#include "core/hle/service/am/frontend/applets.h"
13 13
14namespace Core { 14namespace Core {
15class System; 15class System;
@@ -19,7 +19,7 @@ namespace Core::HID {
19enum class NpadStyleSet : u32; 19enum class NpadStyleSet : u32;
20} 20}
21 21
22namespace Service::AM::Applets { 22namespace Service::AM::Frontend {
23 23
24using IdentificationColor = std::array<u8, 4>; 24using IdentificationColor = std::array<u8, 4>;
25using ExplainText = std::array<char, 0x81>; 25using ExplainText = std::array<char, 0x81>;
@@ -122,15 +122,15 @@ struct ControllerSupportResultInfo {
122static_assert(sizeof(ControllerSupportResultInfo) == 0xC, 122static_assert(sizeof(ControllerSupportResultInfo) == 0xC,
123 "ControllerSupportResultInfo has incorrect size."); 123 "ControllerSupportResultInfo has incorrect size.");
124 124
125class Controller final : public Applet { 125class Controller final : public FrontendApplet {
126public: 126public:
127 explicit Controller(Core::System& system_, LibraryAppletMode applet_mode_, 127 explicit Controller(Core::System& system_, std::shared_ptr<Applet> applet_,
128 LibraryAppletMode applet_mode_,
128 const Core::Frontend::ControllerApplet& frontend_); 129 const Core::Frontend::ControllerApplet& frontend_);
129 ~Controller() override; 130 ~Controller() override;
130 131
131 void Initialize() override; 132 void Initialize() override;
132 133
133 bool TransactionComplete() const override;
134 Result GetStatus() const override; 134 Result GetStatus() const override;
135 void ExecuteInteractive() override; 135 void ExecuteInteractive() override;
136 void Execute() override; 136 void Execute() override;
@@ -140,7 +140,6 @@ public:
140 140
141private: 141private:
142 const Core::Frontend::ControllerApplet& frontend; 142 const Core::Frontend::ControllerApplet& frontend;
143 Core::System& system;
144 143
145 ControllerAppletVersion controller_applet_version; 144 ControllerAppletVersion controller_applet_version;
146 ControllerSupportArgPrivate controller_private_arg; 145 ControllerSupportArgPrivate controller_private_arg;
@@ -154,4 +153,4 @@ private:
154 std::vector<u8> out_data; 153 std::vector<u8> out_data;
155}; 154};
156 155
157} // namespace Service::AM::Applets 156} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/frontend/applet_error.cpp
index 084bc138c..b97a5f3ea 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/frontend/applet_error.cpp
@@ -9,10 +9,11 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/frontend/applets/error.h" 10#include "core/frontend/applets/error.h"
11#include "core/hle/service/am/am.h" 11#include "core/hle/service/am/am.h"
12#include "core/hle/service/am/applets/applet_error.h" 12#include "core/hle/service/am/frontend/applet_error.h"
13#include "core/hle/service/am/storage.h"
13#include "core/reporter.h" 14#include "core/reporter.h"
14 15
15namespace Service::AM::Applets { 16namespace Service::AM::Frontend {
16 17
17struct ErrorCode { 18struct ErrorCode {
18 u32 error_category{}; 19 u32 error_category{};
@@ -103,18 +104,18 @@ Result Decode64BitError(u64 error) {
103 104
104} // Anonymous namespace 105} // Anonymous namespace
105 106
106Error::Error(Core::System& system_, LibraryAppletMode applet_mode_, 107Error::Error(Core::System& system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_,
107 const Core::Frontend::ErrorApplet& frontend_) 108 const Core::Frontend::ErrorApplet& frontend_)
108 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} 109 : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}
109 110
110Error::~Error() = default; 111Error::~Error() = default;
111 112
112void Error::Initialize() { 113void Error::Initialize() {
113 Applet::Initialize(); 114 FrontendApplet::Initialize();
114 args = std::make_unique<ErrorArguments>(); 115 args = std::make_unique<ErrorArguments>();
115 complete = false; 116 complete = false;
116 117
117 const auto storage = broker.PopNormalDataToApplet(); 118 const std::shared_ptr<IStorage> storage = PopInData();
118 ASSERT(storage != nullptr); 119 ASSERT(storage != nullptr);
119 const auto data = storage->GetData(); 120 const auto data = storage->GetData();
120 121
@@ -152,10 +153,6 @@ void Error::Initialize() {
152 } 153 }
153} 154}
154 155
155bool Error::TransactionComplete() const {
156 return complete;
157}
158
159Result Error::GetStatus() const { 156Result Error::GetStatus() const {
160 return ResultSuccess; 157 return ResultSuccess;
161} 158}
@@ -210,8 +207,8 @@ void Error::Execute() {
210 207
211void Error::DisplayCompleted() { 208void Error::DisplayCompleted() {
212 complete = true; 209 complete = true;
213 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>{})); 210 PushOutData(std::make_shared<IStorage>(system, std::vector<u8>(0x1000)));
214 broker.SignalStateChanged(); 211 Exit();
215} 212}
216 213
217Result Error::RequestExit() { 214Result Error::RequestExit() {
@@ -219,4 +216,4 @@ Result Error::RequestExit() {
219 R_SUCCEED(); 216 R_SUCCEED();
220} 217}
221 218
222} // namespace Service::AM::Applets 219} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_error.h b/src/core/hle/service/am/frontend/applet_error.h
index d822a32bb..678bf33fa 100644
--- a/src/core/hle/service/am/applets/applet_error.h
+++ b/src/core/hle/service/am/frontend/applet_error.h
@@ -4,13 +4,13 @@
4#pragma once 4#pragma once
5 5
6#include "core/hle/result.h" 6#include "core/hle/result.h"
7#include "core/hle/service/am/applets/applets.h" 7#include "core/hle/service/am/frontend/applets.h"
8 8
9namespace Core { 9namespace Core {
10class System; 10class System;
11} 11}
12 12
13namespace Service::AM::Applets { 13namespace Service::AM::Frontend {
14 14
15enum class ErrorAppletMode : u8 { 15enum class ErrorAppletMode : u8 {
16 ShowError = 0, 16 ShowError = 0,
@@ -22,15 +22,14 @@ enum class ErrorAppletMode : u8 {
22 ShowUpdateEula = 8, 22 ShowUpdateEula = 8,
23}; 23};
24 24
25class Error final : public Applet { 25class Error final : public FrontendApplet {
26public: 26public:
27 explicit Error(Core::System& system_, LibraryAppletMode applet_mode_, 27 explicit Error(Core::System& system_, std::shared_ptr<Applet> applet_,
28 const Core::Frontend::ErrorApplet& frontend_); 28 LibraryAppletMode applet_mode_, const Core::Frontend::ErrorApplet& frontend_);
29 ~Error() override; 29 ~Error() override;
30 30
31 void Initialize() override; 31 void Initialize() override;
32 32
33 bool TransactionComplete() const override;
34 Result GetStatus() const override; 33 Result GetStatus() const override;
35 void ExecuteInteractive() override; 34 void ExecuteInteractive() override;
36 void Execute() override; 35 void Execute() override;
@@ -47,7 +46,6 @@ private:
47 std::unique_ptr<ErrorArguments> args; 46 std::unique_ptr<ErrorArguments> args;
48 47
49 bool complete = false; 48 bool complete = false;
50 Core::System& system;
51}; 49};
52 50
53} // namespace Service::AM::Applets 51} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/frontend/applet_general.cpp
index c0032f652..3c091a602 100644
--- a/src/core/hle/service/am/applets/applet_general_backend.cpp
+++ b/src/core/hle/service/am/frontend/applet_general.cpp
@@ -5,27 +5,28 @@
5#include "common/hex_util.h" 5#include "common/hex_util.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/frontend/applets/general_frontend.h" 8#include "core/frontend/applets/general.h"
9#include "core/hle/result.h" 9#include "core/hle/result.h"
10#include "core/hle/service/am/am.h" 10#include "core/hle/service/am/am.h"
11#include "core/hle/service/am/applets/applet_general_backend.h" 11#include "core/hle/service/am/applet_data_broker.h"
12#include "core/hle/service/am/frontend/applet_general.h"
13#include "core/hle/service/am/storage.h"
12#include "core/reporter.h" 14#include "core/reporter.h"
13 15
14namespace Service::AM::Applets { 16namespace Service::AM::Frontend {
15 17
16constexpr Result ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; 18constexpr Result ERROR_INVALID_PIN{ErrorModule::PCTL, 221};
17 19
18static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { 20static void LogCurrentStorage(std::shared_ptr<Applet> applet, std::string_view prefix) {
19 std::shared_ptr<IStorage> storage = broker.PopNormalDataToApplet(); 21 std::shared_ptr<IStorage> storage;
20 for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { 22 while (R_SUCCEEDED(applet->caller_applet_broker->GetInData().Pop(&storage))) {
21 const auto data = storage->GetData(); 23 const auto data = storage->GetData();
22 LOG_INFO(Service_AM, 24 LOG_INFO(Service_AM,
23 "called (STUBBED), during {} received normal data with size={:08X}, data={}", 25 "called (STUBBED), during {} received normal data with size={:08X}, data={}",
24 prefix, data.size(), Common::HexToString(data)); 26 prefix, data.size(), Common::HexToString(data));
25 } 27 }
26 28
27 storage = broker.PopInteractiveDataToApplet(); 29 while (R_SUCCEEDED(applet->caller_applet_broker->GetInteractiveInData().Pop(&storage))) {
28 for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) {
29 const auto data = storage->GetData(); 30 const auto data = storage->GetData();
30 LOG_INFO(Service_AM, 31 LOG_INFO(Service_AM,
31 "called (STUBBED), during {} received interactive data with size={:08X}, data={}", 32 "called (STUBBED), during {} received interactive data with size={:08X}, data={}",
@@ -33,17 +34,17 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix)
33 } 34 }
34} 35}
35 36
36Auth::Auth(Core::System& system_, LibraryAppletMode applet_mode_, 37Auth::Auth(Core::System& system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_,
37 Core::Frontend::ParentalControlsApplet& frontend_) 38 Core::Frontend::ParentalControlsApplet& frontend_)
38 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} 39 : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}
39 40
40Auth::~Auth() = default; 41Auth::~Auth() = default;
41 42
42void Auth::Initialize() { 43void Auth::Initialize() {
43 Applet::Initialize(); 44 FrontendApplet::Initialize();
44 complete = false; 45 complete = false;
45 46
46 const auto storage = broker.PopNormalDataToApplet(); 47 const std::shared_ptr<IStorage> storage = PopInData();
47 ASSERT(storage != nullptr); 48 ASSERT(storage != nullptr);
48 const auto data = storage->GetData(); 49 const auto data = storage->GetData();
49 ASSERT(data.size() >= 0xC); 50 ASSERT(data.size() >= 0xC);
@@ -67,10 +68,6 @@ void Auth::Initialize() {
67 arg2 = arg.arg2; 68 arg2 = arg.arg2;
68} 69}
69 70
70bool Auth::TransactionComplete() const {
71 return complete;
72}
73
74Result Auth::GetStatus() const { 71Result Auth::GetStatus() const {
75 return successful ? ResultSuccess : ERROR_INVALID_PIN; 72 return successful ? ResultSuccess : ERROR_INVALID_PIN;
76} 73}
@@ -146,8 +143,8 @@ void Auth::AuthFinished(bool is_successful) {
146 std::vector<u8> out(sizeof(Return)); 143 std::vector<u8> out(sizeof(Return));
147 std::memcpy(out.data(), &return_, sizeof(Return)); 144 std::memcpy(out.data(), &return_, sizeof(Return));
148 145
149 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out))); 146 PushOutData(std::make_shared<IStorage>(system, std::move(out)));
150 broker.SignalStateChanged(); 147 Exit();
151} 148}
152 149
153Result Auth::RequestExit() { 150Result Auth::RequestExit() {
@@ -155,27 +152,24 @@ Result Auth::RequestExit() {
155 R_SUCCEED(); 152 R_SUCCEED();
156} 153}
157 154
158PhotoViewer::PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_, 155PhotoViewer::PhotoViewer(Core::System& system_, std::shared_ptr<Applet> applet_,
156 LibraryAppletMode applet_mode_,
159 const Core::Frontend::PhotoViewerApplet& frontend_) 157 const Core::Frontend::PhotoViewerApplet& frontend_)
160 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} 158 : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}
161 159
162PhotoViewer::~PhotoViewer() = default; 160PhotoViewer::~PhotoViewer() = default;
163 161
164void PhotoViewer::Initialize() { 162void PhotoViewer::Initialize() {
165 Applet::Initialize(); 163 FrontendApplet::Initialize();
166 complete = false; 164 complete = false;
167 165
168 const auto storage = broker.PopNormalDataToApplet(); 166 const std::shared_ptr<IStorage> storage = PopInData();
169 ASSERT(storage != nullptr); 167 ASSERT(storage != nullptr);
170 const auto data = storage->GetData(); 168 const auto data = storage->GetData();
171 ASSERT(!data.empty()); 169 ASSERT(!data.empty());
172 mode = static_cast<PhotoViewerAppletMode>(data[0]); 170 mode = static_cast<PhotoViewerAppletMode>(data[0]);
173} 171}
174 172
175bool PhotoViewer::TransactionComplete() const {
176 return complete;
177}
178
179Result PhotoViewer::GetStatus() const { 173Result PhotoViewer::GetStatus() const {
180 return ResultSuccess; 174 return ResultSuccess;
181} 175}
@@ -203,8 +197,8 @@ void PhotoViewer::Execute() {
203} 197}
204 198
205void PhotoViewer::ViewFinished() { 199void PhotoViewer::ViewFinished() {
206 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>{})); 200 PushOutData(std::make_shared<IStorage>(system, std::vector<u8>{}));
207 broker.SignalStateChanged(); 201 Exit();
208} 202}
209 203
210Result PhotoViewer::RequestExit() { 204Result PhotoViewer::RequestExit() {
@@ -212,27 +206,17 @@ Result PhotoViewer::RequestExit() {
212 R_SUCCEED(); 206 R_SUCCEED();
213} 207}
214 208
215StubApplet::StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_) 209StubApplet::StubApplet(Core::System& system_, std::shared_ptr<Applet> applet_, AppletId id_,
216 : Applet{system_, applet_mode_}, id{id_}, system{system_} {} 210 LibraryAppletMode applet_mode_)
211 : FrontendApplet{system_, applet_, applet_mode_}, id{id_} {}
217 212
218StubApplet::~StubApplet() = default; 213StubApplet::~StubApplet() = default;
219 214
220void StubApplet::Initialize() { 215void StubApplet::Initialize() {
221 LOG_WARNING(Service_AM, "called (STUBBED)"); 216 LOG_WARNING(Service_AM, "called (STUBBED)");
222 Applet::Initialize(); 217 FrontendApplet::Initialize();
223 218
224 const auto data = broker.PeekDataToAppletForDebug(); 219 LogCurrentStorage(applet.lock(), "Initialize");
225 system.GetReporter().SaveUnimplementedAppletReport(
226 static_cast<u32>(id), static_cast<u32>(common_args.arguments_version),
227 common_args.library_version, static_cast<u32>(common_args.theme_color),
228 common_args.play_startup_sound, common_args.system_tick, data.normal, data.interactive);
229
230 LogCurrentStorage(broker, "Initialize");
231}
232
233bool StubApplet::TransactionComplete() const {
234 LOG_WARNING(Service_AM, "called (STUBBED)");
235 return true;
236} 220}
237 221
238Result StubApplet::GetStatus() const { 222Result StubApplet::GetStatus() const {
@@ -242,22 +226,20 @@ Result StubApplet::GetStatus() const {
242 226
243void StubApplet::ExecuteInteractive() { 227void StubApplet::ExecuteInteractive() {
244 LOG_WARNING(Service_AM, "called (STUBBED)"); 228 LOG_WARNING(Service_AM, "called (STUBBED)");
245 LogCurrentStorage(broker, "ExecuteInteractive"); 229 LogCurrentStorage(applet.lock(), "ExecuteInteractive");
246 230
247 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>(0x1000))); 231 PushOutData(std::make_shared<IStorage>(system, std::vector<u8>(0x1000)));
248 broker.PushInteractiveDataFromApplet( 232 PushInteractiveOutData(std::make_shared<IStorage>(system, std::vector<u8>(0x1000)));
249 std::make_shared<IStorage>(system, std::vector<u8>(0x1000))); 233 Exit();
250 broker.SignalStateChanged();
251} 234}
252 235
253void StubApplet::Execute() { 236void StubApplet::Execute() {
254 LOG_WARNING(Service_AM, "called (STUBBED)"); 237 LOG_WARNING(Service_AM, "called (STUBBED)");
255 LogCurrentStorage(broker, "Execute"); 238 LogCurrentStorage(applet.lock(), "Execute");
256 239
257 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>(0x1000))); 240 PushOutData(std::make_shared<IStorage>(system, std::vector<u8>(0x1000)));
258 broker.PushInteractiveDataFromApplet( 241 PushInteractiveOutData(std::make_shared<IStorage>(system, std::vector<u8>(0x1000)));
259 std::make_shared<IStorage>(system, std::vector<u8>(0x1000))); 242 Exit();
260 broker.SignalStateChanged();
261} 243}
262 244
263Result StubApplet::RequestExit() { 245Result StubApplet::RequestExit() {
@@ -265,4 +247,4 @@ Result StubApplet::RequestExit() {
265 R_SUCCEED(); 247 R_SUCCEED();
266} 248}
267 249
268} // namespace Service::AM::Applets 250} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_general_backend.h b/src/core/hle/service/am/frontend/applet_general.h
index 34ecaebb9..eaa7ae25f 100644
--- a/src/core/hle/service/am/applets/applet_general_backend.h
+++ b/src/core/hle/service/am/frontend/applet_general.h
@@ -3,13 +3,13 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/am/applets/applets.h" 6#include "core/hle/service/am/frontend/applets.h"
7 7
8namespace Core { 8namespace Core {
9class System; 9class System;
10} 10}
11 11
12namespace Service::AM::Applets { 12namespace Service::AM::Frontend {
13 13
14enum class AuthAppletType : u32 { 14enum class AuthAppletType : u32 {
15 ShowParentalAuthentication, 15 ShowParentalAuthentication,
@@ -17,14 +17,14 @@ enum class AuthAppletType : u32 {
17 ChangeParentalPasscode, 17 ChangeParentalPasscode,
18}; 18};
19 19
20class Auth final : public Applet { 20class Auth final : public FrontendApplet {
21public: 21public:
22 explicit Auth(Core::System& system_, LibraryAppletMode applet_mode_, 22 explicit Auth(Core::System& system_, std::shared_ptr<Applet> applet_,
23 LibraryAppletMode applet_mode_,
23 Core::Frontend::ParentalControlsApplet& frontend_); 24 Core::Frontend::ParentalControlsApplet& frontend_);
24 ~Auth() override; 25 ~Auth() override;
25 26
26 void Initialize() override; 27 void Initialize() override;
27 bool TransactionComplete() const override;
28 Result GetStatus() const override; 28 Result GetStatus() const override;
29 void ExecuteInteractive() override; 29 void ExecuteInteractive() override;
30 void Execute() override; 30 void Execute() override;
@@ -34,7 +34,6 @@ public:
34 34
35private: 35private:
36 Core::Frontend::ParentalControlsApplet& frontend; 36 Core::Frontend::ParentalControlsApplet& frontend;
37 Core::System& system;
38 bool complete = false; 37 bool complete = false;
39 bool successful = false; 38 bool successful = false;
40 39
@@ -49,14 +48,14 @@ enum class PhotoViewerAppletMode : u8 {
49 AllApps = 1, 48 AllApps = 1,
50}; 49};
51 50
52class PhotoViewer final : public Applet { 51class PhotoViewer final : public FrontendApplet {
53public: 52public:
54 explicit PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_, 53 explicit PhotoViewer(Core::System& system_, std::shared_ptr<Applet> applet_,
54 LibraryAppletMode applet_mode_,
55 const Core::Frontend::PhotoViewerApplet& frontend_); 55 const Core::Frontend::PhotoViewerApplet& frontend_);
56 ~PhotoViewer() override; 56 ~PhotoViewer() override;
57 57
58 void Initialize() override; 58 void Initialize() override;
59 bool TransactionComplete() const override;
60 Result GetStatus() const override; 59 Result GetStatus() const override;
61 void ExecuteInteractive() override; 60 void ExecuteInteractive() override;
62 void Execute() override; 61 void Execute() override;
@@ -68,17 +67,16 @@ private:
68 const Core::Frontend::PhotoViewerApplet& frontend; 67 const Core::Frontend::PhotoViewerApplet& frontend;
69 bool complete = false; 68 bool complete = false;
70 PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp; 69 PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp;
71 Core::System& system;
72}; 70};
73 71
74class StubApplet final : public Applet { 72class StubApplet final : public FrontendApplet {
75public: 73public:
76 explicit StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_); 74 explicit StubApplet(Core::System& system_, std::shared_ptr<Applet> applet_, AppletId id_,
75 LibraryAppletMode applet_mode_);
77 ~StubApplet() override; 76 ~StubApplet() override;
78 77
79 void Initialize() override; 78 void Initialize() override;
80 79
81 bool TransactionComplete() const override;
82 Result GetStatus() const override; 80 Result GetStatus() const override;
83 void ExecuteInteractive() override; 81 void ExecuteInteractive() override;
84 void Execute() override; 82 void Execute() override;
@@ -86,7 +84,6 @@ public:
86 84
87private: 85private:
88 AppletId id; 86 AppletId id;
89 Core::System& system;
90}; 87};
91 88
92} // namespace Service::AM::Applets 89} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/frontend/applet_mii_edit.cpp
index e83e931c5..e3d19fb3d 100644
--- a/src/core/hle/service/am/applets/applet_mii_edit.cpp
+++ b/src/core/hle/service/am/frontend/applet_mii_edit.cpp
@@ -6,16 +6,17 @@
6#include "core/core.h" 6#include "core/core.h"
7#include "core/frontend/applets/mii_edit.h" 7#include "core/frontend/applets/mii_edit.h"
8#include "core/hle/service/am/am.h" 8#include "core/hle/service/am/am.h"
9#include "core/hle/service/am/applets/applet_mii_edit.h" 9#include "core/hle/service/am/frontend/applet_mii_edit.h"
10#include "core/hle/service/am/storage.h"
10#include "core/hle/service/mii/mii.h" 11#include "core/hle/service/mii/mii.h"
11#include "core/hle/service/mii/mii_manager.h" 12#include "core/hle/service/mii/mii_manager.h"
12#include "core/hle/service/sm/sm.h" 13#include "core/hle/service/sm/sm.h"
13 14
14namespace Service::AM::Applets { 15namespace Service::AM::Frontend {
15 16
16MiiEdit::MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_, 17MiiEdit::MiiEdit(Core::System& system_, std::shared_ptr<Applet> applet_,
17 const Core::Frontend::MiiEditApplet& frontend_) 18 LibraryAppletMode applet_mode_, const Core::Frontend::MiiEditApplet& frontend_)
18 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} 19 : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}
19 20
20MiiEdit::~MiiEdit() = default; 21MiiEdit::~MiiEdit() = default;
21 22
@@ -24,7 +25,7 @@ void MiiEdit::Initialize() {
24 // Instead, it is initialized by an AppletInput storage with size 0x100 bytes. 25 // Instead, it is initialized by an AppletInput storage with size 0x100 bytes.
25 // Do NOT call Applet::Initialize() here. 26 // Do NOT call Applet::Initialize() here.
26 27
27 const auto storage = broker.PopNormalDataToApplet(); 28 const std::shared_ptr<IStorage> storage = PopInData();
28 ASSERT(storage != nullptr); 29 ASSERT(storage != nullptr);
29 30
30 const auto applet_input_data = storage->GetData(); 31 const auto applet_input_data = storage->GetData();
@@ -66,10 +67,6 @@ void MiiEdit::Initialize() {
66 manager->Initialize(metadata); 67 manager->Initialize(metadata);
67} 68}
68 69
69bool MiiEdit::TransactionComplete() const {
70 return is_complete;
71}
72
73Result MiiEdit::GetStatus() const { 70Result MiiEdit::GetStatus() const {
74 return ResultSuccess; 71 return ResultSuccess;
75} 72}
@@ -152,8 +149,8 @@ void MiiEdit::MiiEditOutput(MiiEditResult result, s32 index) {
152 149
153 is_complete = true; 150 is_complete = true;
154 151
155 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); 152 PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
156 broker.SignalStateChanged(); 153 Exit();
157} 154}
158 155
159void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result, 156void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result,
@@ -168,8 +165,8 @@ void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result,
168 165
169 is_complete = true; 166 is_complete = true;
170 167
171 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); 168 PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
172 broker.SignalStateChanged(); 169 Exit();
173} 170}
174 171
175Result MiiEdit::RequestExit() { 172Result MiiEdit::RequestExit() {
@@ -177,4 +174,4 @@ Result MiiEdit::RequestExit() {
177 R_SUCCEED(); 174 R_SUCCEED();
178} 175}
179 176
180} // namespace Service::AM::Applets 177} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.h b/src/core/hle/service/am/frontend/applet_mii_edit.h
index 7ff34af49..5db792f7d 100644
--- a/src/core/hle/service/am/applets/applet_mii_edit.h
+++ b/src/core/hle/service/am/frontend/applet_mii_edit.h
@@ -4,8 +4,8 @@
4#pragma once 4#pragma once
5 5
6#include "core/hle/result.h" 6#include "core/hle/result.h"
7#include "core/hle/service/am/applets/applet_mii_edit_types.h" 7#include "core/hle/service/am/frontend/applet_mii_edit_types.h"
8#include "core/hle/service/am/applets/applets.h" 8#include "core/hle/service/am/frontend/applets.h"
9 9
10namespace Core { 10namespace Core {
11class System; 11class System;
@@ -16,17 +16,17 @@ struct DatabaseSessionMetadata;
16class MiiManager; 16class MiiManager;
17} // namespace Service::Mii 17} // namespace Service::Mii
18 18
19namespace Service::AM::Applets { 19namespace Service::AM::Frontend {
20 20
21class MiiEdit final : public Applet { 21class MiiEdit final : public FrontendApplet {
22public: 22public:
23 explicit MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_, 23 explicit MiiEdit(Core::System& system_, std::shared_ptr<Applet> applet_,
24 LibraryAppletMode applet_mode_,
24 const Core::Frontend::MiiEditApplet& frontend_); 25 const Core::Frontend::MiiEditApplet& frontend_);
25 ~MiiEdit() override; 26 ~MiiEdit() override;
26 27
27 void Initialize() override; 28 void Initialize() override;
28 29
29 bool TransactionComplete() const override;
30 Result GetStatus() const override; 30 Result GetStatus() const override;
31 void ExecuteInteractive() override; 31 void ExecuteInteractive() override;
32 void Execute() override; 32 void Execute() override;
@@ -38,7 +38,6 @@ public:
38 38
39private: 39private:
40 const Core::Frontend::MiiEditApplet& frontend; 40 const Core::Frontend::MiiEditApplet& frontend;
41 Core::System& system;
42 41
43 MiiEditAppletInputCommon applet_input_common{}; 42 MiiEditAppletInputCommon applet_input_common{};
44 MiiEditAppletInputV3 applet_input_v3{}; 43 MiiEditAppletInputV3 applet_input_v3{};
@@ -49,4 +48,4 @@ private:
49 Mii::DatabaseSessionMetadata metadata{}; 48 Mii::DatabaseSessionMetadata metadata{};
50}; 49};
51 50
52} // namespace Service::AM::Applets 51} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_mii_edit_types.h b/src/core/hle/service/am/frontend/applet_mii_edit_types.h
index f3d764073..23d9d7a69 100644
--- a/src/core/hle/service/am/applets/applet_mii_edit_types.h
+++ b/src/core/hle/service/am/frontend/applet_mii_edit_types.h
@@ -10,7 +10,7 @@
10#include "common/uuid.h" 10#include "common/uuid.h"
11#include "core/hle/service/mii/types/char_info.h" 11#include "core/hle/service/mii/types/char_info.h"
12 12
13namespace Service::AM::Applets { 13namespace Service::AM::Frontend {
14 14
15enum class MiiEditAppletVersion : s32 { 15enum class MiiEditAppletVersion : s32 {
16 Version3 = 0x3, // 1.0.0 - 10.1.1 16 Version3 = 0x3, // 1.0.0 - 10.1.1
@@ -80,4 +80,4 @@ struct MiiEditAppletOutputForCharInfoEditing {
80static_assert(sizeof(MiiEditAppletOutputForCharInfoEditing) == 0x80, 80static_assert(sizeof(MiiEditAppletOutputForCharInfoEditing) == 0x80,
81 "MiiEditAppletOutputForCharInfoEditing has incorrect size."); 81 "MiiEditAppletOutputForCharInfoEditing has incorrect size.");
82 82
83} // namespace Service::AM::Applets 83} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_profile_select.cpp b/src/core/hle/service/am/frontend/applet_profile_select.cpp
index 89cb323e9..efb4053b8 100644
--- a/src/core/hle/service/am/applets/applet_profile_select.cpp
+++ b/src/core/hle/service/am/frontend/applet_profile_select.cpp
@@ -9,13 +9,15 @@
9#include "core/frontend/applets/profile_select.h" 9#include "core/frontend/applets/profile_select.h"
10#include "core/hle/service/acc/errors.h" 10#include "core/hle/service/acc/errors.h"
11#include "core/hle/service/am/am.h" 11#include "core/hle/service/am/am.h"
12#include "core/hle/service/am/applets/applet_profile_select.h" 12#include "core/hle/service/am/frontend/applet_profile_select.h"
13#include "core/hle/service/am/storage.h"
13 14
14namespace Service::AM::Applets { 15namespace Service::AM::Frontend {
15 16
16ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, 17ProfileSelect::ProfileSelect(Core::System& system_, std::shared_ptr<Applet> applet_,
18 LibraryAppletMode applet_mode_,
17 const Core::Frontend::ProfileSelectApplet& frontend_) 19 const Core::Frontend::ProfileSelectApplet& frontend_)
18 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} 20 : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}
19 21
20ProfileSelect::~ProfileSelect() = default; 22ProfileSelect::~ProfileSelect() = default;
21 23
@@ -24,10 +26,10 @@ void ProfileSelect::Initialize() {
24 status = ResultSuccess; 26 status = ResultSuccess;
25 final_data.clear(); 27 final_data.clear();
26 28
27 Applet::Initialize(); 29 FrontendApplet::Initialize();
28 profile_select_version = ProfileSelectAppletVersion{common_args.library_version}; 30 profile_select_version = ProfileSelectAppletVersion{common_args.library_version};
29 31
30 const auto user_config_storage = broker.PopNormalDataToApplet(); 32 const std::shared_ptr<IStorage> user_config_storage = PopInData();
31 ASSERT(user_config_storage != nullptr); 33 ASSERT(user_config_storage != nullptr);
32 const auto& user_config = user_config_storage->GetData(); 34 const auto& user_config = user_config_storage->GetData();
33 35
@@ -50,10 +52,6 @@ void ProfileSelect::Initialize() {
50 } 52 }
51} 53}
52 54
53bool ProfileSelect::TransactionComplete() const {
54 return complete;
55}
56
57Result ProfileSelect::GetStatus() const { 55Result ProfileSelect::GetStatus() const {
58 return status; 56 return status;
59} 57}
@@ -64,7 +62,8 @@ void ProfileSelect::ExecuteInteractive() {
64 62
65void ProfileSelect::Execute() { 63void ProfileSelect::Execute() {
66 if (complete) { 64 if (complete) {
67 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(final_data))); 65 PushOutData(std::make_shared<IStorage>(system, std::move(final_data)));
66 Exit();
68 return; 67 return;
69 } 68 }
70 69
@@ -111,8 +110,9 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
111 110
112 final_data = std::vector<u8>(sizeof(UiReturnArg)); 111 final_data = std::vector<u8>(sizeof(UiReturnArg));
113 std::memcpy(final_data.data(), &output, final_data.size()); 112 std::memcpy(final_data.data(), &output, final_data.size());
114 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(final_data))); 113
115 broker.SignalStateChanged(); 114 PushOutData(std::make_shared<IStorage>(system, std::move(final_data)));
115 Exit();
116} 116}
117 117
118Result ProfileSelect::RequestExit() { 118Result ProfileSelect::RequestExit() {
@@ -120,4 +120,4 @@ Result ProfileSelect::RequestExit() {
120 R_SUCCEED(); 120 R_SUCCEED();
121} 121}
122 122
123} // namespace Service::AM::Applets 123} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_profile_select.h b/src/core/hle/service/am/frontend/applet_profile_select.h
index 673eed516..674e7afe1 100644
--- a/src/core/hle/service/am/applets/applet_profile_select.h
+++ b/src/core/hle/service/am/frontend/applet_profile_select.h
@@ -8,13 +8,13 @@
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/uuid.h" 9#include "common/uuid.h"
10#include "core/hle/result.h" 10#include "core/hle/result.h"
11#include "core/hle/service/am/applets/applets.h" 11#include "core/hle/service/am/frontend/applets.h"
12 12
13namespace Core { 13namespace Core {
14class System; 14class System;
15} 15}
16 16
17namespace Service::AM::Applets { 17namespace Service::AM::Frontend {
18 18
19enum class ProfileSelectAppletVersion : u32 { 19enum class ProfileSelectAppletVersion : u32 {
20 Version1 = 0x1, // 1.0.0+ 20 Version1 = 0x1, // 1.0.0+
@@ -111,15 +111,15 @@ struct UiReturnArg {
111}; 111};
112static_assert(sizeof(UiReturnArg) == 0x18, "UiReturnArg has incorrect size."); 112static_assert(sizeof(UiReturnArg) == 0x18, "UiReturnArg has incorrect size.");
113 113
114class ProfileSelect final : public Applet { 114class ProfileSelect final : public FrontendApplet {
115public: 115public:
116 explicit ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, 116 explicit ProfileSelect(Core::System& system_, std::shared_ptr<Applet> applet_,
117 LibraryAppletMode applet_mode_,
117 const Core::Frontend::ProfileSelectApplet& frontend_); 118 const Core::Frontend::ProfileSelectApplet& frontend_);
118 ~ProfileSelect() override; 119 ~ProfileSelect() override;
119 120
120 void Initialize() override; 121 void Initialize() override;
121 122
122 bool TransactionComplete() const override;
123 Result GetStatus() const override; 123 Result GetStatus() const override;
124 void ExecuteInteractive() override; 124 void ExecuteInteractive() override;
125 void Execute() override; 125 void Execute() override;
@@ -137,7 +137,6 @@ private:
137 bool complete = false; 137 bool complete = false;
138 Result status = ResultSuccess; 138 Result status = ResultSuccess;
139 std::vector<u8> final_data; 139 std::vector<u8> final_data;
140 Core::System& system;
141}; 140};
142 141
143} // namespace Service::AM::Applets 142} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp
index 4145bb84f..fbf75d379 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp
@@ -5,9 +5,10 @@
5#include "core/core.h" 5#include "core/core.h"
6#include "core/frontend/applets/software_keyboard.h" 6#include "core/frontend/applets/software_keyboard.h"
7#include "core/hle/service/am/am.h" 7#include "core/hle/service/am/am.h"
8#include "core/hle/service/am/applets/applet_software_keyboard.h" 8#include "core/hle/service/am/frontend/applet_software_keyboard.h"
9#include "core/hle/service/am/storage.h"
9 10
10namespace Service::AM::Applets { 11namespace Service::AM::Frontend {
11 12
12namespace { 13namespace {
13 14
@@ -41,14 +42,15 @@ void SetReplyBase(std::vector<u8>& reply, SwkbdState state, SwkbdReplyType reply
41 42
42} // Anonymous namespace 43} // Anonymous namespace
43 44
44SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, 45SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, std::shared_ptr<Applet> applet_,
46 LibraryAppletMode applet_mode_,
45 Core::Frontend::SoftwareKeyboardApplet& frontend_) 47 Core::Frontend::SoftwareKeyboardApplet& frontend_)
46 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} 48 : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}
47 49
48SoftwareKeyboard::~SoftwareKeyboard() = default; 50SoftwareKeyboard::~SoftwareKeyboard() = default;
49 51
50void SoftwareKeyboard::Initialize() { 52void SoftwareKeyboard::Initialize() {
51 Applet::Initialize(); 53 FrontendApplet::Initialize();
52 54
53 LOG_INFO(Service_AM, "Initializing Software Keyboard Applet with LibraryAppletMode={}", 55 LOG_INFO(Service_AM, "Initializing Software Keyboard Applet with LibraryAppletMode={}",
54 applet_mode); 56 applet_mode);
@@ -76,10 +78,6 @@ void SoftwareKeyboard::Initialize() {
76 } 78 }
77} 79}
78 80
79bool SoftwareKeyboard::TransactionComplete() const {
80 return complete;
81}
82
83Result SoftwareKeyboard::GetStatus() const { 81Result SoftwareKeyboard::GetStatus() const {
84 return status; 82 return status;
85} 83}
@@ -184,7 +182,7 @@ void SoftwareKeyboard::InitializeForeground() {
184 182
185 is_background = false; 183 is_background = false;
186 184
187 const auto swkbd_config_storage = broker.PopNormalDataToApplet(); 185 const auto swkbd_config_storage = PopInData();
188 ASSERT(swkbd_config_storage != nullptr); 186 ASSERT(swkbd_config_storage != nullptr);
189 187
190 const auto& swkbd_config_data = swkbd_config_storage->GetData(); 188 const auto& swkbd_config_data = swkbd_config_storage->GetData();
@@ -221,7 +219,7 @@ void SoftwareKeyboard::InitializeForeground() {
221 break; 219 break;
222 } 220 }
223 221
224 const auto work_buffer_storage = broker.PopNormalDataToApplet(); 222 const auto work_buffer_storage = PopInData();
225 ASSERT(work_buffer_storage != nullptr); 223 ASSERT(work_buffer_storage != nullptr);
226 224
227 if (swkbd_config_common.initial_string_length == 0) { 225 if (swkbd_config_common.initial_string_length == 0) {
@@ -250,7 +248,7 @@ void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mod
250 248
251 is_background = true; 249 is_background = true;
252 250
253 const auto swkbd_inline_initialize_arg_storage = broker.PopNormalDataToApplet(); 251 const auto swkbd_inline_initialize_arg_storage = PopInData();
254 ASSERT(swkbd_inline_initialize_arg_storage != nullptr); 252 ASSERT(swkbd_inline_initialize_arg_storage != nullptr);
255 253
256 const auto& swkbd_inline_initialize_arg = swkbd_inline_initialize_arg_storage->GetData(); 254 const auto& swkbd_inline_initialize_arg = swkbd_inline_initialize_arg_storage->GetData();
@@ -267,7 +265,7 @@ void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mod
267} 265}
268 266
269void SoftwareKeyboard::ProcessTextCheck() { 267void SoftwareKeyboard::ProcessTextCheck() {
270 const auto text_check_storage = broker.PopInteractiveDataToApplet(); 268 const auto text_check_storage = PopInteractiveInData();
271 ASSERT(text_check_storage != nullptr); 269 ASSERT(text_check_storage != nullptr);
272 270
273 const auto& text_check_data = text_check_storage->GetData(); 271 const auto& text_check_data = text_check_storage->GetData();
@@ -314,7 +312,7 @@ void SoftwareKeyboard::ProcessTextCheck() {
314} 312}
315 313
316void SoftwareKeyboard::ProcessInlineKeyboardRequest() { 314void SoftwareKeyboard::ProcessInlineKeyboardRequest() {
317 const auto request_data_storage = broker.PopInteractiveDataToApplet(); 315 const auto request_data_storage = PopInteractiveInData();
318 ASSERT(request_data_storage != nullptr); 316 ASSERT(request_data_storage != nullptr);
319 317
320 const auto& request_data = request_data_storage->GetData(); 318 const auto& request_data = request_data_storage->GetData();
@@ -377,7 +375,7 @@ void SoftwareKeyboard::SubmitNormalOutputAndExit(SwkbdResult result,
377 submitted_text.size() * sizeof(char16_t)); 375 submitted_text.size() * sizeof(char16_t));
378 } 376 }
379 377
380 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); 378 PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
381 379
382 ExitKeyboard(); 380 ExitKeyboard();
383} 381}
@@ -410,7 +408,7 @@ void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
410 current_text.size() * sizeof(char16_t)); 408 current_text.size() * sizeof(char16_t));
411 } 409 }
412 410
413 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); 411 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(out_data)));
414} 412}
415 413
416void SoftwareKeyboard::SendReply(SwkbdReplyType reply_type) { 414void SoftwareKeyboard::SendReply(SwkbdReplyType reply_type) {
@@ -767,7 +765,7 @@ void SoftwareKeyboard::ExitKeyboard() {
767 765
768 frontend.ExitKeyboard(); 766 frontend.ExitKeyboard();
769 767
770 broker.SignalStateChanged(); 768 Exit();
771} 769}
772 770
773Result SoftwareKeyboard::RequestExit() { 771Result SoftwareKeyboard::RequestExit() {
@@ -967,7 +965,7 @@ void SoftwareKeyboard::ReplyFinishedInitialize() {
967 965
968 SetReplyBase(reply, swkbd_state, SwkbdReplyType::FinishedInitialize); 966 SetReplyBase(reply, swkbd_state, SwkbdReplyType::FinishedInitialize);
969 967
970 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 968 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
971} 969}
972 970
973void SoftwareKeyboard::ReplyDefault() { 971void SoftwareKeyboard::ReplyDefault() {
@@ -977,7 +975,7 @@ void SoftwareKeyboard::ReplyDefault() {
977 975
978 SetReplyBase(reply, swkbd_state, SwkbdReplyType::Default); 976 SetReplyBase(reply, swkbd_state, SwkbdReplyType::Default);
979 977
980 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 978 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
981} 979}
982 980
983void SoftwareKeyboard::ReplyChangedString() { 981void SoftwareKeyboard::ReplyChangedString() {
@@ -999,7 +997,7 @@ void SoftwareKeyboard::ReplyChangedString() {
999 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, 997 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg,
1000 sizeof(SwkbdChangedStringArg)); 998 sizeof(SwkbdChangedStringArg));
1001 999
1002 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1000 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1003} 1001}
1004 1002
1005void SoftwareKeyboard::ReplyMovedCursor() { 1003void SoftwareKeyboard::ReplyMovedCursor() {
@@ -1019,7 +1017,7 @@ void SoftwareKeyboard::ReplyMovedCursor() {
1019 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, 1017 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg,
1020 sizeof(SwkbdMovedCursorArg)); 1018 sizeof(SwkbdMovedCursorArg));
1021 1019
1022 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1020 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1023} 1021}
1024 1022
1025void SoftwareKeyboard::ReplyMovedTab() { 1023void SoftwareKeyboard::ReplyMovedTab() {
@@ -1039,7 +1037,7 @@ void SoftwareKeyboard::ReplyMovedTab() {
1039 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_tab_arg, 1037 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_tab_arg,
1040 sizeof(SwkbdMovedTabArg)); 1038 sizeof(SwkbdMovedTabArg));
1041 1039
1042 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1040 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1043} 1041}
1044 1042
1045void SoftwareKeyboard::ReplyDecidedEnter() { 1043void SoftwareKeyboard::ReplyDecidedEnter() {
@@ -1058,7 +1056,7 @@ void SoftwareKeyboard::ReplyDecidedEnter() {
1058 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &decided_enter_arg, 1056 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &decided_enter_arg,
1059 sizeof(SwkbdDecidedEnterArg)); 1057 sizeof(SwkbdDecidedEnterArg));
1060 1058
1061 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1059 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1062 1060
1063 HideInlineKeyboard(); 1061 HideInlineKeyboard();
1064} 1062}
@@ -1070,7 +1068,7 @@ void SoftwareKeyboard::ReplyDecidedCancel() {
1070 1068
1071 SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedCancel); 1069 SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedCancel);
1072 1070
1073 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1071 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1074 1072
1075 HideInlineKeyboard(); 1073 HideInlineKeyboard();
1076} 1074}
@@ -1095,7 +1093,7 @@ void SoftwareKeyboard::ReplyChangedStringUtf8() {
1095 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, 1093 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg,
1096 sizeof(SwkbdChangedStringArg)); 1094 sizeof(SwkbdChangedStringArg));
1097 1095
1098 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1096 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1099} 1097}
1100 1098
1101void SoftwareKeyboard::ReplyMovedCursorUtf8() { 1099void SoftwareKeyboard::ReplyMovedCursorUtf8() {
@@ -1116,7 +1114,7 @@ void SoftwareKeyboard::ReplyMovedCursorUtf8() {
1116 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, 1114 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg,
1117 sizeof(SwkbdMovedCursorArg)); 1115 sizeof(SwkbdMovedCursorArg));
1118 1116
1119 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1117 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1120} 1118}
1121 1119
1122void SoftwareKeyboard::ReplyDecidedEnterUtf8() { 1120void SoftwareKeyboard::ReplyDecidedEnterUtf8() {
@@ -1136,7 +1134,7 @@ void SoftwareKeyboard::ReplyDecidedEnterUtf8() {
1136 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &decided_enter_arg, 1134 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &decided_enter_arg,
1137 sizeof(SwkbdDecidedEnterArg)); 1135 sizeof(SwkbdDecidedEnterArg));
1138 1136
1139 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1137 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1140 1138
1141 HideInlineKeyboard(); 1139 HideInlineKeyboard();
1142} 1140}
@@ -1148,7 +1146,7 @@ void SoftwareKeyboard::ReplyUnsetCustomizeDic() {
1148 1146
1149 SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizeDic); 1147 SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizeDic);
1150 1148
1151 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1149 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1152} 1150}
1153 1151
1154void SoftwareKeyboard::ReplyReleasedUserWordInfo() { 1152void SoftwareKeyboard::ReplyReleasedUserWordInfo() {
@@ -1158,7 +1156,7 @@ void SoftwareKeyboard::ReplyReleasedUserWordInfo() {
1158 1156
1159 SetReplyBase(reply, swkbd_state, SwkbdReplyType::ReleasedUserWordInfo); 1157 SetReplyBase(reply, swkbd_state, SwkbdReplyType::ReleasedUserWordInfo);
1160 1158
1161 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1159 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1162} 1160}
1163 1161
1164void SoftwareKeyboard::ReplyUnsetCustomizedDictionaries() { 1162void SoftwareKeyboard::ReplyUnsetCustomizedDictionaries() {
@@ -1168,7 +1166,7 @@ void SoftwareKeyboard::ReplyUnsetCustomizedDictionaries() {
1168 1166
1169 SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizedDictionaries); 1167 SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizedDictionaries);
1170 1168
1171 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1169 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1172} 1170}
1173 1171
1174void SoftwareKeyboard::ReplyChangedStringV2() { 1172void SoftwareKeyboard::ReplyChangedStringV2() {
@@ -1194,7 +1192,7 @@ void SoftwareKeyboard::ReplyChangedStringV2() {
1194 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg), 1192 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg),
1195 &flag, 1); 1193 &flag, 1);
1196 1194
1197 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1195 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1198} 1196}
1199 1197
1200void SoftwareKeyboard::ReplyMovedCursorV2() { 1198void SoftwareKeyboard::ReplyMovedCursorV2() {
@@ -1218,7 +1216,7 @@ void SoftwareKeyboard::ReplyMovedCursorV2() {
1218 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg), 1216 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg),
1219 &flag, 1); 1217 &flag, 1);
1220 1218
1221 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1219 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1222} 1220}
1223 1221
1224void SoftwareKeyboard::ReplyChangedStringUtf8V2() { 1222void SoftwareKeyboard::ReplyChangedStringUtf8V2() {
@@ -1245,7 +1243,7 @@ void SoftwareKeyboard::ReplyChangedStringUtf8V2() {
1245 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg), 1243 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg),
1246 &flag, 1); 1244 &flag, 1);
1247 1245
1248 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1246 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1249} 1247}
1250 1248
1251void SoftwareKeyboard::ReplyMovedCursorUtf8V2() { 1249void SoftwareKeyboard::ReplyMovedCursorUtf8V2() {
@@ -1270,7 +1268,7 @@ void SoftwareKeyboard::ReplyMovedCursorUtf8V2() {
1270 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg), 1268 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg),
1271 &flag, 1); 1269 &flag, 1);
1272 1270
1273 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); 1271 PushInteractiveOutData(std::make_shared<IStorage>(system, std::move(reply)));
1274} 1272}
1275 1273
1276} // namespace Service::AM::Applets 1274} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.h b/src/core/hle/service/am/frontend/applet_software_keyboard.h
index 2e919811b..f464b7e15 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.h
+++ b/src/core/hle/service/am/frontend/applet_software_keyboard.h
@@ -5,8 +5,8 @@
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hle/result.h" 7#include "core/hle/result.h"
8#include "core/hle/service/am/applets/applet_software_keyboard_types.h" 8#include "core/hle/service/am/frontend/applet_software_keyboard_types.h"
9#include "core/hle/service/am/applets/applets.h" 9#include "core/hle/service/am/frontend/applets.h"
10 10
11namespace Core { 11namespace Core {
12class System; 12class System;
@@ -17,17 +17,17 @@ struct KeyboardInitializeParameters;
17struct InlineAppearParameters; 17struct InlineAppearParameters;
18} // namespace Core::Frontend 18} // namespace Core::Frontend
19 19
20namespace Service::AM::Applets { 20namespace Service::AM::Frontend {
21 21
22class SoftwareKeyboard final : public Applet { 22class SoftwareKeyboard final : public FrontendApplet {
23public: 23public:
24 explicit SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, 24 explicit SoftwareKeyboard(Core::System& system_, std::shared_ptr<Applet> applet_,
25 LibraryAppletMode applet_mode_,
25 Core::Frontend::SoftwareKeyboardApplet& frontend_); 26 Core::Frontend::SoftwareKeyboardApplet& frontend_);
26 ~SoftwareKeyboard() override; 27 ~SoftwareKeyboard() override;
27 28
28 void Initialize() override; 29 void Initialize() override;
29 30
30 bool TransactionComplete() const override;
31 Result GetStatus() const override; 31 Result GetStatus() const override;
32 void ExecuteInteractive() override; 32 void ExecuteInteractive() override;
33 void Execute() override; 33 void Execute() override;
@@ -156,7 +156,6 @@ private:
156 void ReplyMovedCursorUtf8V2(); 156 void ReplyMovedCursorUtf8V2();
157 157
158 Core::Frontend::SoftwareKeyboardApplet& frontend; 158 Core::Frontend::SoftwareKeyboardApplet& frontend;
159 Core::System& system;
160 159
161 SwkbdAppletVersion swkbd_applet_version; 160 SwkbdAppletVersion swkbd_applet_version;
162 161
@@ -184,4 +183,4 @@ private:
184 Result status{ResultSuccess}; 183 Result status{ResultSuccess};
185}; 184};
186 185
187} // namespace Service::AM::Applets 186} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard_types.h b/src/core/hle/service/am/frontend/applet_software_keyboard_types.h
index 1f696900e..a25ff2a6d 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard_types.h
+++ b/src/core/hle/service/am/frontend/applet_software_keyboard_types.h
@@ -11,7 +11,7 @@
11#include "common/swap.h" 11#include "common/swap.h"
12#include "common/uuid.h" 12#include "common/uuid.h"
13 13
14namespace Service::AM::Applets { 14namespace Service::AM::Frontend {
15 15
16constexpr std::size_t MAX_OK_TEXT_LENGTH = 8; 16constexpr std::size_t MAX_OK_TEXT_LENGTH = 8;
17constexpr std::size_t MAX_HEADER_TEXT_LENGTH = 64; 17constexpr std::size_t MAX_HEADER_TEXT_LENGTH = 64;
@@ -351,4 +351,4 @@ struct SwkbdDecidedEnterArg {
351}; 351};
352static_assert(sizeof(SwkbdDecidedEnterArg) == 0x4, "SwkbdDecidedEnterArg has incorrect size."); 352static_assert(sizeof(SwkbdDecidedEnterArg) == 0x4, "SwkbdDecidedEnterArg has incorrect size.");
353 353
354} // namespace Service::AM::Applets 354} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/frontend/applet_web_browser.cpp
index 19057ad7b..6ee4caf34 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/frontend/applet_web_browser.cpp
@@ -19,12 +19,13 @@
19#include "core/frontend/applets/web_browser.h" 19#include "core/frontend/applets/web_browser.h"
20#include "core/hle/result.h" 20#include "core/hle/result.h"
21#include "core/hle/service/am/am.h" 21#include "core/hle/service/am/am.h"
22#include "core/hle/service/am/applets/applet_web_browser.h" 22#include "core/hle/service/am/frontend/applet_web_browser.h"
23#include "core/hle/service/am/storage.h"
23#include "core/hle/service/filesystem/filesystem.h" 24#include "core/hle/service/filesystem/filesystem.h"
24#include "core/hle/service/ns/iplatform_service_manager.h" 25#include "core/hle/service/ns/iplatform_service_manager.h"
25#include "core/loader/loader.h" 26#include "core/loader/loader.h"
26 27
27namespace Service::AM::Applets { 28namespace Service::AM::Frontend {
28 29
29namespace { 30namespace {
30 31
@@ -223,14 +224,15 @@ void ExtractSharedFonts(Core::System& system) {
223 224
224} // namespace 225} // namespace
225 226
226WebBrowser::WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, 227WebBrowser::WebBrowser(Core::System& system_, std::shared_ptr<Applet> applet_,
228 LibraryAppletMode applet_mode_,
227 const Core::Frontend::WebBrowserApplet& frontend_) 229 const Core::Frontend::WebBrowserApplet& frontend_)
228 : Applet{system_, applet_mode_}, frontend(frontend_), system{system_} {} 230 : FrontendApplet{system_, applet_, applet_mode_}, frontend(frontend_) {}
229 231
230WebBrowser::~WebBrowser() = default; 232WebBrowser::~WebBrowser() = default;
231 233
232void WebBrowser::Initialize() { 234void WebBrowser::Initialize() {
233 Applet::Initialize(); 235 FrontendApplet::Initialize();
234 236
235 LOG_INFO(Service_AM, "Initializing Web Browser Applet."); 237 LOG_INFO(Service_AM, "Initializing Web Browser Applet.");
236 238
@@ -243,7 +245,7 @@ void WebBrowser::Initialize() {
243 245
244 web_applet_version = WebAppletVersion{common_args.library_version}; 246 web_applet_version = WebAppletVersion{common_args.library_version};
245 247
246 const auto web_arg_storage = broker.PopNormalDataToApplet(); 248 const auto web_arg_storage = PopInData();
247 ASSERT(web_arg_storage != nullptr); 249 ASSERT(web_arg_storage != nullptr);
248 250
249 const auto& web_arg = web_arg_storage->GetData(); 251 const auto& web_arg = web_arg_storage->GetData();
@@ -284,10 +286,6 @@ void WebBrowser::Initialize() {
284 } 286 }
285} 287}
286 288
287bool WebBrowser::TransactionComplete() const {
288 return complete;
289}
290
291Result WebBrowser::GetStatus() const { 289Result WebBrowser::GetStatus() const {
292 return status; 290 return status;
293} 291}
@@ -358,8 +356,8 @@ void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url)
358 complete = true; 356 complete = true;
359 std::vector<u8> out_data(sizeof(WebCommonReturnValue)); 357 std::vector<u8> out_data(sizeof(WebCommonReturnValue));
360 std::memcpy(out_data.data(), &web_common_return_value, out_data.size()); 358 std::memcpy(out_data.data(), &web_common_return_value, out_data.size());
361 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); 359 PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
362 broker.SignalStateChanged(); 360 Exit();
363} 361}
364 362
365Result WebBrowser::RequestExit() { 363Result WebBrowser::RequestExit() {
@@ -504,4 +502,4 @@ void WebBrowser::ExecuteLobby() {
504 LOG_WARNING(Service_AM, "(STUBBED) called, Lobby Applet is not implemented"); 502 LOG_WARNING(Service_AM, "(STUBBED) called, Lobby Applet is not implemented");
505 WebBrowserExit(WebExitReason::EndButtonPressed); 503 WebBrowserExit(WebExitReason::EndButtonPressed);
506} 504}
507} // namespace Service::AM::Applets 505} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_web_browser.h b/src/core/hle/service/am/frontend/applet_web_browser.h
index 36adb2510..ba20b7a4c 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.h
+++ b/src/core/hle/service/am/frontend/applet_web_browser.h
@@ -9,8 +9,8 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/file_sys/vfs/vfs_types.h" 10#include "core/file_sys/vfs/vfs_types.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
12#include "core/hle/service/am/applets/applet_web_browser_types.h" 12#include "core/hle/service/am/frontend/applet_web_browser_types.h"
13#include "core/hle/service/am/applets/applets.h" 13#include "core/hle/service/am/frontend/applets.h"
14 14
15namespace Core { 15namespace Core {
16class System; 16class System;
@@ -20,18 +20,17 @@ namespace FileSys {
20enum class ContentRecordType : u8; 20enum class ContentRecordType : u8;
21} 21}
22 22
23namespace Service::AM::Applets { 23namespace Service::AM::Frontend {
24 24
25class WebBrowser final : public Applet { 25class WebBrowser final : public FrontendApplet {
26public: 26public:
27 WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, 27 WebBrowser(Core::System& system_, std::shared_ptr<Applet> applet_,
28 const Core::Frontend::WebBrowserApplet& frontend_); 28 LibraryAppletMode applet_mode_, const Core::Frontend::WebBrowserApplet& frontend_);
29 29
30 ~WebBrowser() override; 30 ~WebBrowser() override;
31 31
32 void Initialize() override; 32 void Initialize() override;
33 33
34 bool TransactionComplete() const override;
35 Result GetStatus() const override; 34 Result GetStatus() const override;
36 void ExecuteInteractive() override; 35 void ExecuteInteractive() override;
37 void Execute() override; 36 void Execute() override;
@@ -80,8 +79,6 @@ private:
80 FileSys::VirtualFile offline_romfs; 79 FileSys::VirtualFile offline_romfs;
81 80
82 std::string external_url; 81 std::string external_url;
83
84 Core::System& system;
85}; 82};
86 83
87} // namespace Service::AM::Applets 84} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/applets/applet_web_browser_types.h b/src/core/hle/service/am/frontend/applet_web_browser_types.h
index c522c5c1a..2f7c05c24 100644
--- a/src/core/hle/service/am/applets/applet_web_browser_types.h
+++ b/src/core/hle/service/am/frontend/applet_web_browser_types.h
@@ -11,7 +11,7 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/swap.h" 12#include "common/swap.h"
13 13
14namespace Service::AM::Applets { 14namespace Service::AM::Frontend {
15 15
16enum class WebAppletVersion : u32_le { 16enum class WebAppletVersion : u32_le {
17 Version0 = 0x0, // Only used by WifiWebAuthApplet 17 Version0 = 0x0, // Only used by WifiWebAuthApplet
@@ -174,4 +174,4 @@ static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has
174 174
175using WebArgInputTLVMap = std::unordered_map<WebArgInputTLVType, std::vector<u8>>; 175using WebArgInputTLVMap = std::unordered_map<WebArgInputTLVType, std::vector<u8>>;
176 176
177} // namespace Service::AM::Applets 177} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/frontend/applets.cpp b/src/core/hle/service/am/frontend/applets.cpp
new file mode 100644
index 000000000..db2b04575
--- /dev/null
+++ b/src/core/hle/service/am/frontend/applets.cpp
@@ -0,0 +1,240 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstring>
5
6#include "common/assert.h"
7#include "core/core.h"
8#include "core/frontend/applets/cabinet.h"
9#include "core/frontend/applets/controller.h"
10#include "core/frontend/applets/error.h"
11#include "core/frontend/applets/general.h"
12#include "core/frontend/applets/mii_edit.h"
13#include "core/frontend/applets/profile_select.h"
14#include "core/frontend/applets/software_keyboard.h"
15#include "core/frontend/applets/web_browser.h"
16#include "core/hle/kernel/k_event.h"
17#include "core/hle/service/am/am.h"
18#include "core/hle/service/am/applet_ae.h"
19#include "core/hle/service/am/applet_data_broker.h"
20#include "core/hle/service/am/applet_manager.h"
21#include "core/hle/service/am/applet_message_queue.h"
22#include "core/hle/service/am/applet_oe.h"
23#include "core/hle/service/am/frontend/applet_cabinet.h"
24#include "core/hle/service/am/frontend/applet_controller.h"
25#include "core/hle/service/am/frontend/applet_error.h"
26#include "core/hle/service/am/frontend/applet_general.h"
27#include "core/hle/service/am/frontend/applet_mii_edit.h"
28#include "core/hle/service/am/frontend/applet_profile_select.h"
29#include "core/hle/service/am/frontend/applet_software_keyboard.h"
30#include "core/hle/service/am/frontend/applet_web_browser.h"
31#include "core/hle/service/am/frontend/applets.h"
32#include "core/hle/service/am/storage.h"
33#include "core/hle/service/sm/sm.h"
34
35namespace Service::AM::Frontend {
36
37FrontendApplet::FrontendApplet(Core::System& system_, std::shared_ptr<Applet> applet_,
38 LibraryAppletMode applet_mode_)
39 : system{system_}, applet{std::move(applet_)}, applet_mode{applet_mode_} {}
40
41FrontendApplet::~FrontendApplet() = default;
42
43void FrontendApplet::Initialize() {
44 std::shared_ptr<IStorage> common = PopInData();
45 ASSERT(common != nullptr);
46 const auto common_data = common->GetData();
47
48 ASSERT(common_data.size() >= sizeof(CommonArguments));
49 std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments));
50
51 initialized = true;
52}
53
54std::shared_ptr<IStorage> FrontendApplet::PopInData() {
55 std::shared_ptr<IStorage> ret;
56 applet.lock()->caller_applet_broker->GetInData().Pop(&ret);
57 return ret;
58}
59
60std::shared_ptr<IStorage> FrontendApplet::PopInteractiveInData() {
61 std::shared_ptr<IStorage> ret;
62 applet.lock()->caller_applet_broker->GetInteractiveInData().Pop(&ret);
63 return ret;
64}
65
66void FrontendApplet::PushOutData(std::shared_ptr<IStorage> storage) {
67 applet.lock()->caller_applet_broker->GetOutData().Push(storage);
68}
69
70void FrontendApplet::PushInteractiveOutData(std::shared_ptr<IStorage> storage) {
71 applet.lock()->caller_applet_broker->GetInteractiveOutData().Push(storage);
72}
73
74void FrontendApplet::Exit() {
75 applet.lock()->caller_applet_broker->SignalCompletion();
76}
77
78FrontendAppletSet::FrontendAppletSet() = default;
79
80FrontendAppletSet::FrontendAppletSet(CabinetApplet cabinet_applet,
81 ControllerApplet controller_applet, ErrorApplet error_applet,
82 MiiEdit mii_edit_,
83 ParentalControlsApplet parental_controls_applet,
84 PhotoViewer photo_viewer_, ProfileSelect profile_select_,
85 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
86 : cabinet{std::move(cabinet_applet)}, controller{std::move(controller_applet)},
87 error{std::move(error_applet)}, mii_edit{std::move(mii_edit_)},
88 parental_controls{std::move(parental_controls_applet)},
89 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
90 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
91
92FrontendAppletSet::~FrontendAppletSet() = default;
93
94FrontendAppletSet::FrontendAppletSet(FrontendAppletSet&&) noexcept = default;
95
96FrontendAppletSet& FrontendAppletSet::operator=(FrontendAppletSet&&) noexcept = default;
97
98FrontendAppletHolder::FrontendAppletHolder(Core::System& system_) : system{system_} {}
99
100FrontendAppletHolder::~FrontendAppletHolder() = default;
101
102const FrontendAppletSet& FrontendAppletHolder::GetFrontendAppletSet() const {
103 return frontend;
104}
105
106NFP::CabinetMode FrontendAppletHolder::GetCabinetMode() const {
107 return cabinet_mode;
108}
109
110AppletId FrontendAppletHolder::GetCurrentAppletId() const {
111 return current_applet_id;
112}
113
114void FrontendAppletHolder::SetFrontendAppletSet(FrontendAppletSet set) {
115 if (set.cabinet != nullptr) {
116 frontend.cabinet = std::move(set.cabinet);
117 }
118
119 if (set.controller != nullptr) {
120 frontend.controller = std::move(set.controller);
121 }
122
123 if (set.error != nullptr) {
124 frontend.error = std::move(set.error);
125 }
126
127 if (set.mii_edit != nullptr) {
128 frontend.mii_edit = std::move(set.mii_edit);
129 }
130
131 if (set.parental_controls != nullptr) {
132 frontend.parental_controls = std::move(set.parental_controls);
133 }
134
135 if (set.photo_viewer != nullptr) {
136 frontend.photo_viewer = std::move(set.photo_viewer);
137 }
138
139 if (set.profile_select != nullptr) {
140 frontend.profile_select = std::move(set.profile_select);
141 }
142
143 if (set.software_keyboard != nullptr) {
144 frontend.software_keyboard = std::move(set.software_keyboard);
145 }
146
147 if (set.web_browser != nullptr) {
148 frontend.web_browser = std::move(set.web_browser);
149 }
150}
151
152void FrontendAppletHolder::SetCabinetMode(NFP::CabinetMode mode) {
153 cabinet_mode = mode;
154}
155
156void FrontendAppletHolder::SetCurrentAppletId(AppletId applet_id) {
157 current_applet_id = applet_id;
158}
159
160void FrontendAppletHolder::SetDefaultAppletsIfMissing() {
161 if (frontend.cabinet == nullptr) {
162 frontend.cabinet = std::make_unique<Core::Frontend::DefaultCabinetApplet>();
163 }
164
165 if (frontend.controller == nullptr) {
166 frontend.controller =
167 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
168 }
169
170 if (frontend.error == nullptr) {
171 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
172 }
173
174 if (frontend.mii_edit == nullptr) {
175 frontend.mii_edit = std::make_unique<Core::Frontend::DefaultMiiEditApplet>();
176 }
177
178 if (frontend.parental_controls == nullptr) {
179 frontend.parental_controls =
180 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
181 }
182
183 if (frontend.photo_viewer == nullptr) {
184 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
185 }
186
187 if (frontend.profile_select == nullptr) {
188 frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
189 }
190
191 if (frontend.software_keyboard == nullptr) {
192 frontend.software_keyboard =
193 std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
194 }
195
196 if (frontend.web_browser == nullptr) {
197 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
198 }
199}
200
201void FrontendAppletHolder::ClearAll() {
202 frontend = {};
203}
204
205std::shared_ptr<FrontendApplet> FrontendAppletHolder::GetApplet(std::shared_ptr<Applet> applet,
206 AppletId id,
207 LibraryAppletMode mode) const {
208 switch (id) {
209 case AppletId::Auth:
210 return std::make_shared<Auth>(system, applet, mode, *frontend.parental_controls);
211 case AppletId::Cabinet:
212 return std::make_shared<Cabinet>(system, applet, mode, *frontend.cabinet);
213 case AppletId::Controller:
214 return std::make_shared<Controller>(system, applet, mode, *frontend.controller);
215 case AppletId::Error:
216 return std::make_shared<Error>(system, applet, mode, *frontend.error);
217 case AppletId::ProfileSelect:
218 return std::make_shared<ProfileSelect>(system, applet, mode, *frontend.profile_select);
219 case AppletId::SoftwareKeyboard:
220 return std::make_shared<SoftwareKeyboard>(system, applet, mode,
221 *frontend.software_keyboard);
222 case AppletId::MiiEdit:
223 return std::make_shared<MiiEdit>(system, applet, mode, *frontend.mii_edit);
224 case AppletId::Web:
225 case AppletId::Shop:
226 case AppletId::OfflineWeb:
227 case AppletId::LoginShare:
228 case AppletId::WebAuth:
229 return std::make_shared<WebBrowser>(system, applet, mode, *frontend.web_browser);
230 case AppletId::PhotoViewer:
231 return std::make_shared<PhotoViewer>(system, applet, mode, *frontend.photo_viewer);
232 default:
233 UNIMPLEMENTED_MSG(
234 "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
235 static_cast<u8>(id));
236 return std::make_shared<StubApplet>(system, applet, id, mode);
237 }
238}
239
240} // namespace Service::AM::Frontend
diff --git a/src/core/hle/service/am/frontend/applets.h b/src/core/hle/service/am/frontend/applets.h
new file mode 100644
index 000000000..1e1fd28b8
--- /dev/null
+++ b/src/core/hle/service/am/frontend/applets.h
@@ -0,0 +1,146 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <queue>
8
9#include "common/swap.h"
10#include "core/hle/service/am/applet.h"
11
12union Result;
13
14namespace Core {
15class System;
16}
17
18namespace Core::Frontend {
19class CabinetApplet;
20class ControllerApplet;
21class ECommerceApplet;
22class ErrorApplet;
23class MiiEditApplet;
24class ParentalControlsApplet;
25class PhotoViewerApplet;
26class ProfileSelectApplet;
27class SoftwareKeyboardApplet;
28class WebBrowserApplet;
29} // namespace Core::Frontend
30
31namespace Kernel {
32class KernelCore;
33class KEvent;
34class KReadableEvent;
35} // namespace Kernel
36
37namespace Service::NFP {
38enum class CabinetMode : u8;
39} // namespace Service::NFP
40
41namespace Service::AM {
42
43class IStorage;
44
45namespace Frontend {
46
47class FrontendApplet {
48public:
49 explicit FrontendApplet(Core::System& system_, std::shared_ptr<Applet> applet_,
50 LibraryAppletMode applet_mode_);
51 virtual ~FrontendApplet();
52
53 virtual void Initialize();
54
55 virtual Result GetStatus() const = 0;
56 virtual void ExecuteInteractive() = 0;
57 virtual void Execute() = 0;
58 virtual Result RequestExit() = 0;
59
60 LibraryAppletMode GetLibraryAppletMode() const {
61 return applet_mode;
62 }
63
64 bool IsInitialized() const {
65 return initialized;
66 }
67
68protected:
69 std::shared_ptr<IStorage> PopInData();
70 std::shared_ptr<IStorage> PopInteractiveInData();
71 void PushOutData(std::shared_ptr<IStorage> storage);
72 void PushInteractiveOutData(std::shared_ptr<IStorage> storage);
73 void Exit();
74
75protected:
76 Core::System& system;
77 CommonArguments common_args{};
78 std::weak_ptr<Applet> applet{};
79 LibraryAppletMode applet_mode{};
80 bool initialized{false};
81};
82
83struct FrontendAppletSet {
84 using CabinetApplet = std::unique_ptr<Core::Frontend::CabinetApplet>;
85 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
86 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
87 using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
88 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
89 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
90 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
91 using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
92 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
93
94 FrontendAppletSet();
95 FrontendAppletSet(CabinetApplet cabinet_applet, ControllerApplet controller_applet,
96 ErrorApplet error_applet, MiiEdit mii_edit_,
97 ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
98 ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
99 WebBrowser web_browser_);
100 ~FrontendAppletSet();
101
102 FrontendAppletSet(const FrontendAppletSet&) = delete;
103 FrontendAppletSet& operator=(const FrontendAppletSet&) = delete;
104
105 FrontendAppletSet(FrontendAppletSet&&) noexcept;
106 FrontendAppletSet& operator=(FrontendAppletSet&&) noexcept;
107
108 CabinetApplet cabinet;
109 ControllerApplet controller;
110 ErrorApplet error;
111 MiiEdit mii_edit;
112 ParentalControlsApplet parental_controls;
113 PhotoViewer photo_viewer;
114 ProfileSelect profile_select;
115 SoftwareKeyboard software_keyboard;
116 WebBrowser web_browser;
117};
118
119class FrontendAppletHolder {
120public:
121 explicit FrontendAppletHolder(Core::System& system_);
122 ~FrontendAppletHolder();
123
124 const FrontendAppletSet& GetFrontendAppletSet() const;
125 NFP::CabinetMode GetCabinetMode() const;
126 AppletId GetCurrentAppletId() const;
127
128 void SetFrontendAppletSet(FrontendAppletSet set);
129 void SetCabinetMode(NFP::CabinetMode mode);
130 void SetCurrentAppletId(AppletId applet_id);
131 void SetDefaultAppletsIfMissing();
132 void ClearAll();
133
134 std::shared_ptr<FrontendApplet> GetApplet(std::shared_ptr<Applet> applet, AppletId id,
135 LibraryAppletMode mode) const;
136
137private:
138 AppletId current_applet_id{};
139 NFP::CabinetMode cabinet_mode{};
140
141 FrontendAppletSet frontend;
142 Core::System& system;
143};
144
145} // namespace Frontend
146} // namespace Service::AM
diff --git a/src/core/hle/service/am/global_state_controller.cpp b/src/core/hle/service/am/global_state_controller.cpp
new file mode 100644
index 000000000..ed0eb7108
--- /dev/null
+++ b/src/core/hle/service/am/global_state_controller.cpp
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/global_state_controller.h"
5#include "core/hle/service/ipc_helpers.h"
6
7namespace Service::AM {
8
9IGlobalStateController::IGlobalStateController(Core::System& system_)
10 : ServiceFramework{system_, "IGlobalStateController"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, nullptr, "RequestToEnterSleep"},
14 {1, nullptr, "EnterSleep"},
15 {2, nullptr, "StartSleepSequence"},
16 {3, nullptr, "StartShutdownSequence"},
17 {4, nullptr, "StartRebootSequence"},
18 {9, nullptr, "IsAutoPowerDownRequested"},
19 {10, nullptr, "LoadAndApplyIdlePolicySettings"},
20 {11, nullptr, "NotifyCecSettingsChanged"},
21 {12, nullptr, "SetDefaultHomeButtonLongPressTime"},
22 {13, nullptr, "UpdateDefaultDisplayResolution"},
23 {14, nullptr, "ShouldSleepOnBoot"},
24 {15, nullptr, "GetHdcpAuthenticationFailedEvent"},
25 {30, nullptr, "OpenCradleFirmwareUpdater"},
26 };
27 // clang-format on
28
29 RegisterHandlers(functions);
30}
31
32IGlobalStateController::~IGlobalStateController() = default;
33
34} // namespace Service::AM
diff --git a/src/core/hle/service/am/global_state_controller.h b/src/core/hle/service/am/global_state_controller.h
new file mode 100644
index 000000000..7125464a1
--- /dev/null
+++ b/src/core/hle/service/am/global_state_controller.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10class IGlobalStateController final : public ServiceFramework<IGlobalStateController> {
11public:
12 explicit IGlobalStateController(Core::System& system_);
13 ~IGlobalStateController() override;
14};
15
16} // namespace Service::AM
diff --git a/src/core/hle/service/am/hid_registration.cpp b/src/core/hle/service/am/hid_registration.cpp
new file mode 100644
index 000000000..8ed49bac1
--- /dev/null
+++ b/src/core/hle/service/am/hid_registration.cpp
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/am/hid_registration.h"
6#include "core/hle/service/am/process.h"
7#include "core/hle/service/hid/hid_server.h"
8#include "core/hle/service/sm/sm.h"
9#include "hid_core/resource_manager.h"
10
11namespace Service::AM {
12
13HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) {
14 m_hid_server = system.ServiceManager().GetService<HID::IHidServer>("hid");
15
16 if (m_process.IsInitialized()) {
17 m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(),
18 true);
19 }
20}
21
22HidRegistration::~HidRegistration() {
23 if (m_process.IsInitialized()) {
24 m_hid_server->GetResourceManager()->UnregisterAppletResourceUserId(
25 m_process.GetProcessId());
26 }
27}
28
29void HidRegistration::EnableAppletToGetInput(bool enable) {
30 if (m_process.IsInitialized()) {
31 m_hid_server->GetResourceManager()->EnableInput(m_process.GetProcessId(), enable);
32 }
33}
34
35} // namespace Service::AM
diff --git a/src/core/hle/service/am/hid_registration.h b/src/core/hle/service/am/hid_registration.h
new file mode 100644
index 000000000..67cd84961
--- /dev/null
+++ b/src/core/hle/service/am/hid_registration.h
@@ -0,0 +1,32 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8namespace Core {
9class System;
10}
11
12namespace Service::HID {
13class IHidServer;
14}
15
16namespace Service::AM {
17
18class Process;
19
20class HidRegistration {
21public:
22 explicit HidRegistration(Core::System& system, Process& process);
23 ~HidRegistration();
24
25 void EnableAppletToGetInput(bool enable);
26
27private:
28 Process& m_process;
29 std::shared_ptr<Service::HID::IHidServer> m_hid_server;
30};
31
32} // namespace Service::AM
diff --git a/src/core/hle/service/am/home_menu_functions.cpp b/src/core/hle/service/am/home_menu_functions.cpp
new file mode 100644
index 000000000..640e9fbb7
--- /dev/null
+++ b/src/core/hle/service/am/home_menu_functions.cpp
@@ -0,0 +1,57 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/home_menu_functions.h"
5#include "core/hle/service/ipc_helpers.h"
6
7namespace Service::AM {
8
9IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_)
10 : ServiceFramework{system_, "IHomeMenuFunctions"}, service_context{system,
11 "IHomeMenuFunctions"} {
12 // clang-format off
13 static const FunctionInfo functions[] = {
14 {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
15 {11, nullptr, "LockForeground"},
16 {12, nullptr, "UnlockForeground"},
17 {20, nullptr, "PopFromGeneralChannel"},
18 {21, &IHomeMenuFunctions::GetPopFromGeneralChannelEvent, "GetPopFromGeneralChannelEvent"},
19 {30, nullptr, "GetHomeButtonWriterLockAccessor"},
20 {31, nullptr, "GetWriterLockAccessorEx"},
21 {40, nullptr, "IsSleepEnabled"},
22 {41, nullptr, "IsRebootEnabled"},
23 {50, nullptr, "LaunchSystemApplet"},
24 {51, nullptr, "LaunchStarter"},
25 {100, nullptr, "PopRequestLaunchApplicationForDebug"},
26 {110, nullptr, "IsForceTerminateApplicationDisabledForDebug"},
27 {200, nullptr, "LaunchDevMenu"},
28 {1000, nullptr, "SetLastApplicationExitReason"},
29 };
30 // clang-format on
31
32 RegisterHandlers(functions);
33
34 pop_from_general_channel_event =
35 service_context.CreateEvent("IHomeMenuFunctions:PopFromGeneralChannelEvent");
36}
37
38IHomeMenuFunctions::~IHomeMenuFunctions() {
39 service_context.CloseEvent(pop_from_general_channel_event);
40}
41
42void IHomeMenuFunctions::RequestToGetForeground(HLERequestContext& ctx) {
43 LOG_WARNING(Service_AM, "(STUBBED) called");
44
45 IPC::ResponseBuilder rb{ctx, 2};
46 rb.Push(ResultSuccess);
47}
48
49void IHomeMenuFunctions::GetPopFromGeneralChannelEvent(HLERequestContext& ctx) {
50 LOG_WARNING(Service_AM, "(STUBBED) called");
51
52 IPC::ResponseBuilder rb{ctx, 2, 1};
53 rb.Push(ResultSuccess);
54 rb.PushCopyObjects(pop_from_general_channel_event->GetReadableEvent());
55}
56
57} // namespace Service::AM
diff --git a/src/core/hle/service/am/home_menu_functions.h b/src/core/hle/service/am/home_menu_functions.h
new file mode 100644
index 000000000..e082d5d73
--- /dev/null
+++ b/src/core/hle/service/am/home_menu_functions.h
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h"
8
9namespace Service::AM {
10
11class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
12public:
13 explicit IHomeMenuFunctions(Core::System& system_);
14 ~IHomeMenuFunctions() override;
15
16private:
17 void RequestToGetForeground(HLERequestContext& ctx);
18 void GetPopFromGeneralChannelEvent(HLERequestContext& ctx);
19
20 KernelHelpers::ServiceContext service_context;
21
22 Kernel::KEvent* pop_from_general_channel_event;
23};
24
25} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_accessor.cpp b/src/core/hle/service/am/library_applet_accessor.cpp
new file mode 100644
index 000000000..6b20814f8
--- /dev/null
+++ b/src/core/hle/service/am/library_applet_accessor.cpp
@@ -0,0 +1,202 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/hle/service/am/am_results.h"
6#include "core/hle/service/am/applet_data_broker.h"
7#include "core/hle/service/am/frontend/applets.h"
8#include "core/hle/service/am/library_applet_accessor.h"
9#include "core/hle/service/am/storage.h"
10#include "core/hle/service/ipc_helpers.h"
11
12namespace Service::AM {
13
14ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
15 std::shared_ptr<AppletDataBroker> broker_,
16 std::shared_ptr<Applet> applet_)
17 : ServiceFramework{system_, "ILibraryAppletAccessor"}, broker{std::move(broker_)},
18 applet{std::move(applet_)} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
22 {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"},
23 {10, &ILibraryAppletAccessor::Start, "Start"},
24 {20, &ILibraryAppletAccessor::RequestExit, "RequestExit"},
25 {25, nullptr, "Terminate"},
26 {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
27 {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
28 {60, &ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero, "PresetLibraryAppletGpuTimeSliceZero"},
29 {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
30 {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
31 {102, nullptr, "PushExtraStorage"},
32 {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
33 {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
34 {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"},
35 {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
36 {110, nullptr, "NeedsToExitProcess"},
37 {120, nullptr, "GetLibraryAppletInfo"},
38 {150, nullptr, "RequestForAppletToGetForeground"},
39 {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"},
40 };
41 // clang-format on
42
43 RegisterHandlers(functions);
44}
45
46ILibraryAppletAccessor::~ILibraryAppletAccessor() = default;
47
48void ILibraryAppletAccessor::GetAppletStateChangedEvent(HLERequestContext& ctx) {
49 LOG_DEBUG(Service_AM, "called");
50
51 IPC::ResponseBuilder rb{ctx, 2, 1};
52 rb.Push(ResultSuccess);
53 rb.PushCopyObjects(broker->GetStateChangedEvent().GetHandle());
54}
55
56void ILibraryAppletAccessor::IsCompleted(HLERequestContext& ctx) {
57 LOG_DEBUG(Service_AM, "called");
58
59 std::scoped_lock lk{applet->lock};
60
61 IPC::ResponseBuilder rb{ctx, 3};
62 rb.Push(ResultSuccess);
63 rb.Push<u32>(broker->IsCompleted());
64}
65
66void ILibraryAppletAccessor::GetResult(HLERequestContext& ctx) {
67 LOG_DEBUG(Service_AM, "called");
68
69 IPC::ResponseBuilder rb{ctx, 2};
70 rb.Push(applet->terminate_result);
71}
72
73void ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx) {
74 LOG_WARNING(Service_AM, "(STUBBED) called");
75
76 IPC::ResponseBuilder rb{ctx, 2};
77 rb.Push(ResultSuccess);
78}
79
80void ILibraryAppletAccessor::Start(HLERequestContext& ctx) {
81 LOG_DEBUG(Service_AM, "called");
82
83 applet->process->Run();
84 FrontendExecute();
85
86 IPC::ResponseBuilder rb{ctx, 2};
87 rb.Push(ResultSuccess);
88}
89
90void ILibraryAppletAccessor::RequestExit(HLERequestContext& ctx) {
91 LOG_DEBUG(Service_AM, "called");
92
93 ASSERT(applet != nullptr);
94 applet->message_queue.RequestExit();
95 FrontendRequestExit();
96
97 IPC::ResponseBuilder rb{ctx, 2};
98 rb.Push(ResultSuccess);
99}
100
101void ILibraryAppletAccessor::PushInData(HLERequestContext& ctx) {
102 LOG_DEBUG(Service_AM, "called");
103
104 IPC::RequestParser rp{ctx};
105 broker->GetInData().Push(rp.PopIpcInterface<IStorage>().lock());
106
107 IPC::ResponseBuilder rb{ctx, 2};
108 rb.Push(ResultSuccess);
109}
110
111void ILibraryAppletAccessor::PopOutData(HLERequestContext& ctx) {
112 LOG_DEBUG(Service_AM, "called");
113
114 std::shared_ptr<IStorage> data;
115 const auto res = broker->GetOutData().Pop(&data);
116
117 if (res.IsSuccess()) {
118 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
119 rb.Push(res);
120 rb.PushIpcInterface(std::move(data));
121 } else {
122 IPC::ResponseBuilder rb{ctx, 2};
123 rb.Push(res);
124 }
125}
126
127void ILibraryAppletAccessor::PushInteractiveInData(HLERequestContext& ctx) {
128 LOG_DEBUG(Service_AM, "called");
129
130 IPC::RequestParser rp{ctx};
131 broker->GetInteractiveInData().Push(rp.PopIpcInterface<IStorage>().lock());
132 FrontendExecuteInteractive();
133
134 IPC::ResponseBuilder rb{ctx, 2};
135 rb.Push(ResultSuccess);
136}
137
138void ILibraryAppletAccessor::PopInteractiveOutData(HLERequestContext& ctx) {
139 LOG_DEBUG(Service_AM, "called");
140
141 std::shared_ptr<IStorage> data;
142 const auto res = broker->GetInteractiveOutData().Pop(&data);
143
144 if (res.IsSuccess()) {
145 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
146 rb.Push(res);
147 rb.PushIpcInterface(std::move(data));
148 } else {
149 IPC::ResponseBuilder rb{ctx, 2};
150 rb.Push(res);
151 }
152}
153
154void ILibraryAppletAccessor::GetPopOutDataEvent(HLERequestContext& ctx) {
155 LOG_DEBUG(Service_AM, "called");
156
157 IPC::ResponseBuilder rb{ctx, 2, 1};
158 rb.Push(ResultSuccess);
159 rb.PushCopyObjects(broker->GetOutData().GetEvent());
160}
161
162void ILibraryAppletAccessor::GetPopInteractiveOutDataEvent(HLERequestContext& ctx) {
163 LOG_DEBUG(Service_AM, "called");
164
165 IPC::ResponseBuilder rb{ctx, 2, 1};
166 rb.Push(ResultSuccess);
167 rb.PushCopyObjects(broker->GetInteractiveOutData().GetEvent());
168}
169
170void ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(HLERequestContext& ctx) {
171 LOG_WARNING(Service_AM, "(STUBBED) called");
172
173 // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is
174 // actually used anywhere
175 constexpr u64 handle = 0xdeadbeef;
176
177 IPC::ResponseBuilder rb{ctx, 4};
178 rb.Push(ResultSuccess);
179 rb.Push(handle);
180}
181
182void ILibraryAppletAccessor::FrontendExecute() {
183 if (applet->frontend) {
184 applet->frontend->Initialize();
185 applet->frontend->Execute();
186 }
187}
188
189void ILibraryAppletAccessor::FrontendExecuteInteractive() {
190 if (applet->frontend) {
191 applet->frontend->ExecuteInteractive();
192 applet->frontend->Execute();
193 }
194}
195
196void ILibraryAppletAccessor::FrontendRequestExit() {
197 if (applet->frontend) {
198 applet->frontend->RequestExit();
199 }
200}
201
202} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_accessor.h b/src/core/hle/service/am/library_applet_accessor.h
new file mode 100644
index 000000000..8be29e003
--- /dev/null
+++ b/src/core/hle/service/am/library_applet_accessor.h
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10class AppletDataBroker;
11struct Applet;
12
13class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
14public:
15 explicit ILibraryAppletAccessor(Core::System& system_,
16 std::shared_ptr<AppletDataBroker> broker_,
17 std::shared_ptr<Applet> applet_);
18 ~ILibraryAppletAccessor();
19
20protected:
21 void GetAppletStateChangedEvent(HLERequestContext& ctx);
22 void IsCompleted(HLERequestContext& ctx);
23 void GetResult(HLERequestContext& ctx);
24 void PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx);
25 void Start(HLERequestContext& ctx);
26 void RequestExit(HLERequestContext& ctx);
27 void PushInData(HLERequestContext& ctx);
28 void PopOutData(HLERequestContext& ctx);
29 void PushInteractiveInData(HLERequestContext& ctx);
30 void PopInteractiveOutData(HLERequestContext& ctx);
31 void GetPopOutDataEvent(HLERequestContext& ctx);
32 void GetPopInteractiveOutDataEvent(HLERequestContext& ctx);
33 void GetIndirectLayerConsumerHandle(HLERequestContext& ctx);
34
35 void FrontendExecute();
36 void FrontendExecuteInteractive();
37 void FrontendRequestExit();
38
39 const std::shared_ptr<AppletDataBroker> broker;
40 const std::shared_ptr<Applet> applet;
41};
42
43} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_creator.cpp b/src/core/hle/service/am/library_applet_creator.cpp
new file mode 100644
index 000000000..47bab7528
--- /dev/null
+++ b/src/core/hle/service/am/library_applet_creator.cpp
@@ -0,0 +1,271 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_transfer_memory.h"
5#include "core/hle/service/am/applet_data_broker.h"
6#include "core/hle/service/am/applet_manager.h"
7#include "core/hle/service/am/frontend/applets.h"
8#include "core/hle/service/am/library_applet_accessor.h"
9#include "core/hle/service/am/library_applet_creator.h"
10#include "core/hle/service/am/library_applet_storage.h"
11#include "core/hle/service/am/storage.h"
12#include "core/hle/service/ipc_helpers.h"
13#include "core/hle/service/sm/sm.h"
14
15namespace Service::AM {
16
17namespace {
18
19AppletProgramId AppletIdToProgramId(AppletId applet_id) {
20 switch (applet_id) {
21 case AppletId::OverlayDisplay:
22 return AppletProgramId::OverlayDisplay;
23 case AppletId::QLaunch:
24 return AppletProgramId::QLaunch;
25 case AppletId::Starter:
26 return AppletProgramId::Starter;
27 case AppletId::Auth:
28 return AppletProgramId::Auth;
29 case AppletId::Cabinet:
30 return AppletProgramId::Cabinet;
31 case AppletId::Controller:
32 return AppletProgramId::Controller;
33 case AppletId::DataErase:
34 return AppletProgramId::DataErase;
35 case AppletId::Error:
36 return AppletProgramId::Error;
37 case AppletId::NetConnect:
38 return AppletProgramId::NetConnect;
39 case AppletId::ProfileSelect:
40 return AppletProgramId::ProfileSelect;
41 case AppletId::SoftwareKeyboard:
42 return AppletProgramId::SoftwareKeyboard;
43 case AppletId::MiiEdit:
44 return AppletProgramId::MiiEdit;
45 case AppletId::Web:
46 return AppletProgramId::Web;
47 case AppletId::Shop:
48 return AppletProgramId::Shop;
49 case AppletId::PhotoViewer:
50 return AppletProgramId::PhotoViewer;
51 case AppletId::Settings:
52 return AppletProgramId::Settings;
53 case AppletId::OfflineWeb:
54 return AppletProgramId::OfflineWeb;
55 case AppletId::LoginShare:
56 return AppletProgramId::LoginShare;
57 case AppletId::WebAuth:
58 return AppletProgramId::WebAuth;
59 case AppletId::MyPage:
60 return AppletProgramId::MyPage;
61 default:
62 return static_cast<AppletProgramId>(0);
63 }
64}
65
66[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(
67 Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id,
68 LibraryAppletMode mode) {
69 const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
70 if (program_id == 0) {
71 // Unknown applet
72 return {};
73 }
74
75 auto process = std::make_unique<Process>(system);
76 if (!process->Initialize(program_id)) {
77 // Couldn't initialize the guest process
78 return {};
79 }
80
81 const auto applet = std::make_shared<Applet>(system, std::move(process));
82 applet->program_id = program_id;
83 applet->applet_id = applet_id;
84 applet->type = AppletType::LibraryApplet;
85 applet->library_applet_mode = mode;
86
87 // Set focus state
88 switch (mode) {
89 case LibraryAppletMode::AllForeground:
90 case LibraryAppletMode::NoUI:
91 applet->focus_state = FocusState::InFocus;
92 applet->hid_registration.EnableAppletToGetInput(true);
93 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
94 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
95 break;
96 case LibraryAppletMode::AllForegroundInitiallyHidden:
97 applet->system_buffer_manager.SetWindowVisibility(false);
98 applet->focus_state = FocusState::NotInFocus;
99 applet->hid_registration.EnableAppletToGetInput(false);
100 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
101 break;
102 case LibraryAppletMode::Background:
103 case LibraryAppletMode::BackgroundIndirectDisplay:
104 default:
105 applet->focus_state = FocusState::Background;
106 applet->hid_registration.EnableAppletToGetInput(true);
107 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
108 break;
109 }
110
111 auto broker = std::make_shared<AppletDataBroker>(system);
112 applet->caller_applet = caller_applet;
113 applet->caller_applet_broker = broker;
114
115 system.GetAppletManager().InsertApplet(applet);
116
117 return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
118}
119
120[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(
121 Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id,
122 LibraryAppletMode mode) {
123 const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
124
125 auto process = std::make_unique<Process>(system);
126 auto applet = std::make_shared<Applet>(system, std::move(process));
127 applet->program_id = program_id;
128 applet->applet_id = applet_id;
129 applet->type = AppletType::LibraryApplet;
130 applet->library_applet_mode = mode;
131
132 auto storage = std::make_shared<AppletDataBroker>(system);
133 applet->caller_applet = caller_applet;
134 applet->caller_applet_broker = storage;
135 applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
136
137 return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
138}
139
140} // namespace
141
142ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet_)
143 : ServiceFramework{system_, "ILibraryAppletCreator"}, applet{std::move(applet_)} {
144 static const FunctionInfo functions[] = {
145 {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
146 {1, nullptr, "TerminateAllLibraryApplets"},
147 {2, nullptr, "AreAnyLibraryAppletsLeft"},
148 {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
149 {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
150 {12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"},
151 };
152 RegisterHandlers(functions);
153}
154
155ILibraryAppletCreator::~ILibraryAppletCreator() = default;
156
157void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) {
158 IPC::RequestParser rp{ctx};
159
160 const auto applet_id = rp.PopRaw<AppletId>();
161 const auto applet_mode = rp.PopRaw<LibraryAppletMode>();
162
163 LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
164 applet_mode);
165
166 auto library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode);
167 if (!library_applet) {
168 LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
169
170 IPC::ResponseBuilder rb{ctx, 2};
171 rb.Push(ResultUnknown);
172 return;
173 }
174
175 // Applet is created, can now be launched.
176 applet->library_applet_launchable_event.Signal();
177
178 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
179 rb.Push(ResultSuccess);
180 rb.PushIpcInterface<ILibraryAppletAccessor>(library_applet);
181}
182
183void ILibraryAppletCreator::CreateStorage(HLERequestContext& ctx) {
184 IPC::RequestParser rp{ctx};
185
186 const s64 size{rp.Pop<s64>()};
187
188 LOG_DEBUG(Service_AM, "called, size={}", size);
189
190 if (size <= 0) {
191 LOG_ERROR(Service_AM, "size is less than or equal to 0");
192 IPC::ResponseBuilder rb{ctx, 2};
193 rb.Push(ResultUnknown);
194 return;
195 }
196
197 std::vector<u8> data(size);
198
199 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
200 rb.Push(ResultSuccess);
201 rb.PushIpcInterface<IStorage>(system, AM::CreateStorage(std::move(data)));
202}
203
204void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx) {
205 IPC::RequestParser rp{ctx};
206
207 struct Parameters {
208 bool is_writable;
209 s64 size;
210 };
211
212 const auto params{rp.PopRaw<Parameters>()};
213 const auto handle{ctx.GetCopyHandle(0)};
214
215 LOG_DEBUG(Service_AM, "called, is_writable={}, size={}, handle={:08X}", params.is_writable,
216 params.size, handle);
217
218 if (params.size <= 0) {
219 LOG_ERROR(Service_AM, "size is less than or equal to 0");
220 IPC::ResponseBuilder rb{ctx, 2};
221 rb.Push(ResultUnknown);
222 return;
223 }
224
225 auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
226
227 if (transfer_mem.IsNull()) {
228 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
229 IPC::ResponseBuilder rb{ctx, 2};
230 rb.Push(ResultUnknown);
231 return;
232 }
233
234 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
235 rb.Push(ResultSuccess);
236 rb.PushIpcInterface<IStorage>(
237 system, AM::CreateTransferMemoryStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(),
238 params.is_writable, params.size));
239}
240
241void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
242 IPC::RequestParser rp{ctx};
243
244 const s64 size{rp.Pop<s64>()};
245 const auto handle{ctx.GetCopyHandle(0)};
246
247 LOG_DEBUG(Service_AM, "called, size={}, handle={:08X}", size, handle);
248
249 if (size <= 0) {
250 LOG_ERROR(Service_AM, "size is less than or equal to 0");
251 IPC::ResponseBuilder rb{ctx, 2};
252 rb.Push(ResultUnknown);
253 return;
254 }
255
256 auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
257
258 if (transfer_mem.IsNull()) {
259 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
260 IPC::ResponseBuilder rb{ctx, 2};
261 rb.Push(ResultUnknown);
262 return;
263 }
264
265 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
266 rb.Push(ResultSuccess);
267 rb.PushIpcInterface<IStorage>(
268 system, AM::CreateHandleStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(), size));
269}
270
271} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_creator.h b/src/core/hle/service/am/library_applet_creator.h
new file mode 100644
index 000000000..551f287bd
--- /dev/null
+++ b/src/core/hle/service/am/library_applet_creator.h
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10struct Applet;
11
12class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
13public:
14 explicit ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet_);
15 ~ILibraryAppletCreator() override;
16
17private:
18 void CreateLibraryApplet(HLERequestContext& ctx);
19 void CreateStorage(HLERequestContext& ctx);
20 void CreateTransferMemoryStorage(HLERequestContext& ctx);
21 void CreateHandleStorage(HLERequestContext& ctx);
22
23 const std::shared_ptr<Applet> applet;
24};
25
26} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_proxy.cpp b/src/core/hle/service/am/library_applet_proxy.cpp
new file mode 100644
index 000000000..d6108fba3
--- /dev/null
+++ b/src/core/hle/service/am/library_applet_proxy.cpp
@@ -0,0 +1,143 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/applet_common_functions.h"
5#include "core/hle/service/am/audio_controller.h"
6#include "core/hle/service/am/common_state_getter.h"
7#include "core/hle/service/am/debug_functions.h"
8#include "core/hle/service/am/display_controller.h"
9#include "core/hle/service/am/global_state_controller.h"
10#include "core/hle/service/am/home_menu_functions.h"
11#include "core/hle/service/am/library_applet_creator.h"
12#include "core/hle/service/am/library_applet_proxy.h"
13#include "core/hle/service/am/library_applet_self_accessor.h"
14#include "core/hle/service/am/process_winding_controller.h"
15#include "core/hle/service/am/self_controller.h"
16#include "core/hle/service/am/window_controller.h"
17#include "core/hle/service/ipc_helpers.h"
18
19namespace Service::AM {
20
21ILibraryAppletProxy::ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
22 std::shared_ptr<Applet> applet_, Core::System& system_)
23 : ServiceFramework{system_, "ILibraryAppletProxy"}, nvnflinger{nvnflinger_}, applet{std::move(
24 applet_)} {
25 // clang-format off
26 static const FunctionInfo functions[] = {
27 {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
28 {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
29 {2, &ILibraryAppletProxy::GetWindowController, "GetWindowController"},
30 {3, &ILibraryAppletProxy::GetAudioController, "GetAudioController"},
31 {4, &ILibraryAppletProxy::GetDisplayController, "GetDisplayController"},
32 {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"},
33 {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
34 {20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"},
35 {21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"},
36 {22, &ILibraryAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
37 {23, &ILibraryAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
38 {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
39 };
40 // clang-format on
41
42 RegisterHandlers(functions);
43}
44
45ILibraryAppletProxy::~ILibraryAppletProxy() = default;
46
47void ILibraryAppletProxy::GetCommonStateGetter(HLERequestContext& ctx) {
48 LOG_DEBUG(Service_AM, "called");
49
50 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
51 rb.Push(ResultSuccess);
52 rb.PushIpcInterface<ICommonStateGetter>(system, applet);
53}
54
55void ILibraryAppletProxy::GetSelfController(HLERequestContext& ctx) {
56 LOG_DEBUG(Service_AM, "called");
57
58 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
59 rb.Push(ResultSuccess);
60 rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger);
61}
62
63void ILibraryAppletProxy::GetWindowController(HLERequestContext& ctx) {
64 LOG_DEBUG(Service_AM, "called");
65
66 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
67 rb.Push(ResultSuccess);
68 rb.PushIpcInterface<IWindowController>(system, applet);
69}
70
71void ILibraryAppletProxy::GetAudioController(HLERequestContext& ctx) {
72 LOG_DEBUG(Service_AM, "called");
73
74 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
75 rb.Push(ResultSuccess);
76 rb.PushIpcInterface<IAudioController>(system);
77}
78
79void ILibraryAppletProxy::GetDisplayController(HLERequestContext& ctx) {
80 LOG_DEBUG(Service_AM, "called");
81
82 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
83 rb.Push(ResultSuccess);
84 rb.PushIpcInterface<IDisplayController>(system, applet);
85}
86
87void ILibraryAppletProxy::GetProcessWindingController(HLERequestContext& ctx) {
88 LOG_DEBUG(Service_AM, "called");
89
90 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
91 rb.Push(ResultSuccess);
92 rb.PushIpcInterface<IProcessWindingController>(system, applet);
93}
94
95void ILibraryAppletProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
96 LOG_DEBUG(Service_AM, "called");
97
98 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
99 rb.Push(ResultSuccess);
100 rb.PushIpcInterface<ILibraryAppletCreator>(system, applet);
101}
102
103void ILibraryAppletProxy::OpenLibraryAppletSelfAccessor(HLERequestContext& ctx) {
104 LOG_DEBUG(Service_AM, "called");
105
106 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
107 rb.Push(ResultSuccess);
108 rb.PushIpcInterface<ILibraryAppletSelfAccessor>(system, applet);
109}
110
111void ILibraryAppletProxy::GetAppletCommonFunctions(HLERequestContext& ctx) {
112 LOG_DEBUG(Service_AM, "called");
113
114 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
115 rb.Push(ResultSuccess);
116 rb.PushIpcInterface<IAppletCommonFunctions>(system, applet);
117}
118
119void ILibraryAppletProxy::GetHomeMenuFunctions(HLERequestContext& ctx) {
120 LOG_DEBUG(Service_AM, "called");
121
122 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
123 rb.Push(ResultSuccess);
124 rb.PushIpcInterface<IHomeMenuFunctions>(system);
125}
126
127void ILibraryAppletProxy::GetGlobalStateController(HLERequestContext& ctx) {
128 LOG_DEBUG(Service_AM, "called");
129
130 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
131 rb.Push(ResultSuccess);
132 rb.PushIpcInterface<IGlobalStateController>(system);
133}
134
135void ILibraryAppletProxy::GetDebugFunctions(HLERequestContext& ctx) {
136 LOG_DEBUG(Service_AM, "called");
137
138 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
139 rb.Push(ResultSuccess);
140 rb.PushIpcInterface<IDebugFunctions>(system);
141}
142
143} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_proxy.h b/src/core/hle/service/am/library_applet_proxy.h
new file mode 100644
index 000000000..8f7a25897
--- /dev/null
+++ b/src/core/hle/service/am/library_applet_proxy.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10struct Applet;
11
12class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
13public:
14 explicit ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
15 std::shared_ptr<Applet> applet_, Core::System& system_);
16 ~ILibraryAppletProxy();
17
18private:
19 void GetCommonStateGetter(HLERequestContext& ctx);
20 void GetSelfController(HLERequestContext& ctx);
21 void GetWindowController(HLERequestContext& ctx);
22 void GetAudioController(HLERequestContext& ctx);
23 void GetDisplayController(HLERequestContext& ctx);
24 void GetProcessWindingController(HLERequestContext& ctx);
25 void GetLibraryAppletCreator(HLERequestContext& ctx);
26 void OpenLibraryAppletSelfAccessor(HLERequestContext& ctx);
27 void GetAppletCommonFunctions(HLERequestContext& ctx);
28 void GetHomeMenuFunctions(HLERequestContext& ctx);
29 void GetGlobalStateController(HLERequestContext& ctx);
30 void GetDebugFunctions(HLERequestContext& ctx);
31
32 Nvnflinger::Nvnflinger& nvnflinger;
33 std::shared_ptr<Applet> applet;
34};
35
36} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_self_accessor.cpp b/src/core/hle/service/am/library_applet_self_accessor.cpp
new file mode 100644
index 000000000..b560f580b
--- /dev/null
+++ b/src/core/hle/service/am/library_applet_self_accessor.cpp
@@ -0,0 +1,338 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core_timing.h"
6#include "core/file_sys/control_metadata.h"
7#include "core/file_sys/patch_manager.h"
8#include "core/file_sys/registered_cache.h"
9#include "core/hle/service/acc/profile_manager.h"
10#include "core/hle/service/am/am_results.h"
11#include "core/hle/service/am/applet_data_broker.h"
12#include "core/hle/service/am/applet_manager.h"
13#include "core/hle/service/am/frontend/applet_cabinet.h"
14#include "core/hle/service/am/frontend/applet_controller.h"
15#include "core/hle/service/am/frontend/applet_mii_edit_types.h"
16#include "core/hle/service/am/frontend/applet_software_keyboard_types.h"
17#include "core/hle/service/am/frontend/applets.h"
18#include "core/hle/service/am/library_applet_self_accessor.h"
19#include "core/hle/service/am/storage.h"
20#include "core/hle/service/ipc_helpers.h"
21#include "core/hle/service/ns/ns.h"
22#include "core/hle/service/sm/sm.h"
23#include "hid_core/hid_types.h"
24
25namespace Service::AM {
26
27namespace {
28
29AppletIdentityInfo GetCallerIdentity(std::shared_ptr<Applet> applet) {
30 if (const auto caller_applet = applet->caller_applet.lock(); caller_applet) {
31 // TODO: is this actually the application ID?
32 return {
33 .applet_id = caller_applet->applet_id,
34 .application_id = caller_applet->program_id,
35 };
36 } else {
37 return {
38 .applet_id = AppletId::QLaunch,
39 .application_id = 0x0100000000001000ull,
40 };
41 }
42}
43
44} // namespace
45
46ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_,
47 std::shared_ptr<Applet> applet_)
48 : ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, applet{std::move(applet_)},
49 broker{applet->caller_applet_broker} {
50 // clang-format off
51 static const FunctionInfo functions[] = {
52 {0, &ILibraryAppletSelfAccessor::PopInData, "PopInData"},
53 {1, &ILibraryAppletSelfAccessor::PushOutData, "PushOutData"},
54 {2, &ILibraryAppletSelfAccessor::PopInteractiveInData, "PopInteractiveInData"},
55 {3, &ILibraryAppletSelfAccessor::PushInteractiveOutData, "PushInteractiveOutData"},
56 {5, &ILibraryAppletSelfAccessor::GetPopInDataEvent, "GetPopInDataEvent"},
57 {6, &ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent, "GetPopInteractiveInDataEvent"},
58 {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"},
59 {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"},
60 {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"},
61 {13, &ILibraryAppletSelfAccessor::CanUseApplicationCore, "CanUseApplicationCore"},
62 {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"},
63 {15, nullptr, "GetMainAppletApplicationControlProperty"},
64 {16, nullptr, "GetMainAppletStorageId"},
65 {17, nullptr, "GetCallerAppletIdentityInfoStack"},
66 {18, nullptr, "GetNextReturnDestinationAppletIdentityInfo"},
67 {19, &ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout, "GetDesirableKeyboardLayout"},
68 {20, nullptr, "PopExtraStorage"},
69 {25, nullptr, "GetPopExtraStorageEvent"},
70 {30, nullptr, "UnpopInData"},
71 {31, nullptr, "UnpopExtraStorage"},
72 {40, nullptr, "GetIndirectLayerProducerHandle"},
73 {50, nullptr, "ReportVisibleError"},
74 {51, nullptr, "ReportVisibleErrorWithErrorContext"},
75 {60, &ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage, "GetMainAppletApplicationDesiredLanguage"},
76 {70, &ILibraryAppletSelfAccessor::GetCurrentApplicationId, "GetCurrentApplicationId"},
77 {80, nullptr, "RequestExitToSelf"},
78 {90, nullptr, "CreateApplicationAndPushAndRequestToLaunch"},
79 {100, nullptr, "CreateGameMovieTrimmer"},
80 {101, nullptr, "ReserveResourceForMovieOperation"},
81 {102, nullptr, "UnreserveResourceForMovieOperation"},
82 {110, &ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers, "GetMainAppletAvailableUsers"},
83 {120, nullptr, "GetLaunchStorageInfoForDebug"},
84 {130, nullptr, "GetGpuErrorDetectedSystemEvent"},
85 {140, nullptr, "SetApplicationMemoryReservation"},
86 {150, &ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually, "ShouldSetGpuTimeSliceManually"},
87 {160, &ILibraryAppletSelfAccessor::Cmd160, "Cmd160"},
88 };
89 // clang-format on
90 RegisterHandlers(functions);
91}
92
93ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default;
94
95void ILibraryAppletSelfAccessor::PopInData(HLERequestContext& ctx) {
96 LOG_INFO(Service_AM, "called");
97
98 std::shared_ptr<IStorage> data;
99 const auto res = broker->GetInData().Pop(&data);
100
101 if (res.IsSuccess()) {
102 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
103 rb.Push(res);
104 rb.PushIpcInterface(std::move(data));
105 } else {
106 IPC::ResponseBuilder rb{ctx, 2};
107 rb.Push(res);
108 }
109}
110
111void ILibraryAppletSelfAccessor::PushOutData(HLERequestContext& ctx) {
112 LOG_INFO(Service_AM, "called");
113
114 IPC::RequestParser rp{ctx};
115 broker->GetOutData().Push(rp.PopIpcInterface<IStorage>().lock());
116
117 IPC::ResponseBuilder rb{ctx, 2};
118 rb.Push(ResultSuccess);
119}
120
121void ILibraryAppletSelfAccessor::PopInteractiveInData(HLERequestContext& ctx) {
122 LOG_INFO(Service_AM, "called");
123
124 std::shared_ptr<IStorage> data;
125 const auto res = broker->GetInteractiveInData().Pop(&data);
126
127 if (res.IsSuccess()) {
128 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
129 rb.Push(res);
130 rb.PushIpcInterface(std::move(data));
131 } else {
132 IPC::ResponseBuilder rb{ctx, 2};
133 rb.Push(res);
134 }
135}
136
137void ILibraryAppletSelfAccessor::PushInteractiveOutData(HLERequestContext& ctx) {
138 LOG_INFO(Service_AM, "called");
139
140 IPC::RequestParser rp{ctx};
141 broker->GetInteractiveOutData().Push(rp.PopIpcInterface<IStorage>().lock());
142
143 IPC::ResponseBuilder rb{ctx, 2};
144 rb.Push(ResultSuccess);
145}
146
147void ILibraryAppletSelfAccessor::GetPopInDataEvent(HLERequestContext& ctx) {
148 LOG_INFO(Service_AM, "called");
149
150 IPC::ResponseBuilder rb{ctx, 2, 1};
151 rb.Push(ResultSuccess);
152 rb.PushCopyObjects(broker->GetInData().GetEvent());
153}
154
155void ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent(HLERequestContext& ctx) {
156 LOG_INFO(Service_AM, "called");
157
158 IPC::ResponseBuilder rb{ctx, 2, 1};
159 rb.Push(ResultSuccess);
160 rb.PushCopyObjects(broker->GetInteractiveInData().GetEvent());
161}
162
163void ILibraryAppletSelfAccessor::ExitProcessAndReturn(HLERequestContext& ctx) {
164 LOG_INFO(Service_AM, "called");
165
166 system.GetAppletManager().TerminateAndRemoveApplet(applet->aruid);
167 broker->SignalCompletion();
168
169 IPC::ResponseBuilder rb{ctx, 2};
170 rb.Push(ResultSuccess);
171}
172
173void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
174 struct LibraryAppletInfo {
175 AppletId applet_id;
176 LibraryAppletMode library_applet_mode;
177 };
178
179 LOG_WARNING(Service_AM, "(STUBBED) called");
180
181 const LibraryAppletInfo applet_info{
182 .applet_id = applet->applet_id,
183 .library_applet_mode = applet->library_applet_mode,
184 };
185
186 IPC::ResponseBuilder rb{ctx, 4};
187 rb.Push(ResultSuccess);
188 rb.PushRaw(applet_info);
189}
190
191void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) {
192 LOG_WARNING(Service_AM, "(STUBBED) called");
193
194 const AppletIdentityInfo applet_info{
195 .applet_id = AppletId::QLaunch,
196 .application_id = 0x0100000000001000ull,
197 };
198
199 IPC::ResponseBuilder rb{ctx, 6};
200 rb.Push(ResultSuccess);
201 rb.PushRaw(applet_info);
202}
203
204void ILibraryAppletSelfAccessor::CanUseApplicationCore(HLERequestContext& ctx) {
205 LOG_WARNING(Service_AM, "(STUBBED) called");
206
207 // TODO: This appears to read the NPDM from state and check the core mask of the applet.
208 IPC::ResponseBuilder rb{ctx, 3};
209 rb.Push(ResultSuccess);
210 rb.Push<u8>(0);
211}
212
213void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
214 LOG_WARNING(Service_AM, "(STUBBED) called");
215
216 IPC::ResponseBuilder rb{ctx, 6};
217 rb.Push(ResultSuccess);
218 rb.PushRaw(GetCallerIdentity(applet));
219}
220
221void ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout(HLERequestContext& ctx) {
222 LOG_WARNING(Service_AM, "(STUBBED) called");
223
224 IPC::ResponseBuilder rb{ctx, 3};
225 rb.Push(ResultSuccess);
226 rb.Push<u32>(0);
227}
228
229void ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage(HLERequestContext& ctx) {
230 // FIXME: this is copied from IApplicationFunctions::GetDesiredLanguage
231 auto identity = GetCallerIdentity(applet);
232
233 // TODO(bunnei): This should be configurable
234 LOG_DEBUG(Service_AM, "called");
235
236 // Get supported languages from NACP, if possible
237 // Default to 0 (all languages supported)
238 u32 supported_languages = 0;
239
240 const auto res = [this, identity] {
241 const FileSys::PatchManager pm{identity.application_id, system.GetFileSystemController(),
242 system.GetContentProvider()};
243 auto metadata = pm.GetControlMetadata();
244 if (metadata.first != nullptr) {
245 return metadata;
246 }
247
248 const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(identity.application_id),
249 system.GetFileSystemController(),
250 system.GetContentProvider()};
251 return pm_update.GetControlMetadata();
252 }();
253
254 if (res.first != nullptr) {
255 supported_languages = res.first->GetSupportedLanguages();
256 }
257
258 // Call IApplicationManagerInterface implementation.
259 auto& service_manager = system.ServiceManager();
260 auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2");
261 auto app_man = ns_am2->GetApplicationManagerInterface();
262
263 // Get desired application language
264 u8 desired_language{};
265 const auto res_lang =
266 app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages);
267 if (res_lang != ResultSuccess) {
268 IPC::ResponseBuilder rb{ctx, 2};
269 rb.Push(res_lang);
270 return;
271 }
272
273 // Convert to settings language code.
274 u64 language_code{};
275 const auto res_code =
276 app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language);
277 if (res_code != ResultSuccess) {
278 IPC::ResponseBuilder rb{ctx, 2};
279 rb.Push(res_code);
280 return;
281 }
282
283 LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code);
284
285 IPC::ResponseBuilder rb{ctx, 4};
286 rb.Push(ResultSuccess);
287 rb.Push(language_code);
288}
289
290void ILibraryAppletSelfAccessor::GetCurrentApplicationId(HLERequestContext& ctx) {
291 LOG_WARNING(Service_AM, "(STUBBED) called");
292
293 u64 application_id = 0;
294 if (auto caller_applet = applet->caller_applet.lock(); caller_applet) {
295 application_id = caller_applet->program_id;
296 }
297
298 IPC::ResponseBuilder rb{ctx, 4};
299 rb.Push(ResultSuccess);
300 rb.Push(application_id);
301}
302
303void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) {
304 const Service::Account::ProfileManager manager{};
305 bool is_empty{true};
306 s32 user_count{-1};
307
308 LOG_INFO(Service_AM, "called");
309
310 if (manager.GetUserCount() > 0) {
311 is_empty = false;
312 user_count = static_cast<s32>(manager.GetUserCount());
313 ctx.WriteBuffer(manager.GetAllUsers());
314 }
315
316 IPC::ResponseBuilder rb{ctx, 4};
317 rb.Push(ResultSuccess);
318 rb.Push<u8>(is_empty);
319 rb.Push(user_count);
320}
321
322void ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually(HLERequestContext& ctx) {
323 LOG_WARNING(Service_AM, "(STUBBED) called");
324
325 IPC::ResponseBuilder rb{ctx, 3};
326 rb.Push(ResultSuccess);
327 rb.Push<u8>(0);
328}
329
330void ILibraryAppletSelfAccessor::Cmd160(HLERequestContext& ctx) {
331 LOG_WARNING(Service_AM, "(STUBBED) called");
332
333 IPC::ResponseBuilder rb{ctx, 4};
334 rb.Push(ResultSuccess);
335 rb.Push<u64>(0);
336}
337
338} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_self_accessor.h b/src/core/hle/service/am/library_applet_self_accessor.h
new file mode 100644
index 000000000..8717a989a
--- /dev/null
+++ b/src/core/hle/service/am/library_applet_self_accessor.h
@@ -0,0 +1,44 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <deque>
7#include <vector>
8
9#include "core/hle/service/service.h"
10
11namespace Service::AM {
12
13class AppletDataBroker;
14struct Applet;
15
16class ILibraryAppletSelfAccessor final : public ServiceFramework<ILibraryAppletSelfAccessor> {
17public:
18 explicit ILibraryAppletSelfAccessor(Core::System& system_, std::shared_ptr<Applet> applet_);
19 ~ILibraryAppletSelfAccessor() override;
20
21private:
22 void PopInData(HLERequestContext& ctx);
23 void PushOutData(HLERequestContext& ctx);
24 void PopInteractiveInData(HLERequestContext& ctx);
25 void PushInteractiveOutData(HLERequestContext& ctx);
26 void GetPopInDataEvent(HLERequestContext& ctx);
27 void GetPopInteractiveInDataEvent(HLERequestContext& ctx);
28 void GetLibraryAppletInfo(HLERequestContext& ctx);
29 void GetMainAppletIdentityInfo(HLERequestContext& ctx);
30 void CanUseApplicationCore(HLERequestContext& ctx);
31 void ExitProcessAndReturn(HLERequestContext& ctx);
32 void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
33 void GetDesirableKeyboardLayout(HLERequestContext& ctx);
34 void GetMainAppletApplicationDesiredLanguage(HLERequestContext& ctx);
35 void GetCurrentApplicationId(HLERequestContext& ctx);
36 void GetMainAppletAvailableUsers(HLERequestContext& ctx);
37 void ShouldSetGpuTimeSliceManually(HLERequestContext& ctx);
38 void Cmd160(HLERequestContext& ctx);
39
40 const std::shared_ptr<Applet> applet;
41 const std::shared_ptr<AppletDataBroker> broker;
42};
43
44} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_storage.cpp b/src/core/hle/service/am/library_applet_storage.cpp
new file mode 100644
index 000000000..46e6c0111
--- /dev/null
+++ b/src/core/hle/service/am/library_applet_storage.cpp
@@ -0,0 +1,140 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_transfer_memory.h"
5#include "core/hle/service/am/am_results.h"
6#include "core/hle/service/am/library_applet_storage.h"
7#include "core/memory.h"
8
9namespace Service::AM {
10
11namespace {
12
13Result ValidateOffset(s64 offset, size_t size, size_t data_size) {
14 R_UNLESS(offset >= 0, AM::ResultInvalidOffset);
15
16 const size_t begin = offset;
17 const size_t end = begin + size;
18
19 R_UNLESS(begin <= end && end <= data_size, AM::ResultInvalidOffset);
20 R_SUCCEED();
21}
22
23class BufferLibraryAppletStorage final : public LibraryAppletStorage {
24public:
25 explicit BufferLibraryAppletStorage(std::vector<u8>&& data) : m_data(std::move(data)) {}
26 ~BufferLibraryAppletStorage() = default;
27
28 Result Read(s64 offset, void* buffer, size_t size) override {
29 R_TRY(ValidateOffset(offset, size, m_data.size()));
30
31 std::memcpy(buffer, m_data.data() + offset, size);
32
33 R_SUCCEED();
34 }
35
36 Result Write(s64 offset, const void* buffer, size_t size) override {
37 R_TRY(ValidateOffset(offset, size, m_data.size()));
38
39 std::memcpy(m_data.data() + offset, buffer, size);
40
41 R_SUCCEED();
42 }
43
44 s64 GetSize() override {
45 return m_data.size();
46 }
47
48 Kernel::KTransferMemory* GetHandle() override {
49 return nullptr;
50 }
51
52private:
53 std::vector<u8> m_data;
54};
55
56class TransferMemoryLibraryAppletStorage : public LibraryAppletStorage {
57public:
58 explicit TransferMemoryLibraryAppletStorage(Core::Memory::Memory& memory,
59 Kernel::KTransferMemory* trmem, bool is_writable,
60 s64 size)
61 : m_memory(memory), m_trmem(trmem), m_is_writable(is_writable), m_size(size) {
62 m_trmem->Open();
63 }
64
65 ~TransferMemoryLibraryAppletStorage() {
66 m_trmem->Close();
67 m_trmem = nullptr;
68 }
69
70 Result Read(s64 offset, void* buffer, size_t size) override {
71 R_TRY(ValidateOffset(offset, size, m_size));
72
73 m_memory.ReadBlock(m_trmem->GetSourceAddress(), buffer, size);
74
75 R_SUCCEED();
76 }
77
78 Result Write(s64 offset, const void* buffer, size_t size) override {
79 R_UNLESS(m_is_writable, ResultUnknown);
80 R_TRY(ValidateOffset(offset, size, m_size));
81
82 m_memory.WriteBlock(m_trmem->GetSourceAddress(), buffer, size);
83
84 R_SUCCEED();
85 }
86
87 s64 GetSize() override {
88 return m_size;
89 }
90
91 Kernel::KTransferMemory* GetHandle() override {
92 return nullptr;
93 }
94
95protected:
96 Core::Memory::Memory& m_memory;
97 Kernel::KTransferMemory* m_trmem;
98 bool m_is_writable;
99 s64 m_size;
100};
101
102class HandleLibraryAppletStorage : public TransferMemoryLibraryAppletStorage {
103public:
104 explicit HandleLibraryAppletStorage(Core::Memory::Memory& memory,
105 Kernel::KTransferMemory* trmem, s64 size)
106 : TransferMemoryLibraryAppletStorage(memory, trmem, true, size) {}
107 ~HandleLibraryAppletStorage() = default;
108
109 Kernel::KTransferMemory* GetHandle() override {
110 return m_trmem;
111 }
112};
113
114} // namespace
115
116LibraryAppletStorage::~LibraryAppletStorage() = default;
117
118std::vector<u8> LibraryAppletStorage::GetData() {
119 std::vector<u8> data(this->GetSize());
120 this->Read(0, data.data(), data.size());
121 return data;
122}
123
124std::shared_ptr<LibraryAppletStorage> CreateStorage(std::vector<u8>&& data) {
125 return std::make_shared<BufferLibraryAppletStorage>(std::move(data));
126}
127
128std::shared_ptr<LibraryAppletStorage> CreateTransferMemoryStorage(Core::Memory::Memory& memory,
129 Kernel::KTransferMemory* trmem,
130 bool is_writable, s64 size) {
131 return std::make_shared<TransferMemoryLibraryAppletStorage>(memory, trmem, is_writable, size);
132}
133
134std::shared_ptr<LibraryAppletStorage> CreateHandleStorage(Core::Memory::Memory& memory,
135 Kernel::KTransferMemory* trmem,
136 s64 size) {
137 return std::make_shared<HandleLibraryAppletStorage>(memory, trmem, size);
138}
139
140} // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_storage.h b/src/core/hle/service/am/library_applet_storage.h
new file mode 100644
index 000000000..7f53f3a9c
--- /dev/null
+++ b/src/core/hle/service/am/library_applet_storage.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Core::Memory {
9class Memory;
10}
11
12namespace Kernel {
13class KTransferMemory;
14}
15
16namespace Service::AM {
17
18class LibraryAppletStorage {
19public:
20 virtual ~LibraryAppletStorage();
21 virtual Result Read(s64 offset, void* buffer, size_t size) = 0;
22 virtual Result Write(s64 offset, const void* buffer, size_t size) = 0;
23 virtual s64 GetSize() = 0;
24 virtual Kernel::KTransferMemory* GetHandle() = 0;
25
26 std::vector<u8> GetData();
27};
28
29std::shared_ptr<LibraryAppletStorage> CreateStorage(std::vector<u8>&& data);
30std::shared_ptr<LibraryAppletStorage> CreateTransferMemoryStorage(Core::Memory::Memory& memory,
31 Kernel::KTransferMemory* trmem,
32 bool is_writable, s64 size);
33std::shared_ptr<LibraryAppletStorage> CreateHandleStorage(Core::Memory::Memory& memory,
34 Kernel::KTransferMemory* trmem, s64 size);
35
36} // namespace Service::AM
diff --git a/src/core/hle/service/am/lock_accessor.cpp b/src/core/hle/service/am/lock_accessor.cpp
new file mode 100644
index 000000000..d0bd8d95e
--- /dev/null
+++ b/src/core/hle/service/am/lock_accessor.cpp
@@ -0,0 +1,71 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/lock_accessor.h"
5#include "core/hle/service/ipc_helpers.h"
6
7namespace Service::AM {
8
9ILockAccessor::ILockAccessor(Core::System& system_)
10 : ServiceFramework{system_, "ILockAccessor"}, service_context{system_, "ILockAccessor"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {1, &ILockAccessor::TryLock, "TryLock"},
14 {2, &ILockAccessor::Unlock, "Unlock"},
15 {3, &ILockAccessor::GetEvent, "GetEvent"},
16 {4,&ILockAccessor::IsLocked, "IsLocked"},
17 };
18 // clang-format on
19
20 RegisterHandlers(functions);
21
22 lock_event = service_context.CreateEvent("ILockAccessor::LockEvent");
23}
24
25ILockAccessor::~ILockAccessor() {
26 service_context.CloseEvent(lock_event);
27};
28
29void ILockAccessor::TryLock(HLERequestContext& ctx) {
30 IPC::RequestParser rp{ctx};
31 const auto return_handle = rp.Pop<bool>();
32
33 LOG_WARNING(Service_AM, "(STUBBED) called, return_handle={}", return_handle);
34
35 // TODO: When return_handle is true this function should return the lock handle
36
37 is_locked = true;
38
39 IPC::ResponseBuilder rb{ctx, 3};
40 rb.Push(ResultSuccess);
41 rb.Push<u8>(is_locked);
42}
43
44void ILockAccessor::Unlock(HLERequestContext& ctx) {
45 LOG_INFO(Service_AM, "called");
46
47 is_locked = false;
48
49 IPC::ResponseBuilder rb{ctx, 2};
50 rb.Push(ResultSuccess);
51}
52
53void ILockAccessor::GetEvent(HLERequestContext& ctx) {
54 LOG_INFO(Service_AM, "called");
55
56 lock_event->Signal();
57
58 IPC::ResponseBuilder rb{ctx, 2, 1};
59 rb.Push(ResultSuccess);
60 rb.PushCopyObjects(lock_event->GetReadableEvent());
61}
62
63void ILockAccessor::IsLocked(HLERequestContext& ctx) {
64 LOG_INFO(Service_AM, "called");
65
66 IPC::ResponseBuilder rb{ctx, 2};
67 rb.Push(ResultSuccess);
68 rb.Push<u8>(is_locked);
69}
70
71} // namespace Service::AM
diff --git a/src/core/hle/service/am/lock_accessor.h b/src/core/hle/service/am/lock_accessor.h
new file mode 100644
index 000000000..626f60e07
--- /dev/null
+++ b/src/core/hle/service/am/lock_accessor.h
@@ -0,0 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h"
8
9namespace Service::AM {
10
11class ILockAccessor final : public ServiceFramework<ILockAccessor> {
12public:
13 explicit ILockAccessor(Core::System& system_);
14 ~ILockAccessor() override;
15
16private:
17 void TryLock(HLERequestContext& ctx);
18 void Unlock(HLERequestContext& ctx);
19 void GetEvent(HLERequestContext& ctx);
20 void IsLocked(HLERequestContext& ctx);
21
22 bool is_locked{};
23
24 Kernel::KEvent* lock_event;
25 KernelHelpers::ServiceContext service_context;
26};
27
28} // namespace Service::AM
diff --git a/src/core/hle/service/am/managed_layer_holder.cpp b/src/core/hle/service/am/managed_layer_holder.cpp
new file mode 100644
index 000000000..61eb8641a
--- /dev/null
+++ b/src/core/hle/service/am/managed_layer_holder.cpp
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/managed_layer_holder.h"
5#include "core/hle/service/nvnflinger/nvnflinger.h"
6
7namespace Service::AM {
8
9ManagedLayerHolder::ManagedLayerHolder() = default;
10ManagedLayerHolder::~ManagedLayerHolder() {
11 if (!m_nvnflinger) {
12 return;
13 }
14
15 for (const auto& layer : m_managed_display_layers) {
16 m_nvnflinger->DestroyLayer(layer);
17 }
18
19 for (const auto& layer : m_managed_display_recording_layers) {
20 m_nvnflinger->DestroyLayer(layer);
21 }
22
23 m_nvnflinger = nullptr;
24}
25
26void ManagedLayerHolder::Initialize(Nvnflinger::Nvnflinger* nvnflinger) {
27 m_nvnflinger = nvnflinger;
28}
29
30void ManagedLayerHolder::CreateManagedDisplayLayer(u64* out_layer) {
31 // TODO(Subv): Find out how AM determines the display to use, for now just
32 // create the layer in the Default display.
33 const auto display_id = m_nvnflinger->OpenDisplay("Default");
34 const auto layer_id = m_nvnflinger->CreateLayer(*display_id);
35
36 m_managed_display_layers.emplace(*layer_id);
37
38 *out_layer = *layer_id;
39}
40
41void ManagedLayerHolder::CreateManagedDisplaySeparableLayer(u64* out_layer,
42 u64* out_recording_layer) {
43 // TODO(Subv): Find out how AM determines the display to use, for now just
44 // create the layer in the Default display.
45 // This calls nn::vi::CreateRecordingLayer() which creates another layer.
46 // Currently we do not support more than 1 layer per display, output 1 layer id for now.
47 // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
48 // side effects.
49 // TODO: Support multiple layers
50 const auto display_id = m_nvnflinger->OpenDisplay("Default");
51 const auto layer_id = m_nvnflinger->CreateLayer(*display_id);
52
53 m_managed_display_layers.emplace(*layer_id);
54
55 *out_layer = *layer_id;
56 *out_recording_layer = 0;
57}
58
59} // namespace Service::AM
diff --git a/src/core/hle/service/am/managed_layer_holder.h b/src/core/hle/service/am/managed_layer_holder.h
new file mode 100644
index 000000000..f7fe03f24
--- /dev/null
+++ b/src/core/hle/service/am/managed_layer_holder.h
@@ -0,0 +1,32 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <set>
7
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10
11namespace Service::Nvnflinger {
12class Nvnflinger;
13}
14
15namespace Service::AM {
16
17class ManagedLayerHolder {
18public:
19 ManagedLayerHolder();
20 ~ManagedLayerHolder();
21
22 void Initialize(Nvnflinger::Nvnflinger* nvnflinger);
23 void CreateManagedDisplayLayer(u64* out_layer);
24 void CreateManagedDisplaySeparableLayer(u64* out_layer, u64* out_recording_layer);
25
26private:
27 Nvnflinger::Nvnflinger* m_nvnflinger{};
28 std::set<u64> m_managed_display_layers{};
29 std::set<u64> m_managed_display_recording_layers{};
30};
31
32} // namespace Service::AM
diff --git a/src/core/hle/service/am/process.cpp b/src/core/hle/service/am/process.cpp
new file mode 100644
index 000000000..16b685f86
--- /dev/null
+++ b/src/core/hle/service/am/process.cpp
@@ -0,0 +1,138 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5
6#include "core/file_sys/nca_metadata.h"
7#include "core/file_sys/registered_cache.h"
8#include "core/hle/kernel/k_process.h"
9#include "core/hle/service/am/process.h"
10#include "core/hle/service/filesystem/filesystem.h"
11#include "core/loader/loader.h"
12
13namespace Service::AM {
14
15Process::Process(Core::System& system)
16 : m_system(system), m_process(), m_main_thread_priority(), m_main_thread_stack_size(),
17 m_program_id(), m_process_started() {}
18
19Process::~Process() {
20 this->Finalize();
21}
22
23bool Process::Initialize(u64 program_id) {
24 // First, ensure we are not holding another process.
25 this->Finalize();
26
27 // Get the filesystem controller.
28 auto& fsc = m_system.GetFileSystemController();
29
30 // Attempt to load program NCA.
31 const FileSys::RegisteredCache* bis_system{};
32 FileSys::VirtualFile nca{};
33
34 // Get the program NCA from built-in storage.
35 bis_system = fsc.GetSystemNANDContents();
36 if (bis_system) {
37 nca = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
38 }
39
40 // Ensure we retrieved a program NCA.
41 if (!nca) {
42 return false;
43 }
44
45 // Get the appropriate loader to parse this NCA.
46 auto app_loader = Loader::GetLoader(m_system, nca, program_id, 0);
47
48 // Ensure we have a loader which can parse the NCA.
49 if (!app_loader) {
50 return false;
51 }
52
53 // Create the process.
54 auto* const process = Kernel::KProcess::Create(m_system.Kernel());
55 Kernel::KProcess::Register(m_system.Kernel(), process);
56
57 // On exit, ensure we free the additional reference to the process.
58 SCOPE_EXIT({ process->Close(); });
59
60 // Insert process modules into memory.
61 const auto [load_result, load_parameters] = app_loader->Load(*process, m_system);
62
63 // Ensure loading was successful.
64 if (load_result != Loader::ResultStatus::Success) {
65 return false;
66 }
67
68 // TODO: remove this, kernel already tracks this
69 m_system.Kernel().AppendNewProcess(process);
70
71 // Note the load parameters from NPDM.
72 m_main_thread_priority = load_parameters->main_thread_priority;
73 m_main_thread_stack_size = load_parameters->main_thread_stack_size;
74
75 // This process has not started yet.
76 m_process_started = false;
77
78 // Take ownership of the process object.
79 m_process = process;
80 m_process->Open();
81
82 // We succeeded.
83 return true;
84}
85
86void Process::Finalize() {
87 // Terminate, if we are currently holding a process.
88 this->Terminate();
89
90 // Close the process.
91 if (m_process) {
92 m_process->Close();
93
94 // TODO: remove this, kernel already tracks this
95 m_system.Kernel().RemoveProcess(m_process);
96 }
97
98 // Clean up.
99 m_process = nullptr;
100 m_main_thread_priority = 0;
101 m_main_thread_stack_size = 0;
102 m_program_id = 0;
103 m_process_started = false;
104}
105
106bool Process::Run() {
107 // If we already started the process, don't start again.
108 if (m_process_started) {
109 return false;
110 }
111
112 // Start.
113 if (m_process) {
114 m_process->Run(m_main_thread_priority, m_main_thread_stack_size);
115 }
116
117 // Mark as started.
118 m_process_started = true;
119
120 // We succeeded.
121 return true;
122}
123
124void Process::Terminate() {
125 if (m_process) {
126 m_process->Terminate();
127 }
128}
129
130u64 Process::GetProcessId() const {
131 if (m_process) {
132 return m_process->GetProcessId();
133 }
134
135 return 0;
136}
137
138} // namespace Service::AM
diff --git a/src/core/hle/service/am/process.h b/src/core/hle/service/am/process.h
new file mode 100644
index 000000000..4b908ade4
--- /dev/null
+++ b/src/core/hle/service/am/process.h
@@ -0,0 +1,50 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace Kernel {
10class KProcess;
11}
12
13namespace Core {
14class System;
15}
16
17namespace Service::AM {
18
19class Process {
20public:
21 explicit Process(Core::System& system);
22 ~Process();
23
24 bool Initialize(u64 program_id);
25 void Finalize();
26
27 bool Run();
28 void Terminate();
29
30 bool IsInitialized() const {
31 return m_process != nullptr;
32 }
33 u64 GetProcessId() const;
34 u64 GetProgramId() const {
35 return m_program_id;
36 }
37 Kernel::KProcess* GetProcess() const {
38 return m_process;
39 }
40
41private:
42 Core::System& m_system;
43 Kernel::KProcess* m_process{};
44 s32 m_main_thread_priority{};
45 u64 m_main_thread_stack_size{};
46 u64 m_program_id{};
47 bool m_process_started{};
48};
49
50} // namespace Service::AM
diff --git a/src/core/hle/service/am/process_winding_controller.cpp b/src/core/hle/service/am/process_winding_controller.cpp
new file mode 100644
index 000000000..b48b52797
--- /dev/null
+++ b/src/core/hle/service/am/process_winding_controller.cpp
@@ -0,0 +1,56 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/frontend/applets.h"
5#include "core/hle/service/am/library_applet_accessor.h"
6#include "core/hle/service/am/process_winding_controller.h"
7#include "core/hle/service/ipc_helpers.h"
8
9namespace Service::AM {
10
11IProcessWindingController::IProcessWindingController(Core::System& system_,
12 std::shared_ptr<Applet> applet_)
13 : ServiceFramework{system_, "IProcessWindingController"}, applet{std::move(applet_)} {
14 // clang-format off
15 static const FunctionInfo functions[] = {
16 {0, &IProcessWindingController::GetLaunchReason, "GetLaunchReason"},
17 {11, &IProcessWindingController::OpenCallingLibraryApplet, "OpenCallingLibraryApplet"},
18 {21, nullptr, "PushContext"},
19 {22, nullptr, "PopContext"},
20 {23, nullptr, "CancelWindingReservation"},
21 {30, nullptr, "WindAndDoReserved"},
22 {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
23 {41, nullptr, "ReserveToStartAndWait"},
24 };
25 // clang-format on
26
27 RegisterHandlers(functions);
28}
29
30IProcessWindingController::~IProcessWindingController() = default;
31
32void IProcessWindingController::GetLaunchReason(HLERequestContext& ctx) {
33 LOG_WARNING(Service_AM, "(STUBBED) called");
34
35 IPC::ResponseBuilder rb{ctx, 3};
36 rb.Push(ResultSuccess);
37 rb.PushRaw(applet->launch_reason);
38}
39
40void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) {
41 const auto caller_applet = applet->caller_applet.lock();
42 if (caller_applet == nullptr) {
43 LOG_ERROR(Service_AM, "No calling applet available");
44
45 IPC::ResponseBuilder rb{ctx, 2};
46 rb.Push(ResultUnknown);
47 return;
48 }
49
50 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
51 rb.Push(ResultSuccess);
52 rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet->caller_applet_broker,
53 caller_applet);
54}
55
56} // namespace Service::AM
diff --git a/src/core/hle/service/am/process_winding_controller.h b/src/core/hle/service/am/process_winding_controller.h
new file mode 100644
index 000000000..71ae4c4f5
--- /dev/null
+++ b/src/core/hle/service/am/process_winding_controller.h
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10struct Applet;
11
12class IProcessWindingController final : public ServiceFramework<IProcessWindingController> {
13public:
14 explicit IProcessWindingController(Core::System& system_, std::shared_ptr<Applet> applet_);
15 ~IProcessWindingController() override;
16
17private:
18 void GetLaunchReason(HLERequestContext& ctx);
19 void OpenCallingLibraryApplet(HLERequestContext& ctx);
20
21 const std::shared_ptr<Applet> applet;
22};
23
24} // namespace Service::AM
diff --git a/src/core/hle/service/am/self_controller.cpp b/src/core/hle/service/am/self_controller.cpp
new file mode 100644
index 000000000..0289f5cf1
--- /dev/null
+++ b/src/core/hle/service/am/self_controller.cpp
@@ -0,0 +1,456 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/am_results.h"
5#include "core/hle/service/am/frontend/applets.h"
6#include "core/hle/service/am/self_controller.h"
7#include "core/hle/service/caps/caps_su.h"
8#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
10#include "core/hle/service/nvnflinger/nvnflinger.h"
11#include "core/hle/service/sm/sm.h"
12#include "core/hle/service/vi/vi_results.h"
13
14namespace Service::AM {
15
16ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> applet_,
17 Nvnflinger::Nvnflinger& nvnflinger_)
18 : ServiceFramework{system_, "ISelfController"}, nvnflinger{nvnflinger_}, applet{std::move(
19 applet_)} {
20 // clang-format off
21 static const FunctionInfo functions[] = {
22 {0, &ISelfController::Exit, "Exit"},
23 {1, &ISelfController::LockExit, "LockExit"},
24 {2, &ISelfController::UnlockExit, "UnlockExit"},
25 {3, &ISelfController::EnterFatalSection, "EnterFatalSection"},
26 {4, &ISelfController::LeaveFatalSection, "LeaveFatalSection"},
27 {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
28 {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
29 {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
30 {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"},
31 {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
32 {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
33 {15, &ISelfController::SetScreenShotAppletIdentityInfo, "SetScreenShotAppletIdentityInfo"},
34 {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"},
35 {17, nullptr, "SetControllerFirmwareUpdateSection"},
36 {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
37 {19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"},
38 {20, nullptr, "SetDesirableKeyboardLayout"},
39 {21, nullptr, "GetScreenShotProgramId"},
40 {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
41 {41, &ISelfController::IsSystemBufferSharingEnabled, "IsSystemBufferSharingEnabled"},
42 {42, &ISelfController::GetSystemSharedLayerHandle, "GetSystemSharedLayerHandle"},
43 {43, &ISelfController::GetSystemSharedBufferHandle, "GetSystemSharedBufferHandle"},
44 {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
45 {45, nullptr, "SetManagedDisplayLayerSeparationMode"},
46 {46, nullptr, "SetRecordingLayerCompositionEnabled"},
47 {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
48 {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
49 {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
50 {61, nullptr, "SetMediaPlaybackState"},
51 {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
52 {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
53 {64, nullptr, "SetInputDetectionSourceSet"},
54 {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"},
55 {66, nullptr, "GetCurrentIlluminance"},
56 {67, nullptr, "IsIlluminanceAvailable"},
57 {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"},
58 {69, &ISelfController::IsAutoSleepDisabled, "IsAutoSleepDisabled"},
59 {70, nullptr, "ReportMultimediaError"},
60 {71, nullptr, "GetCurrentIlluminanceEx"},
61 {72, nullptr, "SetInputDetectionPolicy"},
62 {80, nullptr, "SetWirelessPriorityMode"},
63 {90, &ISelfController::GetAccumulatedSuspendedTickValue, "GetAccumulatedSuspendedTickValue"},
64 {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
65 {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
66 {110, nullptr, "SetApplicationAlbumUserData"},
67 {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"},
68 {130, &ISelfController::SetRecordVolumeMuted, "SetRecordVolumeMuted"},
69 {1000, nullptr, "GetDebugStorageChannel"},
70 };
71 // clang-format on
72
73 RegisterHandlers(functions);
74}
75
76ISelfController::~ISelfController() = default;
77
78void ISelfController::Exit(HLERequestContext& ctx) {
79 LOG_DEBUG(Service_AM, "called");
80
81 IPC::ResponseBuilder rb{ctx, 2};
82 rb.Push(ResultSuccess);
83
84 // TODO
85 system.Exit();
86}
87
88void ISelfController::LockExit(HLERequestContext& ctx) {
89 LOG_DEBUG(Service_AM, "called");
90
91 system.SetExitLocked(true);
92
93 IPC::ResponseBuilder rb{ctx, 2};
94 rb.Push(ResultSuccess);
95}
96
97void ISelfController::UnlockExit(HLERequestContext& ctx) {
98 LOG_DEBUG(Service_AM, "called");
99
100 system.SetExitLocked(false);
101
102 IPC::ResponseBuilder rb{ctx, 2};
103 rb.Push(ResultSuccess);
104
105 if (system.GetExitRequested()) {
106 system.Exit();
107 }
108}
109
110void ISelfController::EnterFatalSection(HLERequestContext& ctx) {
111
112 std::scoped_lock lk{applet->lock};
113 applet->fatal_section_count++;
114 LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", applet->fatal_section_count);
115
116 IPC::ResponseBuilder rb{ctx, 2};
117 rb.Push(ResultSuccess);
118}
119
120void ISelfController::LeaveFatalSection(HLERequestContext& ctx) {
121 LOG_DEBUG(Service_AM, "called.");
122
123 // Entry and exit of fatal sections must be balanced.
124 std::scoped_lock lk{applet->lock};
125 if (applet->fatal_section_count == 0) {
126 IPC::ResponseBuilder rb{ctx, 2};
127 rb.Push(AM::ResultFatalSectionCountImbalance);
128 return;
129 }
130
131 applet->fatal_section_count--;
132
133 IPC::ResponseBuilder rb{ctx, 2};
134 rb.Push(ResultSuccess);
135}
136
137void ISelfController::GetLibraryAppletLaunchableEvent(HLERequestContext& ctx) {
138 LOG_WARNING(Service_AM, "(STUBBED) called");
139
140 applet->library_applet_launchable_event.Signal();
141
142 IPC::ResponseBuilder rb{ctx, 2, 1};
143 rb.Push(ResultSuccess);
144 rb.PushCopyObjects(applet->library_applet_launchable_event.GetHandle());
145}
146
147void ISelfController::SetScreenShotPermission(HLERequestContext& ctx) {
148 IPC::RequestParser rp{ctx};
149 const auto permission = rp.PopEnum<ScreenshotPermission>();
150 LOG_DEBUG(Service_AM, "called, permission={}", permission);
151
152 std::scoped_lock lk{applet->lock};
153 applet->screenshot_permission = permission;
154
155 IPC::ResponseBuilder rb{ctx, 2};
156 rb.Push(ResultSuccess);
157}
158
159void ISelfController::SetOperationModeChangedNotification(HLERequestContext& ctx) {
160 IPC::RequestParser rp{ctx};
161
162 const bool notification_enabled = rp.Pop<bool>();
163 LOG_WARNING(Service_AM, "(STUBBED) called notification_enabled={}", notification_enabled);
164
165 std::scoped_lock lk{applet->lock};
166 applet->operation_mode_changed_notification_enabled = notification_enabled;
167
168 IPC::ResponseBuilder rb{ctx, 2};
169 rb.Push(ResultSuccess);
170}
171
172void ISelfController::SetPerformanceModeChangedNotification(HLERequestContext& ctx) {
173 IPC::RequestParser rp{ctx};
174
175 const bool notification_enabled = rp.Pop<bool>();
176 LOG_WARNING(Service_AM, "(STUBBED) called notification_enabled={}", notification_enabled);
177
178 std::scoped_lock lk{applet->lock};
179 applet->performance_mode_changed_notification_enabled = notification_enabled;
180
181 IPC::ResponseBuilder rb{ctx, 2};
182 rb.Push(ResultSuccess);
183}
184
185void ISelfController::SetFocusHandlingMode(HLERequestContext& ctx) {
186 IPC::RequestParser rp{ctx};
187
188 const auto flags = rp.PopRaw<FocusHandlingMode>();
189
190 LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}",
191 flags.unknown0, flags.unknown1, flags.unknown2);
192
193 std::scoped_lock lk{applet->lock};
194 applet->focus_handling_mode = flags;
195
196 IPC::ResponseBuilder rb{ctx, 2};
197 rb.Push(ResultSuccess);
198}
199
200void ISelfController::SetRestartMessageEnabled(HLERequestContext& ctx) {
201 LOG_WARNING(Service_AM, "(STUBBED) called");
202
203 std::scoped_lock lk{applet->lock};
204 applet->restart_message_enabled = true;
205
206 IPC::ResponseBuilder rb{ctx, 2};
207 rb.Push(ResultSuccess);
208}
209
210void ISelfController::SetScreenShotAppletIdentityInfo(HLERequestContext& ctx) {
211 LOG_WARNING(Service_AM, "(STUBBED) called");
212
213 IPC::RequestParser rp{ctx};
214 std::scoped_lock lk{applet->lock};
215 applet->screen_shot_identity = rp.PopRaw<AppletIdentityInfo>();
216
217 IPC::ResponseBuilder rb{ctx, 2};
218 rb.Push(ResultSuccess);
219}
220
221void ISelfController::SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx) {
222 IPC::RequestParser rp{ctx};
223
224 const bool enabled = rp.Pop<bool>();
225 LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled);
226
227 std::scoped_lock lk{applet->lock};
228 ASSERT(applet->type == AppletType::Application);
229 applet->out_of_focus_suspension_enabled = enabled;
230
231 IPC::ResponseBuilder rb{ctx, 2};
232 rb.Push(ResultSuccess);
233}
234
235void ISelfController::SetAlbumImageOrientation(HLERequestContext& ctx) {
236 IPC::RequestParser rp{ctx};
237
238 const auto orientation = rp.PopRaw<Capture::AlbumImageOrientation>();
239 LOG_WARNING(Service_AM, "(STUBBED) called, orientation={}", static_cast<s32>(orientation));
240
241 std::scoped_lock lk{applet->lock};
242 applet->album_image_orientation = orientation;
243
244 IPC::ResponseBuilder rb{ctx, 2};
245 rb.Push(ResultSuccess);
246}
247
248void ISelfController::CreateManagedDisplayLayer(HLERequestContext& ctx) {
249 LOG_WARNING(Service_AM, "(STUBBED) called");
250
251 u64 layer_id{};
252 applet->managed_layer_holder.Initialize(&nvnflinger);
253 applet->managed_layer_holder.CreateManagedDisplayLayer(&layer_id);
254
255 IPC::ResponseBuilder rb{ctx, 4};
256 rb.Push(ResultSuccess);
257 rb.Push(layer_id);
258}
259
260void ISelfController::IsSystemBufferSharingEnabled(HLERequestContext& ctx) {
261 LOG_WARNING(Service_AM, "(STUBBED) called");
262
263 IPC::ResponseBuilder rb{ctx, 2};
264 rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess()));
265}
266
267void ISelfController::GetSystemSharedLayerHandle(HLERequestContext& ctx) {
268 LOG_WARNING(Service_AM, "(STUBBED) called");
269
270 u64 buffer_id, layer_id;
271 applet->system_buffer_manager.GetSystemSharedLayerHandle(&buffer_id, &layer_id);
272
273 IPC::ResponseBuilder rb{ctx, 6};
274 rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess()));
275 rb.Push<s64>(buffer_id);
276 rb.Push<s64>(layer_id);
277}
278
279void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) {
280 LOG_WARNING(Service_AM, "(STUBBED) called");
281
282 u64 buffer_id, layer_id;
283 applet->system_buffer_manager.GetSystemSharedLayerHandle(&buffer_id, &layer_id);
284
285 IPC::ResponseBuilder rb{ctx, 4};
286 rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess()));
287 rb.Push<s64>(buffer_id);
288}
289
290Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) {
291 if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id)) {
292 return ResultSuccess;
293 }
294
295 return VI::ResultOperationFailed;
296}
297
298void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) {
299 LOG_WARNING(Service_AM, "(STUBBED) called");
300
301 u64 layer_id{};
302 u64 recording_layer_id{};
303 applet->managed_layer_holder.Initialize(&nvnflinger);
304 applet->managed_layer_holder.CreateManagedDisplaySeparableLayer(&layer_id, &recording_layer_id);
305
306 IPC::ResponseBuilder rb{ctx, 6};
307 rb.Push(ResultSuccess);
308 rb.Push(layer_id);
309 rb.Push(recording_layer_id);
310}
311
312void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) {
313 LOG_WARNING(Service_AM, "(STUBBED) called");
314
315 IPC::ResponseBuilder rb{ctx, 2};
316 rb.Push(ResultSuccess);
317}
318
319void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
320 LOG_WARNING(Service_AM, "(STUBBED) called");
321
322 IPC::ResponseBuilder rb{ctx, 2};
323 rb.Push(ResultSuccess);
324}
325
326void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
327 IPC::RequestParser rp{ctx};
328
329 const auto extension = rp.PopRaw<IdleTimeDetectionExtension>();
330 LOG_DEBUG(Service_AM, "(STUBBED) called extension={}", extension);
331
332 std::scoped_lock lk{applet->lock};
333 applet->idle_time_detection_extension = extension;
334
335 IPC::ResponseBuilder rb{ctx, 2};
336 rb.Push(ResultSuccess);
337}
338
339void ISelfController::GetIdleTimeDetectionExtension(HLERequestContext& ctx) {
340 LOG_WARNING(Service_AM, "(STUBBED) called");
341
342 std::scoped_lock lk{applet->lock};
343
344 IPC::ResponseBuilder rb{ctx, 3};
345 rb.Push(ResultSuccess);
346 rb.PushRaw<IdleTimeDetectionExtension>(applet->idle_time_detection_extension);
347}
348
349void ISelfController::ReportUserIsActive(HLERequestContext& ctx) {
350 LOG_WARNING(Service_AM, "(STUBBED) called");
351
352 IPC::ResponseBuilder rb{ctx, 2};
353 rb.Push(ResultSuccess);
354}
355
356void ISelfController::SetAutoSleepDisabled(HLERequestContext& ctx) {
357 IPC::RequestParser rp{ctx};
358
359 std::scoped_lock lk{applet->lock};
360 applet->auto_sleep_disabled = rp.Pop<bool>();
361
362 // On the system itself, if the previous state of is_auto_sleep_disabled
363 // differed from the current value passed in, it'd signify the internal
364 // window manager to update (and also increment some statistics like update counts)
365 //
366 // It'd also indicate this change to an idle handling context.
367 //
368 // However, given we're emulating this behavior, most of this can be ignored
369 // and it's sufficient to simply set the member variable for querying via
370 // IsAutoSleepDisabled().
371
372 LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", applet->auto_sleep_disabled);
373
374 IPC::ResponseBuilder rb{ctx, 2};
375 rb.Push(ResultSuccess);
376}
377
378void ISelfController::IsAutoSleepDisabled(HLERequestContext& ctx) {
379 LOG_DEBUG(Service_AM, "called.");
380
381 std::scoped_lock lk{applet->lock};
382
383 IPC::ResponseBuilder rb{ctx, 3};
384 rb.Push(ResultSuccess);
385 rb.Push(applet->auto_sleep_disabled);
386}
387
388void ISelfController::GetAccumulatedSuspendedTickValue(HLERequestContext& ctx) {
389 LOG_DEBUG(Service_AM, "called.");
390
391 std::scoped_lock lk{applet->lock};
392 // This command returns the total number of system ticks since ISelfController creation
393 // where the game was suspended. Since Yuzu doesn't implement game suspension, this command
394 // can just always return 0 ticks.
395 IPC::ResponseBuilder rb{ctx, 4};
396 rb.Push(ResultSuccess);
397 rb.Push<u64>(applet->suspended_ticks);
398}
399
400void ISelfController::GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx) {
401 LOG_DEBUG(Service_AM, "called.");
402
403 IPC::ResponseBuilder rb{ctx, 2, 1};
404 rb.Push(ResultSuccess);
405 rb.PushCopyObjects(applet->accumulated_suspended_tick_changed_event.GetHandle());
406}
407
408void ISelfController::SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx) {
409 IPC::RequestParser rp{ctx};
410
411 // This service call sets an internal flag whether a notification is shown when an image is
412 // captured. Currently we do not support capturing images via the capture button, so this can be
413 // stubbed for now.
414 const bool enabled = rp.Pop<bool>();
415 LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled);
416
417 std::scoped_lock lk{applet->lock};
418 applet->album_image_taken_notification_enabled = enabled;
419
420 IPC::ResponseBuilder rb{ctx, 2};
421 rb.Push(ResultSuccess);
422}
423
424void ISelfController::SaveCurrentScreenshot(HLERequestContext& ctx) {
425 IPC::RequestParser rp{ctx};
426
427 const auto report_option = rp.PopEnum<Capture::AlbumReportOption>();
428
429 LOG_INFO(Service_AM, "called, report_option={}", report_option);
430
431 const auto screenshot_service =
432 system.ServiceManager().GetService<Service::Capture::IScreenShotApplicationService>(
433 "caps:su");
434
435 if (screenshot_service) {
436 screenshot_service->CaptureAndSaveScreenshot(report_option);
437 }
438
439 IPC::ResponseBuilder rb{ctx, 2};
440 rb.Push(ResultSuccess);
441}
442
443void ISelfController::SetRecordVolumeMuted(HLERequestContext& ctx) {
444 IPC::RequestParser rp{ctx};
445
446 const auto enabled = rp.Pop<bool>();
447 LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled);
448
449 std::scoped_lock lk{applet->lock};
450 applet->record_volume_muted = enabled;
451
452 IPC::ResponseBuilder rb{ctx, 2};
453 rb.Push(ResultSuccess);
454}
455
456} // namespace Service::AM
diff --git a/src/core/hle/service/am/self_controller.h b/src/core/hle/service/am/self_controller.h
new file mode 100644
index 000000000..a63bc2e74
--- /dev/null
+++ b/src/core/hle/service/am/self_controller.h
@@ -0,0 +1,58 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h"
8
9namespace Service::AM {
10
11struct Applet;
12
13class ISelfController final : public ServiceFramework<ISelfController> {
14public:
15 explicit ISelfController(Core::System& system_, std::shared_ptr<Applet> applet_,
16 Nvnflinger::Nvnflinger& nvnflinger_);
17 ~ISelfController() override;
18
19private:
20 void Exit(HLERequestContext& ctx);
21 void LockExit(HLERequestContext& ctx);
22 void UnlockExit(HLERequestContext& ctx);
23 void EnterFatalSection(HLERequestContext& ctx);
24 void LeaveFatalSection(HLERequestContext& ctx);
25 void GetLibraryAppletLaunchableEvent(HLERequestContext& ctx);
26 void SetScreenShotPermission(HLERequestContext& ctx);
27 void SetOperationModeChangedNotification(HLERequestContext& ctx);
28 void SetPerformanceModeChangedNotification(HLERequestContext& ctx);
29 void SetFocusHandlingMode(HLERequestContext& ctx);
30 void SetRestartMessageEnabled(HLERequestContext& ctx);
31 void SetScreenShotAppletIdentityInfo(HLERequestContext& ctx);
32 void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx);
33 void SetAlbumImageOrientation(HLERequestContext& ctx);
34 void IsSystemBufferSharingEnabled(HLERequestContext& ctx);
35 void GetSystemSharedBufferHandle(HLERequestContext& ctx);
36 void GetSystemSharedLayerHandle(HLERequestContext& ctx);
37 void CreateManagedDisplayLayer(HLERequestContext& ctx);
38 void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
39 void SetHandlesRequestToDisplay(HLERequestContext& ctx);
40 void ApproveToDisplay(HLERequestContext& ctx);
41 void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
42 void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
43 void ReportUserIsActive(HLERequestContext& ctx);
44 void SetAutoSleepDisabled(HLERequestContext& ctx);
45 void IsAutoSleepDisabled(HLERequestContext& ctx);
46 void GetAccumulatedSuspendedTickValue(HLERequestContext& ctx);
47 void GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx);
48 void SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx);
49 void SaveCurrentScreenshot(HLERequestContext& ctx);
50 void SetRecordVolumeMuted(HLERequestContext& ctx);
51
52 Result EnsureBufferSharingEnabled(Kernel::KProcess* process);
53
54 Nvnflinger::Nvnflinger& nvnflinger;
55 const std::shared_ptr<Applet> applet;
56};
57
58} // namespace Service::AM
diff --git a/src/core/hle/service/am/storage.cpp b/src/core/hle/service/am/storage.cpp
new file mode 100644
index 000000000..4e82afd1c
--- /dev/null
+++ b/src/core/hle/service/am/storage.cpp
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/am_results.h"
5#include "core/hle/service/am/library_applet_storage.h"
6#include "core/hle/service/am/storage.h"
7#include "core/hle/service/am/storage_accessor.h"
8#include "core/hle/service/ipc_helpers.h"
9
10namespace Service::AM {
11
12IStorage::IStorage(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl_)
13 : ServiceFramework{system_, "IStorage"}, impl{std::move(impl_)} {
14 static const FunctionInfo functions[] = {
15 {0, &IStorage::Open, "Open"},
16 {1, &IStorage::OpenTransferStorage, "OpenTransferStorage"},
17 };
18
19 RegisterHandlers(functions);
20}
21
22IStorage::IStorage(Core::System& system_, std::vector<u8>&& data)
23 : IStorage(system_, CreateStorage(std::move(data))) {}
24
25IStorage::~IStorage() = default;
26
27void IStorage::Open(HLERequestContext& ctx) {
28 LOG_DEBUG(Service_AM, "called");
29
30 if (impl->GetHandle() != nullptr) {
31 IPC::ResponseBuilder rb{ctx, 2};
32 rb.Push(AM::ResultInvalidStorageType);
33 return;
34 }
35
36 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
37 rb.Push(ResultSuccess);
38 rb.PushIpcInterface<IStorageAccessor>(system, impl);
39}
40
41void IStorage::OpenTransferStorage(HLERequestContext& ctx) {
42 LOG_DEBUG(Service_AM, "called");
43
44 if (impl->GetHandle() == nullptr) {
45 IPC::ResponseBuilder rb{ctx, 2};
46 rb.Push(AM::ResultInvalidStorageType);
47 return;
48 }
49
50 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
51 rb.Push(ResultSuccess);
52 rb.PushIpcInterface<ITransferStorageAccessor>(system, impl);
53}
54
55std::vector<u8> IStorage::GetData() const {
56 return impl->GetData();
57}
58
59} // namespace Service::AM
diff --git a/src/core/hle/service/am/storage.h b/src/core/hle/service/am/storage.h
new file mode 100644
index 000000000..10d00b141
--- /dev/null
+++ b/src/core/hle/service/am/storage.h
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10class LibraryAppletStorage;
11
12class IStorage final : public ServiceFramework<IStorage> {
13public:
14 explicit IStorage(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl_);
15 explicit IStorage(Core::System& system_, std::vector<u8>&& buffer);
16 ~IStorage() override;
17
18 std::shared_ptr<LibraryAppletStorage> GetImpl() const {
19 return impl;
20 }
21
22 std::vector<u8> GetData() const;
23
24private:
25 void Open(HLERequestContext& ctx);
26 void OpenTransferStorage(HLERequestContext& ctx);
27
28 const std::shared_ptr<LibraryAppletStorage> impl;
29};
30
31} // namespace Service::AM
diff --git a/src/core/hle/service/am/storage_accessor.cpp b/src/core/hle/service/am/storage_accessor.cpp
new file mode 100644
index 000000000..a1184b065
--- /dev/null
+++ b/src/core/hle/service/am/storage_accessor.cpp
@@ -0,0 +1,90 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_transfer_memory.h"
5#include "core/hle/service/am/am_results.h"
6#include "core/hle/service/am/library_applet_storage.h"
7#include "core/hle/service/am/storage_accessor.h"
8#include "core/hle/service/ipc_helpers.h"
9
10namespace Service::AM {
11
12IStorageAccessor::IStorageAccessor(Core::System& system_,
13 std::shared_ptr<LibraryAppletStorage> impl_)
14 : ServiceFramework{system_, "IStorageAccessor"}, impl{std::move(impl_)} {
15 static const FunctionInfo functions[] = {
16 {0, &IStorageAccessor::GetSize, "GetSize"},
17 {10, &IStorageAccessor::Write, "Write"},
18 {11, &IStorageAccessor::Read, "Read"},
19 };
20
21 RegisterHandlers(functions);
22}
23
24IStorageAccessor::~IStorageAccessor() = default;
25
26void IStorageAccessor::GetSize(HLERequestContext& ctx) {
27 LOG_DEBUG(Service_AM, "called");
28
29 IPC::ResponseBuilder rb{ctx, 4};
30
31 rb.Push(ResultSuccess);
32 rb.Push(impl->GetSize());
33}
34
35void IStorageAccessor::Write(HLERequestContext& ctx) {
36 IPC::RequestParser rp{ctx};
37
38 const s64 offset{rp.Pop<s64>()};
39 const auto data{ctx.ReadBuffer()};
40 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
41
42 const auto res{impl->Write(offset, data.data(), data.size())};
43
44 IPC::ResponseBuilder rb{ctx, 2};
45 rb.Push(res);
46}
47
48void IStorageAccessor::Read(HLERequestContext& ctx) {
49 IPC::RequestParser rp{ctx};
50
51 const s64 offset{rp.Pop<s64>()};
52 std::vector<u8> data(ctx.GetWriteBufferSize());
53
54 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
55
56 const auto res{impl->Read(offset, data.data(), data.size())};
57
58 ctx.WriteBuffer(data);
59
60 IPC::ResponseBuilder rb{ctx, 2};
61 rb.Push(res);
62}
63
64ITransferStorageAccessor::ITransferStorageAccessor(Core::System& system_,
65 std::shared_ptr<LibraryAppletStorage> impl_)
66 : ServiceFramework{system_, "ITransferStorageAccessor"}, impl{std::move(impl_)} {
67 static const FunctionInfo functions[] = {
68 {0, &ITransferStorageAccessor::GetSize, "GetSize"},
69 {1, &ITransferStorageAccessor::GetHandle, "GetHandle"},
70 };
71
72 RegisterHandlers(functions);
73}
74
75ITransferStorageAccessor::~ITransferStorageAccessor() = default;
76
77void ITransferStorageAccessor::GetSize(HLERequestContext& ctx) {
78 IPC::ResponseBuilder rb{ctx, 4};
79 rb.Push(ResultSuccess);
80 rb.Push(impl->GetSize());
81}
82
83void ITransferStorageAccessor::GetHandle(HLERequestContext& ctx) {
84 IPC::ResponseBuilder rb{ctx, 4, 1};
85 rb.Push(ResultSuccess);
86 rb.Push(impl->GetSize());
87 rb.PushCopyObjects(impl->GetHandle());
88}
89
90} // namespace Service::AM
diff --git a/src/core/hle/service/am/storage_accessor.h b/src/core/hle/service/am/storage_accessor.h
new file mode 100644
index 000000000..b9aa85a66
--- /dev/null
+++ b/src/core/hle/service/am/storage_accessor.h
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/am/storage.h"
7#include "core/hle/service/service.h"
8
9namespace Service::AM {
10
11class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
12public:
13 explicit IStorageAccessor(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl_);
14 ~IStorageAccessor() override;
15
16private:
17 void GetSize(HLERequestContext& ctx);
18 void Write(HLERequestContext& ctx);
19 void Read(HLERequestContext& ctx);
20
21 const std::shared_ptr<LibraryAppletStorage> impl;
22};
23
24class ITransferStorageAccessor final : public ServiceFramework<ITransferStorageAccessor> {
25public:
26 explicit ITransferStorageAccessor(Core::System& system_,
27 std::shared_ptr<LibraryAppletStorage> impl_);
28 ~ITransferStorageAccessor() override;
29
30private:
31 void GetSize(HLERequestContext& ctx);
32 void GetHandle(HLERequestContext& ctx);
33
34 const std::shared_ptr<LibraryAppletStorage> impl;
35};
36
37} // namespace Service::AM
diff --git a/src/core/hle/service/am/system_applet_proxy.cpp b/src/core/hle/service/am/system_applet_proxy.cpp
new file mode 100644
index 000000000..38643408e
--- /dev/null
+++ b/src/core/hle/service/am/system_applet_proxy.cpp
@@ -0,0 +1,136 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/applet_common_functions.h"
5#include "core/hle/service/am/application_creator.h"
6#include "core/hle/service/am/audio_controller.h"
7#include "core/hle/service/am/common_state_getter.h"
8#include "core/hle/service/am/debug_functions.h"
9#include "core/hle/service/am/display_controller.h"
10#include "core/hle/service/am/global_state_controller.h"
11#include "core/hle/service/am/home_menu_functions.h"
12#include "core/hle/service/am/library_applet_creator.h"
13#include "core/hle/service/am/library_applet_self_accessor.h"
14#include "core/hle/service/am/process_winding_controller.h"
15#include "core/hle/service/am/self_controller.h"
16#include "core/hle/service/am/system_applet_proxy.h"
17#include "core/hle/service/am/window_controller.h"
18#include "core/hle/service/ipc_helpers.h"
19
20namespace Service::AM {
21
22ISystemAppletProxy::ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
23 std::shared_ptr<Applet> applet_, Core::System& system_)
24 : ServiceFramework{system_, "ISystemAppletProxy"}, nvnflinger{nvnflinger_}, applet{std::move(
25 applet_)} {
26 // clang-format off
27 static const FunctionInfo functions[] = {
28 {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
29 {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
30 {2, &ISystemAppletProxy::GetWindowController, "GetWindowController"},
31 {3, &ISystemAppletProxy::GetAudioController, "GetAudioController"},
32 {4, &ISystemAppletProxy::GetDisplayController, "GetDisplayController"},
33 {10, nullptr, "GetProcessWindingController"},
34 {11, &ISystemAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
35 {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
36 {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
37 {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"},
38 {23, &ISystemAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"},
39 {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
40 };
41 // clang-format on
42
43 RegisterHandlers(functions);
44}
45
46ISystemAppletProxy::~ISystemAppletProxy() = default;
47
48void ISystemAppletProxy::GetCommonStateGetter(HLERequestContext& ctx) {
49 LOG_DEBUG(Service_AM, "called");
50
51 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
52 rb.Push(ResultSuccess);
53 rb.PushIpcInterface<ICommonStateGetter>(system, applet);
54}
55
56void ISystemAppletProxy::GetSelfController(HLERequestContext& ctx) {
57 LOG_DEBUG(Service_AM, "called");
58
59 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
60 rb.Push(ResultSuccess);
61 rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger);
62}
63
64void ISystemAppletProxy::GetWindowController(HLERequestContext& ctx) {
65 LOG_DEBUG(Service_AM, "called");
66
67 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
68 rb.Push(ResultSuccess);
69 rb.PushIpcInterface<IWindowController>(system, applet);
70}
71
72void ISystemAppletProxy::GetAudioController(HLERequestContext& ctx) {
73 LOG_DEBUG(Service_AM, "called");
74
75 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
76 rb.Push(ResultSuccess);
77 rb.PushIpcInterface<IAudioController>(system);
78}
79
80void ISystemAppletProxy::GetDisplayController(HLERequestContext& ctx) {
81 LOG_DEBUG(Service_AM, "called");
82
83 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
84 rb.Push(ResultSuccess);
85 rb.PushIpcInterface<IDisplayController>(system, applet);
86}
87
88void ISystemAppletProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
89 LOG_DEBUG(Service_AM, "called");
90
91 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
92 rb.Push(ResultSuccess);
93 rb.PushIpcInterface<ILibraryAppletCreator>(system, applet);
94}
95
96void ISystemAppletProxy::GetHomeMenuFunctions(HLERequestContext& ctx) {
97 LOG_DEBUG(Service_AM, "called");
98
99 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
100 rb.Push(ResultSuccess);
101 rb.PushIpcInterface<IHomeMenuFunctions>(system);
102}
103
104void ISystemAppletProxy::GetGlobalStateController(HLERequestContext& ctx) {
105 LOG_DEBUG(Service_AM, "called");
106
107 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
108 rb.Push(ResultSuccess);
109 rb.PushIpcInterface<IGlobalStateController>(system);
110}
111
112void ISystemAppletProxy::GetApplicationCreator(HLERequestContext& ctx) {
113 LOG_DEBUG(Service_AM, "called");
114
115 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
116 rb.Push(ResultSuccess);
117 rb.PushIpcInterface<IApplicationCreator>(system);
118}
119
120void ISystemAppletProxy::GetAppletCommonFunctions(HLERequestContext& ctx) {
121 LOG_DEBUG(Service_AM, "called");
122
123 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
124 rb.Push(ResultSuccess);
125 rb.PushIpcInterface<IAppletCommonFunctions>(system, applet);
126}
127
128void ISystemAppletProxy::GetDebugFunctions(HLERequestContext& ctx) {
129 LOG_DEBUG(Service_AM, "called");
130
131 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
132 rb.Push(ResultSuccess);
133 rb.PushIpcInterface<IDebugFunctions>(system);
134}
135
136} // namespace Service::AM
diff --git a/src/core/hle/service/am/system_applet_proxy.h b/src/core/hle/service/am/system_applet_proxy.h
new file mode 100644
index 000000000..0390cd1e5
--- /dev/null
+++ b/src/core/hle/service/am/system_applet_proxy.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/am/applet_message_queue.h"
7#include "core/hle/service/service.h"
8
9namespace Service::AM {
10
11struct Applet;
12
13class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
14public:
15 explicit ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
16 std::shared_ptr<Applet> applet_, Core::System& system_);
17 ~ISystemAppletProxy();
18
19private:
20 void GetCommonStateGetter(HLERequestContext& ctx);
21 void GetSelfController(HLERequestContext& ctx);
22 void GetWindowController(HLERequestContext& ctx);
23 void GetAudioController(HLERequestContext& ctx);
24 void GetDisplayController(HLERequestContext& ctx);
25 void GetLibraryAppletCreator(HLERequestContext& ctx);
26 void GetHomeMenuFunctions(HLERequestContext& ctx);
27 void GetGlobalStateController(HLERequestContext& ctx);
28 void GetApplicationCreator(HLERequestContext& ctx);
29 void GetAppletCommonFunctions(HLERequestContext& ctx);
30 void GetDebugFunctions(HLERequestContext& ctx);
31
32 Nvnflinger::Nvnflinger& nvnflinger;
33 std::shared_ptr<Applet> applet;
34};
35
36} // namespace Service::AM
diff --git a/src/core/hle/service/am/system_buffer_manager.cpp b/src/core/hle/service/am/system_buffer_manager.cpp
new file mode 100644
index 000000000..60a9afc9d
--- /dev/null
+++ b/src/core/hle/service/am/system_buffer_manager.cpp
@@ -0,0 +1,69 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/system_buffer_manager.h"
5#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
6#include "core/hle/service/nvnflinger/nvnflinger.h"
7#include "core/hle/service/vi/vi_results.h"
8
9namespace Service::AM {
10
11SystemBufferManager::SystemBufferManager() = default;
12
13SystemBufferManager::~SystemBufferManager() {
14 if (!m_nvnflinger) {
15 return;
16 }
17
18 // Clean up shared layers.
19 if (m_buffer_sharing_enabled) {
20 }
21}
22
23bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process,
24 AppletId applet_id) {
25 if (m_nvnflinger) {
26 return m_buffer_sharing_enabled;
27 }
28
29 m_process = process;
30 m_nvnflinger = nvnflinger;
31 m_buffer_sharing_enabled = false;
32 m_system_shared_buffer_id = 0;
33 m_system_shared_layer_id = 0;
34
35 if (applet_id <= AppletId::Application) {
36 return false;
37 }
38
39 const auto display_id = m_nvnflinger->OpenDisplay("Default").value();
40 const auto res = m_nvnflinger->GetSystemBufferManager().Initialize(
41 &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id);
42
43 if (res.IsSuccess()) {
44 m_buffer_sharing_enabled = true;
45 m_nvnflinger->SetLayerVisibility(m_system_shared_layer_id, m_visible);
46 }
47
48 return m_buffer_sharing_enabled;
49}
50
51void SystemBufferManager::SetWindowVisibility(bool visible) {
52 if (m_visible == visible) {
53 return;
54 }
55
56 m_visible = visible;
57
58 if (m_nvnflinger) {
59 m_nvnflinger->SetLayerVisibility(m_system_shared_layer_id, m_visible);
60 }
61}
62
63Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written,
64 s32* out_fbshare_layer_index) {
65 // TODO
66 R_SUCCEED();
67}
68
69} // namespace Service::AM
diff --git a/src/core/hle/service/am/system_buffer_manager.h b/src/core/hle/service/am/system_buffer_manager.h
new file mode 100644
index 000000000..98c3cf055
--- /dev/null
+++ b/src/core/hle/service/am/system_buffer_manager.h
@@ -0,0 +1,51 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <set>
7
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10
11#include "core/hle/service/am/am_types.h"
12
13namespace Kernel {
14class KProcess;
15}
16
17namespace Service::Nvnflinger {
18class Nvnflinger;
19}
20
21union Result;
22
23namespace Service::AM {
24
25class SystemBufferManager {
26public:
27 SystemBufferManager();
28 ~SystemBufferManager();
29
30 bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id);
31
32 void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
33 u64* out_system_shared_layer_id) {
34 *out_system_shared_buffer_id = m_system_shared_buffer_id;
35 *out_system_shared_layer_id = m_system_shared_layer_id;
36 }
37
38 void SetWindowVisibility(bool visible);
39
40 Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
41
42private:
43 Kernel::KProcess* m_process{};
44 Nvnflinger::Nvnflinger* m_nvnflinger{};
45 bool m_buffer_sharing_enabled{};
46 bool m_visible{true};
47 u64 m_system_shared_buffer_id{};
48 u64 m_system_shared_layer_id{};
49};
50
51} // namespace Service::AM
diff --git a/src/core/hle/service/am/window_controller.cpp b/src/core/hle/service/am/window_controller.cpp
new file mode 100644
index 000000000..f00957f83
--- /dev/null
+++ b/src/core/hle/service/am/window_controller.cpp
@@ -0,0 +1,86 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/am/applet.h"
5#include "core/hle/service/am/window_controller.h"
6#include "core/hle/service/ipc_helpers.h"
7
8namespace Service::AM {
9
10IWindowController::IWindowController(Core::System& system_, std::shared_ptr<Applet> applet_)
11 : ServiceFramework{system_, "IWindowController"}, applet{std::move(applet_)} {
12 // clang-format off
13 static const FunctionInfo functions[] = {
14 {0, nullptr, "CreateWindow"},
15 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
16 {2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"},
17 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
18 {11, nullptr, "ReleaseForegroundRights"},
19 {12, nullptr, "RejectToChangeIntoBackground"},
20 {20, &IWindowController::SetAppletWindowVisibility, "SetAppletWindowVisibility"},
21 {21, &IWindowController::SetAppletGpuTimeSlice, "SetAppletGpuTimeSlice"},
22 };
23 // clang-format on
24
25 RegisterHandlers(functions);
26}
27
28IWindowController::~IWindowController() = default;
29
30void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) {
31 IPC::ResponseBuilder rb{ctx, 4};
32 rb.Push(ResultSuccess);
33 rb.Push<u64>(applet->aruid);
34}
35
36void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) {
37 u64 aruid = 0;
38 if (auto caller = applet->caller_applet.lock(); caller) {
39 aruid = caller->aruid;
40 }
41
42 LOG_WARNING(Service_AM, "(STUBBED) called");
43
44 IPC::ResponseBuilder rb{ctx, 4};
45 rb.Push(ResultSuccess);
46 rb.Push<u64>(aruid);
47}
48
49void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) {
50 LOG_WARNING(Service_AM, "(STUBBED) called");
51 IPC::ResponseBuilder rb{ctx, 2};
52 rb.Push(ResultSuccess);
53}
54
55void IWindowController::SetAppletWindowVisibility(HLERequestContext& ctx) {
56 LOG_INFO(Service_AM, "called");
57
58 IPC::RequestParser rp{ctx};
59 const bool visible = rp.Pop<bool>();
60
61 applet->system_buffer_manager.SetWindowVisibility(visible);
62 applet->hid_registration.EnableAppletToGetInput(visible);
63
64 if (visible) {
65 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
66 applet->focus_state = FocusState::InFocus;
67 } else {
68 applet->focus_state = FocusState::NotInFocus;
69 }
70 applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
71
72 IPC::ResponseBuilder rb{ctx, 2};
73 rb.Push(ResultSuccess);
74}
75
76void IWindowController::SetAppletGpuTimeSlice(HLERequestContext& ctx) {
77 IPC::RequestParser rp{ctx};
78 const auto time_slice = rp.Pop<s64>();
79
80 LOG_WARNING(Service_AM, "(STUBBED) called, time_slice={}", time_slice);
81
82 IPC::ResponseBuilder rb{ctx, 2};
83 rb.Push(ResultSuccess);
84}
85
86} // namespace Service::AM
diff --git a/src/core/hle/service/am/window_controller.h b/src/core/hle/service/am/window_controller.h
new file mode 100644
index 000000000..a28219abe
--- /dev/null
+++ b/src/core/hle/service/am/window_controller.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::AM {
9
10struct Applet;
11
12class IWindowController final : public ServiceFramework<IWindowController> {
13public:
14 explicit IWindowController(Core::System& system_, std::shared_ptr<Applet> applet_);
15 ~IWindowController() override;
16
17private:
18 void GetAppletResourceUserId(HLERequestContext& ctx);
19 void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx);
20 void AcquireForegroundRights(HLERequestContext& ctx);
21 void SetAppletWindowVisibility(HLERequestContext& ctx);
22 void SetAppletGpuTimeSlice(HLERequestContext& ctx);
23
24 const std::shared_ptr<Applet> applet;
25};
26
27} // namespace Service::AM
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 7075ab800..486719cc0 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -202,7 +202,7 @@ void AOC_U::ListAddOnContent(HLERequestContext& ctx) {
202 LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count, 202 LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
203 process_id); 203 process_id);
204 204
205 const auto current = system.GetApplicationProcessProgramID(); 205 const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
206 206
207 std::vector<u32> out; 207 std::vector<u32> out;
208 const auto& disabled = Settings::values.disabled_addons[current]; 208 const auto& disabled = Settings::values.disabled_addons[current];
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp
index 69acb3a8b..47ff072c5 100644
--- a/src/core/hle/service/caps/caps_a.cpp
+++ b/src/core/hle/service/caps/caps_a.cpp
@@ -5,7 +5,7 @@
5#include "core/hle/service/caps/caps_a.h" 5#include "core/hle/service/caps/caps_a.h"
6#include "core/hle/service/caps/caps_manager.h" 6#include "core/hle/service/caps/caps_manager.h"
7#include "core/hle/service/caps/caps_result.h" 7#include "core/hle/service/caps/caps_result.h"
8#include "core/hle/service/caps/caps_types.h" 8#include "core/hle/service/cmif_serialization.h"
9#include "core/hle/service/ipc_helpers.h" 9#include "core/hle/service/ipc_helpers.h"
10 10
11namespace Service::Capture { 11namespace Service::Capture {
@@ -18,9 +18,9 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
18 {0, nullptr, "GetAlbumFileCount"}, 18 {0, nullptr, "GetAlbumFileCount"},
19 {1, nullptr, "GetAlbumFileList"}, 19 {1, nullptr, "GetAlbumFileList"},
20 {2, nullptr, "LoadAlbumFile"}, 20 {2, nullptr, "LoadAlbumFile"},
21 {3, &IAlbumAccessorService::DeleteAlbumFile, "DeleteAlbumFile"}, 21 {3, C<&IAlbumAccessorService::DeleteAlbumFile>, "DeleteAlbumFile"},
22 {4, nullptr, "StorageCopyAlbumFile"}, 22 {4, nullptr, "StorageCopyAlbumFile"},
23 {5, &IAlbumAccessorService::IsAlbumMounted, "IsAlbumMounted"}, 23 {5, C<&IAlbumAccessorService::IsAlbumMounted>, "IsAlbumMounted"},
24 {6, nullptr, "GetAlbumUsage"}, 24 {6, nullptr, "GetAlbumUsage"},
25 {7, nullptr, "GetAlbumFileSize"}, 25 {7, nullptr, "GetAlbumFileSize"},
26 {8, nullptr, "LoadAlbumFileThumbnail"}, 26 {8, nullptr, "LoadAlbumFileThumbnail"},
@@ -33,18 +33,18 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
33 {15, nullptr, "GetAlbumUsage3"}, 33 {15, nullptr, "GetAlbumUsage3"},
34 {16, nullptr, "GetAlbumMountResult"}, 34 {16, nullptr, "GetAlbumMountResult"},
35 {17, nullptr, "GetAlbumUsage16"}, 35 {17, nullptr, "GetAlbumUsage16"},
36 {18, &IAlbumAccessorService::Unknown18, "Unknown18"}, 36 {18, C<&IAlbumAccessorService::Unknown18>, "Unknown18"},
37 {19, nullptr, "Unknown19"}, 37 {19, nullptr, "Unknown19"},
38 {100, nullptr, "GetAlbumFileCountEx0"}, 38 {100, nullptr, "GetAlbumFileCountEx0"},
39 {101, &IAlbumAccessorService::GetAlbumFileListEx0, "GetAlbumFileListEx0"}, 39 {101, C<&IAlbumAccessorService::GetAlbumFileListEx0>, "GetAlbumFileListEx0"},
40 {202, nullptr, "SaveEditedScreenShot"}, 40 {202, nullptr, "SaveEditedScreenShot"},
41 {301, nullptr, "GetLastThumbnail"}, 41 {301, nullptr, "GetLastThumbnail"},
42 {302, nullptr, "GetLastOverlayMovieThumbnail"}, 42 {302, nullptr, "GetLastOverlayMovieThumbnail"},
43 {401, &IAlbumAccessorService::GetAutoSavingStorage, "GetAutoSavingStorage"}, 43 {401, C<&IAlbumAccessorService::GetAutoSavingStorage>, "GetAutoSavingStorage"},
44 {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, 44 {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"},
45 {1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"}, 45 {1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"},
46 {1002, &IAlbumAccessorService::LoadAlbumScreenShotImageEx1, "LoadAlbumScreenShotImageEx1"}, 46 {1002, C<&IAlbumAccessorService::LoadAlbumScreenShotImageEx1>, "LoadAlbumScreenShotImageEx1"},
47 {1003, &IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1, "LoadAlbumScreenShotThumbnailImageEx1"}, 47 {1003, C<&IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1>, "LoadAlbumScreenShotThumbnailImageEx1"},
48 {8001, nullptr, "ForceAlbumUnmounted"}, 48 {8001, nullptr, "ForceAlbumUnmounted"},
49 {8002, nullptr, "ResetAlbumMountStatus"}, 49 {8002, nullptr, "ResetAlbumMountStatus"},
50 {8011, nullptr, "RefreshAlbumCache"}, 50 {8011, nullptr, "RefreshAlbumCache"},
@@ -62,138 +62,70 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
62 62
63IAlbumAccessorService::~IAlbumAccessorService() = default; 63IAlbumAccessorService::~IAlbumAccessorService() = default;
64 64
65void IAlbumAccessorService::DeleteAlbumFile(HLERequestContext& ctx) { 65Result IAlbumAccessorService::DeleteAlbumFile(AlbumFileId file_id) {
66 IPC::RequestParser rp{ctx};
67 const auto file_id{rp.PopRaw<AlbumFileId>()};
68
69 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}", 66 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}",
70 file_id.application_id, file_id.storage, file_id.type); 67 file_id.application_id, file_id.storage, file_id.type);
71 68
72 Result result = manager->DeleteAlbumFile(file_id); 69 const Result result = manager->DeleteAlbumFile(file_id);
73 result = TranslateResult(result); 70 R_RETURN(TranslateResult(result));
74
75 IPC::ResponseBuilder rb{ctx, 2};
76 rb.Push(result);
77} 71}
78 72
79void IAlbumAccessorService::IsAlbumMounted(HLERequestContext& ctx) { 73Result IAlbumAccessorService::IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage) {
80 IPC::RequestParser rp{ctx};
81 const auto storage{rp.PopEnum<AlbumStorage>()};
82
83 LOG_INFO(Service_Capture, "called, storage={}", storage); 74 LOG_INFO(Service_Capture, "called, storage={}", storage);
84 75
85 Result result = manager->IsAlbumMounted(storage); 76 const Result result = manager->IsAlbumMounted(storage);
86 const bool is_mounted = result.IsSuccess(); 77 *out_is_mounted = result.IsSuccess();
87 result = TranslateResult(result); 78 R_RETURN(TranslateResult(result));
88
89 IPC::ResponseBuilder rb{ctx, 3};
90 rb.Push(result);
91 rb.Push<u8>(is_mounted);
92} 79}
93 80
94void IAlbumAccessorService::Unknown18(HLERequestContext& ctx) { 81Result IAlbumAccessorService::Unknown18(
95 struct UnknownBuffer { 82 Out<u32> out_buffer_size,
96 INSERT_PADDING_BYTES(0x10); 83 OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer) {
97 };
98 static_assert(sizeof(UnknownBuffer) == 0x10, "UnknownBuffer is an invalid size");
99
100 LOG_WARNING(Service_Capture, "(STUBBED) called"); 84 LOG_WARNING(Service_Capture, "(STUBBED) called");
101 85 *out_buffer_size = 0;
102 std::vector<UnknownBuffer> buffer{}; 86 R_SUCCEED();
103
104 if (!buffer.empty()) {
105 ctx.WriteBuffer(buffer);
106 }
107
108 IPC::ResponseBuilder rb{ctx, 3};
109 rb.Push(ResultSuccess);
110 rb.Push(static_cast<u32>(buffer.size()));
111} 87}
112 88
113void IAlbumAccessorService::GetAlbumFileListEx0(HLERequestContext& ctx) { 89Result IAlbumAccessorService::GetAlbumFileListEx0(
114 IPC::RequestParser rp{ctx}; 90 Out<u64> out_entries_size, AlbumStorage storage, u8 flags,
115 const auto storage{rp.PopEnum<AlbumStorage>()}; 91 OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries) {
116 const auto flags{rp.Pop<u8>()};
117 const auto album_entry_size{ctx.GetWriteBufferNumElements<AlbumEntry>()};
118
119 LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags); 92 LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags);
120 93
121 std::vector<AlbumEntry> entries; 94 const Result result = manager->GetAlbumFileList(out_entries, *out_entries_size, storage, flags);
122 Result result = manager->GetAlbumFileList(entries, storage, flags); 95 R_RETURN(TranslateResult(result));
123 result = TranslateResult(result);
124
125 entries.resize(std::min(album_entry_size, entries.size()));
126
127 if (!entries.empty()) {
128 ctx.WriteBuffer(entries);
129 }
130
131 IPC::ResponseBuilder rb{ctx, 4};
132 rb.Push(result);
133 rb.Push<u64>(entries.size());
134} 96}
135 97
136void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) { 98Result IAlbumAccessorService::GetAutoSavingStorage(Out<bool> out_is_autosaving) {
137 LOG_WARNING(Service_Capture, "(STUBBED) called"); 99 LOG_WARNING(Service_Capture, "(STUBBED) called");
138 100
139 bool is_autosaving{}; 101 const Result result = manager->GetAutoSavingStorage(*out_is_autosaving);
140 Result result = manager->GetAutoSavingStorage(is_autosaving); 102 R_RETURN(TranslateResult(result));
141 result = TranslateResult(result);
142
143 IPC::ResponseBuilder rb{ctx, 3};
144 rb.Push(result);
145 rb.Push<u8>(is_autosaving);
146} 103}
147 104
148void IAlbumAccessorService::LoadAlbumScreenShotImageEx1(HLERequestContext& ctx) { 105Result IAlbumAccessorService::LoadAlbumScreenShotImageEx1(
149 IPC::RequestParser rp{ctx}; 106 const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options,
150 const auto file_id{rp.PopRaw<AlbumFileId>()}; 107 OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
151 const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()}; 108 OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
152 const auto image_buffer_size{ctx.GetWriteBufferSize(1)}; 109 OutArray<u8, BufferAttr_HipcMapAlias> out_buffer) {
153
154 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", 110 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}",
155 file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); 111 file_id.application_id, file_id.storage, file_id.type, decoder_options.flags);
156 112
157 std::vector<u8> image; 113 const Result result =
158 LoadAlbumScreenShotImageOutput image_output; 114 manager->LoadAlbumScreenShotImage(*out_image_output, out_image, file_id, decoder_options);
159 Result result = 115 R_RETURN(TranslateResult(result));
160 manager->LoadAlbumScreenShotImage(image_output, image, file_id, decoder_options);
161 result = TranslateResult(result);
162
163 if (image.size() > image_buffer_size) {
164 result = ResultWorkMemoryError;
165 }
166
167 if (result.IsSuccess()) {
168 ctx.WriteBuffer(image_output, 0);
169 ctx.WriteBuffer(image, 1);
170 }
171
172 IPC::ResponseBuilder rb{ctx, 2};
173 rb.Push(result);
174} 116}
175 117
176void IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx) { 118Result IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(
177 IPC::RequestParser rp{ctx}; 119 const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options,
178 const auto file_id{rp.PopRaw<AlbumFileId>()}; 120 OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
179 const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()}; 121 OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
180 122 OutArray<u8, BufferAttr_HipcMapAlias> out_buffer) {
181 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", 123 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}",
182 file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); 124 file_id.application_id, file_id.storage, file_id.type, decoder_options.flags);
183 125
184 std::vector<u8> image(ctx.GetWriteBufferSize(1)); 126 const Result result = manager->LoadAlbumScreenShotThumbnail(*out_image_output, out_image,
185 LoadAlbumScreenShotImageOutput image_output; 127 file_id, decoder_options);
186 Result result = 128 R_RETURN(TranslateResult(result));
187 manager->LoadAlbumScreenShotThumbnail(image_output, image, file_id, decoder_options);
188 result = TranslateResult(result);
189
190 if (result.IsSuccess()) {
191 ctx.WriteBuffer(image_output, 0);
192 ctx.WriteBuffer(image, 1);
193 }
194
195 IPC::ResponseBuilder rb{ctx, 2};
196 rb.Push(result);
197} 129}
198 130
199Result IAlbumAccessorService::TranslateResult(Result in_result) { 131Result IAlbumAccessorService::TranslateResult(Result in_result) {
diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h
index c90cff71e..2cb9b4547 100644
--- a/src/core/hle/service/caps/caps_a.h
+++ b/src/core/hle/service/caps/caps_a.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/caps/caps_types.h"
7#include "core/hle/service/cmif_types.h"
6#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
7 9
8namespace Core { 10namespace Core {
@@ -19,13 +21,31 @@ public:
19 ~IAlbumAccessorService() override; 21 ~IAlbumAccessorService() override;
20 22
21private: 23private:
22 void DeleteAlbumFile(HLERequestContext& ctx); 24 Result DeleteAlbumFile(AlbumFileId file_id);
23 void IsAlbumMounted(HLERequestContext& ctx); 25
24 void Unknown18(HLERequestContext& ctx); 26 Result IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage);
25 void GetAlbumFileListEx0(HLERequestContext& ctx); 27
26 void GetAutoSavingStorage(HLERequestContext& ctx); 28 Result Unknown18(
27 void LoadAlbumScreenShotImageEx1(HLERequestContext& ctx); 29 Out<u32> out_buffer_size,
28 void LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx); 30 OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure>
31 out_buffer);
32
33 Result GetAlbumFileListEx0(Out<u64> out_entries_size, AlbumStorage storage, u8 flags,
34 OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries);
35
36 Result GetAutoSavingStorage(Out<bool> out_is_autosaving);
37
38 Result LoadAlbumScreenShotImageEx1(
39 const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options,
40 OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
41 OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
42 OutArray<u8, BufferAttr_HipcMapAlias> out_buffer);
43
44 Result LoadAlbumScreenShotThumbnailImageEx1(
45 const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options,
46 OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
47 OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
48 OutArray<u8, BufferAttr_HipcMapAlias> out_buffer);
29 49
30 Result TranslateResult(Result in_result); 50 Result TranslateResult(Result in_result);
31 51
diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp
index 1e7fe6474..6993c04c2 100644
--- a/src/core/hle/service/caps/caps_c.cpp
+++ b/src/core/hle/service/caps/caps_c.cpp
@@ -6,6 +6,7 @@
6#include "core/hle/service/caps/caps_manager.h" 6#include "core/hle/service/caps/caps_manager.h"
7#include "core/hle/service/caps/caps_result.h" 7#include "core/hle/service/caps/caps_result.h"
8#include "core/hle/service/caps/caps_types.h" 8#include "core/hle/service/caps/caps_types.h"
9#include "core/hle/service/cmif_serialization.h"
9#include "core/hle/service/ipc_helpers.h" 10#include "core/hle/service/ipc_helpers.h"
10 11
11namespace Service::Capture { 12namespace Service::Capture {
@@ -17,7 +18,7 @@ IAlbumControlService::IAlbumControlService(Core::System& system_,
17 static const FunctionInfo functions[] = { 18 static const FunctionInfo functions[] = {
18 {1, nullptr, "CaptureRawImage"}, 19 {1, nullptr, "CaptureRawImage"},
19 {2, nullptr, "CaptureRawImageWithTimeout"}, 20 {2, nullptr, "CaptureRawImageWithTimeout"},
20 {33, &IAlbumControlService::SetShimLibraryVersion, "SetShimLibraryVersion"}, 21 {33, C<&IAlbumControlService::SetShimLibraryVersion>, "SetShimLibraryVersion"},
21 {1001, nullptr, "RequestTakingScreenShot"}, 22 {1001, nullptr, "RequestTakingScreenShot"},
22 {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, 23 {1002, nullptr, "RequestTakingScreenShotWithTimeout"},
23 {1011, nullptr, "NotifyTakingScreenShotRefused"}, 24 {1011, nullptr, "NotifyTakingScreenShotRefused"},
@@ -42,16 +43,11 @@ IAlbumControlService::IAlbumControlService(Core::System& system_,
42 43
43IAlbumControlService::~IAlbumControlService() = default; 44IAlbumControlService::~IAlbumControlService() = default;
44 45
45void IAlbumControlService::SetShimLibraryVersion(HLERequestContext& ctx) { 46Result IAlbumControlService::SetShimLibraryVersion(ShimLibraryVersion library_version,
46 IPC::RequestParser rp{ctx}; 47 ClientAppletResourceUserId aruid) {
47 const auto library_version{rp.Pop<u64>()};
48 const auto applet_resource_user_id{rp.Pop<u64>()};
49
50 LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", 48 LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
51 library_version, applet_resource_user_id); 49 library_version, aruid.pid);
52 50 R_SUCCEED();
53 IPC::ResponseBuilder rb{ctx, 2};
54 rb.Push(ResultSuccess);
55} 51}
56 52
57} // namespace Service::Capture 53} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h
index 92ba242db..0ecdfa114 100644
--- a/src/core/hle/service/caps/caps_c.h
+++ b/src/core/hle/service/caps/caps_c.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/cmif_types.h"
6#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
7 8
8namespace Core { 9namespace Core {
@@ -11,6 +12,7 @@ class System;
11 12
12namespace Service::Capture { 13namespace Service::Capture {
13class AlbumManager; 14class AlbumManager;
15enum class ShimLibraryVersion : u64;
14 16
15class IAlbumControlService final : public ServiceFramework<IAlbumControlService> { 17class IAlbumControlService final : public ServiceFramework<IAlbumControlService> {
16public: 18public:
@@ -19,7 +21,8 @@ public:
19 ~IAlbumControlService() override; 21 ~IAlbumControlService() override;
20 22
21private: 23private:
22 void SetShimLibraryVersion(HLERequestContext& ctx); 24 Result SetShimLibraryVersion(ShimLibraryVersion library_version,
25 ClientAppletResourceUserId aruid);
23 26
24 std::shared_ptr<AlbumManager> manager = nullptr; 27 std::shared_ptr<AlbumManager> manager = nullptr;
25}; 28};
diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp
index 3a22b135f..7f0bc127f 100644
--- a/src/core/hle/service/caps/caps_manager.cpp
+++ b/src/core/hle/service/caps/caps_manager.cpp
@@ -58,8 +58,8 @@ Result AlbumManager::IsAlbumMounted(AlbumStorage storage) {
58 return is_mounted ? ResultSuccess : ResultIsNotMounted; 58 return is_mounted ? ResultSuccess : ResultIsNotMounted;
59} 59}
60 60
61Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, 61Result AlbumManager::GetAlbumFileList(std::span<AlbumEntry> out_entries, u64& out_entries_count,
62 u8 flags) const { 62 AlbumStorage storage, u8 flags) const {
63 if (storage > AlbumStorage::Sd) { 63 if (storage > AlbumStorage::Sd) {
64 return ResultInvalidStorage; 64 return ResultInvalidStorage;
65 } 65 }
@@ -72,51 +72,55 @@ Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, Albu
72 if (file_id.storage != storage) { 72 if (file_id.storage != storage) {
73 continue; 73 continue;
74 } 74 }
75 if (out_entries.size() >= SdAlbumFileLimit) { 75 if (out_entries_count >= SdAlbumFileLimit) {
76 break;
77 }
78 if (out_entries_count >= out_entries.size()) {
76 break; 79 break;
77 } 80 }
78 81
79 const auto entry_size = Common::FS::GetSize(path); 82 const auto entry_size = Common::FS::GetSize(path);
80 out_entries.push_back({ 83 out_entries[out_entries_count++] = {
81 .entry_size = entry_size, 84 .entry_size = entry_size,
82 .file_id = file_id, 85 .file_id = file_id,
83 }); 86 };
84 } 87 }
85 88
86 return ResultSuccess; 89 return ResultSuccess;
87} 90}
88 91
89Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, 92Result AlbumManager::GetAlbumFileList(std::span<ApplicationAlbumFileEntry> out_entries,
90 ContentType content_type, s64 start_posix_time, 93 u64& out_entries_count, ContentType content_type,
91 s64 end_posix_time, u64 aruid) const { 94 s64 start_posix_time, s64 end_posix_time, u64 aruid) const {
92 if (!is_mounted) { 95 if (!is_mounted) {
93 return ResultIsNotMounted; 96 return ResultIsNotMounted;
94 } 97 }
95 98
96 std::vector<ApplicationAlbumEntry> album_entries; 99 std::vector<ApplicationAlbumEntry> album_entries(out_entries.size());
97 const auto start_date = ConvertToAlbumDateTime(start_posix_time); 100 const auto start_date = ConvertToAlbumDateTime(start_posix_time);
98 const auto end_date = ConvertToAlbumDateTime(end_posix_time); 101 const auto end_date = ConvertToAlbumDateTime(end_posix_time);
99 const auto result = GetAlbumFileList(album_entries, content_type, start_date, end_date, aruid); 102 const auto result = GetAlbumFileList(album_entries, out_entries_count, content_type, start_date,
103 end_date, aruid);
100 104
101 if (result.IsError()) { 105 if (result.IsError()) {
102 return result; 106 return result;
103 } 107 }
104 108
105 for (const auto& album_entry : album_entries) { 109 for (std::size_t i = 0; i < out_entries_count; i++) {
106 ApplicationAlbumFileEntry entry{ 110 out_entries[i] = {
107 .entry = album_entry, 111 .entry = album_entries[i],
108 .datetime = album_entry.datetime, 112 .datetime = album_entries[i].datetime,
109 .unknown = {}, 113 .unknown = {},
110 }; 114 };
111 out_entries.push_back(entry);
112 } 115 }
113 116
114 return ResultSuccess; 117 return ResultSuccess;
115} 118}
116 119
117Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, 120Result AlbumManager::GetAlbumFileList(std::span<ApplicationAlbumEntry> out_entries,
118 ContentType content_type, AlbumFileDateTime start_date, 121 u64& out_entries_count, ContentType content_type,
119 AlbumFileDateTime end_date, u64 aruid) const { 122 AlbumFileDateTime start_date, AlbumFileDateTime end_date,
123 u64 aruid) const {
120 if (!is_mounted) { 124 if (!is_mounted) {
121 return ResultIsNotMounted; 125 return ResultIsNotMounted;
122 } 126 }
@@ -131,12 +135,15 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_en
131 if (file_id.date < end_date) { 135 if (file_id.date < end_date) {
132 continue; 136 continue;
133 } 137 }
134 if (out_entries.size() >= SdAlbumFileLimit) { 138 if (out_entries_count >= SdAlbumFileLimit) {
139 break;
140 }
141 if (out_entries_count >= out_entries.size()) {
135 break; 142 break;
136 } 143 }
137 144
138 const auto entry_size = Common::FS::GetSize(path); 145 const auto entry_size = Common::FS::GetSize(path);
139 ApplicationAlbumEntry entry{ 146 out_entries[out_entries_count++] = {
140 .size = entry_size, 147 .size = entry_size,
141 .hash{}, 148 .hash{},
142 .datetime = file_id.date, 149 .datetime = file_id.date,
@@ -144,7 +151,6 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_en
144 .content = content_type, 151 .content = content_type,
145 .unknown = 1, 152 .unknown = 1,
146 }; 153 };
147 out_entries.push_back(entry);
148 } 154 }
149 155
150 return ResultSuccess; 156 return ResultSuccess;
@@ -156,8 +162,7 @@ Result AlbumManager::GetAutoSavingStorage(bool& out_is_autosaving) const {
156} 162}
157 163
158Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, 164Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
159 std::vector<u8>& out_image, 165 std::span<u8> out_image, const AlbumFileId& file_id,
160 const AlbumFileId& file_id,
161 const ScreenShotDecodeOption& decoder_options) const { 166 const ScreenShotDecodeOption& decoder_options) const {
162 if (file_id.storage > AlbumStorage::Sd) { 167 if (file_id.storage > AlbumStorage::Sd) {
163 return ResultInvalidStorage; 168 return ResultInvalidStorage;
@@ -176,7 +181,9 @@ Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& ou
176 .orientation = AlbumImageOrientation::None, 181 .orientation = AlbumImageOrientation::None,
177 .unknown_1{}, 182 .unknown_1{},
178 .unknown_2{}, 183 .unknown_2{},
184 .pad163{},
179 }, 185 },
186 .pad179{},
180 }; 187 };
181 188
182 std::filesystem::path path; 189 std::filesystem::path path;
@@ -186,14 +193,12 @@ Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& ou
186 return result; 193 return result;
187 } 194 }
188 195
189 out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha);
190
191 return LoadImage(out_image, path, static_cast<int>(out_image_output.width), 196 return LoadImage(out_image, path, static_cast<int>(out_image_output.width),
192 +static_cast<int>(out_image_output.height), decoder_options.flags); 197 +static_cast<int>(out_image_output.height), decoder_options.flags);
193} 198}
194 199
195Result AlbumManager::LoadAlbumScreenShotThumbnail( 200Result AlbumManager::LoadAlbumScreenShotThumbnail(
196 LoadAlbumScreenShotImageOutput& out_image_output, std::vector<u8>& out_image, 201 LoadAlbumScreenShotImageOutput& out_image_output, std::span<u8> out_image,
197 const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const { 202 const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const {
198 if (file_id.storage > AlbumStorage::Sd) { 203 if (file_id.storage > AlbumStorage::Sd) {
199 return ResultInvalidStorage; 204 return ResultInvalidStorage;
@@ -212,7 +217,9 @@ Result AlbumManager::LoadAlbumScreenShotThumbnail(
212 .orientation = AlbumImageOrientation::None, 217 .orientation = AlbumImageOrientation::None,
213 .unknown_1{}, 218 .unknown_1{},
214 .unknown_2{}, 219 .unknown_2{},
220 .pad163{},
215 }, 221 },
222 .pad179{},
216 }; 223 };
217 224
218 std::filesystem::path path; 225 std::filesystem::path path;
@@ -222,8 +229,6 @@ Result AlbumManager::LoadAlbumScreenShotThumbnail(
222 return result; 229 return result;
223 } 230 }
224 231
225 out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha);
226
227 return LoadImage(out_image, path, static_cast<int>(out_image_output.width), 232 return LoadImage(out_image, path, static_cast<int>(out_image_output.width),
228 +static_cast<int>(out_image_output.height), decoder_options.flags); 233 +static_cast<int>(out_image_output.height), decoder_options.flags);
229} 234}
diff --git a/src/core/hle/service/caps/caps_manager.h b/src/core/hle/service/caps/caps_manager.h
index 6fd34f589..893a9075a 100644
--- a/src/core/hle/service/caps/caps_manager.h
+++ b/src/core/hle/service/caps/caps_manager.h
@@ -42,20 +42,20 @@ public:
42 42
43 Result DeleteAlbumFile(const AlbumFileId& file_id); 43 Result DeleteAlbumFile(const AlbumFileId& file_id);
44 Result IsAlbumMounted(AlbumStorage storage); 44 Result IsAlbumMounted(AlbumStorage storage);
45 Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, 45 Result GetAlbumFileList(std::span<AlbumEntry> out_entries, u64& out_entries_count,
46 u8 flags) const; 46 AlbumStorage storage, u8 flags) const;
47 Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, 47 Result GetAlbumFileList(std::span<ApplicationAlbumFileEntry> out_entries,
48 ContentType content_type, s64 start_posix_time, s64 end_posix_time, 48 u64& out_entries_count, ContentType content_type, s64 start_posix_time,
49 u64 aruid) const; 49 s64 end_posix_time, u64 aruid) const;
50 Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, 50 Result GetAlbumFileList(std::span<ApplicationAlbumEntry> out_entries, u64& out_entries_count,
51 ContentType content_type, AlbumFileDateTime start_date, 51 ContentType content_type, AlbumFileDateTime start_date,
52 AlbumFileDateTime end_date, u64 aruid) const; 52 AlbumFileDateTime end_date, u64 aruid) const;
53 Result GetAutoSavingStorage(bool& out_is_autosaving) const; 53 Result GetAutoSavingStorage(bool& out_is_autosaving) const;
54 Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, 54 Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
55 std::vector<u8>& out_image, const AlbumFileId& file_id, 55 std::span<u8> out_image, const AlbumFileId& file_id,
56 const ScreenShotDecodeOption& decoder_options) const; 56 const ScreenShotDecodeOption& decoder_options) const;
57 Result LoadAlbumScreenShotThumbnail(LoadAlbumScreenShotImageOutput& out_image_output, 57 Result LoadAlbumScreenShotThumbnail(LoadAlbumScreenShotImageOutput& out_image_output,
58 std::vector<u8>& out_image, const AlbumFileId& file_id, 58 std::span<u8> out_image, const AlbumFileId& file_id,
59 const ScreenShotDecodeOption& decoder_options) const; 59 const ScreenShotDecodeOption& decoder_options) const;
60 60
61 Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, 61 Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute,
diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp
index eab023568..dfa7f1a84 100644
--- a/src/core/hle/service/caps/caps_ss.cpp
+++ b/src/core/hle/service/caps/caps_ss.cpp
@@ -3,10 +3,9 @@
3 3
4#include "common/logging/log.h" 4#include "common/logging/log.h"
5#include "core/hle/service/caps/caps_manager.h" 5#include "core/hle/service/caps/caps_manager.h"
6#include "core/hle/service/caps/caps_types.h"
7#include "core/hle/service/ipc_helpers.h"
8
9#include "core/hle/service/caps/caps_ss.h" 6#include "core/hle/service/caps/caps_ss.h"
7#include "core/hle/service/cmif_serialization.h"
8#include "core/hle/service/ipc_helpers.h"
10 9
11namespace Service::Capture { 10namespace Service::Capture {
12 11
@@ -17,9 +16,9 @@ IScreenShotService::IScreenShotService(Core::System& system_,
17 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
18 {201, nullptr, "SaveScreenShot"}, 17 {201, nullptr, "SaveScreenShot"},
19 {202, nullptr, "SaveEditedScreenShot"}, 18 {202, nullptr, "SaveEditedScreenShot"},
20 {203, &IScreenShotService::SaveScreenShotEx0, "SaveScreenShotEx0"}, 19 {203, C<&IScreenShotService::SaveScreenShotEx0>, "SaveScreenShotEx0"},
21 {204, nullptr, "SaveEditedScreenShotEx0"}, 20 {204, nullptr, "SaveEditedScreenShotEx0"},
22 {206, &IScreenShotService::SaveEditedScreenShotEx1, "SaveEditedScreenShotEx1"}, 21 {206, C<&IScreenShotService::SaveEditedScreenShotEx1>, "SaveEditedScreenShotEx1"},
23 {208, nullptr, "SaveScreenShotOfMovieEx1"}, 22 {208, nullptr, "SaveScreenShotOfMovieEx1"},
24 {1000, nullptr, "Unknown1000"}, 23 {1000, nullptr, "Unknown1000"},
25 }; 24 };
@@ -30,69 +29,38 @@ IScreenShotService::IScreenShotService(Core::System& system_,
30 29
31IScreenShotService::~IScreenShotService() = default; 30IScreenShotService::~IScreenShotService() = default;
32 31
33void IScreenShotService::SaveScreenShotEx0(HLERequestContext& ctx) { 32Result IScreenShotService::SaveScreenShotEx0(
34 IPC::RequestParser rp{ctx}; 33 Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
35 struct Parameters { 34 AlbumReportOption report_option, ClientAppletResourceUserId aruid,
36 ScreenShotAttribute attribute{}; 35 InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
37 AlbumReportOption report_option{}; 36 image_data_buffer) {
38 INSERT_PADDING_BYTES(0x4);
39 u64 applet_resource_user_id{};
40 };
41 static_assert(sizeof(Parameters) == 0x50, "Parameters has incorrect size.");
42
43 const auto parameters{rp.PopRaw<Parameters>()};
44 const auto image_data_buffer = ctx.ReadBuffer();
45
46 LOG_INFO(Service_Capture, 37 LOG_INFO(Service_Capture,
47 "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}", 38 "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}",
48 parameters.report_option, image_data_buffer.size(), 39 report_option, image_data_buffer.size(), aruid.pid);
49 parameters.applet_resource_user_id);
50 40
51 ApplicationAlbumEntry entry{};
52 manager->FlipVerticallyOnWrite(false); 41 manager->FlipVerticallyOnWrite(false);
53 const auto result = 42 R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, image_data_buffer,
54 manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, 43 aruid.pid));
55 image_data_buffer, parameters.applet_resource_user_id);
56
57 IPC::ResponseBuilder rb{ctx, 10};
58 rb.Push(result);
59 rb.PushRaw(entry);
60} 44}
61 45
62void IScreenShotService::SaveEditedScreenShotEx1(HLERequestContext& ctx) { 46Result IScreenShotService::SaveEditedScreenShotEx1(
63 IPC::RequestParser rp{ctx}; 47 Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, u64 width,
64 struct Parameters { 48 u64 height, u64 thumbnail_width, u64 thumbnail_height, const AlbumFileId& file_id,
65 ScreenShotAttribute attribute; 49 const InLargeData<std::array<u8, 0x400>, BufferAttr_HipcMapAlias> application_data_buffer,
66 u64 width; 50 const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
67 u64 height; 51 image_data_buffer,
68 u64 thumbnail_width; 52 const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
69 u64 thumbnail_height; 53 thumbnail_image_data_buffer) {
70 AlbumFileId file_id;
71 };
72 static_assert(sizeof(Parameters) == 0x78, "Parameters has incorrect size.");
73
74 const auto parameters{rp.PopRaw<Parameters>()};
75 const auto application_data_buffer = ctx.ReadBuffer(0);
76 const auto image_data_buffer = ctx.ReadBuffer(1);
77 const auto thumbnail_image_data_buffer = ctx.ReadBuffer(2);
78
79 LOG_INFO(Service_Capture, 54 LOG_INFO(Service_Capture,
80 "called, width={}, height={}, thumbnail_width={}, thumbnail_height={}, " 55 "called, width={}, height={}, thumbnail_width={}, thumbnail_height={}, "
81 "application_id={:016x}, storage={}, type={}, app_data_buffer_size={}, " 56 "application_id={:016x}, storage={}, type={}, "
82 "image_data_buffer_size={}, thumbnail_image_buffer_size={}", 57 "image_data_buffer_size={}, thumbnail_image_buffer_size={}",
83 parameters.width, parameters.height, parameters.thumbnail_width, 58 width, height, thumbnail_width, thumbnail_height, file_id.application_id,
84 parameters.thumbnail_height, parameters.file_id.application_id, 59 file_id.storage, file_id.type, image_data_buffer.size(),
85 parameters.file_id.storage, parameters.file_id.type, application_data_buffer.size(), 60 thumbnail_image_data_buffer.size());
86 image_data_buffer.size(), thumbnail_image_data_buffer.size());
87 61
88 ApplicationAlbumEntry entry{};
89 manager->FlipVerticallyOnWrite(false); 62 manager->FlipVerticallyOnWrite(false);
90 const auto result = manager->SaveEditedScreenShot(entry, parameters.attribute, 63 R_RETURN(manager->SaveEditedScreenShot(*out_entry, attribute, file_id, image_data_buffer));
91 parameters.file_id, image_data_buffer);
92
93 IPC::ResponseBuilder rb{ctx, 10};
94 rb.Push(result);
95 rb.PushRaw(entry);
96} 64}
97 65
98} // namespace Service::Capture 66} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h
index a7e9972ab..da4b4cc5f 100644
--- a/src/core/hle/service/caps/caps_ss.h
+++ b/src/core/hle/service/caps/caps_ss.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/caps/caps_types.h"
7#include "core/hle/service/cmif_types.h"
6#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
7 9
8namespace Core { 10namespace Core {
@@ -17,8 +19,20 @@ public:
17 ~IScreenShotService() override; 19 ~IScreenShotService() override;
18 20
19private: 21private:
20 void SaveScreenShotEx0(HLERequestContext& ctx); 22 Result SaveScreenShotEx0(
21 void SaveEditedScreenShotEx1(HLERequestContext& ctx); 23 Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
24 AlbumReportOption report_option, ClientAppletResourceUserId aruid,
25 InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
26 image_data_buffer);
27
28 Result SaveEditedScreenShotEx1(
29 Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, u64 width,
30 u64 height, u64 thumbnail_width, u64 thumbnail_height, const AlbumFileId& file_id,
31 const InLargeData<std::array<u8, 0x400>, BufferAttr_HipcMapAlias> application_data_buffer,
32 const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
33 image_data_buffer,
34 const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
35 thumbnail_image_data_buffer);
22 36
23 std::shared_ptr<AlbumManager> manager; 37 std::shared_ptr<AlbumManager> manager;
24}; 38};
diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp
index 296b07b00..528f364f5 100644
--- a/src/core/hle/service/caps/caps_su.cpp
+++ b/src/core/hle/service/caps/caps_su.cpp
@@ -6,6 +6,7 @@
6#include "core/hle/service/caps/caps_manager.h" 6#include "core/hle/service/caps/caps_manager.h"
7#include "core/hle/service/caps/caps_su.h" 7#include "core/hle/service/caps/caps_su.h"
8#include "core/hle/service/caps/caps_types.h" 8#include "core/hle/service/caps/caps_types.h"
9#include "core/hle/service/cmif_serialization.h"
9#include "core/hle/service/ipc_helpers.h" 10#include "core/hle/service/ipc_helpers.h"
10#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
11 12
@@ -16,10 +17,10 @@ IScreenShotApplicationService::IScreenShotApplicationService(
16 : ServiceFramework{system_, "caps:su"}, manager{album_manager} { 17 : ServiceFramework{system_, "caps:su"}, manager{album_manager} {
17 // clang-format off 18 // clang-format off
18 static const FunctionInfo functions[] = { 19 static const FunctionInfo functions[] = {
19 {32, &IScreenShotApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"}, 20 {32, C<&IScreenShotApplicationService::SetShimLibraryVersion>, "SetShimLibraryVersion"},
20 {201, nullptr, "SaveScreenShot"}, 21 {201, nullptr, "SaveScreenShot"},
21 {203, &IScreenShotApplicationService::SaveScreenShotEx0, "SaveScreenShotEx0"}, 22 {203, C<&IScreenShotApplicationService::SaveScreenShotEx0>, "SaveScreenShotEx0"},
22 {205, &IScreenShotApplicationService::SaveScreenShotEx1, "SaveScreenShotEx1"}, 23 {205, C<&IScreenShotApplicationService::SaveScreenShotEx1>, "SaveScreenShotEx1"},
23 {210, nullptr, "SaveScreenShotEx2"}, 24 {210, nullptr, "SaveScreenShotEx2"},
24 }; 25 };
25 // clang-format on 26 // clang-format on
@@ -29,77 +30,40 @@ IScreenShotApplicationService::IScreenShotApplicationService(
29 30
30IScreenShotApplicationService::~IScreenShotApplicationService() = default; 31IScreenShotApplicationService::~IScreenShotApplicationService() = default;
31 32
32void IScreenShotApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) { 33Result IScreenShotApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version,
33 IPC::RequestParser rp{ctx}; 34 ClientAppletResourceUserId aruid) {
34 const auto library_version{rp.Pop<u64>()};
35 const auto applet_resource_user_id{rp.Pop<u64>()};
36
37 LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", 35 LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
38 library_version, applet_resource_user_id); 36 library_version, aruid.pid);
39 37 R_SUCCEED();
40 IPC::ResponseBuilder rb{ctx, 2};
41 rb.Push(ResultSuccess);
42} 38}
43 39
44void IScreenShotApplicationService::SaveScreenShotEx0(HLERequestContext& ctx) { 40Result IScreenShotApplicationService::SaveScreenShotEx0(
45 IPC::RequestParser rp{ctx}; 41 Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
46 struct Parameters { 42 AlbumReportOption report_option, ClientAppletResourceUserId aruid,
47 ScreenShotAttribute attribute{}; 43 InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
48 AlbumReportOption report_option{}; 44 image_data_buffer) {
49 INSERT_PADDING_BYTES(0x4);
50 u64 applet_resource_user_id{};
51 };
52 static_assert(sizeof(Parameters) == 0x50, "Parameters has incorrect size.");
53
54 const auto parameters{rp.PopRaw<Parameters>()};
55 const auto image_data_buffer = ctx.ReadBuffer();
56
57 LOG_INFO(Service_Capture, 45 LOG_INFO(Service_Capture,
58 "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}", 46 "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}",
59 parameters.report_option, image_data_buffer.size(), 47 report_option, image_data_buffer.size(), aruid.pid);
60 parameters.applet_resource_user_id);
61 48
62 ApplicationAlbumEntry entry{};
63 manager->FlipVerticallyOnWrite(false); 49 manager->FlipVerticallyOnWrite(false);
64 const auto result = 50 R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, image_data_buffer,
65 manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, 51 aruid.pid));
66 image_data_buffer, parameters.applet_resource_user_id);
67
68 IPC::ResponseBuilder rb{ctx, 10};
69 rb.Push(result);
70 rb.PushRaw(entry);
71} 52}
72 53
73void IScreenShotApplicationService::SaveScreenShotEx1(HLERequestContext& ctx) { 54Result IScreenShotApplicationService::SaveScreenShotEx1(
74 IPC::RequestParser rp{ctx}; 55 Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
75 struct Parameters { 56 AlbumReportOption report_option, ClientAppletResourceUserId aruid,
76 ScreenShotAttribute attribute{}; 57 const InLargeData<ApplicationData, BufferAttr_HipcMapAlias> app_data_buffer,
77 AlbumReportOption report_option{}; 58 const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
78 INSERT_PADDING_BYTES(0x4); 59 image_data_buffer) {
79 u64 applet_resource_user_id{};
80 };
81 static_assert(sizeof(Parameters) == 0x50, "Parameters has incorrect size.");
82
83 const auto parameters{rp.PopRaw<Parameters>()};
84 const auto app_data_buffer = ctx.ReadBuffer(0);
85 const auto image_data_buffer = ctx.ReadBuffer(1);
86
87 LOG_INFO(Service_Capture, 60 LOG_INFO(Service_Capture,
88 "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}", 61 "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}",
89 parameters.report_option, image_data_buffer.size(), 62 report_option, image_data_buffer.size(), aruid.pid);
90 parameters.applet_resource_user_id);
91 63
92 ApplicationAlbumEntry entry{};
93 ApplicationData app_data{};
94 std::memcpy(&app_data, app_data_buffer.data(), sizeof(ApplicationData));
95 manager->FlipVerticallyOnWrite(false); 64 manager->FlipVerticallyOnWrite(false);
96 const auto result = 65 R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, *app_data_buffer,
97 manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, app_data, 66 image_data_buffer, aruid.pid));
98 image_data_buffer, parameters.applet_resource_user_id);
99
100 IPC::ResponseBuilder rb{ctx, 10};
101 rb.Push(result);
102 rb.PushRaw(entry);
103} 67}
104 68
105void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption report_option) { 69void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption report_option) {
@@ -112,6 +76,7 @@ void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption r
112 .orientation = Capture::AlbumImageOrientation::None, 76 .orientation = Capture::AlbumImageOrientation::None,
113 .unknown_1{}, 77 .unknown_1{},
114 .unknown_2{}, 78 .unknown_2{},
79 .pad163{},
115 }; 80 };
116 81
117 renderer.RequestScreenshot( 82 renderer.RequestScreenshot(
diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h
index 21912e95f..4b4cbd09e 100644
--- a/src/core/hle/service/caps/caps_su.h
+++ b/src/core/hle/service/caps/caps_su.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/caps/caps_types.h"
7#include "core/hle/service/cmif_types.h"
6#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
7 9
8namespace Core { 10namespace Core {
@@ -26,9 +28,19 @@ private:
26 static constexpr std::size_t screenshot_height = 720; 28 static constexpr std::size_t screenshot_height = 720;
27 static constexpr std::size_t bytes_per_pixel = 4; 29 static constexpr std::size_t bytes_per_pixel = 4;
28 30
29 void SetShimLibraryVersion(HLERequestContext& ctx); 31 Result SetShimLibraryVersion(ShimLibraryVersion library_version,
30 void SaveScreenShotEx0(HLERequestContext& ctx); 32 ClientAppletResourceUserId aruid);
31 void SaveScreenShotEx1(HLERequestContext& ctx); 33 Result SaveScreenShotEx0(
34 Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
35 AlbumReportOption report_option, ClientAppletResourceUserId aruid,
36 InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
37 image_data_buffer);
38 Result SaveScreenShotEx1(
39 Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
40 AlbumReportOption report_option, ClientAppletResourceUserId aruid,
41 const InLargeData<ApplicationData, BufferAttr_HipcMapAlias> app_data_buffer,
42 const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
43 image_data_buffer);
32 44
33 std::array<u8, screenshot_width * screenshot_height * bytes_per_pixel> image_data; 45 std::array<u8, screenshot_width * screenshot_height * bytes_per_pixel> image_data;
34 46
diff --git a/src/core/hle/service/caps/caps_types.h b/src/core/hle/service/caps/caps_types.h
index 589ac28d3..3deaaad5b 100644
--- a/src/core/hle/service/caps/caps_types.h
+++ b/src/core/hle/service/caps/caps_types.h
@@ -41,6 +41,10 @@ enum class ScreenShotDecoderFlag : u64 {
41 EnableBlockSmoothing = 1 << 1, 41 EnableBlockSmoothing = 1 << 1,
42}; 42};
43 43
44enum class ShimLibraryVersion : u64 {
45 Version1 = 1,
46};
47
44// This is nn::capsrv::AlbumFileDateTime 48// This is nn::capsrv::AlbumFileDateTime
45struct AlbumFileDateTime { 49struct AlbumFileDateTime {
46 s16 year{}; 50 s16 year{};
@@ -144,19 +148,23 @@ static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30,
144 "ApplicationAlbumFileEntry has incorrect size."); 148 "ApplicationAlbumFileEntry has incorrect size.");
145 149
146struct ApplicationData { 150struct ApplicationData {
147 std::array<u8, 0x400> data{}; 151 std::array<u8, 0x400> data;
148 u32 data_size{}; 152 u32 data_size;
149}; 153};
150static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData is an invalid size"); 154static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData is an invalid size");
155static_assert(std::is_trivial_v<ApplicationData>,
156 "ApplicationData type must be trivially copyable.");
151 157
152struct ScreenShotAttribute { 158struct ScreenShotAttribute {
153 u32 unknown_0{}; 159 u32 unknown_0;
154 AlbumImageOrientation orientation{}; 160 AlbumImageOrientation orientation;
155 u32 unknown_1{}; 161 u32 unknown_1;
156 u32 unknown_2{}; 162 u32 unknown_2;
157 INSERT_PADDING_BYTES(0x30); 163 INSERT_PADDING_BYTES_NOINIT(0x30);
158}; 164};
159static_assert(sizeof(ScreenShotAttribute) == 0x40, "ScreenShotAttribute is an invalid size"); 165static_assert(sizeof(ScreenShotAttribute) == 0x40, "ScreenShotAttribute is an invalid size");
166static_assert(std::is_trivial_v<ScreenShotAttribute>,
167 "ScreenShotAttribute type must be trivially copyable.");
160 168
161struct ScreenShotDecodeOption { 169struct ScreenShotDecodeOption {
162 ScreenShotDecoderFlag flags{}; 170 ScreenShotDecoderFlag flags{};
@@ -165,13 +173,15 @@ struct ScreenShotDecodeOption {
165static_assert(sizeof(ScreenShotDecodeOption) == 0x20, "ScreenShotDecodeOption is an invalid size"); 173static_assert(sizeof(ScreenShotDecodeOption) == 0x20, "ScreenShotDecodeOption is an invalid size");
166 174
167struct LoadAlbumScreenShotImageOutput { 175struct LoadAlbumScreenShotImageOutput {
168 s64 width{}; 176 s64 width;
169 s64 height{}; 177 s64 height;
170 ScreenShotAttribute attribute{}; 178 ScreenShotAttribute attribute;
171 INSERT_PADDING_BYTES(0x400); 179 INSERT_PADDING_BYTES_NOINIT(0x400);
172}; 180};
173static_assert(sizeof(LoadAlbumScreenShotImageOutput) == 0x450, 181static_assert(sizeof(LoadAlbumScreenShotImageOutput) == 0x450,
174 "LoadAlbumScreenShotImageOutput is an invalid size"); 182 "LoadAlbumScreenShotImageOutput is an invalid size");
183static_assert(std::is_trivial_v<LoadAlbumScreenShotImageOutput>,
184 "LoadAlbumScreenShotImageOutput type must be trivially copyable.");
175 185
176struct LoadAlbumScreenShotImageOutputForApplication { 186struct LoadAlbumScreenShotImageOutputForApplication {
177 s64 width{}; 187 s64 width{};
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp
index b6b33fb2f..40d4d05fe 100644
--- a/src/core/hle/service/caps/caps_u.cpp
+++ b/src/core/hle/service/caps/caps_u.cpp
@@ -5,6 +5,7 @@
5#include "core/hle/service/caps/caps_manager.h" 5#include "core/hle/service/caps/caps_manager.h"
6#include "core/hle/service/caps/caps_types.h" 6#include "core/hle/service/caps/caps_types.h"
7#include "core/hle/service/caps/caps_u.h" 7#include "core/hle/service/caps/caps_u.h"
8#include "core/hle/service/cmif_serialization.h"
8#include "core/hle/service/ipc_helpers.h" 9#include "core/hle/service/ipc_helpers.h"
9 10
10namespace Service::Capture { 11namespace Service::Capture {
@@ -14,8 +15,8 @@ IAlbumApplicationService::IAlbumApplicationService(Core::System& system_,
14 : ServiceFramework{system_, "caps:u"}, manager{album_manager} { 15 : ServiceFramework{system_, "caps:u"}, manager{album_manager} {
15 // clang-format off 16 // clang-format off
16 static const FunctionInfo functions[] = { 17 static const FunctionInfo functions[] = {
17 {32, &IAlbumApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"}, 18 {32, C<&IAlbumApplicationService::SetShimLibraryVersion>, "SetShimLibraryVersion"},
18 {102, &IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated, "GetAlbumFileList0AafeAruidDeprecated"}, 19 {102, C<&IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated>, "GetAlbumFileList0AafeAruidDeprecated"},
19 {103, nullptr, "DeleteAlbumFileByAruid"}, 20 {103, nullptr, "DeleteAlbumFileByAruid"},
20 {104, nullptr, "GetAlbumFileSizeByAruid"}, 21 {104, nullptr, "GetAlbumFileSizeByAruid"},
21 {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, 22 {105, nullptr, "DeleteAlbumFileByAruidForDebug"},
@@ -24,7 +25,7 @@ IAlbumApplicationService::IAlbumApplicationService(Core::System& system_,
24 {130, nullptr, "PrecheckToCreateContentsByAruid"}, 25 {130, nullptr, "PrecheckToCreateContentsByAruid"},
25 {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, 26 {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
26 {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, 27 {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
27 {142, &IAlbumApplicationService::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"}, 28 {142, C<&IAlbumApplicationService::GetAlbumFileList3AaeAruid>, "GetAlbumFileList3AaeAruid"},
28 {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, 29 {143, nullptr, "GetAlbumFileList4AaeUidAruid"},
29 {144, nullptr, "GetAllAlbumFileList3AaeAruid"}, 30 {144, nullptr, "GetAllAlbumFileList3AaeAruid"},
30 {60002, nullptr, "OpenAccessorSessionForApplication"}, 31 {60002, nullptr, "OpenAccessorSessionForApplication"},
@@ -36,101 +37,40 @@ IAlbumApplicationService::IAlbumApplicationService(Core::System& system_,
36 37
37IAlbumApplicationService::~IAlbumApplicationService() = default; 38IAlbumApplicationService::~IAlbumApplicationService() = default;
38 39
39void IAlbumApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) { 40Result IAlbumApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version,
40 IPC::RequestParser rp{ctx}; 41 ClientAppletResourceUserId aruid) {
41 const auto library_version{rp.Pop<u64>()};
42 const auto applet_resource_user_id{rp.Pop<u64>()};
43
44 LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", 42 LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
45 library_version, applet_resource_user_id); 43 library_version, aruid.pid);
46 44 R_SUCCEED();
47 IPC::ResponseBuilder rb{ctx, 2};
48 rb.Push(ResultSuccess);
49} 45}
50 46
51void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx) { 47Result IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(
52 IPC::RequestParser rp{ctx}; 48 Out<u64> out_entries_count, ContentType content_type, s64 start_posix_time, s64 end_posix_time,
53 struct Parameters { 49 ClientAppletResourceUserId aruid,
54 ContentType content_type; 50 OutArray<ApplicationAlbumFileEntry, BufferAttr_HipcMapAlias> out_entries) {
55 INSERT_PADDING_BYTES(7);
56 s64 start_posix_time;
57 s64 end_posix_time;
58 u64 applet_resource_user_id;
59 };
60 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
61
62 const auto parameters{rp.PopRaw<Parameters>()};
63
64 LOG_WARNING(Service_Capture, 51 LOG_WARNING(Service_Capture,
65 "(STUBBED) called. content_type={}, start_posix_time={}, end_posix_time={}, " 52 "(STUBBED) called. content_type={}, start_posix_time={}, end_posix_time={}, "
66 "applet_resource_user_id={}", 53 "applet_resource_user_id={}",
67 parameters.content_type, parameters.start_posix_time, parameters.end_posix_time, 54 content_type, start_posix_time, end_posix_time, aruid.pid);
68 parameters.applet_resource_user_id);
69
70 Result result = ResultSuccess;
71
72 if (result.IsSuccess()) {
73 result = manager->IsAlbumMounted(AlbumStorage::Sd);
74 }
75
76 std::vector<ApplicationAlbumFileEntry> entries;
77 if (result.IsSuccess()) {
78 result = manager->GetAlbumFileList(entries, parameters.content_type,
79 parameters.start_posix_time, parameters.end_posix_time,
80 parameters.applet_resource_user_id);
81 }
82 55
83 if (!entries.empty()) { 56 R_TRY(manager->IsAlbumMounted(AlbumStorage::Sd));
84 ctx.WriteBuffer(entries); 57 R_RETURN(manager->GetAlbumFileList(out_entries, *out_entries_count, content_type,
85 } 58 start_posix_time, end_posix_time, aruid.pid));
86
87 IPC::ResponseBuilder rb{ctx, 4};
88 rb.Push(result);
89 rb.Push<u64>(entries.size());
90} 59}
91 60
92void IAlbumApplicationService::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) { 61Result IAlbumApplicationService::GetAlbumFileList3AaeAruid(
93 IPC::RequestParser rp{ctx}; 62 Out<u64> out_entries_count, ContentType content_type, AlbumFileDateTime start_date_time,
94 struct Parameters { 63 AlbumFileDateTime end_date_time, ClientAppletResourceUserId aruid,
95 ContentType content_type; 64 OutArray<ApplicationAlbumEntry, BufferAttr_HipcMapAlias> out_entries) {
96 INSERT_PADDING_BYTES(1);
97 AlbumFileDateTime start_date_time;
98 AlbumFileDateTime end_date_time;
99 INSERT_PADDING_BYTES(6);
100 u64 applet_resource_user_id;
101 };
102 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
103
104 const auto parameters{rp.PopRaw<Parameters>()};
105
106 LOG_WARNING(Service_Capture, 65 LOG_WARNING(Service_Capture,
107 "(STUBBED) called. content_type={}, start_date={}/{}/{}, " 66 "(STUBBED) called. content_type={}, start_date={}/{}/{}, "
108 "end_date={}/{}/{}, applet_resource_user_id={}", 67 "end_date={}/{}/{}, applet_resource_user_id={}",
109 parameters.content_type, parameters.start_date_time.year, 68 content_type, start_date_time.year, start_date_time.month, start_date_time.day,
110 parameters.start_date_time.month, parameters.start_date_time.day, 69 end_date_time.year, end_date_time.month, end_date_time.day, aruid.pid);
111 parameters.end_date_time.year, parameters.end_date_time.month,
112 parameters.end_date_time.day, parameters.applet_resource_user_id);
113
114 Result result = ResultSuccess;
115
116 if (result.IsSuccess()) {
117 result = manager->IsAlbumMounted(AlbumStorage::Sd);
118 }
119
120 std::vector<ApplicationAlbumEntry> entries;
121 if (result.IsSuccess()) {
122 result =
123 manager->GetAlbumFileList(entries, parameters.content_type, parameters.start_date_time,
124 parameters.end_date_time, parameters.applet_resource_user_id);
125 }
126
127 if (!entries.empty()) {
128 ctx.WriteBuffer(entries);
129 }
130 70
131 IPC::ResponseBuilder rb{ctx, 4}; 71 R_TRY(manager->IsAlbumMounted(AlbumStorage::Sd));
132 rb.Push(result); 72 R_RETURN(manager->GetAlbumFileList(out_entries, *out_entries_count, content_type,
133 rb.Push<u64>(entries.size()); 73 start_date_time, end_date_time, aruid.pid));
134} 74}
135 75
136} // namespace Service::Capture 76} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h
index 9458c128e..023ee1fe7 100644
--- a/src/core/hle/service/caps/caps_u.h
+++ b/src/core/hle/service/caps/caps_u.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hle/service/cmif_types.h"
6#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
7 8
8namespace Core { 9namespace Core {
@@ -19,9 +20,18 @@ public:
19 ~IAlbumApplicationService() override; 20 ~IAlbumApplicationService() override;
20 21
21private: 22private:
22 void SetShimLibraryVersion(HLERequestContext& ctx); 23 Result SetShimLibraryVersion(ShimLibraryVersion library_version,
23 void GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx); 24 ClientAppletResourceUserId aruid);
24 void GetAlbumFileList3AaeAruid(HLERequestContext& ctx); 25
26 Result GetAlbumFileList0AafeAruidDeprecated(
27 Out<u64> out_entries_count, ContentType content_type, s64 start_posix_time,
28 s64 end_posix_time, ClientAppletResourceUserId aruid,
29 OutArray<ApplicationAlbumFileEntry, BufferAttr_HipcMapAlias> out_entries);
30
31 Result GetAlbumFileList3AaeAruid(
32 Out<u64> out_entries_count, ContentType content_type, AlbumFileDateTime start_date_time,
33 AlbumFileDateTime end_date_time, ClientAppletResourceUserId aruid,
34 OutArray<ApplicationAlbumEntry, BufferAttr_HipcMapAlias> out_entries);
25 35
26 std::shared_ptr<AlbumManager> manager = nullptr; 36 std::shared_ptr<AlbumManager> manager = nullptr;
27}; 37};
diff --git a/src/core/hle/service/event.cpp b/src/core/hle/service/event.cpp
new file mode 100644
index 000000000..375660d72
--- /dev/null
+++ b/src/core/hle/service/event.cpp
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_event.h"
5#include "core/hle/service/event.h"
6#include "core/hle/service/kernel_helpers.h"
7
8namespace Service {
9
10Event::Event(KernelHelpers::ServiceContext& ctx) {
11 m_event = ctx.CreateEvent("Event");
12}
13
14Event::~Event() {
15 m_event->GetReadableEvent().Close();
16 m_event->Close();
17}
18
19void Event::Signal() {
20 m_event->Signal();
21}
22
23void Event::Clear() {
24 m_event->Clear();
25}
26
27Kernel::KReadableEvent* Event::GetHandle() {
28 return &m_event->GetReadableEvent();
29}
30
31} // namespace Service
diff --git a/src/core/hle/service/event.h b/src/core/hle/service/event.h
new file mode 100644
index 000000000..cdbc4635a
--- /dev/null
+++ b/src/core/hle/service/event.h
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace Kernel {
7class KEvent;
8class KReadableEvent;
9} // namespace Kernel
10
11namespace Service {
12
13namespace KernelHelpers {
14class ServiceContext;
15}
16
17class Event {
18public:
19 explicit Event(KernelHelpers::ServiceContext& ctx);
20 ~Event();
21
22 void Signal();
23 void Clear();
24
25 Kernel::KReadableEvent* GetHandle();
26
27private:
28 Kernel::KEvent* m_event;
29};
30
31} // namespace Service
diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
index 2be72b021..5fe534c73 100644
--- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp
@@ -15,11 +15,13 @@
15#include "common/settings.h" 15#include "common/settings.h"
16#include "common/string_util.h" 16#include "common/string_util.h"
17#include "core/core.h" 17#include "core/core.h"
18#include "core/file_sys/content_archive.h"
18#include "core/file_sys/errors.h" 19#include "core/file_sys/errors.h"
19#include "core/file_sys/fs_directory.h" 20#include "core/file_sys/fs_directory.h"
20#include "core/file_sys/fs_filesystem.h" 21#include "core/file_sys/fs_filesystem.h"
21#include "core/file_sys/nca_metadata.h" 22#include "core/file_sys/nca_metadata.h"
22#include "core/file_sys/patch_manager.h" 23#include "core/file_sys/patch_manager.h"
24#include "core/file_sys/romfs.h"
23#include "core/file_sys/romfs_factory.h" 25#include "core/file_sys/romfs_factory.h"
24#include "core/file_sys/savedata_factory.h" 26#include "core/file_sys/savedata_factory.h"
25#include "core/file_sys/system_archive/system_archive.h" 27#include "core/file_sys/system_archive/system_archive.h"
@@ -33,18 +35,20 @@
33#include "core/hle/service/filesystem/save_data_controller.h" 35#include "core/hle/service/filesystem/save_data_controller.h"
34#include "core/hle/service/hle_ipc.h" 36#include "core/hle/service/hle_ipc.h"
35#include "core/hle/service/ipc_helpers.h" 37#include "core/hle/service/ipc_helpers.h"
38#include "core/loader/loader.h"
36#include "core/reporter.h" 39#include "core/reporter.h"
37 40
38namespace Service::FileSystem { 41namespace Service::FileSystem {
39enum class FileSystemType : u8 { 42enum class FileSystemProxyType : u8 {
40 Invalid0 = 0, 43 Code = 0,
41 Invalid1 = 1, 44 Rom = 1,
42 Logo = 2, 45 Logo = 2,
43 ContentControl = 3, 46 Control = 3,
44 ContentManual = 4, 47 Manual = 4,
45 ContentMeta = 5, 48 Meta = 5,
46 ContentData = 6, 49 Data = 6,
47 ApplicationPackage = 7, 50 Package = 7,
51 RegisteredUpdate = 8,
48}; 52};
49 53
50class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { 54class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
@@ -357,12 +361,30 @@ void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) {
357void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) { 361void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) {
358 IPC::RequestParser rp{ctx}; 362 IPC::RequestParser rp{ctx};
359 363
360 const auto type = rp.PopRaw<FileSystemType>(); 364 struct InputParameters {
361 const auto title_id = rp.PopRaw<u64>(); 365 FileSystemProxyType type;
362 LOG_WARNING(Service_FS, "(STUBBED) called with type={}, title_id={:016X}", type, title_id); 366 u64 program_id;
367 };
368 static_assert(sizeof(InputParameters) == 0x10, "InputParameters has wrong size");
369
370 const auto params = rp.PopRaw<InputParameters>();
371 LOG_ERROR(Service_FS, "(STUBBED) called with type={}, program_id={:016X}", params.type,
372 params.program_id);
373
374 // FIXME: many issues with this
375 ASSERT(params.type == FileSystemProxyType::Manual);
376 const auto manual_romfs = romfs_controller->OpenPatchedRomFS(
377 params.program_id, FileSys::ContentRecordType::HtmlDocument);
363 378
364 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 379 ASSERT(manual_romfs != nullptr);
365 rb.Push(ResultUnknown); 380
381 const auto extracted_romfs = FileSys::ExtractRomFS(manual_romfs);
382 ASSERT(extracted_romfs != nullptr);
383
384 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
385 rb.Push(ResultSuccess);
386 rb.PushIpcInterface<IFileSystem>(system, extracted_romfs,
387 SizeGetter::FromStorageId(fsc, FileSys::StorageId::NandUser));
366} 388}
367 389
368void FSP_SRV::OpenSdCardFileSystem(HLERequestContext& ctx) { 390void FSP_SRV::OpenSdCardFileSystem(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp
index 5dc1187cb..98d928697 100644
--- a/src/core/hle/service/glue/time/time_zone.cpp
+++ b/src/core/hle/service/glue/time/time_zone.cpp
@@ -197,32 +197,27 @@ Result TimeZoneService::ToCalendarTimeWithMyRule(
197 197
198Result TimeZoneService::ToPosixTime(Out<u32> out_count, 198Result TimeZoneService::ToPosixTime(Out<u32> out_count,
199 OutArray<s64, BufferAttr_HipcPointer> out_times, 199 OutArray<s64, BufferAttr_HipcPointer> out_times,
200 Out<u32> out_times_count, 200 const Service::PSC::Time::CalendarTime& calendar_time,
201 Service::PSC::Time::CalendarTime& calendar_time, InRule rule) { 201 InRule rule) {
202 SCOPE_EXIT({ 202 SCOPE_EXIT({
203 LOG_DEBUG(Service_Time, 203 LOG_DEBUG(Service_Time,
204 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} " 204 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
205 "out_times_count={}", 205 calendar_time, *out_count, out_times[0], out_times[1]);
206 calendar_time, *out_count, out_times[0], out_times[1], *out_times_count);
207 }); 206 });
208 207
209 R_RETURN( 208 R_RETURN(m_wrapped_service->ToPosixTime(out_count, out_times, calendar_time, rule));
210 m_wrapped_service->ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule));
211} 209}
212 210
213Result TimeZoneService::ToPosixTimeWithMyRule(Out<u32> out_count, 211Result TimeZoneService::ToPosixTimeWithMyRule(
214 OutArray<s64, BufferAttr_HipcPointer> out_times, 212 Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times,
215 Out<u32> out_times_count, 213 const Service::PSC::Time::CalendarTime& calendar_time) {
216 Service::PSC::Time::CalendarTime& calendar_time) {
217 SCOPE_EXIT({ 214 SCOPE_EXIT({
218 LOG_DEBUG(Service_Time, 215 LOG_DEBUG(Service_Time,
219 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} " 216 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
220 "out_times_count={}", 217 calendar_time, *out_count, out_times[0], out_times[1]);
221 calendar_time, *out_count, out_times[0], out_times[1], *out_times_count);
222 }); 218 });
223 219
224 R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, out_times_count, 220 R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, calendar_time));
225 calendar_time));
226} 221}
227 222
228} // namespace Service::Glue::Time 223} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone.h b/src/core/hle/service/glue/time/time_zone.h
index bf12adbdc..9c1530966 100644
--- a/src/core/hle/service/glue/time/time_zone.h
+++ b/src/core/hle/service/glue/time/time_zone.h
@@ -68,12 +68,10 @@ public:
68 Out<Service::PSC::Time::CalendarTime> out_calendar_time, 68 Out<Service::PSC::Time::CalendarTime> out_calendar_time,
69 Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time); 69 Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time);
70 Result ToPosixTime(Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times, 70 Result ToPosixTime(Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times,
71 Out<u32> out_times_count, Service::PSC::Time::CalendarTime& calendar_time, 71 const Service::PSC::Time::CalendarTime& calendar_time, InRule rule);
72 InRule rule);
73 Result ToPosixTimeWithMyRule(Out<u32> out_count, 72 Result ToPosixTimeWithMyRule(Out<u32> out_count,
74 OutArray<s64, BufferAttr_HipcPointer> out_times, 73 OutArray<s64, BufferAttr_HipcPointer> out_times,
75 Out<u32> out_times_count, 74 const Service::PSC::Time::CalendarTime& calendar_time);
76 Service::PSC::Time::CalendarTime& calendar_time);
77 75
78private: 76private:
79 Core::System& m_system; 77 Core::System& m_system;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 5b28be577..b60fb9139 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -18,9 +18,10 @@ namespace Service::HID {
18 18
19void LoopProcess(Core::System& system) { 19void LoopProcess(Core::System& system) {
20 auto server_manager = std::make_unique<ServerManager>(system); 20 auto server_manager = std::make_unique<ServerManager>(system);
21 std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system);
22 std::shared_ptr<HidFirmwareSettings> firmware_settings = 21 std::shared_ptr<HidFirmwareSettings> firmware_settings =
23 std::make_shared<HidFirmwareSettings>(system); 22 std::make_shared<HidFirmwareSettings>(system);
23 std::shared_ptr<ResourceManager> resource_manager =
24 std::make_shared<ResourceManager>(system, firmware_settings);
24 25
25 // TODO: Remove this hack when am is emulated properly. 26 // TODO: Remove this hack when am is emulated properly.
26 resource_manager->Initialize(); 27 resource_manager->Initialize();
@@ -31,7 +32,7 @@ void LoopProcess(Core::System& system) {
31 server_manager->RegisterNamedService( 32 server_manager->RegisterNamedService(
32 "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings)); 33 "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));
33 server_manager->RegisterNamedService( 34 server_manager->RegisterNamedService(
34 "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager)); 35 "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager, firmware_settings));
35 server_manager->RegisterNamedService( 36 server_manager->RegisterNamedService(
36 "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings)); 37 "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings));
37 38
diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp
index f2a767d37..610af34dd 100644
--- a/src/core/hle/service/hid/hid_debug_server.cpp
+++ b/src/core/hle/service/hid/hid_debug_server.cpp
@@ -1,27 +1,37 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <algorithm>
5
4#include "core/hle/service/hid/hid_debug_server.h" 6#include "core/hle/service/hid/hid_debug_server.h"
5#include "core/hle/service/ipc_helpers.h" 7#include "core/hle/service/ipc_helpers.h"
8#include "hid_core/hid_types.h"
6#include "hid_core/resource_manager.h" 9#include "hid_core/resource_manager.h"
10#include "hid_core/resources/hid_firmware_settings.h"
11
12#include "hid_core/resources/touch_screen/gesture.h"
13#include "hid_core/resources/touch_screen/touch_screen.h"
14#include "hid_core/resources/touch_screen/touch_types.h"
7 15
8namespace Service::HID { 16namespace Service::HID {
9 17
10IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource) 18IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
11 : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} { 19 std::shared_ptr<HidFirmwareSettings> settings)
20 : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource}, firmware_settings{
21 settings} {
12 // clang-format off 22 // clang-format off
13 static const FunctionInfo functions[] = { 23 static const FunctionInfo functions[] = {
14 {0, nullptr, "DeactivateDebugPad"}, 24 {0, nullptr, "DeactivateDebugPad"},
15 {1, nullptr, "SetDebugPadAutoPilotState"}, 25 {1, nullptr, "SetDebugPadAutoPilotState"},
16 {2, nullptr, "UnsetDebugPadAutoPilotState"}, 26 {2, nullptr, "UnsetDebugPadAutoPilotState"},
17 {10, nullptr, "DeactivateTouchScreen"}, 27 {10, &IHidDebugServer::DeactivateTouchScreen, "DeactivateTouchScreen"},
18 {11, nullptr, "SetTouchScreenAutoPilotState"}, 28 {11, &IHidDebugServer::SetTouchScreenAutoPilotState, "SetTouchScreenAutoPilotState"},
19 {12, nullptr, "UnsetTouchScreenAutoPilotState"}, 29 {12, &IHidDebugServer::UnsetTouchScreenAutoPilotState, "UnsetTouchScreenAutoPilotState"},
20 {13, nullptr, "GetTouchScreenConfiguration"}, 30 {13, &IHidDebugServer::GetTouchScreenConfiguration, "GetTouchScreenConfiguration"},
21 {14, nullptr, "ProcessTouchScreenAutoTune"}, 31 {14, &IHidDebugServer::ProcessTouchScreenAutoTune, "ProcessTouchScreenAutoTune"},
22 {15, nullptr, "ForceStopTouchScreenManagement"}, 32 {15, &IHidDebugServer::ForceStopTouchScreenManagement, "ForceStopTouchScreenManagement"},
23 {16, nullptr, "ForceRestartTouchScreenManagement"}, 33 {16, &IHidDebugServer::ForceRestartTouchScreenManagement, "ForceRestartTouchScreenManagement"},
24 {17, nullptr, "IsTouchScreenManaged"}, 34 {17, &IHidDebugServer::IsTouchScreenManaged, "IsTouchScreenManaged"},
25 {20, nullptr, "DeactivateMouse"}, 35 {20, nullptr, "DeactivateMouse"},
26 {21, nullptr, "SetMouseAutoPilotState"}, 36 {21, nullptr, "SetMouseAutoPilotState"},
27 {22, nullptr, "UnsetMouseAutoPilotState"}, 37 {22, nullptr, "UnsetMouseAutoPilotState"},
@@ -37,7 +47,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
37 {60, nullptr, "ClearNpadSystemCommonPolicy"}, 47 {60, nullptr, "ClearNpadSystemCommonPolicy"},
38 {61, nullptr, "DeactivateNpad"}, 48 {61, nullptr, "DeactivateNpad"},
39 {62, nullptr, "ForceDisconnectNpad"}, 49 {62, nullptr, "ForceDisconnectNpad"},
40 {91, nullptr, "DeactivateGesture"}, 50 {91, &IHidDebugServer::DeactivateGesture, "DeactivateGesture"},
41 {110, nullptr, "DeactivateHomeButton"}, 51 {110, nullptr, "DeactivateHomeButton"},
42 {111, nullptr, "SetHomeButtonAutoPilotState"}, 52 {111, nullptr, "SetHomeButtonAutoPilotState"},
43 {112, nullptr, "UnsetHomeButtonAutoPilotState"}, 53 {112, nullptr, "UnsetHomeButtonAutoPilotState"},
@@ -150,6 +160,170 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
150} 160}
151 161
152IHidDebugServer::~IHidDebugServer() = default; 162IHidDebugServer::~IHidDebugServer() = default;
163void IHidDebugServer::DeactivateTouchScreen(HLERequestContext& ctx) {
164 LOG_INFO(Service_HID, "called");
165
166 Result result = ResultSuccess;
167
168 if (!firmware_settings->IsDeviceManaged()) {
169 result = GetResourceManager()->GetTouchScreen()->Deactivate();
170 }
171
172 IPC::ResponseBuilder rb{ctx, 2};
173 rb.Push(result);
174}
175
176void IHidDebugServer::SetTouchScreenAutoPilotState(HLERequestContext& ctx) {
177 AutoPilotState auto_pilot{};
178 auto_pilot.count = ctx.GetReadBufferNumElements<TouchState>();
179 const auto buffer = ctx.ReadBuffer();
180
181 auto_pilot.count = std::min(auto_pilot.count, static_cast<u64>(auto_pilot.state.size()));
182 memcpy(auto_pilot.state.data(), buffer.data(), auto_pilot.count * sizeof(TouchState));
183
184 LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count);
185
186 const Result result =
187 GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot);
188
189 IPC::ResponseBuilder rb{ctx, 2};
190 rb.Push(result);
191}
192
193void IHidDebugServer::UnsetTouchScreenAutoPilotState(HLERequestContext& ctx) {
194 LOG_INFO(Service_HID, "called");
195
196 const Result result = GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState();
197
198 IPC::ResponseBuilder rb{ctx, 2};
199 rb.Push(result);
200}
201
202void IHidDebugServer::GetTouchScreenConfiguration(HLERequestContext& ctx) {
203 IPC::RequestParser rp{ctx};
204 const auto applet_resource_user_id{rp.Pop<u64>()};
205
206 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
207
208 Core::HID::TouchScreenConfigurationForNx touchscreen_config{};
209 const Result result = GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration(
210 touchscreen_config, applet_resource_user_id);
211
212 if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
213 touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
214 touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
215 }
216
217 IPC::ResponseBuilder rb{ctx, 6};
218 rb.Push(result);
219 rb.PushRaw(touchscreen_config);
220}
221
222void IHidDebugServer::ProcessTouchScreenAutoTune(HLERequestContext& ctx) {
223 LOG_INFO(Service_HID, "called");
224
225 Result result = GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune();
226
227 IPC::ResponseBuilder rb{ctx, 2};
228 rb.Push(result);
229}
230
231void IHidDebugServer::ForceStopTouchScreenManagement(HLERequestContext& ctx) {
232 LOG_INFO(Service_HID, "called");
233
234 if (!firmware_settings->IsDeviceManaged()) {
235 IPC::ResponseBuilder rb{ctx, 2};
236 rb.Push(ResultSuccess);
237 return;
238 }
239
240 Result result = ResultSuccess;
241 bool is_touch_active{};
242 bool is_gesture_active{};
243 auto touch_screen = GetResourceManager()->GetTouchScreen();
244 auto gesture = GetResourceManager()->GetGesture();
245
246 if (firmware_settings->IsTouchI2cManaged()) {
247 result = touch_screen->IsActive(is_touch_active);
248 if (result.IsSuccess()) {
249 result = gesture->IsActive(is_gesture_active);
250 }
251 if (result.IsSuccess() && is_touch_active) {
252 result = touch_screen->Deactivate();
253 }
254 if (result.IsSuccess() && is_gesture_active) {
255 result = gesture->Deactivate();
256 }
257 }
258
259 IPC::ResponseBuilder rb{ctx, 2};
260 rb.Push(result);
261}
262
263void IHidDebugServer::ForceRestartTouchScreenManagement(HLERequestContext& ctx) {
264 IPC::RequestParser rp{ctx};
265 struct Parameters {
266 u32 basic_gesture_id;
267 INSERT_PADDING_WORDS_NOINIT(1);
268 u64 applet_resource_user_id;
269 };
270 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
271
272 const auto parameters{rp.PopRaw<Parameters>()};
273
274 LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
275 parameters.basic_gesture_id, parameters.applet_resource_user_id);
276
277 Result result = ResultSuccess;
278 auto touch_screen = GetResourceManager()->GetTouchScreen();
279 auto gesture = GetResourceManager()->GetGesture();
280
281 if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) {
282 result = gesture->Activate();
283 if (result.IsSuccess()) {
284 result =
285 gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id);
286 }
287 if (result.IsSuccess()) {
288 result = touch_screen->Activate();
289 }
290 if (result.IsSuccess()) {
291 result = touch_screen->Activate(parameters.applet_resource_user_id);
292 }
293 }
294
295 IPC::ResponseBuilder rb{ctx, 2};
296 rb.Push(result);
297}
298
299void IHidDebugServer::IsTouchScreenManaged(HLERequestContext& ctx) {
300 LOG_INFO(Service_HID, "called");
301
302 bool is_touch_active{};
303 bool is_gesture_active{};
304
305 Result result = GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active);
306 if (result.IsSuccess()) {
307 result = GetResourceManager()->GetGesture()->IsActive(is_gesture_active);
308 }
309
310 IPC::ResponseBuilder rb{ctx, 3};
311 rb.Push(result);
312 rb.Push(is_touch_active | is_gesture_active);
313}
314
315void IHidDebugServer::DeactivateGesture(HLERequestContext& ctx) {
316 LOG_INFO(Service_HID, "called");
317
318 Result result = ResultSuccess;
319
320 if (!firmware_settings->IsDeviceManaged()) {
321 result = GetResourceManager()->GetGesture()->Deactivate();
322 }
323
324 IPC::ResponseBuilder rb{ctx, 2};
325 rb.Push(result);
326}
153 327
154std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() { 328std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
155 resource_manager->Initialize(); 329 resource_manager->Initialize();
diff --git a/src/core/hle/service/hid/hid_debug_server.h b/src/core/hle/service/hid/hid_debug_server.h
index 406db2211..7d5b082b3 100644
--- a/src/core/hle/service/hid/hid_debug_server.h
+++ b/src/core/hle/service/hid/hid_debug_server.h
@@ -11,16 +11,29 @@ class System;
11 11
12namespace Service::HID { 12namespace Service::HID {
13class ResourceManager; 13class ResourceManager;
14class HidFirmwareSettings;
14 15
15class IHidDebugServer final : public ServiceFramework<IHidDebugServer> { 16class IHidDebugServer final : public ServiceFramework<IHidDebugServer> {
16public: 17public:
17 explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource); 18 explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
19 std::shared_ptr<HidFirmwareSettings> settings);
18 ~IHidDebugServer() override; 20 ~IHidDebugServer() override;
19 21
20private: 22private:
23 void DeactivateTouchScreen(HLERequestContext& ctx);
24 void SetTouchScreenAutoPilotState(HLERequestContext& ctx);
25 void UnsetTouchScreenAutoPilotState(HLERequestContext& ctx);
26 void GetTouchScreenConfiguration(HLERequestContext& ctx);
27 void ProcessTouchScreenAutoTune(HLERequestContext& ctx);
28 void ForceStopTouchScreenManagement(HLERequestContext& ctx);
29 void ForceRestartTouchScreenManagement(HLERequestContext& ctx);
30 void IsTouchScreenManaged(HLERequestContext& ctx);
31 void DeactivateGesture(HLERequestContext& ctx);
32
21 std::shared_ptr<ResourceManager> GetResourceManager(); 33 std::shared_ptr<ResourceManager> GetResourceManager();
22 34
23 std::shared_ptr<ResourceManager> resource_manager; 35 std::shared_ptr<ResourceManager> resource_manager;
36 std::shared_ptr<HidFirmwareSettings> firmware_settings;
24}; 37};
25 38
26} // namespace Service::HID 39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index 938b93451..3603d8ccf 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -990,8 +990,7 @@ void IHidServer::ActivateGesture(HLERequestContext& ctx) {
990 } 990 }
991 991
992 if (result.IsSuccess()) { 992 if (result.IsSuccess()) {
993 // TODO: Use gesture id here 993 result = gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id);
994 result = gesture->Activate(parameters.applet_resource_user_id);
995 } 994 }
996 995
997 IPC::ResponseBuilder rb{ctx, 2}; 996 IPC::ResponseBuilder rb{ctx, 2};
@@ -2470,14 +2469,22 @@ void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) {
2470 2469
2471void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) { 2470void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) {
2472 IPC::RequestParser rp{ctx}; 2471 IPC::RequestParser rp{ctx};
2473 const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()}; 2472 auto touchscreen_config{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
2474 const auto applet_resource_user_id{rp.Pop<u64>()}; 2473 const auto applet_resource_user_id{rp.Pop<u64>()};
2475 2474
2476 LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", 2475 LOG_INFO(Service_HID, "called, touchscreen_config={}, applet_resource_user_id={}",
2477 touchscreen_mode.mode, applet_resource_user_id); 2476 touchscreen_config.mode, applet_resource_user_id);
2477
2478 if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
2479 touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
2480 touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
2481 }
2482
2483 const Result result = GetResourceManager()->GetTouchScreen()->SetTouchScreenConfiguration(
2484 touchscreen_config, applet_resource_user_id);
2478 2485
2479 IPC::ResponseBuilder rb{ctx, 2}; 2486 IPC::ResponseBuilder rb{ctx, 2};
2480 rb.Push(ResultSuccess); 2487 rb.Push(result);
2481} 2488}
2482 2489
2483void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) { 2490void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
@@ -2505,11 +2512,12 @@ void IHidServer::SetTouchScreenResolution(HLERequestContext& ctx) {
2505 const auto height{rp.Pop<u32>()}; 2512 const auto height{rp.Pop<u32>()};
2506 const auto applet_resource_user_id{rp.Pop<u64>()}; 2513 const auto applet_resource_user_id{rp.Pop<u64>()};
2507 2514
2508 GetResourceManager()->GetTouchScreen()->SetTouchscreenDimensions(width, height);
2509
2510 LOG_INFO(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, height, 2515 LOG_INFO(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, height,
2511 applet_resource_user_id); 2516 applet_resource_user_id);
2512 2517
2518 GetResourceManager()->GetTouchScreen()->SetTouchScreenResolution(width, height,
2519 applet_resource_user_id);
2520
2513 IPC::ResponseBuilder rb{ctx, 2}; 2521 IPC::ResponseBuilder rb{ctx, 2};
2514 rb.Push(ResultSuccess); 2522 rb.Push(ResultSuccess);
2515} 2523}
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
index d1ec42edc..22471e9e2 100644
--- a/src/core/hle/service/hid/hid_system_server.cpp
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -155,9 +155,9 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
155 {1133, nullptr, "StartUsbFirmwareUpdate"}, 155 {1133, nullptr, "StartUsbFirmwareUpdate"},
156 {1134, nullptr, "GetUsbFirmwareUpdateState"}, 156 {1134, nullptr, "GetUsbFirmwareUpdateState"},
157 {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"}, 157 {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
158 {1150, nullptr, "SetTouchScreenMagnification"}, 158 {1150, &IHidSystemServer::SetTouchScreenMagnification, "SetTouchScreenMagnification"},
159 {1151, nullptr, "GetTouchScreenFirmwareVersion"}, 159 {1151, &IHidSystemServer::GetTouchScreenFirmwareVersion, "GetTouchScreenFirmwareVersion"},
160 {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, 160 {1152, &IHidSystemServer::SetTouchScreenDefaultConfiguration, "SetTouchScreenDefaultConfiguration"},
161 {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, 161 {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
162 {1154, nullptr, "IsFirmwareAvailableForNotification"}, 162 {1154, nullptr, "IsFirmwareAvailableForNotification"},
163 {1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"}, 163 {1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"},
@@ -845,12 +845,60 @@ void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContex
845 rb.Push(ResultSuccess); 845 rb.Push(ResultSuccess);
846} 846}
847 847
848void IHidSystemServer::SetTouchScreenMagnification(HLERequestContext& ctx) {
849 IPC::RequestParser rp{ctx};
850 const auto point1x{rp.Pop<f32>()};
851 const auto point1y{rp.Pop<f32>()};
852 const auto point2x{rp.Pop<f32>()};
853 const auto point2y{rp.Pop<f32>()};
854
855 LOG_INFO(Service_HID, "called, point1=-({},{}), point2=({},{})", point1x, point1y, point2x,
856 point2y);
857
858 const Result result = GetResourceManager()->GetTouchScreen()->SetTouchScreenMagnification(
859 point1x, point1y, point2x, point2y);
860
861 IPC::ResponseBuilder rb{ctx, 2};
862 rb.Push(result);
863}
864
865void IHidSystemServer::GetTouchScreenFirmwareVersion(HLERequestContext& ctx) {
866 LOG_INFO(Service_HID, "called");
867
868 Core::HID::FirmwareVersion firmware{};
869 const auto result = GetResourceManager()->GetTouchScreenFirmwareVersion(firmware);
870
871 IPC::ResponseBuilder rb{ctx, 6};
872 rb.Push(result);
873 rb.PushRaw(firmware);
874}
875
876void IHidSystemServer::SetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
877 IPC::RequestParser rp{ctx};
878 auto touchscreen_config{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
879
880 LOG_INFO(Service_HID, "called, touchscreen_config={}", touchscreen_config.mode);
881
882 if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
883 touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
884 touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
885 }
886
887 const Result result =
888 GetResourceManager()->GetTouchScreen()->SetTouchScreenDefaultConfiguration(
889 touchscreen_config);
890
891 IPC::ResponseBuilder rb{ctx, 2};
892 rb.Push(result);
893}
894
848void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { 895void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
849 LOG_WARNING(Service_HID, "(STUBBED) called"); 896 LOG_INFO(Service_HID, "called");
850 897
851 Core::HID::TouchScreenConfigurationForNx touchscreen_config{ 898 Core::HID::TouchScreenConfigurationForNx touchscreen_config{};
852 .mode = Core::HID::TouchScreenModeForNx::Finger, 899 const Result result =
853 }; 900 GetResourceManager()->GetTouchScreen()->GetTouchScreenDefaultConfiguration(
901 touchscreen_config);
854 902
855 if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && 903 if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
856 touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { 904 touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
@@ -858,7 +906,7 @@ void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx
858 } 906 }
859 907
860 IPC::ResponseBuilder rb{ctx, 6}; 908 IPC::ResponseBuilder rb{ctx, 6};
861 rb.Push(ResultSuccess); 909 rb.Push(result);
862 rb.PushRaw(touchscreen_config); 910 rb.PushRaw(touchscreen_config);
863} 911}
864 912
diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h
index 4ab4d3931..738313e08 100644
--- a/src/core/hle/service/hid/hid_system_server.h
+++ b/src/core/hle/service/hid/hid_system_server.h
@@ -71,6 +71,9 @@ private:
71 void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx); 71 void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx);
72 void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx); 72 void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx);
73 void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); 73 void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
74 void SetTouchScreenMagnification(HLERequestContext& ctx);
75 void GetTouchScreenFirmwareVersion(HLERequestContext& ctx);
76 void SetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
74 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); 77 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
75 void SetForceHandheldStyleVibration(HLERequestContext& ctx); 78 void SetForceHandheldStyleVibration(HLERequestContext& ctx);
76 void IsUsingCustomButtonConfig(HLERequestContext& ctx); 79 void IsUsingCustomButtonConfig(HLERequestContext& ctx);
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 22dc55a6d..8e3224f73 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -185,7 +185,7 @@ public:
185 {3, &IRequest::Cancel, "Cancel"}, 185 {3, &IRequest::Cancel, "Cancel"},
186 {4, &IRequest::Submit, "Submit"}, 186 {4, &IRequest::Submit, "Submit"},
187 {5, nullptr, "SetRequirement"}, 187 {5, nullptr, "SetRequirement"},
188 {6, nullptr, "SetRequirementPreset"}, 188 {6, &IRequest::SetRequirementPreset, "SetRequirementPreset"},
189 {8, nullptr, "SetPriority"}, 189 {8, nullptr, "SetPriority"},
190 {9, nullptr, "SetNetworkProfileId"}, 190 {9, nullptr, "SetNetworkProfileId"},
191 {10, nullptr, "SetRejectable"}, 191 {10, nullptr, "SetRejectable"},
@@ -237,6 +237,16 @@ private:
237 rb.PushEnum(state); 237 rb.PushEnum(state);
238 } 238 }
239 239
240 void SetRequirementPreset(HLERequestContext& ctx) {
241 IPC::RequestParser rp{ctx};
242 const auto param_1 = rp.Pop<u32>();
243
244 LOG_WARNING(Service_NIFM, "(STUBBED) called, param_1={}", param_1);
245
246 IPC::ResponseBuilder rb{ctx, 2};
247 rb.Push(ResultSuccess);
248 }
249
240 void GetResult(HLERequestContext& ctx) { 250 void GetResult(HLERequestContext& ctx) {
241 LOG_DEBUG(Service_NIFM, "(STUBBED) called"); 251 LOG_DEBUG(Service_NIFM, "(STUBBED) called");
242 252
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 2258ee609..19c3ff01b 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -3,6 +3,7 @@
3 3
4#include "common/logging/log.h" 4#include "common/logging/log.h"
5#include "common/settings.h" 5#include "common/settings.h"
6#include "core/arm/debug.h"
6#include "core/core.h" 7#include "core/core.h"
7#include "core/file_sys/control_metadata.h" 8#include "core/file_sys/control_metadata.h"
8#include "core/file_sys/patch_manager.h" 9#include "core/file_sys/patch_manager.h"
@@ -544,8 +545,8 @@ IDocumentInterface::IDocumentInterface(Core::System& system_)
544 // clang-format off 545 // clang-format off
545 static const FunctionInfo functions[] = { 546 static const FunctionInfo functions[] = {
546 {21, nullptr, "GetApplicationContentPath"}, 547 {21, nullptr, "GetApplicationContentPath"},
547 {23, nullptr, "ResolveApplicationContentPath"}, 548 {23, &IDocumentInterface::ResolveApplicationContentPath, "ResolveApplicationContentPath"},
548 {93, nullptr, "GetRunningApplicationProgramId"}, 549 {92, &IDocumentInterface::GetRunningApplicationProgramId, "GetRunningApplicationProgramId"},
549 }; 550 };
550 // clang-format on 551 // clang-format on
551 552
@@ -554,6 +555,32 @@ IDocumentInterface::IDocumentInterface(Core::System& system_)
554 555
555IDocumentInterface::~IDocumentInterface() = default; 556IDocumentInterface::~IDocumentInterface() = default;
556 557
558void IDocumentInterface::ResolveApplicationContentPath(HLERequestContext& ctx) {
559 struct ContentPath {
560 u8 file_system_proxy_type;
561 u64 program_id;
562 };
563 static_assert(sizeof(ContentPath) == 0x10, "ContentPath has wrong size");
564
565 IPC::RequestParser rp{ctx};
566 auto content_path = rp.PopRaw<ContentPath>();
567 LOG_WARNING(Service_NS, "(STUBBED) called, file_system_proxy_type={}, program_id={:016X}",
568 content_path.file_system_proxy_type, content_path.program_id);
569
570 IPC::ResponseBuilder rb{ctx, 2};
571 rb.Push(ResultSuccess);
572}
573
574void IDocumentInterface::GetRunningApplicationProgramId(HLERequestContext& ctx) {
575 IPC::RequestParser rp{ctx};
576 const auto caller_program_id = rp.PopRaw<u64>();
577 LOG_WARNING(Service_NS, "(STUBBED) called, caller_program_id={:016X}", caller_program_id);
578
579 IPC::ResponseBuilder rb{ctx, 4};
580 rb.Push(ResultSuccess);
581 rb.Push<u64>(system.GetApplicationProcessProgramID());
582}
583
557IDownloadTaskInterface::IDownloadTaskInterface(Core::System& system_) 584IDownloadTaskInterface::IDownloadTaskInterface(Core::System& system_)
558 : ServiceFramework{system_, "IDownloadTaskInterface"} { 585 : ServiceFramework{system_, "IDownloadTaskInterface"} {
559 // clang-format off 586 // clang-format off
@@ -613,6 +640,40 @@ IFactoryResetInterface::IFactoryResetInterface(Core::System& system_)
613 640
614IFactoryResetInterface::~IFactoryResetInterface() = default; 641IFactoryResetInterface::~IFactoryResetInterface() = default;
615 642
643IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::System& system_)
644 : ServiceFramework{system_, "IReadOnlyApplicationRecordInterface"} {
645 static const FunctionInfo functions[] = {
646 {0, &IReadOnlyApplicationRecordInterface::HasApplicationRecord, "HasApplicationRecord"},
647 {1, nullptr, "NotifyApplicationFailure"},
648 {2, &IReadOnlyApplicationRecordInterface::IsDataCorruptedResult, "IsDataCorruptedResult"},
649 };
650 // clang-format on
651
652 RegisterHandlers(functions);
653}
654
655IReadOnlyApplicationRecordInterface::~IReadOnlyApplicationRecordInterface() = default;
656
657void IReadOnlyApplicationRecordInterface::HasApplicationRecord(HLERequestContext& ctx) {
658 IPC::RequestParser rp{ctx};
659 const u64 program_id = rp.PopRaw<u64>();
660 LOG_WARNING(Service_NS, "(STUBBED) called, program_id={:X}", program_id);
661
662 IPC::ResponseBuilder rb{ctx, 3};
663 rb.Push(ResultSuccess);
664 rb.Push<u8>(1);
665}
666
667void IReadOnlyApplicationRecordInterface::IsDataCorruptedResult(HLERequestContext& ctx) {
668 IPC::RequestParser rp{ctx};
669 const auto result = rp.PopRaw<Result>();
670 LOG_WARNING(Service_NS, "(STUBBED) called, result={:#x}", result.GetInnerValue());
671
672 IPC::ResponseBuilder rb{ctx, 3};
673 rb.Push(ResultSuccess);
674 rb.Push<u8>(0);
675}
676
616IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface( 677IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
617 Core::System& system_) 678 Core::System& system_)
618 : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} { 679 : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
@@ -663,7 +724,7 @@ NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name
663 static const FunctionInfo functions[] = { 724 static const FunctionInfo functions[] = {
664 {7988, nullptr, "GetDynamicRightsInterface"}, 725 {7988, nullptr, "GetDynamicRightsInterface"},
665 {7989, &NS::PushInterface<IReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"}, 726 {7989, &NS::PushInterface<IReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"},
666 {7991, nullptr, "GetReadOnlyApplicationRecordInterface"}, 727 {7991, &NS::PushInterface<IReadOnlyApplicationRecordInterface>, "GetReadOnlyApplicationRecordInterface"},
667 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, 728 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
668 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, 729 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
669 {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"}, 730 {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 34d2a45dc..9ee306ef9 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -58,6 +58,10 @@ class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
58public: 58public:
59 explicit IDocumentInterface(Core::System& system_); 59 explicit IDocumentInterface(Core::System& system_);
60 ~IDocumentInterface() override; 60 ~IDocumentInterface() override;
61
62private:
63 void ResolveApplicationContentPath(HLERequestContext& ctx);
64 void GetRunningApplicationProgramId(HLERequestContext& ctx);
61}; 65};
62 66
63class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> { 67class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
@@ -78,6 +82,17 @@ public:
78 ~IFactoryResetInterface() override; 82 ~IFactoryResetInterface() override;
79}; 83};
80 84
85class IReadOnlyApplicationRecordInterface final
86 : public ServiceFramework<IReadOnlyApplicationRecordInterface> {
87public:
88 explicit IReadOnlyApplicationRecordInterface(Core::System& system_);
89 ~IReadOnlyApplicationRecordInterface() override;
90
91private:
92 void HasApplicationRecord(HLERequestContext& ctx);
93 void IsDataCorruptedResult(HLERequestContext& ctx);
94};
95
81class IReadOnlyApplicationControlDataInterface final 96class IReadOnlyApplicationControlDataInterface final
82 : public ServiceFramework<IReadOnlyApplicationControlDataInterface> { 97 : public ServiceFramework<IReadOnlyApplicationControlDataInterface> {
83public: 98public:
diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp
index dc1b4d5be..e89cca6f2 100644
--- a/src/core/hle/service/nvdrv/core/container.cpp
+++ b/src/core/hle/service/nvdrv/core/container.cpp
@@ -83,7 +83,9 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
83 83
84 // Check if this memory block is heap. 84 // Check if this memory block is heap.
85 if (svc_mem_info.state == Kernel::Svc::MemoryState::Normal) { 85 if (svc_mem_info.state == Kernel::Svc::MemoryState::Normal) {
86 if (svc_mem_info.size > region_size) { 86 if (region_start + region_size == svc_mem_info.base_address) {
87 region_size += svc_mem_info.size;
88 } else if (svc_mem_info.size > region_size) {
87 region_size = svc_mem_info.size; 89 region_size = svc_mem_info.size;
88 region_start = svc_mem_info.base_address; 90 region_start = svc_mem_info.base_address;
89 } 91 }
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index c1ebbd62d..abe95303e 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <boost/container/small_vector.hpp>
5
4#include "common/assert.h" 6#include "common/assert.h"
5#include "common/logging/log.h" 7#include "common/logging/log.h"
6#include "core/core.h" 8#include "core/core.h"
@@ -38,19 +40,30 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
38void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} 40void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
39void nvdisp_disp0::OnClose(DeviceFD fd) {} 41void nvdisp_disp0::OnClose(DeviceFD fd) {}
40 42
41void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, 43void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers) {
42 u32 height, u32 stride, android::BufferTransformFlags transform, 44 std::vector<Tegra::FramebufferConfig> output_layers;
43 const Common::Rectangle<int>& crop_rect, 45 std::vector<Service::Nvidia::NvFence> output_fences;
44 std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) { 46 output_layers.reserve(sorted_layers.size());
45 const DAddr addr = nvmap.GetHandleAddress(buffer_handle); 47 output_fences.reserve(sorted_layers.size());
46 LOG_TRACE(Service, 48
47 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", 49 for (auto& layer : sorted_layers) {
48 addr, offset, width, height, stride, format); 50 output_layers.emplace_back(Tegra::FramebufferConfig{
51 .address = nvmap.GetHandleAddress(layer.buffer_handle),
52 .offset = layer.offset,
53 .width = layer.width,
54 .height = layer.height,
55 .stride = layer.stride,
56 .pixel_format = layer.format,
57 .transform_flags = layer.transform,
58 .crop_rect = layer.crop_rect,
59 });
49 60
50 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, 61 for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
51 stride, format, transform, crop_rect}; 62 output_fences.push_back(layer.acquire_fence.fences[i]);
63 }
64 }
52 65
53 system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); 66 system.GPU().RequestComposite(std::move(output_layers), std::move(output_fences));
54 system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); 67 system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs());
55 system.GetPerfStats().EndSystemFrame(); 68 system.GetPerfStats().EndSystemFrame();
56 system.GetPerfStats().BeginSystemFrame(); 69 system.GetPerfStats().BeginSystemFrame();
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index 5f13a50a2..1082b85c2 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -8,8 +8,7 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/math_util.h" 9#include "common/math_util.h"
10#include "core/hle/service/nvdrv/devices/nvdevice.h" 10#include "core/hle/service/nvdrv/devices/nvdevice.h"
11#include "core/hle/service/nvnflinger/buffer_transform_flags.h" 11#include "core/hle/service/nvnflinger/hwc_layer.h"
12#include "core/hle/service/nvnflinger/pixel_format.h"
13 12
14namespace Service::Nvidia::NvCore { 13namespace Service::Nvidia::NvCore {
15class Container; 14class Container;
@@ -35,11 +34,8 @@ public:
35 void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; 34 void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
36 void OnClose(DeviceFD fd) override; 35 void OnClose(DeviceFD fd) override;
37 36
38 /// Performs a screen flip, drawing the buffer pointed to by the handle. 37 /// Performs a screen flip, compositing each buffer.
39 void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, 38 void Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers);
40 u32 stride, android::BufferTransformFlags transform,
41 const Common::Rectangle<int>& crop_rect,
42 std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences);
43 39
44 Kernel::KEvent* QueryEvent(u32 event_id) override; 40 Kernel::KEvent* QueryEvent(u32 event_id) override;
45 41
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
index 86e272b41..e71652cdf 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -128,7 +128,7 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::D
128 128
129 // Ensure we maintain a clean state on failure. 129 // Ensure we maintain a clean state on failure.
130 ON_RESULT_FAILURE { 130 ON_RESULT_FAILURE {
131 ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd))); 131 R_ASSERT(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd));
132 }; 132 };
133 133
134 // Assign the allocated memory to the handle. 134 // Assign the allocated memory to the handle.
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp
new file mode 100644
index 000000000..c720dd1f8
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp
@@ -0,0 +1,215 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <boost/container/small_vector.hpp>
5
6#include "common/microprofile.h"
7#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
8#include "core/hle/service/nvnflinger/buffer_item.h"
9#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
10#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
11#include "core/hle/service/nvnflinger/hardware_composer.h"
12#include "core/hle/service/nvnflinger/hwc_layer.h"
13#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
14#include "core/hle/service/vi/display/vi_display.h"
15#include "core/hle/service/vi/layer/vi_layer.h"
16
17namespace Service::Nvnflinger {
18
19namespace {
20
21s32 NormalizeSwapInterval(f32* out_speed_scale, s32 swap_interval) {
22 if (swap_interval <= 0) {
23 // As an extension, treat nonpositive swap interval as speed multiplier.
24 if (out_speed_scale) {
25 *out_speed_scale = 2.f * static_cast<f32>(1 - swap_interval);
26 }
27
28 swap_interval = 1;
29 }
30
31 if (swap_interval >= 5) {
32 // As an extension, treat high swap interval as precise speed control.
33 if (out_speed_scale) {
34 *out_speed_scale = static_cast<f32>(swap_interval) / 100.f;
35 }
36
37 swap_interval = 1;
38 }
39
40 return swap_interval;
41}
42
43} // namespace
44
45HardwareComposer::HardwareComposer() = default;
46HardwareComposer::~HardwareComposer() = default;
47
48u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
49 Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance) {
50 boost::container::small_vector<HwcLayer, 2> composition_stack;
51
52 m_frame_number += frame_advance;
53
54 // Release any necessary framebuffers.
55 for (auto& [layer_id, framebuffer] : m_framebuffers) {
56 if (framebuffer.release_frame_number > m_frame_number) {
57 // Not yet ready to release this framebuffer.
58 continue;
59 }
60
61 if (!framebuffer.is_acquired) {
62 // Already released.
63 continue;
64 }
65
66 if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
67 // TODO: support release fence
68 // This is needed to prevent screen tearing
69 layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
70 framebuffer.is_acquired = false;
71 }
72 }
73
74 // Set default speed limit to 100%.
75 *out_speed_scale = 1.0f;
76
77 // Determine the number of vsync periods to wait before composing again.
78 std::optional<s32> swap_interval{};
79 bool has_acquired_buffer{};
80
81 // Acquire all necessary framebuffers.
82 for (size_t i = 0; i < display.GetNumLayers(); i++) {
83 auto& layer = display.GetLayer(i);
84 auto layer_id = layer.GetLayerId();
85
86 // Try to fetch the framebuffer (either new or stale).
87 const auto result = this->CacheFramebufferLocked(layer, layer_id);
88
89 // If we failed, skip this layer.
90 if (result == CacheStatus::NoBufferAvailable) {
91 continue;
92 }
93
94 // If we acquired a new buffer, we need to present.
95 if (result == CacheStatus::BufferAcquired) {
96 has_acquired_buffer = true;
97 }
98
99 const auto& buffer = m_framebuffers[layer_id];
100 const auto& item = buffer.item;
101 const auto& igbp_buffer = *item.graphic_buffer;
102
103 // TODO: get proper Z-index from layer
104 composition_stack.emplace_back(HwcLayer{
105 .buffer_handle = igbp_buffer.BufferId(),
106 .offset = igbp_buffer.Offset(),
107 .format = igbp_buffer.ExternalFormat(),
108 .width = igbp_buffer.Width(),
109 .height = igbp_buffer.Height(),
110 .stride = igbp_buffer.Stride(),
111 .z_index = 0,
112 .transform = static_cast<android::BufferTransformFlags>(item.transform),
113 .crop_rect = item.crop,
114 .acquire_fence = item.fence,
115 });
116
117 // We need to compose again either before this frame is supposed to
118 // be released, or exactly on the vsync period it should be released.
119 const s32 item_swap_interval = NormalizeSwapInterval(out_speed_scale, item.swap_interval);
120
121 // TODO: handle cases where swap intervals are relatively prime. So far,
122 // only swap intervals of 0, 1 and 2 have been observed, but if 3 were
123 // to be introduced, this would cause an issue.
124 if (swap_interval) {
125 swap_interval = std::min(*swap_interval, item_swap_interval);
126 } else {
127 swap_interval = item_swap_interval;
128 }
129 }
130
131 // If any new buffers were acquired, we can present.
132 if (has_acquired_buffer) {
133 // Sort by Z-index.
134 std::stable_sort(composition_stack.begin(), composition_stack.end(),
135 [&](auto& l, auto& r) { return l.z_index < r.z_index; });
136
137 // Composite.
138 nvdisp.Composite(composition_stack);
139 }
140
141 // Render MicroProfile.
142 MicroProfileFlip();
143
144 // Advance by at least one frame.
145 return swap_interval.value_or(1);
146}
147
148void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) {
149 // Check if we are tracking a slot with this layer_id.
150 const auto it = m_framebuffers.find(layer_id);
151 if (it == m_framebuffers.end()) {
152 return;
153 }
154
155 // Try to release the buffer item.
156 auto* const layer = display.FindLayer(layer_id);
157 if (layer && it->second.is_acquired) {
158 layer->GetConsumer().ReleaseBuffer(it->second.item, android::Fence::NoFence());
159 }
160
161 // Erase the slot.
162 m_framebuffers.erase(it);
163}
164
165bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer) {
166 // Attempt the update.
167 const auto status = layer.GetConsumer().AcquireBuffer(&framebuffer.item, {}, false);
168 if (status != android::Status::NoError) {
169 return false;
170 }
171
172 // We succeeded, so set the new release frame info.
173 framebuffer.release_frame_number =
174 NormalizeSwapInterval(nullptr, framebuffer.item.swap_interval);
175 framebuffer.is_acquired = true;
176
177 return true;
178}
179
180HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer& layer,
181 LayerId layer_id) {
182 // Check if this framebuffer is already present.
183 const auto it = m_framebuffers.find(layer_id);
184 if (it != m_framebuffers.end()) {
185 // If it's currently still acquired, we are done.
186 if (it->second.is_acquired) {
187 return CacheStatus::CachedBufferReused;
188 }
189
190 // Try to acquire a new item.
191 if (this->TryAcquireFramebufferLocked(layer, it->second)) {
192 // We got a new item.
193 return CacheStatus::BufferAcquired;
194 } else {
195 // We didn't acquire a new item, but we can reuse the slot.
196 return CacheStatus::CachedBufferReused;
197 }
198 }
199
200 // Framebuffer is not present, so try to create it.
201 Framebuffer framebuffer{};
202
203 if (this->TryAcquireFramebufferLocked(layer, framebuffer)) {
204 // Move the buffer item into a new slot.
205 m_framebuffers.emplace(layer_id, std::move(framebuffer));
206
207 // We succeeded.
208 return CacheStatus::BufferAcquired;
209 }
210
211 // We couldn't acquire the buffer item, so don't create a slot.
212 return CacheStatus::NoBufferAvailable;
213}
214
215} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/hardware_composer.h b/src/core/hle/service/nvnflinger/hardware_composer.h
new file mode 100644
index 000000000..ddab94ac9
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/hardware_composer.h
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <boost/container/flat_map.hpp>
8
9#include "core/hle/service/nvnflinger/buffer_item.h"
10
11namespace Service::Nvidia::Devices {
12class nvdisp_disp0;
13}
14
15namespace Service::VI {
16class Display;
17class Layer;
18} // namespace Service::VI
19
20namespace Service::Nvnflinger {
21
22using LayerId = u64;
23
24class HardwareComposer {
25public:
26 explicit HardwareComposer();
27 ~HardwareComposer();
28
29 u32 ComposeLocked(f32* out_speed_scale, VI::Display& display,
30 Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance);
31 void RemoveLayerLocked(VI::Display& display, LayerId layer_id);
32
33private:
34 // TODO: do we want to track frame number in vi instead?
35 u64 m_frame_number{0};
36
37private:
38 using ReleaseFrameNumber = u64;
39
40 struct Framebuffer {
41 android::BufferItem item{};
42 ReleaseFrameNumber release_frame_number{};
43 bool is_acquired{false};
44 };
45
46 enum class CacheStatus : u32 {
47 NoBufferAvailable,
48 BufferAcquired,
49 CachedBufferReused,
50 };
51
52 boost::container::flat_map<LayerId, Framebuffer> m_framebuffers{};
53
54private:
55 bool TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer);
56 CacheStatus CacheFramebufferLocked(VI::Layer& layer, LayerId layer_id);
57};
58
59} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/hwc_layer.h b/src/core/hle/service/nvnflinger/hwc_layer.h
new file mode 100644
index 000000000..3af668a25
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/hwc_layer.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/math_util.h"
7#include "core/hle/service/nvdrv/nvdata.h"
8#include "core/hle/service/nvnflinger/buffer_transform_flags.h"
9#include "core/hle/service/nvnflinger/pixel_format.h"
10#include "core/hle/service/nvnflinger/ui/fence.h"
11
12namespace Service::Nvnflinger {
13
14struct HwcLayer {
15 u32 buffer_handle;
16 u32 offset;
17 android::PixelFormat format;
18 u32 width;
19 u32 height;
20 u32 stride;
21 s32 z_index;
22 android::BufferTransformFlags transform;
23 Common::Rectangle<int> crop_rect;
24 android::Fence acquire_fence;
25};
26
27} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index 71d6fdb0c..a4e848882 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -18,6 +18,7 @@
18#include "core/hle/service/nvnflinger/buffer_item_consumer.h" 18#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
19#include "core/hle/service/nvnflinger/buffer_queue_core.h" 19#include "core/hle/service/nvnflinger/buffer_queue_core.h"
20#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" 20#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
21#include "core/hle/service/nvnflinger/hardware_composer.h"
21#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" 22#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
22#include "core/hle/service/nvnflinger/nvnflinger.h" 23#include "core/hle/service/nvnflinger/nvnflinger.h"
23#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" 24#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
@@ -198,6 +199,16 @@ bool Nvnflinger::CloseLayer(u64 layer_id) {
198 return false; 199 return false;
199} 200}
200 201
202void Nvnflinger::SetLayerVisibility(u64 layer_id, bool visible) {
203 const auto lock_guard = Lock();
204
205 for (auto& display : displays) {
206 if (auto* layer = display.FindLayer(layer_id); layer) {
207 layer->SetVisibility(visible);
208 }
209 }
210}
211
201void Nvnflinger::DestroyLayer(u64 layer_id) { 212void Nvnflinger::DestroyLayer(u64 layer_id) {
202 const auto lock_guard = Lock(); 213 const auto lock_guard = Lock();
203 214
@@ -269,45 +280,19 @@ void Nvnflinger::Compose() {
269 SCOPE_EXIT({ display.SignalVSyncEvent(); }); 280 SCOPE_EXIT({ display.SignalVSyncEvent(); });
270 281
271 // Don't do anything for displays without layers. 282 // Don't do anything for displays without layers.
272 if (!display.HasLayers()) 283 if (!display.HasLayers()) {
273 continue;
274
275 // TODO(Subv): Support more than 1 layer.
276 VI::Layer& layer = display.GetLayer(0);
277
278 android::BufferItem buffer{};
279 const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false);
280
281 if (status != android::Status::NoError) {
282 continue; 284 continue;
283 } 285 }
284 286
285 const auto& igbp_buffer = *buffer.graphic_buffer;
286
287 if (!system.IsPoweredOn()) { 287 if (!system.IsPoweredOn()) {
288 return; // We are likely shutting down 288 return; // We are likely shutting down
289 } 289 }
290 290
291 // Now send the buffer to the GPU for drawing.
292 // TODO(Subv): Support more than just disp0. The display device selection is probably based
293 // on which display we're drawing (Default, Internal, External, etc)
294 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); 291 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
295 ASSERT(nvdisp); 292 ASSERT(nvdisp);
296 293
297 Common::Rectangle<int> crop_rect{ 294 swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp,
298 static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), 295 swap_interval);
299 static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
300
301 nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(),
302 igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(),
303 static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect,
304 buffer.fence.fences, buffer.fence.num_fences);
305
306 MicroProfileFlip();
307
308 swap_interval = buffer.swap_interval;
309
310 layer.GetConsumer().ReleaseBuffer(buffer, android::Fence::NoFence());
311 } 296 }
312} 297}
313 298
@@ -324,15 +309,16 @@ s64 Nvnflinger::GetNextTicks() const {
324 speed_scale = 0.01f; 309 speed_scale = 0.01f;
325 } 310 }
326 } 311 }
312
313 // Adjust by speed limit determined during composition.
314 speed_scale /= compose_speed_scale;
315
327 if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) { 316 if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) {
328 // Run at intended presentation rate during video playback. 317 // Run at intended presentation rate during video playback.
329 speed_scale = 1.f; 318 speed_scale = 1.f;
330 } 319 }
331 320
332 // As an extension, treat nonpositive swap interval as framerate multiplier. 321 const f32 effective_fps = 60.f / static_cast<f32>(swap_interval);
333 const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval)
334 : 60.f / static_cast<f32>(swap_interval);
335
336 return static_cast<s64>(speed_scale * (1000000000.f / effective_fps)); 322 return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
337} 323}
338 324
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index a60e0ae6b..c984d55a0 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -46,6 +46,7 @@ class BufferQueueProducer;
46namespace Service::Nvnflinger { 46namespace Service::Nvnflinger {
47 47
48class FbShareBufferManager; 48class FbShareBufferManager;
49class HardwareComposer;
49class HosBinderDriverServer; 50class HosBinderDriverServer;
50 51
51class Nvnflinger final { 52class Nvnflinger final {
@@ -79,6 +80,9 @@ public:
79 /// Closes a layer on all displays for the given layer ID. 80 /// Closes a layer on all displays for the given layer ID.
80 bool CloseLayer(u64 layer_id); 81 bool CloseLayer(u64 layer_id);
81 82
83 /// Makes a layer visible on all displays for the given layer ID.
84 void SetLayerVisibility(u64 layer_id, bool visible);
85
82 /// Destroys the given layer ID. 86 /// Destroys the given layer ID.
83 void DestroyLayer(u64 layer_id); 87 void DestroyLayer(u64 layer_id);
84 88
@@ -140,6 +144,7 @@ private:
140 u32 next_buffer_queue_id = 1; 144 u32 next_buffer_queue_id = 1;
141 145
142 s32 swap_interval = 1; 146 s32 swap_interval = 1;
147 f32 compose_speed_scale = 1.0f;
143 148
144 bool is_abandoned = false; 149 bool is_abandoned = false;
145 150
diff --git a/src/core/hle/service/psc/time/common.h b/src/core/hle/service/psc/time/common.h
index 596828b8b..3e13144a0 100644
--- a/src/core/hle/service/psc/time/common.h
+++ b/src/core/hle/service/psc/time/common.h
@@ -189,7 +189,7 @@ struct fmt::formatter<Service::PSC::Time::SteadyClockTimePoint> : fmt::formatter
189 template <typename FormatContext> 189 template <typename FormatContext>
190 auto format(const Service::PSC::Time::SteadyClockTimePoint& time_point, 190 auto format(const Service::PSC::Time::SteadyClockTimePoint& time_point,
191 FormatContext& ctx) const { 191 FormatContext& ctx) const {
192 return fmt::format_to(ctx.out(), "time_point={}", time_point.time_point); 192 return fmt::format_to(ctx.out(), "[time_point={}]", time_point.time_point);
193 } 193 }
194}; 194};
195 195
@@ -197,7 +197,7 @@ template <>
197struct fmt::formatter<Service::PSC::Time::SystemClockContext> : fmt::formatter<fmt::string_view> { 197struct fmt::formatter<Service::PSC::Time::SystemClockContext> : fmt::formatter<fmt::string_view> {
198 template <typename FormatContext> 198 template <typename FormatContext>
199 auto format(const Service::PSC::Time::SystemClockContext& context, FormatContext& ctx) const { 199 auto format(const Service::PSC::Time::SystemClockContext& context, FormatContext& ctx) const {
200 return fmt::format_to(ctx.out(), "offset={} steady_time_point={}", context.offset, 200 return fmt::format_to(ctx.out(), "[offset={} steady_time_point={}]", context.offset,
201 context.steady_time_point.time_point); 201 context.steady_time_point.time_point);
202 } 202 }
203}; 203};
@@ -206,8 +206,9 @@ template <>
206struct fmt::formatter<Service::PSC::Time::CalendarTime> : fmt::formatter<fmt::string_view> { 206struct fmt::formatter<Service::PSC::Time::CalendarTime> : fmt::formatter<fmt::string_view> {
207 template <typename FormatContext> 207 template <typename FormatContext>
208 auto format(const Service::PSC::Time::CalendarTime& calendar, FormatContext& ctx) const { 208 auto format(const Service::PSC::Time::CalendarTime& calendar, FormatContext& ctx) const {
209 return fmt::format_to(ctx.out(), "{}/{}/{} {}:{}:{}", calendar.day, calendar.month, 209 return fmt::format_to(ctx.out(), "[{:02}/{:02}/{:04} {:02}:{:02}:{:02}]", calendar.day,
210 calendar.year, calendar.hour, calendar.minute, calendar.second); 210 calendar.month, calendar.year, calendar.hour, calendar.minute,
211 calendar.second);
211 } 212 }
212}; 213};
213 214
@@ -217,7 +218,7 @@ struct fmt::formatter<Service::PSC::Time::CalendarAdditionalInfo>
217 template <typename FormatContext> 218 template <typename FormatContext>
218 auto format(const Service::PSC::Time::CalendarAdditionalInfo& additional, 219 auto format(const Service::PSC::Time::CalendarAdditionalInfo& additional,
219 FormatContext& ctx) const { 220 FormatContext& ctx) const {
220 return fmt::format_to(ctx.out(), "weekday={} yearday={} name={} is_dst={} ut_offset={}", 221 return fmt::format_to(ctx.out(), "[weekday={} yearday={} name={} is_dst={} ut_offset={}]",
221 additional.day_of_week, additional.day_of_year, 222 additional.day_of_week, additional.day_of_year,
222 additional.name.data(), additional.is_dst, additional.ut_offset); 223 additional.name.data(), additional.is_dst, additional.ut_offset);
223 } 224 }
@@ -227,8 +228,7 @@ template <>
227struct fmt::formatter<Service::PSC::Time::LocationName> : fmt::formatter<fmt::string_view> { 228struct fmt::formatter<Service::PSC::Time::LocationName> : fmt::formatter<fmt::string_view> {
228 template <typename FormatContext> 229 template <typename FormatContext>
229 auto format(const Service::PSC::Time::LocationName& name, FormatContext& ctx) const { 230 auto format(const Service::PSC::Time::LocationName& name, FormatContext& ctx) const {
230 std::string_view n{name.data(), name.size()}; 231 return formatter<string_view>::format(name.data(), ctx);
231 return formatter<string_view>::format(n, ctx);
232 } 232 }
233}; 233};
234 234
@@ -236,8 +236,7 @@ template <>
236struct fmt::formatter<Service::PSC::Time::RuleVersion> : fmt::formatter<fmt::string_view> { 236struct fmt::formatter<Service::PSC::Time::RuleVersion> : fmt::formatter<fmt::string_view> {
237 template <typename FormatContext> 237 template <typename FormatContext>
238 auto format(const Service::PSC::Time::RuleVersion& version, FormatContext& ctx) const { 238 auto format(const Service::PSC::Time::RuleVersion& version, FormatContext& ctx) const {
239 std::string_view v{version.data(), version.size()}; 239 return formatter<string_view>::format(version.data(), ctx);
240 return formatter<string_view>::format(v, ctx);
241 } 240 }
242}; 241};
243 242
@@ -247,10 +246,11 @@ struct fmt::formatter<Service::PSC::Time::ClockSnapshot> : fmt::formatter<fmt::s
247 auto format(const Service::PSC::Time::ClockSnapshot& snapshot, FormatContext& ctx) const { 246 auto format(const Service::PSC::Time::ClockSnapshot& snapshot, FormatContext& ctx) const {
248 return fmt::format_to( 247 return fmt::format_to(
249 ctx.out(), 248 ctx.out(),
250 "user_context={} network_context={} user_time={} network_time={} user_calendar_time={} " 249 "[user_context={} network_context={} user_time={} network_time={} "
250 "user_calendar_time={} "
251 "network_calendar_time={} user_calendar_additional_time={} " 251 "network_calendar_time={} user_calendar_additional_time={} "
252 "network_calendar_additional_time={} steady_clock_time_point={} location={} " 252 "network_calendar_additional_time={} steady_clock_time_point={} location={} "
253 "is_automatic_correction_enabled={} type={}", 253 "is_automatic_correction_enabled={} type={}]",
254 snapshot.user_context, snapshot.network_context, snapshot.user_time, 254 snapshot.user_context, snapshot.network_context, snapshot.user_time,
255 snapshot.network_time, snapshot.user_calendar_time, snapshot.network_calendar_time, 255 snapshot.network_time, snapshot.user_calendar_time, snapshot.network_calendar_time,
256 snapshot.user_calendar_additional_time, snapshot.network_calendar_additional_time, 256 snapshot.user_calendar_additional_time, snapshot.network_calendar_additional_time,
@@ -266,7 +266,7 @@ struct fmt::formatter<Service::PSC::Time::ContinuousAdjustmentTimePoint>
266 auto format(const Service::PSC::Time::ContinuousAdjustmentTimePoint& time_point, 266 auto format(const Service::PSC::Time::ContinuousAdjustmentTimePoint& time_point,
267 FormatContext& ctx) const { 267 FormatContext& ctx) const {
268 return fmt::format_to(ctx.out(), 268 return fmt::format_to(ctx.out(),
269 "rtc_offset={} diff_scale={} shift_amount={} lower={} upper={}", 269 "[rtc_offset={} diff_scale={} shift_amount={} lower={} upper={}]",
270 time_point.rtc_offset, time_point.diff_scale, time_point.shift_amount, 270 time_point.rtc_offset, time_point.diff_scale, time_point.shift_amount,
271 time_point.lower, time_point.upper); 271 time_point.lower, time_point.upper);
272 } 272 }
diff --git a/src/core/hle/service/psc/time/service_manager.cpp b/src/core/hle/service/psc/time/service_manager.cpp
index ec906b723..4e1643fcb 100644
--- a/src/core/hle/service/psc/time/service_manager.cpp
+++ b/src/core/hle/service/psc/time/service_manager.cpp
@@ -120,11 +120,8 @@ Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& c
120 context, context.steady_time_point.clock_source_id.RawString(), accuracy); 120 context, context.steady_time_point.clock_source_id.RawString(), accuracy);
121 121
122 // TODO this is a hack! The network clock should be updated independently, from the ntc service 122 // TODO this is a hack! The network clock should be updated independently, from the ntc service
123 // and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot 123 // and maybe elsewhere. We do not do that, so fix the clock to the local clock.
124 // to avoid it being stuck at 0. 124 m_local_system_clock.GetContext(context);
125 if (context == Service::PSC::Time::SystemClockContext{}) {
126 m_local_system_clock.GetContext(context);
127 }
128 125
129 m_network_system_clock.SetContextWriter(m_network_system_context_writer); 126 m_network_system_clock.SetContextWriter(m_network_system_context_writer);
130 m_network_system_clock.Initialize(context, accuracy); 127 m_network_system_clock.Initialize(context, accuracy);
@@ -138,13 +135,6 @@ Result ServiceManager::SetupStandardUserSystemClockCore(bool automatic_correctio
138 LOG_DEBUG(Service_Time, "called. automatic_correction={} time_point={} clock_source_id={}", 135 LOG_DEBUG(Service_Time, "called. automatic_correction={} time_point={} clock_source_id={}",
139 automatic_correction, time_point, time_point.clock_source_id.RawString()); 136 automatic_correction, time_point, time_point.clock_source_id.RawString());
140 137
141 // TODO this is a hack! The user clock should be updated independently, from the ntc service
142 // and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot
143 // to avoid it being stuck at 0.
144 if (time_point == Service::PSC::Time::SteadyClockTimePoint{}) {
145 m_local_system_clock.GetCurrentTimePoint(time_point);
146 }
147
148 m_user_system_clock.SetAutomaticCorrection(automatic_correction); 138 m_user_system_clock.SetAutomaticCorrection(automatic_correction);
149 m_user_system_clock.SetTimePointAndSignal(time_point); 139 m_user_system_clock.SetTimePointAndSignal(time_point);
150 m_user_system_clock.SetInitialized(); 140 m_user_system_clock.SetInitialized();
diff --git a/src/core/hle/service/psc/time/time_zone.cpp b/src/core/hle/service/psc/time/time_zone.cpp
index 82ddba42f..cc855c763 100644
--- a/src/core/hle/service/psc/time/time_zone.cpp
+++ b/src/core/hle/service/psc/time/time_zone.cpp
@@ -140,11 +140,11 @@ Result TimeZone::ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary)
140 R_RETURN(ParseBinaryImpl(out_rule, binary)); 140 R_RETURN(ParseBinaryImpl(out_rule, binary));
141} 141}
142 142
143Result TimeZone::ToPosixTime(u32& out_count, std::span<s64> out_times, u32 out_times_count, 143Result TimeZone::ToPosixTime(u32& out_count, std::span<s64> out_times, size_t out_times_max_count,
144 CalendarTime& calendar, const Tz::Rule& rule) { 144 const CalendarTime& calendar, const Tz::Rule& rule) {
145 std::scoped_lock l{m_mutex}; 145 std::scoped_lock l{m_mutex};
146 146
147 auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, rule, -1); 147 auto res = ToPosixTimeImpl(out_count, out_times, out_times_max_count, calendar, rule, -1);
148 148
149 if (res != ResultSuccess) { 149 if (res != ResultSuccess) {
150 if (res == ResultTimeZoneNotFound) { 150 if (res == ResultTimeZoneNotFound) {
@@ -158,10 +158,10 @@ Result TimeZone::ToPosixTime(u32& out_count, std::span<s64> out_times, u32 out_t
158} 158}
159 159
160Result TimeZone::ToPosixTimeWithMyRule(u32& out_count, std::span<s64> out_times, 160Result TimeZone::ToPosixTimeWithMyRule(u32& out_count, std::span<s64> out_times,
161 u32 out_times_count, CalendarTime& calendar) { 161 size_t out_times_max_count, const CalendarTime& calendar) {
162 std::scoped_lock l{m_mutex}; 162 std::scoped_lock l{m_mutex};
163 163
164 auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, m_my_rule, -1); 164 auto res = ToPosixTimeImpl(out_count, out_times, out_times_max_count, calendar, m_my_rule, -1);
165 165
166 if (res != ResultSuccess) { 166 if (res != ResultSuccess) {
167 if (res == ResultTimeZoneNotFound) { 167 if (res == ResultTimeZoneNotFound) {
@@ -212,20 +212,23 @@ Result TimeZone::ToCalendarTimeImpl(CalendarTime& out_calendar_time,
212 R_SUCCEED(); 212 R_SUCCEED();
213} 213}
214 214
215Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64> out_times, u32 out_times_count, 215Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64> out_times,
216 CalendarTime& calendar, const Tz::Rule& rule, s32 is_dst) { 216 size_t out_times_max_count, const CalendarTime& calendar,
217 const Tz::Rule& rule, s32 is_dst) {
217 R_TRY(ValidateRule(rule)); 218 R_TRY(ValidateRule(rule));
218 219
219 calendar.month -= 1; 220 CalendarTime local_calendar{calendar};
220 calendar.year -= 1900; 221
222 local_calendar.month -= 1;
223 local_calendar.year -= 1900;
221 224
222 Tz::CalendarTimeInternal internal{ 225 Tz::CalendarTimeInternal internal{
223 .tm_sec = calendar.second, 226 .tm_sec = local_calendar.second,
224 .tm_min = calendar.minute, 227 .tm_min = local_calendar.minute,
225 .tm_hour = calendar.hour, 228 .tm_hour = local_calendar.hour,
226 .tm_mday = calendar.day, 229 .tm_mday = local_calendar.day,
227 .tm_mon = calendar.month, 230 .tm_mon = local_calendar.month,
228 .tm_year = calendar.year, 231 .tm_year = local_calendar.year,
229 .tm_wday = 0, 232 .tm_wday = 0,
230 .tm_yday = 0, 233 .tm_yday = 0,
231 .tm_isdst = is_dst, 234 .tm_isdst = is_dst,
@@ -243,9 +246,9 @@ Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64> out_times, u32 o
243 R_RETURN(ResultTimeZoneNotFound); 246 R_RETURN(ResultTimeZoneNotFound);
244 } 247 }
245 248
246 if (internal.tm_sec != calendar.second || internal.tm_min != calendar.minute || 249 if (internal.tm_sec != local_calendar.second || internal.tm_min != local_calendar.minute ||
247 internal.tm_hour != calendar.hour || internal.tm_mday != calendar.day || 250 internal.tm_hour != local_calendar.hour || internal.tm_mday != local_calendar.day ||
248 internal.tm_mon != calendar.month || internal.tm_year != calendar.year) { 251 internal.tm_mon != local_calendar.month || internal.tm_year != local_calendar.year) {
249 R_RETURN(ResultTimeZoneNotFound); 252 R_RETURN(ResultTimeZoneNotFound);
250 } 253 }
251 254
@@ -254,7 +257,7 @@ Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64> out_times, u32 o
254 } 257 }
255 258
256 out_times[0] = time; 259 out_times[0] = time;
257 if (out_times_count < 2) { 260 if (out_times_max_count < 2) {
258 out_count = 1; 261 out_count = 1;
259 R_SUCCEED(); 262 R_SUCCEED();
260 } 263 }
diff --git a/src/core/hle/service/psc/time/time_zone.h b/src/core/hle/service/psc/time/time_zone.h
index 6bd8f2fda..6248e45f9 100644
--- a/src/core/hle/service/psc/time/time_zone.h
+++ b/src/core/hle/service/psc/time/time_zone.h
@@ -38,18 +38,18 @@ public:
38 CalendarAdditionalInfo& calendar_additional, s64 time); 38 CalendarAdditionalInfo& calendar_additional, s64 time);
39 Result ParseBinary(LocationName& name, std::span<const u8> binary); 39 Result ParseBinary(LocationName& name, std::span<const u8> binary);
40 Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary); 40 Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary);
41 Result ToPosixTime(u32& out_count, std::span<s64> out_times, u32 out_times_count, 41 Result ToPosixTime(u32& out_count, std::span<s64> out_times, size_t out_times_max_count,
42 CalendarTime& calendar, const Tz::Rule& rule); 42 const CalendarTime& calendar, const Tz::Rule& rule);
43 Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64> out_times, u32 out_times_count, 43 Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64> out_times,
44 CalendarTime& calendar); 44 size_t out_times_max_count, const CalendarTime& calendar);
45 45
46private: 46private:
47 Result ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary); 47 Result ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary);
48 Result ToCalendarTimeImpl(CalendarTime& out_calendar_time, 48 Result ToCalendarTimeImpl(CalendarTime& out_calendar_time,
49 CalendarAdditionalInfo& out_additional_info, s64 time, 49 CalendarAdditionalInfo& out_additional_info, s64 time,
50 const Tz::Rule& rule); 50 const Tz::Rule& rule);
51 Result ToPosixTimeImpl(u32& out_count, std::span<s64> out_times, u32 out_times_count, 51 Result ToPosixTimeImpl(u32& out_count, std::span<s64> out_times, size_t out_times_max_count,
52 CalendarTime& calendar, const Tz::Rule& rule, s32 is_dst); 52 const CalendarTime& calendar, const Tz::Rule& rule, s32 is_dst);
53 53
54 bool m_initialized{}; 54 bool m_initialized{};
55 std::recursive_mutex m_mutex; 55 std::recursive_mutex m_mutex;
diff --git a/src/core/hle/service/psc/time/time_zone_service.cpp b/src/core/hle/service/psc/time/time_zone_service.cpp
index 9376a0324..eb81f5b03 100644
--- a/src/core/hle/service/psc/time/time_zone_service.cpp
+++ b/src/core/hle/service/psc/time/time_zone_service.cpp
@@ -138,32 +138,28 @@ Result TimeZoneService::ToCalendarTimeWithMyRule(Out<CalendarTime> out_calendar_
138 138
139Result TimeZoneService::ToPosixTime(Out<u32> out_count, 139Result TimeZoneService::ToPosixTime(Out<u32> out_count,
140 OutArray<s64, BufferAttr_HipcPointer> out_times, 140 OutArray<s64, BufferAttr_HipcPointer> out_times,
141 Out<u32> out_times_count, CalendarTime& calendar_time, 141 const CalendarTime& calendar_time, InRule rule) {
142 InRule rule) {
143 SCOPE_EXIT({ 142 SCOPE_EXIT({
144 LOG_DEBUG(Service_Time, 143 LOG_DEBUG(Service_Time,
145 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} " 144 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ",
146 "out_times_count={}", 145 calendar_time, *out_count, out_times[0], out_times[1]);
147 calendar_time, *out_count, out_times[0], out_times[1], *out_times_count);
148 }); 146 });
149 147
150 R_RETURN( 148 R_RETURN(
151 m_time_zone.ToPosixTime(*out_count, out_times, *out_times_count, calendar_time, *rule)); 149 m_time_zone.ToPosixTime(*out_count, out_times, out_times.size(), calendar_time, *rule));
152} 150}
153 151
154Result TimeZoneService::ToPosixTimeWithMyRule(Out<u32> out_count, 152Result TimeZoneService::ToPosixTimeWithMyRule(Out<u32> out_count,
155 OutArray<s64, BufferAttr_HipcPointer> out_times, 153 OutArray<s64, BufferAttr_HipcPointer> out_times,
156 Out<u32> out_times_count, 154 const CalendarTime& calendar_time) {
157 CalendarTime& calendar_time) {
158 SCOPE_EXIT({ 155 SCOPE_EXIT({
159 LOG_DEBUG(Service_Time, 156 LOG_DEBUG(Service_Time,
160 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} " 157 "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ",
161 "out_times_count={}", 158 calendar_time, *out_count, out_times[0], out_times[1]);
162 calendar_time, *out_count, out_times[0], out_times[1], *out_times_count);
163 }); 159 });
164 160
165 R_RETURN( 161 R_RETURN(
166 m_time_zone.ToPosixTimeWithMyRule(*out_count, out_times, *out_times_count, calendar_time)); 162 m_time_zone.ToPosixTimeWithMyRule(*out_count, out_times, out_times.size(), calendar_time));
167} 163}
168 164
169} // namespace Service::PSC::Time 165} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/time_zone_service.h b/src/core/hle/service/psc/time/time_zone_service.h
index 084e3f907..6eb9ddc4b 100644
--- a/src/core/hle/service/psc/time/time_zone_service.h
+++ b/src/core/hle/service/psc/time/time_zone_service.h
@@ -50,10 +50,10 @@ public:
50 Result ToCalendarTimeWithMyRule(Out<CalendarTime> out_calendar_time, 50 Result ToCalendarTimeWithMyRule(Out<CalendarTime> out_calendar_time,
51 Out<CalendarAdditionalInfo> out_additional_info, s64 time); 51 Out<CalendarAdditionalInfo> out_additional_info, s64 time);
52 Result ToPosixTime(Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times, 52 Result ToPosixTime(Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times,
53 Out<u32> out_times_count, CalendarTime& calendar_time, InRule rule); 53 const CalendarTime& calendar_time, InRule rule);
54 Result ToPosixTimeWithMyRule(Out<u32> out_count, 54 Result ToPosixTimeWithMyRule(Out<u32> out_count,
55 OutArray<s64, BufferAttr_HipcPointer> out_times, 55 OutArray<s64, BufferAttr_HipcPointer> out_times,
56 Out<u32> out_times_count, CalendarTime& calendar_time); 56 const CalendarTime& calendar_time);
57 57
58private: 58private:
59 Core::System& m_system; 59 Core::System& m_system;
diff --git a/src/core/hle/service/set/setting_formats/system_settings.h b/src/core/hle/service/set/setting_formats/system_settings.h
index ebc373da5..40230182a 100644
--- a/src/core/hle/service/set/setting_formats/system_settings.h
+++ b/src/core/hle/service/set/setting_formats/system_settings.h
@@ -12,6 +12,7 @@
12#include "common/vector_math.h" 12#include "common/vector_math.h"
13#include "core/hle/service/set/setting_formats/private_settings.h" 13#include "core/hle/service/set/setting_formats/private_settings.h"
14#include "core/hle/service/set/settings_types.h" 14#include "core/hle/service/set/settings_types.h"
15#include "hid_core/resources/touch_screen/touch_types.h"
15 16
16namespace Service::Set { 17namespace Service::Set {
17 18
@@ -257,8 +258,7 @@ struct SystemSettings {
257 std::array<u8, 0x10> analog_stick_user_calibration_left; 258 std::array<u8, 0x10> analog_stick_user_calibration_left;
258 std::array<u8, 0x10> analog_stick_user_calibration_right; 259 std::array<u8, 0x10> analog_stick_user_calibration_right;
259 260
260 // nn::settings::system::TouchScreenMode 261 TouchScreenMode touch_screen_mode;
261 s32 touch_screen_mode;
262 INSERT_PADDING_BYTES(0x14); // Reserved 262 INSERT_PADDING_BYTES(0x14); // Reserved
263 263
264 TvSettings tv_settings; 264 TvSettings tv_settings;
diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp
index 100cb2db4..7ef4a0ded 100644
--- a/src/core/hle/service/set/system_settings_server.cpp
+++ b/src/core/hle/service/set/system_settings_server.cpp
@@ -25,7 +25,7 @@
25namespace Service::Set { 25namespace Service::Set {
26 26
27namespace { 27namespace {
28constexpr u32 SETTINGS_VERSION{2u}; 28constexpr u32 SETTINGS_VERSION{3u};
29constexpr auto SETTINGS_MAGIC = Common::MakeMagic('y', 'u', 'z', 'u', '_', 's', 'e', 't'); 29constexpr auto SETTINGS_MAGIC = Common::MakeMagic('y', 'u', 'z', 'u', '_', 's', 'e', 't');
30struct SettingsHeader { 30struct SettingsHeader {
31 u64 magic; 31 u64 magic;
@@ -275,8 +275,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
275 {184, nullptr, "SetPlatformRegion"}, 275 {184, nullptr, "SetPlatformRegion"},
276 {185, &ISystemSettingsServer::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"}, 276 {185, &ISystemSettingsServer::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"},
277 {186, nullptr, "GetMemoryUsageRateFlag"}, 277 {186, nullptr, "GetMemoryUsageRateFlag"},
278 {187, nullptr, "GetTouchScreenMode"}, 278 {187, &ISystemSettingsServer::GetTouchScreenMode, "GetTouchScreenMode"},
279 {188, nullptr, "SetTouchScreenMode"}, 279 {188, &ISystemSettingsServer::SetTouchScreenMode, "SetTouchScreenMode"},
280 {189, nullptr, "GetButtonConfigSettingsFull"}, 280 {189, nullptr, "GetButtonConfigSettingsFull"},
281 {190, nullptr, "SetButtonConfigSettingsFull"}, 281 {190, nullptr, "SetButtonConfigSettingsFull"},
282 {191, nullptr, "GetButtonConfigSettingsEmbedded"}, 282 {191, nullptr, "GetButtonConfigSettingsEmbedded"},
@@ -1395,6 +1395,28 @@ void ISystemSettingsServer::GetHomeMenuSchemeModel(HLERequestContext& ctx) {
1395 rb.Push(0); 1395 rb.Push(0);
1396} 1396}
1397 1397
1398void ISystemSettingsServer::GetTouchScreenMode(HLERequestContext& ctx) {
1399 TouchScreenMode touch_screen_mode{};
1400 auto res = GetTouchScreenMode(touch_screen_mode);
1401
1402 LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode);
1403
1404 IPC::ResponseBuilder rb{ctx, 3};
1405 rb.Push(res);
1406 rb.PushEnum(touch_screen_mode);
1407}
1408
1409void ISystemSettingsServer::SetTouchScreenMode(HLERequestContext& ctx) {
1410 IPC::RequestParser rp{ctx};
1411 const auto touch_screen_mode = rp.PopEnum<TouchScreenMode>();
1412 auto res = SetTouchScreenMode(touch_screen_mode);
1413
1414 LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode);
1415
1416 IPC::ResponseBuilder rb{ctx, 2};
1417 rb.Push(res);
1418}
1419
1398void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) { 1420void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) {
1399 LOG_INFO(Service_SET, "called, field_testing_flag={}", m_system_settings.field_testing_flag); 1421 LOG_INFO(Service_SET, "called, field_testing_flag={}", m_system_settings.field_testing_flag);
1400 1422
@@ -1670,4 +1692,15 @@ Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
1670 R_SUCCEED(); 1692 R_SUCCEED();
1671} 1693}
1672 1694
1695Result ISystemSettingsServer::GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const {
1696 touch_screen_mode = m_system_settings.touch_screen_mode;
1697 R_SUCCEED();
1698}
1699
1700Result ISystemSettingsServer::SetTouchScreenMode(TouchScreenMode touch_screen_mode) {
1701 m_system_settings.touch_screen_mode = touch_screen_mode;
1702 SetSaveNeeded();
1703 R_SUCCEED();
1704}
1705
1673} // namespace Service::Set 1706} // namespace Service::Set
diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h
index 1982b9723..9a3b36f0c 100644
--- a/src/core/hle/service/set/system_settings_server.h
+++ b/src/core/hle/service/set/system_settings_server.h
@@ -74,6 +74,8 @@ public:
74 Service::PSC::Time::SteadyClockTimePoint& out_time_point) const; 74 Service::PSC::Time::SteadyClockTimePoint& out_time_point) const;
75 Result SetUserSystemClockAutomaticCorrectionUpdatedTime( 75 Result SetUserSystemClockAutomaticCorrectionUpdatedTime(
76 const Service::PSC::Time::SteadyClockTimePoint& time_point); 76 const Service::PSC::Time::SteadyClockTimePoint& time_point);
77 Result GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const;
78 Result SetTouchScreenMode(TouchScreenMode touch_screen_mode);
77 79
78private: 80private:
79 void SetLanguageCode(HLERequestContext& ctx); 81 void SetLanguageCode(HLERequestContext& ctx);
@@ -154,6 +156,8 @@ private:
154 void GetChineseTraditionalInputMethod(HLERequestContext& ctx); 156 void GetChineseTraditionalInputMethod(HLERequestContext& ctx);
155 void GetHomeMenuScheme(HLERequestContext& ctx); 157 void GetHomeMenuScheme(HLERequestContext& ctx);
156 void GetHomeMenuSchemeModel(HLERequestContext& ctx); 158 void GetHomeMenuSchemeModel(HLERequestContext& ctx);
159 void GetTouchScreenMode(HLERequestContext& ctx);
160 void SetTouchScreenMode(HLERequestContext& ctx);
157 void GetFieldTestingFlag(HLERequestContext& ctx); 161 void GetFieldTestingFlag(HLERequestContext& ctx);
158 void GetPanelCrcMode(HLERequestContext& ctx); 162 void GetPanelCrcMode(HLERequestContext& ctx);
159 void SetPanelCrcMode(HLERequestContext& ctx); 163 void SetPanelCrcMode(HLERequestContext& ctx);
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index 725311c53..7f2af9acc 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -16,6 +16,7 @@
16#include "core/hle/service/nvnflinger/buffer_queue_consumer.h" 16#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
17#include "core/hle/service/nvnflinger/buffer_queue_core.h" 17#include "core/hle/service/nvnflinger/buffer_queue_core.h"
18#include "core/hle/service/nvnflinger/buffer_queue_producer.h" 18#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
19#include "core/hle/service/nvnflinger/hardware_composer.h"
19#include "core/hle/service/nvnflinger/hos_binder_driver_server.h" 20#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
20#include "core/hle/service/vi/display/vi_display.h" 21#include "core/hle/service/vi/display/vi_display.h"
21#include "core/hle/service/vi/layer/vi_layer.h" 22#include "core/hle/service/vi/layer/vi_layer.h"
@@ -43,6 +44,7 @@ Display::Display(u64 id, std::string name_,
43 KernelHelpers::ServiceContext& service_context_, Core::System& system_) 44 KernelHelpers::ServiceContext& service_context_, Core::System& system_)
44 : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_}, 45 : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_},
45 service_context{service_context_} { 46 service_context{service_context_} {
47 hardware_composer = std::make_unique<Nvnflinger::HardwareComposer>();
46 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); 48 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
47} 49}
48 50
@@ -53,7 +55,7 @@ Display::~Display() {
53Layer& Display::GetLayer(std::size_t index) { 55Layer& Display::GetLayer(std::size_t index) {
54 size_t i = 0; 56 size_t i = 0;
55 for (auto& layer : layers) { 57 for (auto& layer : layers) {
56 if (!layer->IsOpen()) { 58 if (!layer->IsOpen() || !layer->IsVisible()) {
57 continue; 59 continue;
58 } 60 }
59 61
@@ -68,7 +70,7 @@ Layer& Display::GetLayer(std::size_t index) {
68} 70}
69 71
70size_t Display::GetNumLayers() const { 72size_t Display::GetNumLayers() const {
71 return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen(); }); 73 return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen() && l->IsVisible(); });
72} 74}
73 75
74Kernel::KReadableEvent* Display::GetVSyncEvent() { 76Kernel::KReadableEvent* Display::GetVSyncEvent() {
@@ -81,8 +83,6 @@ void Display::SignalVSyncEvent() {
81 83
82void Display::CreateLayer(u64 layer_id, u32 binder_id, 84void Display::CreateLayer(u64 layer_id, u32 binder_id,
83 Service::Nvidia::NvCore::Container& nv_core) { 85 Service::Nvidia::NvCore::Container& nv_core) {
84 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment");
85
86 auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); 86 auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile());
87 87
88 auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); 88 auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 8eb8a5155..220292cff 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -11,9 +11,14 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/hle/result.h" 12#include "core/hle/result.h"
13 13
14namespace Core {
15class System;
16}
17
14namespace Kernel { 18namespace Kernel {
15class KEvent; 19class KEvent;
16} 20class KReadableEvent;
21} // namespace Kernel
17 22
18namespace Service::android { 23namespace Service::android {
19class BufferQueueProducer; 24class BufferQueueProducer;
@@ -24,8 +29,9 @@ class ServiceContext;
24} 29}
25 30
26namespace Service::Nvnflinger { 31namespace Service::Nvnflinger {
32class HardwareComposer;
27class HosBinderDriverServer; 33class HosBinderDriverServer;
28} 34} // namespace Service::Nvnflinger
29 35
30namespace Service::Nvidia::NvCore { 36namespace Service::Nvidia::NvCore {
31class Container; 37class Container;
@@ -118,6 +124,10 @@ public:
118 /// 124 ///
119 const Layer* FindLayer(u64 layer_id) const; 125 const Layer* FindLayer(u64 layer_id) const;
120 126
127 Nvnflinger::HardwareComposer& GetComposer() const {
128 return *hardware_composer;
129 }
130
121private: 131private:
122 u64 display_id; 132 u64 display_id;
123 std::string name; 133 std::string name;
@@ -125,6 +135,7 @@ private:
125 KernelHelpers::ServiceContext& service_context; 135 KernelHelpers::ServiceContext& service_context;
126 136
127 std::vector<std::unique_ptr<Layer>> layers; 137 std::vector<std::unique_ptr<Layer>> layers;
138 std::unique_ptr<Nvnflinger::HardwareComposer> hardware_composer;
128 Kernel::KEvent* vsync_event{}; 139 Kernel::KEvent* vsync_event{};
129 bool is_abandoned{}; 140 bool is_abandoned{};
130}; 141};
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp
index 04e52a23b..493bd6e9e 100644
--- a/src/core/hle/service/vi/layer/vi_layer.cpp
+++ b/src/core/hle/service/vi/layer/vi_layer.cpp
@@ -9,7 +9,7 @@ Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
9 android::BufferQueueProducer& binder_, 9 android::BufferQueueProducer& binder_,
10 std::shared_ptr<android::BufferItemConsumer>&& consumer_) 10 std::shared_ptr<android::BufferItemConsumer>&& consumer_)
11 : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, 11 : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_},
12 consumer{std::move(consumer_)}, open{false} {} 12 consumer{std::move(consumer_)}, open{false}, visible{true} {}
13 13
14Layer::~Layer() = default; 14Layer::~Layer() = default;
15 15
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
index f95e2dc71..b4b031ee7 100644
--- a/src/core/hle/service/vi/layer/vi_layer.h
+++ b/src/core/hle/service/vi/layer/vi_layer.h
@@ -72,6 +72,14 @@ public:
72 return core; 72 return core;
73 } 73 }
74 74
75 bool IsVisible() const {
76 return visible;
77 }
78
79 void SetVisibility(bool v) {
80 visible = v;
81 }
82
75 bool IsOpen() const { 83 bool IsOpen() const {
76 return open; 84 return open;
77 } 85 }
@@ -91,6 +99,7 @@ private:
91 android::BufferQueueProducer& binder; 99 android::BufferQueueProducer& binder;
92 std::shared_ptr<android::BufferItemConsumer> consumer; 100 std::shared_ptr<android::BufferItemConsumer> consumer;
93 bool open; 101 bool open;
102 bool visible;
94}; 103};
95 104
96} // namespace Service::VI 105} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 1f3d82c57..d508ed28c 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -195,8 +195,9 @@ private:
195 void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) { 195 void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) {
196 IPC::RequestParser rp{ctx}; 196 IPC::RequestParser rp{ctx};
197 const u64 buffer_id = rp.PopRaw<u64>(); 197 const u64 buffer_id = rp.PopRaw<u64>();
198 const u64 aruid = ctx.GetPID();
198 199
199 LOG_INFO(Service_VI, "called. buffer_id={:#x}", buffer_id); 200 LOG_INFO(Service_VI, "called. buffer_id={:#x}, aruid={:#x}", buffer_id, aruid);
200 201
201 struct OutputParameters { 202 struct OutputParameters {
202 s32 nvmap_handle; 203 s32 nvmap_handle;
@@ -206,7 +207,7 @@ private:
206 OutputParameters out{}; 207 OutputParameters out{};
207 Nvnflinger::SharedMemoryPoolLayout layout{}; 208 Nvnflinger::SharedMemoryPoolLayout layout{};
208 const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId( 209 const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId(
209 &out.size, &out.nvmap_handle, &layout, buffer_id, 0); 210 &out.size, &out.nvmap_handle, &layout, buffer_id, aruid);
210 211
211 ctx.WriteBuffer(&layout, sizeof(layout)); 212 ctx.WriteBuffer(&layout, sizeof(layout));
212 213
@@ -535,6 +536,12 @@ public:
535 RegisterHandlers(functions); 536 RegisterHandlers(functions);
536 } 537 }
537 538
539 ~IApplicationDisplayService() {
540 for (const auto layer_id : stray_layer_ids) {
541 nvnflinger.DestroyLayer(layer_id);
542 }
543 }
544
538private: 545private:
539 enum class ConvertedScaleMode : u64 { 546 enum class ConvertedScaleMode : u64 {
540 Freeze = 0, 547 Freeze = 0,
@@ -770,6 +777,7 @@ private:
770 return; 777 return;
771 } 778 }
772 779
780 stray_layer_ids.push_back(*layer_id);
773 const auto buffer_queue_id = nvnflinger.FindBufferQueueId(display_id, *layer_id); 781 const auto buffer_queue_id = nvnflinger.FindBufferQueueId(display_id, *layer_id);
774 if (!buffer_queue_id) { 782 if (!buffer_queue_id) {
775 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); 783 LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
@@ -916,6 +924,7 @@ private:
916 924
917 Nvnflinger::Nvnflinger& nvnflinger; 925 Nvnflinger::Nvnflinger& nvnflinger;
918 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; 926 Nvnflinger::HosBinderDriverServer& hos_binder_driver_server;
927 std::vector<u64> stray_layer_ids;
919 bool vsync_event_fetched{false}; 928 bool vsync_event_fetched{false};
920}; 929};
921 930
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index a983f23ea..7785c1d16 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -693,20 +693,23 @@ std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() {
693 sockaddr_in addr; 693 sockaddr_in addr;
694 socklen_t addrlen = sizeof(addr); 694 socklen_t addrlen = sizeof(addr);
695 695
696 std::vector<WSAPOLLFD> host_pollfds{ 696 const bool wait_for_accept = !is_non_blocking;
697 WSAPOLLFD{fd, POLLIN, 0}, 697 if (wait_for_accept) {
698 WSAPOLLFD{GetInterruptSocket(), POLLIN, 0}, 698 std::vector<WSAPOLLFD> host_pollfds{
699 }; 699 WSAPOLLFD{fd, POLLIN, 0},
700 700 WSAPOLLFD{GetInterruptSocket(), POLLIN, 0},
701 while (true) { 701 };
702 const int pollres = 702
703 WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), -1); 703 while (true) {
704 if (host_pollfds[1].revents != 0) { 704 const int pollres =
705 // Interrupt signaled before a client could be accepted, break 705 WSAPoll(host_pollfds.data(), static_cast<ULONG>(host_pollfds.size()), -1);
706 return {AcceptResult{}, Errno::AGAIN}; 706 if (host_pollfds[1].revents != 0) {
707 } 707 // Interrupt signaled before a client could be accepted, break
708 if (pollres > 0) { 708 return {AcceptResult{}, Errno::AGAIN};
709 break; 709 }
710 if (pollres > 0) {
711 break;
712 }
710 } 713 }
711 } 714 }
712 715
@@ -913,6 +916,7 @@ Errno Socket::SetRcvTimeo(u32 value) {
913 916
914Errno Socket::SetNonBlock(bool enable) { 917Errno Socket::SetNonBlock(bool enable) {
915 if (EnableNonBlock(fd, enable)) { 918 if (EnableNonBlock(fd, enable)) {
919 is_non_blocking = enable;
916 return Errno::SUCCESS; 920 return Errno::SUCCESS;
917 } 921 }
918 return GetAndLogLastError(); 922 return GetAndLogLastError();
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h
index 4ba51f62c..3a32dff75 100644
--- a/src/core/internal_network/sockets.h
+++ b/src/core/internal_network/sockets.h
@@ -166,6 +166,9 @@ public:
166 bool IsOpened() const override; 166 bool IsOpened() const override;
167 167
168 void HandleProxyPacket(const ProxyPacket& packet) override; 168 void HandleProxyPacket(const ProxyPacket& packet) override;
169
170private:
171 bool is_non_blocking = false;
169}; 172};
170 173
171std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); 174std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
index 905f35118..d34624d28 100644
--- a/src/frontend_common/config.cpp
+++ b/src/frontend_common/config.cpp
@@ -190,9 +190,9 @@ void Config::ReadTouchscreenValues() {
190 Settings::values.touchscreen.rotation_angle = 190 Settings::values.touchscreen.rotation_angle =
191 static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0)); 191 static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0));
192 Settings::values.touchscreen.diameter_x = 192 Settings::values.touchscreen.diameter_x =
193 static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15)); 193 static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 90));
194 Settings::values.touchscreen.diameter_y = 194 Settings::values.touchscreen.diameter_y =
195 static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15)); 195 static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 90));
196} 196}
197 197
198void Config::ReadAudioValues() { 198void Config::ReadAudioValues() {
@@ -478,9 +478,9 @@ void Config::SaveTouchscreenValues() {
478 WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle, 478 WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
479 std::make_optional(static_cast<u32>(0))); 479 std::make_optional(static_cast<u32>(0)));
480 WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x, 480 WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
481 std::make_optional(static_cast<u32>(15))); 481 std::make_optional(static_cast<u32>(90)));
482 WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y, 482 WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
483 std::make_optional(static_cast<u32>(15))); 483 std::make_optional(static_cast<u32>(90)));
484} 484}
485 485
486void Config::SaveMotionTouchValues() { 486void Config::SaveMotionTouchValues() {
diff --git a/src/hid_core/CMakeLists.txt b/src/hid_core/CMakeLists.txt
index 64cd6e726..2699e1599 100644
--- a/src/hid_core/CMakeLists.txt
+++ b/src/hid_core/CMakeLists.txt
@@ -99,9 +99,14 @@ add_library(hid_core STATIC
99 resources/system_buttons/system_button_types.h 99 resources/system_buttons/system_button_types.h
100 resources/touch_screen/gesture.cpp 100 resources/touch_screen/gesture.cpp
101 resources/touch_screen/gesture.h 101 resources/touch_screen/gesture.h
102 resources/touch_screen/gesture_types.h 102 resources/touch_screen/gesture_handler.cpp
103 resources/touch_screen/gesture_handler.h
103 resources/touch_screen/touch_screen.cpp 104 resources/touch_screen/touch_screen.cpp
104 resources/touch_screen/touch_screen.h 105 resources/touch_screen/touch_screen.h
106 resources/touch_screen/touch_screen_driver.cpp
107 resources/touch_screen/touch_screen_driver.h
108 resources/touch_screen/touch_screen_resource.cpp
109 resources/touch_screen/touch_screen_resource.h
105 resources/touch_screen/touch_types.h 110 resources/touch_screen/touch_types.h
106 resources/unique_pad/unique_pad.cpp 111 resources/unique_pad/unique_pad.cpp
107 resources/unique_pad/unique_pad.h 112 resources/unique_pad/unique_pad.h
diff --git a/src/hid_core/hid_result.h b/src/hid_core/hid_result.h
index df9b28c9a..c8dd07bfe 100644
--- a/src/hid_core/hid_result.h
+++ b/src/hid_core/hid_result.h
@@ -8,6 +8,10 @@
8namespace Service::HID { 8namespace Service::HID {
9 9
10constexpr Result PalmaResultSuccess{ErrorModule::HID, 0}; 10constexpr Result PalmaResultSuccess{ErrorModule::HID, 0};
11
12constexpr Result ResultTouchNotInitialized{ErrorModule::HID, 41};
13constexpr Result ResultTouchOverflow{ErrorModule::HID, 42};
14
11constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; 15constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
12constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; 16constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
13 17
@@ -23,6 +27,10 @@ constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};
23constexpr Result ResultNfcIsNotReady{ErrorModule::HID, 461}; 27constexpr Result ResultNfcIsNotReady{ErrorModule::HID, 461};
24constexpr Result ResultNfcXcdHandleIsNotInitialized{ErrorModule::HID, 464}; 28constexpr Result ResultNfcXcdHandleIsNotInitialized{ErrorModule::HID, 464};
25constexpr Result ResultIrSensorIsNotReady{ErrorModule::HID, 501}; 29constexpr Result ResultIrSensorIsNotReady{ErrorModule::HID, 501};
30
31constexpr Result ResultGestureOverflow{ErrorModule::HID, 522};
32constexpr Result ResultGestureNotInitialized{ErrorModule::HID, 523};
33
26constexpr Result ResultMcuIsNotReady{ErrorModule::HID, 541}; 34constexpr Result ResultMcuIsNotReady{ErrorModule::HID, 541};
27 35
28constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; 36constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
diff --git a/src/hid_core/hid_types.h b/src/hid_core/hid_types.h
index ffb5f1926..1b2fc6295 100644
--- a/src/hid_core/hid_types.h
+++ b/src/hid_core/hid_types.h
@@ -299,12 +299,6 @@ enum class GyroscopeZeroDriftMode : u32 {
299 Tight = 2, 299 Tight = 2,
300}; 300};
301 301
302// This is nn::settings::system::TouchScreenMode
303enum class TouchScreenMode : u32 {
304 Stylus = 0,
305 Standard = 1,
306};
307
308// This is nn::hid::TouchScreenModeForNx 302// This is nn::hid::TouchScreenModeForNx
309enum class TouchScreenModeForNx : u8 { 303enum class TouchScreenModeForNx : u8 {
310 UseSystemSetting, 304 UseSystemSetting,
@@ -354,18 +348,6 @@ struct TouchAttribute {
354}; 348};
355static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size"); 349static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size");
356 350
357// This is nn::hid::TouchState
358struct TouchState {
359 u64 delta_time{};
360 TouchAttribute attribute{};
361 u32 finger{};
362 Common::Point<u32> position{};
363 u32 diameter_x{};
364 u32 diameter_y{};
365 u32 rotation_angle{};
366};
367static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
368
369struct TouchFinger { 351struct TouchFinger {
370 u64 last_touch{}; 352 u64 last_touch{};
371 Common::Point<float> position{}; 353 Common::Point<float> position{};
@@ -756,4 +738,14 @@ struct UniquePadId {
756}; 738};
757static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); 739static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
758 740
741// This is nn::hid::system::FirmwareVersion
742struct FirmwareVersion {
743 u8 major;
744 u8 minor;
745 u8 micro;
746 u8 revision;
747 std::array<char, 0xc> device_identifier;
748};
749static_assert(sizeof(FirmwareVersion) == 0x10, "FirmwareVersion is an invalid size");
750
759} // namespace Core::HID 751} // namespace Core::HID
diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp
index e78665d31..68ce2c7ae 100644
--- a/src/hid_core/resource_manager.cpp
+++ b/src/hid_core/resource_manager.cpp
@@ -15,6 +15,7 @@
15#include "hid_core/resources/applet_resource.h" 15#include "hid_core/resources/applet_resource.h"
16#include "hid_core/resources/debug_pad/debug_pad.h" 16#include "hid_core/resources/debug_pad/debug_pad.h"
17#include "hid_core/resources/digitizer/digitizer.h" 17#include "hid_core/resources/digitizer/digitizer.h"
18#include "hid_core/resources/hid_firmware_settings.h"
18#include "hid_core/resources/keyboard/keyboard.h" 19#include "hid_core/resources/keyboard/keyboard.h"
19#include "hid_core/resources/mouse/debug_mouse.h" 20#include "hid_core/resources/mouse/debug_mouse.h"
20#include "hid_core/resources/mouse/mouse.h" 21#include "hid_core/resources/mouse/mouse.h"
@@ -29,6 +30,8 @@
29#include "hid_core/resources/system_buttons/sleep_button.h" 30#include "hid_core/resources/system_buttons/sleep_button.h"
30#include "hid_core/resources/touch_screen/gesture.h" 31#include "hid_core/resources/touch_screen/gesture.h"
31#include "hid_core/resources/touch_screen/touch_screen.h" 32#include "hid_core/resources/touch_screen/touch_screen.h"
33#include "hid_core/resources/touch_screen/touch_screen_driver.h"
34#include "hid_core/resources/touch_screen/touch_screen_resource.h"
32#include "hid_core/resources/unique_pad/unique_pad.h" 35#include "hid_core/resources/unique_pad/unique_pad.h"
33#include "hid_core/resources/vibration/gc_vibration_device.h" 36#include "hid_core/resources/vibration/gc_vibration_device.h"
34#include "hid_core/resources/vibration/n64_vibration_device.h" 37#include "hid_core/resources/vibration/n64_vibration_device.h"
@@ -45,12 +48,16 @@ constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; //
45constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) 48constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
46constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) 49constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
47 50
48ResourceManager::ResourceManager(Core::System& system_) 51ResourceManager::ResourceManager(Core::System& system_,
49 : system{system_}, service_context{system_, "hid"} { 52 std::shared_ptr<HidFirmwareSettings> settings)
53 : firmware_settings{settings}, system{system_}, service_context{system_, "hid"} {
50 applet_resource = std::make_shared<AppletResource>(system); 54 applet_resource = std::make_shared<AppletResource>(system);
51} 55}
52 56
53ResourceManager::~ResourceManager() = default; 57ResourceManager::~ResourceManager() {
58 system.CoreTiming().UnscheduleEvent(touch_update_event);
59 input_event->Finalize();
60};
54 61
55void ResourceManager::Initialize() { 62void ResourceManager::Initialize() {
56 if (is_initialized) { 63 if (is_initialized) {
@@ -59,7 +66,9 @@ void ResourceManager::Initialize() {
59 66
60 system.HIDCore().ReloadInputDevices(); 67 system.HIDCore().ReloadInputDevices();
61 68
62 handheld_config = std::make_shared<HandheldConfig>(); 69 input_event = service_context.CreateEvent("ResourceManager:InputEvent");
70
71 InitializeHandheldConfig();
63 InitializeHidCommonSampler(); 72 InitializeHidCommonSampler();
64 InitializeTouchScreenSampler(); 73 InitializeTouchScreenSampler();
65 InitializeConsoleSixAxisSampler(); 74 InitializeConsoleSixAxisSampler();
@@ -154,6 +163,7 @@ Result ResourceManager::CreateAppletResource(u64 aruid) {
154 npad->Activate(); 163 npad->Activate();
155 six_axis->Activate(); 164 six_axis->Activate();
156 touch_screen->Activate(); 165 touch_screen->Activate();
166 gesture->Activate();
157 167
158 return GetNpad()->ActivateNpadResource(aruid); 168 return GetNpad()->ActivateNpadResource(aruid);
159} 169}
@@ -163,6 +173,17 @@ Result ResourceManager::CreateAppletResourceImpl(u64 aruid) {
163 return applet_resource->CreateAppletResource(aruid); 173 return applet_resource->CreateAppletResource(aruid);
164} 174}
165 175
176void ResourceManager::InitializeHandheldConfig() {
177 handheld_config = std::make_shared<HandheldConfig>();
178 handheld_config->is_handheld_hid_enabled = true;
179 handheld_config->is_joycon_rail_enabled = true;
180 handheld_config->is_force_handheld_style_vibration = false;
181 handheld_config->is_force_handheld = false;
182 if (firmware_settings->IsHandheldForced()) {
183 handheld_config->is_joycon_rail_enabled = false;
184 }
185}
186
166void ResourceManager::InitializeHidCommonSampler() { 187void ResourceManager::InitializeHidCommonSampler() {
167 debug_pad = std::make_shared<DebugPad>(system.HIDCore()); 188 debug_pad = std::make_shared<DebugPad>(system.HIDCore());
168 mouse = std::make_shared<Mouse>(system.HIDCore()); 189 mouse = std::make_shared<Mouse>(system.HIDCore());
@@ -170,7 +191,6 @@ void ResourceManager::InitializeHidCommonSampler() {
170 keyboard = std::make_shared<Keyboard>(system.HIDCore()); 191 keyboard = std::make_shared<Keyboard>(system.HIDCore());
171 unique_pad = std::make_shared<UniquePad>(system.HIDCore()); 192 unique_pad = std::make_shared<UniquePad>(system.HIDCore());
172 npad = std::make_shared<NPad>(system.HIDCore(), service_context); 193 npad = std::make_shared<NPad>(system.HIDCore(), service_context);
173 gesture = std::make_shared<Gesture>(system.HIDCore());
174 home_button = std::make_shared<HomeButton>(system.HIDCore()); 194 home_button = std::make_shared<HomeButton>(system.HIDCore());
175 sleep_button = std::make_shared<SleepButton>(system.HIDCore()); 195 sleep_button = std::make_shared<SleepButton>(system.HIDCore());
176 capture_button = std::make_shared<CaptureButton>(system.HIDCore()); 196 capture_button = std::make_shared<CaptureButton>(system.HIDCore());
@@ -185,7 +205,8 @@ void ResourceManager::InitializeHidCommonSampler() {
185 205
186 const auto settings = 206 const auto settings =
187 system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); 207 system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
188 npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, settings); 208 npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, input_event,
209 &input_mutex, settings);
189 210
190 six_axis->SetAppletResource(applet_resource, &shared_mutex); 211 six_axis->SetAppletResource(applet_resource, &shared_mutex);
191 mouse->SetAppletResource(applet_resource, &shared_mutex); 212 mouse->SetAppletResource(applet_resource, &shared_mutex);
@@ -196,11 +217,25 @@ void ResourceManager::InitializeHidCommonSampler() {
196} 217}
197 218
198void ResourceManager::InitializeTouchScreenSampler() { 219void ResourceManager::InitializeTouchScreenSampler() {
199 gesture = std::make_shared<Gesture>(system.HIDCore()); 220 // This is nn.hid.TouchScreenSampler
200 touch_screen = std::make_shared<TouchScreen>(system.HIDCore()); 221 touch_resource = std::make_shared<TouchResource>(system);
222 touch_driver = std::make_shared<TouchDriver>(system.HIDCore());
223 touch_screen = std::make_shared<TouchScreen>(touch_resource);
224 gesture = std::make_shared<Gesture>(touch_resource);
225
226 touch_update_event = Core::Timing::CreateEvent(
227 "HID::TouchUpdateCallback",
228 [this](s64 time,
229 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
230 touch_resource->OnTouchUpdate(time);
231 return std::nullopt;
232 });
201 233
202 touch_screen->SetAppletResource(applet_resource, &shared_mutex); 234 touch_resource->SetTouchDriver(touch_driver);
203 gesture->SetAppletResource(applet_resource, &shared_mutex); 235 touch_resource->SetAppletResource(applet_resource, &shared_mutex);
236 touch_resource->SetInputEvent(input_event, &input_mutex);
237 touch_resource->SetHandheldConfig(handheld_config);
238 touch_resource->SetTimerEvent(touch_update_event);
204} 239}
205 240
206void ResourceManager::InitializeConsoleSixAxisSampler() { 241void ResourceManager::InitializeConsoleSixAxisSampler() {
@@ -388,13 +423,15 @@ Result ResourceManager::SendVibrationValue(u64 aruid,
388 return result; 423 return result;
389} 424}
390 425
426Result ResourceManager::GetTouchScreenFirmwareVersion(Core::HID::FirmwareVersion& firmware) const {
427 return ResultSuccess;
428}
429
391void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) { 430void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
392 auto& core_timing = system.CoreTiming(); 431 auto& core_timing = system.CoreTiming();
393 debug_pad->OnUpdate(core_timing); 432 debug_pad->OnUpdate(core_timing);
394 digitizer->OnUpdate(core_timing); 433 digitizer->OnUpdate(core_timing);
395 unique_pad->OnUpdate(core_timing); 434 unique_pad->OnUpdate(core_timing);
396 gesture->OnUpdate(core_timing);
397 touch_screen->OnUpdate(core_timing);
398 palma->OnUpdate(core_timing); 435 palma->OnUpdate(core_timing);
399 home_button->OnUpdate(core_timing); 436 home_button->OnUpdate(core_timing);
400 sleep_button->OnUpdate(core_timing); 437 sleep_button->OnUpdate(core_timing);
diff --git a/src/hid_core/resource_manager.h b/src/hid_core/resource_manager.h
index 128e00125..0bfe09511 100644
--- a/src/hid_core/resource_manager.h
+++ b/src/hid_core/resource_manager.h
@@ -11,6 +11,7 @@ class System;
11} 11}
12 12
13namespace Core::HID { 13namespace Core::HID {
14struct FirmwareVersion;
14struct VibrationDeviceHandle; 15struct VibrationDeviceHandle;
15struct VibrationValue; 16struct VibrationValue;
16struct VibrationDeviceInfo; 17struct VibrationDeviceInfo;
@@ -21,8 +22,9 @@ struct EventType;
21} 22}
22 23
23namespace Kernel { 24namespace Kernel {
25class KEvent;
24class KSharedMemory; 26class KSharedMemory;
25} 27} // namespace Kernel
26 28
27namespace Service::HID { 29namespace Service::HID {
28class AppletResource; 30class AppletResource;
@@ -33,6 +35,7 @@ class DebugMouse;
33class DebugPad; 35class DebugPad;
34class Digitizer; 36class Digitizer;
35class Gesture; 37class Gesture;
38class HidFirmwareSettings;
36class HomeButton; 39class HomeButton;
37class Keyboard; 40class Keyboard;
38class Mouse; 41class Mouse;
@@ -42,6 +45,8 @@ class SevenSixAxis;
42class SixAxis; 45class SixAxis;
43class SleepButton; 46class SleepButton;
44class TouchScreen; 47class TouchScreen;
48class TouchDriver;
49class TouchResource;
45class UniquePad; 50class UniquePad;
46class NpadVibrationBase; 51class NpadVibrationBase;
47class NpadN64VibrationDevice; 52class NpadN64VibrationDevice;
@@ -52,7 +57,7 @@ struct HandheldConfig;
52class ResourceManager { 57class ResourceManager {
53 58
54public: 59public:
55 explicit ResourceManager(Core::System& system_); 60 explicit ResourceManager(Core::System& system_, std::shared_ptr<HidFirmwareSettings> settings);
56 ~ResourceManager(); 61 ~ResourceManager();
57 62
58 void Initialize(); 63 void Initialize();
@@ -102,6 +107,8 @@ public:
102 Result SendVibrationValue(u64 aruid, const Core::HID::VibrationDeviceHandle& handle, 107 Result SendVibrationValue(u64 aruid, const Core::HID::VibrationDeviceHandle& handle,
103 const Core::HID::VibrationValue& value); 108 const Core::HID::VibrationValue& value);
104 109
110 Result GetTouchScreenFirmwareVersion(Core::HID::FirmwareVersion& firmware) const;
111
105 void UpdateControllers(std::chrono::nanoseconds ns_late); 112 void UpdateControllers(std::chrono::nanoseconds ns_late);
106 void UpdateNpad(std::chrono::nanoseconds ns_late); 113 void UpdateNpad(std::chrono::nanoseconds ns_late);
107 void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late); 114 void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late);
@@ -109,6 +116,7 @@ public:
109 116
110private: 117private:
111 Result CreateAppletResourceImpl(u64 aruid); 118 Result CreateAppletResourceImpl(u64 aruid);
119 void InitializeHandheldConfig();
112 void InitializeHidCommonSampler(); 120 void InitializeHidCommonSampler();
113 void InitializeTouchScreenSampler(); 121 void InitializeTouchScreenSampler();
114 void InitializeConsoleSixAxisSampler(); 122 void InitializeConsoleSixAxisSampler();
@@ -117,37 +125,46 @@ private:
117 bool is_initialized{false}; 125 bool is_initialized{false};
118 126
119 mutable std::recursive_mutex shared_mutex; 127 mutable std::recursive_mutex shared_mutex;
120 std::shared_ptr<AppletResource> applet_resource = nullptr; 128 std::shared_ptr<AppletResource> applet_resource{nullptr};
121 129
122 std::shared_ptr<CaptureButton> capture_button = nullptr; 130 mutable std::mutex input_mutex;
123 std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr; 131 Kernel::KEvent* input_event{nullptr};
124 std::shared_ptr<DebugMouse> debug_mouse = nullptr; 132
125 std::shared_ptr<DebugPad> debug_pad = nullptr; 133 std::shared_ptr<HandheldConfig> handheld_config{nullptr};
126 std::shared_ptr<Digitizer> digitizer = nullptr; 134 std::shared_ptr<HidFirmwareSettings> firmware_settings{nullptr};
127 std::shared_ptr<Gesture> gesture = nullptr; 135
128 std::shared_ptr<HomeButton> home_button = nullptr; 136 std::shared_ptr<CaptureButton> capture_button{nullptr};
129 std::shared_ptr<Keyboard> keyboard = nullptr; 137 std::shared_ptr<ConsoleSixAxis> console_six_axis{nullptr};
130 std::shared_ptr<Mouse> mouse = nullptr; 138 std::shared_ptr<DebugMouse> debug_mouse{nullptr};
131 std::shared_ptr<NPad> npad = nullptr; 139 std::shared_ptr<DebugPad> debug_pad{nullptr};
132 std::shared_ptr<Palma> palma = nullptr; 140 std::shared_ptr<Digitizer> digitizer{nullptr};
133 std::shared_ptr<SevenSixAxis> seven_six_axis = nullptr; 141 std::shared_ptr<HomeButton> home_button{nullptr};
134 std::shared_ptr<SixAxis> six_axis = nullptr; 142 std::shared_ptr<Keyboard> keyboard{nullptr};
135 std::shared_ptr<SleepButton> sleep_button = nullptr; 143 std::shared_ptr<Mouse> mouse{nullptr};
136 std::shared_ptr<TouchScreen> touch_screen = nullptr; 144 std::shared_ptr<NPad> npad{nullptr};
137 std::shared_ptr<UniquePad> unique_pad = nullptr; 145 std::shared_ptr<Palma> palma{nullptr};
138 146 std::shared_ptr<SevenSixAxis> seven_six_axis{nullptr};
139 std::shared_ptr<HandheldConfig> handheld_config = nullptr; 147 std::shared_ptr<SixAxis> six_axis{nullptr};
148 std::shared_ptr<SleepButton> sleep_button{nullptr};
149 std::shared_ptr<UniquePad> unique_pad{nullptr};
140 150
141 // TODO: Create these resources 151 // TODO: Create these resources
142 // std::shared_ptr<AudioControl> audio_control = nullptr; 152 // std::shared_ptr<AudioControl> audio_control{nullptr};
143 // std::shared_ptr<ButtonConfig> button_config = nullptr; 153 // std::shared_ptr<ButtonConfig> button_config{nullptr};
144 // std::shared_ptr<Config> config = nullptr; 154 // std::shared_ptr<Config> config{nullptr};
145 // std::shared_ptr<Connection> connection = nullptr; 155 // std::shared_ptr<Connection> connection{nullptr};
146 // std::shared_ptr<CustomConfig> custom_config = nullptr; 156 // std::shared_ptr<CustomConfig> custom_config{nullptr};
147 // std::shared_ptr<Digitizer> digitizer = nullptr; 157 // std::shared_ptr<Digitizer> digitizer{nullptr};
148 // std::shared_ptr<Hdls> hdls = nullptr; 158 // std::shared_ptr<Hdls> hdls{nullptr};
149 // std::shared_ptr<PlayReport> play_report = nullptr; 159 // std::shared_ptr<PlayReport> play_report{nullptr};
150 // std::shared_ptr<Rail> rail = nullptr; 160 // std::shared_ptr<Rail> rail{nullptr};
161
162 // Touch Resources
163 std::shared_ptr<Gesture> gesture{nullptr};
164 std::shared_ptr<TouchScreen> touch_screen{nullptr};
165 std::shared_ptr<TouchResource> touch_resource{nullptr};
166 std::shared_ptr<TouchDriver> touch_driver{nullptr};
167 std::shared_ptr<Core::Timing::EventType> touch_update_event{nullptr};
151 168
152 Core::System& system; 169 Core::System& system;
153 KernelHelpers::ServiceContext service_context; 170 KernelHelpers::ServiceContext service_context;
@@ -162,12 +179,12 @@ public:
162private: 179private:
163 void GetSharedMemoryHandle(HLERequestContext& ctx); 180 void GetSharedMemoryHandle(HLERequestContext& ctx);
164 181
165 std::shared_ptr<Core::Timing::EventType> npad_update_event; 182 std::shared_ptr<Core::Timing::EventType> npad_update_event{nullptr};
166 std::shared_ptr<Core::Timing::EventType> default_update_event; 183 std::shared_ptr<Core::Timing::EventType> default_update_event{nullptr};
167 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; 184 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event{nullptr};
168 std::shared_ptr<Core::Timing::EventType> motion_update_event; 185 std::shared_ptr<Core::Timing::EventType> motion_update_event{nullptr};
169 186
170 u64 aruid; 187 u64 aruid{};
171 std::shared_ptr<ResourceManager> resource_manager; 188 std::shared_ptr<ResourceManager> resource_manager;
172}; 189};
173 190
diff --git a/src/hid_core/resources/applet_resource.cpp b/src/hid_core/resources/applet_resource.cpp
index db4134037..243beb1c7 100644
--- a/src/hid_core/resources/applet_resource.cpp
+++ b/src/hid_core/resources/applet_resource.cpp
@@ -118,6 +118,12 @@ void AppletResource::UnregisterAppletResourceUserId(u64 aruid) {
118 data[index].aruid = 0; 118 data[index].aruid = 0;
119 119
120 registration_list.flag[index] = RegistrationStatus::PendingDelete; 120 registration_list.flag[index] = RegistrationStatus::PendingDelete;
121
122 for (std::size_t i = 0; i < AruidIndexMax; i++) {
123 if (registration_list.flag[i] == RegistrationStatus::Initialized) {
124 active_aruid = registration_list.aruid[i];
125 }
126 }
121} 127}
122 128
123void AppletResource::FreeAppletResourceId(u64 aruid) { 129void AppletResource::FreeAppletResourceId(u64 aruid) {
diff --git a/src/hid_core/resources/applet_resource.h b/src/hid_core/resources/applet_resource.h
index e9710d306..4a5416fb2 100644
--- a/src/hid_core/resources/applet_resource.h
+++ b/src/hid_core/resources/applet_resource.h
@@ -13,11 +13,12 @@
13 13
14namespace Core { 14namespace Core {
15class System; 15class System;
16} 16} // namespace Core
17 17
18namespace Kernel { 18namespace Kernel {
19class KEvent;
19class KSharedMemory; 20class KSharedMemory;
20} 21} // namespace Kernel
21 22
22namespace Service::HID { 23namespace Service::HID {
23struct SharedMemoryFormat; 24struct SharedMemoryFormat;
@@ -73,7 +74,8 @@ struct AppletResourceHolder {
73 std::recursive_mutex* shared_mutex{nullptr}; 74 std::recursive_mutex* shared_mutex{nullptr};
74 NPadResource* shared_npad_resource{nullptr}; 75 NPadResource* shared_npad_resource{nullptr};
75 std::shared_ptr<HandheldConfig> handheld_config{nullptr}; 76 std::shared_ptr<HandheldConfig> handheld_config{nullptr};
76 long* handle_1; 77 Kernel::KEvent* input_event{nullptr};
78 std::mutex* input_mutex{nullptr};
77}; 79};
78 80
79class AppletResource { 81class AppletResource {
diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp
index 2823be348..1a58eff4a 100644
--- a/src/hid_core/resources/npad/npad.cpp
+++ b/src/hid_core/resources/npad/npad.cpp
@@ -1070,11 +1070,14 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) {
1070void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource, 1070void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource,
1071 std::recursive_mutex* shared_mutex, 1071 std::recursive_mutex* shared_mutex,
1072 std::shared_ptr<HandheldConfig> handheld_config, 1072 std::shared_ptr<HandheldConfig> handheld_config,
1073 Kernel::KEvent* input_event, std::mutex* input_mutex,
1073 std::shared_ptr<Service::Set::ISystemSettingsServer> settings) { 1074 std::shared_ptr<Service::Set::ISystemSettingsServer> settings) {
1074 applet_resource_holder.applet_resource = resource; 1075 applet_resource_holder.applet_resource = resource;
1075 applet_resource_holder.shared_mutex = shared_mutex; 1076 applet_resource_holder.shared_mutex = shared_mutex;
1076 applet_resource_holder.shared_npad_resource = &npad_resource; 1077 applet_resource_holder.shared_npad_resource = &npad_resource;
1077 applet_resource_holder.handheld_config = handheld_config; 1078 applet_resource_holder.handheld_config = handheld_config;
1079 applet_resource_holder.input_event = input_event;
1080 applet_resource_holder.input_mutex = input_mutex;
1078 1081
1079 vibration_handler.SetSettingsService(settings); 1082 vibration_handler.SetSettingsService(settings);
1080 1083
diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h
index 3b1a69e7f..4e26ed2e8 100644
--- a/src/hid_core/resources/npad/npad.h
+++ b/src/hid_core/resources/npad/npad.h
@@ -131,6 +131,7 @@ public:
131 void SetNpadExternals(std::shared_ptr<AppletResource> resource, 131 void SetNpadExternals(std::shared_ptr<AppletResource> resource,
132 std::recursive_mutex* shared_mutex, 132 std::recursive_mutex* shared_mutex,
133 std::shared_ptr<HandheldConfig> handheld_config, 133 std::shared_ptr<HandheldConfig> handheld_config,
134 Kernel::KEvent* input_event, std::mutex* input_mutex,
134 std::shared_ptr<Service::Set::ISystemSettingsServer> settings); 135 std::shared_ptr<Service::Set::ISystemSettingsServer> settings);
135 136
136 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); 137 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
@@ -204,9 +205,6 @@ private:
204 std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads; 205 std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads;
205 NpadVibration vibration_handler{}; 206 NpadVibration vibration_handler{};
206 207
207 Kernel::KEvent* input_event{nullptr};
208 std::mutex* input_mutex{nullptr};
209
210 std::atomic<u64> press_state{}; 208 std::atomic<u64> press_state{};
211 std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax> 209 std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax>
212 controller_data{}; 210 controller_data{};
diff --git a/src/hid_core/resources/npad/npad_resource.cpp b/src/hid_core/resources/npad/npad_resource.cpp
index ea9fc14ed..8dd86b58e 100644
--- a/src/hid_core/resources/npad/npad_resource.cpp
+++ b/src/hid_core/resources/npad/npad_resource.cpp
@@ -72,6 +72,12 @@ void NPadResource::UnregisterAppletResourceUserId(u64 aruid) {
72 state[aruid_index] = {}; 72 state[aruid_index] = {};
73 registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete; 73 registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete;
74 } 74 }
75
76 for (std::size_t i = 0; i < AruidIndexMax; i++) {
77 if (registration_list.flag[i] == RegistrationStatus::Initialized) {
78 active_data_aruid = registration_list.aruid[i];
79 }
80 }
75} 81}
76 82
77void NPadResource::DestroyStyleSetUpdateEvents(u64 aruid) { 83void NPadResource::DestroyStyleSetUpdateEvents(u64 aruid) {
diff --git a/src/hid_core/resources/touch_screen/gesture.cpp b/src/hid_core/resources/touch_screen/gesture.cpp
index 0ecc0941f..eaa0cc7d0 100644
--- a/src/hid_core/resources/touch_screen/gesture.cpp
+++ b/src/hid_core/resources/touch_screen/gesture.cpp
@@ -1,366 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include "common/math_util.h"
5#include "common/settings.h"
6#include "core/frontend/emu_window.h"
7#include "hid_core/frontend/emulated_console.h"
8#include "hid_core/hid_core.h"
9#include "hid_core/resources/applet_resource.h"
10#include "hid_core/resources/shared_memory_format.h"
11#include "hid_core/resources/touch_screen/gesture.h" 4#include "hid_core/resources/touch_screen/gesture.h"
5#include "hid_core/resources/touch_screen/touch_screen_resource.h"
12 6
13namespace Service::HID { 7namespace Service::HID {
14// HW is around 700, value is set to 400 to make it easier to trigger with mouse
15constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
16constexpr f32 angle_threshold = 0.015f; // Threshold in radians
17constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels
18constexpr f32 press_delay = 0.5f; // Time in seconds
19constexpr f32 double_tap_delay = 0.35f; // Time in seconds
20 8
21constexpr f32 Square(s32 num) { 9Gesture::Gesture(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {}
22 return static_cast<f32>(num * num);
23}
24 10
25Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
26 console = hid_core.GetEmulatedConsole();
27}
28Gesture::~Gesture() = default; 11Gesture::~Gesture() = default;
29 12
30void Gesture::OnInit() { 13Result Gesture::Activate() {
31 std::scoped_lock shared_lock{*shared_mutex}; 14 std::scoped_lock lock{mutex};
32 const u64 aruid = applet_resource->GetActiveAruid();
33 auto* data = applet_resource->GetAruidData(aruid);
34
35 if (data == nullptr || !data->flag.is_assigned) {
36 return;
37 }
38
39 shared_memory = &data->shared_memory_format->gesture;
40 shared_memory->gesture_lifo.buffer_count = 0;
41 shared_memory->gesture_lifo.buffer_tail = 0;
42 force_update = true;
43}
44
45void Gesture::OnRelease() {}
46
47void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
48 std::scoped_lock shared_lock{*shared_mutex};
49 const u64 aruid = applet_resource->GetActiveAruid();
50 auto* data = applet_resource->GetAruidData(aruid);
51
52 if (data == nullptr || !data->flag.is_assigned) {
53 return;
54 }
55
56 shared_memory = &data->shared_memory_format->gesture;
57
58 if (!IsControllerActivated()) {
59 shared_memory->gesture_lifo.buffer_count = 0;
60 shared_memory->gesture_lifo.buffer_tail = 0;
61 return;
62 }
63
64 ReadTouchInput();
65
66 GestureProperties gesture = GetGestureProperties();
67 f32 time_difference =
68 static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
69 (1000 * 1000 * 1000);
70
71 // Only update if necessary
72 if (!ShouldUpdateGesture(gesture, time_difference)) {
73 return;
74 }
75
76 last_update_timestamp = shared_memory->gesture_lifo.timestamp;
77 UpdateGestureSharedMemory(gesture, time_difference);
78}
79
80void Gesture::ReadTouchInput() {
81 if (!Settings::values.touchscreen.enabled) {
82 fingers = {};
83 return;
84 }
85
86 const auto touch_status = console->GetTouch();
87 for (std::size_t id = 0; id < fingers.size(); ++id) {
88 fingers[id] = touch_status[id];
89 }
90}
91
92bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) {
93 const auto& last_entry = GetLastGestureEntry();
94 if (force_update) {
95 force_update = false;
96 return true;
97 }
98
99 // Update if coordinates change
100 for (size_t id = 0; id < MAX_POINTS; id++) {
101 if (gesture.points[id] != last_gesture.points[id]) {
102 return true;
103 }
104 }
105
106 // Update on press and hold event after 0.5 seconds
107 if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 &&
108 time_difference > press_delay) {
109 return enable_press_and_tap;
110 }
111
112 return false;
113}
114
115void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) {
116 GestureType type = GestureType::Idle;
117 GestureAttribute attributes{};
118
119 const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
120
121 // Reset next state to default
122 next_state.sampling_number = last_entry.sampling_number + 1;
123 next_state.delta = {};
124 next_state.vel_x = 0;
125 next_state.vel_y = 0;
126 next_state.direction = GestureDirection::None;
127 next_state.rotation_angle = 0;
128 next_state.scale = 0;
129
130 if (gesture.active_points > 0) {
131 if (last_gesture.active_points == 0) {
132 NewGesture(gesture, type, attributes);
133 } else {
134 UpdateExistingGesture(gesture, type, time_difference);
135 }
136 } else {
137 EndGesture(gesture, last_gesture, type, attributes, time_difference);
138 }
139
140 // Apply attributes
141 next_state.detection_count = gesture.detection_count;
142 next_state.type = type;
143 next_state.attributes = attributes;
144 next_state.pos = gesture.mid_point;
145 next_state.point_count = static_cast<s32>(gesture.active_points);
146 next_state.points = gesture.points;
147 last_gesture = gesture;
148
149 shared_memory->gesture_lifo.WriteNextEntry(next_state);
150}
151
152void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
153 GestureAttribute& attributes) {
154 const auto& last_entry = GetLastGestureEntry();
155
156 gesture.detection_count++;
157 type = GestureType::Touch;
158
159 // New touch after cancel is not considered new
160 if (last_entry.type != GestureType::Cancel) {
161 attributes.is_new_touch.Assign(1);
162 enable_press_and_tap = true;
163 }
164}
165
166void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type,
167 f32 time_difference) {
168 const auto& last_entry = GetLastGestureEntry();
169 15
170 // Promote to pan type if touch moved 16 // TODO: Result result = CreateThread();
171 for (size_t id = 0; id < MAX_POINTS; id++) { 17 Result result = ResultSuccess;
172 if (gesture.points[id] != last_gesture.points[id]) { 18 if (result.IsError()) {
173 type = GestureType::Pan; 19 return result;
174 break;
175 }
176 } 20 }
177 21
178 // Number of fingers changed cancel the last event and clear data 22 result = touch_resource->ActivateGesture();
179 if (gesture.active_points != last_gesture.active_points) {
180 type = GestureType::Cancel;
181 enable_press_and_tap = false;
182 gesture.active_points = 0;
183 gesture.mid_point = {};
184 gesture.points.fill({});
185 return;
186 }
187
188 // Calculate extra parameters of panning
189 if (type == GestureType::Pan) {
190 UpdatePanEvent(gesture, last_gesture, type, time_difference);
191 return;
192 }
193 23
194 // Promote to press type 24 if (result.IsError()) {
195 if (last_entry.type == GestureType::Touch) { 25 // TODO: StopThread();
196 type = GestureType::Press;
197 } 26 }
198}
199
200void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
201 GestureType& type, GestureAttribute& attributes, f32 time_difference) {
202 const auto& last_entry = GetLastGestureEntry();
203 27
204 if (last_gesture_props.active_points != 0) { 28 return result;
205 switch (last_entry.type) {
206 case GestureType::Touch:
207 if (enable_press_and_tap) {
208 SetTapEvent(gesture, last_gesture_props, type, attributes);
209 return;
210 }
211 type = GestureType::Cancel;
212 force_update = true;
213 break;
214 case GestureType::Press:
215 case GestureType::Tap:
216 case GestureType::Swipe:
217 case GestureType::Pinch:
218 case GestureType::Rotate:
219 type = GestureType::Complete;
220 force_update = true;
221 break;
222 case GestureType::Pan:
223 EndPanEvent(gesture, last_gesture_props, type, time_difference);
224 break;
225 default:
226 break;
227 }
228 return;
229 }
230 if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) {
231 gesture.detection_count++;
232 }
233} 29}
234 30
235void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 31Result Gesture::Activate(u64 aruid, u32 basic_gesture_id) {
236 GestureType& type, GestureAttribute& attributes) { 32 std::scoped_lock lock{mutex};
237 type = GestureType::Tap; 33 return touch_resource->ActivateGesture(aruid, basic_gesture_id);
238 gesture = last_gesture_props;
239 force_update = true;
240 f32 tap_time_difference =
241 static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
242 last_tap_timestamp = last_update_timestamp;
243 if (tap_time_difference < double_tap_delay) {
244 attributes.is_double_tap.Assign(1);
245 }
246} 34}
247 35
248void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 36Result Gesture::Deactivate() {
249 GestureType& type, f32 time_difference) { 37 std::scoped_lock lock{mutex};
250 const auto& last_entry = GetLastGestureEntry(); 38 const auto result = touch_resource->DeactivateGesture();
251 39
252 next_state.delta = gesture.mid_point - last_entry.pos; 40 if (result.IsError()) {
253 next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference; 41 return result;
254 next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
255 last_pan_time_difference = time_difference;
256
257 // Promote to pinch type
258 if (std::abs(gesture.average_distance - last_gesture_props.average_distance) >
259 pinch_threshold) {
260 type = GestureType::Pinch;
261 next_state.scale = gesture.average_distance / last_gesture_props.average_distance;
262 } 42 }
263 43
264 const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / 44 // TODO: return StopThread();
265 (1 + (gesture.angle * last_gesture_props.angle))); 45 return ResultSuccess;
266 // Promote to rotate type
267 if (std::abs(angle_between_two_lines) > angle_threshold) {
268 type = GestureType::Rotate;
269 next_state.scale = 0;
270 next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
271 }
272} 46}
273 47
274void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, 48Result Gesture::IsActive(bool& out_is_active) const {
275 GestureType& type, f32 time_difference) { 49 out_is_active = touch_resource->IsGestureActive();
276 const auto& last_entry = GetLastGestureEntry(); 50 return ResultSuccess;
277 next_state.vel_x =
278 static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference);
279 next_state.vel_y =
280 static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference);
281 const f32 curr_vel =
282 std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
283
284 // Set swipe event with parameters
285 if (curr_vel > swipe_threshold) {
286 SetSwipeEvent(gesture, last_gesture_props, type);
287 return;
288 }
289
290 // End panning without swipe
291 type = GestureType::Complete;
292 next_state.vel_x = 0;
293 next_state.vel_y = 0;
294 force_update = true;
295}
296
297void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
298 GestureType& type) {
299 const auto& last_entry = GetLastGestureEntry();
300
301 type = GestureType::Swipe;
302 gesture = last_gesture_props;
303 force_update = true;
304 next_state.delta = last_entry.delta;
305
306 if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
307 if (next_state.delta.x > 0) {
308 next_state.direction = GestureDirection::Right;
309 return;
310 }
311 next_state.direction = GestureDirection::Left;
312 return;
313 }
314 if (next_state.delta.y > 0) {
315 next_state.direction = GestureDirection::Down;
316 return;
317 }
318 next_state.direction = GestureDirection::Up;
319}
320
321const GestureState& Gesture::GetLastGestureEntry() const {
322 return shared_memory->gesture_lifo.ReadCurrentEntry().state;
323}
324
325GestureProperties Gesture::GetGestureProperties() {
326 GestureProperties gesture;
327 std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
328 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
329 [](const auto& finger) { return finger.pressed; });
330 gesture.active_points =
331 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
332
333 for (size_t id = 0; id < gesture.active_points; ++id) {
334 const auto& [active_x, active_y] = active_fingers[id].position;
335 gesture.points[id] = {
336 .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width),
337 .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height),
338 };
339
340 // Hack: There is no touch in docked but games still allow it
341 if (Settings::IsDockedMode()) {
342 gesture.points[id] = {
343 .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
344 .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
345 };
346 }
347
348 gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
349 gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
350 }
351
352 for (size_t id = 0; id < gesture.active_points; ++id) {
353 const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
354 Square(gesture.mid_point.y - gesture.points[id].y));
355 gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
356 }
357
358 gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
359 static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
360
361 gesture.detection_count = last_gesture.detection_count;
362
363 return gesture;
364} 51}
365 52
366} // namespace Service::HID 53} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/gesture.h b/src/hid_core/resources/touch_screen/gesture.h
index 32e9a8690..d92912bb6 100644
--- a/src/hid_core/resources/touch_screen/gesture.h
+++ b/src/hid_core/resources/touch_screen/gesture.h
@@ -1,87 +1,32 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <mutex>
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "hid_core/resources/controller_base.h" 9#include "core/hle/result.h"
10#include "hid_core/resources/touch_screen/touch_types.h"
11
12namespace Core::HID {
13class EmulatedConsole;
14}
15 10
16namespace Service::HID { 11namespace Service::HID {
17struct GestureSharedMemoryFormat; 12class TouchResource;
18 13
19class Gesture final : public ControllerBase { 14/// Handles gesture request from HID interfaces
15class Gesture {
20public: 16public:
21 explicit Gesture(Core::HID::HIDCore& hid_core_); 17 Gesture(std::shared_ptr<TouchResource> resource);
22 ~Gesture() override; 18 ~Gesture();
23 19
24 // Called when the controller is initialized 20 Result Activate();
25 void OnInit() override; 21 Result Activate(u64 aruid, u32 basic_gesture_id);
26 22
27 // When the controller is released 23 Result Deactivate();
28 void OnRelease() override;
29 24
30 // When the controller is requesting an update for the shared memory 25 Result IsActive(bool& out_is_active) const;
31 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
32 26
33private: 27private:
34 // Reads input from all available input engines 28 mutable std::mutex mutex;
35 void ReadTouchInput(); 29 std::shared_ptr<TouchResource> touch_resource;
36
37 // Returns true if gesture state needs to be updated
38 bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
39
40 // Updates the shared memory to the next state
41 void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
42
43 // Initializes new gesture
44 void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
45
46 // Updates existing gesture state
47 void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference);
48
49 // Terminates exiting gesture
50 void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props,
51 GestureType& type, GestureAttribute& attributes, f32 time_difference);
52
53 // Set current event to a tap event
54 void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
55 GestureType& type, GestureAttribute& attributes);
56
57 // Calculates and set the extra parameters related to a pan event
58 void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
59 GestureType& type, f32 time_difference);
60
61 // Terminates the pan event
62 void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
63 GestureType& type, f32 time_difference);
64
65 // Set current event to a swipe event
66 void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props,
67 GestureType& type);
68
69 // Retrieves the last gesture entry, as indicated by shared memory indices.
70 [[nodiscard]] const GestureState& GetLastGestureEntry() const;
71
72 // Returns the average distance, angle and middle point of the active fingers
73 GestureProperties GetGestureProperties();
74
75 GestureState next_state{};
76 GestureSharedMemoryFormat* shared_memory;
77 Core::HID::EmulatedConsole* console = nullptr;
78
79 std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
80 GestureProperties last_gesture{};
81 s64 last_update_timestamp{};
82 s64 last_tap_timestamp{};
83 f32 last_pan_time_difference{};
84 bool force_update{false};
85 bool enable_press_and_tap{false};
86}; 30};
31
87} // namespace Service::HID 32} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/gesture_handler.cpp b/src/hid_core/resources/touch_screen/gesture_handler.cpp
new file mode 100644
index 000000000..4fcaf6ecf
--- /dev/null
+++ b/src/hid_core/resources/touch_screen/gesture_handler.cpp
@@ -0,0 +1,260 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/math_util.h"
5#include "hid_core/resources/touch_screen/gesture_handler.h"
6
7namespace Service::HID {
8
9constexpr f32 Square(s32 num) {
10 return static_cast<f32>(num * num);
11}
12
13GestureHandler::GestureHandler() {}
14
15GestureHandler::~GestureHandler() {}
16
17void GestureHandler::SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp) {
18 gesture = {};
19 gesture.active_points = std::min(MaxPoints, static_cast<std::size_t>(count));
20
21 for (size_t id = 0; id < gesture.active_points; ++id) {
22 const auto& [active_x, active_y] = touch_state[id].position;
23 gesture.points[id] = {
24 .x = static_cast<s32>(active_x),
25 .y = static_cast<s32>(active_y),
26 };
27
28 gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points);
29 gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points);
30 }
31
32 for (size_t id = 0; id < gesture.active_points; ++id) {
33 const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) +
34 Square(gesture.mid_point.y - gesture.points[id].y));
35 gesture.average_distance += distance / static_cast<f32>(gesture.active_points);
36 }
37
38 gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y),
39 static_cast<f32>(gesture.mid_point.x - gesture.points[0].x));
40
41 gesture.detection_count = last_gesture.detection_count;
42
43 if (last_update_timestamp > timestamp) {
44 timestamp = last_tap_timestamp;
45 }
46
47 time_difference = static_cast<f32>(timestamp - last_update_timestamp) / (1000 * 1000 * 1000);
48}
49
50bool GestureHandler::NeedsUpdate() {
51 if (force_update) {
52 force_update = false;
53 return true;
54 }
55
56 // Update if coordinates change
57 for (size_t id = 0; id < MaxPoints; id++) {
58 if (gesture.points[id] != last_gesture.points[id]) {
59 return true;
60 }
61 }
62
63 // Update on press and hold event after 0.5 seconds
64 if (last_gesture_state.type == GestureType::Touch && last_gesture_state.point_count == 1 &&
65 time_difference > PressDelay) {
66 return enable_press_and_tap;
67 }
68
69 return false;
70}
71
72void GestureHandler::UpdateGestureState(GestureState& next_state, s64 timestamp) {
73 last_update_timestamp = timestamp;
74
75 GestureType type = GestureType::Idle;
76 GestureAttribute attributes{};
77
78 // Reset next state to default
79 next_state.sampling_number = last_gesture_state.sampling_number + 1;
80 next_state.delta = {};
81 next_state.vel_x = 0;
82 next_state.vel_y = 0;
83 next_state.direction = GestureDirection::None;
84 next_state.rotation_angle = 0;
85 next_state.scale = 0;
86
87 if (gesture.active_points > 0) {
88 if (last_gesture.active_points == 0) {
89 NewGesture(type, attributes);
90 } else {
91 UpdateExistingGesture(next_state, type);
92 }
93 } else {
94 EndGesture(next_state, type, attributes);
95 }
96
97 // Apply attributes
98 next_state.detection_count = gesture.detection_count;
99 next_state.type = type;
100 next_state.attributes = attributes;
101 next_state.pos = gesture.mid_point;
102 next_state.point_count = static_cast<s32>(gesture.active_points);
103 next_state.points = gesture.points;
104 last_gesture = gesture;
105 last_gesture_state = next_state;
106}
107
108void GestureHandler::NewGesture(GestureType& type, GestureAttribute& attributes) {
109 gesture.detection_count++;
110 type = GestureType::Touch;
111
112 // New touch after cancel is not considered new
113 if (last_gesture_state.type != GestureType::Cancel) {
114 attributes.is_new_touch.Assign(1);
115 enable_press_and_tap = true;
116 }
117}
118
119void GestureHandler::UpdateExistingGesture(GestureState& next_state, GestureType& type) {
120 // Promote to pan type if touch moved
121 for (size_t id = 0; id < MaxPoints; id++) {
122 if (gesture.points[id] != last_gesture.points[id]) {
123 type = GestureType::Pan;
124 break;
125 }
126 }
127
128 // Number of fingers changed cancel the last event and clear data
129 if (gesture.active_points != last_gesture.active_points) {
130 type = GestureType::Cancel;
131 enable_press_and_tap = false;
132 gesture.active_points = 0;
133 gesture.mid_point = {};
134 gesture.points.fill({});
135 return;
136 }
137
138 // Calculate extra parameters of panning
139 if (type == GestureType::Pan) {
140 UpdatePanEvent(next_state, type);
141 return;
142 }
143
144 // Promote to press type
145 if (last_gesture_state.type == GestureType::Touch) {
146 type = GestureType::Press;
147 }
148}
149
150void GestureHandler::EndGesture(GestureState& next_state, GestureType& type,
151 GestureAttribute& attributes) {
152 if (last_gesture.active_points != 0) {
153 switch (last_gesture_state.type) {
154 case GestureType::Touch:
155 if (enable_press_and_tap) {
156 SetTapEvent(type, attributes);
157 return;
158 }
159 type = GestureType::Cancel;
160 force_update = true;
161 break;
162 case GestureType::Press:
163 case GestureType::Tap:
164 case GestureType::Swipe:
165 case GestureType::Pinch:
166 case GestureType::Rotate:
167 type = GestureType::Complete;
168 force_update = true;
169 break;
170 case GestureType::Pan:
171 EndPanEvent(next_state, type);
172 break;
173 default:
174 break;
175 }
176 return;
177 }
178 if (last_gesture_state.type == GestureType::Complete ||
179 last_gesture_state.type == GestureType::Cancel) {
180 gesture.detection_count++;
181 }
182}
183
184void GestureHandler::SetTapEvent(GestureType& type, GestureAttribute& attributes) {
185 type = GestureType::Tap;
186 gesture = last_gesture;
187 force_update = true;
188 f32 tap_time_difference =
189 static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000);
190 last_tap_timestamp = last_update_timestamp;
191 if (tap_time_difference < DoubleTapDelay) {
192 attributes.is_double_tap.Assign(1);
193 }
194}
195
196void GestureHandler::UpdatePanEvent(GestureState& next_state, GestureType& type) {
197 next_state.delta = gesture.mid_point - last_gesture_state.pos;
198 next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference;
199 next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference;
200 last_pan_time_difference = time_difference;
201
202 // Promote to pinch type
203 if (std::abs(gesture.average_distance - last_gesture.average_distance) > PinchThreshold) {
204 type = GestureType::Pinch;
205 next_state.scale = gesture.average_distance / last_gesture.average_distance;
206 }
207
208 const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture.angle) /
209 (1 + (gesture.angle * last_gesture.angle)));
210 // Promote to rotate type
211 if (std::abs(angle_between_two_lines) > AngleThreshold) {
212 type = GestureType::Rotate;
213 next_state.scale = 0;
214 next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI;
215 }
216}
217
218void GestureHandler::EndPanEvent(GestureState& next_state, GestureType& type) {
219 next_state.vel_x =
220 static_cast<f32>(last_gesture_state.delta.x) / (last_pan_time_difference + time_difference);
221 next_state.vel_y =
222 static_cast<f32>(last_gesture_state.delta.y) / (last_pan_time_difference + time_difference);
223 const f32 curr_vel =
224 std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y));
225
226 // Set swipe event with parameters
227 if (curr_vel > SwipeThreshold) {
228 SetSwipeEvent(next_state, type);
229 return;
230 }
231
232 // End panning without swipe
233 type = GestureType::Complete;
234 next_state.vel_x = 0;
235 next_state.vel_y = 0;
236 force_update = true;
237}
238
239void GestureHandler::SetSwipeEvent(GestureState& next_state, GestureType& type) {
240 type = GestureType::Swipe;
241 gesture = last_gesture;
242 force_update = true;
243 next_state.delta = last_gesture_state.delta;
244
245 if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) {
246 if (next_state.delta.x > 0) {
247 next_state.direction = GestureDirection::Right;
248 return;
249 }
250 next_state.direction = GestureDirection::Left;
251 return;
252 }
253 if (next_state.delta.y > 0) {
254 next_state.direction = GestureDirection::Down;
255 return;
256 }
257 next_state.direction = GestureDirection::Up;
258}
259
260} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/gesture_handler.h b/src/hid_core/resources/touch_screen/gesture_handler.h
new file mode 100644
index 000000000..fda2040c9
--- /dev/null
+++ b/src/hid_core/resources/touch_screen/gesture_handler.h
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <span>
7
8#include "hid_core/resources/touch_screen/touch_types.h"
9
10namespace Service::HID {
11
12class GestureHandler {
13public:
14 GestureHandler();
15 ~GestureHandler();
16
17 void SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp);
18
19 bool NeedsUpdate();
20 void UpdateGestureState(GestureState& next_state, s64 timestamp);
21
22private:
23 // Initializes new gesture
24 void NewGesture(GestureType& type, GestureAttribute& attributes);
25
26 // Updates existing gesture state
27 void UpdateExistingGesture(GestureState& next_state, GestureType& type);
28
29 // Terminates exiting gesture
30 void EndGesture(GestureState& next_state, GestureType& type, GestureAttribute& attributes);
31
32 // Set current event to a tap event
33 void SetTapEvent(GestureType& type, GestureAttribute& attributes);
34
35 // Calculates and set the extra parameters related to a pan event
36 void UpdatePanEvent(GestureState& next_state, GestureType& type);
37
38 // Terminates the pan event
39 void EndPanEvent(GestureState& next_state, GestureType& type);
40
41 // Set current event to a swipe event
42 void SetSwipeEvent(GestureState& next_state, GestureType& type);
43
44 GestureProperties gesture{};
45 GestureProperties last_gesture{};
46 GestureState last_gesture_state{};
47 s64 last_update_timestamp{};
48 s64 last_tap_timestamp{};
49 f32 last_pan_time_difference{};
50 f32 time_difference{};
51 bool force_update{true};
52 bool enable_press_and_tap{false};
53};
54
55} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/gesture_types.h b/src/hid_core/resources/touch_screen/gesture_types.h
deleted file mode 100644
index b4f034cd3..000000000
--- a/src/hid_core/resources/touch_screen/gesture_types.h
+++ /dev/null
@@ -1,77 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include "common/bit_field.h"
8#include "common/common_types.h"
9#include "common/point.h"
10
11namespace Service::HID {
12static constexpr size_t MAX_FINGERS = 16;
13static constexpr size_t MAX_POINTS = 4;
14
15// This is nn::hid::GestureType
16enum class GestureType : u32 {
17 Idle, // Nothing touching the screen
18 Complete, // Set at the end of a touch event
19 Cancel, // Set when the number of fingers change
20 Touch, // A finger just touched the screen
21 Press, // Set if last type is touch and the finger hasn't moved
22 Tap, // Fast press then release
23 Pan, // All points moving together across the screen
24 Swipe, // Fast press movement and release of a single point
25 Pinch, // All points moving away/closer to the midpoint
26 Rotate, // All points rotating from the midpoint
27};
28
29// This is nn::hid::GestureDirection
30enum class GestureDirection : u32 {
31 None,
32 Left,
33 Up,
34 Right,
35 Down,
36};
37
38// This is nn::hid::GestureAttribute
39struct GestureAttribute {
40 union {
41 u32 raw{};
42
43 BitField<4, 1, u32> is_new_touch;
44 BitField<8, 1, u32> is_double_tap;
45 };
46};
47static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
48
49// This is nn::hid::GestureState
50struct GestureState {
51 s64 sampling_number{};
52 s64 detection_count{};
53 GestureType type{GestureType::Idle};
54 GestureDirection direction{GestureDirection::None};
55 Common::Point<s32> pos{};
56 Common::Point<s32> delta{};
57 f32 vel_x{};
58 f32 vel_y{};
59 GestureAttribute attributes{};
60 f32 scale{};
61 f32 rotation_angle{};
62 s32 point_count{};
63 std::array<Common::Point<s32>, 4> points{};
64};
65static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
66
67struct GestureProperties {
68 std::array<Common::Point<s32>, MAX_POINTS> points{};
69 std::size_t active_points{};
70 Common::Point<s32> mid_point{};
71 s64 detection_count{};
72 u64 delta_time{};
73 f32 average_distance{};
74 f32 angle{};
75};
76
77} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_screen.cpp b/src/hid_core/resources/touch_screen/touch_screen.cpp
index 48d956c51..35efb1786 100644
--- a/src/hid_core/resources/touch_screen/touch_screen.cpp
+++ b/src/hid_core/resources/touch_screen/touch_screen.cpp
@@ -1,132 +1,119 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#include <algorithm> 4#include "hid_core/hid_types.h"
5#include "common/common_types.h"
6#include "common/settings.h"
7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h"
9#include "hid_core/frontend/emulated_console.h"
10#include "hid_core/hid_core.h"
11#include "hid_core/resources/applet_resource.h"
12#include "hid_core/resources/shared_memory_format.h"
13#include "hid_core/resources/touch_screen/touch_screen.h" 5#include "hid_core/resources/touch_screen/touch_screen.h"
6#include "hid_core/resources/touch_screen/touch_screen_resource.h"
14 7
15namespace Service::HID { 8namespace Service::HID {
16 9
17TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_) 10TouchScreen::TouchScreen(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {}
18 : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width),
19 touchscreen_height(Layout::ScreenUndocked::Height) {
20 console = hid_core.GetEmulatedConsole();
21}
22 11
23TouchScreen::~TouchScreen() = default; 12TouchScreen::~TouchScreen() = default;
24 13
25void TouchScreen::OnInit() {} 14Result TouchScreen::Activate() {
15 std::scoped_lock lock{mutex};
26 16
27void TouchScreen::OnRelease() {} 17 // TODO: Result result = CreateThread();
28 18 Result result = ResultSuccess;
29void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 19 if (result.IsError()) {
30 const u64 aruid = applet_resource->GetActiveAruid(); 20 return result;
31 auto* data = applet_resource->GetAruidData(aruid); 21 }
32 22
33 if (data == nullptr || !data->flag.is_assigned) { 23 result = touch_resource->ActivateTouch();
34 return; 24 if (result.IsError()) {
25 // TODO: StopThread();
35 } 26 }
36 27
37 TouchScreenSharedMemoryFormat& shared_memory = data->shared_memory_format->touch_screen; 28 return result;
38 shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); 29}
39 30
40 if (!IsControllerActivated()) { 31Result TouchScreen::Activate(u64 aruid) {
41 shared_memory.touch_screen_lifo.buffer_count = 0; 32 std::scoped_lock lock{mutex};
42 shared_memory.touch_screen_lifo.buffer_tail = 0; 33 return touch_resource->ActivateTouch(aruid);
43 return; 34}
44 }
45 35
46 const auto touch_status = console->GetTouch(); 36Result TouchScreen::Deactivate() {
47 for (std::size_t id = 0; id < MAX_FINGERS; id++) { 37 std::scoped_lock lock{mutex};
48 const auto& current_touch = touch_status[id]; 38 const auto result = touch_resource->DeactivateTouch();
49 auto& finger = fingers[id];
50 finger.id = current_touch.id;
51
52 if (finger.attribute.start_touch) {
53 finger.attribute.raw = 0;
54 continue;
55 }
56
57 if (finger.attribute.end_touch) {
58 finger.attribute.raw = 0;
59 finger.pressed = false;
60 continue;
61 }
62
63 if (!finger.pressed && current_touch.pressed) {
64 // Ignore all touch fingers if disabled
65 if (!Settings::values.touchscreen.enabled) {
66 continue;
67 }
68
69 finger.attribute.start_touch.Assign(1);
70 finger.pressed = true;
71 finger.position = current_touch.position;
72 continue;
73 }
74
75 if (finger.pressed && !current_touch.pressed) {
76 finger.attribute.raw = 0;
77 finger.attribute.end_touch.Assign(1);
78 continue;
79 }
80
81 // Only update position if touch is not on a special frame
82 finger.position = current_touch.position;
83 }
84 39
85 std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers; 40 if (result.IsError()) {
86 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), 41 return result;
87 [](const auto& finger) { return finger.pressed; });
88 const auto active_fingers_count =
89 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
90
91 const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count());
92 const auto& last_entry = shared_memory.touch_screen_lifo.ReadCurrentEntry().state;
93
94 next_state.sampling_number = last_entry.sampling_number + 1;
95 next_state.entry_count = static_cast<s32>(active_fingers_count);
96
97 for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
98 auto& touch_entry = next_state.states[id];
99 if (id < active_fingers_count) {
100 const auto& [active_x, active_y] = active_fingers[id].position;
101 touch_entry.position = {
102 .x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)),
103 .y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)),
104 };
105 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
106 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
107 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
108 touch_entry.delta_time = timestamp - active_fingers[id].last_touch;
109 fingers[active_fingers[id].id].last_touch = timestamp;
110 touch_entry.finger = active_fingers[id].id;
111 touch_entry.attribute.raw = active_fingers[id].attribute.raw;
112 } else {
113 // Clear touch entry
114 touch_entry.attribute.raw = 0;
115 touch_entry.position = {};
116 touch_entry.diameter_x = 0;
117 touch_entry.diameter_y = 0;
118 touch_entry.rotation_angle = 0;
119 touch_entry.delta_time = 0;
120 touch_entry.finger = 0;
121 }
122 } 42 }
123 43
124 shared_memory.touch_screen_lifo.WriteNextEntry(next_state); 44 // TODO: return StopThread();
45 return ResultSuccess;
46}
47
48Result TouchScreen::IsActive(bool& out_is_active) const {
49 out_is_active = touch_resource->IsTouchActive();
50 return ResultSuccess;
51}
52
53Result TouchScreen::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) {
54 std::scoped_lock lock{mutex};
55 return touch_resource->SetTouchScreenAutoPilotState(auto_pilot_state);
56}
57
58Result TouchScreen::UnsetTouchScreenAutoPilotState() {
59 std::scoped_lock lock{mutex};
60 return touch_resource->UnsetTouchScreenAutoPilotState();
61}
62
63Result TouchScreen::RequestNextTouchInput() {
64 std::scoped_lock lock{mutex};
65 return touch_resource->RequestNextTouchInput();
66}
67
68Result TouchScreen::RequestNextDummyInput() {
69 std::scoped_lock lock{mutex};
70 return touch_resource->RequestNextDummyInput();
71}
72
73Result TouchScreen::ProcessTouchScreenAutoTune() {
74 std::scoped_lock lock{mutex};
75 return touch_resource->ProcessTouchScreenAutoTune();
76}
77
78Result TouchScreen::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x,
79 f32 point2_y) {
80 std::scoped_lock lock{mutex};
81 touch_resource->SetTouchScreenMagnification(point1_x, point1_y, point2_x, point2_y);
82 return ResultSuccess;
83}
84
85Result TouchScreen::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) {
86 std::scoped_lock lock{mutex};
87 return touch_resource->SetTouchScreenResolution(width, height, aruid);
88}
89
90Result TouchScreen::SetTouchScreenConfiguration(
91 const Core::HID::TouchScreenConfigurationForNx& mode, u64 aruid) {
92 std::scoped_lock lock{mutex};
93 return touch_resource->SetTouchScreenConfiguration(mode, aruid);
94}
95
96Result TouchScreen::GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode,
97 u64 aruid) const {
98 std::scoped_lock lock{mutex};
99 return touch_resource->GetTouchScreenConfiguration(out_mode, aruid);
100}
101
102Result TouchScreen::SetTouchScreenDefaultConfiguration(
103 const Core::HID::TouchScreenConfigurationForNx& mode) {
104 std::scoped_lock lock{mutex};
105 return touch_resource->SetTouchScreenDefaultConfiguration(mode);
106}
107
108Result TouchScreen::GetTouchScreenDefaultConfiguration(
109 Core::HID::TouchScreenConfigurationForNx& out_mode) const {
110 std::scoped_lock lock{mutex};
111 return touch_resource->GetTouchScreenDefaultConfiguration(out_mode);
125} 112}
126 113
127void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) { 114void TouchScreen::OnTouchUpdate(u64 timestamp) {
128 touchscreen_width = width; 115 std::scoped_lock lock{mutex};
129 touchscreen_height = height; 116 touch_resource->OnTouchUpdate(timestamp);
130} 117}
131 118
132} // namespace Service::HID 119} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_screen.h b/src/hid_core/resources/touch_screen/touch_screen.h
index 4b3824742..2fcb6247f 100644
--- a/src/hid_core/resources/touch_screen/touch_screen.h
+++ b/src/hid_core/resources/touch_screen/touch_screen.h
@@ -1,43 +1,64 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-3.0-or-later
3 3
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <mutex>
7 7
8#include "hid_core/hid_types.h" 8#include "common/common_types.h"
9#include "hid_core/resources/controller_base.h" 9#include "core/hle/result.h"
10#include "hid_core/resources/touch_screen/touch_types.h"
11 10
12namespace Core::HID { 11namespace Core::HID {
13class EmulatedConsole; 12struct TouchScreenConfigurationForNx;
14} // namespace Core::HID 13}
14
15namespace Core::Timing {
16struct EventType;
17}
15 18
16namespace Service::HID { 19namespace Service::HID {
17struct TouchScreenSharedMemoryFormat; 20class TouchResource;
21struct AutoPilotState;
18 22
19class TouchScreen final : public ControllerBase { 23/// Handles touch request from HID interfaces
24class TouchScreen {
20public: 25public:
21 explicit TouchScreen(Core::HID::HIDCore& hid_core_); 26 TouchScreen(std::shared_ptr<TouchResource> resource);
22 ~TouchScreen() override; 27 ~TouchScreen();
23 28
24 // Called when the controller is initialized 29 Result Activate();
25 void OnInit() override; 30 Result Activate(u64 aruid);
26 31
27 // When the controller is released 32 Result Deactivate();
28 void OnRelease() override;
29 33
30 // When the controller is requesting an update for the shared memory 34 Result IsActive(bool& out_is_active) const;
31 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
32 35
33 void SetTouchscreenDimensions(u32 width, u32 height); 36 Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state);
37 Result UnsetTouchScreenAutoPilotState();
34 38
35private: 39 Result RequestNextTouchInput();
36 TouchScreenState next_state{}; 40 Result RequestNextDummyInput();
37 Core::HID::EmulatedConsole* console = nullptr; 41
42 Result ProcessTouchScreenAutoTune();
43
44 Result SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y);
45 Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid);
46
47 Result SetTouchScreenConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode,
48 u64 aruid);
49 Result GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode,
50 u64 aruid) const;
38 51
39 std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; 52 Result SetTouchScreenDefaultConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode);
40 u32 touchscreen_width; 53 Result GetTouchScreenDefaultConfiguration(
41 u32 touchscreen_height; 54 Core::HID::TouchScreenConfigurationForNx& out_mode) const;
55
56 void OnTouchUpdate(u64 timestamp);
57
58private:
59 mutable std::mutex mutex;
60 std::shared_ptr<TouchResource> touch_resource;
61 std::shared_ptr<Core::Timing::EventType> touch_update_event;
42}; 62};
63
43} // namespace Service::HID 64} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_screen_driver.cpp b/src/hid_core/resources/touch_screen/touch_screen_driver.cpp
new file mode 100644
index 000000000..6a64c75b3
--- /dev/null
+++ b/src/hid_core/resources/touch_screen/touch_screen_driver.cpp
@@ -0,0 +1,114 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <algorithm>
5#include "common/settings.h"
6#include "core/frontend/emu_window.h"
7#include "hid_core/hid_core.h"
8#include "hid_core/resources/touch_screen/touch_screen_driver.h"
9
10namespace Service::HID {
11
12TouchDriver::TouchDriver(Core::HID::HIDCore& hid_core) {
13 console = hid_core.GetEmulatedConsole();
14}
15
16TouchDriver::~TouchDriver() = default;
17
18Result TouchDriver::StartTouchSensor() {
19 is_running = true;
20 return ResultSuccess;
21}
22
23Result TouchDriver::StopTouchSensor() {
24 is_running = false;
25 return ResultSuccess;
26}
27
28bool TouchDriver::IsRunning() const {
29 return is_running;
30}
31
32void TouchDriver::ProcessTouchScreenAutoTune() const {
33 // TODO
34}
35
36Result TouchDriver::WaitForDummyInput() {
37 touch_status = {};
38 return ResultSuccess;
39}
40
41Result TouchDriver::WaitForInput() {
42 touch_status = {};
43 const auto touch_input = console->GetTouch();
44 for (std::size_t id = 0; id < touch_status.states.size(); id++) {
45 const auto& current_touch = touch_input[id];
46 auto& finger = fingers[id];
47 finger.id = current_touch.id;
48
49 if (finger.attribute.start_touch) {
50 finger.attribute.raw = 0;
51 continue;
52 }
53
54 if (finger.attribute.end_touch) {
55 finger.attribute.raw = 0;
56 finger.pressed = false;
57 continue;
58 }
59
60 if (!finger.pressed && current_touch.pressed) {
61 finger.attribute.start_touch.Assign(1);
62 finger.pressed = true;
63 finger.position = current_touch.position;
64 continue;
65 }
66
67 if (finger.pressed && !current_touch.pressed) {
68 finger.attribute.raw = 0;
69 finger.attribute.end_touch.Assign(1);
70 continue;
71 }
72
73 // Only update position if touch is not on a special frame
74 finger.position = current_touch.position;
75 }
76
77 std::array<Core::HID::TouchFinger, MaxFingers> active_fingers;
78 const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
79 [](const auto& finger) { return finger.pressed; });
80 const auto active_fingers_count =
81 static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
82
83 touch_status.entry_count = static_cast<s32>(active_fingers_count);
84 for (std::size_t id = 0; id < MaxFingers; ++id) {
85 auto& touch_entry = touch_status.states[id];
86 if (id < active_fingers_count) {
87 const auto& [active_x, active_y] = active_fingers[id].position;
88 touch_entry.position = {
89 .x = static_cast<u16>(active_x * TouchSensorWidth),
90 .y = static_cast<u16>(active_y * TouchSensorHeight),
91 };
92 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
93 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
94 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
95 touch_entry.finger = active_fingers[id].id;
96 touch_entry.attribute.raw = active_fingers[id].attribute.raw;
97 }
98 }
99 return ResultSuccess;
100}
101
102void TouchDriver::GetNextTouchState(TouchScreenState& out_state) const {
103 out_state = touch_status;
104}
105
106void TouchDriver::SetTouchMode(Core::HID::TouchScreenModeForNx mode) {
107 touch_mode = mode;
108}
109
110Core::HID::TouchScreenModeForNx TouchDriver::GetTouchMode() const {
111 return touch_mode;
112}
113
114} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_screen_driver.h b/src/hid_core/resources/touch_screen/touch_screen_driver.h
new file mode 100644
index 000000000..ca7e4970e
--- /dev/null
+++ b/src/hid_core/resources/touch_screen/touch_screen_driver.h
@@ -0,0 +1,47 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/result.h"
8#include "hid_core/frontend/emulated_console.h"
9#include "hid_core/hid_types.h"
10#include "hid_core/resources/touch_screen/touch_types.h"
11
12namespace Core::HID {
13class HIDCore;
14} // namespace Core::HID
15
16namespace Service::HID {
17
18/// This handles all request to Ftm3bd56(TouchPanel) hardware
19class TouchDriver {
20public:
21 explicit TouchDriver(Core::HID::HIDCore& hid_core);
22 ~TouchDriver();
23
24 Result StartTouchSensor();
25 Result StopTouchSensor();
26 bool IsRunning() const;
27
28 void ProcessTouchScreenAutoTune() const;
29
30 Result WaitForDummyInput();
31 Result WaitForInput();
32
33 void GetNextTouchState(TouchScreenState& out_state) const;
34
35 void SetTouchMode(Core::HID::TouchScreenModeForNx mode);
36 Core::HID::TouchScreenModeForNx GetTouchMode() const;
37
38private:
39 bool is_running{};
40 TouchScreenState touch_status{};
41 Core::HID::TouchFingerState fingers{};
42 Core::HID::TouchScreenModeForNx touch_mode{};
43
44 Core::HID::EmulatedConsole* console = nullptr;
45};
46
47} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp
new file mode 100644
index 000000000..56e8e8e51
--- /dev/null
+++ b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp
@@ -0,0 +1,579 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/kernel/k_shared_memory.h"
8#include "core/hle/service/set/system_settings_server.h"
9#include "core/hle/service/sm/sm.h"
10#include "hid_core/hid_result.h"
11#include "hid_core/resources/applet_resource.h"
12#include "hid_core/resources/shared_memory_format.h"
13#include "hid_core/resources/touch_screen/touch_screen_driver.h"
14#include "hid_core/resources/touch_screen/touch_screen_resource.h"
15
16namespace Service::HID {
17constexpr auto GestureUpdatePeriod = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
18
19TouchResource::TouchResource(Core::System& system_) : system{system_} {
20 m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys");
21}
22
23TouchResource::~TouchResource() {
24 Finalize();
25};
26
27Result TouchResource::ActivateTouch() {
28 if (global_ref_counter == std::numeric_limits<s32>::max() - 1 ||
29 touch_ref_counter == std::numeric_limits<s32>::max() - 1) {
30 return ResultTouchOverflow;
31 }
32
33 if (global_ref_counter == 0) {
34 std::scoped_lock lock{*shared_mutex};
35
36 const auto result = touch_driver->StartTouchSensor();
37 if (result.IsError()) {
38 return result;
39 }
40
41 is_initalized = true;
42 system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod,
43 timer_event);
44 current_touch_state = {};
45 ReadTouchInput();
46 gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count,
47 0);
48 }
49
50 Set::TouchScreenMode touch_mode{Set::TouchScreenMode::Standard};
51 m_set_sys->GetTouchScreenMode(touch_mode);
52 default_touch_screen_mode = static_cast<Core::HID::TouchScreenModeForNx>(touch_mode);
53
54 global_ref_counter++;
55 touch_ref_counter++;
56 return ResultSuccess;
57}
58
59Result TouchResource::ActivateTouch(u64 aruid) {
60 std::scoped_lock lock{*shared_mutex};
61
62 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
63 auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
64 TouchAruidData& touch_data = aruid_data[aruid_index];
65
66 if (!applet_data->flag.is_assigned) {
67 touch_data = {};
68 continue;
69 }
70
71 const u64 aruid_id = applet_data->aruid;
72 if (touch_data.aruid != aruid_id) {
73 touch_data = {};
74 touch_data.aruid = aruid_id;
75 }
76
77 if (aruid != aruid_id) {
78 continue;
79 }
80
81 auto& touch_shared = applet_data->shared_memory_format->touch_screen;
82
83 if (touch_shared.touch_screen_lifo.buffer_count == 0) {
84 StorePreviousTouchState(previous_touch_state, touch_data.finger_map,
85 current_touch_state,
86 applet_data->flag.enable_touchscreen.Value() != 0);
87 touch_shared.touch_screen_lifo.WriteNextEntry(previous_touch_state);
88 }
89 }
90 return ResultSuccess;
91}
92
93Result TouchResource::ActivateGesture() {
94 if (global_ref_counter == std::numeric_limits<s32>::max() - 1 ||
95 gesture_ref_counter == std::numeric_limits<s32>::max() - 1) {
96 return ResultGestureOverflow;
97 }
98
99 // Initialize first instance
100 if (global_ref_counter == 0) {
101 const auto result = touch_driver->StartTouchSensor();
102 if (result.IsError()) {
103 return result;
104 }
105
106 is_initalized = true;
107 system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod,
108 timer_event);
109 current_touch_state = {};
110 ReadTouchInput();
111 gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count,
112 0);
113 }
114
115 global_ref_counter++;
116 gesture_ref_counter++;
117 return ResultSuccess;
118}
119
120Result TouchResource::ActivateGesture(u64 aruid, u32 basic_gesture_id) {
121 std::scoped_lock lock{*shared_mutex};
122
123 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
124 auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
125 TouchAruidData& touch_data = aruid_data[aruid_index];
126
127 if (!applet_data->flag.is_assigned) {
128 touch_data = {};
129 continue;
130 }
131
132 const u64 aruid_id = applet_data->aruid;
133 if (touch_data.aruid != aruid_id) {
134 touch_data = {};
135 touch_data.aruid = aruid_id;
136 }
137
138 if (aruid != aruid_id) {
139 continue;
140 }
141
142 auto& gesture_shared = applet_data->shared_memory_format->gesture;
143 if (touch_data.basic_gesture_id != basic_gesture_id) {
144 gesture_shared.gesture_lifo.buffer_count = 0;
145 }
146
147 if (gesture_shared.gesture_lifo.buffer_count == 0) {
148 touch_data.basic_gesture_id = basic_gesture_id;
149
150 gesture_shared.gesture_lifo.WriteNextEntry(gesture_state);
151 }
152 }
153
154 return ResultSuccess;
155}
156
157Result TouchResource::DeactivateTouch() {
158 if (touch_ref_counter == 0 || global_ref_counter == 0) {
159 return ResultTouchNotInitialized;
160 }
161
162 global_ref_counter--;
163 touch_ref_counter--;
164
165 if (touch_ref_counter + global_ref_counter != 0) {
166 return ResultSuccess;
167 }
168
169 return Finalize();
170}
171
172Result TouchResource::DeactivateGesture() {
173 if (gesture_ref_counter == 0 || global_ref_counter == 0) {
174 return ResultGestureNotInitialized;
175 }
176
177 global_ref_counter--;
178 gesture_ref_counter--;
179
180 if (touch_ref_counter + global_ref_counter != 0) {
181 return ResultSuccess;
182 }
183
184 return Finalize();
185}
186
187bool TouchResource::IsTouchActive() const {
188 return touch_ref_counter != 0;
189}
190
191bool TouchResource::IsGestureActive() const {
192 return gesture_ref_counter != 0;
193}
194
195void TouchResource::SetTouchDriver(std::shared_ptr<TouchDriver> driver) {
196 touch_driver = driver;
197}
198
199void TouchResource::SetAppletResource(std::shared_ptr<AppletResource> shared,
200 std::recursive_mutex* mutex) {
201 applet_resource = shared;
202 shared_mutex = mutex;
203}
204
205void TouchResource::SetInputEvent(Kernel::KEvent* event, std::mutex* mutex) {
206 input_event = event;
207 input_mutex = mutex;
208}
209
210void TouchResource::SetHandheldConfig(std::shared_ptr<HandheldConfig> config) {
211 handheld_config = config;
212}
213
214void TouchResource::SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event) {
215 timer_event = event;
216}
217
218Result TouchResource::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) {
219 if (global_ref_counter == 0) {
220 return ResultTouchNotInitialized;
221 }
222
223 if (!is_auto_pilot_initialized) {
224 is_auto_pilot_initialized = true;
225 auto_pilot = {};
226 }
227
228 TouchScreenState state = {
229 .entry_count = static_cast<s32>(auto_pilot_state.count),
230 .states = auto_pilot_state.state,
231 };
232
233 SanitizeInput(state);
234
235 auto_pilot.count = state.entry_count;
236 auto_pilot.state = state.states;
237 return ResultSuccess;
238}
239
240Result TouchResource::UnsetTouchScreenAutoPilotState() {
241 if (global_ref_counter == 0) {
242 return ResultTouchNotInitialized;
243 }
244
245 is_auto_pilot_initialized = false;
246 auto_pilot = {};
247 return ResultSuccess;
248}
249
250Result TouchResource::RequestNextTouchInput() {
251 if (global_ref_counter == 0) {
252 return ResultTouchNotInitialized;
253 }
254
255 if (handheld_config->is_handheld_hid_enabled) {
256 const Result result = touch_driver->WaitForInput();
257 if (result.IsError()) {
258 return result;
259 }
260 }
261
262 is_initalized = true;
263 return ResultSuccess;
264}
265
266Result TouchResource::RequestNextDummyInput() {
267 if (global_ref_counter == 0) {
268 return ResultTouchNotInitialized;
269 }
270
271 if (handheld_config->is_handheld_hid_enabled) {
272 const Result result = touch_driver->WaitForDummyInput();
273 if (result.IsError()) {
274 return result;
275 }
276 }
277
278 is_initalized = false;
279 return ResultSuccess;
280}
281
282Result TouchResource::ProcessTouchScreenAutoTune() {
283 touch_driver->ProcessTouchScreenAutoTune();
284 return ResultSuccess;
285}
286
287void TouchResource::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x,
288 f32 point2_y) {
289 offset = {
290 .x = point1_x,
291 .y = point1_y,
292 };
293 magnification = {
294 .x = point2_x,
295 .y = point2_y,
296 };
297}
298
299Result TouchResource::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) {
300 std::scoped_lock lock{*shared_mutex};
301
302 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
303 const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
304 TouchAruidData& data = aruid_data[aruid_index];
305
306 if (!applet_data->flag.is_assigned) {
307 continue;
308 }
309 if (aruid != data.aruid) {
310 continue;
311 }
312 data.resolution_width = static_cast<u16>(width);
313 data.resolution_height = static_cast<u16>(height);
314 }
315
316 return ResultSuccess;
317}
318
319Result TouchResource::SetTouchScreenConfiguration(
320 const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid) {
321 std::scoped_lock lock{*shared_mutex};
322
323 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
324 const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
325 TouchAruidData& data = aruid_data[aruid_index];
326
327 if (!applet_data->flag.is_assigned) {
328 continue;
329 }
330 if (aruid != data.aruid) {
331 continue;
332 }
333 data.finger_map.touch_mode = touch_configuration.mode;
334 }
335
336 return ResultSuccess;
337}
338
339Result TouchResource::GetTouchScreenConfiguration(
340 Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const {
341 std::scoped_lock lock{*shared_mutex};
342
343 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
344 const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
345 const TouchAruidData& data = aruid_data[aruid_index];
346
347 if (!applet_data->flag.is_assigned) {
348 continue;
349 }
350 if (aruid != data.aruid) {
351 continue;
352 }
353 out_touch_configuration.mode = data.finger_map.touch_mode;
354 }
355
356 return ResultSuccess;
357}
358
359Result TouchResource::SetTouchScreenDefaultConfiguration(
360 const Core::HID::TouchScreenConfigurationForNx& touch_configuration) {
361 default_touch_screen_mode = touch_configuration.mode;
362 return ResultSuccess;
363}
364
365Result TouchResource::GetTouchScreenDefaultConfiguration(
366 Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const {
367 out_touch_configuration.mode = default_touch_screen_mode;
368 return ResultSuccess;
369}
370
371Result TouchResource::Finalize() {
372 is_auto_pilot_initialized = false;
373 auto_pilot = {};
374 system.CoreTiming().UnscheduleEvent(timer_event);
375
376 const auto result = touch_driver->StopTouchSensor();
377 if (result.IsError()) {
378 return result;
379 }
380
381 is_initalized = false;
382 return ResultSuccess;
383}
384
385void TouchResource::StorePreviousTouchState(TouchScreenState& out_previous_touch,
386 TouchFingerMap& out_finger_map,
387 const TouchScreenState& current_touch,
388 bool is_touch_enabled) const {
389 s32 finger_count{};
390
391 if (is_touch_enabled) {
392 finger_count = current_touch.entry_count;
393 if (finger_count < 1) {
394 out_finger_map.finger_count = 0;
395 out_finger_map.finger_ids = {};
396 out_previous_touch.sampling_number = current_touch.sampling_number;
397 out_previous_touch.entry_count = 0;
398 out_previous_touch.states = {};
399 return;
400 }
401 for (std::size_t i = 0; i < static_cast<u32>(finger_count); i++) {
402 out_finger_map.finger_ids[i] = current_touch.states[i].finger;
403 out_previous_touch.states[i] = current_touch.states[i];
404 }
405 out_finger_map.finger_count = finger_count;
406 return;
407 }
408
409 if (!is_touch_enabled && out_finger_map.finger_count > 0 && current_touch.entry_count > 0) {
410 // TODO
411 }
412
413 // Zero out unused entries
414 for (std::size_t i = finger_count; i < MaxFingers; i++) {
415 out_finger_map.finger_ids[i] = 0;
416 out_previous_touch.states[i] = {};
417 }
418
419 out_previous_touch.sampling_number = current_touch.sampling_number;
420 out_previous_touch.entry_count = finger_count;
421}
422
423void TouchResource::ReadTouchInput() {
424 previous_touch_state = current_touch_state;
425
426 if (!is_initalized || !handheld_config->is_handheld_hid_enabled || !touch_driver->IsRunning()) {
427 touch_driver->WaitForDummyInput();
428 } else {
429 touch_driver->WaitForInput();
430 }
431
432 touch_driver->GetNextTouchState(current_touch_state);
433 SanitizeInput(current_touch_state);
434 current_touch_state.sampling_number = sample_number;
435 sample_number++;
436
437 if (is_auto_pilot_initialized && current_touch_state.entry_count == 0) {
438 const std::size_t finger_count = static_cast<std::size_t>(auto_pilot.count);
439 current_touch_state.entry_count = static_cast<s32>(finger_count);
440 for (std::size_t i = 0; i < finger_count; i++) {
441 current_touch_state.states[i] = auto_pilot.state[i];
442 }
443
444 std::size_t index = 0;
445 for (std::size_t i = 0; i < finger_count; i++) {
446 if (auto_pilot.state[i].attribute.end_touch) {
447 continue;
448 }
449 auto_pilot.state[i].attribute.raw = 0;
450 auto_pilot.state[index] = auto_pilot.state[i];
451 index++;
452 }
453
454 auto_pilot.count = index;
455 for (std::size_t i = index; i < auto_pilot.state.size(); i++) {
456 auto_pilot.state[i] = {};
457 }
458 }
459
460 for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) {
461 auto& state = current_touch_state.states[i];
462 state.position.x = static_cast<u32>((magnification.y * static_cast<f32>(state.position.x)) +
463 (offset.x * static_cast<f32>(TouchSensorWidth)));
464 state.position.y = static_cast<u32>((magnification.y * static_cast<f32>(state.position.y)) +
465 (offset.x * static_cast<f32>(TouchSensorHeight)));
466 state.diameter_x = static_cast<u32>(magnification.x * static_cast<f32>(state.diameter_x));
467 state.diameter_y = static_cast<u32>(magnification.y * static_cast<f32>(state.diameter_y));
468 }
469
470 std::size_t index = 0;
471 for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) {
472 const auto& old_state = current_touch_state.states[i];
473 auto& state = current_touch_state.states[index];
474 if ((TouchSensorWidth <= old_state.position.x) ||
475 (TouchSensorHeight <= old_state.position.y)) {
476 continue;
477 }
478 state = old_state;
479 index++;
480 }
481 current_touch_state.entry_count = static_cast<s32>(index);
482
483 SanitizeInput(current_touch_state);
484
485 std::scoped_lock lock{*input_mutex};
486 if (current_touch_state.entry_count == previous_touch_state.entry_count) {
487 if (current_touch_state.entry_count < 1) {
488 return;
489 }
490 bool has_moved = false;
491 for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count);
492 i++) {
493 s32 delta_x = std::abs(static_cast<s32>(current_touch_state.states[i].position.x) -
494 static_cast<s32>(previous_touch_state.states[i].position.x));
495 s32 delta_y = std::abs(static_cast<s32>(current_touch_state.states[i].position.y) -
496 static_cast<s32>(previous_touch_state.states[i].position.y));
497 if (delta_x > 1 || delta_y > 1) {
498 has_moved = true;
499 }
500 }
501 if (!has_moved) {
502 return;
503 }
504 }
505
506 input_event->Signal();
507}
508
509void TouchResource::OnTouchUpdate(s64 timestamp) {
510 if (global_ref_counter == 0) {
511 return;
512 }
513
514 ReadTouchInput();
515 gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count,
516 timestamp);
517
518 std::scoped_lock lock{*shared_mutex};
519
520 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
521 const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
522 TouchAruidData& data = aruid_data[aruid_index];
523
524 if (applet_data == nullptr || !applet_data->flag.is_assigned) {
525 data = {};
526 continue;
527 }
528
529 if (data.aruid != applet_data->aruid) {
530 data = {};
531 data.aruid = applet_data->aruid;
532 }
533
534 if (gesture_ref_counter != 0) {
535 if (!applet_data->flag.enable_touchscreen) {
536 gesture_state = {};
537 }
538 if (gesture_handler.NeedsUpdate()) {
539 gesture_handler.UpdateGestureState(gesture_state, timestamp);
540 auto& gesture_shared = applet_data->shared_memory_format->gesture;
541 gesture_shared.gesture_lifo.WriteNextEntry(gesture_state);
542 }
543 }
544
545 if (touch_ref_counter != 0) {
546 auto touch_mode = data.finger_map.touch_mode;
547 if (touch_mode == Core::HID::TouchScreenModeForNx::UseSystemSetting) {
548 touch_mode = default_touch_screen_mode;
549 }
550
551 if (applet_resource->GetActiveAruid() == applet_data->aruid &&
552 touch_mode != Core::HID::TouchScreenModeForNx::UseSystemSetting && is_initalized &&
553 handheld_config->is_handheld_hid_enabled && touch_driver->IsRunning()) {
554 touch_driver->SetTouchMode(touch_mode);
555 }
556
557 auto& touch_shared = applet_data->shared_memory_format->touch_screen;
558 StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state,
559 applet_data->flag.enable_touchscreen.As<bool>());
560 touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state);
561 }
562 }
563}
564
565void TouchResource::SanitizeInput(TouchScreenState& state) const {
566 for (std::size_t i = 0; i < static_cast<std::size_t>(state.entry_count); i++) {
567 auto& entry = state.states[i];
568 entry.position.x =
569 std::clamp(entry.position.x, TouchBorders, TouchSensorWidth - TouchBorders - 1);
570 entry.position.y =
571 std::clamp(entry.position.y, TouchBorders, TouchSensorHeight - TouchBorders - 1);
572 entry.diameter_x = std::clamp(entry.diameter_x, 0u, TouchSensorWidth - MaxTouchDiameter);
573 entry.diameter_y = std::clamp(entry.diameter_y, 0u, TouchSensorHeight - MaxTouchDiameter);
574 entry.rotation_angle =
575 std::clamp(entry.rotation_angle, -MaxRotationAngle, MaxRotationAngle);
576 }
577}
578
579} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_screen_resource.h b/src/hid_core/resources/touch_screen/touch_screen_resource.h
new file mode 100644
index 000000000..095cddd76
--- /dev/null
+++ b/src/hid_core/resources/touch_screen/touch_screen_resource.h
@@ -0,0 +1,126 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <mutex>
8
9#include "common/common_types.h"
10#include "common/point.h"
11#include "core/hle/result.h"
12#include "hid_core/hid_types.h"
13#include "hid_core/resources/touch_screen/gesture_handler.h"
14#include "hid_core/resources/touch_screen/touch_types.h"
15
16namespace Core {
17class System;
18}
19
20namespace Core::Timing {
21struct EventType;
22}
23
24namespace Kernel {
25class KEvent;
26} // namespace Kernel
27
28namespace Service::Set {
29class ISystemSettingsServer;
30}
31
32namespace Service::HID {
33class AppletResource;
34class TouchSharedMemoryManager;
35class TouchDriver;
36struct HandheldConfig;
37
38class TouchResource {
39public:
40 TouchResource(Core::System& system_);
41 ~TouchResource();
42
43 Result ActivateTouch();
44 Result ActivateTouch(u64 aruid);
45
46 Result ActivateGesture();
47 Result ActivateGesture(u64 aruid, u32 basic_gesture_id);
48
49 Result DeactivateTouch();
50 Result DeactivateGesture();
51
52 bool IsTouchActive() const;
53 bool IsGestureActive() const;
54
55 void SetTouchDriver(std::shared_ptr<TouchDriver> driver);
56 void SetAppletResource(std::shared_ptr<AppletResource> shared, std::recursive_mutex* mutex);
57 void SetInputEvent(Kernel::KEvent* event, std::mutex* mutex);
58 void SetHandheldConfig(std::shared_ptr<HandheldConfig> config);
59 void SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event);
60
61 Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state);
62 Result UnsetTouchScreenAutoPilotState();
63
64 Result RequestNextTouchInput();
65 Result RequestNextDummyInput();
66
67 Result ProcessTouchScreenAutoTune();
68 void SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y);
69 Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid);
70
71 Result SetTouchScreenConfiguration(
72 const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid);
73 Result GetTouchScreenConfiguration(
74 Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const;
75
76 Result SetTouchScreenDefaultConfiguration(
77 const Core::HID::TouchScreenConfigurationForNx& touch_configuration);
78 Result GetTouchScreenDefaultConfiguration(
79 Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const;
80
81 void OnTouchUpdate(s64 timestamp);
82
83private:
84 Result Finalize();
85
86 void StorePreviousTouchState(TouchScreenState& out_previous_touch,
87 TouchFingerMap& out_finger_map,
88 const TouchScreenState& current_touch,
89 bool is_touch_enabled) const;
90 void ReadTouchInput();
91
92 void SanitizeInput(TouchScreenState& state) const;
93
94 s32 global_ref_counter{};
95 s32 gesture_ref_counter{};
96 s32 touch_ref_counter{};
97 bool is_initalized{};
98 u64 sample_number{};
99
100 // External resources
101 std::shared_ptr<Core::Timing::EventType> timer_event{nullptr};
102 std::shared_ptr<TouchDriver> touch_driver{nullptr};
103 std::shared_ptr<AppletResource> applet_resource{nullptr};
104 std::recursive_mutex* shared_mutex{nullptr};
105 std::shared_ptr<HandheldConfig> handheld_config{nullptr};
106 Kernel::KEvent* input_event{nullptr};
107 std::mutex* input_mutex{nullptr};
108
109 // Internal state
110 TouchScreenState current_touch_state{};
111 TouchScreenState previous_touch_state{};
112 GestureState gesture_state{};
113 bool is_auto_pilot_initialized{};
114 AutoPilotState auto_pilot{};
115 GestureHandler gesture_handler{};
116 std::array<TouchAruidData, 0x20> aruid_data{};
117 Common::Point<f32> magnification{1.0f, 1.0f};
118 Common::Point<f32> offset{0.0f, 0.0f};
119 Core::HID::TouchScreenModeForNx default_touch_screen_mode{
120 Core::HID::TouchScreenModeForNx::Finger};
121
122 Core::System& system;
123 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
124};
125
126} // namespace Service::HID
diff --git a/src/hid_core/resources/touch_screen/touch_types.h b/src/hid_core/resources/touch_screen/touch_types.h
index 97ee847da..362088939 100644
--- a/src/hid_core/resources/touch_screen/touch_types.h
+++ b/src/hid_core/resources/touch_screen/touch_types.h
@@ -13,8 +13,20 @@
13#include "hid_core/hid_types.h" 13#include "hid_core/hid_types.h"
14 14
15namespace Service::HID { 15namespace Service::HID {
16static constexpr std::size_t MAX_FINGERS = 16; 16constexpr std::size_t MaxFingers = 16;
17static constexpr size_t MAX_POINTS = 4; 17constexpr std::size_t MaxPoints = 4;
18constexpr u32 TouchSensorWidth = 1280;
19constexpr u32 TouchSensorHeight = 720;
20constexpr s32 MaxRotationAngle = 270;
21constexpr u32 MaxTouchDiameter = 30;
22constexpr u32 TouchBorders = 15;
23
24// HW is around 700, value is set to 400 to make it easier to trigger with mouse
25constexpr f32 SwipeThreshold = 400.0f; // Threshold in pixels/s
26constexpr f32 AngleThreshold = 0.015f; // Threshold in radians
27constexpr f32 PinchThreshold = 0.5f; // Threshold in pixels
28constexpr f32 PressDelay = 0.5f; // Time in seconds
29constexpr f32 DoubleTapDelay = 0.35f; // Time in seconds
18 30
19// This is nn::hid::GestureType 31// This is nn::hid::GestureType
20enum class GestureType : u32 { 32enum class GestureType : u32 {
@@ -28,6 +40,7 @@ enum class GestureType : u32 {
28 Swipe, // Fast press movement and release of a single point 40 Swipe, // Fast press movement and release of a single point
29 Pinch, // All points moving away/closer to the midpoint 41 Pinch, // All points moving away/closer to the midpoint
30 Rotate, // All points rotating from the midpoint 42 Rotate, // All points rotating from the midpoint
43 GestureTypeMax = Rotate,
31}; 44};
32 45
33// This is nn::hid::GestureDirection 46// This is nn::hid::GestureDirection
@@ -69,7 +82,7 @@ struct GestureState {
69static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); 82static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
70 83
71struct GestureProperties { 84struct GestureProperties {
72 std::array<Common::Point<s32>, MAX_POINTS> points{}; 85 std::array<Common::Point<s32>, MaxPoints> points{};
73 std::size_t active_points{}; 86 std::size_t active_points{};
74 Common::Point<s32> mid_point{}; 87 Common::Point<s32> mid_point{};
75 s64 detection_count{}; 88 s64 detection_count{};
@@ -78,13 +91,53 @@ struct GestureProperties {
78 f32 angle{}; 91 f32 angle{};
79}; 92};
80 93
94// This is nn::hid::TouchState
95struct TouchState {
96 u64 delta_time{};
97 Core::HID::TouchAttribute attribute{};
98 u32 finger{};
99 Common::Point<u32> position{};
100 u32 diameter_x{};
101 u32 diameter_y{};
102 s32 rotation_angle{};
103};
104static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
105
81// This is nn::hid::TouchScreenState 106// This is nn::hid::TouchScreenState
82struct TouchScreenState { 107struct TouchScreenState {
83 s64 sampling_number{}; 108 s64 sampling_number{};
84 s32 entry_count{}; 109 s32 entry_count{};
85 INSERT_PADDING_BYTES(4); // Reserved 110 INSERT_PADDING_BYTES(4); // Reserved
86 std::array<Core::HID::TouchState, MAX_FINGERS> states{}; 111 std::array<TouchState, MaxFingers> states{};
87}; 112};
88static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); 113static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
89 114
115struct TouchFingerMap {
116 s32 finger_count{};
117 Core::HID::TouchScreenModeForNx touch_mode;
118 INSERT_PADDING_BYTES(3);
119 std::array<u32, MaxFingers> finger_ids{};
120};
121static_assert(sizeof(TouchFingerMap) == 0x48, "TouchFingerMap is an invalid size");
122
123struct TouchAruidData {
124 u64 aruid;
125 u32 basic_gesture_id;
126 u64 used_1;
127 u64 used_2;
128 u64 used_3;
129 u64 used_4;
130 GestureType gesture_type;
131 u16 resolution_width;
132 u16 resolution_height;
133 TouchFingerMap finger_map;
134};
135static_assert(sizeof(TouchAruidData) == 0x80, "TouchAruidData is an invalid size");
136
137struct AutoPilotState {
138 u64 count;
139 std::array<TouchState, 16> state;
140};
141static_assert(sizeof(AutoPilotState) == 0x288, "AutoPilotState is an invalid size");
142
90} // namespace Service::HID 143} // namespace Service::HID
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 0755ba772..16c905db9 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -55,6 +55,7 @@ add_library(video_core STATIC
55 engines/maxwell_dma.h 55 engines/maxwell_dma.h
56 engines/puller.cpp 56 engines/puller.cpp
57 engines/puller.h 57 engines/puller.h
58 framebuffer_config.cpp
58 framebuffer_config.h 59 framebuffer_config.h
59 fsr.cpp 60 fsr.cpp
60 fsr.h 61 fsr.h
@@ -115,8 +116,24 @@ add_library(video_core STATIC
115 renderer_null/null_rasterizer.h 116 renderer_null/null_rasterizer.h
116 renderer_null/renderer_null.cpp 117 renderer_null/renderer_null.cpp
117 renderer_null/renderer_null.h 118 renderer_null/renderer_null.h
119 renderer_opengl/present/filters.cpp
120 renderer_opengl/present/filters.h
121 renderer_opengl/present/fsr.cpp
122 renderer_opengl/present/fsr.h
123 renderer_opengl/present/fxaa.cpp
124 renderer_opengl/present/fxaa.h
125 renderer_opengl/present/layer.cpp
126 renderer_opengl/present/layer.h
127 renderer_opengl/present/present_uniforms.h
128 renderer_opengl/present/smaa.cpp
129 renderer_opengl/present/smaa.h
130 renderer_opengl/present/util.h
131 renderer_opengl/present/window_adapt_pass.cpp
132 renderer_opengl/present/window_adapt_pass.h
118 renderer_opengl/blit_image.cpp 133 renderer_opengl/blit_image.cpp
119 renderer_opengl/blit_image.h 134 renderer_opengl/blit_image.h
135 renderer_opengl/gl_blit_screen.cpp
136 renderer_opengl/gl_blit_screen.h
120 renderer_opengl/gl_buffer_cache_base.cpp 137 renderer_opengl/gl_buffer_cache_base.cpp
121 renderer_opengl/gl_buffer_cache.cpp 138 renderer_opengl/gl_buffer_cache.cpp
122 renderer_opengl/gl_buffer_cache.h 139 renderer_opengl/gl_buffer_cache.h
@@ -126,8 +143,6 @@ add_library(video_core STATIC
126 renderer_opengl/gl_device.h 143 renderer_opengl/gl_device.h
127 renderer_opengl/gl_fence_manager.cpp 144 renderer_opengl/gl_fence_manager.cpp
128 renderer_opengl/gl_fence_manager.h 145 renderer_opengl/gl_fence_manager.h
129 renderer_opengl/gl_fsr.cpp
130 renderer_opengl/gl_fsr.h
131 renderer_opengl/gl_graphics_pipeline.cpp 146 renderer_opengl/gl_graphics_pipeline.cpp
132 renderer_opengl/gl_graphics_pipeline.h 147 renderer_opengl/gl_graphics_pipeline.h
133 renderer_opengl/gl_rasterizer.cpp 148 renderer_opengl/gl_rasterizer.cpp
@@ -155,6 +170,22 @@ add_library(video_core STATIC
155 renderer_opengl/renderer_opengl.h 170 renderer_opengl/renderer_opengl.h
156 renderer_opengl/util_shaders.cpp 171 renderer_opengl/util_shaders.cpp
157 renderer_opengl/util_shaders.h 172 renderer_opengl/util_shaders.h
173 renderer_vulkan/present/anti_alias_pass.h
174 renderer_vulkan/present/filters.cpp
175 renderer_vulkan/present/filters.h
176 renderer_vulkan/present/fsr.cpp
177 renderer_vulkan/present/fsr.h
178 renderer_vulkan/present/fxaa.cpp
179 renderer_vulkan/present/fxaa.h
180 renderer_vulkan/present/layer.cpp
181 renderer_vulkan/present/layer.h
182 renderer_vulkan/present/present_push_constants.h
183 renderer_vulkan/present/smaa.cpp
184 renderer_vulkan/present/smaa.h
185 renderer_vulkan/present/util.cpp
186 renderer_vulkan/present/util.h
187 renderer_vulkan/present/window_adapt_pass.cpp
188 renderer_vulkan/present/window_adapt_pass.h
158 renderer_vulkan/blit_image.cpp 189 renderer_vulkan/blit_image.cpp
159 renderer_vulkan/blit_image.h 190 renderer_vulkan/blit_image.h
160 renderer_vulkan/fixed_pipeline_state.cpp 191 renderer_vulkan/fixed_pipeline_state.cpp
@@ -181,8 +212,6 @@ add_library(video_core STATIC
181 renderer_vulkan/vk_descriptor_pool.h 212 renderer_vulkan/vk_descriptor_pool.h
182 renderer_vulkan/vk_fence_manager.cpp 213 renderer_vulkan/vk_fence_manager.cpp
183 renderer_vulkan/vk_fence_manager.h 214 renderer_vulkan/vk_fence_manager.h
184 renderer_vulkan/vk_fsr.cpp
185 renderer_vulkan/vk_fsr.h
186 renderer_vulkan/vk_graphics_pipeline.cpp 215 renderer_vulkan/vk_graphics_pipeline.cpp
187 renderer_vulkan/vk_graphics_pipeline.h 216 renderer_vulkan/vk_graphics_pipeline.h
188 renderer_vulkan/vk_master_semaphore.cpp 217 renderer_vulkan/vk_master_semaphore.cpp
@@ -203,8 +232,6 @@ add_library(video_core STATIC
203 renderer_vulkan/vk_scheduler.h 232 renderer_vulkan/vk_scheduler.h
204 renderer_vulkan/vk_shader_util.cpp 233 renderer_vulkan/vk_shader_util.cpp
205 renderer_vulkan/vk_shader_util.h 234 renderer_vulkan/vk_shader_util.h
206 renderer_vulkan/vk_smaa.cpp
207 renderer_vulkan/vk_smaa.h
208 renderer_vulkan/vk_staging_buffer_pool.cpp 235 renderer_vulkan/vk_staging_buffer_pool.cpp
209 renderer_vulkan/vk_staging_buffer_pool.h 236 renderer_vulkan/vk_staging_buffer_pool.h
210 renderer_vulkan/vk_state_tracker.cpp 237 renderer_vulkan/vk_state_tracker.cpp
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 1a43e24b6..99341e431 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -8,6 +8,7 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_funcs.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12#include "common/scratch_buffer.h" 13#include "common/scratch_buffer.h"
13#include "video_core/engines/engine_interface.h" 14#include "video_core/engines/engine_interface.h"
diff --git a/src/video_core/engines/sw_blitter/blitter.cpp b/src/video_core/engines/sw_blitter/blitter.cpp
index 4bc079024..8bcc2f7a7 100644
--- a/src/video_core/engines/sw_blitter/blitter.cpp
+++ b/src/video_core/engines/sw_blitter/blitter.cpp
@@ -111,6 +111,20 @@ void Bilinear(std::span<const f32> input, std::span<f32> output, size_t src_widt
111 } 111 }
112} 112}
113 113
114template <bool unpack>
115void ProcessPitchLinear(std::span<const u8> input, std::span<u8> output, size_t extent_x,
116 size_t extent_y, u32 pitch, u32 x0, u32 y0, size_t bpp) {
117 const size_t base_offset = x0 * bpp;
118 const size_t copy_size = extent_x * bpp;
119 for (size_t y = 0; y < extent_y; y++) {
120 const size_t first_offset = (y + y0) * pitch + base_offset;
121 const size_t second_offset = y * extent_x * bpp;
122 u8* write_to = unpack ? &output[first_offset] : &output[second_offset];
123 const u8* read_from = unpack ? &input[second_offset] : &input[first_offset];
124 std::memcpy(write_to, read_from, copy_size);
125 }
126}
127
114} // namespace 128} // namespace
115 129
116struct SoftwareBlitEngine::BlitEngineImpl { 130struct SoftwareBlitEngine::BlitEngineImpl {
@@ -138,19 +152,6 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
138 } 152 }
139 return static_cast<size_t>(surface.pitch * surface.height); 153 return static_cast<size_t>(surface.pitch * surface.height);
140 }; 154 };
141 const auto process_pitch_linear = [](bool unpack, std::span<const u8> input,
142 std::span<u8> output, u32 extent_x, u32 extent_y,
143 u32 pitch, u32 x0, u32 y0, size_t bpp) {
144 const size_t base_offset = x0 * bpp;
145 const size_t copy_size = extent_x * bpp;
146 for (u32 y = y0; y < extent_y; y++) {
147 const size_t first_offset = y * pitch + base_offset;
148 const size_t second_offset = y * extent_x * bpp;
149 u8* write_to = unpack ? &output[first_offset] : &output[second_offset];
150 const u8* read_from = unpack ? &input[second_offset] : &input[first_offset];
151 std::memcpy(write_to, read_from, copy_size);
152 }
153 };
154 155
155 const u32 src_extent_x = config.src_x1 - config.src_x0; 156 const u32 src_extent_x = config.src_x1 - config.src_x0;
156 const u32 src_extent_y = config.src_y1 - config.src_y0; 157 const u32 src_extent_y = config.src_y1 - config.src_y0;
@@ -205,8 +206,8 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
205 src.depth, config.src_x0, config.src_y0, src_extent_x, src_extent_y, 206 src.depth, config.src_x0, config.src_y0, src_extent_x, src_extent_y,
206 src.block_height, src.block_depth, src_extent_x * src_bytes_per_pixel); 207 src.block_height, src.block_depth, src_extent_x * src_bytes_per_pixel);
207 } else { 208 } else {
208 process_pitch_linear(false, tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y, 209 ProcessPitchLinear<false>(tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y,
209 src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel); 210 src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel);
210 } 211 }
211 212
212 // Conversion Phase 213 // Conversion Phase
@@ -229,9 +230,9 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst,
229 dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, dst_extent_y, 230 dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, dst_extent_y,
230 dst.block_height, dst.block_depth, dst_extent_x * dst_bytes_per_pixel); 231 dst.block_height, dst.block_depth, dst_extent_x * dst_bytes_per_pixel);
231 } else { 232 } else {
232 process_pitch_linear(true, impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y, 233 ProcessPitchLinear<true>(impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y,
233 dst.pitch, config.dst_x0, config.dst_y0, 234 dst.pitch, config.dst_x0, config.dst_y0,
234 static_cast<size_t>(dst_bytes_per_pixel)); 235 static_cast<size_t>(dst_bytes_per_pixel));
235 } 236 }
236 return true; 237 return true;
237} 238}
diff --git a/src/video_core/framebuffer_config.cpp b/src/video_core/framebuffer_config.cpp
new file mode 100644
index 000000000..e28d41f84
--- /dev/null
+++ b/src/video_core/framebuffer_config.cpp
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "video_core/framebuffer_config.h"
6
7namespace Tegra {
8
9Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,
10 u32 texture_height) {
11 f32 left, top, right, bottom;
12
13 if (!framebuffer.crop_rect.IsEmpty()) {
14 // If crop rectangle is not empty, apply properties from rectangle.
15 left = static_cast<f32>(framebuffer.crop_rect.left);
16 top = static_cast<f32>(framebuffer.crop_rect.top);
17 right = static_cast<f32>(framebuffer.crop_rect.right);
18 bottom = static_cast<f32>(framebuffer.crop_rect.bottom);
19 } else {
20 // Otherwise, fall back to framebuffer dimensions.
21 left = 0;
22 top = 0;
23 right = static_cast<f32>(framebuffer.width);
24 bottom = static_cast<f32>(framebuffer.height);
25 }
26
27 // Apply transformation flags.
28 auto framebuffer_transform_flags = framebuffer.transform_flags;
29
30 if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipH)) {
31 // Switch left and right.
32 std::swap(left, right);
33 }
34 if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipV)) {
35 // Switch top and bottom.
36 std::swap(top, bottom);
37 }
38
39 framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipH;
40 framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipV;
41 if (True(framebuffer_transform_flags)) {
42 UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}",
43 static_cast<u32>(framebuffer_transform_flags));
44 }
45
46 // Normalize coordinate space.
47 left /= static_cast<f32>(texture_width);
48 top /= static_cast<f32>(texture_height);
49 right /= static_cast<f32>(texture_width);
50 bottom /= static_cast<f32>(texture_height);
51
52 return Common::Rectangle<f32>(left, top, right, bottom);
53}
54
55} // namespace Tegra
diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h
index 856f4bd52..6a18b76fb 100644
--- a/src/video_core/framebuffer_config.h
+++ b/src/video_core/framebuffer_config.h
@@ -7,6 +7,7 @@
7#include "common/math_util.h" 7#include "common/math_util.h"
8#include "core/hle/service/nvnflinger/buffer_transform_flags.h" 8#include "core/hle/service/nvnflinger/buffer_transform_flags.h"
9#include "core/hle/service/nvnflinger/pixel_format.h" 9#include "core/hle/service/nvnflinger/pixel_format.h"
10#include "core/hle/service/nvnflinger/ui/fence.h"
10 11
11namespace Tegra { 12namespace Tegra {
12 13
@@ -21,7 +22,10 @@ struct FramebufferConfig {
21 u32 stride{}; 22 u32 stride{};
22 Service::android::PixelFormat pixel_format{}; 23 Service::android::PixelFormat pixel_format{};
23 Service::android::BufferTransformFlags transform_flags{}; 24 Service::android::BufferTransformFlags transform_flags{};
24 Common::Rectangle<int> crop_rect; 25 Common::Rectangle<int> crop_rect{};
25}; 26};
26 27
28Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,
29 u32 texture_height);
30
27} // namespace Tegra 31} // namespace Tegra
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 609704b33..f4a5d831c 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -274,11 +274,6 @@ struct GPU::Impl {
274 } 274 }
275 } 275 }
276 276
277 /// Swap buffers (render frame)
278 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
279 gpu_thread.SwapBuffers(framebuffer);
280 }
281
282 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 277 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
283 void FlushRegion(DAddr addr, u64 size) { 278 void FlushRegion(DAddr addr, u64 size) {
284 gpu_thread.FlushRegion(addr, size); 279 gpu_thread.FlushRegion(addr, size);
@@ -313,8 +308,9 @@ struct GPU::Impl {
313 gpu_thread.FlushAndInvalidateRegion(addr, size); 308 gpu_thread.FlushAndInvalidateRegion(addr, size);
314 } 309 }
315 310
316 void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, 311 void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
317 std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) { 312 std::vector<Service::Nvidia::NvFence>&& fences) {
313 size_t num_fences{fences.size()};
318 size_t current_request_counter{}; 314 size_t current_request_counter{};
319 { 315 {
320 std::unique_lock<std::mutex> lk(request_swap_mutex); 316 std::unique_lock<std::mutex> lk(request_swap_mutex);
@@ -328,13 +324,12 @@ struct GPU::Impl {
328 } 324 }
329 } 325 }
330 const auto wait_fence = 326 const auto wait_fence =
331 RequestSyncOperation([this, current_request_counter, framebuffer, fences, num_fences] { 327 RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] {
332 auto& syncpoint_manager = host1x.GetSyncpointManager(); 328 auto& syncpoint_manager = host1x.GetSyncpointManager();
333 if (num_fences == 0) { 329 if (num_fences == 0) {
334 renderer->SwapBuffers(framebuffer); 330 renderer->Composite(layers);
335 } 331 }
336 const auto executer = [this, current_request_counter, 332 const auto executer = [this, current_request_counter, layers_copy = layers]() {
337 framebuffer_copy = *framebuffer]() {
338 { 333 {
339 std::unique_lock<std::mutex> lk(request_swap_mutex); 334 std::unique_lock<std::mutex> lk(request_swap_mutex);
340 if (--request_swap_counters[current_request_counter] != 0) { 335 if (--request_swap_counters[current_request_counter] != 0) {
@@ -342,7 +337,7 @@ struct GPU::Impl {
342 } 337 }
343 free_swap_counters.push_back(current_request_counter); 338 free_swap_counters.push_back(current_request_counter);
344 } 339 }
345 renderer->SwapBuffers(&framebuffer_copy); 340 renderer->Composite(layers_copy);
346 }; 341 };
347 for (size_t i = 0; i < num_fences; i++) { 342 for (size_t i = 0; i < num_fences; i++) {
348 syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); 343 syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer);
@@ -505,9 +500,9 @@ const VideoCore::ShaderNotify& GPU::ShaderNotify() const {
505 return impl->ShaderNotify(); 500 return impl->ShaderNotify();
506} 501}
507 502
508void GPU::RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, 503void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
509 std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) { 504 std::vector<Service::Nvidia::NvFence>&& fences) {
510 impl->RequestSwapBuffers(framebuffer, fences, num_fences); 505 impl->RequestComposite(std::move(layers), std::move(fences));
511} 506}
512 507
513u64 GPU::GetTicks() const { 508u64 GPU::GetTicks() const {
@@ -554,10 +549,6 @@ void GPU::ClearCdmaInstance(u32 id) {
554 impl->ClearCdmaInstance(id); 549 impl->ClearCdmaInstance(id);
555} 550}
556 551
557void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
558 impl->SwapBuffers(framebuffer);
559}
560
561VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) { 552VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) {
562 return impl->OnCPURead(addr, size); 553 return impl->OnCPURead(addr, size);
563} 554}
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index b3c1d15bd..c4602ca37 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -212,8 +212,8 @@ public:
212 212
213 void RendererFrameEndNotify(); 213 void RendererFrameEndNotify();
214 214
215 void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, 215 void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
216 std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences); 216 std::vector<Service::Nvidia::NvFence>&& fences);
217 217
218 /// Performs any additional setup necessary in order to begin GPU emulation. 218 /// Performs any additional setup necessary in order to begin GPU emulation.
219 /// This can be used to launch any necessary threads and register any necessary 219 /// This can be used to launch any necessary threads and register any necessary
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 788d4f61e..58d8110b8 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -40,8 +40,6 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
40 } 40 }
41 if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { 41 if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
42 scheduler.Push(submit_list->channel, std::move(submit_list->entries)); 42 scheduler.Push(submit_list->channel, std::move(submit_list->entries));
43 } else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) {
44 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
45 } else if (std::holds_alternative<GPUTickCommand>(next.data)) { 43 } else if (std::holds_alternative<GPUTickCommand>(next.data)) {
46 system.GPU().TickWork(); 44 system.GPU().TickWork();
47 } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { 45 } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
@@ -78,10 +76,6 @@ void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) {
78 PushCommand(SubmitListCommand(channel, std::move(entries))); 76 PushCommand(SubmitListCommand(channel, std::move(entries)));
79} 77}
80 78
81void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
82 PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt));
83}
84
85void ThreadManager::FlushRegion(DAddr addr, u64 size) { 79void ThreadManager::FlushRegion(DAddr addr, u64 size) {
86 if (!is_async) { 80 if (!is_async) {
87 // Always flush with synchronous GPU mode 81 // Always flush with synchronous GPU mode
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 2de25e9ef..dc0fce9f8 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -44,14 +44,6 @@ struct SubmitListCommand final {
44 Tegra::CommandList entries; 44 Tegra::CommandList entries;
45}; 45};
46 46
47/// Command to signal to the GPU thread that a swap buffers is pending
48struct SwapBuffersCommand final {
49 explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer_)
50 : framebuffer{std::move(framebuffer_)} {}
51
52 std::optional<Tegra::FramebufferConfig> framebuffer;
53};
54
55/// Command to signal to the GPU thread to flush a region 47/// Command to signal to the GPU thread to flush a region
56struct FlushRegionCommand final { 48struct FlushRegionCommand final {
57 explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {} 49 explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {}
@@ -81,8 +73,8 @@ struct FlushAndInvalidateRegionCommand final {
81struct GPUTickCommand final {}; 73struct GPUTickCommand final {};
82 74
83using CommandData = 75using CommandData =
84 std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, 76 std::variant<std::monostate, SubmitListCommand, FlushRegionCommand, InvalidateRegionCommand,
85 InvalidateRegionCommand, FlushAndInvalidateRegionCommand, GPUTickCommand>; 77 FlushAndInvalidateRegionCommand, GPUTickCommand>;
86 78
87struct CommandDataContainer { 79struct CommandDataContainer {
88 CommandDataContainer() = default; 80 CommandDataContainer() = default;
@@ -118,9 +110,6 @@ public:
118 /// Push GPU command entries to be processed 110 /// Push GPU command entries to be processed
119 void SubmitList(s32 channel, Tegra::CommandList&& entries); 111 void SubmitList(s32 channel, Tegra::CommandList&& entries);
120 112
121 /// Swap buffers (render frame)
122 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
123
124 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory 113 /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
125 void FlushRegion(DAddr addr, u64 size); 114 void FlushRegion(DAddr addr, u64 size);
126 115
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
index cd2549232..969f21d50 100644
--- a/src/video_core/host_shaders/CMakeLists.txt
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -9,7 +9,7 @@ set(FIDELITYFX_FILES
9) 9)
10 10
11set(GLSL_INCLUDES 11set(GLSL_INCLUDES
12 fidelityfx_fsr.comp 12 fidelityfx_fsr.frag
13 ${FIDELITYFX_FILES} 13 ${FIDELITYFX_FILES}
14) 14)
15 15
@@ -56,10 +56,11 @@ set(SHADER_FILES
56 vulkan_color_clear.frag 56 vulkan_color_clear.frag
57 vulkan_color_clear.vert 57 vulkan_color_clear.vert
58 vulkan_depthstencil_clear.frag 58 vulkan_depthstencil_clear.frag
59 vulkan_fidelityfx_fsr_easu_fp16.comp 59 vulkan_fidelityfx_fsr.vert
60 vulkan_fidelityfx_fsr_easu_fp32.comp 60 vulkan_fidelityfx_fsr_easu_fp16.frag
61 vulkan_fidelityfx_fsr_rcas_fp16.comp 61 vulkan_fidelityfx_fsr_easu_fp32.frag
62 vulkan_fidelityfx_fsr_rcas_fp32.comp 62 vulkan_fidelityfx_fsr_rcas_fp16.frag
63 vulkan_fidelityfx_fsr_rcas_fp32.frag
63 vulkan_present.frag 64 vulkan_present.frag
64 vulkan_present.vert 65 vulkan_present.vert
65 vulkan_present_scaleforce_fp16.frag 66 vulkan_present_scaleforce_fp16.frag
diff --git a/src/video_core/host_shaders/fidelityfx_fsr.comp b/src/video_core/host_shaders/fidelityfx_fsr.frag
index f91b1aa9f..a266e1c4e 100644
--- a/src/video_core/host_shaders/fidelityfx_fsr.comp
+++ b/src/video_core/host_shaders/fidelityfx_fsr.frag
@@ -34,7 +34,6 @@ layout( push_constant ) uniform constants {
34}; 34};
35 35
36layout(set=0,binding=0) uniform sampler2D InputTexture; 36layout(set=0,binding=0) uniform sampler2D InputTexture;
37layout(set=0,binding=1,rgba16f) uniform image2D OutputTexture;
38 37
39#define A_GPU 1 38#define A_GPU 1
40#define A_GLSL 1 39#define A_GLSL 1
@@ -72,44 +71,40 @@ layout(set=0,binding=1,rgba16f) uniform image2D OutputTexture;
72 71
73#include "ffx_fsr1.h" 72#include "ffx_fsr1.h"
74 73
75void CurrFilter(AU2 pos) { 74#if USE_RCAS
76#if USE_BILINEAR 75 layout(location = 0) in vec2 frag_texcoord;
77 AF2 pp = (AF2(pos) * AF2_AU2(Const0.xy) + AF2_AU2(Const0.zw)) * AF2_AU2(Const1.xy) + AF2(0.5, -0.5) * AF2_AU2(Const1.zw);
78 imageStore(OutputTexture, ASU2(pos), textureLod(InputTexture, pp, 0.0));
79#endif 76#endif
77layout (location = 0) out vec4 frag_color;
78
79void CurrFilter(AU2 pos) {
80#if USE_EASU 80#if USE_EASU
81 #ifndef YUZU_USE_FP16 81 #ifndef YUZU_USE_FP16
82 AF3 c; 82 AF3 c;
83 FsrEasuF(c, pos, Const0, Const1, Const2, Const3); 83 FsrEasuF(c, pos, Const0, Const1, Const2, Const3);
84 imageStore(OutputTexture, ASU2(pos), AF4(c, 1)); 84 frag_color = AF4(c, 1.0);
85 #else 85 #else
86 AH3 c; 86 AH3 c;
87 FsrEasuH(c, pos, Const0, Const1, Const2, Const3); 87 FsrEasuH(c, pos, Const0, Const1, Const2, Const3);
88 imageStore(OutputTexture, ASU2(pos), AH4(c, 1)); 88 frag_color = AH4(c, 1.0);
89 #endif 89 #endif
90#endif 90#endif
91#if USE_RCAS 91#if USE_RCAS
92 #ifndef YUZU_USE_FP16 92 #ifndef YUZU_USE_FP16
93 AF3 c; 93 AF3 c;
94 FsrRcasF(c.r, c.g, c.b, pos, Const0); 94 FsrRcasF(c.r, c.g, c.b, pos, Const0);
95 imageStore(OutputTexture, ASU2(pos), AF4(c, 1)); 95 frag_color = AF4(c, 1.0);
96 #else 96 #else
97 AH3 c; 97 AH3 c;
98 FsrRcasH(c.r, c.g, c.b, pos, Const0); 98 FsrRcasH(c.r, c.g, c.b, pos, Const0);
99 imageStore(OutputTexture, ASU2(pos), AH4(c, 1)); 99 frag_color = AH4(c, 1.0);
100 #endif 100 #endif
101#endif 101#endif
102} 102}
103 103
104layout(local_size_x=64) in;
105void main() { 104void main() {
106 // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. 105#if USE_RCAS
107 AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); 106 CurrFilter(AU2(frag_texcoord * vec2(textureSize(InputTexture, 0))));
108 CurrFilter(gxy); 107#else
109 gxy.x += 8u; 108 CurrFilter(AU2(gl_FragCoord.xy));
110 CurrFilter(gxy); 109#endif
111 gxy.y += 8u;
112 CurrFilter(gxy);
113 gxy.x -= 8u;
114 CurrFilter(gxy);
115} 110}
diff --git a/src/video_core/host_shaders/fxaa.vert b/src/video_core/host_shaders/fxaa.vert
index c2717d90d..223ab785e 100644
--- a/src/video_core/host_shaders/fxaa.vert
+++ b/src/video_core/host_shaders/fxaa.vert
@@ -7,8 +7,8 @@ out gl_PerVertex {
7 vec4 gl_Position; 7 vec4 gl_Position;
8}; 8};
9 9
10const vec2 vertices[4] = 10const vec2 vertices[3] =
11 vec2[4](vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(-1.0, -1.0), vec2(1.0, -1.0)); 11 vec2[3](vec2(-1,-1), vec2(3,-1), vec2(-1, 3));
12 12
13layout (location = 0) out vec4 posPos; 13layout (location = 0) out vec4 posPos;
14 14
diff --git a/src/video_core/host_shaders/opengl_present_scaleforce.frag b/src/video_core/host_shaders/opengl_present_scaleforce.frag
index a780373e3..1598575a1 100644
--- a/src/video_core/host_shaders/opengl_present_scaleforce.frag
+++ b/src/video_core/host_shaders/opengl_present_scaleforce.frag
@@ -26,21 +26,11 @@
26 26
27#endif 27#endif
28 28
29#ifdef VULKAN
30
31#define BINDING_COLOR_TEXTURE 1
32
33#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
34
35#define BINDING_COLOR_TEXTURE 0
36
37#endif
38
39layout (location = 0) in vec2 tex_coord; 29layout (location = 0) in vec2 tex_coord;
40 30
41layout (location = 0) out vec4 frag_color; 31layout (location = 0) out vec4 frag_color;
42 32
43layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; 33layout (binding = 0) uniform sampler2D input_texture;
44 34
45const bool ignore_alpha = true; 35const bool ignore_alpha = true;
46 36
diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag
index c57dd2851..c814629cf 100644
--- a/src/video_core/host_shaders/present_bicubic.frag
+++ b/src/video_core/host_shaders/present_bicubic.frag
@@ -3,22 +3,12 @@
3 3
4#version 460 core 4#version 460 core
5 5
6#ifdef VULKAN
7
8#define BINDING_COLOR_TEXTURE 1
9
10#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
11
12#define BINDING_COLOR_TEXTURE 0
13
14#endif
15
16 6
17layout (location = 0) in vec2 frag_tex_coord; 7layout (location = 0) in vec2 frag_tex_coord;
18 8
19layout (location = 0) out vec4 color; 9layout (location = 0) out vec4 color;
20 10
21layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D color_texture; 11layout (binding = 0) uniform sampler2D color_texture;
22 12
23vec4 cubic(float v) { 13vec4 cubic(float v) {
24 vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; 14 vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v;
diff --git a/src/video_core/host_shaders/present_gaussian.frag b/src/video_core/host_shaders/present_gaussian.frag
index 5f54b71b6..ad9bb76a4 100644
--- a/src/video_core/host_shaders/present_gaussian.frag
+++ b/src/video_core/host_shaders/present_gaussian.frag
@@ -7,21 +7,11 @@
7 7
8#version 460 core 8#version 460 core
9 9
10#ifdef VULKAN
11
12#define BINDING_COLOR_TEXTURE 1
13
14#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
15
16#define BINDING_COLOR_TEXTURE 0
17
18#endif
19
20layout(location = 0) in vec2 frag_tex_coord; 10layout(location = 0) in vec2 frag_tex_coord;
21 11
22layout(location = 0) out vec4 color; 12layout(location = 0) out vec4 color;
23 13
24layout(binding = BINDING_COLOR_TEXTURE) uniform sampler2D color_texture; 14layout(binding = 0) uniform sampler2D color_texture;
25 15
26const float offset[3] = float[](0.0, 1.3846153846, 3.2307692308); 16const float offset[3] = float[](0.0, 1.3846153846, 3.2307692308);
27const float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703); 17const float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703);
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr.vert b/src/video_core/host_shaders/vulkan_fidelityfx_fsr.vert
new file mode 100644
index 000000000..6a87a7cac
--- /dev/null
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr.vert
@@ -0,0 +1,13 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#version 450
5
6layout(location = 0) out vec2 texcoord;
7
8void main() {
9 float x = float((gl_VertexIndex & 1) << 2);
10 float y = float((gl_VertexIndex & 2) << 1);
11 gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0);
12 texcoord = vec2(x, y) / 2.0;
13}
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag
index 00af13726..d369bef06 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.comp
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag
@@ -7,4 +7,4 @@
7#define YUZU_USE_FP16 7#define YUZU_USE_FP16
8#define USE_EASU 1 8#define USE_EASU 1
9 9
10#include "fidelityfx_fsr.comp" 10#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag
index 13d783fa8..6f25ef00f 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.comp
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag
@@ -6,4 +6,4 @@
6 6
7#define USE_EASU 1 7#define USE_EASU 1
8 8
9#include "fidelityfx_fsr.comp" 9#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag
index 331549d96..0c953a900 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.comp
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag
@@ -7,4 +7,4 @@
7#define YUZU_USE_FP16 7#define YUZU_USE_FP16
8#define USE_RCAS 1 8#define USE_RCAS 1
9 9
10#include "fidelityfx_fsr.comp" 10#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag
index 013ca0014..02e9a27c6 100644
--- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.comp
+++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag
@@ -6,4 +6,4 @@
6 6
7#define USE_RCAS 1 7#define USE_RCAS 1
8 8
9#include "fidelityfx_fsr.comp" 9#include "fidelityfx_fsr.frag"
diff --git a/src/video_core/host_shaders/vulkan_present.frag b/src/video_core/host_shaders/vulkan_present.frag
index 97e098ced..adada9411 100644
--- a/src/video_core/host_shaders/vulkan_present.frag
+++ b/src/video_core/host_shaders/vulkan_present.frag
@@ -7,7 +7,7 @@ layout (location = 0) in vec2 frag_tex_coord;
7 7
8layout (location = 0) out vec4 color; 8layout (location = 0) out vec4 color;
9 9
10layout (binding = 1) uniform sampler2D color_texture; 10layout (binding = 0) uniform sampler2D color_texture;
11 11
12void main() { 12void main() {
13 color = texture(color_texture, frag_tex_coord); 13 color = texture(color_texture, frag_tex_coord);
diff --git a/src/video_core/host_shaders/vulkan_present.vert b/src/video_core/host_shaders/vulkan_present.vert
index 89dc80468..249c9675a 100644
--- a/src/video_core/host_shaders/vulkan_present.vert
+++ b/src/video_core/host_shaders/vulkan_present.vert
@@ -3,16 +3,37 @@
3 3
4#version 460 core 4#version 460 core
5 5
6layout (location = 0) in vec2 vert_position;
7layout (location = 1) in vec2 vert_tex_coord;
8
9layout (location = 0) out vec2 frag_tex_coord; 6layout (location = 0) out vec2 frag_tex_coord;
10 7
11layout (set = 0, binding = 0) uniform MatrixBlock { 8struct ScreenRectVertex {
9 vec2 position;
10 vec2 tex_coord;
11};
12
13layout (push_constant) uniform PushConstants {
12 mat4 modelview_matrix; 14 mat4 modelview_matrix;
15 ScreenRectVertex vertices[4];
13}; 16};
14 17
18// Vulkan spec 15.8.1:
19// Any member of a push constant block that is declared as an
20// array must only be accessed with dynamically uniform indices.
21ScreenRectVertex GetVertex(int index) {
22 switch (index) {
23 case 0:
24 default:
25 return vertices[0];
26 case 1:
27 return vertices[1];
28 case 2:
29 return vertices[2];
30 case 3:
31 return vertices[3];
32 }
33}
34
15void main() { 35void main() {
16 gl_Position = modelview_matrix * vec4(vert_position, 0.0, 1.0); 36 ScreenRectVertex vertex = GetVertex(gl_VertexIndex);
17 frag_tex_coord = vert_tex_coord; 37 gl_Position = modelview_matrix * vec4(vertex.position, 0.0, 1.0);
38 frag_tex_coord = vertex.tex_coord;
18} 39}
diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag
index 3dc9c0df5..79ea817c2 100644
--- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag
+++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag
@@ -5,6 +5,7 @@
5 5
6#extension GL_GOOGLE_include_directive : enable 6#extension GL_GOOGLE_include_directive : enable
7 7
8#define VERSION 1
8#define YUZU_USE_FP16 9#define YUZU_USE_FP16
9 10
10#include "opengl_present_scaleforce.frag" 11#include "opengl_present_scaleforce.frag"
diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag
index 77ed07552..9605bb58b 100644
--- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag
+++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag
@@ -5,4 +5,6 @@
5 5
6#extension GL_GOOGLE_include_directive : enable 6#extension GL_GOOGLE_include_directive : enable
7 7
8#define VERSION 1
9
8#include "opengl_present_scaleforce.frag" 10#include "opengl_present_scaleforce.frag"
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 8fa4e4d9a..6e2eccfbf 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -155,12 +155,6 @@ public:
155 virtual void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 155 virtual void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
156 std::span<const u8> memory) = 0; 156 std::span<const u8> memory) = 0;
157 157
158 /// Attempt to use a faster method to display the framebuffer to screen
159 [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config,
160 DAddr framebuffer_addr, u32 pixel_stride) {
161 return false;
162 }
163
164 /// Initialize disk cached resources for the game being emulated 158 /// Initialize disk cached resources for the game being emulated
165 virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 159 virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
166 const DiskResourceLoadCallback& callback) {} 160 const DiskResourceLoadCallback& callback) {}
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 78ea5208b..3ad180f67 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -38,7 +38,7 @@ public:
38 virtual ~RendererBase(); 38 virtual ~RendererBase();
39 39
40 /// Finalize rendering the guest frame and draw into the presentation texture 40 /// Finalize rendering the guest frame and draw into the presentation texture
41 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; 41 virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0;
42 42
43 [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; 43 [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0;
44 44
diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp
index abfabb65b..a5cda0f38 100644
--- a/src/video_core/renderer_null/null_rasterizer.cpp
+++ b/src/video_core/renderer_null/null_rasterizer.cpp
@@ -92,10 +92,6 @@ bool RasterizerNull::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surfac
92} 92}
93void RasterizerNull::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 93void RasterizerNull::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
94 std::span<const u8> memory) {} 94 std::span<const u8> memory) {}
95bool RasterizerNull::AccelerateDisplay(const Tegra::FramebufferConfig& config,
96 DAddr framebuffer_addr, u32 pixel_stride) {
97 return true;
98}
99void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading, 95void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
100 const VideoCore::DiskResourceLoadCallback& callback) {} 96 const VideoCore::DiskResourceLoadCallback& callback) {}
101void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) { 97void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) {
diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h
index a5789604f..c7f5849c7 100644
--- a/src/video_core/renderer_null/null_rasterizer.h
+++ b/src/video_core/renderer_null/null_rasterizer.h
@@ -77,8 +77,6 @@ public:
77 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; 77 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override;
78 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 78 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
79 std::span<const u8> memory) override; 79 std::span<const u8> memory) override;
80 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, DAddr framebuffer_addr,
81 u32 pixel_stride) override;
82 void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 80 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
83 const VideoCore::DiskResourceLoadCallback& callback) override; 81 const VideoCore::DiskResourceLoadCallback& callback) override;
84 void InitializeChannel(Tegra::Control::ChannelState& channel) override; 82 void InitializeChannel(Tegra::Control::ChannelState& channel) override;
diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp
index 078feb925..c89daff53 100644
--- a/src/video_core/renderer_null/renderer_null.cpp
+++ b/src/video_core/renderer_null/renderer_null.cpp
@@ -13,8 +13,8 @@ RendererNull::RendererNull(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gp
13 13
14RendererNull::~RendererNull() = default; 14RendererNull::~RendererNull() = default;
15 15
16void RendererNull::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 16void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
17 if (!framebuffer) { 17 if (framebuffers.empty()) {
18 return; 18 return;
19 } 19 }
20 20
diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h
index 9531b43f6..063b476bb 100644
--- a/src/video_core/renderer_null/renderer_null.h
+++ b/src/video_core/renderer_null/renderer_null.h
@@ -17,7 +17,7 @@ public:
17 std::unique_ptr<Core::Frontend::GraphicsContext> context); 17 std::unique_ptr<Core::Frontend::GraphicsContext> context);
18 ~RendererNull() override; 18 ~RendererNull() override;
19 19
20 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 20 void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override;
21 21
22 VideoCore::RasterizerInterface* ReadRasterizer() override { 22 VideoCore::RasterizerInterface* ReadRasterizer() override {
23 return &m_rasterizer; 23 return &m_rasterizer;
diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp
new file mode 100644
index 000000000..6ba8b214b
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp
@@ -0,0 +1,96 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "video_core/renderer_opengl/gl_blit_screen.h"
6#include "video_core/renderer_opengl/gl_state_tracker.h"
7#include "video_core/renderer_opengl/present/filters.h"
8#include "video_core/renderer_opengl/present/layer.h"
9#include "video_core/renderer_opengl/present/window_adapt_pass.h"
10
11namespace OpenGL {
12
13BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_,
14 Tegra::MaxwellDeviceMemoryManager& device_memory_,
15 StateTracker& state_tracker_, ProgramManager& program_manager_,
16 Device& device_)
17 : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_),
18 program_manager(program_manager_), device(device_) {}
19
20BlitScreen::~BlitScreen() = default;
21
22void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers,
23 const Layout::FramebufferLayout& layout) {
24 // TODO: Signal state tracker about these changes
25 state_tracker.NotifyScreenDrawVertexArray();
26 state_tracker.NotifyPolygonModes();
27 state_tracker.NotifyViewport0();
28 state_tracker.NotifyScissor0();
29 state_tracker.NotifyColorMask(0);
30 state_tracker.NotifyBlend0();
31 state_tracker.NotifyFramebuffer();
32 state_tracker.NotifyFrontFace();
33 state_tracker.NotifyCullTest();
34 state_tracker.NotifyDepthTest();
35 state_tracker.NotifyStencilTest();
36 state_tracker.NotifyPolygonOffset();
37 state_tracker.NotifyRasterizeEnable();
38 state_tracker.NotifyFramebufferSRGB();
39 state_tracker.NotifyLogicOp();
40 state_tracker.NotifyClipControl();
41 state_tracker.NotifyAlphaTest();
42 state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
43
44 glEnable(GL_CULL_FACE);
45 glDisable(GL_COLOR_LOGIC_OP);
46 glDisable(GL_DEPTH_TEST);
47 glDisable(GL_STENCIL_TEST);
48 glDisable(GL_POLYGON_OFFSET_FILL);
49 glDisable(GL_RASTERIZER_DISCARD);
50 glDisable(GL_ALPHA_TEST);
51 glDisablei(GL_BLEND, 0);
52 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
53 glCullFace(GL_BACK);
54 glFrontFace(GL_CW);
55 glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
56 glDepthRangeIndexed(0, 0.0, 0.0);
57
58 while (layers.size() < framebuffers.size()) {
59 layers.emplace_back(rasterizer, device_memory);
60 }
61
62 CreateWindowAdapt();
63 window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout);
64
65 // TODO
66 // program_manager.RestoreGuestPipeline();
67}
68
69void BlitScreen::CreateWindowAdapt() {
70 if (window_adapt && Settings::values.scaling_filter.GetValue() == current_window_adapt) {
71 return;
72 }
73
74 current_window_adapt = Settings::values.scaling_filter.GetValue();
75 switch (current_window_adapt) {
76 case Settings::ScalingFilter::NearestNeighbor:
77 window_adapt = MakeNearestNeighbor(device);
78 break;
79 case Settings::ScalingFilter::Bicubic:
80 window_adapt = MakeBicubic(device);
81 break;
82 case Settings::ScalingFilter::Gaussian:
83 window_adapt = MakeGaussian(device);
84 break;
85 case Settings::ScalingFilter::ScaleForce:
86 window_adapt = MakeScaleForce(device);
87 break;
88 case Settings::ScalingFilter::Fsr:
89 case Settings::ScalingFilter::Bilinear:
90 default:
91 window_adapt = MakeBilinear(device);
92 break;
93 }
94}
95
96} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_blit_screen.h b/src/video_core/renderer_opengl/gl_blit_screen.h
new file mode 100644
index 000000000..0c3d838f1
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_blit_screen.h
@@ -0,0 +1,71 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <list>
7#include <memory>
8#include <span>
9
10#include "core/hle/service/nvnflinger/pixel_format.h"
11#include "video_core/host1x/gpu_device_memory_manager.h"
12#include "video_core/renderer_opengl/gl_resource_manager.h"
13
14namespace Layout {
15struct FramebufferLayout;
16}
17
18namespace Tegra {
19struct FramebufferConfig;
20}
21
22namespace Settings {
23enum class ScalingFilter : u32;
24}
25
26namespace OpenGL {
27
28class Device;
29class Layer;
30class ProgramManager;
31class RasterizerOpenGL;
32class StateTracker;
33class WindowAdaptPass;
34
35/// Structure used for storing information about the display target for the Switch screen
36struct FramebufferTextureInfo {
37 GLuint display_texture{};
38 u32 width;
39 u32 height;
40 u32 scaled_width;
41 u32 scaled_height;
42};
43
44class BlitScreen {
45public:
46 explicit BlitScreen(RasterizerOpenGL& rasterizer,
47 Tegra::MaxwellDeviceMemoryManager& device_memory,
48 StateTracker& state_tracker, ProgramManager& program_manager,
49 Device& device);
50 ~BlitScreen();
51
52 /// Draws the emulated screens to the emulator window.
53 void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers,
54 const Layout::FramebufferLayout& layout);
55
56private:
57 void CreateWindowAdapt();
58
59 RasterizerOpenGL& rasterizer;
60 Tegra::MaxwellDeviceMemoryManager& device_memory;
61 StateTracker& state_tracker;
62 ProgramManager& program_manager;
63 Device& device;
64
65 Settings::ScalingFilter current_window_adapt{};
66 std::unique_ptr<WindowAdaptPass> window_adapt;
67
68 std::list<Layer> layers;
69};
70
71} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_fsr.cpp b/src/video_core/renderer_opengl/gl_fsr.cpp
deleted file mode 100644
index 77262dcf1..000000000
--- a/src/video_core/renderer_opengl/gl_fsr.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "video_core/fsr.h"
6#include "video_core/renderer_opengl/gl_fsr.h"
7#include "video_core/renderer_opengl/gl_shader_manager.h"
8#include "video_core/renderer_opengl/gl_shader_util.h"
9
10namespace OpenGL {
11using namespace FSR;
12
13using FsrConstants = std::array<u32, 4 * 4>;
14
15FSR::FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source,
16 std::string_view fsr_rcas_source)
17 : fsr_vertex{CreateProgram(fsr_vertex_source, GL_VERTEX_SHADER)},
18 fsr_easu_frag{CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER)},
19 fsr_rcas_frag{CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER)} {
20 glProgramUniform2f(fsr_vertex.handle, 0, 1.0f, 1.0f);
21 glProgramUniform2f(fsr_vertex.handle, 1, 0.0f, 0.0f);
22}
23
24FSR::~FSR() = default;
25
26void FSR::Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
27 u32 input_image_width, u32 input_image_height,
28 const Common::Rectangle<int>& crop_rect) {
29
30 const auto output_image_width = screen.GetWidth();
31 const auto output_image_height = screen.GetHeight();
32
33 if (fsr_intermediate_tex.handle) {
34 GLint fsr_tex_width, fsr_tex_height;
35 glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_WIDTH,
36 &fsr_tex_width);
37 glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_HEIGHT,
38 &fsr_tex_height);
39 if (static_cast<u32>(fsr_tex_width) != output_image_width ||
40 static_cast<u32>(fsr_tex_height) != output_image_height) {
41 fsr_intermediate_tex.Release();
42 }
43 }
44 if (!fsr_intermediate_tex.handle) {
45 fsr_intermediate_tex.Create(GL_TEXTURE_2D);
46 glTextureStorage2D(fsr_intermediate_tex.handle, 1, GL_RGB16F, output_image_width,
47 output_image_height);
48 glNamedFramebufferTexture(fsr_framebuffer.handle, GL_COLOR_ATTACHMENT0,
49 fsr_intermediate_tex.handle, 0);
50 }
51
52 GLint old_draw_fb;
53 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
54
55 glFrontFace(GL_CW);
56 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fsr_framebuffer.handle);
57 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(output_image_width),
58 static_cast<GLfloat>(output_image_height));
59
60 FsrConstants constants;
61 FsrEasuConOffset(
62 constants.data() + 0, constants.data() + 4, constants.data() + 8, constants.data() + 12,
63
64 static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()),
65 static_cast<f32>(input_image_width), static_cast<f32>(input_image_height),
66 static_cast<f32>(output_image_width), static_cast<f32>(output_image_height),
67 static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top));
68
69 glProgramUniform4uiv(fsr_easu_frag.handle, 0, sizeof(constants), std::data(constants));
70
71 program_manager.BindPresentPrograms(fsr_vertex.handle, fsr_easu_frag.handle);
72 glDrawArrays(GL_TRIANGLES, 0, 3);
73
74 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
75 glBindTextureUnit(0, fsr_intermediate_tex.handle);
76
77 const float sharpening =
78 static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
79
80 FsrRcasCon(constants.data(), sharpening);
81 glProgramUniform4uiv(fsr_rcas_frag.handle, 0, sizeof(constants), std::data(constants));
82}
83
84void FSR::InitBuffers() {
85 fsr_framebuffer.Create();
86}
87
88void FSR::ReleaseBuffers() {
89 fsr_framebuffer.Release();
90 fsr_intermediate_tex.Release();
91}
92
93const OGLProgram& FSR::GetPresentFragmentProgram() const noexcept {
94 return fsr_rcas_frag;
95}
96
97bool FSR::AreBuffersInitialized() const noexcept {
98 return fsr_framebuffer.handle;
99}
100
101} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_fsr.h b/src/video_core/renderer_opengl/gl_fsr.h
deleted file mode 100644
index 1f6ae3115..000000000
--- a/src/video_core/renderer_opengl/gl_fsr.h
+++ /dev/null
@@ -1,43 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string_view>
7
8#include "common/common_types.h"
9#include "common/math_util.h"
10#include "video_core/fsr.h"
11#include "video_core/renderer_opengl/gl_resource_manager.h"
12
13namespace OpenGL {
14
15class ProgramManager;
16
17class FSR {
18public:
19 explicit FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source,
20 std::string_view fsr_rcas_source);
21 ~FSR();
22
23 void Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen,
24 u32 input_image_width, u32 input_image_height,
25 const Common::Rectangle<int>& crop_rect);
26
27 void InitBuffers();
28
29 void ReleaseBuffers();
30
31 [[nodiscard]] const OGLProgram& GetPresentFragmentProgram() const noexcept;
32
33 [[nodiscard]] bool AreBuffersInitialized() const noexcept;
34
35private:
36 OGLFramebuffer fsr_framebuffer;
37 OGLProgram fsr_vertex;
38 OGLProgram fsr_easu_frag;
39 OGLProgram fsr_rcas_frag;
40 OGLTexture fsr_intermediate_tex;
41};
42
43} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index d5354ef2d..b42fb110c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -71,10 +71,10 @@ std::optional<VideoCore::QueryType> MaxwellToVideoCoreQuery(VideoCommon::QueryTy
71 71
72RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 72RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
73 Tegra::MaxwellDeviceMemoryManager& device_memory_, 73 Tegra::MaxwellDeviceMemoryManager& device_memory_,
74 const Device& device_, ScreenInfo& screen_info_, 74 const Device& device_, ProgramManager& program_manager_,
75 ProgramManager& program_manager_, StateTracker& state_tracker_) 75 StateTracker& state_tracker_)
76 : gpu(gpu_), device_memory(device_memory_), device(device_), screen_info(screen_info_), 76 : gpu(gpu_), device_memory(device_memory_), device(device_), program_manager(program_manager_),
77 program_manager(program_manager_), state_tracker(state_tracker_), 77 state_tracker(state_tracker_),
78 texture_cache_runtime(device, program_manager, state_tracker, staging_buffer_pool), 78 texture_cache_runtime(device, program_manager, state_tracker, staging_buffer_pool),
79 texture_cache(texture_cache_runtime, device_memory_), 79 texture_cache(texture_cache_runtime, device_memory_),
80 buffer_cache_runtime(device, staging_buffer_pool), 80 buffer_cache_runtime(device, staging_buffer_pool),
@@ -739,27 +739,29 @@ void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si
739 query_cache.InvalidateRegion(*cpu_addr, copy_size); 739 query_cache.InvalidateRegion(*cpu_addr, copy_size);
740} 740}
741 741
742bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, 742std::optional<FramebufferTextureInfo> RasterizerOpenGL::AccelerateDisplay(
743 DAddr framebuffer_addr, u32 pixel_stride) { 743 const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, u32 pixel_stride) {
744 if (framebuffer_addr == 0) { 744 if (framebuffer_addr == 0) {
745 return false; 745 return {};
746 } 746 }
747 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 747 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
748 748
749 std::scoped_lock lock{texture_cache.mutex}; 749 std::scoped_lock lock{texture_cache.mutex};
750 ImageView* const image_view{ 750 const auto [image_view, scaled] =
751 texture_cache.TryFindFramebufferImageView(config, framebuffer_addr)}; 751 texture_cache.TryFindFramebufferImageView(config, framebuffer_addr);
752 if (!image_view) { 752 if (!image_view) {
753 return false; 753 return {};
754 } 754 }
755 // Verify that the cached surface is the same size and format as the requested framebuffer
756 // ASSERT_MSG(image_view->size.width == config.width, "Framebuffer width is different");
757 // ASSERT_MSG(image_view->size.height == config.height, "Framebuffer height is different");
758 755
759 screen_info.texture.width = image_view->size.width; 756 const auto& resolution = Settings::values.resolution_info;
760 screen_info.texture.height = image_view->size.height; 757
761 screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D); 758 FramebufferTextureInfo info{};
762 return true; 759 info.display_texture = image_view->Handle(Shader::TextureType::Color2D);
760 info.width = image_view->size.width;
761 info.height = image_view->size.height;
762 info.scaled_width = scaled ? resolution.ScaleUp(info.width) : info.width;
763 info.scaled_height = scaled ? resolution.ScaleUp(info.height) : info.height;
764 return info;
763} 765}
764 766
765void RasterizerOpenGL::SyncState() { 767void RasterizerOpenGL::SyncState() {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 34aa73526..6eae51ff7 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -16,6 +16,7 @@
16#include "video_core/engines/maxwell_dma.h" 16#include "video_core/engines/maxwell_dma.h"
17#include "video_core/rasterizer_interface.h" 17#include "video_core/rasterizer_interface.h"
18#include "video_core/renderer_opengl/blit_image.h" 18#include "video_core/renderer_opengl/blit_image.h"
19#include "video_core/renderer_opengl/gl_blit_screen.h"
19#include "video_core/renderer_opengl/gl_buffer_cache.h" 20#include "video_core/renderer_opengl/gl_buffer_cache.h"
20#include "video_core/renderer_opengl/gl_device.h" 21#include "video_core/renderer_opengl/gl_device.h"
21#include "video_core/renderer_opengl/gl_fence_manager.h" 22#include "video_core/renderer_opengl/gl_fence_manager.h"
@@ -37,7 +38,7 @@ class MemoryManager;
37 38
38namespace OpenGL { 39namespace OpenGL {
39 40
40struct ScreenInfo; 41struct FramebufferTextureInfo;
41struct ShaderEntries; 42struct ShaderEntries;
42 43
43struct BindlessSSBO { 44struct BindlessSSBO {
@@ -76,8 +77,8 @@ class RasterizerOpenGL : public VideoCore::RasterizerInterface,
76public: 77public:
77 explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 78 explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
78 Tegra::MaxwellDeviceMemoryManager& device_memory_, 79 Tegra::MaxwellDeviceMemoryManager& device_memory_,
79 const Device& device_, ScreenInfo& screen_info_, 80 const Device& device_, ProgramManager& program_manager_,
80 ProgramManager& program_manager_, StateTracker& state_tracker_); 81 StateTracker& state_tracker_);
81 ~RasterizerOpenGL() override; 82 ~RasterizerOpenGL() override;
82 83
83 void Draw(bool is_indexed, u32 instance_count) override; 84 void Draw(bool is_indexed, u32 instance_count) override;
@@ -122,8 +123,6 @@ public:
122 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; 123 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override;
123 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 124 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
124 std::span<const u8> memory) override; 125 std::span<const u8> memory) override;
125 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, DAddr framebuffer_addr,
126 u32 pixel_stride) override;
127 void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 126 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
128 const VideoCore::DiskResourceLoadCallback& callback) override; 127 const VideoCore::DiskResourceLoadCallback& callback) override;
129 128
@@ -144,6 +143,10 @@ public:
144 return true; 143 return true;
145 } 144 }
146 145
146 std::optional<FramebufferTextureInfo> AccelerateDisplay(const Tegra::FramebufferConfig& config,
147 VAddr framebuffer_addr,
148 u32 pixel_stride);
149
147private: 150private:
148 static constexpr size_t MAX_TEXTURES = 192; 151 static constexpr size_t MAX_TEXTURES = 192;
149 static constexpr size_t MAX_IMAGES = 48; 152 static constexpr size_t MAX_IMAGES = 48;
@@ -237,7 +240,6 @@ private:
237 Tegra::MaxwellDeviceMemoryManager& device_memory; 240 Tegra::MaxwellDeviceMemoryManager& device_memory;
238 241
239 const Device& device; 242 const Device& device;
240 ScreenInfo& screen_info;
241 ProgramManager& program_manager; 243 ProgramManager& program_manager;
242 StateTracker& state_tracker; 244 StateTracker& state_tracker;
243 245
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 66a5ca03e..be14494ca 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -1051,6 +1051,10 @@ void Image::Scale(bool up_scale) {
1051 state_tracker.NotifyScissor0(); 1051 state_tracker.NotifyScissor0();
1052} 1052}
1053 1053
1054bool Image::IsRescaled() const {
1055 return True(flags & ImageFlagBits::Rescaled);
1056}
1057
1054bool Image::ScaleUp(bool ignore) { 1058bool Image::ScaleUp(bool ignore) {
1055 const auto& resolution = runtime->resolution; 1059 const auto& resolution = runtime->resolution;
1056 if (!resolution.active) { 1060 if (!resolution.active) {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 34870c81f..3e54edcc2 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -217,6 +217,8 @@ public:
217 return gl_type; 217 return gl_type;
218 } 218 }
219 219
220 bool IsRescaled() const;
221
220 bool ScaleUp(bool ignore = false); 222 bool ScaleUp(bool ignore = false);
221 223
222 bool ScaleDown(bool ignore = false); 224 bool ScaleDown(bool ignore = false);
diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp
new file mode 100644
index 000000000..819e5d77f
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/filters.cpp
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/host_shaders/opengl_present_frag.h"
5#include "video_core/host_shaders/opengl_present_scaleforce_frag.h"
6#include "video_core/host_shaders/present_bicubic_frag.h"
7#include "video_core/host_shaders/present_gaussian_frag.h"
8#include "video_core/renderer_opengl/present/filters.h"
9#include "video_core/renderer_opengl/present/util.h"
10
11namespace OpenGL {
12
13std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device) {
14 return std::make_unique<WindowAdaptPass>(device, CreateNearestNeighborSampler(),
15 HostShaders::OPENGL_PRESENT_FRAG);
16}
17
18std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device) {
19 return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(),
20 HostShaders::OPENGL_PRESENT_FRAG);
21}
22
23std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device) {
24 return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(),
25 HostShaders::PRESENT_BICUBIC_FRAG);
26}
27
28std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device) {
29 return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(),
30 HostShaders::PRESENT_GAUSSIAN_FRAG);
31}
32
33std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device) {
34 return std::make_unique<WindowAdaptPass>(
35 device, CreateBilinearSampler(),
36 fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG));
37}
38
39} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h
new file mode 100644
index 000000000..122ab7436
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/filters.h
@@ -0,0 +1,17 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include "video_core/renderer_opengl/present/window_adapt_pass.h"
8
9namespace OpenGL {
10
11std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device);
12std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device);
13std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device);
14std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device);
15std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device);
16
17} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/fsr.cpp b/src/video_core/renderer_opengl/present/fsr.cpp
new file mode 100644
index 000000000..b764aadae
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/fsr.cpp
@@ -0,0 +1,98 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "video_core/fsr.h"
6#include "video_core/host_shaders/ffx_a_h.h"
7#include "video_core/host_shaders/ffx_fsr1_h.h"
8#include "video_core/host_shaders/full_screen_triangle_vert.h"
9#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h"
10#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h"
11#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h"
12#include "video_core/renderer_opengl/gl_shader_manager.h"
13#include "video_core/renderer_opengl/gl_shader_util.h"
14#include "video_core/renderer_opengl/present/fsr.h"
15#include "video_core/renderer_opengl/present/util.h"
16
17namespace OpenGL {
18using namespace FSR;
19
20using FsrConstants = std::array<u32, 4 * 4>;
21
22FSR::FSR(u32 output_width_, u32 output_height_) : width(output_width_), height(output_height_) {
23 std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG};
24 ReplaceInclude(fsr_source, "ffx_a.h", HostShaders::FFX_A_H);
25 ReplaceInclude(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H);
26
27 std::string fsr_easu_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG};
28 std::string fsr_rcas_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG};
29 ReplaceInclude(fsr_easu_source, "opengl_fidelityfx_fsr.frag", fsr_source);
30 ReplaceInclude(fsr_rcas_source, "opengl_fidelityfx_fsr.frag", fsr_source);
31
32 vert = CreateProgram(HostShaders::FULL_SCREEN_TRIANGLE_VERT, GL_VERTEX_SHADER);
33 easu_frag = CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER);
34 rcas_frag = CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER);
35
36 glProgramUniform2f(vert.handle, 0, 1.0f, -1.0f);
37 glProgramUniform2f(vert.handle, 1, 0.0f, 1.0f);
38
39 sampler = CreateBilinearSampler();
40 framebuffer.Create();
41
42 easu_tex.Create(GL_TEXTURE_2D);
43 glTextureStorage2D(easu_tex.handle, 1, GL_RGBA16F, width, height);
44
45 rcas_tex.Create(GL_TEXTURE_2D);
46 glTextureStorage2D(rcas_tex.handle, 1, GL_RGBA16F, width, height);
47}
48
49FSR::~FSR() = default;
50
51GLuint FSR::Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width,
52 u32 input_image_height, const Common::Rectangle<f32>& crop_rect) {
53 const f32 input_width = static_cast<f32>(input_image_width);
54 const f32 input_height = static_cast<f32>(input_image_height);
55 const f32 output_width = static_cast<f32>(width);
56 const f32 output_height = static_cast<f32>(height);
57 const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_width;
58 const f32 viewport_x = crop_rect.left * input_width;
59 const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_height;
60 const f32 viewport_y = crop_rect.top * input_height;
61
62 FsrConstants easu_con{};
63 FsrConstants rcas_con{};
64
65 FsrEasuConOffset(easu_con.data() + 0, easu_con.data() + 4, easu_con.data() + 8,
66 easu_con.data() + 12, viewport_width, viewport_height, input_width,
67 input_height, output_width, output_height, viewport_x, viewport_y);
68
69 const float sharpening =
70 static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
71
72 FsrRcasCon(rcas_con.data(), sharpening);
73
74 glProgramUniform4uiv(easu_frag.handle, 0, sizeof(easu_con), easu_con.data());
75 glProgramUniform4uiv(rcas_frag.handle, 0, sizeof(rcas_con), rcas_con.data());
76
77 glFrontFace(GL_CW);
78 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
79 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, easu_tex.handle, 0);
80 glViewportIndexedf(0, 0.0f, 0.0f, output_width, output_height);
81 program_manager.BindPresentPrograms(vert.handle, easu_frag.handle);
82 glBindTextureUnit(0, texture);
83 glBindSampler(0, sampler.handle);
84 glDrawArrays(GL_TRIANGLES, 0, 3);
85
86 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, rcas_tex.handle, 0);
87 program_manager.BindPresentPrograms(vert.handle, rcas_frag.handle);
88 glBindTextureUnit(0, easu_tex.handle);
89 glDrawArrays(GL_TRIANGLES, 0, 3);
90
91 return rcas_tex.handle;
92}
93
94bool FSR::NeedsRecreation(const Common::Rectangle<u32>& screen) {
95 return screen.GetWidth() != width || screen.GetHeight() != height;
96}
97
98} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/fsr.h b/src/video_core/renderer_opengl/present/fsr.h
new file mode 100644
index 000000000..606935a01
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/fsr.h
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string_view>
7
8#include "common/common_types.h"
9#include "common/math_util.h"
10#include "video_core/fsr.h"
11#include "video_core/renderer_opengl/gl_resource_manager.h"
12
13namespace OpenGL {
14
15class ProgramManager;
16
17class FSR {
18public:
19 explicit FSR(u32 output_width, u32 output_height);
20 ~FSR();
21
22 GLuint Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width,
23 u32 input_image_height, const Common::Rectangle<f32>& crop_rect);
24
25 bool NeedsRecreation(const Common::Rectangle<u32>& screen);
26
27private:
28 const u32 width;
29 const u32 height;
30 OGLFramebuffer framebuffer;
31 OGLSampler sampler;
32 OGLProgram vert;
33 OGLProgram easu_frag;
34 OGLProgram rcas_frag;
35 OGLTexture easu_tex;
36 OGLTexture rcas_tex;
37};
38
39} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/fxaa.cpp b/src/video_core/renderer_opengl/present/fxaa.cpp
new file mode 100644
index 000000000..d9b58512d
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/fxaa.cpp
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/host_shaders/fxaa_frag.h"
5#include "video_core/host_shaders/fxaa_vert.h"
6#include "video_core/renderer_opengl/gl_shader_manager.h"
7#include "video_core/renderer_opengl/gl_shader_util.h"
8#include "video_core/renderer_opengl/present/fxaa.h"
9#include "video_core/renderer_opengl/present/util.h"
10
11namespace OpenGL {
12
13FXAA::FXAA(u32 width, u32 height) {
14 vert_shader = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER);
15 frag_shader = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER);
16
17 sampler = CreateBilinearSampler();
18
19 framebuffer.Create();
20
21 texture.Create(GL_TEXTURE_2D);
22 glTextureStorage2D(texture.handle, 1, GL_RGBA16F, width, height);
23 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0);
24}
25
26FXAA::~FXAA() = default;
27
28GLuint FXAA::Draw(ProgramManager& program_manager, GLuint input_texture) {
29 glFrontFace(GL_CCW);
30
31 program_manager.BindPresentPrograms(vert_shader.handle, frag_shader.handle);
32 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
33 glBindTextureUnit(0, input_texture);
34 glBindSampler(0, sampler.handle);
35 glDrawArrays(GL_TRIANGLES, 0, 3);
36 glFrontFace(GL_CW);
37
38 return texture.handle;
39}
40
41} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/fxaa.h b/src/video_core/renderer_opengl/present/fxaa.h
new file mode 100644
index 000000000..b898198f1
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/fxaa.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "video_core/renderer_opengl/gl_resource_manager.h"
7
8namespace OpenGL {
9
10class ProgramManager;
11
12class FXAA {
13public:
14 explicit FXAA(u32 width, u32 height);
15 ~FXAA();
16
17 GLuint Draw(ProgramManager& program_manager, GLuint input_texture);
18
19private:
20 OGLProgram vert_shader;
21 OGLProgram frag_shader;
22 OGLSampler sampler;
23 OGLFramebuffer framebuffer;
24 OGLTexture texture;
25};
26
27} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/layer.cpp b/src/video_core/renderer_opengl/present/layer.cpp
new file mode 100644
index 000000000..8643e07c6
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/layer.cpp
@@ -0,0 +1,215 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/framebuffer_config.h"
5#include "video_core/renderer_opengl/gl_blit_screen.h"
6#include "video_core/renderer_opengl/gl_rasterizer.h"
7#include "video_core/renderer_opengl/present/fsr.h"
8#include "video_core/renderer_opengl/present/fxaa.h"
9#include "video_core/renderer_opengl/present/layer.h"
10#include "video_core/renderer_opengl/present/present_uniforms.h"
11#include "video_core/renderer_opengl/present/smaa.h"
12#include "video_core/surface.h"
13#include "video_core/textures/decoders.h"
14
15namespace OpenGL {
16
17Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_)
18 : rasterizer(rasterizer_), device_memory(device_memory_) {
19 // Allocate textures for the screen
20 framebuffer_texture.resource.Create(GL_TEXTURE_2D);
21
22 const GLuint texture = framebuffer_texture.resource.handle;
23 glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1);
24
25 // Clear screen to black
26 const u8 framebuffer_data[4] = {0, 0, 0, 0};
27 glClearTexImage(framebuffer_texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE,
28 framebuffer_data);
29}
30
31Layer::~Layer() = default;
32
33GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
34 std::array<ScreenRectVertex, 4>& out_vertices,
35 ProgramManager& program_manager,
36 const Tegra::FramebufferConfig& framebuffer,
37 const Layout::FramebufferLayout& layout) {
38 FramebufferTextureInfo info = PrepareRenderTarget(framebuffer);
39 auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height);
40 GLuint texture = info.display_texture;
41
42 auto anti_aliasing = Settings::values.anti_aliasing.GetValue();
43 if (anti_aliasing != Settings::AntiAliasing::None) {
44 glEnablei(GL_SCISSOR_TEST, 0);
45 auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width);
46 auto viewport_height = Settings::values.resolution_info.ScaleUp(framebuffer_texture.height);
47
48 glScissorIndexed(0, 0, 0, viewport_width, viewport_height);
49 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(viewport_width),
50 static_cast<GLfloat>(viewport_height));
51
52 switch (anti_aliasing) {
53 case Settings::AntiAliasing::Fxaa:
54 CreateFXAA();
55 texture = fxaa->Draw(program_manager, info.display_texture);
56 break;
57 case Settings::AntiAliasing::Smaa:
58 default:
59 CreateSMAA();
60 texture = smaa->Draw(program_manager, info.display_texture);
61 break;
62 }
63 }
64
65 glDisablei(GL_SCISSOR_TEST, 0);
66
67 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
68 if (!fsr || fsr->NeedsRecreation(layout.screen)) {
69 fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight());
70 }
71
72 texture = fsr->Draw(program_manager, texture, info.scaled_width, info.scaled_height, crop);
73 crop = {0, 0, 1, 1};
74 }
75
76 out_matrix =
77 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
78
79 // Map the coordinates to the screen.
80 const auto& screen = layout.screen;
81 const auto x = screen.left;
82 const auto y = screen.top;
83 const auto w = screen.GetWidth();
84 const auto h = screen.GetHeight();
85
86 out_vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top);
87 out_vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top);
88 out_vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom);
89 out_vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom);
90
91 return texture;
92}
93
94FramebufferTextureInfo Layer::PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer) {
95 // If framebuffer is provided, reload it from memory to a texture
96 if (framebuffer_texture.width != static_cast<GLsizei>(framebuffer.width) ||
97 framebuffer_texture.height != static_cast<GLsizei>(framebuffer.height) ||
98 framebuffer_texture.pixel_format != framebuffer.pixel_format ||
99 gl_framebuffer_data.empty()) {
100 // Reallocate texture if the framebuffer size has changed.
101 // This is expected to not happen very often and hence should not be a
102 // performance problem.
103 ConfigureFramebufferTexture(framebuffer);
104 }
105
106 // Load the framebuffer from memory if needed
107 return LoadFBToScreenInfo(framebuffer);
108}
109
110FramebufferTextureInfo Layer::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
111 const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset};
112 const auto accelerated_info =
113 rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride);
114 if (accelerated_info) {
115 return *accelerated_info;
116 }
117
118 // Reset the screen info's display texture to its own permanent texture
119 FramebufferTextureInfo info{};
120 info.display_texture = framebuffer_texture.resource.handle;
121 info.width = framebuffer.width;
122 info.height = framebuffer.height;
123 info.scaled_width = framebuffer.width;
124 info.scaled_height = framebuffer.height;
125
126 // TODO(Rodrigo): Read this from HLE
127 constexpr u32 block_height_log2 = 4;
128 const auto pixel_format{
129 VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
130 const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)};
131 const u64 size_in_bytes{Tegra::Texture::CalculateSize(
132 true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
133 const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)};
134 const std::span<const u8> input_data(host_ptr, size_in_bytes);
135 Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
136 framebuffer.width, framebuffer.height, 1, block_height_log2,
137 0);
138
139 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
140 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
141
142 // Update existing texture
143 // TODO: Test what happens on hardware when you change the framebuffer dimensions so that
144 // they differ from the LCD resolution.
145 // TODO: Applications could theoretically crash yuzu here by specifying too large
146 // framebuffer sizes. We should make sure that this cannot happen.
147 glTextureSubImage2D(framebuffer_texture.resource.handle, 0, 0, 0, framebuffer.width,
148 framebuffer.height, framebuffer_texture.gl_format,
149 framebuffer_texture.gl_type, gl_framebuffer_data.data());
150
151 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
152
153 return info;
154}
155
156void Layer::ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer) {
157 framebuffer_texture.width = framebuffer.width;
158 framebuffer_texture.height = framebuffer.height;
159 framebuffer_texture.pixel_format = framebuffer.pixel_format;
160
161 const auto pixel_format{
162 VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
163 const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)};
164 gl_framebuffer_data.resize(framebuffer_texture.width * framebuffer_texture.height *
165 bytes_per_pixel);
166
167 GLint internal_format;
168 switch (framebuffer.pixel_format) {
169 case Service::android::PixelFormat::Rgba8888:
170 internal_format = GL_RGBA8;
171 framebuffer_texture.gl_format = GL_RGBA;
172 framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
173 break;
174 case Service::android::PixelFormat::Rgb565:
175 internal_format = GL_RGB565;
176 framebuffer_texture.gl_format = GL_RGB;
177 framebuffer_texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
178 break;
179 default:
180 internal_format = GL_RGBA8;
181 framebuffer_texture.gl_format = GL_RGBA;
182 framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
183 // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
184 // static_cast<u32>(framebuffer.pixel_format));
185 break;
186 }
187
188 framebuffer_texture.resource.Release();
189 framebuffer_texture.resource.Create(GL_TEXTURE_2D);
190 glTextureStorage2D(framebuffer_texture.resource.handle, 1, internal_format,
191 framebuffer_texture.width, framebuffer_texture.height);
192
193 fxaa.reset();
194 smaa.reset();
195}
196
197void Layer::CreateFXAA() {
198 smaa.reset();
199 if (!fxaa) {
200 fxaa = std::make_unique<FXAA>(
201 Settings::values.resolution_info.ScaleUp(framebuffer_texture.width),
202 Settings::values.resolution_info.ScaleUp(framebuffer_texture.height));
203 }
204}
205
206void Layer::CreateSMAA() {
207 fxaa.reset();
208 if (!smaa) {
209 smaa = std::make_unique<SMAA>(
210 Settings::values.resolution_info.ScaleUp(framebuffer_texture.width),
211 Settings::values.resolution_info.ScaleUp(framebuffer_texture.height));
212 }
213}
214
215} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/layer.h b/src/video_core/renderer_opengl/present/layer.h
new file mode 100644
index 000000000..ef1055abf
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/layer.h
@@ -0,0 +1,80 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <vector>
8
9#include "video_core/host1x/gpu_device_memory_manager.h"
10#include "video_core/renderer_opengl/gl_resource_manager.h"
11
12namespace Layout {
13struct FramebufferLayout;
14}
15
16namespace Service::android {
17enum class PixelFormat : u32;
18};
19
20namespace Tegra {
21struct FramebufferConfig;
22}
23
24namespace OpenGL {
25
26struct FramebufferTextureInfo;
27class FSR;
28class FXAA;
29class ProgramManager;
30class RasterizerOpenGL;
31class SMAA;
32
33/// Structure used for storing information about the textures for the Switch screen
34struct TextureInfo {
35 OGLTexture resource;
36 GLsizei width;
37 GLsizei height;
38 GLenum gl_format;
39 GLenum gl_type;
40 Service::android::PixelFormat pixel_format;
41};
42
43struct ScreenRectVertex;
44
45class Layer {
46public:
47 explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory);
48 ~Layer();
49
50 GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
51 std::array<ScreenRectVertex, 4>& out_vertices,
52 ProgramManager& program_manager,
53 const Tegra::FramebufferConfig& framebuffer,
54 const Layout::FramebufferLayout& layout);
55
56private:
57 /// Loads framebuffer from emulated memory into the active OpenGL texture.
58 FramebufferTextureInfo LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
59 FramebufferTextureInfo PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer);
60 void ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer);
61
62 void CreateFXAA();
63 void CreateSMAA();
64
65private:
66 RasterizerOpenGL& rasterizer;
67 Tegra::MaxwellDeviceMemoryManager& device_memory;
68
69 /// OpenGL framebuffer data
70 std::vector<u8> gl_framebuffer_data;
71
72 /// Display information for Switch screen
73 TextureInfo framebuffer_texture;
74
75 std::unique_ptr<FSR> fsr;
76 std::unique_ptr<FXAA> fxaa;
77 std::unique_ptr<SMAA> smaa;
78};
79
80} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/present_uniforms.h b/src/video_core/renderer_opengl/present/present_uniforms.h
new file mode 100644
index 000000000..3a19f05c7
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/present_uniforms.h
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "video_core/renderer_opengl/gl_resource_manager.h"
7
8namespace OpenGL {
9
10constexpr GLint PositionLocation = 0;
11constexpr GLint TexCoordLocation = 1;
12constexpr GLint ModelViewMatrixLocation = 0;
13
14struct ScreenRectVertex {
15 constexpr ScreenRectVertex() = default;
16
17 constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v)
18 : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {}
19
20 std::array<GLfloat, 2> position{};
21 std::array<GLfloat, 2> tex_coord{};
22};
23
24/**
25 * Defines a 1:1 pixel orthographic projection matrix with (0,0) on the top-left
26 * corner and (width, height) on the lower-bottom.
27 *
28 * The projection part of the matrix is trivial, hence these operations are represented
29 * by a 3x2 matrix.
30 */
31static inline std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) {
32 std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order
33
34 // clang-format off
35 matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
36 matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;
37 // Last matrix row is implicitly assumed to be [0, 0, 1].
38 // clang-format on
39
40 return matrix;
41}
42
43} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/smaa.cpp b/src/video_core/renderer_opengl/present/smaa.cpp
new file mode 100644
index 000000000..de7f6e502
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/smaa.cpp
@@ -0,0 +1,102 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/host_shaders/opengl_smaa_glsl.h"
5#include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h"
6#include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h"
7#include "video_core/host_shaders/smaa_edge_detection_frag.h"
8#include "video_core/host_shaders/smaa_edge_detection_vert.h"
9#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h"
10#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h"
11#include "video_core/renderer_opengl/gl_shader_manager.h"
12#include "video_core/renderer_opengl/gl_shader_util.h"
13#include "video_core/renderer_opengl/present/smaa.h"
14#include "video_core/renderer_opengl/present/util.h"
15#include "video_core/smaa_area_tex.h"
16#include "video_core/smaa_search_tex.h"
17
18namespace OpenGL {
19
20SMAA::SMAA(u32 width, u32 height) {
21 const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) {
22 std::string shader_source{specialized_source};
23 ReplaceInclude(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL);
24 return CreateProgram(shader_source, stage);
25 };
26
27 edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER);
28 edge_detection_frag = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER);
29 blending_weight_calculation_vert =
30 SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER);
31 blending_weight_calculation_frag =
32 SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER);
33 neighborhood_blending_vert =
34 SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER);
35 neighborhood_blending_frag =
36 SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER);
37
38 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
39 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
40
41 area_tex.Create(GL_TEXTURE_2D);
42 glTextureStorage2D(area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT);
43 glTextureSubImage2D(area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG,
44 GL_UNSIGNED_BYTE, areaTexBytes);
45 search_tex.Create(GL_TEXTURE_2D);
46 glTextureStorage2D(search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT);
47 glTextureSubImage2D(search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED,
48 GL_UNSIGNED_BYTE, searchTexBytes);
49
50 edges_tex.Create(GL_TEXTURE_2D);
51 glTextureStorage2D(edges_tex.handle, 1, GL_RG16F, width, height);
52
53 blend_tex.Create(GL_TEXTURE_2D);
54 glTextureStorage2D(blend_tex.handle, 1, GL_RGBA16F, width, height);
55
56 sampler = CreateBilinearSampler();
57
58 framebuffer.Create();
59
60 texture.Create(GL_TEXTURE_2D);
61 glTextureStorage2D(texture.handle, 1, GL_RGBA16F, width, height);
62 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0);
63}
64
65SMAA::~SMAA() = default;
66
67GLuint SMAA::Draw(ProgramManager& program_manager, GLuint input_texture) {
68 glClearColor(0, 0, 0, 0);
69 glFrontFace(GL_CCW);
70 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
71 glBindSampler(0, sampler.handle);
72 glBindSampler(1, sampler.handle);
73 glBindSampler(2, sampler.handle);
74
75 glBindTextureUnit(0, input_texture);
76 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, edges_tex.handle, 0);
77 glClear(GL_COLOR_BUFFER_BIT);
78 program_manager.BindPresentPrograms(edge_detection_vert.handle, edge_detection_frag.handle);
79 glDrawArrays(GL_TRIANGLES, 0, 3);
80
81 glBindTextureUnit(0, edges_tex.handle);
82 glBindTextureUnit(1, area_tex.handle);
83 glBindTextureUnit(2, search_tex.handle);
84 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, blend_tex.handle, 0);
85 glClear(GL_COLOR_BUFFER_BIT);
86 program_manager.BindPresentPrograms(blending_weight_calculation_vert.handle,
87 blending_weight_calculation_frag.handle);
88 glDrawArrays(GL_TRIANGLES, 0, 3);
89
90 glBindTextureUnit(0, input_texture);
91 glBindTextureUnit(1, blend_tex.handle);
92 glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0);
93 program_manager.BindPresentPrograms(neighborhood_blending_vert.handle,
94 neighborhood_blending_frag.handle);
95 glClear(GL_COLOR_BUFFER_BIT);
96 glDrawArrays(GL_TRIANGLES, 0, 3);
97 glFrontFace(GL_CW);
98
99 return texture.handle;
100}
101
102} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/smaa.h b/src/video_core/renderer_opengl/present/smaa.h
new file mode 100644
index 000000000..a48cb4fa9
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/smaa.h
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "video_core/renderer_opengl/gl_resource_manager.h"
7
8namespace OpenGL {
9
10class ProgramManager;
11
12class SMAA {
13public:
14 explicit SMAA(u32 width, u32 height);
15 ~SMAA();
16
17 GLuint Draw(ProgramManager& program_manager, GLuint input_texture);
18
19private:
20 OGLProgram edge_detection_vert;
21 OGLProgram blending_weight_calculation_vert;
22 OGLProgram neighborhood_blending_vert;
23 OGLProgram edge_detection_frag;
24 OGLProgram blending_weight_calculation_frag;
25 OGLProgram neighborhood_blending_frag;
26 OGLTexture area_tex;
27 OGLTexture search_tex;
28 OGLTexture edges_tex;
29 OGLTexture blend_tex;
30 OGLSampler sampler;
31 OGLFramebuffer framebuffer;
32 OGLTexture texture;
33};
34
35} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/util.h b/src/video_core/renderer_opengl/present/util.h
new file mode 100644
index 000000000..67f03aa27
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/util.h
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "common/assert.h"
9#include "video_core/renderer_opengl/gl_resource_manager.h"
10
11namespace OpenGL {
12
13static inline void ReplaceInclude(std::string& shader_source, std::string_view include_name,
14 std::string_view include_content) {
15 const std::string include_string = fmt::format("#include \"{}\"", include_name);
16 const std::size_t pos = shader_source.find(include_string);
17 ASSERT(pos != std::string::npos);
18 shader_source.replace(pos, include_string.size(), include_content);
19};
20
21static inline OGLSampler CreateBilinearSampler() {
22 OGLSampler sampler;
23 sampler.Create();
24 glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
25 glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
26 glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
27 glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
28 glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
29 return sampler;
30}
31
32static inline OGLSampler CreateNearestNeighborSampler() {
33 OGLSampler sampler;
34 sampler.Create();
35 glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
36 glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
37 glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
38 glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
39 glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
40 return sampler;
41}
42
43} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp
new file mode 100644
index 000000000..4d681606b
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp
@@ -0,0 +1,103 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/settings.h"
5#include "video_core/framebuffer_config.h"
6#include "video_core/host_shaders/opengl_present_vert.h"
7#include "video_core/renderer_opengl/gl_device.h"
8#include "video_core/renderer_opengl/gl_shader_manager.h"
9#include "video_core/renderer_opengl/gl_shader_util.h"
10#include "video_core/renderer_opengl/present/layer.h"
11#include "video_core/renderer_opengl/present/present_uniforms.h"
12#include "video_core/renderer_opengl/present/window_adapt_pass.h"
13
14namespace OpenGL {
15
16WindowAdaptPass::WindowAdaptPass(const Device& device_, OGLSampler&& sampler_,
17 std::string_view frag_source)
18 : device(device_), sampler(std::move(sampler_)) {
19 vert = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
20 frag = CreateProgram(frag_source, GL_FRAGMENT_SHADER);
21
22 // Generate VBO handle for drawing
23 vertex_buffer.Create();
24
25 // Attach vertex data to VAO
26 glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
27
28 // Query vertex buffer address when the driver supports unified vertex attributes
29 if (device.HasVertexBufferUnifiedMemory()) {
30 glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY);
31 glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV,
32 &vertex_buffer_address);
33 }
34}
35
36WindowAdaptPass::~WindowAdaptPass() = default;
37
38void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
39 std::span<const Tegra::FramebufferConfig> framebuffers,
40 const Layout::FramebufferLayout& layout) {
41 GLint old_read_fb;
42 GLint old_draw_fb;
43 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
44 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
45
46 const size_t layer_count = framebuffers.size();
47 std::vector<GLuint> textures(layer_count);
48 std::vector<std::array<GLfloat, 3 * 2>> matrices(layer_count);
49 std::vector<std::array<ScreenRectVertex, 4>> vertices(layer_count);
50
51 auto layer_it = layers.begin();
52 for (size_t i = 0; i < layer_count; i++) {
53 textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager,
54 framebuffers[i], layout);
55 layer_it++;
56 }
57
58 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
59 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
60
61 program_manager.BindPresentPrograms(vert.handle, frag.handle);
62
63 glDisable(GL_FRAMEBUFFER_SRGB);
64 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
65 static_cast<GLfloat>(layout.height));
66
67 glEnableVertexAttribArray(PositionLocation);
68 glEnableVertexAttribArray(TexCoordLocation);
69 glVertexAttribDivisor(PositionLocation, 0);
70 glVertexAttribDivisor(TexCoordLocation, 0);
71 glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
72 offsetof(ScreenRectVertex, position));
73 glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE,
74 offsetof(ScreenRectVertex, tex_coord));
75 glVertexAttribBinding(PositionLocation, 0);
76 glVertexAttribBinding(TexCoordLocation, 0);
77 if (device.HasVertexBufferUnifiedMemory()) {
78 glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex));
79 glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address,
80 sizeof(decltype(vertices)::value_type));
81 } else {
82 glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
83 }
84
85 glBindSampler(0, sampler.handle);
86
87 // Update background color before drawing
88 glClearColor(Settings::values.bg_red.GetValue() / 255.0f,
89 Settings::values.bg_green.GetValue() / 255.0f,
90 Settings::values.bg_blue.GetValue() / 255.0f, 1.0f);
91
92 glClear(GL_COLOR_BUFFER_BIT);
93
94 for (size_t i = 0; i < layer_count; i++) {
95 glBindTextureUnit(0, textures[i]);
96 glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE,
97 matrices[i].data());
98 glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices[i]), std::data(vertices[i]));
99 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
100 }
101}
102
103} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.h b/src/video_core/renderer_opengl/present/window_adapt_pass.h
new file mode 100644
index 000000000..00975a9c6
--- /dev/null
+++ b/src/video_core/renderer_opengl/present/window_adapt_pass.h
@@ -0,0 +1,47 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <list>
7#include <span>
8
9#include "common/math_util.h"
10#include "video_core/renderer_opengl/gl_resource_manager.h"
11
12namespace Layout {
13struct FramebufferLayout;
14}
15
16namespace Tegra {
17struct FramebufferConfig;
18}
19
20namespace OpenGL {
21
22class Device;
23class Layer;
24class ProgramManager;
25
26class WindowAdaptPass final {
27public:
28 explicit WindowAdaptPass(const Device& device, OGLSampler&& sampler,
29 std::string_view frag_source);
30 ~WindowAdaptPass();
31
32 void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
33 std::span<const Tegra::FramebufferConfig> framebuffers,
34 const Layout::FramebufferLayout& layout);
35
36private:
37 const Device& device;
38 OGLSampler sampler;
39 OGLProgram vert;
40 OGLProgram frag;
41 OGLBuffer vertex_buffer;
42
43 // GPU address of the vertex buffer
44 GLuint64EXT vertex_buffer_address = 0;
45};
46
47} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index b75376fdb..e33a32592 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -16,68 +16,15 @@
16#include "core/core_timing.h" 16#include "core/core_timing.h"
17#include "core/frontend/emu_window.h" 17#include "core/frontend/emu_window.h"
18#include "core/telemetry_session.h" 18#include "core/telemetry_session.h"
19#include "video_core/host_shaders/ffx_a_h.h" 19#include "video_core/renderer_opengl/gl_blit_screen.h"
20#include "video_core/host_shaders/ffx_fsr1_h.h"
21#include "video_core/host_shaders/full_screen_triangle_vert.h"
22#include "video_core/host_shaders/fxaa_frag.h"
23#include "video_core/host_shaders/fxaa_vert.h"
24#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h"
25#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h"
26#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h"
27#include "video_core/host_shaders/opengl_present_frag.h"
28#include "video_core/host_shaders/opengl_present_scaleforce_frag.h"
29#include "video_core/host_shaders/opengl_present_vert.h"
30#include "video_core/host_shaders/opengl_smaa_glsl.h"
31#include "video_core/host_shaders/present_bicubic_frag.h"
32#include "video_core/host_shaders/present_gaussian_frag.h"
33#include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h"
34#include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h"
35#include "video_core/host_shaders/smaa_edge_detection_frag.h"
36#include "video_core/host_shaders/smaa_edge_detection_vert.h"
37#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h"
38#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h"
39#include "video_core/renderer_opengl/gl_fsr.h"
40#include "video_core/renderer_opengl/gl_rasterizer.h" 20#include "video_core/renderer_opengl/gl_rasterizer.h"
41#include "video_core/renderer_opengl/gl_shader_manager.h" 21#include "video_core/renderer_opengl/gl_shader_manager.h"
42#include "video_core/renderer_opengl/gl_shader_util.h" 22#include "video_core/renderer_opengl/gl_shader_util.h"
43#include "video_core/renderer_opengl/renderer_opengl.h" 23#include "video_core/renderer_opengl/renderer_opengl.h"
44#include "video_core/smaa_area_tex.h"
45#include "video_core/smaa_search_tex.h"
46#include "video_core/textures/decoders.h" 24#include "video_core/textures/decoders.h"
47 25
48namespace OpenGL { 26namespace OpenGL {
49namespace { 27namespace {
50constexpr GLint PositionLocation = 0;
51constexpr GLint TexCoordLocation = 1;
52constexpr GLint ModelViewMatrixLocation = 0;
53
54struct ScreenRectVertex {
55 constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v)
56 : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {}
57
58 std::array<GLfloat, 2> position;
59 std::array<GLfloat, 2> tex_coord;
60};
61
62/**
63 * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
64 * corner and (width, height) on the lower-bottom.
65 *
66 * The projection part of the matrix is trivial, hence these operations are represented
67 * by a 3x2 matrix.
68 */
69std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) {
70 std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order
71
72 // clang-format off
73 matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
74 matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;
75 // Last matrix row is implicitly assumed to be [0, 0, 1].
76 // clang-format on
77
78 return matrix;
79}
80
81const char* GetSource(GLenum source) { 28const char* GetSource(GLenum source) {
82 switch (source) { 29 switch (source) {
83 case GL_DEBUG_SOURCE_API: 30 case GL_DEBUG_SOURCE_API:
@@ -148,15 +95,13 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
148 : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, 95 : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_},
149 emu_window{emu_window_}, device_memory{device_memory_}, gpu{gpu_}, device{emu_window_}, 96 emu_window{emu_window_}, device_memory{device_memory_}, gpu{gpu_}, device{emu_window_},
150 state_tracker{}, program_manager{device}, 97 state_tracker{}, program_manager{device},
151 rasterizer(emu_window, gpu, device_memory, device, screen_info, program_manager, 98 rasterizer(emu_window, gpu, device_memory, device, program_manager, state_tracker) {
152 state_tracker) {
153 if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { 99 if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) {
154 glEnable(GL_DEBUG_OUTPUT); 100 glEnable(GL_DEBUG_OUTPUT);
155 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); 101 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
156 glDebugMessageCallback(DebugHandler, nullptr); 102 glDebugMessageCallback(DebugHandler, nullptr);
157 } 103 }
158 AddTelemetryFields(); 104 AddTelemetryFields();
159 InitOpenGLObjects();
160 105
161 // Initialize default attributes to match hardware's disabled attributes 106 // Initialize default attributes to match hardware's disabled attributes
162 GLint max_attribs{}; 107 GLint max_attribs{};
@@ -168,27 +113,27 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
168 if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) { 113 if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) {
169 glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); 114 glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
170 } 115 }
171 // Enable unified vertex attributes and query vertex buffer address when the driver supports it 116
117 // Enable unified vertex attributes when the driver supports it
172 if (device.HasVertexBufferUnifiedMemory()) { 118 if (device.HasVertexBufferUnifiedMemory()) {
173 glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); 119 glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV);
174 glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); 120 glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV);
175 glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY);
176 glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV,
177 &vertex_buffer_address);
178 } 121 }
122 blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker,
123 program_manager, device);
179} 124}
180 125
181RendererOpenGL::~RendererOpenGL() = default; 126RendererOpenGL::~RendererOpenGL() = default;
182 127
183void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 128void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
184 if (!framebuffer) { 129 if (framebuffers.empty()) {
185 return; 130 return;
186 } 131 }
187 PrepareRendertarget(framebuffer); 132
188 RenderScreenshot(); 133 RenderScreenshot(framebuffers);
189 134
190 state_tracker.BindFramebuffer(0); 135 state_tracker.BindFramebuffer(0);
191 DrawScreen(emu_window.GetFramebufferLayout()); 136 blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout());
192 137
193 ++m_current_frame; 138 ++m_current_frame;
194 139
@@ -199,172 +144,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
199 render_window.OnFrameDisplayed(); 144 render_window.OnFrameDisplayed();
200} 145}
201 146
202void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
203 if (!framebuffer) {
204 return;
205 }
206 // If framebuffer is provided, reload it from memory to a texture
207 if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
208 screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
209 screen_info.texture.pixel_format != framebuffer->pixel_format ||
210 gl_framebuffer_data.empty()) {
211 // Reallocate texture if the framebuffer size has changed.
212 // This is expected to not happen very often and hence should not be a
213 // performance problem.
214 ConfigureFramebufferTexture(screen_info.texture, *framebuffer);
215 }
216
217 // Load the framebuffer from memory, draw it to the screen, and swap buffers
218 LoadFBToScreenInfo(*framebuffer);
219}
220
221void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
222 // Framebuffer orientation handling
223 framebuffer_transform_flags = framebuffer.transform_flags;
224 framebuffer_crop_rect = framebuffer.crop_rect;
225 framebuffer_width = framebuffer.width;
226 framebuffer_height = framebuffer.height;
227
228 const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset};
229 screen_info.was_accelerated =
230 rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride);
231 if (screen_info.was_accelerated) {
232 return;
233 }
234
235 // Reset the screen info's display texture to its own permanent texture
236 screen_info.display_texture = screen_info.texture.resource.handle;
237
238 // TODO(Rodrigo): Read this from HLE
239 constexpr u32 block_height_log2 = 4;
240 const auto pixel_format{
241 VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
242 const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)};
243 const u64 size_in_bytes{Tegra::Texture::CalculateSize(
244 true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
245 const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)};
246 const std::span<const u8> input_data(host_ptr, size_in_bytes);
247 Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
248 framebuffer.width, framebuffer.height, 1, block_height_log2,
249 0);
250
251 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
252 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
253
254 // Update existing texture
255 // TODO: Test what happens on hardware when you change the framebuffer dimensions so that
256 // they differ from the LCD resolution.
257 // TODO: Applications could theoretically crash yuzu here by specifying too large
258 // framebuffer sizes. We should make sure that this cannot happen.
259 glTextureSubImage2D(screen_info.texture.resource.handle, 0, 0, 0, framebuffer.width,
260 framebuffer.height, screen_info.texture.gl_format,
261 screen_info.texture.gl_type, gl_framebuffer_data.data());
262
263 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
264}
265
266void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
267 const TextureInfo& texture) {
268 const u8 framebuffer_data[4] = {color_a, color_b, color_g, color_r};
269 glClearTexImage(texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data);
270}
271
272void RendererOpenGL::InitOpenGLObjects() {
273 // Create shader programs
274 fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER);
275 fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER);
276
277 const auto replace_include = [](std::string& shader_source, std::string_view include_name,
278 std::string_view include_content) {
279 const std::string include_string = fmt::format("#include \"{}\"", include_name);
280 const std::size_t pos = shader_source.find(include_string);
281 ASSERT(pos != std::string::npos);
282 shader_source.replace(pos, include_string.size(), include_content);
283 };
284
285 const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) {
286 std::string shader_source{specialized_source};
287 replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL);
288 return CreateProgram(shader_source, stage);
289 };
290
291 smaa_edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER);
292 smaa_edge_detection_frag =
293 SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER);
294 smaa_blending_weight_calculation_vert =
295 SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER);
296 smaa_blending_weight_calculation_frag =
297 SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER);
298 smaa_neighborhood_blending_vert =
299 SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER);
300 smaa_neighborhood_blending_frag =
301 SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER);
302
303 present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
304 present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER);
305 present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER);
306 present_gaussian_fragment =
307 CreateProgram(HostShaders::PRESENT_GAUSSIAN_FRAG, GL_FRAGMENT_SHADER);
308 present_scaleforce_fragment =
309 CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG),
310 GL_FRAGMENT_SHADER);
311
312 std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG};
313 replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H);
314 replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H);
315
316 std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG};
317 std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG};
318 replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source);
319 replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source);
320
321 fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source,
322 fsr_rcas_frag_source);
323
324 // Generate presentation sampler
325 present_sampler.Create();
326 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
327 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
328 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
329 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
330 glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
331
332 present_sampler_nn.Create();
333 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
334 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
335 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
336 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
337 glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
338
339 // Generate VBO handle for drawing
340 vertex_buffer.Create();
341
342 // Attach vertex data to VAO
343 glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
344
345 // Allocate textures for the screen
346 screen_info.texture.resource.Create(GL_TEXTURE_2D);
347
348 const GLuint texture = screen_info.texture.resource.handle;
349 glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1);
350
351 screen_info.display_texture = screen_info.texture.resource.handle;
352
353 // Clear screen to black
354 LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture);
355
356 aa_framebuffer.Create();
357
358 smaa_area_tex.Create(GL_TEXTURE_2D);
359 glTextureStorage2D(smaa_area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT);
360 glTextureSubImage2D(smaa_area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG,
361 GL_UNSIGNED_BYTE, areaTexBytes);
362 smaa_search_tex.Create(GL_TEXTURE_2D);
363 glTextureStorage2D(smaa_search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT);
364 glTextureSubImage2D(smaa_search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED,
365 GL_UNSIGNED_BYTE, searchTexBytes);
366}
367
368void RendererOpenGL::AddTelemetryFields() { 147void RendererOpenGL::AddTelemetryFields() {
369 const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))}; 148 const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))};
370 const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))}; 149 const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))};
@@ -380,328 +159,7 @@ void RendererOpenGL::AddTelemetryFields() {
380 telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); 159 telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version));
381} 160}
382 161
383void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, 162void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) {
384 const Tegra::FramebufferConfig& framebuffer) {
385 texture.width = framebuffer.width;
386 texture.height = framebuffer.height;
387 texture.pixel_format = framebuffer.pixel_format;
388
389 const auto pixel_format{
390 VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
391 const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)};
392 gl_framebuffer_data.resize(texture.width * texture.height * bytes_per_pixel);
393
394 GLint internal_format;
395 switch (framebuffer.pixel_format) {
396 case Service::android::PixelFormat::Rgba8888:
397 internal_format = GL_RGBA8;
398 texture.gl_format = GL_RGBA;
399 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
400 break;
401 case Service::android::PixelFormat::Rgb565:
402 internal_format = GL_RGB565;
403 texture.gl_format = GL_RGB;
404 texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
405 break;
406 default:
407 internal_format = GL_RGBA8;
408 texture.gl_format = GL_RGBA;
409 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
410 // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
411 // static_cast<u32>(framebuffer.pixel_format));
412 break;
413 }
414
415 texture.resource.Release();
416 texture.resource.Create(GL_TEXTURE_2D);
417 glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height);
418 aa_texture.Release();
419 aa_texture.Create(GL_TEXTURE_2D);
420 glTextureStorage2D(aa_texture.handle, 1, GL_RGBA16F,
421 Settings::values.resolution_info.ScaleUp(screen_info.texture.width),
422 Settings::values.resolution_info.ScaleUp(screen_info.texture.height));
423 glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, aa_texture.handle, 0);
424 smaa_edges_tex.Release();
425 smaa_edges_tex.Create(GL_TEXTURE_2D);
426 glTextureStorage2D(smaa_edges_tex.handle, 1, GL_RG16F,
427 Settings::values.resolution_info.ScaleUp(screen_info.texture.width),
428 Settings::values.resolution_info.ScaleUp(screen_info.texture.height));
429 smaa_blend_tex.Release();
430 smaa_blend_tex.Create(GL_TEXTURE_2D);
431 glTextureStorage2D(smaa_blend_tex.handle, 1, GL_RGBA16F,
432 Settings::values.resolution_info.ScaleUp(screen_info.texture.width),
433 Settings::values.resolution_info.ScaleUp(screen_info.texture.height));
434}
435
436void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
437 // TODO: Signal state tracker about these changes
438 state_tracker.NotifyScreenDrawVertexArray();
439 state_tracker.NotifyPolygonModes();
440 state_tracker.NotifyViewport0();
441 state_tracker.NotifyScissor0();
442 state_tracker.NotifyColorMask(0);
443 state_tracker.NotifyBlend0();
444 state_tracker.NotifyFramebuffer();
445 state_tracker.NotifyFrontFace();
446 state_tracker.NotifyCullTest();
447 state_tracker.NotifyDepthTest();
448 state_tracker.NotifyStencilTest();
449 state_tracker.NotifyPolygonOffset();
450 state_tracker.NotifyRasterizeEnable();
451 state_tracker.NotifyFramebufferSRGB();
452 state_tracker.NotifyLogicOp();
453 state_tracker.NotifyClipControl();
454 state_tracker.NotifyAlphaTest();
455
456 state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
457
458 glEnable(GL_CULL_FACE);
459 glDisable(GL_COLOR_LOGIC_OP);
460 glDisable(GL_DEPTH_TEST);
461 glDisable(GL_STENCIL_TEST);
462 glDisable(GL_POLYGON_OFFSET_FILL);
463 glDisable(GL_RASTERIZER_DISCARD);
464 glDisable(GL_ALPHA_TEST);
465 glDisablei(GL_BLEND, 0);
466 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
467 glCullFace(GL_BACK);
468 glFrontFace(GL_CW);
469 glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
470 glDepthRangeIndexed(0, 0.0, 0.0);
471
472 glBindTextureUnit(0, screen_info.display_texture);
473
474 auto anti_aliasing = Settings::values.anti_aliasing.GetValue();
475 if (anti_aliasing >= Settings::AntiAliasing::MaxEnum) {
476 LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing);
477 anti_aliasing = Settings::AntiAliasing::None;
478 Settings::values.anti_aliasing.SetValue(anti_aliasing);
479 }
480
481 if (anti_aliasing != Settings::AntiAliasing::None) {
482 glEnablei(GL_SCISSOR_TEST, 0);
483 auto viewport_width = screen_info.texture.width;
484 auto scissor_width = framebuffer_crop_rect.GetWidth();
485 if (scissor_width <= 0) {
486 scissor_width = viewport_width;
487 }
488 auto viewport_height = screen_info.texture.height;
489 auto scissor_height = framebuffer_crop_rect.GetHeight();
490 if (scissor_height <= 0) {
491 scissor_height = viewport_height;
492 }
493 if (screen_info.was_accelerated) {
494 viewport_width = Settings::values.resolution_info.ScaleUp(viewport_width);
495 scissor_width = Settings::values.resolution_info.ScaleUp(scissor_width);
496 viewport_height = Settings::values.resolution_info.ScaleUp(viewport_height);
497 scissor_height = Settings::values.resolution_info.ScaleUp(scissor_height);
498 }
499 glScissorIndexed(0, 0, 0, scissor_width, scissor_height);
500 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(viewport_width),
501 static_cast<GLfloat>(viewport_height));
502
503 glBindSampler(0, present_sampler.handle);
504 GLint old_read_fb;
505 GLint old_draw_fb;
506 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
507 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
508
509 switch (anti_aliasing) {
510 case Settings::AntiAliasing::Fxaa: {
511 program_manager.BindPresentPrograms(fxaa_vertex.handle, fxaa_fragment.handle);
512 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle);
513 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
514 } break;
515 case Settings::AntiAliasing::Smaa: {
516 glClearColor(0, 0, 0, 0);
517 glFrontFace(GL_CCW);
518 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle);
519 glBindSampler(1, present_sampler.handle);
520 glBindSampler(2, present_sampler.handle);
521
522 glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0,
523 smaa_edges_tex.handle, 0);
524 glClear(GL_COLOR_BUFFER_BIT);
525 program_manager.BindPresentPrograms(smaa_edge_detection_vert.handle,
526 smaa_edge_detection_frag.handle);
527 glDrawArrays(GL_TRIANGLES, 0, 3);
528
529 glBindTextureUnit(0, smaa_edges_tex.handle);
530 glBindTextureUnit(1, smaa_area_tex.handle);
531 glBindTextureUnit(2, smaa_search_tex.handle);
532 glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0,
533 smaa_blend_tex.handle, 0);
534 glClear(GL_COLOR_BUFFER_BIT);
535 program_manager.BindPresentPrograms(smaa_blending_weight_calculation_vert.handle,
536 smaa_blending_weight_calculation_frag.handle);
537 glDrawArrays(GL_TRIANGLES, 0, 3);
538
539 glBindTextureUnit(0, screen_info.display_texture);
540 glBindTextureUnit(1, smaa_blend_tex.handle);
541 glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0,
542 aa_texture.handle, 0);
543 program_manager.BindPresentPrograms(smaa_neighborhood_blending_vert.handle,
544 smaa_neighborhood_blending_frag.handle);
545 glDrawArrays(GL_TRIANGLES, 0, 3);
546 glFrontFace(GL_CW);
547 } break;
548 default:
549 UNREACHABLE();
550 }
551
552 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
553 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
554
555 glBindTextureUnit(0, aa_texture.handle);
556 }
557 glDisablei(GL_SCISSOR_TEST, 0);
558
559 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
560 if (!fsr->AreBuffersInitialized()) {
561 fsr->InitBuffers();
562 }
563
564 auto crop_rect = framebuffer_crop_rect;
565 if (crop_rect.GetWidth() == 0) {
566 crop_rect.right = framebuffer_width;
567 }
568 if (crop_rect.GetHeight() == 0) {
569 crop_rect.bottom = framebuffer_height;
570 }
571 crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor);
572 const auto fsr_input_width = Settings::values.resolution_info.ScaleUp(framebuffer_width);
573 const auto fsr_input_height = Settings::values.resolution_info.ScaleUp(framebuffer_height);
574 glBindSampler(0, present_sampler.handle);
575 fsr->Draw(program_manager, layout.screen, fsr_input_width, fsr_input_height, crop_rect);
576 } else {
577 if (fsr->AreBuffersInitialized()) {
578 fsr->ReleaseBuffers();
579 }
580 }
581
582 const std::array ortho_matrix =
583 MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
584
585 const auto fragment_handle = [this]() {
586 switch (Settings::values.scaling_filter.GetValue()) {
587 case Settings::ScalingFilter::NearestNeighbor:
588 case Settings::ScalingFilter::Bilinear:
589 return present_bilinear_fragment.handle;
590 case Settings::ScalingFilter::Bicubic:
591 return present_bicubic_fragment.handle;
592 case Settings::ScalingFilter::Gaussian:
593 return present_gaussian_fragment.handle;
594 case Settings::ScalingFilter::ScaleForce:
595 return present_scaleforce_fragment.handle;
596 case Settings::ScalingFilter::Fsr:
597 return fsr->GetPresentFragmentProgram().handle;
598 default:
599 return present_bilinear_fragment.handle;
600 }
601 }();
602 program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle);
603 glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE,
604 ortho_matrix.data());
605
606 const auto& texcoords = screen_info.display_texcoords;
607 auto left = texcoords.left;
608 auto right = texcoords.right;
609 if (framebuffer_transform_flags != Service::android::BufferTransformFlags::Unset) {
610 if (framebuffer_transform_flags == Service::android::BufferTransformFlags::FlipV) {
611 // Flip the framebuffer vertically
612 left = texcoords.right;
613 right = texcoords.left;
614 } else {
615 // Other transformations are unsupported
616 LOG_CRITICAL(Render_OpenGL, "Unsupported framebuffer_transform_flags={}",
617 framebuffer_transform_flags);
618 UNIMPLEMENTED();
619 }
620 }
621
622 ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented");
623
624 f32 left_start{};
625 if (framebuffer_crop_rect.Top() > 0) {
626 left_start = static_cast<f32>(framebuffer_crop_rect.Top()) /
627 static_cast<f32>(framebuffer_crop_rect.Bottom());
628 }
629 f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width);
630 f32 scale_v =
631 static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height);
632
633 if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::Fsr) {
634 // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
635 // (e.g. handheld mode) on a 1920x1080 framebuffer.
636 if (framebuffer_crop_rect.GetWidth() > 0) {
637 scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) /
638 static_cast<f32>(screen_info.texture.width);
639 }
640 if (framebuffer_crop_rect.GetHeight() > 0) {
641 scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) /
642 static_cast<f32>(screen_info.texture.height);
643 }
644 }
645 if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa &&
646 !screen_info.was_accelerated) {
647 scale_u /= Settings::values.resolution_info.up_factor;
648 scale_v /= Settings::values.resolution_info.up_factor;
649 }
650
651 const auto& screen = layout.screen;
652 const std::array vertices = {
653 ScreenRectVertex(screen.left, screen.top, texcoords.top * scale_u,
654 left_start + left * scale_v),
655 ScreenRectVertex(screen.right, screen.top, texcoords.bottom * scale_u,
656 left_start + left * scale_v),
657 ScreenRectVertex(screen.left, screen.bottom, texcoords.top * scale_u,
658 left_start + right * scale_v),
659 ScreenRectVertex(screen.right, screen.bottom, texcoords.bottom * scale_u,
660 left_start + right * scale_v),
661 };
662 glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
663
664 glDisable(GL_FRAMEBUFFER_SRGB);
665 glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
666 static_cast<GLfloat>(layout.height));
667
668 glEnableVertexAttribArray(PositionLocation);
669 glEnableVertexAttribArray(TexCoordLocation);
670 glVertexAttribDivisor(PositionLocation, 0);
671 glVertexAttribDivisor(TexCoordLocation, 0);
672 glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
673 offsetof(ScreenRectVertex, position));
674 glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE,
675 offsetof(ScreenRectVertex, tex_coord));
676 glVertexAttribBinding(PositionLocation, 0);
677 glVertexAttribBinding(TexCoordLocation, 0);
678 if (device.HasVertexBufferUnifiedMemory()) {
679 glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex));
680 glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address,
681 sizeof(vertices));
682 } else {
683 glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
684 }
685
686 if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) {
687 glBindSampler(0, present_sampler.handle);
688 } else {
689 glBindSampler(0, present_sampler_nn.handle);
690 }
691
692 // Update background color before drawing
693 glClearColor(Settings::values.bg_red.GetValue() / 255.0f,
694 Settings::values.bg_green.GetValue() / 255.0f,
695 Settings::values.bg_blue.GetValue() / 255.0f, 1.0f);
696
697 glClear(GL_COLOR_BUFFER_BIT);
698 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
699
700 // TODO
701 // program_manager.RestoreGuestPipeline();
702}
703
704void RendererOpenGL::RenderScreenshot() {
705 if (!renderer_settings.screenshot_requested) { 163 if (!renderer_settings.screenshot_requested) {
706 return; 164 return;
707 } 165 }
@@ -723,7 +181,7 @@ void RendererOpenGL::RenderScreenshot() {
723 glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); 181 glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height);
724 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); 182 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
725 183
726 DrawScreen(layout); 184 blit_screen->DrawScreen(framebuffers, layout);
727 185
728 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 186 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
729 glPixelStorei(GL_PACK_ROW_LENGTH, 0); 187 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 18699610a..c4625c96e 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -10,7 +10,6 @@
10 10
11#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
12#include "video_core/renderer_opengl/gl_device.h" 12#include "video_core/renderer_opengl/gl_device.h"
13#include "video_core/renderer_opengl/gl_fsr.h"
14#include "video_core/renderer_opengl/gl_rasterizer.h" 13#include "video_core/renderer_opengl/gl_rasterizer.h"
15#include "video_core/renderer_opengl/gl_resource_manager.h" 14#include "video_core/renderer_opengl/gl_resource_manager.h"
16#include "video_core/renderer_opengl/gl_shader_manager.h" 15#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -25,37 +24,13 @@ namespace Core::Frontend {
25class EmuWindow; 24class EmuWindow;
26} 25}
27 26
28namespace Core::Memory {
29class Memory;
30}
31
32namespace Layout {
33struct FramebufferLayout;
34}
35
36namespace Tegra { 27namespace Tegra {
37class GPU; 28class GPU;
38} 29}
39 30
40namespace OpenGL { 31namespace OpenGL {
41 32
42/// Structure used for storing information about the textures for the Switch screen 33class BlitScreen;
43struct TextureInfo {
44 OGLTexture resource;
45 GLsizei width;
46 GLsizei height;
47 GLenum gl_format;
48 GLenum gl_type;
49 Service::android::PixelFormat pixel_format;
50};
51
52/// Structure used for storing information about the display target for the Switch screen
53struct ScreenInfo {
54 GLuint display_texture{};
55 bool was_accelerated = false;
56 const Common::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f};
57 TextureInfo texture;
58};
59 34
60class RendererOpenGL final : public VideoCore::RendererBase { 35class RendererOpenGL final : public VideoCore::RendererBase {
61public: 36public:
@@ -65,7 +40,7 @@ public:
65 std::unique_ptr<Core::Frontend::GraphicsContext> context_); 40 std::unique_ptr<Core::Frontend::GraphicsContext> context_);
66 ~RendererOpenGL() override; 41 ~RendererOpenGL() override;
67 42
68 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 43 void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;
69 44
70 VideoCore::RasterizerInterface* ReadRasterizer() override { 45 VideoCore::RasterizerInterface* ReadRasterizer() override {
71 return &rasterizer; 46 return &rasterizer;
@@ -76,28 +51,8 @@ public:
76 } 51 }
77 52
78private: 53private:
79 /// Initializes the OpenGL state and creates persistent objects.
80 void InitOpenGLObjects();
81
82 void AddTelemetryFields(); 54 void AddTelemetryFields();
83 55 void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
84 void ConfigureFramebufferTexture(TextureInfo& texture,
85 const Tegra::FramebufferConfig& framebuffer);
86
87 /// Draws the emulated screens to the emulator window.
88 void DrawScreen(const Layout::FramebufferLayout& layout);
89
90 void RenderScreenshot();
91
92 /// Loads framebuffer from emulated memory into the active OpenGL texture.
93 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
94
95 /// Fills active OpenGL texture with the given RGB color.Since the color is solid, the texture
96 /// can be 1x1 but will stretch across whatever it's rendered on.
97 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
98 const TextureInfo& texture);
99
100 void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
101 56
102 Core::TelemetrySession& telemetry_session; 57 Core::TelemetrySession& telemetry_session;
103 Core::Frontend::EmuWindow& emu_window; 58 Core::Frontend::EmuWindow& emu_window;
@@ -108,49 +63,9 @@ private:
108 StateTracker state_tracker; 63 StateTracker state_tracker;
109 ProgramManager program_manager; 64 ProgramManager program_manager;
110 RasterizerOpenGL rasterizer; 65 RasterizerOpenGL rasterizer;
111
112 // OpenGL object IDs
113 OGLSampler present_sampler;
114 OGLSampler present_sampler_nn;
115 OGLBuffer vertex_buffer;
116 OGLProgram fxaa_vertex;
117 OGLProgram fxaa_fragment;
118 OGLProgram present_vertex;
119 OGLProgram present_bilinear_fragment;
120 OGLProgram present_bicubic_fragment;
121 OGLProgram present_gaussian_fragment;
122 OGLProgram present_scaleforce_fragment;
123 OGLFramebuffer screenshot_framebuffer; 66 OGLFramebuffer screenshot_framebuffer;
124 67
125 // GPU address of the vertex buffer 68 std::unique_ptr<BlitScreen> blit_screen;
126 GLuint64EXT vertex_buffer_address = 0;
127
128 /// Display information for Switch screen
129 ScreenInfo screen_info;
130 OGLTexture aa_texture;
131 OGLFramebuffer aa_framebuffer;
132
133 OGLProgram smaa_edge_detection_vert;
134 OGLProgram smaa_blending_weight_calculation_vert;
135 OGLProgram smaa_neighborhood_blending_vert;
136 OGLProgram smaa_edge_detection_frag;
137 OGLProgram smaa_blending_weight_calculation_frag;
138 OGLProgram smaa_neighborhood_blending_frag;
139 OGLTexture smaa_area_tex;
140 OGLTexture smaa_search_tex;
141 OGLTexture smaa_edges_tex;
142 OGLTexture smaa_blend_tex;
143
144 std::unique_ptr<FSR> fsr;
145
146 /// OpenGL framebuffer data
147 std::vector<u8> gl_framebuffer_data;
148
149 /// Used for transforming the framebuffer orientation
150 Service::android::BufferTransformFlags framebuffer_transform_flags{};
151 Common::Rectangle<int> framebuffer_crop_rect;
152 u32 framebuffer_width;
153 u32 framebuffer_height;
154}; 69};
155 70
156} // namespace OpenGL 71} // namespace OpenGL
diff --git a/src/video_core/renderer_vulkan/present/anti_alias_pass.h b/src/video_core/renderer_vulkan/present/anti_alias_pass.h
new file mode 100644
index 000000000..1f20fbd7f
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/anti_alias_pass.h
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "video_core/vulkan_common/vulkan_wrapper.h"
7
8namespace Vulkan {
9
10class Scheduler;
11
12class AntiAliasPass {
13public:
14 virtual ~AntiAliasPass() = default;
15 virtual void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
16 VkImageView* inout_image_view) = 0;
17};
18
19class NoAA final : public AntiAliasPass {
20public:
21 void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
22 VkImageView* inout_image_view) override {}
23};
24
25} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp
new file mode 100644
index 000000000..b5e08938e
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/filters.cpp
@@ -0,0 +1,56 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/common_types.h"
5
6#include "video_core/host_shaders/present_bicubic_frag_spv.h"
7#include "video_core/host_shaders/present_gaussian_frag_spv.h"
8#include "video_core/host_shaders/vulkan_present_frag_spv.h"
9#include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h"
10#include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h"
11#include "video_core/renderer_vulkan/present/filters.h"
12#include "video_core/renderer_vulkan/present/util.h"
13#include "video_core/renderer_vulkan/vk_shader_util.h"
14#include "video_core/vulkan_common/vulkan_device.h"
15
16namespace Vulkan {
17
18namespace {
19
20vk::ShaderModule SelectScaleForceShader(const Device& device) {
21 if (device.IsFloat16Supported()) {
22 return BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP16_FRAG_SPV);
23 } else {
24 return BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP32_FRAG_SPV);
25 }
26}
27
28} // Anonymous namespace
29
30std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format) {
31 return std::make_unique<WindowAdaptPass>(device, frame_format,
32 CreateNearestNeighborSampler(device),
33 BuildShader(device, VULKAN_PRESENT_FRAG_SPV));
34}
35
36std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format) {
37 return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device),
38 BuildShader(device, VULKAN_PRESENT_FRAG_SPV));
39}
40
41std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format) {
42 return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device),
43 BuildShader(device, PRESENT_BICUBIC_FRAG_SPV));
44}
45
46std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format) {
47 return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device),
48 BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV));
49}
50
51std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format) {
52 return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device),
53 SelectScaleForceShader(device));
54}
55
56} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h
new file mode 100644
index 000000000..6c83726dd
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/filters.h
@@ -0,0 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "video_core/renderer_vulkan/present/window_adapt_pass.h"
7
8namespace Vulkan {
9
10class MemoryAllocator;
11
12std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format);
13std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format);
14std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format);
15std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format);
16std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format);
17
18} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/fsr.cpp b/src/video_core/renderer_vulkan/present/fsr.cpp
new file mode 100644
index 000000000..3f708be70
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/fsr.cpp
@@ -0,0 +1,226 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/common_types.h"
5#include "common/div_ceil.h"
6#include "common/settings.h"
7
8#include "video_core/fsr.h"
9#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_frag_spv.h"
10#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_frag_spv.h"
11#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_frag_spv.h"
12#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32_frag_spv.h"
13#include "video_core/host_shaders/vulkan_fidelityfx_fsr_vert_spv.h"
14#include "video_core/renderer_vulkan/present/fsr.h"
15#include "video_core/renderer_vulkan/present/util.h"
16#include "video_core/renderer_vulkan/vk_scheduler.h"
17#include "video_core/renderer_vulkan/vk_shader_util.h"
18#include "video_core/vulkan_common/vulkan_device.h"
19
20namespace Vulkan {
21using namespace FSR;
22
23using PushConstants = std::array<u32, 4 * 4>;
24
25FSR::FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count,
26 VkExtent2D extent)
27 : m_device{device}, m_memory_allocator{memory_allocator},
28 m_image_count{image_count}, m_extent{extent} {
29
30 CreateImages();
31 CreateRenderPasses();
32 CreateSampler();
33 CreateShaders();
34 CreateDescriptorPool();
35 CreateDescriptorSetLayout();
36 CreateDescriptorSets();
37 CreatePipelineLayouts();
38 CreatePipelines();
39}
40
41void FSR::CreateImages() {
42 m_dynamic_images.resize(m_image_count);
43 for (auto& images : m_dynamic_images) {
44 images.images[Easu] =
45 CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
46 images.images[Rcas] =
47 CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
48 images.image_views[Easu] =
49 CreateWrappedImageView(m_device, images.images[Easu], VK_FORMAT_R16G16B16A16_SFLOAT);
50 images.image_views[Rcas] =
51 CreateWrappedImageView(m_device, images.images[Rcas], VK_FORMAT_R16G16B16A16_SFLOAT);
52 }
53}
54
55void FSR::CreateRenderPasses() {
56 m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
57
58 for (auto& images : m_dynamic_images) {
59 images.framebuffers[Easu] =
60 CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[Easu], m_extent);
61 images.framebuffers[Rcas] =
62 CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[Rcas], m_extent);
63 }
64}
65
66void FSR::CreateSampler() {
67 m_sampler = CreateBilinearSampler(m_device);
68}
69
70void FSR::CreateShaders() {
71 m_vert_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_VERT_SPV);
72
73 if (m_device.IsFloat16Supported()) {
74 m_easu_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_EASU_FP16_FRAG_SPV);
75 m_rcas_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_RCAS_FP16_FRAG_SPV);
76 } else {
77 m_easu_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_EASU_FP32_FRAG_SPV);
78 m_rcas_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_RCAS_FP32_FRAG_SPV);
79 }
80}
81
82void FSR::CreateDescriptorPool() {
83 // EASU: 1 descriptor
84 // RCAS: 1 descriptor
85 // 2 descriptors, 2 descriptor sets per invocation
86 m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, 2 * m_image_count);
87}
88
89void FSR::CreateDescriptorSetLayout() {
90 m_descriptor_set_layout =
91 CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
92}
93
94void FSR::CreateDescriptorSets() {
95 std::vector<VkDescriptorSetLayout> layouts(MaxFsrStage, *m_descriptor_set_layout);
96
97 for (auto& images : m_dynamic_images) {
98 images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts);
99 }
100}
101
102void FSR::CreatePipelineLayouts() {
103 const VkPushConstantRange range{
104 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
105 .offset = 0,
106 .size = sizeof(PushConstants),
107 };
108 VkPipelineLayoutCreateInfo ci{
109 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
110 .pNext = nullptr,
111 .flags = 0,
112 .setLayoutCount = 1,
113 .pSetLayouts = m_descriptor_set_layout.address(),
114 .pushConstantRangeCount = 1,
115 .pPushConstantRanges = &range,
116 };
117
118 m_pipeline_layout = m_device.GetLogical().CreatePipelineLayout(ci);
119}
120
121void FSR::CreatePipelines() {
122 m_easu_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout,
123 std::tie(m_vert_shader, m_easu_shader));
124 m_rcas_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout,
125 std::tie(m_vert_shader, m_rcas_shader));
126}
127
128void FSR::UpdateDescriptorSets(VkImageView image_view, size_t image_index) {
129 Images& images = m_dynamic_images[image_index];
130 std::vector<VkDescriptorImageInfo> image_infos;
131 std::vector<VkWriteDescriptorSet> updates;
132 image_infos.reserve(2);
133
134 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view,
135 images.descriptor_sets[Easu], 0));
136 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Easu],
137 images.descriptor_sets[Rcas], 0));
138
139 m_device.GetLogical().UpdateDescriptorSets(updates, {});
140}
141
142void FSR::UploadImages(Scheduler& scheduler) {
143 if (m_images_ready) {
144 return;
145 }
146
147 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
148 for (auto& image : m_dynamic_images) {
149 ClearColorImage(cmdbuf, *image.images[Easu]);
150 ClearColorImage(cmdbuf, *image.images[Rcas]);
151 }
152 });
153 scheduler.Finish();
154
155 m_images_ready = true;
156}
157
158VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image,
159 VkImageView source_image_view, VkExtent2D input_image_extent,
160 const Common::Rectangle<f32>& crop_rect) {
161 Images& images = m_dynamic_images[image_index];
162
163 VkImage easu_image = *images.images[Easu];
164 VkImage rcas_image = *images.images[Rcas];
165 VkDescriptorSet easu_descriptor_set = images.descriptor_sets[Easu];
166 VkDescriptorSet rcas_descriptor_set = images.descriptor_sets[Rcas];
167 VkFramebuffer easu_framebuffer = *images.framebuffers[Easu];
168 VkFramebuffer rcas_framebuffer = *images.framebuffers[Rcas];
169 VkPipeline easu_pipeline = *m_easu_pipeline;
170 VkPipeline rcas_pipeline = *m_rcas_pipeline;
171 VkPipelineLayout pipeline_layout = *m_pipeline_layout;
172 VkRenderPass renderpass = *m_renderpass;
173 VkExtent2D extent = m_extent;
174
175 const f32 input_image_width = static_cast<f32>(input_image_extent.width);
176 const f32 input_image_height = static_cast<f32>(input_image_extent.height);
177 const f32 output_image_width = static_cast<f32>(extent.width);
178 const f32 output_image_height = static_cast<f32>(extent.height);
179 const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width;
180 const f32 viewport_x = crop_rect.left * input_image_width;
181 const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
182 const f32 viewport_y = crop_rect.top * input_image_height;
183
184 PushConstants easu_con{};
185 PushConstants rcas_con{};
186 FsrEasuConOffset(easu_con.data() + 0, easu_con.data() + 4, easu_con.data() + 8,
187 easu_con.data() + 12, viewport_width, viewport_height, input_image_width,
188 input_image_height, output_image_width, output_image_height, viewport_x,
189 viewport_y);
190
191 const float sharpening =
192 static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
193 FsrRcasCon(rcas_con.data(), sharpening);
194
195 UploadImages(scheduler);
196 UpdateDescriptorSets(source_image_view, image_index);
197
198 scheduler.RequestOutsideRenderPassOperationContext();
199 scheduler.Record([=](vk::CommandBuffer cmdbuf) {
200 TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL);
201 TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL);
202 BeginRenderPass(cmdbuf, renderpass, easu_framebuffer, extent);
203 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, easu_pipeline);
204 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0,
205 easu_descriptor_set, {});
206 cmdbuf.PushConstants(pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, easu_con);
207 cmdbuf.Draw(3, 1, 0, 0);
208 cmdbuf.EndRenderPass();
209
210 TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL);
211 TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL);
212 BeginRenderPass(cmdbuf, renderpass, rcas_framebuffer, extent);
213 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, rcas_pipeline);
214 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0,
215 rcas_descriptor_set, {});
216 cmdbuf.PushConstants(pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, rcas_con);
217 cmdbuf.Draw(3, 1, 0, 0);
218 cmdbuf.EndRenderPass();
219
220 TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL);
221 });
222
223 return *images.image_views[Rcas];
224}
225
226} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/fsr.h b/src/video_core/renderer_vulkan/present/fsr.h
new file mode 100644
index 000000000..8602e8146
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/fsr.h
@@ -0,0 +1,69 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/math_util.h"
7#include "video_core/vulkan_common/vulkan_memory_allocator.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h"
9
10namespace Vulkan {
11
12class Device;
13class Scheduler;
14
15class FSR {
16public:
17 explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count,
18 VkExtent2D extent);
19 VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image,
20 VkImageView source_image_view, VkExtent2D input_image_extent,
21 const Common::Rectangle<f32>& crop_rect);
22
23private:
24 void CreateImages();
25 void CreateRenderPasses();
26 void CreateSampler();
27 void CreateShaders();
28 void CreateDescriptorPool();
29 void CreateDescriptorSetLayout();
30 void CreateDescriptorSets();
31 void CreatePipelineLayouts();
32 void CreatePipelines();
33
34 void UploadImages(Scheduler& scheduler);
35 void UpdateDescriptorSets(VkImageView image_view, size_t image_index);
36
37 const Device& m_device;
38 MemoryAllocator& m_memory_allocator;
39 const size_t m_image_count;
40 const VkExtent2D m_extent;
41
42 enum FsrStage {
43 Easu,
44 Rcas,
45 MaxFsrStage,
46 };
47
48 vk::DescriptorPool m_descriptor_pool;
49 vk::DescriptorSetLayout m_descriptor_set_layout;
50 vk::PipelineLayout m_pipeline_layout;
51 vk::ShaderModule m_vert_shader;
52 vk::ShaderModule m_easu_shader;
53 vk::ShaderModule m_rcas_shader;
54 vk::Pipeline m_easu_pipeline;
55 vk::Pipeline m_rcas_pipeline;
56 vk::RenderPass m_renderpass;
57 vk::Sampler m_sampler;
58
59 struct Images {
60 vk::DescriptorSets descriptor_sets;
61 std::array<vk::Image, MaxFsrStage> images;
62 std::array<vk::ImageView, MaxFsrStage> image_views;
63 std::array<vk::Framebuffer, MaxFsrStage> framebuffers;
64 };
65 std::vector<Images> m_dynamic_images;
66 bool m_images_ready{};
67};
68
69} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/fxaa.cpp b/src/video_core/renderer_vulkan/present/fxaa.cpp
new file mode 100644
index 000000000..bdafd1f4d
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/fxaa.cpp
@@ -0,0 +1,148 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/common_types.h"
5
6#include "video_core/host_shaders/fxaa_frag_spv.h"
7#include "video_core/host_shaders/fxaa_vert_spv.h"
8#include "video_core/renderer_vulkan/present/fxaa.h"
9#include "video_core/renderer_vulkan/present/util.h"
10#include "video_core/renderer_vulkan/vk_scheduler.h"
11#include "video_core/renderer_vulkan/vk_shader_util.h"
12#include "video_core/vulkan_common/vulkan_device.h"
13
14namespace Vulkan {
15
16FXAA::FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent)
17 : m_device(device), m_allocator(allocator), m_extent(extent),
18 m_image_count(static_cast<u32>(image_count)) {
19 CreateImages();
20 CreateRenderPasses();
21 CreateSampler();
22 CreateShaders();
23 CreateDescriptorPool();
24 CreateDescriptorSetLayouts();
25 CreateDescriptorSets();
26 CreatePipelineLayouts();
27 CreatePipelines();
28}
29
30FXAA::~FXAA() = default;
31
32void FXAA::CreateImages() {
33 for (u32 i = 0; i < m_image_count; i++) {
34 Image& image = m_dynamic_images.emplace_back();
35
36 image.image = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
37 image.image_view =
38 CreateWrappedImageView(m_device, image.image, VK_FORMAT_R16G16B16A16_SFLOAT);
39 }
40}
41
42void FXAA::CreateRenderPasses() {
43 m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
44
45 for (auto& image : m_dynamic_images) {
46 image.framebuffer =
47 CreateWrappedFramebuffer(m_device, m_renderpass, image.image_view, m_extent);
48 }
49}
50
51void FXAA::CreateSampler() {
52 m_sampler = CreateWrappedSampler(m_device);
53}
54
55void FXAA::CreateShaders() {
56 m_vertex_shader = CreateWrappedShaderModule(m_device, FXAA_VERT_SPV);
57 m_fragment_shader = CreateWrappedShaderModule(m_device, FXAA_FRAG_SPV);
58}
59
60void FXAA::CreateDescriptorPool() {
61 // 2 descriptors, 1 descriptor set per image
62 m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, m_image_count);
63}
64
65void FXAA::CreateDescriptorSetLayouts() {
66 m_descriptor_set_layout =
67 CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
68 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
69}
70
71void FXAA::CreateDescriptorSets() {
72 VkDescriptorSetLayout layout = *m_descriptor_set_layout;
73
74 for (auto& images : m_dynamic_images) {
75 images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, {layout});
76 }
77}
78
79void FXAA::CreatePipelineLayouts() {
80 m_pipeline_layout = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layout);
81}
82
83void FXAA::CreatePipelines() {
84 m_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout,
85 std::tie(m_vertex_shader, m_fragment_shader));
86}
87
88void FXAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) {
89 Image& image = m_dynamic_images[image_index];
90 std::vector<VkDescriptorImageInfo> image_infos;
91 std::vector<VkWriteDescriptorSet> updates;
92 image_infos.reserve(2);
93
94 updates.push_back(
95 CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 0));
96 updates.push_back(
97 CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 1));
98
99 m_device.GetLogical().UpdateDescriptorSets(updates, {});
100}
101
102void FXAA::UploadImages(Scheduler& scheduler) {
103 if (m_images_ready) {
104 return;
105 }
106
107 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
108 for (auto& image : m_dynamic_images) {
109 ClearColorImage(cmdbuf, *image.image);
110 }
111 });
112 scheduler.Finish();
113
114 m_images_ready = true;
115}
116
117void FXAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
118 VkImageView* inout_image_view) {
119 const Image& image{m_dynamic_images[image_index]};
120 const VkImage input_image{*inout_image};
121 const VkImage output_image{*image.image};
122 const VkDescriptorSet descriptor_set{image.descriptor_sets[0]};
123 const VkFramebuffer framebuffer{*image.framebuffer};
124 const VkRenderPass renderpass{*m_renderpass};
125 const VkPipeline pipeline{*m_pipeline};
126 const VkPipelineLayout layout{*m_pipeline_layout};
127 const VkExtent2D extent{m_extent};
128
129 UploadImages(scheduler);
130 UpdateDescriptorSets(*inout_image_view, image_index);
131
132 scheduler.RequestOutsideRenderPassOperationContext();
133 scheduler.Record([=](vk::CommandBuffer cmdbuf) {
134 TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL);
135 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
136 BeginRenderPass(cmdbuf, renderpass, framebuffer, extent);
137 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
138 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, {});
139 cmdbuf.Draw(3, 1, 0, 0);
140 cmdbuf.EndRenderPass();
141 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
142 });
143
144 *inout_image = *image.image;
145 *inout_image_view = *image.image_view;
146}
147
148} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/fxaa.h b/src/video_core/renderer_vulkan/present/fxaa.h
new file mode 100644
index 000000000..97a2e5c1c
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/fxaa.h
@@ -0,0 +1,63 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "video_core/renderer_vulkan/present/anti_alias_pass.h"
7#include "video_core/vulkan_common/vulkan_memory_allocator.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h"
9
10namespace Vulkan {
11
12class Device;
13class Scheduler;
14class StagingBufferPool;
15
16class FXAA final : public AntiAliasPass {
17public:
18 explicit FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count,
19 VkExtent2D extent);
20 ~FXAA() override;
21
22 void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
23 VkImageView* inout_image_view) override;
24
25private:
26 void CreateImages();
27 void CreateRenderPasses();
28 void CreateSampler();
29 void CreateShaders();
30 void CreateDescriptorPool();
31 void CreateDescriptorSetLayouts();
32 void CreateDescriptorSets();
33 void CreatePipelineLayouts();
34 void CreatePipelines();
35 void UpdateDescriptorSets(VkImageView image_view, size_t image_index);
36 void UploadImages(Scheduler& scheduler);
37
38 const Device& m_device;
39 MemoryAllocator& m_allocator;
40 const VkExtent2D m_extent;
41 const u32 m_image_count;
42
43 vk::ShaderModule m_vertex_shader{};
44 vk::ShaderModule m_fragment_shader{};
45 vk::DescriptorPool m_descriptor_pool{};
46 vk::DescriptorSetLayout m_descriptor_set_layout{};
47 vk::PipelineLayout m_pipeline_layout{};
48 vk::Pipeline m_pipeline{};
49 vk::RenderPass m_renderpass{};
50
51 struct Image {
52 vk::DescriptorSets descriptor_sets{};
53 vk::Framebuffer framebuffer{};
54 vk::Image image{};
55 vk::ImageView image_view{};
56 };
57 std::vector<Image> m_dynamic_images{};
58 bool m_images_ready{};
59
60 vk::Sampler m_sampler{};
61};
62
63} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp
new file mode 100644
index 000000000..cfc04be44
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/layer.cpp
@@ -0,0 +1,336 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/renderer_vulkan/vk_rasterizer.h"
5
6#include "common/settings.h"
7#include "video_core/framebuffer_config.h"
8#include "video_core/renderer_vulkan/present/fsr.h"
9#include "video_core/renderer_vulkan/present/fxaa.h"
10#include "video_core/renderer_vulkan/present/layer.h"
11#include "video_core/renderer_vulkan/present/present_push_constants.h"
12#include "video_core/renderer_vulkan/present/smaa.h"
13#include "video_core/renderer_vulkan/present/util.h"
14#include "video_core/renderer_vulkan/vk_blit_screen.h"
15#include "video_core/textures/decoders.h"
16
17namespace Vulkan {
18
19namespace {
20
21u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) {
22 using namespace VideoCore::Surface;
23 return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format));
24}
25
26std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) {
27 return static_cast<std::size_t>(framebuffer.stride) *
28 static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer);
29}
30
31VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) {
32 switch (framebuffer.pixel_format) {
33 case Service::android::PixelFormat::Rgba8888:
34 case Service::android::PixelFormat::Rgbx8888:
35 return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
36 case Service::android::PixelFormat::Rgb565:
37 return VK_FORMAT_R5G6B5_UNORM_PACK16;
38 case Service::android::PixelFormat::Bgra8888:
39 return VK_FORMAT_B8G8R8A8_UNORM;
40 default:
41 UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
42 static_cast<u32>(framebuffer.pixel_format));
43 return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
44 }
45}
46
47} // Anonymous namespace
48
49Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
50 Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_,
51 VkExtent2D output_size, VkDescriptorSetLayout layout)
52 : device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_),
53 device_memory(device_memory_), image_count(image_count_) {
54 CreateDescriptorPool();
55 CreateDescriptorSets(layout);
56 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
57 CreateFSR(output_size);
58 }
59}
60
61Layer::~Layer() {
62 ReleaseRawImages();
63}
64
65void Layer::ConfigureDraw(PresentPushConstants* out_push_constants,
66 VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer,
67 VkSampler sampler, size_t image_index,
68 const Tegra::FramebufferConfig& framebuffer,
69 const Layout::FramebufferLayout& layout) {
70 const auto texture_info = rasterizer.AccelerateDisplay(
71 framebuffer, framebuffer.address + framebuffer.offset, framebuffer.stride);
72 const u32 texture_width = texture_info ? texture_info->width : framebuffer.width;
73 const u32 texture_height = texture_info ? texture_info->height : framebuffer.height;
74 const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width;
75 const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height;
76 const bool use_accelerated = texture_info.has_value();
77
78 RefreshResources(framebuffer);
79 SetAntiAliasPass();
80
81 // Finish any pending renderpass
82 scheduler.RequestOutsideRenderPassOperationContext();
83 scheduler.Wait(resource_ticks[image_index]);
84 SCOPE_EXIT({ resource_ticks[image_index] = scheduler.CurrentTick(); });
85
86 if (!use_accelerated) {
87 UpdateRawImage(framebuffer, image_index);
88 }
89
90 VkImage source_image = texture_info ? texture_info->image : *raw_images[image_index];
91 VkImageView source_image_view =
92 texture_info ? texture_info->image_view : *raw_image_views[image_index];
93
94 anti_alias->Draw(scheduler, image_index, &source_image, &source_image_view);
95
96 auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height);
97 const VkExtent2D render_extent{
98 .width = scaled_width,
99 .height = scaled_height,
100 };
101
102 if (fsr) {
103 source_image_view = fsr->Draw(scheduler, image_index, source_image, source_image_view,
104 render_extent, crop_rect);
105 crop_rect = {0, 0, 1, 1};
106 }
107
108 SetMatrixData(*out_push_constants, layout);
109 SetVertexData(*out_push_constants, layout, crop_rect);
110
111 UpdateDescriptorSet(source_image_view, sampler, image_index);
112 *out_descriptor_set = descriptor_sets[image_index];
113}
114
115void Layer::CreateDescriptorPool() {
116 descriptor_pool = CreateWrappedDescriptorPool(device, image_count, image_count);
117}
118
119void Layer::CreateDescriptorSets(VkDescriptorSetLayout layout) {
120 const std::vector layouts(image_count, layout);
121 descriptor_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts);
122}
123
124void Layer::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) {
125 const VkBufferCreateInfo ci{
126 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
127 .pNext = nullptr,
128 .flags = 0,
129 .size = CalculateBufferSize(framebuffer),
130 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
131 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
132 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
133 .queueFamilyIndexCount = 0,
134 .pQueueFamilyIndices = nullptr,
135 };
136
137 buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload);
138}
139
140void Layer::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
141 const auto format = GetFormat(framebuffer);
142 resource_ticks.resize(image_count);
143 raw_images.resize(image_count);
144 raw_image_views.resize(image_count);
145
146 for (size_t i = 0; i < image_count; ++i) {
147 raw_images[i] =
148 CreateWrappedImage(memory_allocator, {framebuffer.width, framebuffer.height}, format);
149 raw_image_views[i] = CreateWrappedImageView(device, raw_images[i], format);
150 }
151}
152
153void Layer::CreateFSR(VkExtent2D output_size) {
154 fsr = std::make_unique<FSR>(device, memory_allocator, image_count, output_size);
155}
156
157void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) {
158 if (framebuffer.width == raw_width && framebuffer.height == raw_height &&
159 framebuffer.pixel_format == pixel_format && !raw_images.empty()) {
160 return;
161 }
162
163 raw_width = framebuffer.width;
164 raw_height = framebuffer.height;
165 pixel_format = framebuffer.pixel_format;
166 anti_alias.reset();
167
168 ReleaseRawImages();
169 CreateStagingBuffer(framebuffer);
170 CreateRawImages(framebuffer);
171}
172
173void Layer::SetAntiAliasPass() {
174 if (anti_alias && anti_alias_setting == Settings::values.anti_aliasing.GetValue()) {
175 return;
176 }
177
178 anti_alias_setting = Settings::values.anti_aliasing.GetValue();
179
180 const VkExtent2D render_area{
181 .width = Settings::values.resolution_info.ScaleUp(raw_width),
182 .height = Settings::values.resolution_info.ScaleUp(raw_height),
183 };
184
185 switch (anti_alias_setting) {
186 case Settings::AntiAliasing::Fxaa:
187 anti_alias = std::make_unique<FXAA>(device, memory_allocator, image_count, render_area);
188 break;
189 case Settings::AntiAliasing::Smaa:
190 anti_alias = std::make_unique<SMAA>(device, memory_allocator, image_count, render_area);
191 break;
192 default:
193 anti_alias = std::make_unique<NoAA>();
194 break;
195 }
196}
197
198void Layer::ReleaseRawImages() {
199 for (const u64 tick : resource_ticks) {
200 scheduler.Wait(tick);
201 }
202 raw_images.clear();
203 buffer.reset();
204}
205
206u64 Layer::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const {
207 return GetSizeInBytes(framebuffer) * image_count;
208}
209
210u64 Layer::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer,
211 size_t image_index) const {
212 return GetSizeInBytes(framebuffer) * image_index;
213}
214
215void Layer::SetMatrixData(PresentPushConstants& data,
216 const Layout::FramebufferLayout& layout) const {
217 data.modelview_matrix =
218 MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height));
219}
220
221void Layer::SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout,
222 const Common::Rectangle<f32>& crop) const {
223 // Map the coordinates to the screen.
224 const auto& screen = layout.screen;
225 const auto x = static_cast<f32>(screen.left);
226 const auto y = static_cast<f32>(screen.top);
227 const auto w = static_cast<f32>(screen.GetWidth());
228 const auto h = static_cast<f32>(screen.GetHeight());
229
230 data.vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top);
231 data.vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top);
232 data.vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom);
233 data.vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom);
234}
235
236void Layer::UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index) {
237 const VkDescriptorImageInfo image_info{
238 .sampler = sampler,
239 .imageView = image_view,
240 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
241 };
242
243 const VkWriteDescriptorSet sampler_write{
244 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
245 .pNext = nullptr,
246 .dstSet = descriptor_sets[image_index],
247 .dstBinding = 0,
248 .dstArrayElement = 0,
249 .descriptorCount = 1,
250 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
251 .pImageInfo = &image_info,
252 .pBufferInfo = nullptr,
253 .pTexelBufferView = nullptr,
254 };
255
256 device.GetLogical().UpdateDescriptorSets(std::array{sampler_write}, {});
257}
258
259void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index) {
260 const std::span<u8> mapped_span = buffer.Mapped();
261
262 const u64 image_offset = GetRawImageOffset(framebuffer, image_index);
263
264 const DAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
265 const u8* const host_ptr = device_memory.GetPointer<u8>(framebuffer_addr);
266
267 // TODO(Rodrigo): Read this from HLE
268 constexpr u32 block_height_log2 = 4;
269 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer);
270 const u64 linear_size{GetSizeInBytes(framebuffer)};
271 const u64 tiled_size{Tegra::Texture::CalculateSize(
272 true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
273 Tegra::Texture::UnswizzleTexture(
274 mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
275 bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
276
277 const VkBufferImageCopy copy{
278 .bufferOffset = image_offset,
279 .bufferRowLength = 0,
280 .bufferImageHeight = 0,
281 .imageSubresource =
282 {
283 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
284 .mipLevel = 0,
285 .baseArrayLayer = 0,
286 .layerCount = 1,
287 },
288 .imageOffset = {.x = 0, .y = 0, .z = 0},
289 .imageExtent =
290 {
291 .width = framebuffer.width,
292 .height = framebuffer.height,
293 .depth = 1,
294 },
295 };
296 scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) {
297 const VkImage image = *raw_images[index];
298 const VkImageMemoryBarrier base_barrier{
299 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
300 .pNext = nullptr,
301 .srcAccessMask = 0,
302 .dstAccessMask = 0,
303 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
304 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
305 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
306 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
307 .image = image,
308 .subresourceRange{
309 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
310 .baseMipLevel = 0,
311 .levelCount = 1,
312 .baseArrayLayer = 0,
313 .layerCount = 1,
314 },
315 };
316 VkImageMemoryBarrier read_barrier = base_barrier;
317 read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
318 read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
319 read_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
320
321 VkImageMemoryBarrier write_barrier = base_barrier;
322 write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
323 write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
324 write_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
325
326 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
327 read_barrier);
328 cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
329 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
330 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
331 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
332 0, write_barrier);
333 });
334}
335
336} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/layer.h b/src/video_core/renderer_vulkan/present/layer.h
new file mode 100644
index 000000000..88d43fc5f
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/layer.h
@@ -0,0 +1,92 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/math_util.h"
7#include "video_core/host1x/gpu_device_memory_manager.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h"
9
10namespace Layout {
11struct FramebufferLayout;
12}
13
14namespace Tegra {
15struct FramebufferConfig;
16}
17
18namespace Service::android {
19enum class PixelFormat : u32;
20}
21
22namespace Settings {
23enum class AntiAliasing : u32;
24}
25
26namespace Vulkan {
27
28class AntiAliasPass;
29class Device;
30class FSR;
31class MemoryAllocator;
32struct PresentPushConstants;
33class RasterizerVulkan;
34class Scheduler;
35
36class Layer final {
37public:
38 explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler,
39 Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count,
40 VkExtent2D output_size, VkDescriptorSetLayout layout);
41 ~Layer();
42
43 void ConfigureDraw(PresentPushConstants* out_push_constants,
44 VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer,
45 VkSampler sampler, size_t image_index,
46 const Tegra::FramebufferConfig& framebuffer,
47 const Layout::FramebufferLayout& layout);
48
49private:
50 void CreateDescriptorPool();
51 void CreateDescriptorSets(VkDescriptorSetLayout layout);
52 void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer);
53 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
54 void CreateFSR(VkExtent2D output_size);
55
56 void RefreshResources(const Tegra::FramebufferConfig& framebuffer);
57 void SetAntiAliasPass();
58 void ReleaseRawImages();
59
60 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
61 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, size_t image_index) const;
62
63 void SetMatrixData(PresentPushConstants& data, const Layout::FramebufferLayout& layout) const;
64 void SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout,
65 const Common::Rectangle<f32>& crop) const;
66 void UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index);
67 void UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index);
68
69private:
70 const Device& device;
71 MemoryAllocator& memory_allocator;
72 Scheduler& scheduler;
73 Tegra::MaxwellDeviceMemoryManager& device_memory;
74 const size_t image_count{};
75 vk::DescriptorPool descriptor_pool{};
76 vk::DescriptorSets descriptor_sets{};
77
78 vk::Buffer buffer{};
79 std::vector<vk::Image> raw_images{};
80 std::vector<vk::ImageView> raw_image_views{};
81 u32 raw_width{};
82 u32 raw_height{};
83 Service::android::PixelFormat pixel_format{};
84
85 Settings::AntiAliasing anti_alias_setting{};
86 std::unique_ptr<AntiAliasPass> anti_alias{};
87
88 std::unique_ptr<FSR> fsr{};
89 std::vector<u64> resource_ticks{};
90};
91
92} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/present_push_constants.h b/src/video_core/renderer_vulkan/present/present_push_constants.h
new file mode 100644
index 000000000..f1949e7aa
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/present_push_constants.h
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Vulkan {
9
10struct ScreenRectVertex {
11 ScreenRectVertex() = default;
12 explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {}
13
14 std::array<f32, 2> position;
15 std::array<f32, 2> tex_coord;
16};
17
18static inline std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
19 // clang-format off
20 return { 2.f / width, 0.f, 0.f, 0.f,
21 0.f, 2.f / height, 0.f, 0.f,
22 0.f, 0.f, 1.f, 0.f,
23 -1.f, -1.f, 0.f, 1.f};
24 // clang-format on
25}
26
27struct PresentPushConstants {
28 std::array<f32, 4 * 4> modelview_matrix;
29 std::array<ScreenRectVertex, 4> vertices;
30};
31
32static_assert(sizeof(PresentPushConstants) <= 128, "Push constants are too large");
33
34} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/smaa.cpp b/src/video_core/renderer_vulkan/present/smaa.cpp
new file mode 100644
index 000000000..39645fd1d
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/smaa.cpp
@@ -0,0 +1,277 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <list>
5
6#include "common/assert.h"
7#include "common/polyfill_ranges.h"
8
9#include "video_core/renderer_vulkan/present/smaa.h"
10#include "video_core/renderer_vulkan/present/util.h"
11#include "video_core/renderer_vulkan/vk_scheduler.h"
12#include "video_core/renderer_vulkan/vk_shader_util.h"
13#include "video_core/smaa_area_tex.h"
14#include "video_core/smaa_search_tex.h"
15#include "video_core/vulkan_common/vulkan_device.h"
16
17#include "video_core/host_shaders/smaa_blending_weight_calculation_frag_spv.h"
18#include "video_core/host_shaders/smaa_blending_weight_calculation_vert_spv.h"
19#include "video_core/host_shaders/smaa_edge_detection_frag_spv.h"
20#include "video_core/host_shaders/smaa_edge_detection_vert_spv.h"
21#include "video_core/host_shaders/smaa_neighborhood_blending_frag_spv.h"
22#include "video_core/host_shaders/smaa_neighborhood_blending_vert_spv.h"
23
24namespace Vulkan {
25
26SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent)
27 : m_device(device), m_allocator(allocator), m_extent(extent),
28 m_image_count(static_cast<u32>(image_count)) {
29 CreateImages();
30 CreateRenderPasses();
31 CreateSampler();
32 CreateShaders();
33 CreateDescriptorPool();
34 CreateDescriptorSetLayouts();
35 CreateDescriptorSets();
36 CreatePipelineLayouts();
37 CreatePipelines();
38}
39
40SMAA::~SMAA() = default;
41
42void SMAA::CreateImages() {
43 static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
44 static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
45
46 m_static_images[Area] = CreateWrappedImage(m_allocator, area_extent, VK_FORMAT_R8G8_UNORM);
47 m_static_images[Search] = CreateWrappedImage(m_allocator, search_extent, VK_FORMAT_R8_UNORM);
48
49 m_static_image_views[Area] =
50 CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM);
51 m_static_image_views[Search] =
52 CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM);
53
54 for (u32 i = 0; i < m_image_count; i++) {
55 Images& images = m_dynamic_images.emplace_back();
56
57 images.images[Blend] =
58 CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
59 images.images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT);
60 images.images[Output] =
61 CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
62
63 images.image_views[Blend] =
64 CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT);
65 images.image_views[Edges] =
66 CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT);
67 images.image_views[Output] =
68 CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT);
69 }
70}
71
72void SMAA::CreateRenderPasses() {
73 m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT);
74 m_renderpasses[BlendingWeightCalculation] =
75 CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
76 m_renderpasses[NeighborhoodBlending] =
77 CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
78
79 for (auto& images : m_dynamic_images) {
80 images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer(
81 m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent);
82
83 images.framebuffers[BlendingWeightCalculation] =
84 CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation],
85 images.image_views[Blend], m_extent);
86
87 images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer(
88 m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent);
89 }
90}
91
92void SMAA::CreateSampler() {
93 m_sampler = CreateWrappedSampler(m_device);
94}
95
96void SMAA::CreateShaders() {
97 // These match the order of the SMAAStage enum
98 static constexpr std::array vert_shader_sources{
99 ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV),
100 ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV),
101 ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV),
102 };
103 static constexpr std::array frag_shader_sources{
104 ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV),
105 ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV),
106 ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV),
107 };
108
109 for (size_t i = 0; i < MaxSMAAStage; i++) {
110 m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]);
111 m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]);
112 }
113}
114
115void SMAA::CreateDescriptorPool() {
116 // Edge detection: 1 descriptor
117 // Blending weight calculation: 3 descriptors
118 // Neighborhood blending: 2 descriptors
119
120 // 6 descriptors, 3 descriptor sets per image
121 m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count);
122}
123
124void SMAA::CreateDescriptorSetLayouts() {
125 m_descriptor_set_layouts[EdgeDetection] =
126 CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
127 m_descriptor_set_layouts[BlendingWeightCalculation] =
128 CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
129 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
130 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
131 m_descriptor_set_layouts[NeighborhoodBlending] =
132 CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
133 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
134}
135
136void SMAA::CreateDescriptorSets() {
137 std::vector<VkDescriptorSetLayout> layouts(m_descriptor_set_layouts.size());
138 std::ranges::transform(m_descriptor_set_layouts, layouts.begin(),
139 [](auto& layout) { return *layout; });
140
141 for (auto& images : m_dynamic_images) {
142 images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts);
143 }
144}
145
146void SMAA::CreatePipelineLayouts() {
147 for (size_t i = 0; i < MaxSMAAStage; i++) {
148 m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]);
149 }
150}
151
152void SMAA::CreatePipelines() {
153 for (size_t i = 0; i < MaxSMAAStage; i++) {
154 m_pipelines[i] =
155 CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i],
156 std::tie(m_vertex_shaders[i], m_fragment_shaders[i]));
157 }
158}
159
160void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) {
161 Images& images = m_dynamic_images[image_index];
162 std::vector<VkDescriptorImageInfo> image_infos;
163 std::vector<VkWriteDescriptorSet> updates;
164 image_infos.reserve(6);
165
166 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view,
167 images.descriptor_sets[EdgeDetection], 0));
168
169 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges],
170 images.descriptor_sets[BlendingWeightCalculation],
171 0));
172 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area],
173 images.descriptor_sets[BlendingWeightCalculation],
174 1));
175 updates.push_back(
176 CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search],
177 images.descriptor_sets[BlendingWeightCalculation], 2));
178
179 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view,
180 images.descriptor_sets[NeighborhoodBlending], 0));
181 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend],
182 images.descriptor_sets[NeighborhoodBlending], 1));
183
184 m_device.GetLogical().UpdateDescriptorSets(updates, {});
185}
186
187void SMAA::UploadImages(Scheduler& scheduler) {
188 if (m_images_ready) {
189 return;
190 }
191
192 static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
193 static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
194
195 UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent,
196 VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes));
197 UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent,
198 VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes));
199
200 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
201 for (auto& images : m_dynamic_images) {
202 for (size_t i = 0; i < MaxDynamicImage; i++) {
203 ClearColorImage(cmdbuf, *images.images[i]);
204 }
205 }
206 });
207 scheduler.Finish();
208
209 m_images_ready = true;
210}
211
212void SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
213 VkImageView* inout_image_view) {
214 Images& images = m_dynamic_images[image_index];
215
216 VkImage input_image = *inout_image;
217 VkImage output_image = *images.images[Output];
218 VkImage edges_image = *images.images[Edges];
219 VkImage blend_image = *images.images[Blend];
220
221 VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection];
222 VkDescriptorSet blending_weight_calculation_descriptor_set =
223 images.descriptor_sets[BlendingWeightCalculation];
224 VkDescriptorSet neighborhood_blending_descriptor_set =
225 images.descriptor_sets[NeighborhoodBlending];
226
227 VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection];
228 VkFramebuffer blending_weight_calculation_framebuffer =
229 *images.framebuffers[BlendingWeightCalculation];
230 VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending];
231
232 UploadImages(scheduler);
233 UpdateDescriptorSets(*inout_image_view, image_index);
234
235 scheduler.RequestOutsideRenderPassOperationContext();
236 scheduler.Record([=, this](vk::CommandBuffer cmdbuf) {
237 TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL);
238 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
239 BeginRenderPass(cmdbuf, *m_renderpasses[EdgeDetection], edge_detection_framebuffer,
240 m_extent);
241 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]);
242 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
243 *m_pipeline_layouts[EdgeDetection], 0,
244 edge_detection_descriptor_set, {});
245 cmdbuf.Draw(3, 1, 0, 0);
246 cmdbuf.EndRenderPass();
247
248 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
249 TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL);
250 BeginRenderPass(cmdbuf, *m_renderpasses[BlendingWeightCalculation],
251 blending_weight_calculation_framebuffer, m_extent);
252 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS,
253 *m_pipelines[BlendingWeightCalculation]);
254 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
255 *m_pipeline_layouts[BlendingWeightCalculation], 0,
256 blending_weight_calculation_descriptor_set, {});
257 cmdbuf.Draw(3, 1, 0, 0);
258 cmdbuf.EndRenderPass();
259
260 TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL);
261 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
262 BeginRenderPass(cmdbuf, *m_renderpasses[NeighborhoodBlending],
263 neighborhood_blending_framebuffer, m_extent);
264 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]);
265 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
266 *m_pipeline_layouts[NeighborhoodBlending], 0,
267 neighborhood_blending_descriptor_set, {});
268 cmdbuf.Draw(3, 1, 0, 0);
269 cmdbuf.EndRenderPass();
270 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
271 });
272
273 *inout_image = *images.images[Output];
274 *inout_image_view = *images.image_views[Output];
275}
276
277} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_smaa.h b/src/video_core/renderer_vulkan/present/smaa.h
index 0e214258a..fdf6def07 100644
--- a/src/video_core/renderer_vulkan/vk_smaa.h
+++ b/src/video_core/renderer_vulkan/present/smaa.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include "video_core/renderer_vulkan/present/anti_alias_pass.h"
7#include "video_core/vulkan_common/vulkan_memory_allocator.h" 8#include "video_core/vulkan_common/vulkan_memory_allocator.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h" 9#include "video_core/vulkan_common/vulkan_wrapper.h"
9 10
@@ -13,12 +14,14 @@ class Device;
13class Scheduler; 14class Scheduler;
14class StagingBufferPool; 15class StagingBufferPool;
15 16
16class SMAA { 17class SMAA final : public AntiAliasPass {
17public: 18public:
18 explicit SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, 19 explicit SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count,
19 VkExtent2D extent); 20 VkExtent2D extent);
20 VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, 21 ~SMAA() override;
21 VkImageView source_image_view); 22
23 void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image,
24 VkImageView* inout_image_view) override;
22 25
23private: 26private:
24 enum SMAAStage { 27 enum SMAAStage {
diff --git a/src/video_core/renderer_vulkan/vk_smaa.cpp b/src/video_core/renderer_vulkan/present/util.cpp
index 70644ea82..6ee16595d 100644
--- a/src/video_core/renderer_vulkan/vk_smaa.cpp
+++ b/src/video_core/renderer_vulkan/present/util.cpp
@@ -1,29 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <list>
5
6#include "common/assert.h" 4#include "common/assert.h"
7#include "common/polyfill_ranges.h" 5#include "common/polyfill_ranges.h"
8 6#include "video_core/renderer_vulkan/present/util.h"
9#include "video_core/renderer_vulkan/vk_scheduler.h"
10#include "video_core/renderer_vulkan/vk_shader_util.h"
11#include "video_core/renderer_vulkan/vk_smaa.h"
12#include "video_core/smaa_area_tex.h"
13#include "video_core/smaa_search_tex.h"
14#include "video_core/vulkan_common/vulkan_device.h"
15
16#include "video_core/host_shaders/smaa_blending_weight_calculation_frag_spv.h"
17#include "video_core/host_shaders/smaa_blending_weight_calculation_vert_spv.h"
18#include "video_core/host_shaders/smaa_edge_detection_frag_spv.h"
19#include "video_core/host_shaders/smaa_edge_detection_vert_spv.h"
20#include "video_core/host_shaders/smaa_neighborhood_blending_frag_spv.h"
21#include "video_core/host_shaders/smaa_neighborhood_blending_vert_spv.h"
22 7
23namespace Vulkan { 8namespace Vulkan {
24namespace {
25 9
26#define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) 10vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage) {
11 const VkBufferCreateInfo dst_buffer_info{
12 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
13 .pNext = nullptr,
14 .flags = 0,
15 .size = size,
16 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
17 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
18 .queueFamilyIndexCount = 0,
19 .pQueueFamilyIndices = nullptr,
20 };
21 return allocator.CreateBuffer(dst_buffer_info, usage);
22}
27 23
28vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) { 24vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) {
29 const VkImageCreateInfo image_ci{ 25 const VkImageCreateInfo image_ci{
@@ -48,7 +44,7 @@ vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions,
48} 44}
49 45
50void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, 46void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout,
51 VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) { 47 VkImageLayout source_layout) {
52 constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | 48 constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
53 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}; 49 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT};
54 const VkImageMemoryBarrier barrier{ 50 const VkImageMemoryBarrier barrier{
@@ -75,7 +71,7 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo
75 71
76void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, 72void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler,
77 vk::Image& image, VkExtent2D dimensions, VkFormat format, 73 vk::Image& image, VkExtent2D dimensions, VkFormat format,
78 std::span<const u8> initial_contents = {}) { 74 std::span<const u8> initial_contents) {
79 const VkBufferCreateInfo upload_ci = { 75 const VkBufferCreateInfo upload_ci = {
80 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 76 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
81 .pNext = nullptr, 77 .pNext = nullptr,
@@ -114,6 +110,70 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc
114 scheduler.Finish(); 110 scheduler.Finish();
115} 111}
116 112
113void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer,
114 VkExtent3D extent) {
115 const VkImageMemoryBarrier read_barrier{
116 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
117 .pNext = nullptr,
118 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
119 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
120 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
121 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
122 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
123 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
124 .image = image,
125 .subresourceRange{
126 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
127 .baseMipLevel = 0,
128 .levelCount = VK_REMAINING_MIP_LEVELS,
129 .baseArrayLayer = 0,
130 .layerCount = VK_REMAINING_ARRAY_LAYERS,
131 },
132 };
133 const VkImageMemoryBarrier image_write_barrier{
134 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
135 .pNext = nullptr,
136 .srcAccessMask = 0,
137 .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
138 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
139 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
140 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
141 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
142 .image = image,
143 .subresourceRange{
144 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
145 .baseMipLevel = 0,
146 .levelCount = VK_REMAINING_MIP_LEVELS,
147 .baseArrayLayer = 0,
148 .layerCount = VK_REMAINING_ARRAY_LAYERS,
149 },
150 };
151 static constexpr VkMemoryBarrier memory_write_barrier{
152 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
153 .pNext = nullptr,
154 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
155 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
156 };
157 const VkBufferImageCopy copy{
158 .bufferOffset = 0,
159 .bufferRowLength = 0,
160 .bufferImageHeight = 0,
161 .imageSubresource{
162 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
163 .mipLevel = 0,
164 .baseArrayLayer = 0,
165 .layerCount = 1,
166 },
167 .imageOffset{.x = 0, .y = 0, .z = 0},
168 .imageExtent{extent},
169 };
170 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
171 read_barrier);
172 cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, copy);
173 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0,
174 memory_write_barrier, nullptr, image_write_barrier);
175}
176
117vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { 177vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) {
118 return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ 178 return device.GetLogical().CreateImageView(VkImageViewCreateInfo{
119 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 179 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
@@ -131,16 +191,18 @@ vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkF
131 }); 191 });
132} 192}
133 193
134vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format) { 194vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format,
195 VkImageLayout initial_layout) {
135 const VkAttachmentDescription attachment{ 196 const VkAttachmentDescription attachment{
136 .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, 197 .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT,
137 .format = format, 198 .format = format,
138 .samples = VK_SAMPLE_COUNT_1_BIT, 199 .samples = VK_SAMPLE_COUNT_1_BIT,
139 .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, 200 .loadOp = initial_layout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE
201 : VK_ATTACHMENT_LOAD_OP_LOAD,
140 .storeOp = VK_ATTACHMENT_STORE_OP_STORE, 202 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
141 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD, 203 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
142 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, 204 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE,
143 .initialLayout = VK_IMAGE_LAYOUT_GENERAL, 205 .initialLayout = initial_layout,
144 .finalLayout = VK_IMAGE_LAYOUT_GENERAL, 206 .finalLayout = VK_IMAGE_LAYOUT_GENERAL,
145 }; 207 };
146 208
@@ -200,13 +262,13 @@ vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& r
200 }); 262 });
201} 263}
202 264
203vk::Sampler CreateWrappedSampler(const Device& device) { 265vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter) {
204 return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ 266 return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
205 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, 267 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
206 .pNext = nullptr, 268 .pNext = nullptr,
207 .flags = 0, 269 .flags = 0,
208 .magFilter = VK_FILTER_LINEAR, 270 .magFilter = filter,
209 .minFilter = VK_FILTER_LINEAR, 271 .minFilter = filter,
210 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, 272 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
211 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 273 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
212 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, 274 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
@@ -233,30 +295,34 @@ vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span<const
233 }); 295 });
234} 296}
235 297
236vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, u32 max_descriptors, 298vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, size_t max_descriptors,
237 u32 max_sets) { 299 size_t max_sets,
238 const VkDescriptorPoolSize pool_size{ 300 std::initializer_list<VkDescriptorType> types) {
239 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 301 std::vector<VkDescriptorPoolSize> pool_sizes(types.size());
240 .descriptorCount = static_cast<u32>(max_descriptors), 302 for (u32 i = 0; i < types.size(); i++) {
241 }; 303 pool_sizes[i] = VkDescriptorPoolSize{
304 .type = std::data(types)[i],
305 .descriptorCount = static_cast<u32>(max_descriptors),
306 };
307 }
242 308
243 return device.GetLogical().CreateDescriptorPool(VkDescriptorPoolCreateInfo{ 309 return device.GetLogical().CreateDescriptorPool(VkDescriptorPoolCreateInfo{
244 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, 310 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
245 .pNext = nullptr, 311 .pNext = nullptr,
246 .flags = 0, 312 .flags = 0,
247 .maxSets = max_sets, 313 .maxSets = static_cast<u32>(max_sets),
248 .poolSizeCount = 1, 314 .poolSizeCount = static_cast<u32>(pool_sizes.size()),
249 .pPoolSizes = &pool_size, 315 .pPoolSizes = pool_sizes.data(),
250 }); 316 });
251} 317}
252 318
253vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout(const Device& device, 319vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout(
254 u32 max_sampler_bindings) { 320 const Device& device, std::initializer_list<VkDescriptorType> types) {
255 std::vector<VkDescriptorSetLayoutBinding> bindings(max_sampler_bindings); 321 std::vector<VkDescriptorSetLayoutBinding> bindings(types.size());
256 for (u32 i = 0; i < max_sampler_bindings; i++) { 322 for (size_t i = 0; i < types.size(); i++) {
257 bindings[i] = { 323 bindings[i] = {
258 .binding = i, 324 .binding = static_cast<u32>(i),
259 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 325 .descriptorType = std::data(types)[i],
260 .descriptorCount = 1, 326 .descriptorCount = 1,
261 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 327 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
262 .pImmutableSamplers = nullptr, 328 .pImmutableSamplers = nullptr,
@@ -298,7 +364,8 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device,
298 364
299vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, 365vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
300 vk::PipelineLayout& layout, 366 vk::PipelineLayout& layout,
301 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) { 367 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders,
368 bool enable_blending) {
302 const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ 369 const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{
303 { 370 {
304 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 371 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
@@ -376,7 +443,7 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
376 .alphaToOneEnable = VK_FALSE, 443 .alphaToOneEnable = VK_FALSE,
377 }; 444 };
378 445
379 constexpr VkPipelineColorBlendAttachmentState color_blend_attachment{ 446 constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{
380 .blendEnable = VK_FALSE, 447 .blendEnable = VK_FALSE,
381 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, 448 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
382 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, 449 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
@@ -388,6 +455,18 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
388 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, 455 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
389 }; 456 };
390 457
458 constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_enabled{
459 .blendEnable = VK_TRUE,
460 .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
461 .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
462 .colorBlendOp = VK_BLEND_OP_ADD,
463 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
464 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
465 .alphaBlendOp = VK_BLEND_OP_ADD,
466 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
467 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
468 };
469
391 const VkPipelineColorBlendStateCreateInfo color_blend_ci{ 470 const VkPipelineColorBlendStateCreateInfo color_blend_ci{
392 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 471 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
393 .pNext = nullptr, 472 .pNext = nullptr,
@@ -395,7 +474,8 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp
395 .logicOpEnable = VK_FALSE, 474 .logicOpEnable = VK_FALSE,
396 .logicOp = VK_LOGIC_OP_COPY, 475 .logicOp = VK_LOGIC_OP_COPY,
397 .attachmentCount = 1, 476 .attachmentCount = 1,
398 .pAttachments = &color_blend_attachment, 477 .pAttachments =
478 enable_blending ? &color_blend_attachment_enabled : &color_blend_attachment_disabled,
399 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, 479 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
400 }; 480 };
401 481
@@ -459,6 +539,56 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>
459 }; 539 };
460} 540}
461 541
542vk::Sampler CreateBilinearSampler(const Device& device) {
543 const VkSamplerCreateInfo ci{
544 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
545 .pNext = nullptr,
546 .flags = 0,
547 .magFilter = VK_FILTER_LINEAR,
548 .minFilter = VK_FILTER_LINEAR,
549 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
550 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
551 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
552 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
553 .mipLodBias = 0.0f,
554 .anisotropyEnable = VK_FALSE,
555 .maxAnisotropy = 0.0f,
556 .compareEnable = VK_FALSE,
557 .compareOp = VK_COMPARE_OP_NEVER,
558 .minLod = 0.0f,
559 .maxLod = 0.0f,
560 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
561 .unnormalizedCoordinates = VK_FALSE,
562 };
563
564 return device.GetLogical().CreateSampler(ci);
565}
566
567vk::Sampler CreateNearestNeighborSampler(const Device& device) {
568 const VkSamplerCreateInfo ci_nn{
569 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
570 .pNext = nullptr,
571 .flags = 0,
572 .magFilter = VK_FILTER_NEAREST,
573 .minFilter = VK_FILTER_NEAREST,
574 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
575 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
576 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
577 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
578 .mipLodBias = 0.0f,
579 .anisotropyEnable = VK_FALSE,
580 .maxAnisotropy = 0.0f,
581 .compareEnable = VK_FALSE,
582 .compareOp = VK_COMPARE_OP_NEVER,
583 .minLod = 0.0f,
584 .maxLod = 0.0f,
585 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
586 .unnormalizedCoordinates = VK_FALSE,
587 };
588
589 return device.GetLogical().CreateSampler(ci_nn);
590}
591
462void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { 592void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) {
463 static constexpr std::array<VkImageSubresourceRange, 1> subresources{{{ 593 static constexpr std::array<VkImageSubresourceRange, 1> subresources{{{
464 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 594 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
@@ -471,12 +601,12 @@ void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) {
471 cmdbuf.ClearColorImage(image, VK_IMAGE_LAYOUT_GENERAL, {}, subresources); 601 cmdbuf.ClearColorImage(image, VK_IMAGE_LAYOUT_GENERAL, {}, subresources);
472} 602}
473 603
474void BeginRenderPass(vk::CommandBuffer& cmdbuf, vk::RenderPass& render_pass, 604void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer,
475 VkFramebuffer framebuffer, VkExtent2D extent) { 605 VkExtent2D extent) {
476 const VkRenderPassBeginInfo renderpass_bi{ 606 const VkRenderPassBeginInfo renderpass_bi{
477 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, 607 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
478 .pNext = nullptr, 608 .pNext = nullptr,
479 .renderPass = *render_pass, 609 .renderPass = render_pass,
480 .framebuffer = framebuffer, 610 .framebuffer = framebuffer,
481 .renderArea{ 611 .renderArea{
482 .offset{}, 612 .offset{},
@@ -503,248 +633,4 @@ void BeginRenderPass(vk::CommandBuffer& cmdbuf, vk::RenderPass& render_pass,
503 cmdbuf.SetScissor(0, scissor); 633 cmdbuf.SetScissor(0, scissor);
504} 634}
505 635
506} // Anonymous namespace
507
508SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent)
509 : m_device(device), m_allocator(allocator), m_extent(extent),
510 m_image_count(static_cast<u32>(image_count)) {
511 CreateImages();
512 CreateRenderPasses();
513 CreateSampler();
514 CreateShaders();
515 CreateDescriptorPool();
516 CreateDescriptorSetLayouts();
517 CreateDescriptorSets();
518 CreatePipelineLayouts();
519 CreatePipelines();
520}
521
522void SMAA::CreateImages() {
523 static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
524 static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
525
526 m_static_images[Area] = CreateWrappedImage(m_allocator, area_extent, VK_FORMAT_R8G8_UNORM);
527 m_static_images[Search] = CreateWrappedImage(m_allocator, search_extent, VK_FORMAT_R8_UNORM);
528
529 m_static_image_views[Area] =
530 CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM);
531 m_static_image_views[Search] =
532 CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM);
533
534 for (u32 i = 0; i < m_image_count; i++) {
535 Images& images = m_dynamic_images.emplace_back();
536
537 images.images[Blend] =
538 CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
539 images.images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT);
540 images.images[Output] =
541 CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT);
542
543 images.image_views[Blend] =
544 CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT);
545 images.image_views[Edges] =
546 CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT);
547 images.image_views[Output] =
548 CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT);
549 }
550}
551
552void SMAA::CreateRenderPasses() {
553 m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT);
554 m_renderpasses[BlendingWeightCalculation] =
555 CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
556 m_renderpasses[NeighborhoodBlending] =
557 CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT);
558
559 for (auto& images : m_dynamic_images) {
560 images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer(
561 m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent);
562
563 images.framebuffers[BlendingWeightCalculation] =
564 CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation],
565 images.image_views[Blend], m_extent);
566
567 images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer(
568 m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent);
569 }
570}
571
572void SMAA::CreateSampler() {
573 m_sampler = CreateWrappedSampler(m_device);
574}
575
576void SMAA::CreateShaders() {
577 // These match the order of the SMAAStage enum
578 static constexpr std::array vert_shader_sources{
579 ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV),
580 ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV),
581 ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV),
582 };
583 static constexpr std::array frag_shader_sources{
584 ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV),
585 ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV),
586 ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV),
587 };
588
589 for (size_t i = 0; i < MaxSMAAStage; i++) {
590 m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]);
591 m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]);
592 }
593}
594
595void SMAA::CreateDescriptorPool() {
596 // Edge detection: 1 descriptor
597 // Blending weight calculation: 3 descriptors
598 // Neighborhood blending: 2 descriptors
599
600 // 6 descriptors, 3 descriptor sets per image
601 m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count);
602}
603
604void SMAA::CreateDescriptorSetLayouts() {
605 m_descriptor_set_layouts[EdgeDetection] = CreateWrappedDescriptorSetLayout(m_device, 1);
606 m_descriptor_set_layouts[BlendingWeightCalculation] =
607 CreateWrappedDescriptorSetLayout(m_device, 3);
608 m_descriptor_set_layouts[NeighborhoodBlending] = CreateWrappedDescriptorSetLayout(m_device, 2);
609}
610
611void SMAA::CreateDescriptorSets() {
612 std::vector<VkDescriptorSetLayout> layouts(m_descriptor_set_layouts.size());
613 std::ranges::transform(m_descriptor_set_layouts, layouts.begin(),
614 [](auto& layout) { return *layout; });
615
616 for (auto& images : m_dynamic_images) {
617 images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts);
618 }
619}
620
621void SMAA::CreatePipelineLayouts() {
622 for (size_t i = 0; i < MaxSMAAStage; i++) {
623 m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]);
624 }
625}
626
627void SMAA::CreatePipelines() {
628 for (size_t i = 0; i < MaxSMAAStage; i++) {
629 m_pipelines[i] =
630 CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i],
631 std::tie(m_vertex_shaders[i], m_fragment_shaders[i]));
632 }
633}
634
635void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) {
636 Images& images = m_dynamic_images[image_index];
637 std::vector<VkDescriptorImageInfo> image_infos;
638 std::vector<VkWriteDescriptorSet> updates;
639 image_infos.reserve(6);
640
641 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view,
642 images.descriptor_sets[EdgeDetection], 0));
643
644 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges],
645 images.descriptor_sets[BlendingWeightCalculation],
646 0));
647 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area],
648 images.descriptor_sets[BlendingWeightCalculation],
649 1));
650 updates.push_back(
651 CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search],
652 images.descriptor_sets[BlendingWeightCalculation], 2));
653
654 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view,
655 images.descriptor_sets[NeighborhoodBlending], 0));
656 updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend],
657 images.descriptor_sets[NeighborhoodBlending], 1));
658
659 m_device.GetLogical().UpdateDescriptorSets(updates, {});
660}
661
662void SMAA::UploadImages(Scheduler& scheduler) {
663 if (m_images_ready) {
664 return;
665 }
666
667 static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT};
668 static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT};
669
670 UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent,
671 VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes));
672 UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent,
673 VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes));
674
675 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
676 for (auto& images : m_dynamic_images) {
677 for (size_t i = 0; i < MaxDynamicImage; i++) {
678 ClearColorImage(cmdbuf, *images.images[i]);
679 }
680 }
681 });
682 scheduler.Finish();
683
684 m_images_ready = true;
685}
686
687VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image,
688 VkImageView source_image_view) {
689 Images& images = m_dynamic_images[image_index];
690
691 VkImage output_image = *images.images[Output];
692 VkImage edges_image = *images.images[Edges];
693 VkImage blend_image = *images.images[Blend];
694
695 VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection];
696 VkDescriptorSet blending_weight_calculation_descriptor_set =
697 images.descriptor_sets[BlendingWeightCalculation];
698 VkDescriptorSet neighborhood_blending_descriptor_set =
699 images.descriptor_sets[NeighborhoodBlending];
700
701 VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection];
702 VkFramebuffer blending_weight_calculation_framebuffer =
703 *images.framebuffers[BlendingWeightCalculation];
704 VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending];
705
706 UploadImages(scheduler);
707 UpdateDescriptorSets(source_image_view, image_index);
708
709 scheduler.RequestOutsideRenderPassOperationContext();
710 scheduler.Record([=, this](vk::CommandBuffer cmdbuf) {
711 TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL);
712 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
713 BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer,
714 m_extent);
715 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]);
716 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
717 *m_pipeline_layouts[EdgeDetection], 0,
718 edge_detection_descriptor_set, {});
719 cmdbuf.Draw(3, 1, 0, 0);
720 cmdbuf.EndRenderPass();
721
722 TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL);
723 TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL);
724 BeginRenderPass(cmdbuf, m_renderpasses[BlendingWeightCalculation],
725 blending_weight_calculation_framebuffer, m_extent);
726 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS,
727 *m_pipelines[BlendingWeightCalculation]);
728 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
729 *m_pipeline_layouts[BlendingWeightCalculation], 0,
730 blending_weight_calculation_descriptor_set, {});
731 cmdbuf.Draw(3, 1, 0, 0);
732 cmdbuf.EndRenderPass();
733
734 TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL);
735 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
736 BeginRenderPass(cmdbuf, m_renderpasses[NeighborhoodBlending],
737 neighborhood_blending_framebuffer, m_extent);
738 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]);
739 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS,
740 *m_pipeline_layouts[NeighborhoodBlending], 0,
741 neighborhood_blending_descriptor_set, {});
742 cmdbuf.Draw(3, 1, 0, 0);
743 cmdbuf.EndRenderPass();
744 TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL);
745 });
746
747 return *images.image_views[Output];
748}
749
750} // namespace Vulkan 636} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h
new file mode 100644
index 000000000..1104aaa15
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/util.h
@@ -0,0 +1,56 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "video_core/renderer_vulkan/vk_scheduler.h"
7#include "video_core/vulkan_common/vulkan_memory_allocator.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h"
9
10namespace Vulkan {
11
12#define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0])))
13
14vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage);
15
16vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format);
17void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout,
18 VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL);
19void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler,
20 vk::Image& image, VkExtent2D dimensions, VkFormat format,
21 std::span<const u8> initial_contents = {});
22void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer,
23 VkExtent3D extent);
24void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image);
25
26vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format);
27vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format,
28 VkImageLayout initial_layout = VK_IMAGE_LAYOUT_GENERAL);
29vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& render_pass,
30 vk::ImageView& dest_image, VkExtent2D extent);
31vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter = VK_FILTER_LINEAR);
32vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span<const u32> code);
33vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, size_t max_descriptors,
34 size_t max_sets,
35 std::initializer_list<VkDescriptorType> types = {
36 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
37vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout(
38 const Device& device, std::initializer_list<VkDescriptorType> types);
39vk::DescriptorSets CreateWrappedDescriptorSets(vk::DescriptorPool& pool,
40 vk::Span<VkDescriptorSetLayout> layouts);
41vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device,
42 vk::DescriptorSetLayout& layout);
43vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass,
44 vk::PipelineLayout& layout,
45 std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders,
46 bool enable_blending = false);
47VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images,
48 VkSampler sampler, VkImageView view,
49 VkDescriptorSet set, u32 binding);
50vk::Sampler CreateBilinearSampler(const Device& device);
51vk::Sampler CreateNearestNeighborSampler(const Device& device);
52
53void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer,
54 VkExtent2D extent);
55
56} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp
new file mode 100644
index 000000000..c5db0230d
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp
@@ -0,0 +1,137 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/frontend/framebuffer_layout.h"
5#include "video_core/framebuffer_config.h"
6#include "video_core/host_shaders/vulkan_present_vert_spv.h"
7#include "video_core/renderer_vulkan/present/layer.h"
8#include "video_core/renderer_vulkan/present/present_push_constants.h"
9#include "video_core/renderer_vulkan/present/util.h"
10#include "video_core/renderer_vulkan/present/window_adapt_pass.h"
11#include "video_core/renderer_vulkan/vk_present_manager.h"
12#include "video_core/renderer_vulkan/vk_shader_util.h"
13#include "video_core/vulkan_common/vulkan_device.h"
14#include "video_core/vulkan_common/vulkan_memory_allocator.h"
15
16namespace Vulkan {
17
18WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format,
19 vk::Sampler&& sampler_, vk::ShaderModule&& fragment_shader_)
20 : device(device_), sampler(std::move(sampler_)), fragment_shader(std::move(fragment_shader_)) {
21 CreateDescriptorSetLayout();
22 CreatePipelineLayout();
23 CreateVertexShader();
24 CreateRenderPass(frame_format);
25 CreatePipeline();
26}
27
28WindowAdaptPass::~WindowAdaptPass() = default;
29
30void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index,
31 std::list<Layer>& layers,
32 std::span<const Tegra::FramebufferConfig> configs,
33 const Layout::FramebufferLayout& layout, Frame* dst) {
34
35 const VkFramebuffer host_framebuffer{*dst->framebuffer};
36 const VkRenderPass renderpass{*render_pass};
37 const VkPipeline graphics_pipeline{*pipeline};
38 const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout};
39 const VkExtent2D render_area{
40 .width = dst->width,
41 .height = dst->height,
42 };
43
44 const size_t layer_count = configs.size();
45 std::vector<PresentPushConstants> push_constants(layer_count);
46 std::vector<VkDescriptorSet> descriptor_sets(layer_count);
47
48 auto layer_it = layers.begin();
49 for (size_t i = 0; i < layer_count; i++) {
50 layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler,
51 image_index, configs[i], layout);
52 layer_it++;
53 }
54
55 scheduler.Record([=](vk::CommandBuffer cmdbuf) {
56 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
57 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
58 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
59 const VkClearAttachment clear_attachment{
60 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
61 .colorAttachment = 0,
62 .clearValue =
63 {
64 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
65 },
66 };
67 const VkClearRect clear_rect{
68 .rect =
69 {
70 .offset = {0, 0},
71 .extent = render_area,
72 },
73 .baseArrayLayer = 0,
74 .layerCount = 1,
75 };
76
77 BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area);
78 cmdbuf.ClearAttachments({clear_attachment}, {clear_rect});
79
80 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
81 for (size_t i = 0; i < layer_count; i++) {
82 cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT,
83 push_constants[i]);
84 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0,
85 descriptor_sets[i], {});
86 cmdbuf.Draw(4, 1, 0, 0);
87 }
88
89 cmdbuf.EndRenderPass();
90 });
91}
92
93VkDescriptorSetLayout WindowAdaptPass::GetDescriptorSetLayout() {
94 return *descriptor_set_layout;
95}
96
97VkRenderPass WindowAdaptPass::GetRenderPass() {
98 return *render_pass;
99}
100
101void WindowAdaptPass::CreateDescriptorSetLayout() {
102 descriptor_set_layout =
103 CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER});
104}
105
106void WindowAdaptPass::CreatePipelineLayout() {
107 const VkPushConstantRange range{
108 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
109 .offset = 0,
110 .size = sizeof(PresentPushConstants),
111 };
112
113 pipeline_layout = device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{
114 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
115 .pNext = nullptr,
116 .flags = 0,
117 .setLayoutCount = 1,
118 .pSetLayouts = descriptor_set_layout.address(),
119 .pushConstantRangeCount = 1,
120 .pPushConstantRanges = &range,
121 });
122}
123
124void WindowAdaptPass::CreateVertexShader() {
125 vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV);
126}
127
128void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) {
129 render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED);
130}
131
132void WindowAdaptPass::CreatePipeline() {
133 pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout,
134 std::tie(vertex_shader, fragment_shader), false);
135}
136
137} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.h b/src/video_core/renderer_vulkan/present/window_adapt_pass.h
new file mode 100644
index 000000000..0e2edfc31
--- /dev/null
+++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.h
@@ -0,0 +1,58 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <list>
7
8#include "common/math_util.h"
9#include "video_core/vulkan_common/vulkan_wrapper.h"
10
11namespace Layout {
12struct FramebufferLayout;
13}
14
15namespace Tegra {
16struct FramebufferConfig;
17}
18
19namespace Vulkan {
20
21class Device;
22struct Frame;
23class Layer;
24class Scheduler;
25class RasterizerVulkan;
26
27class WindowAdaptPass final {
28public:
29 explicit WindowAdaptPass(const Device& device, VkFormat frame_format, vk::Sampler&& sampler,
30 vk::ShaderModule&& fragment_shader);
31 ~WindowAdaptPass();
32
33 void Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index,
34 std::list<Layer>& layers, std::span<const Tegra::FramebufferConfig> configs,
35 const Layout::FramebufferLayout& layout, Frame* dst);
36
37 VkDescriptorSetLayout GetDescriptorSetLayout();
38 VkRenderPass GetRenderPass();
39
40private:
41 void CreateDescriptorSetLayout();
42 void CreatePipelineLayout();
43 void CreateVertexShader();
44 void CreateRenderPass(VkFormat frame_format);
45 void CreatePipeline();
46
47private:
48 const Device& device;
49 vk::DescriptorSetLayout descriptor_set_layout;
50 vk::PipelineLayout pipeline_layout;
51 vk::Sampler sampler;
52 vk::ShaderModule vertex_shader;
53 vk::ShaderModule fragment_shader;
54 vk::RenderPass render_pass;
55 vk::Pipeline pipeline;
56};
57
58} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 1631276c6..48a105327 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -20,12 +20,14 @@
20#include "core/frontend/graphics_context.h" 20#include "core/frontend/graphics_context.h"
21#include "core/telemetry_session.h" 21#include "core/telemetry_session.h"
22#include "video_core/gpu.h" 22#include "video_core/gpu.h"
23#include "video_core/renderer_vulkan/present/util.h"
23#include "video_core/renderer_vulkan/renderer_vulkan.h" 24#include "video_core/renderer_vulkan/renderer_vulkan.h"
24#include "video_core/renderer_vulkan/vk_blit_screen.h" 25#include "video_core/renderer_vulkan/vk_blit_screen.h"
25#include "video_core/renderer_vulkan/vk_rasterizer.h" 26#include "video_core/renderer_vulkan/vk_rasterizer.h"
26#include "video_core/renderer_vulkan/vk_scheduler.h" 27#include "video_core/renderer_vulkan/vk_scheduler.h"
27#include "video_core/renderer_vulkan/vk_state_tracker.h" 28#include "video_core/renderer_vulkan/vk_state_tracker.h"
28#include "video_core/renderer_vulkan/vk_swapchain.h" 29#include "video_core/renderer_vulkan/vk_swapchain.h"
30#include "video_core/textures/decoders.h"
29#include "video_core/vulkan_common/vulkan_debug_callback.h" 31#include "video_core/vulkan_common/vulkan_debug_callback.h"
30#include "video_core/vulkan_common/vulkan_device.h" 32#include "video_core/vulkan_common/vulkan_device.h"
31#include "video_core/vulkan_common/vulkan_instance.h" 33#include "video_core/vulkan_common/vulkan_instance.h"
@@ -97,10 +99,10 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
97 render_window.GetFramebufferLayout().height), 99 render_window.GetFramebufferLayout().height),
98 present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain, 100 present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain,
99 surface), 101 surface),
100 blit_screen(device_memory, render_window, device, memory_allocator, swapchain, 102 blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler),
101 present_manager, scheduler, screen_info), 103 blit_screenshot(device_memory, device, memory_allocator, present_manager, scheduler),
102 rasterizer(render_window, gpu, device_memory, screen_info, device, memory_allocator, 104 rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker,
103 state_tracker, scheduler) { 105 scheduler) {
104 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { 106 if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
105 turbo_mode.emplace(instance, dld); 107 turbo_mode.emplace(instance, dld);
106 scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); 108 scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
@@ -116,25 +118,22 @@ RendererVulkan::~RendererVulkan() {
116 void(device.GetLogical().WaitIdle()); 118 void(device.GetLogical().WaitIdle());
117} 119}
118 120
119void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 121void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
120 if (!framebuffer) { 122 if (framebuffers.empty()) {
121 return; 123 return;
122 } 124 }
125
123 SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); 126 SCOPE_EXIT({ render_window.OnFrameDisplayed(); });
127
124 if (!render_window.IsShown()) { 128 if (!render_window.IsShown()) {
125 return; 129 return;
126 } 130 }
127 // Update screen info if the framebuffer size has changed.
128 screen_info.width = framebuffer->width;
129 screen_info.height = framebuffer->height;
130
131 const DAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
132 const bool use_accelerated =
133 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
134 RenderScreenshot(*framebuffer, use_accelerated);
135 131
132 RenderScreenshot(framebuffers);
136 Frame* frame = present_manager.GetRenderFrame(); 133 Frame* frame = present_manager.GetRenderFrame();
137 blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated); 134 blit_swapchain.DrawToFrame(rasterizer, frame, framebuffers,
135 render_window.GetFramebufferLayout(), swapchain.GetImageCount(),
136 swapchain.GetImageViewFormat());
138 scheduler.Flush(*frame->render_ready); 137 scheduler.Flush(*frame->render_ready);
139 present_manager.Present(frame); 138 present_manager.Present(frame);
140 139
@@ -168,143 +167,37 @@ void RendererVulkan::Report() const {
168 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); 167 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
169} 168}
170 169
171void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& framebuffer, 170void Vulkan::RendererVulkan::RenderScreenshot(
172 bool use_accelerated) { 171 std::span<const Tegra::FramebufferConfig> framebuffers) {
173 if (!renderer_settings.screenshot_requested) { 172 if (!renderer_settings.screenshot_requested) {
174 return; 173 return;
175 } 174 }
175
176 constexpr VkFormat ScreenshotFormat{VK_FORMAT_B8G8R8A8_UNORM};
176 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; 177 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
177 vk::Image staging_image = memory_allocator.CreateImage(VkImageCreateInfo{
178 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
179 .pNext = nullptr,
180 .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
181 .imageType = VK_IMAGE_TYPE_2D,
182 .format = VK_FORMAT_B8G8R8A8_UNORM,
183 .extent =
184 {
185 .width = layout.width,
186 .height = layout.height,
187 .depth = 1,
188 },
189 .mipLevels = 1,
190 .arrayLayers = 1,
191 .samples = VK_SAMPLE_COUNT_1_BIT,
192 .tiling = VK_IMAGE_TILING_OPTIMAL,
193 .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
194 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
195 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
196 .queueFamilyIndexCount = 0,
197 .pQueueFamilyIndices = nullptr,
198 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
199 });
200 178
201 const vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ 179 auto frame = [&]() {
202 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 180 Frame f{};
203 .pNext = nullptr, 181 f.image = CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height},
204 .flags = 0, 182 ScreenshotFormat);
205 .image = *staging_image, 183 f.image_view = CreateWrappedImageView(device, f.image, ScreenshotFormat);
206 .viewType = VK_IMAGE_VIEW_TYPE_2D, 184 f.framebuffer = blit_screenshot.CreateFramebuffer(layout, *f.image_view, ScreenshotFormat);
207 .format = VK_FORMAT_B8G8R8A8_UNORM, 185 return f;
208 .components{ 186 }();
209 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
210 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
211 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
212 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
213 },
214 .subresourceRange{
215 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
216 .baseMipLevel = 0,
217 .levelCount = 1,
218 .baseArrayLayer = 0,
219 .layerCount = VK_REMAINING_ARRAY_LAYERS,
220 },
221 });
222 const VkExtent2D render_area{.width = layout.width, .height = layout.height};
223 const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area);
224 blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated);
225 187
226 const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); 188 blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1,
227 const VkBufferCreateInfo dst_buffer_info{ 189 VK_FORMAT_B8G8R8A8_UNORM);
228 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 190
229 .pNext = nullptr, 191 const auto dst_buffer = CreateWrappedBuffer(
230 .flags = 0, 192 memory_allocator, static_cast<VkDeviceSize>(layout.width * layout.height * 4),
231 .size = buffer_size, 193 MemoryUsage::Download);
232 .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
233 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
234 .queueFamilyIndexCount = 0,
235 .pQueueFamilyIndices = nullptr,
236 };
237 const vk::Buffer dst_buffer =
238 memory_allocator.CreateBuffer(dst_buffer_info, MemoryUsage::Download);
239 194
240 scheduler.RequestOutsideRenderPassOperationContext(); 195 scheduler.RequestOutsideRenderPassOperationContext();
241 scheduler.Record([&](vk::CommandBuffer cmdbuf) { 196 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
242 const VkImageMemoryBarrier read_barrier{ 197 DownloadColorImage(cmdbuf, *frame.image, *dst_buffer,
243 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 198 VkExtent3D{layout.width, layout.height, 1});
244 .pNext = nullptr,
245 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
246 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
247 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
248 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
249 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
250 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
251 .image = *staging_image,
252 .subresourceRange{
253 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
254 .baseMipLevel = 0,
255 .levelCount = VK_REMAINING_MIP_LEVELS,
256 .baseArrayLayer = 0,
257 .layerCount = VK_REMAINING_ARRAY_LAYERS,
258 },
259 };
260 const VkImageMemoryBarrier image_write_barrier{
261 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
262 .pNext = nullptr,
263 .srcAccessMask = 0,
264 .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
265 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
266 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
267 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
268 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
269 .image = *staging_image,
270 .subresourceRange{
271 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
272 .baseMipLevel = 0,
273 .levelCount = VK_REMAINING_MIP_LEVELS,
274 .baseArrayLayer = 0,
275 .layerCount = VK_REMAINING_ARRAY_LAYERS,
276 },
277 };
278 static constexpr VkMemoryBarrier memory_write_barrier{
279 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
280 .pNext = nullptr,
281 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
282 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
283 };
284 const VkBufferImageCopy copy{
285 .bufferOffset = 0,
286 .bufferRowLength = 0,
287 .bufferImageHeight = 0,
288 .imageSubresource{
289 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
290 .mipLevel = 0,
291 .baseArrayLayer = 0,
292 .layerCount = 1,
293 },
294 .imageOffset{.x = 0, .y = 0, .z = 0},
295 .imageExtent{
296 .width = layout.width,
297 .height = layout.height,
298 .depth = 1,
299 },
300 };
301 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
302 0, read_barrier);
303 cmdbuf.CopyImageToBuffer(*staging_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *dst_buffer,
304 copy);
305 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
306 0, memory_write_barrier, nullptr, image_write_barrier);
307 }); 199 });
200
308 // Ensure the copy is fully completed before saving the screenshot 201 // Ensure the copy is fully completed before saving the screenshot
309 scheduler.Finish(); 202 scheduler.Finish();
310 203
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 11c52287a..c6d8a0f21 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -46,7 +46,7 @@ public:
46 std::unique_ptr<Core::Frontend::GraphicsContext> context_); 46 std::unique_ptr<Core::Frontend::GraphicsContext> context_);
47 ~RendererVulkan() override; 47 ~RendererVulkan() override;
48 48
49 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 49 void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;
50 50
51 VideoCore::RasterizerInterface* ReadRasterizer() override { 51 VideoCore::RasterizerInterface* ReadRasterizer() override {
52 return &rasterizer; 52 return &rasterizer;
@@ -59,7 +59,7 @@ public:
59private: 59private:
60 void Report() const; 60 void Report() const;
61 61
62 void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated); 62 void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
63 63
64 Core::TelemetrySession& telemetry_session; 64 Core::TelemetrySession& telemetry_session;
65 Tegra::MaxwellDeviceMemoryManager& device_memory; 65 Tegra::MaxwellDeviceMemoryManager& device_memory;
@@ -72,15 +72,14 @@ private:
72 vk::DebugUtilsMessenger debug_messenger; 72 vk::DebugUtilsMessenger debug_messenger;
73 vk::SurfaceKHR surface; 73 vk::SurfaceKHR surface;
74 74
75 ScreenInfo screen_info;
76
77 Device device; 75 Device device;
78 MemoryAllocator memory_allocator; 76 MemoryAllocator memory_allocator;
79 StateTracker state_tracker; 77 StateTracker state_tracker;
80 Scheduler scheduler; 78 Scheduler scheduler;
81 Swapchain swapchain; 79 Swapchain swapchain;
82 PresentManager present_manager; 80 PresentManager present_manager;
83 BlitScreen blit_screen; 81 BlitScreen blit_swapchain;
82 BlitScreen blit_screenshot;
84 RasterizerVulkan rasterizer; 83 RasterizerVulkan rasterizer;
85 std::optional<TurboMode> turbo_mode; 84 std::optional<TurboMode> turbo_mode;
86}; 85};
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 610f27c84..2275fcc46 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -1,522 +1,143 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include "video_core/framebuffer_config.h"
5#include <array> 5#include "video_core/renderer_vulkan/present/filters.h"
6#include <cstring> 6#include "video_core/renderer_vulkan/present/layer.h"
7#include <memory>
8#include <vector>
9
10#include "common/assert.h"
11#include "common/common_types.h"
12#include "common/math_util.h"
13#include "common/polyfill_ranges.h"
14#include "common/settings.h"
15#include "core/core.h"
16#include "core/frontend/emu_window.h"
17#include "video_core/gpu.h"
18#include "video_core/host1x/gpu_device_memory_manager.h"
19#include "video_core/host_shaders/fxaa_frag_spv.h"
20#include "video_core/host_shaders/fxaa_vert_spv.h"
21#include "video_core/host_shaders/present_bicubic_frag_spv.h"
22#include "video_core/host_shaders/present_gaussian_frag_spv.h"
23#include "video_core/host_shaders/vulkan_present_frag_spv.h"
24#include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h"
25#include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h"
26#include "video_core/host_shaders/vulkan_present_vert_spv.h"
27#include "video_core/renderer_vulkan/renderer_vulkan.h"
28#include "video_core/renderer_vulkan/vk_blit_screen.h" 7#include "video_core/renderer_vulkan/vk_blit_screen.h"
29#include "video_core/renderer_vulkan/vk_fsr.h" 8#include "video_core/renderer_vulkan/vk_present_manager.h"
30#include "video_core/renderer_vulkan/vk_scheduler.h" 9#include "video_core/renderer_vulkan/vk_scheduler.h"
31#include "video_core/renderer_vulkan/vk_shader_util.h"
32#include "video_core/renderer_vulkan/vk_smaa.h"
33#include "video_core/renderer_vulkan/vk_swapchain.h"
34#include "video_core/surface.h"
35#include "video_core/textures/decoders.h"
36#include "video_core/vulkan_common/vulkan_device.h"
37#include "video_core/vulkan_common/vulkan_memory_allocator.h"
38#include "video_core/vulkan_common/vulkan_wrapper.h"
39 10
40namespace Vulkan { 11namespace Vulkan {
41 12
42namespace { 13BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_,
43 14 MemoryAllocator& memory_allocator_, PresentManager& present_manager_,
44struct ScreenRectVertex { 15 Scheduler& scheduler_)
45 ScreenRectVertex() = default; 16 : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_},
46 explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {} 17 present_manager{present_manager_}, scheduler{scheduler_}, image_count{1},
47 18 swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {}
48 std::array<f32, 2> position;
49 std::array<f32, 2> tex_coord;
50
51 static VkVertexInputBindingDescription GetDescription() {
52 return {
53 .binding = 0,
54 .stride = sizeof(ScreenRectVertex),
55 .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
56 };
57 }
58
59 static std::array<VkVertexInputAttributeDescription, 2> GetAttributes() {
60 return {{
61 {
62 .location = 0,
63 .binding = 0,
64 .format = VK_FORMAT_R32G32_SFLOAT,
65 .offset = offsetof(ScreenRectVertex, position),
66 },
67 {
68 .location = 1,
69 .binding = 0,
70 .format = VK_FORMAT_R32G32_SFLOAT,
71 .offset = offsetof(ScreenRectVertex, tex_coord),
72 },
73 }};
74 }
75};
76 19
77std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) { 20BlitScreen::~BlitScreen() = default;
78 // clang-format off
79 return { 2.f / width, 0.f, 0.f, 0.f,
80 0.f, 2.f / height, 0.f, 0.f,
81 0.f, 0.f, 1.f, 0.f,
82 -1.f, -1.f, 0.f, 1.f};
83 // clang-format on
84}
85
86u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) {
87 using namespace VideoCore::Surface;
88 return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format));
89}
90 21
91std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { 22void BlitScreen::WaitIdle() {
92 return static_cast<std::size_t>(framebuffer.stride) * 23 present_manager.WaitPresent();
93 static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer); 24 scheduler.Finish();
25 device.GetLogical().WaitIdle();
94} 26}
95 27
96VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { 28void BlitScreen::SetWindowAdaptPass() {
97 switch (framebuffer.pixel_format) { 29 layers.clear();
98 case Service::android::PixelFormat::Rgba8888: 30 scaling_filter = Settings::values.scaling_filter.GetValue();
99 case Service::android::PixelFormat::Rgbx8888: 31
100 return VK_FORMAT_A8B8G8R8_UNORM_PACK32; 32 switch (scaling_filter) {
101 case Service::android::PixelFormat::Rgb565: 33 case Settings::ScalingFilter::NearestNeighbor:
102 return VK_FORMAT_R5G6B5_UNORM_PACK16; 34 window_adapt = MakeNearestNeighbor(device, swapchain_view_format);
103 case Service::android::PixelFormat::Bgra8888: 35 break;
104 return VK_FORMAT_B8G8R8A8_UNORM; 36 case Settings::ScalingFilter::Bicubic:
37 window_adapt = MakeBicubic(device, swapchain_view_format);
38 break;
39 case Settings::ScalingFilter::Gaussian:
40 window_adapt = MakeGaussian(device, swapchain_view_format);
41 break;
42 case Settings::ScalingFilter::ScaleForce:
43 window_adapt = MakeScaleForce(device, swapchain_view_format);
44 break;
45 case Settings::ScalingFilter::Fsr:
46 case Settings::ScalingFilter::Bilinear:
105 default: 47 default:
106 UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", 48 window_adapt = MakeBilinear(device, swapchain_view_format);
107 static_cast<u32>(framebuffer.pixel_format)); 49 break;
108 return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
109 } 50 }
110} 51}
111 52
112} // Anonymous namespace 53void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
113 54 std::span<const Tegra::FramebufferConfig> framebuffers,
114struct BlitScreen::BufferData { 55 const Layout::FramebufferLayout& layout,
115 struct { 56 size_t current_swapchain_image_count,
116 std::array<f32, 4 * 4> modelview_matrix; 57 VkFormat current_swapchain_view_format) {
117 } uniform; 58 bool resource_update_required = false;
118 59 bool presentation_recreate_required = false;
119 std::array<ScreenRectVertex, 4> vertices;
120
121 // Unaligned image data goes here
122};
123
124BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_,
125 Core::Frontend::EmuWindow& render_window_, const Device& device_,
126 MemoryAllocator& memory_allocator_, Swapchain& swapchain_,
127 PresentManager& present_manager_, Scheduler& scheduler_,
128 const ScreenInfo& screen_info_)
129 : device_memory{device_memory_}, render_window{render_window_}, device{device_},
130 memory_allocator{memory_allocator_}, swapchain{swapchain_}, present_manager{present_manager_},
131 scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
132 resource_ticks.resize(image_count);
133 swapchain_view_format = swapchain.GetImageViewFormat();
134
135 CreateStaticResources();
136 CreateDynamicResources();
137}
138
139BlitScreen::~BlitScreen() = default;
140
141static Common::Rectangle<f32> NormalizeCrop(const Tegra::FramebufferConfig& framebuffer,
142 const ScreenInfo& screen_info) {
143 f32 left, top, right, bottom;
144
145 if (!framebuffer.crop_rect.IsEmpty()) {
146 // If crop rectangle is not empty, apply properties from rectangle.
147 left = static_cast<f32>(framebuffer.crop_rect.left);
148 top = static_cast<f32>(framebuffer.crop_rect.top);
149 right = static_cast<f32>(framebuffer.crop_rect.right);
150 bottom = static_cast<f32>(framebuffer.crop_rect.bottom);
151 } else {
152 // Otherwise, fall back to framebuffer dimensions.
153 left = 0;
154 top = 0;
155 right = static_cast<f32>(framebuffer.width);
156 bottom = static_cast<f32>(framebuffer.height);
157 }
158
159 // Apply transformation flags.
160 auto framebuffer_transform_flags = framebuffer.transform_flags;
161 60
162 if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipH)) { 61 // Recreate dynamic resources if the adapting filter changed
163 // Switch left and right. 62 if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue()) {
164 std::swap(left, right); 63 resource_update_required = true;
165 }
166 if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipV)) {
167 // Switch top and bottom.
168 std::swap(top, bottom);
169 } 64 }
170 65
171 framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipH; 66 // Recreate dynamic resources if the image count changed
172 framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipV; 67 const size_t old_swapchain_image_count =
173 if (True(framebuffer_transform_flags)) { 68 std::exchange(image_count, current_swapchain_image_count);
174 UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}", 69 if (old_swapchain_image_count != current_swapchain_image_count) {
175 static_cast<u32>(framebuffer_transform_flags)); 70 resource_update_required = true;
176 } 71 }
177 72
178 // Get the screen properties. 73 // Recreate the presentation frame if the format or dimensions of the window changed
179 const f32 screen_width = static_cast<f32>(screen_info.width); 74 const VkFormat old_swapchain_view_format =
180 const f32 screen_height = static_cast<f32>(screen_info.height); 75 std::exchange(swapchain_view_format, current_swapchain_view_format);
181 76 if (old_swapchain_view_format != current_swapchain_view_format ||
182 // Normalize coordinate space. 77 layout.width != frame->width || layout.height != frame->height) {
183 left /= screen_width; 78 resource_update_required = true;
184 top /= screen_height; 79 presentation_recreate_required = true;
185 right /= screen_width;
186 bottom /= screen_height;
187
188 return Common::Rectangle<f32>(left, top, right, bottom);
189}
190
191void BlitScreen::Recreate() {
192 present_manager.WaitPresent();
193 scheduler.Finish();
194 device.GetLogical().WaitIdle();
195 CreateDynamicResources();
196}
197
198void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
199 const VkFramebuffer& host_framebuffer, const Layout::FramebufferLayout layout,
200 VkExtent2D render_area, bool use_accelerated) {
201 RefreshResources(framebuffer);
202
203 // Finish any pending renderpass
204 scheduler.RequestOutsideRenderPassOperationContext();
205
206 scheduler.Wait(resource_ticks[image_index]);
207 resource_ticks[image_index] = scheduler.CurrentTick();
208
209 VkImage source_image = use_accelerated ? screen_info.image : *raw_images[image_index];
210 VkImageView source_image_view =
211 use_accelerated ? screen_info.image_view : *raw_image_views[image_index];
212
213 BufferData data;
214 SetUniformData(data, layout);
215 SetVertexData(data, framebuffer, layout);
216
217 const std::span<u8> mapped_span = buffer.Mapped();
218 std::memcpy(mapped_span.data(), &data, sizeof(data));
219
220 if (!use_accelerated) {
221 const u64 image_offset = GetRawImageOffset(framebuffer);
222
223 const DAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
224 const u8* const host_ptr = device_memory.GetPointer<u8>(framebuffer_addr);
225
226 // TODO(Rodrigo): Read this from HLE
227 constexpr u32 block_height_log2 = 4;
228 const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer);
229 const u64 linear_size{GetSizeInBytes(framebuffer)};
230 const u64 tiled_size{Tegra::Texture::CalculateSize(true, bytes_per_pixel,
231 framebuffer.stride, framebuffer.height,
232 1, block_height_log2, 0)};
233 Tegra::Texture::UnswizzleTexture(
234 mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
235 bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
236
237 const VkBufferImageCopy copy{
238 .bufferOffset = image_offset,
239 .bufferRowLength = 0,
240 .bufferImageHeight = 0,
241 .imageSubresource =
242 {
243 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
244 .mipLevel = 0,
245 .baseArrayLayer = 0,
246 .layerCount = 1,
247 },
248 .imageOffset = {.x = 0, .y = 0, .z = 0},
249 .imageExtent =
250 {
251 .width = framebuffer.width,
252 .height = framebuffer.height,
253 .depth = 1,
254 },
255 };
256 scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) {
257 const VkImage image = *raw_images[index];
258 const VkImageMemoryBarrier base_barrier{
259 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
260 .pNext = nullptr,
261 .srcAccessMask = 0,
262 .dstAccessMask = 0,
263 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
264 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
265 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
266 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
267 .image = image,
268 .subresourceRange{
269 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
270 .baseMipLevel = 0,
271 .levelCount = 1,
272 .baseArrayLayer = 0,
273 .layerCount = 1,
274 },
275 };
276 VkImageMemoryBarrier read_barrier = base_barrier;
277 read_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
278 read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
279 read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
280
281 VkImageMemoryBarrier write_barrier = base_barrier;
282 write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
283 write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
284
285 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
286 read_barrier);
287 cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_GENERAL, copy);
288 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
289 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
290 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
291 0, write_barrier);
292 });
293 } 80 }
294 81
295 const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue(); 82 // If we have a pending resource update, perform it
296 if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) { 83 if (resource_update_required) {
297 UpdateAADescriptorSet(source_image_view, false); 84 // Wait for idle to ensure no resources are in use
298 const u32 up_scale = Settings::values.resolution_info.up_scale; 85 WaitIdle();
299 const u32 down_shift = Settings::values.resolution_info.down_shift;
300 VkExtent2D size{
301 .width = (up_scale * framebuffer.width) >> down_shift,
302 .height = (up_scale * framebuffer.height) >> down_shift,
303 };
304 scheduler.Record([this, index = image_index, size,
305 anti_alias_pass](vk::CommandBuffer cmdbuf) {
306 const VkImageMemoryBarrier base_barrier{
307 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
308 .pNext = nullptr,
309 .srcAccessMask = 0,
310 .dstAccessMask = 0,
311 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
312 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
313 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
314 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
315 .image = {},
316 .subresourceRange =
317 {
318 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
319 .baseMipLevel = 0,
320 .levelCount = 1,
321 .baseArrayLayer = 0,
322 .layerCount = 1,
323 },
324 };
325
326 {
327 VkImageMemoryBarrier fsr_write_barrier = base_barrier;
328 fsr_write_barrier.image = *aa_image;
329 fsr_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
330 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
331 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, fsr_write_barrier);
332 }
333 86
334 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; 87 // Update window adapt pass
335 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; 88 SetWindowAdaptPass();
336 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
337 const VkClearValue clear_color{
338 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
339 };
340 const VkRenderPassBeginInfo renderpass_bi{
341 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
342 .pNext = nullptr,
343 .renderPass = *aa_renderpass,
344 .framebuffer = *aa_framebuffer,
345 .renderArea =
346 {
347 .offset = {0, 0},
348 .extent = size,
349 },
350 .clearValueCount = 1,
351 .pClearValues = &clear_color,
352 };
353 const VkViewport viewport{
354 .x = 0.0f,
355 .y = 0.0f,
356 .width = static_cast<float>(size.width),
357 .height = static_cast<float>(size.height),
358 .minDepth = 0.0f,
359 .maxDepth = 1.0f,
360 };
361 const VkRect2D scissor{
362 .offset = {0, 0},
363 .extent = size,
364 };
365 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
366 switch (anti_alias_pass) {
367 case Settings::AntiAliasing::Fxaa:
368 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline);
369 break;
370 default:
371 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline);
372 break;
373 }
374 cmdbuf.SetViewport(0, viewport);
375 cmdbuf.SetScissor(0, scissor);
376 89
377 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); 90 // Update frame format if needed
378 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline_layout, 0, 91 if (presentation_recreate_required) {
379 aa_descriptor_sets[index], {}); 92 present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format,
380 cmdbuf.Draw(4, 1, 0, 0); 93 window_adapt->GetRenderPass());
381 cmdbuf.EndRenderPass();
382
383 {
384 VkImageMemoryBarrier blit_read_barrier = base_barrier;
385 blit_read_barrier.image = *aa_image;
386 blit_read_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
387 blit_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
388
389 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
390 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, blit_read_barrier);
391 }
392 });
393 source_image_view = *aa_image_view;
394 }
395 if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Smaa) {
396 if (!smaa) {
397 const u32 up_scale = Settings::values.resolution_info.up_scale;
398 const u32 down_shift = Settings::values.resolution_info.down_shift;
399 const VkExtent2D smaa_size{
400 .width = (up_scale * framebuffer.width) >> down_shift,
401 .height = (up_scale * framebuffer.height) >> down_shift,
402 };
403 CreateSMAA(smaa_size);
404 } 94 }
405 source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view);
406 } 95 }
407 if (fsr) {
408 const auto crop_rect = NormalizeCrop(framebuffer, screen_info);
409 const VkExtent2D fsr_input_size{
410 .width = Settings::values.resolution_info.ScaleUp(screen_info.width),
411 .height = Settings::values.resolution_info.ScaleUp(screen_info.height),
412 };
413 VkImageView fsr_image_view =
414 fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect);
415 UpdateDescriptorSet(fsr_image_view, true);
416 } else {
417 const bool is_nn =
418 Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor;
419 UpdateDescriptorSet(source_image_view, is_nn);
420 }
421
422 scheduler.Record([this, host_framebuffer, index = image_index,
423 size = render_area](vk::CommandBuffer cmdbuf) {
424 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
425 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
426 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
427 const VkClearValue clear_color{
428 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
429 };
430 const VkRenderPassBeginInfo renderpass_bi{
431 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
432 .pNext = nullptr,
433 .renderPass = *renderpass,
434 .framebuffer = host_framebuffer,
435 .renderArea =
436 {
437 .offset = {0, 0},
438 .extent = size,
439 },
440 .clearValueCount = 1,
441 .pClearValues = &clear_color,
442 };
443 const VkViewport viewport{
444 .x = 0.0f,
445 .y = 0.0f,
446 .width = static_cast<float>(size.width),
447 .height = static_cast<float>(size.height),
448 .minDepth = 0.0f,
449 .maxDepth = 1.0f,
450 };
451 const VkRect2D scissor{
452 .offset = {0, 0},
453 .extent = size,
454 };
455 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
456 auto graphics_pipeline = [this]() {
457 switch (Settings::values.scaling_filter.GetValue()) {
458 case Settings::ScalingFilter::NearestNeighbor:
459 case Settings::ScalingFilter::Bilinear:
460 return *bilinear_pipeline;
461 case Settings::ScalingFilter::Bicubic:
462 return *bicubic_pipeline;
463 case Settings::ScalingFilter::Gaussian:
464 return *gaussian_pipeline;
465 case Settings::ScalingFilter::ScaleForce:
466 return *scaleforce_pipeline;
467 default:
468 return *bilinear_pipeline;
469 }
470 }();
471 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
472 cmdbuf.SetViewport(0, viewport);
473 cmdbuf.SetScissor(0, scissor);
474 96
475 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); 97 // Add additional layers if needed
476 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, 98 const VkExtent2D window_size{
477 descriptor_sets[index], {}); 99 .width = layout.screen.GetWidth(),
478 cmdbuf.Draw(4, 1, 0, 0); 100 .height = layout.screen.GetHeight(),
479 cmdbuf.EndRenderPass(); 101 };
480 });
481}
482 102
483void BlitScreen::DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer, 103 while (layers.size() < framebuffers.size()) {
484 bool use_accelerated) { 104 layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count,
485 // Recreate dynamic resources if the the image count or input format changed 105 window_size, window_adapt->GetDescriptorSetLayout());
486 const VkFormat current_framebuffer_format =
487 std::exchange(framebuffer_view_format, GetFormat(framebuffer));
488 if (const std::size_t swapchain_images = swapchain.GetImageCount();
489 swapchain_images != image_count || current_framebuffer_format != framebuffer_view_format) {
490 image_count = swapchain_images;
491 Recreate();
492 } 106 }
493 107
494 // Recreate the presentation frame if the dimensions of the window changed 108 // Perform the draw
495 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); 109 window_adapt->Draw(rasterizer, scheduler, image_index, layers, framebuffers, layout, frame);
496 if (layout.width != frame->width || layout.height != frame->height) {
497 Recreate();
498 present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format,
499 *renderpass);
500 }
501 110
502 const VkExtent2D render_area{frame->width, frame->height}; 111 // Advance to next image
503 Draw(framebuffer, *frame->framebuffer, layout, render_area, use_accelerated);
504 if (++image_index >= image_count) { 112 if (++image_index >= image_count) {
505 image_index = 0; 113 image_index = 0;
506 } 114 }
507} 115}
508 116
509vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) { 117vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout,
510 return CreateFramebuffer(image_view, extent, renderpass); 118 VkImageView image_view,
119 VkFormat current_view_format) {
120 const bool format_updated =
121 std::exchange(swapchain_view_format, current_view_format) != current_view_format;
122 if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() ||
123 format_updated) {
124 WaitIdle();
125 SetWindowAdaptPass();
126 }
127 const VkExtent2D extent{
128 .width = layout.width,
129 .height = layout.height,
130 };
131 return CreateFramebuffer(image_view, extent, window_adapt->GetRenderPass());
511} 132}
512 133
513vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, 134vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent,
514 vk::RenderPass& rd) { 135 VkRenderPass render_pass) {
515 return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ 136 return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
516 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, 137 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
517 .pNext = nullptr, 138 .pNext = nullptr,
518 .flags = 0, 139 .flags = 0,
519 .renderPass = *rd, 140 .renderPass = render_pass,
520 .attachmentCount = 1, 141 .attachmentCount = 1,
521 .pAttachments = &image_view, 142 .pAttachments = &image_view,
522 .width = extent.width, 143 .width = extent.width,
@@ -525,969 +146,4 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkE
525 }); 146 });
526} 147}
527 148
528void BlitScreen::CreateStaticResources() {
529 CreateShaders();
530 CreateSampler();
531}
532
533void BlitScreen::CreateDynamicResources() {
534 CreateDescriptorPool();
535 CreateDescriptorSetLayout();
536 CreateDescriptorSets();
537 CreatePipelineLayout();
538 CreateRenderPass();
539 CreateGraphicsPipeline();
540 fsr.reset();
541 smaa.reset();
542 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
543 CreateFSR();
544 }
545}
546
547void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) {
548 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
549 if (!fsr) {
550 CreateFSR();
551 }
552 } else {
553 fsr.reset();
554 }
555
556 if (framebuffer.width == raw_width && framebuffer.height == raw_height &&
557 framebuffer.pixel_format == pixel_format && !raw_images.empty()) {
558 return;
559 }
560
561 raw_width = framebuffer.width;
562 raw_height = framebuffer.height;
563 pixel_format = framebuffer.pixel_format;
564
565 smaa.reset();
566 ReleaseRawImages();
567
568 CreateStagingBuffer(framebuffer);
569 CreateRawImages(framebuffer);
570}
571
572void BlitScreen::CreateShaders() {
573 vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV);
574 fxaa_vertex_shader = BuildShader(device, FXAA_VERT_SPV);
575 fxaa_fragment_shader = BuildShader(device, FXAA_FRAG_SPV);
576 bilinear_fragment_shader = BuildShader(device, VULKAN_PRESENT_FRAG_SPV);
577 bicubic_fragment_shader = BuildShader(device, PRESENT_BICUBIC_FRAG_SPV);
578 gaussian_fragment_shader = BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV);
579 if (device.IsFloat16Supported()) {
580 scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP16_FRAG_SPV);
581 } else {
582 scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP32_FRAG_SPV);
583 }
584}
585
586void BlitScreen::CreateDescriptorPool() {
587 const std::array<VkDescriptorPoolSize, 2> pool_sizes{{
588 {
589 .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
590 .descriptorCount = static_cast<u32>(image_count),
591 },
592 {
593 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
594 .descriptorCount = static_cast<u32>(image_count),
595 },
596 }};
597
598 const std::array<VkDescriptorPoolSize, 1> pool_sizes_aa{{
599 {
600 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
601 .descriptorCount = static_cast<u32>(image_count * 2),
602 },
603 }};
604
605 const VkDescriptorPoolCreateInfo ci{
606 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
607 .pNext = nullptr,
608 .flags = 0,
609 .maxSets = static_cast<u32>(image_count),
610 .poolSizeCount = static_cast<u32>(pool_sizes.size()),
611 .pPoolSizes = pool_sizes.data(),
612 };
613 descriptor_pool = device.GetLogical().CreateDescriptorPool(ci);
614
615 const VkDescriptorPoolCreateInfo ci_aa{
616 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
617 .pNext = nullptr,
618 .flags = 0,
619 .maxSets = static_cast<u32>(image_count),
620 .poolSizeCount = static_cast<u32>(pool_sizes_aa.size()),
621 .pPoolSizes = pool_sizes_aa.data(),
622 };
623 aa_descriptor_pool = device.GetLogical().CreateDescriptorPool(ci_aa);
624}
625
626void BlitScreen::CreateRenderPass() {
627 renderpass = CreateRenderPassImpl(swapchain_view_format);
628}
629
630vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format) {
631 const VkAttachmentDescription color_attachment{
632 .flags = 0,
633 .format = format,
634 .samples = VK_SAMPLE_COUNT_1_BIT,
635 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
636 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
637 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
638 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
639 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
640 .finalLayout = VK_IMAGE_LAYOUT_GENERAL,
641 };
642
643 const VkAttachmentReference color_attachment_ref{
644 .attachment = 0,
645 .layout = VK_IMAGE_LAYOUT_GENERAL,
646 };
647
648 const VkSubpassDescription subpass_description{
649 .flags = 0,
650 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
651 .inputAttachmentCount = 0,
652 .pInputAttachments = nullptr,
653 .colorAttachmentCount = 1,
654 .pColorAttachments = &color_attachment_ref,
655 .pResolveAttachments = nullptr,
656 .pDepthStencilAttachment = nullptr,
657 .preserveAttachmentCount = 0,
658 .pPreserveAttachments = nullptr,
659 };
660
661 const VkSubpassDependency dependency{
662 .srcSubpass = VK_SUBPASS_EXTERNAL,
663 .dstSubpass = 0,
664 .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
665 .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
666 .srcAccessMask = 0,
667 .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
668 .dependencyFlags = 0,
669 };
670
671 const VkRenderPassCreateInfo renderpass_ci{
672 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
673 .pNext = nullptr,
674 .flags = 0,
675 .attachmentCount = 1,
676 .pAttachments = &color_attachment,
677 .subpassCount = 1,
678 .pSubpasses = &subpass_description,
679 .dependencyCount = 1,
680 .pDependencies = &dependency,
681 };
682
683 return device.GetLogical().CreateRenderPass(renderpass_ci);
684}
685
686void BlitScreen::CreateDescriptorSetLayout() {
687 const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{
688 {
689 .binding = 0,
690 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
691 .descriptorCount = 1,
692 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
693 .pImmutableSamplers = nullptr,
694 },
695 {
696 .binding = 1,
697 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
698 .descriptorCount = 1,
699 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
700 .pImmutableSamplers = nullptr,
701 },
702 }};
703
704 const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings_aa{{
705 {
706 .binding = 0,
707 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
708 .descriptorCount = 1,
709 .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
710 .pImmutableSamplers = nullptr,
711 },
712 {
713 .binding = 1,
714 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
715 .descriptorCount = 1,
716 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
717 .pImmutableSamplers = nullptr,
718 },
719 }};
720
721 const VkDescriptorSetLayoutCreateInfo ci{
722 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
723 .pNext = nullptr,
724 .flags = 0,
725 .bindingCount = static_cast<u32>(layout_bindings.size()),
726 .pBindings = layout_bindings.data(),
727 };
728
729 const VkDescriptorSetLayoutCreateInfo ci_aa{
730 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
731 .pNext = nullptr,
732 .flags = 0,
733 .bindingCount = static_cast<u32>(layout_bindings_aa.size()),
734 .pBindings = layout_bindings_aa.data(),
735 };
736
737 descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci);
738 aa_descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci_aa);
739}
740
741void BlitScreen::CreateDescriptorSets() {
742 const std::vector layouts(image_count, *descriptor_set_layout);
743 const std::vector layouts_aa(image_count, *aa_descriptor_set_layout);
744
745 const VkDescriptorSetAllocateInfo ai{
746 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
747 .pNext = nullptr,
748 .descriptorPool = *descriptor_pool,
749 .descriptorSetCount = static_cast<u32>(image_count),
750 .pSetLayouts = layouts.data(),
751 };
752
753 const VkDescriptorSetAllocateInfo ai_aa{
754 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
755 .pNext = nullptr,
756 .descriptorPool = *aa_descriptor_pool,
757 .descriptorSetCount = static_cast<u32>(image_count),
758 .pSetLayouts = layouts_aa.data(),
759 };
760
761 descriptor_sets = descriptor_pool.Allocate(ai);
762 aa_descriptor_sets = aa_descriptor_pool.Allocate(ai_aa);
763}
764
765void BlitScreen::CreatePipelineLayout() {
766 const VkPipelineLayoutCreateInfo ci{
767 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
768 .pNext = nullptr,
769 .flags = 0,
770 .setLayoutCount = 1,
771 .pSetLayouts = descriptor_set_layout.address(),
772 .pushConstantRangeCount = 0,
773 .pPushConstantRanges = nullptr,
774 };
775 const VkPipelineLayoutCreateInfo ci_aa{
776 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
777 .pNext = nullptr,
778 .flags = 0,
779 .setLayoutCount = 1,
780 .pSetLayouts = aa_descriptor_set_layout.address(),
781 .pushConstantRangeCount = 0,
782 .pPushConstantRanges = nullptr,
783 };
784 pipeline_layout = device.GetLogical().CreatePipelineLayout(ci);
785 aa_pipeline_layout = device.GetLogical().CreatePipelineLayout(ci_aa);
786}
787
788void BlitScreen::CreateGraphicsPipeline() {
789 const std::array<VkPipelineShaderStageCreateInfo, 2> bilinear_shader_stages{{
790 {
791 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
792 .pNext = nullptr,
793 .flags = 0,
794 .stage = VK_SHADER_STAGE_VERTEX_BIT,
795 .module = *vertex_shader,
796 .pName = "main",
797 .pSpecializationInfo = nullptr,
798 },
799 {
800 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
801 .pNext = nullptr,
802 .flags = 0,
803 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
804 .module = *bilinear_fragment_shader,
805 .pName = "main",
806 .pSpecializationInfo = nullptr,
807 },
808 }};
809
810 const std::array<VkPipelineShaderStageCreateInfo, 2> bicubic_shader_stages{{
811 {
812 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
813 .pNext = nullptr,
814 .flags = 0,
815 .stage = VK_SHADER_STAGE_VERTEX_BIT,
816 .module = *vertex_shader,
817 .pName = "main",
818 .pSpecializationInfo = nullptr,
819 },
820 {
821 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
822 .pNext = nullptr,
823 .flags = 0,
824 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
825 .module = *bicubic_fragment_shader,
826 .pName = "main",
827 .pSpecializationInfo = nullptr,
828 },
829 }};
830
831 const std::array<VkPipelineShaderStageCreateInfo, 2> gaussian_shader_stages{{
832 {
833 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
834 .pNext = nullptr,
835 .flags = 0,
836 .stage = VK_SHADER_STAGE_VERTEX_BIT,
837 .module = *vertex_shader,
838 .pName = "main",
839 .pSpecializationInfo = nullptr,
840 },
841 {
842 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
843 .pNext = nullptr,
844 .flags = 0,
845 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
846 .module = *gaussian_fragment_shader,
847 .pName = "main",
848 .pSpecializationInfo = nullptr,
849 },
850 }};
851
852 const std::array<VkPipelineShaderStageCreateInfo, 2> scaleforce_shader_stages{{
853 {
854 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
855 .pNext = nullptr,
856 .flags = 0,
857 .stage = VK_SHADER_STAGE_VERTEX_BIT,
858 .module = *vertex_shader,
859 .pName = "main",
860 .pSpecializationInfo = nullptr,
861 },
862 {
863 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
864 .pNext = nullptr,
865 .flags = 0,
866 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
867 .module = *scaleforce_fragment_shader,
868 .pName = "main",
869 .pSpecializationInfo = nullptr,
870 },
871 }};
872
873 const auto vertex_binding_description = ScreenRectVertex::GetDescription();
874 const auto vertex_attrs_description = ScreenRectVertex::GetAttributes();
875
876 const VkPipelineVertexInputStateCreateInfo vertex_input_ci{
877 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
878 .pNext = nullptr,
879 .flags = 0,
880 .vertexBindingDescriptionCount = 1,
881 .pVertexBindingDescriptions = &vertex_binding_description,
882 .vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()},
883 .pVertexAttributeDescriptions = vertex_attrs_description.data(),
884 };
885
886 const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
887 .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
888 .pNext = nullptr,
889 .flags = 0,
890 .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
891 .primitiveRestartEnable = VK_FALSE,
892 };
893
894 const VkPipelineViewportStateCreateInfo viewport_state_ci{
895 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
896 .pNext = nullptr,
897 .flags = 0,
898 .viewportCount = 1,
899 .pViewports = nullptr,
900 .scissorCount = 1,
901 .pScissors = nullptr,
902 };
903
904 const VkPipelineRasterizationStateCreateInfo rasterization_ci{
905 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
906 .pNext = nullptr,
907 .flags = 0,
908 .depthClampEnable = VK_FALSE,
909 .rasterizerDiscardEnable = VK_FALSE,
910 .polygonMode = VK_POLYGON_MODE_FILL,
911 .cullMode = VK_CULL_MODE_NONE,
912 .frontFace = VK_FRONT_FACE_CLOCKWISE,
913 .depthBiasEnable = VK_FALSE,
914 .depthBiasConstantFactor = 0.0f,
915 .depthBiasClamp = 0.0f,
916 .depthBiasSlopeFactor = 0.0f,
917 .lineWidth = 1.0f,
918 };
919
920 const VkPipelineMultisampleStateCreateInfo multisampling_ci{
921 .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
922 .pNext = nullptr,
923 .flags = 0,
924 .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
925 .sampleShadingEnable = VK_FALSE,
926 .minSampleShading = 0.0f,
927 .pSampleMask = nullptr,
928 .alphaToCoverageEnable = VK_FALSE,
929 .alphaToOneEnable = VK_FALSE,
930 };
931
932 const VkPipelineColorBlendAttachmentState color_blend_attachment{
933 .blendEnable = VK_FALSE,
934 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
935 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
936 .colorBlendOp = VK_BLEND_OP_ADD,
937 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
938 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
939 .alphaBlendOp = VK_BLEND_OP_ADD,
940 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
941 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
942 };
943
944 const VkPipelineColorBlendStateCreateInfo color_blend_ci{
945 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
946 .pNext = nullptr,
947 .flags = 0,
948 .logicOpEnable = VK_FALSE,
949 .logicOp = VK_LOGIC_OP_COPY,
950 .attachmentCount = 1,
951 .pAttachments = &color_blend_attachment,
952 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
953 };
954
955 static constexpr std::array dynamic_states{
956 VK_DYNAMIC_STATE_VIEWPORT,
957 VK_DYNAMIC_STATE_SCISSOR,
958 };
959 const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
960 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
961 .pNext = nullptr,
962 .flags = 0,
963 .dynamicStateCount = static_cast<u32>(dynamic_states.size()),
964 .pDynamicStates = dynamic_states.data(),
965 };
966
967 const VkGraphicsPipelineCreateInfo bilinear_pipeline_ci{
968 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
969 .pNext = nullptr,
970 .flags = 0,
971 .stageCount = static_cast<u32>(bilinear_shader_stages.size()),
972 .pStages = bilinear_shader_stages.data(),
973 .pVertexInputState = &vertex_input_ci,
974 .pInputAssemblyState = &input_assembly_ci,
975 .pTessellationState = nullptr,
976 .pViewportState = &viewport_state_ci,
977 .pRasterizationState = &rasterization_ci,
978 .pMultisampleState = &multisampling_ci,
979 .pDepthStencilState = nullptr,
980 .pColorBlendState = &color_blend_ci,
981 .pDynamicState = &dynamic_state_ci,
982 .layout = *pipeline_layout,
983 .renderPass = *renderpass,
984 .subpass = 0,
985 .basePipelineHandle = 0,
986 .basePipelineIndex = 0,
987 };
988
989 const VkGraphicsPipelineCreateInfo bicubic_pipeline_ci{
990 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
991 .pNext = nullptr,
992 .flags = 0,
993 .stageCount = static_cast<u32>(bicubic_shader_stages.size()),
994 .pStages = bicubic_shader_stages.data(),
995 .pVertexInputState = &vertex_input_ci,
996 .pInputAssemblyState = &input_assembly_ci,
997 .pTessellationState = nullptr,
998 .pViewportState = &viewport_state_ci,
999 .pRasterizationState = &rasterization_ci,
1000 .pMultisampleState = &multisampling_ci,
1001 .pDepthStencilState = nullptr,
1002 .pColorBlendState = &color_blend_ci,
1003 .pDynamicState = &dynamic_state_ci,
1004 .layout = *pipeline_layout,
1005 .renderPass = *renderpass,
1006 .subpass = 0,
1007 .basePipelineHandle = 0,
1008 .basePipelineIndex = 0,
1009 };
1010
1011 const VkGraphicsPipelineCreateInfo gaussian_pipeline_ci{
1012 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
1013 .pNext = nullptr,
1014 .flags = 0,
1015 .stageCount = static_cast<u32>(gaussian_shader_stages.size()),
1016 .pStages = gaussian_shader_stages.data(),
1017 .pVertexInputState = &vertex_input_ci,
1018 .pInputAssemblyState = &input_assembly_ci,
1019 .pTessellationState = nullptr,
1020 .pViewportState = &viewport_state_ci,
1021 .pRasterizationState = &rasterization_ci,
1022 .pMultisampleState = &multisampling_ci,
1023 .pDepthStencilState = nullptr,
1024 .pColorBlendState = &color_blend_ci,
1025 .pDynamicState = &dynamic_state_ci,
1026 .layout = *pipeline_layout,
1027 .renderPass = *renderpass,
1028 .subpass = 0,
1029 .basePipelineHandle = 0,
1030 .basePipelineIndex = 0,
1031 };
1032
1033 const VkGraphicsPipelineCreateInfo scaleforce_pipeline_ci{
1034 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
1035 .pNext = nullptr,
1036 .flags = 0,
1037 .stageCount = static_cast<u32>(scaleforce_shader_stages.size()),
1038 .pStages = scaleforce_shader_stages.data(),
1039 .pVertexInputState = &vertex_input_ci,
1040 .pInputAssemblyState = &input_assembly_ci,
1041 .pTessellationState = nullptr,
1042 .pViewportState = &viewport_state_ci,
1043 .pRasterizationState = &rasterization_ci,
1044 .pMultisampleState = &multisampling_ci,
1045 .pDepthStencilState = nullptr,
1046 .pColorBlendState = &color_blend_ci,
1047 .pDynamicState = &dynamic_state_ci,
1048 .layout = *pipeline_layout,
1049 .renderPass = *renderpass,
1050 .subpass = 0,
1051 .basePipelineHandle = 0,
1052 .basePipelineIndex = 0,
1053 };
1054
1055 bilinear_pipeline = device.GetLogical().CreateGraphicsPipeline(bilinear_pipeline_ci);
1056 bicubic_pipeline = device.GetLogical().CreateGraphicsPipeline(bicubic_pipeline_ci);
1057 gaussian_pipeline = device.GetLogical().CreateGraphicsPipeline(gaussian_pipeline_ci);
1058 scaleforce_pipeline = device.GetLogical().CreateGraphicsPipeline(scaleforce_pipeline_ci);
1059}
1060
1061void BlitScreen::CreateSampler() {
1062 const VkSamplerCreateInfo ci{
1063 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
1064 .pNext = nullptr,
1065 .flags = 0,
1066 .magFilter = VK_FILTER_LINEAR,
1067 .minFilter = VK_FILTER_LINEAR,
1068 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
1069 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
1070 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
1071 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
1072 .mipLodBias = 0.0f,
1073 .anisotropyEnable = VK_FALSE,
1074 .maxAnisotropy = 0.0f,
1075 .compareEnable = VK_FALSE,
1076 .compareOp = VK_COMPARE_OP_NEVER,
1077 .minLod = 0.0f,
1078 .maxLod = 0.0f,
1079 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
1080 .unnormalizedCoordinates = VK_FALSE,
1081 };
1082
1083 const VkSamplerCreateInfo ci_nn{
1084 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
1085 .pNext = nullptr,
1086 .flags = 0,
1087 .magFilter = VK_FILTER_NEAREST,
1088 .minFilter = VK_FILTER_NEAREST,
1089 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
1090 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
1091 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
1092 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
1093 .mipLodBias = 0.0f,
1094 .anisotropyEnable = VK_FALSE,
1095 .maxAnisotropy = 0.0f,
1096 .compareEnable = VK_FALSE,
1097 .compareOp = VK_COMPARE_OP_NEVER,
1098 .minLod = 0.0f,
1099 .maxLod = 0.0f,
1100 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
1101 .unnormalizedCoordinates = VK_FALSE,
1102 };
1103
1104 sampler = device.GetLogical().CreateSampler(ci);
1105 nn_sampler = device.GetLogical().CreateSampler(ci_nn);
1106}
1107
1108void BlitScreen::ReleaseRawImages() {
1109 for (const u64 tick : resource_ticks) {
1110 scheduler.Wait(tick);
1111 }
1112 raw_images.clear();
1113 aa_image_view.reset();
1114 aa_image.reset();
1115 buffer.reset();
1116}
1117
1118void BlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) {
1119 const VkBufferCreateInfo ci{
1120 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
1121 .pNext = nullptr,
1122 .flags = 0,
1123 .size = CalculateBufferSize(framebuffer),
1124 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
1125 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1126 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
1127 .queueFamilyIndexCount = 0,
1128 .pQueueFamilyIndices = nullptr,
1129 };
1130
1131 buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload);
1132}
1133
1134void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
1135 raw_images.resize(image_count);
1136 raw_image_views.resize(image_count);
1137
1138 const auto create_image = [&](bool used_on_framebuffer = false, u32 up_scale = 1,
1139 u32 down_shift = 0) {
1140 u32 extra_usages = used_on_framebuffer ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
1141 : VK_IMAGE_USAGE_TRANSFER_DST_BIT;
1142 return memory_allocator.CreateImage(VkImageCreateInfo{
1143 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
1144 .pNext = nullptr,
1145 .flags = 0,
1146 .imageType = VK_IMAGE_TYPE_2D,
1147 .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : framebuffer_view_format,
1148 .extent =
1149 {
1150 .width = (up_scale * framebuffer.width) >> down_shift,
1151 .height = (up_scale * framebuffer.height) >> down_shift,
1152 .depth = 1,
1153 },
1154 .mipLevels = 1,
1155 .arrayLayers = 1,
1156 .samples = VK_SAMPLE_COUNT_1_BIT,
1157 .tiling = used_on_framebuffer ? VK_IMAGE_TILING_OPTIMAL : VK_IMAGE_TILING_LINEAR,
1158 .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | extra_usages,
1159 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
1160 .queueFamilyIndexCount = 0,
1161 .pQueueFamilyIndices = nullptr,
1162 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
1163 });
1164 };
1165 const auto create_image_view = [&](vk::Image& image, bool used_on_framebuffer = false) {
1166 return device.GetLogical().CreateImageView(VkImageViewCreateInfo{
1167 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
1168 .pNext = nullptr,
1169 .flags = 0,
1170 .image = *image,
1171 .viewType = VK_IMAGE_VIEW_TYPE_2D,
1172 .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : framebuffer_view_format,
1173 .components =
1174 {
1175 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
1176 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
1177 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
1178 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
1179 },
1180 .subresourceRange =
1181 {
1182 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1183 .baseMipLevel = 0,
1184 .levelCount = 1,
1185 .baseArrayLayer = 0,
1186 .layerCount = 1,
1187 },
1188 });
1189 };
1190
1191 for (size_t i = 0; i < image_count; ++i) {
1192 raw_images[i] = create_image();
1193 raw_image_views[i] = create_image_view(raw_images[i]);
1194 }
1195
1196 // AA Resources
1197 const u32 up_scale = Settings::values.resolution_info.up_scale;
1198 const u32 down_shift = Settings::values.resolution_info.down_shift;
1199 aa_image = create_image(true, up_scale, down_shift);
1200 aa_image_view = create_image_view(aa_image, true);
1201 VkExtent2D size{
1202 .width = (up_scale * framebuffer.width) >> down_shift,
1203 .height = (up_scale * framebuffer.height) >> down_shift,
1204 };
1205 if (aa_renderpass) {
1206 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
1207 return;
1208 }
1209 aa_renderpass = CreateRenderPassImpl(VK_FORMAT_R16G16B16A16_SFLOAT);
1210 aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass);
1211
1212 const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{
1213 {
1214 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1215 .pNext = nullptr,
1216 .flags = 0,
1217 .stage = VK_SHADER_STAGE_VERTEX_BIT,
1218 .module = *fxaa_vertex_shader,
1219 .pName = "main",
1220 .pSpecializationInfo = nullptr,
1221 },
1222 {
1223 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1224 .pNext = nullptr,
1225 .flags = 0,
1226 .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
1227 .module = *fxaa_fragment_shader,
1228 .pName = "main",
1229 .pSpecializationInfo = nullptr,
1230 },
1231 }};
1232
1233 const auto vertex_binding_description = ScreenRectVertex::GetDescription();
1234 const auto vertex_attrs_description = ScreenRectVertex::GetAttributes();
1235
1236 const VkPipelineVertexInputStateCreateInfo vertex_input_ci{
1237 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
1238 .pNext = nullptr,
1239 .flags = 0,
1240 .vertexBindingDescriptionCount = 1,
1241 .pVertexBindingDescriptions = &vertex_binding_description,
1242 .vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()},
1243 .pVertexAttributeDescriptions = vertex_attrs_description.data(),
1244 };
1245
1246 const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
1247 .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
1248 .pNext = nullptr,
1249 .flags = 0,
1250 .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
1251 .primitiveRestartEnable = VK_FALSE,
1252 };
1253
1254 const VkPipelineViewportStateCreateInfo viewport_state_ci{
1255 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
1256 .pNext = nullptr,
1257 .flags = 0,
1258 .viewportCount = 1,
1259 .pViewports = nullptr,
1260 .scissorCount = 1,
1261 .pScissors = nullptr,
1262 };
1263
1264 const VkPipelineRasterizationStateCreateInfo rasterization_ci{
1265 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
1266 .pNext = nullptr,
1267 .flags = 0,
1268 .depthClampEnable = VK_FALSE,
1269 .rasterizerDiscardEnable = VK_FALSE,
1270 .polygonMode = VK_POLYGON_MODE_FILL,
1271 .cullMode = VK_CULL_MODE_NONE,
1272 .frontFace = VK_FRONT_FACE_CLOCKWISE,
1273 .depthBiasEnable = VK_FALSE,
1274 .depthBiasConstantFactor = 0.0f,
1275 .depthBiasClamp = 0.0f,
1276 .depthBiasSlopeFactor = 0.0f,
1277 .lineWidth = 1.0f,
1278 };
1279
1280 const VkPipelineMultisampleStateCreateInfo multisampling_ci{
1281 .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
1282 .pNext = nullptr,
1283 .flags = 0,
1284 .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
1285 .sampleShadingEnable = VK_FALSE,
1286 .minSampleShading = 0.0f,
1287 .pSampleMask = nullptr,
1288 .alphaToCoverageEnable = VK_FALSE,
1289 .alphaToOneEnable = VK_FALSE,
1290 };
1291
1292 const VkPipelineColorBlendAttachmentState color_blend_attachment{
1293 .blendEnable = VK_FALSE,
1294 .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
1295 .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
1296 .colorBlendOp = VK_BLEND_OP_ADD,
1297 .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
1298 .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
1299 .alphaBlendOp = VK_BLEND_OP_ADD,
1300 .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
1301 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
1302 };
1303
1304 const VkPipelineColorBlendStateCreateInfo color_blend_ci{
1305 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
1306 .pNext = nullptr,
1307 .flags = 0,
1308 .logicOpEnable = VK_FALSE,
1309 .logicOp = VK_LOGIC_OP_COPY,
1310 .attachmentCount = 1,
1311 .pAttachments = &color_blend_attachment,
1312 .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
1313 };
1314
1315 static constexpr std::array dynamic_states{
1316 VK_DYNAMIC_STATE_VIEWPORT,
1317 VK_DYNAMIC_STATE_SCISSOR,
1318 };
1319 const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
1320 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
1321 .pNext = nullptr,
1322 .flags = 0,
1323 .dynamicStateCount = static_cast<u32>(dynamic_states.size()),
1324 .pDynamicStates = dynamic_states.data(),
1325 };
1326
1327 const VkGraphicsPipelineCreateInfo fxaa_pipeline_ci{
1328 .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
1329 .pNext = nullptr,
1330 .flags = 0,
1331 .stageCount = static_cast<u32>(fxaa_shader_stages.size()),
1332 .pStages = fxaa_shader_stages.data(),
1333 .pVertexInputState = &vertex_input_ci,
1334 .pInputAssemblyState = &input_assembly_ci,
1335 .pTessellationState = nullptr,
1336 .pViewportState = &viewport_state_ci,
1337 .pRasterizationState = &rasterization_ci,
1338 .pMultisampleState = &multisampling_ci,
1339 .pDepthStencilState = nullptr,
1340 .pColorBlendState = &color_blend_ci,
1341 .pDynamicState = &dynamic_state_ci,
1342 .layout = *aa_pipeline_layout,
1343 .renderPass = *aa_renderpass,
1344 .subpass = 0,
1345 .basePipelineHandle = 0,
1346 .basePipelineIndex = 0,
1347 };
1348
1349 // AA
1350 aa_pipeline = device.GetLogical().CreateGraphicsPipeline(fxaa_pipeline_ci);
1351}
1352
1353void BlitScreen::UpdateAADescriptorSet(VkImageView image_view, bool nn) const {
1354 const VkDescriptorImageInfo image_info{
1355 .sampler = nn ? *nn_sampler : *sampler,
1356 .imageView = image_view,
1357 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
1358 };
1359
1360 const VkWriteDescriptorSet sampler_write{
1361 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1362 .pNext = nullptr,
1363 .dstSet = aa_descriptor_sets[image_index],
1364 .dstBinding = 0,
1365 .dstArrayElement = 0,
1366 .descriptorCount = 1,
1367 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1368 .pImageInfo = &image_info,
1369 .pBufferInfo = nullptr,
1370 .pTexelBufferView = nullptr,
1371 };
1372
1373 const VkWriteDescriptorSet sampler_write_2{
1374 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1375 .pNext = nullptr,
1376 .dstSet = aa_descriptor_sets[image_index],
1377 .dstBinding = 1,
1378 .dstArrayElement = 0,
1379 .descriptorCount = 1,
1380 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1381 .pImageInfo = &image_info,
1382 .pBufferInfo = nullptr,
1383 .pTexelBufferView = nullptr,
1384 };
1385
1386 device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, sampler_write_2}, {});
1387}
1388
1389void BlitScreen::UpdateDescriptorSet(VkImageView image_view, bool nn) const {
1390 const VkDescriptorBufferInfo buffer_info{
1391 .buffer = *buffer,
1392 .offset = offsetof(BufferData, uniform),
1393 .range = sizeof(BufferData::uniform),
1394 };
1395
1396 const VkWriteDescriptorSet ubo_write{
1397 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1398 .pNext = nullptr,
1399 .dstSet = descriptor_sets[image_index],
1400 .dstBinding = 0,
1401 .dstArrayElement = 0,
1402 .descriptorCount = 1,
1403 .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1404 .pImageInfo = nullptr,
1405 .pBufferInfo = &buffer_info,
1406 .pTexelBufferView = nullptr,
1407 };
1408
1409 const VkDescriptorImageInfo image_info{
1410 .sampler = nn ? *nn_sampler : *sampler,
1411 .imageView = image_view,
1412 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
1413 };
1414
1415 const VkWriteDescriptorSet sampler_write{
1416 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1417 .pNext = nullptr,
1418 .dstSet = descriptor_sets[image_index],
1419 .dstBinding = 1,
1420 .dstArrayElement = 0,
1421 .descriptorCount = 1,
1422 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1423 .pImageInfo = &image_info,
1424 .pBufferInfo = nullptr,
1425 .pTexelBufferView = nullptr,
1426 };
1427
1428 device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {});
1429}
1430
1431void BlitScreen::SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const {
1432 data.uniform.modelview_matrix =
1433 MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height));
1434}
1435
1436void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
1437 const Layout::FramebufferLayout layout) const {
1438 f32 left, top, right, bottom;
1439
1440 if (fsr) {
1441 // FSR has already applied the crop, so we just want to render the image
1442 // it has produced.
1443 left = 0;
1444 top = 0;
1445 right = 1;
1446 bottom = 1;
1447 } else {
1448 // Get the normalized crop rectangle.
1449 const auto crop = NormalizeCrop(framebuffer, screen_info);
1450
1451 // Apply the crop.
1452 left = crop.left;
1453 top = crop.top;
1454 right = crop.right;
1455 bottom = crop.bottom;
1456 }
1457
1458 // Map the coordinates to the screen.
1459 const auto& screen = layout.screen;
1460 const auto x = static_cast<f32>(screen.left);
1461 const auto y = static_cast<f32>(screen.top);
1462 const auto w = static_cast<f32>(screen.GetWidth());
1463 const auto h = static_cast<f32>(screen.GetHeight());
1464
1465 data.vertices[0] = ScreenRectVertex(x, y, left, top);
1466 data.vertices[1] = ScreenRectVertex(x + w, y, right, top);
1467 data.vertices[2] = ScreenRectVertex(x, y + h, left, bottom);
1468 data.vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom);
1469}
1470
1471void BlitScreen::CreateSMAA(VkExtent2D smaa_size) {
1472 smaa = std::make_unique<SMAA>(device, memory_allocator, image_count, smaa_size);
1473}
1474
1475void BlitScreen::CreateFSR() {
1476 const auto& layout = render_window.GetFramebufferLayout();
1477 const VkExtent2D fsr_size{
1478 .width = layout.screen.GetWidth(),
1479 .height = layout.screen.GetHeight(),
1480 };
1481 fsr = std::make_unique<FSR>(device, memory_allocator, image_count, fsr_size);
1482}
1483
1484u64 BlitScreen::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const {
1485 return sizeof(BufferData) + GetSizeInBytes(framebuffer) * image_count;
1486}
1487
1488u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const {
1489 constexpr auto first_image_offset = static_cast<u64>(sizeof(BufferData));
1490 return first_image_offset + GetSizeInBytes(framebuffer) * image_index;
1491}
1492
1493} // namespace Vulkan 149} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 3eff76009..cbdf2d5d0 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -3,10 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <list>
6#include <memory> 7#include <memory>
7 8
8#include "core/frontend/framebuffer_layout.h" 9#include "core/frontend/framebuffer_layout.h"
9#include "video_core/host1x/gpu_device_memory_manager.h" 10#include "video_core/host1x/gpu_device_memory_manager.h"
11#include "video_core/renderer_vulkan/present/layer.h"
10#include "video_core/vulkan_common/vulkan_memory_allocator.h" 12#include "video_core/vulkan_common/vulkan_memory_allocator.h"
11#include "video_core/vulkan_common/vulkan_wrapper.h" 13#include "video_core/vulkan_common/vulkan_wrapper.h"
12 14
@@ -14,155 +16,67 @@ namespace Core {
14class System; 16class System;
15} 17}
16 18
17namespace Core::Frontend {
18class EmuWindow;
19}
20
21namespace Tegra { 19namespace Tegra {
22struct FramebufferConfig; 20struct FramebufferConfig;
23} 21}
24 22
25namespace VideoCore { 23namespace Settings {
26class RasterizerInterface; 24enum class ScalingFilter : u32;
27} 25} // namespace Settings
28
29namespace Service::android {
30enum class PixelFormat : u32;
31}
32 26
33namespace Vulkan { 27namespace Vulkan {
34 28
35struct ScreenInfo;
36
37class Device; 29class Device;
38class FSR;
39class RasterizerVulkan; 30class RasterizerVulkan;
40class Scheduler; 31class Scheduler;
41class SMAA;
42class Swapchain;
43class PresentManager; 32class PresentManager;
33class WindowAdaptPass;
44 34
45struct Frame; 35struct Frame;
46 36
47struct ScreenInfo { 37struct FramebufferTextureInfo {
48 VkImage image{}; 38 VkImage image{};
49 VkImageView image_view{}; 39 VkImageView image_view{};
50 u32 width{}; 40 u32 width{};
51 u32 height{}; 41 u32 height{};
42 u32 scaled_width{};
43 u32 scaled_height{};
52}; 44};
53 45
54class BlitScreen { 46class BlitScreen {
55public: 47public:
56 explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, 48 explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device,
57 Core::Frontend::EmuWindow& render_window, const Device& device, 49 MemoryAllocator& memory_allocator, PresentManager& present_manager,
58 MemoryAllocator& memory_manager, Swapchain& swapchain, 50 Scheduler& scheduler);
59 PresentManager& present_manager, Scheduler& scheduler,
60 const ScreenInfo& screen_info);
61 ~BlitScreen(); 51 ~BlitScreen();
62 52
63 void Recreate(); 53 void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
64 54 std::span<const Tegra::FramebufferConfig> framebuffers,
65 void Draw(const Tegra::FramebufferConfig& framebuffer, const VkFramebuffer& host_framebuffer, 55 const Layout::FramebufferLayout& layout, size_t current_swapchain_image_count,
66 const Layout::FramebufferLayout layout, VkExtent2D render_area, bool use_accelerated); 56 VkFormat current_swapchain_view_format);
67
68 void DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer,
69 bool use_accelerated);
70 57
71 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, 58 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout,
72 VkExtent2D extent); 59 VkImageView image_view,
73 60 VkFormat current_view_format);
74 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view,
75 VkExtent2D extent, vk::RenderPass& rd);
76 61
77private: 62private:
78 struct BufferData; 63 void WaitIdle();
79 64 void SetWindowAdaptPass();
80 void CreateStaticResources(); 65 vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent,
81 void CreateShaders(); 66 VkRenderPass render_pass);
82 void CreateDescriptorPool();
83 void CreateRenderPass();
84 vk::RenderPass CreateRenderPassImpl(VkFormat format);
85 void CreateDescriptorSetLayout();
86 void CreateDescriptorSets();
87 void CreatePipelineLayout();
88 void CreateGraphicsPipeline();
89 void CreateSampler();
90
91 void CreateDynamicResources();
92
93 void RefreshResources(const Tegra::FramebufferConfig& framebuffer);
94 void ReleaseRawImages();
95 void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer);
96 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
97
98 void UpdateDescriptorSet(VkImageView image_view, bool nn) const;
99 void UpdateAADescriptorSet(VkImageView image_view, bool nn) const;
100 void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const;
101 void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
102 const Layout::FramebufferLayout layout) const;
103
104 void CreateSMAA(VkExtent2D smaa_size);
105 void CreateFSR();
106
107 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
108 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const;
109 67
110 Tegra::MaxwellDeviceMemoryManager& device_memory; 68 Tegra::MaxwellDeviceMemoryManager& device_memory;
111 Core::Frontend::EmuWindow& render_window;
112 const Device& device; 69 const Device& device;
113 MemoryAllocator& memory_allocator; 70 MemoryAllocator& memory_allocator;
114 Swapchain& swapchain;
115 PresentManager& present_manager; 71 PresentManager& present_manager;
116 Scheduler& scheduler; 72 Scheduler& scheduler;
117 std::size_t image_count; 73 std::size_t image_count{};
118 std::size_t image_index{}; 74 std::size_t image_index{};
119 const ScreenInfo& screen_info; 75 VkFormat swapchain_view_format{};
120 76
121 vk::ShaderModule vertex_shader; 77 Settings::ScalingFilter scaling_filter{};
122 vk::ShaderModule fxaa_vertex_shader; 78 std::unique_ptr<WindowAdaptPass> window_adapt{};
123 vk::ShaderModule fxaa_fragment_shader; 79 std::list<Layer> layers{};
124 vk::ShaderModule bilinear_fragment_shader;
125 vk::ShaderModule bicubic_fragment_shader;
126 vk::ShaderModule gaussian_fragment_shader;
127 vk::ShaderModule scaleforce_fragment_shader;
128 vk::DescriptorPool descriptor_pool;
129 vk::DescriptorSetLayout descriptor_set_layout;
130 vk::PipelineLayout pipeline_layout;
131 vk::Pipeline nearest_neighbor_pipeline;
132 vk::Pipeline bilinear_pipeline;
133 vk::Pipeline bicubic_pipeline;
134 vk::Pipeline gaussian_pipeline;
135 vk::Pipeline scaleforce_pipeline;
136 vk::RenderPass renderpass;
137 vk::DescriptorSets descriptor_sets;
138 vk::Sampler nn_sampler;
139 vk::Sampler sampler;
140
141 vk::Buffer buffer;
142
143 std::vector<u64> resource_ticks;
144
145 std::vector<vk::Image> raw_images;
146 std::vector<vk::ImageView> raw_image_views;
147
148 vk::DescriptorPool aa_descriptor_pool;
149 vk::DescriptorSetLayout aa_descriptor_set_layout;
150 vk::PipelineLayout aa_pipeline_layout;
151 vk::Pipeline aa_pipeline;
152 vk::RenderPass aa_renderpass;
153 vk::Framebuffer aa_framebuffer;
154 vk::DescriptorSets aa_descriptor_sets;
155 vk::Image aa_image;
156 vk::ImageView aa_image_view;
157
158 u32 raw_width = 0;
159 u32 raw_height = 0;
160 Service::android::PixelFormat pixel_format{};
161 VkFormat framebuffer_view_format;
162 VkFormat swapchain_view_format;
163
164 std::unique_ptr<FSR> fsr;
165 std::unique_ptr<SMAA> smaa;
166}; 80};
167 81
168} // namespace Vulkan 82} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp
deleted file mode 100644
index f7a05fbc0..000000000
--- a/src/video_core/renderer_vulkan/vk_fsr.cpp
+++ /dev/null
@@ -1,420 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/common_types.h"
5#include "common/div_ceil.h"
6#include "common/settings.h"
7
8#include "video_core/fsr.h"
9#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h"
10#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h"
11#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h"
12#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32_comp_spv.h"
13#include "video_core/renderer_vulkan/vk_fsr.h"
14#include "video_core/renderer_vulkan/vk_scheduler.h"
15#include "video_core/renderer_vulkan/vk_shader_util.h"
16#include "video_core/vulkan_common/vulkan_device.h"
17
18namespace Vulkan {
19using namespace FSR;
20
21FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_,
22 VkExtent2D output_size_)
23 : device{device_}, memory_allocator{memory_allocator_}, image_count{image_count_},
24 output_size{output_size_} {
25
26 CreateImages();
27 CreateSampler();
28 CreateShaders();
29 CreateDescriptorPool();
30 CreateDescriptorSetLayout();
31 CreateDescriptorSets();
32 CreatePipelineLayout();
33 CreatePipeline();
34}
35
36VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view,
37 VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect) {
38
39 UpdateDescriptorSet(image_index, image_view);
40
41 scheduler.Record([this, image_index, input_image_extent, crop_rect](vk::CommandBuffer cmdbuf) {
42 const VkImageMemoryBarrier base_barrier{
43 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
44 .pNext = nullptr,
45 .srcAccessMask = 0,
46 .dstAccessMask = 0,
47 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
48 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
49 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
50 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
51 .image = {},
52 .subresourceRange =
53 {
54 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
55 .baseMipLevel = 0,
56 .levelCount = 1,
57 .baseArrayLayer = 0,
58 .layerCount = 1,
59 },
60 };
61
62 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *easu_pipeline);
63
64 const f32 input_image_width = static_cast<f32>(input_image_extent.width);
65 const f32 input_image_height = static_cast<f32>(input_image_extent.height);
66 const f32 output_image_width = static_cast<f32>(output_size.width);
67 const f32 output_image_height = static_cast<f32>(output_size.height);
68 const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width;
69 const f32 viewport_x = crop_rect.left * input_image_width;
70 const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
71 const f32 viewport_y = crop_rect.top * input_image_height;
72
73 std::array<u32, 4 * 4> push_constants;
74 FsrEasuConOffset(push_constants.data() + 0, push_constants.data() + 4,
75 push_constants.data() + 8, push_constants.data() + 12,
76
77 viewport_width, viewport_height, input_image_width, input_image_height,
78 output_image_width, output_image_height, viewport_x, viewport_y);
79 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants);
80
81 {
82 VkImageMemoryBarrier fsr_write_barrier = base_barrier;
83 fsr_write_barrier.image = *images[image_index];
84 fsr_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
85
86 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
87 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, fsr_write_barrier);
88 }
89
90 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0,
91 descriptor_sets[image_index * 2], {});
92 cmdbuf.Dispatch(Common::DivCeil(output_size.width, 16u),
93 Common::DivCeil(output_size.height, 16u), 1);
94
95 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *rcas_pipeline);
96
97 const float sharpening =
98 static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
99
100 FsrRcasCon(push_constants.data(), sharpening);
101 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants);
102
103 {
104 std::array<VkImageMemoryBarrier, 2> barriers;
105 auto& fsr_read_barrier = barriers[0];
106 auto& blit_write_barrier = barriers[1];
107
108 fsr_read_barrier = base_barrier;
109 fsr_read_barrier.image = *images[image_index];
110 fsr_read_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
111 fsr_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
112
113 blit_write_barrier = base_barrier;
114 blit_write_barrier.image = *images[image_count + image_index];
115 blit_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
116 blit_write_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
117
118 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
119 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, {}, {}, barriers);
120 }
121
122 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0,
123 descriptor_sets[image_index * 2 + 1], {});
124 cmdbuf.Dispatch(Common::DivCeil(output_size.width, 16u),
125 Common::DivCeil(output_size.height, 16u), 1);
126
127 {
128 std::array<VkImageMemoryBarrier, 1> barriers;
129 auto& blit_read_barrier = barriers[0];
130
131 blit_read_barrier = base_barrier;
132 blit_read_barrier.image = *images[image_count + image_index];
133 blit_read_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
134 blit_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
135
136 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
137 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, {}, {}, barriers);
138 }
139 });
140
141 return *image_views[image_count + image_index];
142}
143
144void FSR::CreateDescriptorPool() {
145 const std::array<VkDescriptorPoolSize, 2> pool_sizes{{
146 {
147 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
148 .descriptorCount = static_cast<u32>(image_count * 2),
149 },
150 {
151 .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
152 .descriptorCount = static_cast<u32>(image_count * 2),
153 },
154 }};
155
156 const VkDescriptorPoolCreateInfo ci{
157 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
158 .pNext = nullptr,
159 .flags = 0,
160 .maxSets = static_cast<u32>(image_count * 2),
161 .poolSizeCount = static_cast<u32>(pool_sizes.size()),
162 .pPoolSizes = pool_sizes.data(),
163 };
164 descriptor_pool = device.GetLogical().CreateDescriptorPool(ci);
165}
166
167void FSR::CreateDescriptorSetLayout() {
168 const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{
169 {
170 .binding = 0,
171 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
172 .descriptorCount = 1,
173 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
174 .pImmutableSamplers = sampler.address(),
175 },
176 {
177 .binding = 1,
178 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
179 .descriptorCount = 1,
180 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
181 .pImmutableSamplers = sampler.address(),
182 },
183 }};
184
185 const VkDescriptorSetLayoutCreateInfo ci{
186 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
187 .pNext = nullptr,
188 .flags = 0,
189 .bindingCount = static_cast<u32>(layout_bindings.size()),
190 .pBindings = layout_bindings.data(),
191 };
192
193 descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci);
194}
195
196void FSR::CreateDescriptorSets() {
197 const u32 sets = static_cast<u32>(image_count * 2);
198 const std::vector layouts(sets, *descriptor_set_layout);
199
200 const VkDescriptorSetAllocateInfo ai{
201 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
202 .pNext = nullptr,
203 .descriptorPool = *descriptor_pool,
204 .descriptorSetCount = sets,
205 .pSetLayouts = layouts.data(),
206 };
207
208 descriptor_sets = descriptor_pool.Allocate(ai);
209}
210
211void FSR::CreateImages() {
212 images.resize(image_count * 2);
213 image_views.resize(image_count * 2);
214
215 for (size_t i = 0; i < image_count * 2; ++i) {
216 images[i] = memory_allocator.CreateImage(VkImageCreateInfo{
217 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
218 .pNext = nullptr,
219 .flags = 0,
220 .imageType = VK_IMAGE_TYPE_2D,
221 .format = VK_FORMAT_R16G16B16A16_SFLOAT,
222 .extent =
223 {
224 .width = output_size.width,
225 .height = output_size.height,
226 .depth = 1,
227 },
228 .mipLevels = 1,
229 .arrayLayers = 1,
230 .samples = VK_SAMPLE_COUNT_1_BIT,
231 .tiling = VK_IMAGE_TILING_OPTIMAL,
232 .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
233 VK_IMAGE_USAGE_SAMPLED_BIT,
234 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
235 .queueFamilyIndexCount = 0,
236 .pQueueFamilyIndices = nullptr,
237 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
238 });
239 image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
240 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
241 .pNext = nullptr,
242 .flags = 0,
243 .image = *images[i],
244 .viewType = VK_IMAGE_VIEW_TYPE_2D,
245 .format = VK_FORMAT_R16G16B16A16_SFLOAT,
246 .components =
247 {
248 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
249 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
250 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
251 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
252 },
253 .subresourceRange =
254 {
255 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
256 .baseMipLevel = 0,
257 .levelCount = 1,
258 .baseArrayLayer = 0,
259 .layerCount = 1,
260 },
261 });
262 }
263}
264
265void FSR::CreatePipelineLayout() {
266 VkPushConstantRange push_const{
267 .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
268 .offset = 0,
269 .size = sizeof(std::array<u32, 4 * 4>),
270 };
271 VkPipelineLayoutCreateInfo ci{
272 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
273 .pNext = nullptr,
274 .flags = 0,
275 .setLayoutCount = 1,
276 .pSetLayouts = descriptor_set_layout.address(),
277 .pushConstantRangeCount = 1,
278 .pPushConstantRanges = &push_const,
279 };
280
281 pipeline_layout = device.GetLogical().CreatePipelineLayout(ci);
282}
283
284void FSR::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const {
285 const auto fsr_image_view = *image_views[image_index];
286 const auto blit_image_view = *image_views[image_count + image_index];
287
288 const VkDescriptorImageInfo image_info{
289 .sampler = VK_NULL_HANDLE,
290 .imageView = image_view,
291 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
292 };
293 const VkDescriptorImageInfo fsr_image_info{
294 .sampler = VK_NULL_HANDLE,
295 .imageView = fsr_image_view,
296 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
297 };
298 const VkDescriptorImageInfo blit_image_info{
299 .sampler = VK_NULL_HANDLE,
300 .imageView = blit_image_view,
301 .imageLayout = VK_IMAGE_LAYOUT_GENERAL,
302 };
303
304 VkWriteDescriptorSet sampler_write{
305 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
306 .pNext = nullptr,
307 .dstSet = descriptor_sets[image_index * 2],
308 .dstBinding = 0,
309 .dstArrayElement = 0,
310 .descriptorCount = 1,
311 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
312 .pImageInfo = &image_info,
313 .pBufferInfo = nullptr,
314 .pTexelBufferView = nullptr,
315 };
316
317 VkWriteDescriptorSet output_write{
318 .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
319 .pNext = nullptr,
320 .dstSet = descriptor_sets[image_index * 2],
321 .dstBinding = 1,
322 .dstArrayElement = 0,
323 .descriptorCount = 1,
324 .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
325 .pImageInfo = &fsr_image_info,
326 .pBufferInfo = nullptr,
327 .pTexelBufferView = nullptr,
328 };
329
330 device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, output_write}, {});
331
332 sampler_write.dstSet = descriptor_sets[image_index * 2 + 1];
333 sampler_write.pImageInfo = &fsr_image_info;
334 output_write.dstSet = descriptor_sets[image_index * 2 + 1];
335 output_write.pImageInfo = &blit_image_info;
336
337 device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, output_write}, {});
338}
339
340void FSR::CreateSampler() {
341 const VkSamplerCreateInfo ci{
342 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
343 .pNext = nullptr,
344 .flags = 0,
345 .magFilter = VK_FILTER_LINEAR,
346 .minFilter = VK_FILTER_LINEAR,
347 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
348 .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
349 .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
350 .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
351 .mipLodBias = 0.0f,
352 .anisotropyEnable = VK_FALSE,
353 .maxAnisotropy = 0.0f,
354 .compareEnable = VK_FALSE,
355 .compareOp = VK_COMPARE_OP_NEVER,
356 .minLod = 0.0f,
357 .maxLod = 0.0f,
358 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
359 .unnormalizedCoordinates = VK_FALSE,
360 };
361
362 sampler = device.GetLogical().CreateSampler(ci);
363}
364
365void FSR::CreateShaders() {
366 if (device.IsFloat16Supported()) {
367 easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_FP16_COMP_SPV);
368 rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_FP16_COMP_SPV);
369 } else {
370 easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_FP32_COMP_SPV);
371 rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_FP32_COMP_SPV);
372 }
373}
374
375void FSR::CreatePipeline() {
376 VkPipelineShaderStageCreateInfo shader_stage_easu{
377 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
378 .pNext = nullptr,
379 .flags = 0,
380 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
381 .module = *easu_shader,
382 .pName = "main",
383 .pSpecializationInfo = nullptr,
384 };
385
386 VkPipelineShaderStageCreateInfo shader_stage_rcas{
387 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
388 .pNext = nullptr,
389 .flags = 0,
390 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
391 .module = *rcas_shader,
392 .pName = "main",
393 .pSpecializationInfo = nullptr,
394 };
395
396 VkComputePipelineCreateInfo pipeline_ci_easu{
397 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
398 .pNext = nullptr,
399 .flags = 0,
400 .stage = shader_stage_easu,
401 .layout = *pipeline_layout,
402 .basePipelineHandle = VK_NULL_HANDLE,
403 .basePipelineIndex = 0,
404 };
405
406 VkComputePipelineCreateInfo pipeline_ci_rcas{
407 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
408 .pNext = nullptr,
409 .flags = 0,
410 .stage = shader_stage_rcas,
411 .layout = *pipeline_layout,
412 .basePipelineHandle = VK_NULL_HANDLE,
413 .basePipelineIndex = 0,
414 };
415
416 easu_pipeline = device.GetLogical().CreateComputePipeline(pipeline_ci_easu);
417 rcas_pipeline = device.GetLogical().CreateComputePipeline(pipeline_ci_rcas);
418}
419
420} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_fsr.h b/src/video_core/renderer_vulkan/vk_fsr.h
deleted file mode 100644
index 3505c1416..000000000
--- a/src/video_core/renderer_vulkan/vk_fsr.h
+++ /dev/null
@@ -1,52 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/math_util.h"
7#include "video_core/vulkan_common/vulkan_memory_allocator.h"
8#include "video_core/vulkan_common/vulkan_wrapper.h"
9
10namespace Vulkan {
11
12class Device;
13class Scheduler;
14
15class FSR {
16public:
17 explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count,
18 VkExtent2D output_size);
19 VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view,
20 VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect);
21
22private:
23 void CreateDescriptorPool();
24 void CreateDescriptorSetLayout();
25 void CreateDescriptorSets();
26 void CreateImages();
27 void CreateSampler();
28 void CreateShaders();
29 void CreatePipeline();
30 void CreatePipelineLayout();
31
32 void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const;
33
34 const Device& device;
35 MemoryAllocator& memory_allocator;
36 size_t image_count;
37 VkExtent2D output_size;
38
39 vk::DescriptorPool descriptor_pool;
40 vk::DescriptorSetLayout descriptor_set_layout;
41 vk::DescriptorSets descriptor_sets;
42 vk::PipelineLayout pipeline_layout;
43 vk::ShaderModule easu_shader;
44 vk::ShaderModule rcas_shader;
45 vk::Pipeline easu_pipeline;
46 vk::Pipeline rcas_pipeline;
47 vk::Sampler sampler;
48 std::vector<vk::Image> images;
49 std::vector<vk::ImageView> image_views;
50};
51
52} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 5bf41b81f..aa0a027bb 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -165,10 +165,9 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances,
165 165
166RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 166RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
167 Tegra::MaxwellDeviceMemoryManager& device_memory_, 167 Tegra::MaxwellDeviceMemoryManager& device_memory_,
168 ScreenInfo& screen_info_, const Device& device_, 168 const Device& device_, MemoryAllocator& memory_allocator_,
169 MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, 169 StateTracker& state_tracker_, Scheduler& scheduler_)
170 Scheduler& scheduler_) 170 : gpu{gpu_}, device_memory{device_memory_}, device{device_},
171 : gpu{gpu_}, device_memory{device_memory_}, screen_info{screen_info_}, device{device_},
172 memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_}, 171 memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_},
173 staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), 172 staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler),
174 guest_descriptor_queue(device, scheduler), compute_pass_descriptor_queue(device, scheduler), 173 guest_descriptor_queue(device, scheduler), compute_pass_descriptor_queue(device, scheduler),
@@ -783,23 +782,29 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si
783 query_cache.InvalidateRegion(*cpu_addr, copy_size); 782 query_cache.InvalidateRegion(*cpu_addr, copy_size);
784} 783}
785 784
786bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config, 785std::optional<FramebufferTextureInfo> RasterizerVulkan::AccelerateDisplay(
787 DAddr framebuffer_addr, u32 pixel_stride) { 786 const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, u32 pixel_stride) {
788 if (!framebuffer_addr) { 787 if (!framebuffer_addr) {
789 return false; 788 return {};
790 } 789 }
791 std::scoped_lock lock{texture_cache.mutex}; 790 std::scoped_lock lock{texture_cache.mutex};
792 ImageView* const image_view = 791 const auto [image_view, scaled] =
793 texture_cache.TryFindFramebufferImageView(config, framebuffer_addr); 792 texture_cache.TryFindFramebufferImageView(config, framebuffer_addr);
794 if (!image_view) { 793 if (!image_view) {
795 return false; 794 return {};
796 } 795 }
797 query_cache.NotifySegment(false); 796 query_cache.NotifySegment(false);
798 screen_info.image = image_view->ImageHandle(); 797
799 screen_info.image_view = image_view->Handle(Shader::TextureType::Color2D); 798 const auto& resolution = Settings::values.resolution_info;
800 screen_info.width = image_view->size.width; 799
801 screen_info.height = image_view->size.height; 800 FramebufferTextureInfo info{};
802 return true; 801 info.image = image_view->ImageHandle();
802 info.image_view = image_view->Handle(Shader::TextureType::Color2D);
803 info.width = image_view->size.width;
804 info.height = image_view->size.height;
805 info.scaled_width = scaled ? resolution.ScaleUp(info.width) : info.width;
806 info.scaled_height = scaled ? resolution.ScaleUp(info.height) : info.height;
807 return info;
803} 808}
804 809
805void RasterizerVulkan::LoadDiskResources(u64 title_id, std::stop_token stop_loading, 810void RasterizerVulkan::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 881ee0993..0617b37f0 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -43,7 +43,7 @@ class Maxwell3D;
43 43
44namespace Vulkan { 44namespace Vulkan {
45 45
46struct ScreenInfo; 46struct FramebufferTextureInfo;
47 47
48class StateTracker; 48class StateTracker;
49 49
@@ -78,9 +78,8 @@ class RasterizerVulkan final : public VideoCore::RasterizerInterface,
78public: 78public:
79 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 79 explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
80 Tegra::MaxwellDeviceMemoryManager& device_memory_, 80 Tegra::MaxwellDeviceMemoryManager& device_memory_,
81 ScreenInfo& screen_info_, const Device& device_, 81 const Device& device_, MemoryAllocator& memory_allocator_,
82 MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, 82 StateTracker& state_tracker_, Scheduler& scheduler_);
83 Scheduler& scheduler_);
84 ~RasterizerVulkan() override; 83 ~RasterizerVulkan() override;
85 84
86 void Draw(bool is_indexed, u32 instance_count) override; 85 void Draw(bool is_indexed, u32 instance_count) override;
@@ -126,8 +125,6 @@ public:
126 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; 125 Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override;
127 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, 126 void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
128 std::span<const u8> memory) override; 127 std::span<const u8> memory) override;
129 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, DAddr framebuffer_addr,
130 u32 pixel_stride) override;
131 void LoadDiskResources(u64 title_id, std::stop_token stop_loading, 128 void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
132 const VideoCore::DiskResourceLoadCallback& callback) override; 129 const VideoCore::DiskResourceLoadCallback& callback) override;
133 130
@@ -137,6 +134,10 @@ public:
137 134
138 void ReleaseChannel(s32 channel_id) override; 135 void ReleaseChannel(s32 channel_id) override;
139 136
137 std::optional<FramebufferTextureInfo> AccelerateDisplay(const Tegra::FramebufferConfig& config,
138 VAddr framebuffer_addr,
139 u32 pixel_stride);
140
140private: 141private:
141 static constexpr size_t MAX_TEXTURES = 192; 142 static constexpr size_t MAX_TEXTURES = 192;
142 static constexpr size_t MAX_IMAGES = 48; 143 static constexpr size_t MAX_IMAGES = 48;
@@ -182,7 +183,6 @@ private:
182 Tegra::GPU& gpu; 183 Tegra::GPU& gpu;
183 Tegra::MaxwellDeviceMemoryManager& device_memory; 184 Tegra::MaxwellDeviceMemoryManager& device_memory;
184 185
185 ScreenInfo& screen_info;
186 const Device& device; 186 const Device& device;
187 MemoryAllocator& memory_allocator; 187 MemoryAllocator& memory_allocator;
188 StateTracker& state_tracker; 188 StateTracker& state_tracker;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 7398ed2ec..a20c956ff 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -713,12 +713,12 @@ bool TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
713} 713}
714 714
715template <class P> 715template <class P>
716typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView( 716std::pair<typename P::ImageView*, bool> TextureCache<P>::TryFindFramebufferImageView(
717 const Tegra::FramebufferConfig& config, DAddr cpu_addr) { 717 const Tegra::FramebufferConfig& config, DAddr cpu_addr) {
718 // TODO: Properly implement this 718 // TODO: Properly implement this
719 const auto it = page_table.find(cpu_addr >> YUZU_PAGEBITS); 719 const auto it = page_table.find(cpu_addr >> YUZU_PAGEBITS);
720 if (it == page_table.end()) { 720 if (it == page_table.end()) {
721 return nullptr; 721 return {};
722 } 722 }
723 const auto& image_map_ids = it->second; 723 const auto& image_map_ids = it->second;
724 boost::container::small_vector<ImageId, 4> valid_image_ids; 724 boost::container::small_vector<ImageId, 4> valid_image_ids;
@@ -747,7 +747,8 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(
747 747
748 const auto GetImageViewForFramebuffer = [&](ImageId image_id) { 748 const auto GetImageViewForFramebuffer = [&](ImageId image_id) {
749 const ImageViewInfo info{ImageViewType::e2D, view_format}; 749 const ImageViewInfo info{ImageViewType::e2D, view_format};
750 return &slot_image_views[FindOrEmplaceImageView(image_id, info)]; 750 return std::make_pair(&slot_image_views[FindOrEmplaceImageView(image_id, info)],
751 slot_images[image_id].IsRescaled());
751 }; 752 };
752 753
753 if (valid_image_ids.size() == 1) [[likely]] { 754 if (valid_image_ids.size() == 1) [[likely]] {
@@ -761,7 +762,7 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView(
761 return GetImageViewForFramebuffer(*most_recent); 762 return GetImageViewForFramebuffer(*most_recent);
762 } 763 }
763 764
764 return nullptr; 765 return {};
765} 766}
766 767
767template <class P> 768template <class P>
@@ -1431,7 +1432,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA
1431 } 1432 }
1432 } 1433 }
1433 }; 1434 };
1434 ForEachSparseImageInRegion(gpu_addr, size_bytes, region_check_gpu); 1435 ForEachSparseImageInRegion(channel_state->gpu_memory.GetID(), gpu_addr, size_bytes,
1436 region_check_gpu);
1435 1437
1436 bool can_rescale = info.rescaleable; 1438 bool can_rescale = info.rescaleable;
1437 bool any_rescaled = false; 1439 bool any_rescaled = false;
@@ -1842,7 +1844,7 @@ void TextureCache<P>::ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, s
1842 if (!storage_id) { 1844 if (!storage_id) {
1843 return; 1845 return;
1844 } 1846 }
1845 auto& gpu_page_table = gpu_page_table_storage[*storage_id]; 1847 auto& gpu_page_table = gpu_page_table_storage[*storage_id * 2];
1846 ForEachGPUPage(gpu_addr, size, 1848 ForEachGPUPage(gpu_addr, size,
1847 [this, &gpu_page_table, &images, gpu_addr, size, func](u64 page) { 1849 [this, &gpu_page_table, &images, gpu_addr, size, func](u64 page) {
1848 const auto it = gpu_page_table.find(page); 1850 const auto it = gpu_page_table.find(page);
@@ -1882,41 +1884,48 @@ void TextureCache<P>::ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, s
1882 1884
1883template <class P> 1885template <class P>
1884template <typename Func> 1886template <typename Func>
1885void TextureCache<P>::ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func) { 1887void TextureCache<P>::ForEachSparseImageInRegion(size_t as_id, GPUVAddr gpu_addr, size_t size,
1888 Func&& func) {
1886 using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type; 1889 using FuncReturn = typename std::invoke_result<Func, ImageId, Image&>::type;
1887 static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>; 1890 static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
1888 boost::container::small_vector<ImageId, 8> images; 1891 boost::container::small_vector<ImageId, 8> images;
1889 ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) { 1892 auto storage_id = getStorageID(as_id);
1890 const auto it = sparse_page_table.find(page); 1893 if (!storage_id) {
1891 if (it == sparse_page_table.end()) { 1894 return;
1892 if constexpr (BOOL_BREAK) { 1895 }
1893 return false; 1896 auto& sparse_page_table = gpu_page_table_storage[*storage_id * 2 + 1];
1894 } else { 1897 ForEachGPUPage(gpu_addr, size,
1895 return; 1898 [this, &sparse_page_table, &images, gpu_addr, size, func](u64 page) {
1896 } 1899 const auto it = sparse_page_table.find(page);
1897 } 1900 if (it == sparse_page_table.end()) {
1898 for (const ImageId image_id : it->second) { 1901 if constexpr (BOOL_BREAK) {
1899 Image& image = slot_images[image_id]; 1902 return false;
1900 if (True(image.flags & ImageFlagBits::Picked)) { 1903 } else {
1901 continue; 1904 return;
1902 } 1905 }
1903 if (!image.OverlapsGPU(gpu_addr, size)) { 1906 }
1904 continue; 1907 for (const ImageId image_id : it->second) {
1905 } 1908 Image& image = slot_images[image_id];
1906 image.flags |= ImageFlagBits::Picked; 1909 if (True(image.flags & ImageFlagBits::Picked)) {
1907 images.push_back(image_id); 1910 continue;
1908 if constexpr (BOOL_BREAK) { 1911 }
1909 if (func(image_id, image)) { 1912 if (!image.OverlapsGPU(gpu_addr, size)) {
1910 return true; 1913 continue;
1911 } 1914 }
1912 } else { 1915 image.flags |= ImageFlagBits::Picked;
1913 func(image_id, image); 1916 images.push_back(image_id);
1914 } 1917 if constexpr (BOOL_BREAK) {
1915 } 1918 if (func(image_id, image)) {
1916 if constexpr (BOOL_BREAK) { 1919 return true;
1917 return false; 1920 }
1918 } 1921 } else {
1919 }); 1922 func(image_id, image);
1923 }
1924 }
1925 if constexpr (BOOL_BREAK) {
1926 return false;
1927 }
1928 });
1920 for (const ImageId image_id : images) { 1929 for (const ImageId image_id : images) {
1921 slot_images[image_id].flags &= ~ImageFlagBits::Picked; 1930 slot_images[image_id].flags &= ~ImageFlagBits::Picked;
1922 } 1931 }
@@ -1988,8 +1997,9 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
1988 sparse_maps.push_back(map_id); 1997 sparse_maps.push_back(map_id);
1989 }); 1998 });
1990 sparse_views.emplace(image_id, std::move(sparse_maps)); 1999 sparse_views.emplace(image_id, std::move(sparse_maps));
1991 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, 2000 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, image_id](u64 page) {
1992 [this, image_id](u64 page) { sparse_page_table[page].push_back(image_id); }); 2001 (*channel_state->sparse_page_table)[page].push_back(image_id);
2002 });
1993} 2003}
1994 2004
1995template <class P> 2005template <class P>
@@ -2042,7 +2052,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
2042 return; 2052 return;
2043 } 2053 }
2044 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, &clear_page_table](u64 page) { 2054 ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, &clear_page_table](u64 page) {
2045 clear_page_table(page, sparse_page_table); 2055 clear_page_table(page, (*channel_state->sparse_page_table));
2046 }); 2056 });
2047 auto it = sparse_views.find(image_id); 2057 auto it = sparse_views.find(image_id);
2048 ASSERT(it != sparse_views.end()); 2058 ASSERT(it != sparse_views.end());
@@ -2496,13 +2506,15 @@ void TextureCache<P>::CreateChannel(struct Tegra::Control::ChannelState& channel
2496 const auto it = channel_map.find(channel.bind_id); 2506 const auto it = channel_map.find(channel.bind_id);
2497 auto* this_state = &channel_storage[it->second]; 2507 auto* this_state = &channel_storage[it->second];
2498 const auto& this_as_ref = address_spaces[channel.memory_manager->GetID()]; 2508 const auto& this_as_ref = address_spaces[channel.memory_manager->GetID()];
2499 this_state->gpu_page_table = &gpu_page_table_storage[this_as_ref.storage_id]; 2509 this_state->gpu_page_table = &gpu_page_table_storage[this_as_ref.storage_id * 2];
2510 this_state->sparse_page_table = &gpu_page_table_storage[this_as_ref.storage_id * 2 + 1];
2500} 2511}
2501 2512
2502/// Bind a channel for execution. 2513/// Bind a channel for execution.
2503template <class P> 2514template <class P>
2504void TextureCache<P>::OnGPUASRegister([[maybe_unused]] size_t map_id) { 2515void TextureCache<P>::OnGPUASRegister([[maybe_unused]] size_t map_id) {
2505 gpu_page_table_storage.emplace_back(); 2516 gpu_page_table_storage.emplace_back();
2517 gpu_page_table_storage.emplace_back();
2506} 2518}
2507 2519
2508} // namespace VideoCommon 2520} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 8699d40d4..e7b910121 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -86,6 +86,7 @@ public:
86 std::unordered_map<TSCEntry, SamplerId> samplers; 86 std::unordered_map<TSCEntry, SamplerId> samplers;
87 87
88 TextureCacheGPUMap* gpu_page_table; 88 TextureCacheGPUMap* gpu_page_table;
89 TextureCacheGPUMap* sparse_page_table;
89}; 90};
90 91
91template <class P> 92template <class P>
@@ -211,8 +212,8 @@ public:
211 const Tegra::Engines::Fermi2D::Config& copy); 212 const Tegra::Engines::Fermi2D::Config& copy);
212 213
213 /// Try to find a cached image view in the given CPU address 214 /// Try to find a cached image view in the given CPU address
214 [[nodiscard]] ImageView* TryFindFramebufferImageView(const Tegra::FramebufferConfig& config, 215 [[nodiscard]] std::pair<ImageView*, bool> TryFindFramebufferImageView(
215 DAddr cpu_addr); 216 const Tegra::FramebufferConfig& config, DAddr cpu_addr);
216 217
217 /// Return true when there are uncommitted images to be downloaded 218 /// Return true when there are uncommitted images to be downloaded
218 [[nodiscard]] bool HasUncommittedFlushes() const noexcept; 219 [[nodiscard]] bool HasUncommittedFlushes() const noexcept;
@@ -357,7 +358,7 @@ private:
357 void ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, size_t size, Func&& func); 358 void ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, size_t size, Func&& func);
358 359
359 template <typename Func> 360 template <typename Func>
360 void ForEachSparseImageInRegion(GPUVAddr gpu_addr, size_t size, Func&& func); 361 void ForEachSparseImageInRegion(size_t as_id, GPUVAddr gpu_addr, size_t size, Func&& func);
361 362
362 /// Iterates over all the images in a region calling func 363 /// Iterates over all the images in a region calling func
363 template <typename Func> 364 template <typename Func>
@@ -431,7 +432,6 @@ private:
431 std::unordered_map<RenderTargets, FramebufferId> framebuffers; 432 std::unordered_map<RenderTargets, FramebufferId> framebuffers;
432 433
433 std::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table; 434 std::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table;
434 std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>> sparse_page_table;
435 std::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views; 435 std::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views;
436 436
437 DAddr virtual_invalid_space{}; 437 DAddr virtual_invalid_space{};
diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp
index 79162a491..66edd6acd 100644
--- a/src/yuzu/applets/qt_profile_select.cpp
+++ b/src/yuzu/applets/qt_profile_select.cpp
@@ -162,7 +162,7 @@ void QtProfileSelectionDialog::SelectUser(const QModelIndex& index) {
162 162
163void QtProfileSelectionDialog::SetWindowTitle( 163void QtProfileSelectionDialog::SetWindowTitle(
164 const Core::Frontend::ProfileSelectParameters& parameters) { 164 const Core::Frontend::ProfileSelectParameters& parameters) {
165 using Service::AM::Applets::UiMode; 165 using Service::AM::Frontend::UiMode;
166 switch (parameters.mode) { 166 switch (parameters.mode) {
167 case UiMode::UserCreator: 167 case UiMode::UserCreator:
168 case UiMode::UserCreatorForStarter: 168 case UiMode::UserCreatorForStarter:
@@ -193,7 +193,7 @@ void QtProfileSelectionDialog::SetWindowTitle(
193 193
194void QtProfileSelectionDialog::SetDialogPurpose( 194void QtProfileSelectionDialog::SetDialogPurpose(
195 const Core::Frontend::ProfileSelectParameters& parameters) { 195 const Core::Frontend::ProfileSelectParameters& parameters) {
196 using Service::AM::Applets::UserSelectionPurpose; 196 using Service::AM::Frontend::UserSelectionPurpose;
197 197
198 switch (parameters.purpose) { 198 switch (parameters.purpose) {
199 case UserSelectionPurpose::GameCardRegistration: 199 case UserSelectionPurpose::GameCardRegistration:
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index ac81ace9e..2749e6ed3 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -20,7 +20,7 @@
20 20
21namespace { 21namespace {
22 22
23using namespace Service::AM::Applets; 23using namespace Service::AM::Frontend;
24 24
25constexpr float BASE_HEADER_FONT_SIZE = 23.0f; 25constexpr float BASE_HEADER_FONT_SIZE = 23.0f;
26constexpr float BASE_SUB_FONT_SIZE = 17.0f; 26constexpr float BASE_SUB_FONT_SIZE = 17.0f;
@@ -389,7 +389,7 @@ void QtSoftwareKeyboardDialog::ShowNormalKeyboard(QPoint pos, QSize size) {
389} 389}
390 390
391void QtSoftwareKeyboardDialog::ShowTextCheckDialog( 391void QtSoftwareKeyboardDialog::ShowTextCheckDialog(
392 Service::AM::Applets::SwkbdTextCheckResult text_check_result, 392 Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
393 std::u16string text_check_message) { 393 std::u16string text_check_message) {
394 switch (text_check_result) { 394 switch (text_check_result) {
395 case SwkbdTextCheckResult::Success: 395 case SwkbdTextCheckResult::Success:
@@ -1612,7 +1612,7 @@ void QtSoftwareKeyboard::ShowNormalKeyboard() const {
1612} 1612}
1613 1613
1614void QtSoftwareKeyboard::ShowTextCheckDialog( 1614void QtSoftwareKeyboard::ShowTextCheckDialog(
1615 Service::AM::Applets::SwkbdTextCheckResult text_check_result, 1615 Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
1616 std::u16string text_check_message) const { 1616 std::u16string text_check_message) const {
1617 emit MainWindowShowTextCheckDialog(text_check_result, std::move(text_check_message)); 1617 emit MainWindowShowTextCheckDialog(text_check_result, std::move(text_check_message));
1618} 1618}
@@ -1662,12 +1662,12 @@ void QtSoftwareKeyboard::ExitKeyboard() const {
1662 emit MainWindowExitKeyboard(); 1662 emit MainWindowExitKeyboard();
1663} 1663}
1664 1664
1665void QtSoftwareKeyboard::SubmitNormalText(Service::AM::Applets::SwkbdResult result, 1665void QtSoftwareKeyboard::SubmitNormalText(Service::AM::Frontend::SwkbdResult result,
1666 std::u16string submitted_text, bool confirmed) const { 1666 std::u16string submitted_text, bool confirmed) const {
1667 submit_normal_callback(result, submitted_text, confirmed); 1667 submit_normal_callback(result, submitted_text, confirmed);
1668} 1668}
1669 1669
1670void QtSoftwareKeyboard::SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, 1670void QtSoftwareKeyboard::SubmitInlineText(Service::AM::Frontend::SwkbdReplyType reply_type,
1671 std::u16string submitted_text, 1671 std::u16string submitted_text,
1672 s32 cursor_position) const { 1672 s32 cursor_position) const {
1673 submit_inline_callback(reply_type, submitted_text, cursor_position); 1673 submit_inline_callback(reply_type, submitted_text, cursor_position);
diff --git a/src/yuzu/applets/qt_software_keyboard.h b/src/yuzu/applets/qt_software_keyboard.h
index ac23ce047..7e2fdf09e 100644
--- a/src/yuzu/applets/qt_software_keyboard.h
+++ b/src/yuzu/applets/qt_software_keyboard.h
@@ -39,7 +39,7 @@ public:
39 39
40 void ShowNormalKeyboard(QPoint pos, QSize size); 40 void ShowNormalKeyboard(QPoint pos, QSize size);
41 41
42 void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, 42 void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
43 std::u16string text_check_message); 43 std::u16string text_check_message);
44 44
45 void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters, QPoint pos, 45 void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters, QPoint pos,
@@ -52,10 +52,10 @@ public:
52 void ExitKeyboard(); 52 void ExitKeyboard();
53 53
54signals: 54signals:
55 void SubmitNormalText(Service::AM::Applets::SwkbdResult result, std::u16string submitted_text, 55 void SubmitNormalText(Service::AM::Frontend::SwkbdResult result, std::u16string submitted_text,
56 bool confirmed = false) const; 56 bool confirmed = false) const;
57 57
58 void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, 58 void SubmitInlineText(Service::AM::Frontend::SwkbdReplyType reply_type,
59 std::u16string submitted_text, s32 cursor_position) const; 59 std::u16string submitted_text, s32 cursor_position) const;
60 60
61public slots: 61public slots:
@@ -244,7 +244,7 @@ public:
244 244
245 void ShowNormalKeyboard() const override; 245 void ShowNormalKeyboard() const override;
246 246
247 void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, 247 void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
248 std::u16string text_check_message) const override; 248 std::u16string text_check_message) const override;
249 249
250 void ShowInlineKeyboard( 250 void ShowInlineKeyboard(
@@ -262,8 +262,9 @@ signals:
262 262
263 void MainWindowShowNormalKeyboard() const; 263 void MainWindowShowNormalKeyboard() const;
264 264
265 void MainWindowShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, 265 void MainWindowShowTextCheckDialog(
266 std::u16string text_check_message) const; 266 Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
267 std::u16string text_check_message) const;
267 268
268 void MainWindowShowInlineKeyboard( 269 void MainWindowShowInlineKeyboard(
269 Core::Frontend::InlineAppearParameters appear_parameters) const; 270 Core::Frontend::InlineAppearParameters appear_parameters) const;
@@ -275,10 +276,10 @@ signals:
275 void MainWindowExitKeyboard() const; 276 void MainWindowExitKeyboard() const;
276 277
277private: 278private:
278 void SubmitNormalText(Service::AM::Applets::SwkbdResult result, std::u16string submitted_text, 279 void SubmitNormalText(Service::AM::Frontend::SwkbdResult result, std::u16string submitted_text,
279 bool confirmed) const; 280 bool confirmed) const;
280 281
281 void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, 282 void SubmitInlineText(Service::AM::Frontend::SwkbdReplyType reply_type,
282 std::u16string submitted_text, s32 cursor_position) const; 283 std::u16string submitted_text, s32 cursor_position) const;
283 284
284 mutable SubmitNormalCallback submit_normal_callback; 285 mutable SubmitNormalCallback submit_normal_callback;
diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp
index 34c5fd3be..cce9b2efb 100644
--- a/src/yuzu/applets/qt_web_browser.cpp
+++ b/src/yuzu/applets/qt_web_browser.cpp
@@ -96,7 +96,7 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system,
96 [this] { 96 [this] {
97 if (page()->url() == url_interceptor->GetRequestedURL()) { 97 if (page()->url() == url_interceptor->GetRequestedURL()) {
98 SetFinished(true); 98 SetFinished(true);
99 SetExitReason(Service::AM::Applets::WebExitReason::WindowClosed); 99 SetExitReason(Service::AM::Frontend::WebExitReason::WindowClosed);
100 } 100 }
101 }, 101 },
102 Qt::QueuedConnection); 102 Qt::QueuedConnection);
@@ -115,7 +115,7 @@ void QtNXWebEngineView::LoadLocalWebPage(const std::string& main_url,
115 FocusFirstLinkElement(); 115 FocusFirstLinkElement();
116 SetUserAgent(UserAgent::WebApplet); 116 SetUserAgent(UserAgent::WebApplet);
117 SetFinished(false); 117 SetFinished(false);
118 SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed); 118 SetExitReason(Service::AM::Frontend::WebExitReason::EndButtonPressed);
119 SetLastURL("http://localhost/"); 119 SetLastURL("http://localhost/");
120 StartInputThread(); 120 StartInputThread();
121 121
@@ -130,7 +130,7 @@ void QtNXWebEngineView::LoadExternalWebPage(const std::string& main_url,
130 FocusFirstLinkElement(); 130 FocusFirstLinkElement();
131 SetUserAgent(UserAgent::WebApplet); 131 SetUserAgent(UserAgent::WebApplet);
132 SetFinished(false); 132 SetFinished(false);
133 SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed); 133 SetExitReason(Service::AM::Frontend::WebExitReason::EndButtonPressed);
134 SetLastURL("http://localhost/"); 134 SetLastURL("http://localhost/");
135 StartInputThread(); 135 StartInputThread();
136 136
@@ -170,11 +170,11 @@ void QtNXWebEngineView::SetFinished(bool finished_) {
170 finished = finished_; 170 finished = finished_;
171} 171}
172 172
173Service::AM::Applets::WebExitReason QtNXWebEngineView::GetExitReason() const { 173Service::AM::Frontend::WebExitReason QtNXWebEngineView::GetExitReason() const {
174 return exit_reason; 174 return exit_reason;
175} 175}
176 176
177void QtNXWebEngineView::SetExitReason(Service::AM::Applets::WebExitReason exit_reason_) { 177void QtNXWebEngineView::SetExitReason(Service::AM::Frontend::WebExitReason exit_reason_) {
178 exit_reason = exit_reason_; 178 exit_reason = exit_reason_;
179} 179}
180 180
@@ -441,7 +441,7 @@ void QtWebBrowser::MainWindowExtractOfflineRomFS() {
441 extract_romfs_callback(); 441 extract_romfs_callback();
442} 442}
443 443
444void QtWebBrowser::MainWindowWebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, 444void QtWebBrowser::MainWindowWebBrowserClosed(Service::AM::Frontend::WebExitReason exit_reason,
445 std::string last_url) { 445 std::string last_url) {
446 if (callback) { 446 if (callback) {
447 callback(exit_reason, last_url); 447 callback(exit_reason, last_url);
diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h
index 1234108ae..e8a0b6931 100644
--- a/src/yuzu/applets/qt_web_browser.h
+++ b/src/yuzu/applets/qt_web_browser.h
@@ -85,8 +85,8 @@ public:
85 [[nodiscard]] bool IsFinished() const; 85 [[nodiscard]] bool IsFinished() const;
86 void SetFinished(bool finished_); 86 void SetFinished(bool finished_);
87 87
88 [[nodiscard]] Service::AM::Applets::WebExitReason GetExitReason() const; 88 [[nodiscard]] Service::AM::Frontend::WebExitReason GetExitReason() const;
89 void SetExitReason(Service::AM::Applets::WebExitReason exit_reason_); 89 void SetExitReason(Service::AM::Frontend::WebExitReason exit_reason_);
90 90
91 [[nodiscard]] const std::string& GetLastURL() const; 91 [[nodiscard]] const std::string& GetLastURL() const;
92 void SetLastURL(std::string last_url_); 92 void SetLastURL(std::string last_url_);
@@ -176,8 +176,8 @@ private:
176 176
177 std::atomic<bool> finished{}; 177 std::atomic<bool> finished{};
178 178
179 Service::AM::Applets::WebExitReason exit_reason{ 179 Service::AM::Frontend::WebExitReason exit_reason{
180 Service::AM::Applets::WebExitReason::EndButtonPressed}; 180 Service::AM::Frontend::WebExitReason::EndButtonPressed};
181 181
182 std::string last_url{"http://localhost/"}; 182 std::string last_url{"http://localhost/"};
183 183
@@ -212,7 +212,7 @@ signals:
212private: 212private:
213 void MainWindowExtractOfflineRomFS(); 213 void MainWindowExtractOfflineRomFS();
214 214
215 void MainWindowWebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, 215 void MainWindowWebBrowserClosed(Service::AM::Frontend::WebExitReason exit_reason,
216 std::string last_url); 216 std::string last_url);
217 217
218 mutable ExtractROMFSCallback extract_romfs_callback; 218 mutable ExtractROMFSCallback extract_romfs_callback;
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index 9b6ef47a7..c235b0fca 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -5,6 +5,7 @@
5#include <memory> 5#include <memory>
6#include <vector> 6#include <vector>
7#include <QComboBox> 7#include <QComboBox>
8#include <QPushButton>
8 9
9#include "audio_core/sink/sink.h" 10#include "audio_core/sink/sink.h"
10#include "audio_core/sink/sink_details.h" 11#include "audio_core/sink/sink_details.h"
@@ -67,19 +68,99 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) {
67 68
68 hold.emplace(std::pair{setting->Id(), widget}); 69 hold.emplace(std::pair{setting->Id(), widget});
69 70
71 auto global_sink_match = [this] {
72 return static_cast<Settings::AudioEngine>(sink_combo_box->currentIndex()) ==
73 Settings::values.sink_id.GetValue(true);
74 };
70 if (setting->Id() == Settings::values.sink_id.Id()) { 75 if (setting->Id() == Settings::values.sink_id.Id()) {
71 // TODO (lat9nq): Let the system manage sink_id 76 // TODO (lat9nq): Let the system manage sink_id
72 sink_combo_box = widget->combobox; 77 sink_combo_box = widget->combobox;
73 InitializeAudioSinkComboBox(); 78 InitializeAudioSinkComboBox();
74 79
75 connect(sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this, 80 if (Settings::IsConfiguringGlobal()) {
76 &ConfigureAudio::UpdateAudioDevices); 81 connect(sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
82 &ConfigureAudio::UpdateAudioDevices);
83 } else {
84 restore_sink_button = ConfigurationShared::Widget::CreateRestoreGlobalButton(
85 Settings::values.sink_id.UsingGlobal(), widget);
86 widget->layout()->addWidget(restore_sink_button);
87 connect(restore_sink_button, &QAbstractButton::clicked, [this](bool) {
88 Settings::values.sink_id.SetGlobal(true);
89 const int sink_index = static_cast<int>(Settings::values.sink_id.GetValue());
90 sink_combo_box->setCurrentIndex(sink_index);
91 ConfigureAudio::UpdateAudioDevices(sink_index);
92 Settings::values.audio_output_device_id.SetGlobal(true);
93 Settings::values.audio_input_device_id.SetGlobal(true);
94 restore_sink_button->setVisible(false);
95 });
96 connect(sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged),
97 [this, global_sink_match](const int slot) {
98 Settings::values.sink_id.SetGlobal(false);
99 Settings::values.audio_output_device_id.SetGlobal(false);
100 Settings::values.audio_input_device_id.SetGlobal(false);
101
102 restore_sink_button->setVisible(true);
103 restore_sink_button->setEnabled(true);
104 output_device_combo_box->setCurrentIndex(0);
105 restore_output_device_button->setVisible(true);
106 restore_output_device_button->setEnabled(global_sink_match());
107 input_device_combo_box->setCurrentIndex(0);
108 restore_input_device_button->setVisible(true);
109 restore_input_device_button->setEnabled(global_sink_match());
110 ConfigureAudio::UpdateAudioDevices(slot);
111 });
112 }
77 } else if (setting->Id() == Settings::values.audio_output_device_id.Id()) { 113 } else if (setting->Id() == Settings::values.audio_output_device_id.Id()) {
78 // Keep track of output (and input) device comboboxes to populate them with system 114 // Keep track of output (and input) device comboboxes to populate them with system
79 // devices, which are determined at run time 115 // devices, which are determined at run time
80 output_device_combo_box = widget->combobox; 116 output_device_combo_box = widget->combobox;
117
118 if (!Settings::IsConfiguringGlobal()) {
119 restore_output_device_button =
120 ConfigurationShared::Widget::CreateRestoreGlobalButton(
121 Settings::values.audio_output_device_id.UsingGlobal(), widget);
122 restore_output_device_button->setEnabled(global_sink_match());
123 restore_output_device_button->setVisible(
124 !Settings::values.audio_output_device_id.UsingGlobal());
125 widget->layout()->addWidget(restore_output_device_button);
126 connect(restore_output_device_button, &QAbstractButton::clicked, [this](bool) {
127 Settings::values.audio_output_device_id.SetGlobal(true);
128 SetOutputDevicesFromDeviceID();
129 restore_output_device_button->setVisible(false);
130 });
131 connect(output_device_combo_box, qOverload<int>(&QComboBox::currentIndexChanged),
132 [this, global_sink_match](int) {
133 if (updating_devices) {
134 return;
135 }
136 Settings::values.audio_output_device_id.SetGlobal(false);
137 restore_output_device_button->setVisible(true);
138 restore_output_device_button->setEnabled(global_sink_match());
139 });
140 }
81 } else if (setting->Id() == Settings::values.audio_input_device_id.Id()) { 141 } else if (setting->Id() == Settings::values.audio_input_device_id.Id()) {
82 input_device_combo_box = widget->combobox; 142 input_device_combo_box = widget->combobox;
143
144 if (!Settings::IsConfiguringGlobal()) {
145 restore_input_device_button =
146 ConfigurationShared::Widget::CreateRestoreGlobalButton(
147 Settings::values.audio_input_device_id.UsingGlobal(), widget);
148 widget->layout()->addWidget(restore_input_device_button);
149 connect(restore_input_device_button, &QAbstractButton::clicked, [this](bool) {
150 Settings::values.audio_input_device_id.SetGlobal(true);
151 SetInputDevicesFromDeviceID();
152 restore_input_device_button->setVisible(false);
153 });
154 connect(input_device_combo_box, qOverload<int>(&QComboBox::currentIndexChanged),
155 [this, global_sink_match](int) {
156 if (updating_devices) {
157 return;
158 }
159 Settings::values.audio_input_device_id.SetGlobal(false);
160 restore_input_device_button->setVisible(true);
161 restore_input_device_button->setEnabled(global_sink_match());
162 });
163 }
83 } 164 }
84 } 165 }
85 166
@@ -89,16 +170,13 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) {
89} 170}
90 171
91void ConfigureAudio::SetConfiguration() { 172void ConfigureAudio::SetConfiguration() {
92 if (!Settings::IsConfiguringGlobal()) {
93 return;
94 }
95
96 SetOutputSinkFromSinkID(); 173 SetOutputSinkFromSinkID();
97 174
98 // The device list cannot be pre-populated (nor listed) until the output sink is known. 175 // The device list cannot be pre-populated (nor listed) until the output sink is known.
99 UpdateAudioDevices(sink_combo_box->currentIndex()); 176 UpdateAudioDevices(sink_combo_box->currentIndex());
100 177
101 SetAudioDevicesFromDeviceID(); 178 SetOutputDevicesFromDeviceID();
179 SetInputDevicesFromDeviceID();
102} 180}
103 181
104void ConfigureAudio::SetOutputSinkFromSinkID() { 182void ConfigureAudio::SetOutputSinkFromSinkID() {
@@ -116,8 +194,8 @@ void ConfigureAudio::SetOutputSinkFromSinkID() {
116 sink_combo_box->setCurrentIndex(new_sink_index); 194 sink_combo_box->setCurrentIndex(new_sink_index);
117} 195}
118 196
119void ConfigureAudio::SetAudioDevicesFromDeviceID() { 197void ConfigureAudio::SetOutputDevicesFromDeviceID() {
120 int new_device_index = -1; 198 int new_device_index = 0;
121 199
122 const QString output_device_id = 200 const QString output_device_id =
123 QString::fromStdString(Settings::values.audio_output_device_id.GetValue()); 201 QString::fromStdString(Settings::values.audio_output_device_id.GetValue());
@@ -129,8 +207,10 @@ void ConfigureAudio::SetAudioDevicesFromDeviceID() {
129 } 207 }
130 208
131 output_device_combo_box->setCurrentIndex(new_device_index); 209 output_device_combo_box->setCurrentIndex(new_device_index);
210}
132 211
133 new_device_index = -1; 212void ConfigureAudio::SetInputDevicesFromDeviceID() {
213 int new_device_index = 0;
134 const QString input_device_id = 214 const QString input_device_id =
135 QString::fromStdString(Settings::values.audio_input_device_id.GetValue()); 215 QString::fromStdString(Settings::values.audio_input_device_id.GetValue());
136 for (int index = 0; index < input_device_combo_box->count(); index++) { 216 for (int index = 0; index < input_device_combo_box->count(); index++) {
@@ -149,15 +229,12 @@ void ConfigureAudio::ApplyConfiguration() {
149 apply_func(is_powered_on); 229 apply_func(is_powered_on);
150 } 230 }
151 231
152 if (Settings::IsConfiguringGlobal()) { 232 Settings::values.sink_id.LoadString(
153 Settings::values.sink_id.LoadString( 233 sink_combo_box->itemText(sink_combo_box->currentIndex()).toStdString());
154 sink_combo_box->itemText(sink_combo_box->currentIndex()).toStdString()); 234 Settings::values.audio_output_device_id.SetValue(
155 Settings::values.audio_output_device_id.SetValue( 235 output_device_combo_box->itemText(output_device_combo_box->currentIndex()).toStdString());
156 output_device_combo_box->itemText(output_device_combo_box->currentIndex()) 236 Settings::values.audio_input_device_id.SetValue(
157 .toStdString()); 237 input_device_combo_box->itemText(input_device_combo_box->currentIndex()).toStdString());
158 Settings::values.audio_input_device_id.SetValue(
159 input_device_combo_box->itemText(input_device_combo_box->currentIndex()).toStdString());
160 }
161} 238}
162 239
163void ConfigureAudio::changeEvent(QEvent* event) { 240void ConfigureAudio::changeEvent(QEvent* event) {
@@ -169,6 +246,7 @@ void ConfigureAudio::changeEvent(QEvent* event) {
169} 246}
170 247
171void ConfigureAudio::UpdateAudioDevices(int sink_index) { 248void ConfigureAudio::UpdateAudioDevices(int sink_index) {
249 updating_devices = true;
172 output_device_combo_box->clear(); 250 output_device_combo_box->clear();
173 output_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); 251 output_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
174 252
@@ -183,6 +261,7 @@ void ConfigureAudio::UpdateAudioDevices(int sink_index) {
183 for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, true)) { 261 for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, true)) {
184 input_device_combo_box->addItem(QString::fromStdString(device)); 262 input_device_combo_box->addItem(QString::fromStdString(device));
185 } 263 }
264 updating_devices = false;
186} 265}
187 266
188void ConfigureAudio::InitializeAudioSinkComboBox() { 267void ConfigureAudio::InitializeAudioSinkComboBox() {
diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h
index 82d7f6524..32a2fa5f0 100644
--- a/src/yuzu/configuration/configure_audio.h
+++ b/src/yuzu/configuration/configure_audio.h
@@ -45,7 +45,8 @@ private:
45 void UpdateAudioDevices(int sink_index); 45 void UpdateAudioDevices(int sink_index);
46 46
47 void SetOutputSinkFromSinkID(); 47 void SetOutputSinkFromSinkID();
48 void SetAudioDevicesFromDeviceID(); 48 void SetOutputDevicesFromDeviceID();
49 void SetInputDevicesFromDeviceID();
49 50
50 void Setup(const ConfigurationShared::Builder& builder); 51 void Setup(const ConfigurationShared::Builder& builder);
51 52
@@ -55,7 +56,11 @@ private:
55 56
56 std::vector<std::function<void(bool)>> apply_funcs{}; 57 std::vector<std::function<void(bool)>> apply_funcs{};
57 58
59 bool updating_devices = false;
58 QComboBox* sink_combo_box; 60 QComboBox* sink_combo_box;
61 QPushButton* restore_sink_button;
59 QComboBox* output_device_combo_box; 62 QComboBox* output_device_combo_box;
63 QPushButton* restore_output_device_button;
60 QComboBox* input_device_combo_box; 64 QComboBox* input_device_combo_box;
65 QPushButton* restore_input_device_button;
61}; 66};
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 49ec52546..e28df10bd 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -9,6 +9,8 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/service/am/am.h" 10#include "core/hle/service/am/am.h"
11#include "core/hle/service/am/applet_ae.h" 11#include "core/hle/service/am/applet_ae.h"
12#include "core/hle/service/am/applet_manager.h"
13#include "core/hle/service/am/applet_message_queue.h"
12#include "core/hle/service/am/applet_oe.h" 14#include "core/hle/service/am/applet_oe.h"
13#include "core/hle/service/sm/sm.h" 15#include "core/hle/service/sm/sm.h"
14#include "hid_core/frontend/emulated_controller.h" 16#include "hid_core/frontend/emulated_controller.h"
@@ -47,22 +49,8 @@ void OnDockedModeChanged(bool last_state, bool new_state, Core::System& system)
47 if (!system.IsPoweredOn()) { 49 if (!system.IsPoweredOn()) {
48 return; 50 return;
49 } 51 }
50 Service::SM::ServiceManager& sm = system.ServiceManager();
51 52
52 // Message queue is shared between these services, we just need to signal an operation 53 system.GetAppletManager().OperationModeChanged();
53 // change to one and it will handle both automatically
54 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
55 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
56 bool has_signalled = false;
57
58 if (applet_oe != nullptr) {
59 applet_oe->GetMessageQueue()->OperationModeChanged();
60 has_signalled = true;
61 }
62
63 if (applet_ae != nullptr && !has_signalled) {
64 applet_ae->GetMessageQueue()->OperationModeChanged();
65 }
66} 54}
67 55
68ConfigureInput::ConfigureInput(Core::System& system_, QWidget* parent) 56ConfigureInput::ConfigureInput(Core::System& system_, QWidget* parent)
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 59b317135..b40af957c 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -596,14 +596,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
596 connect(open_save_location, &QAction::triggered, [this, program_id, path]() { 596 connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
597 emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path); 597 emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
598 }); 598 });
599 connect(start_game, &QAction::triggered, [this, path]() { 599 connect(start_game, &QAction::triggered,
600 emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal, 600 [this, path]() { emit BootGame(QString::fromStdString(path), StartGameType::Normal); });
601 AmLaunchType::UserInitiated); 601 connect(start_game_global, &QAction::triggered,
602 }); 602 [this, path]() { emit BootGame(QString::fromStdString(path), StartGameType::Global); });
603 connect(start_game_global, &QAction::triggered, [this, path]() {
604 emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global,
605 AmLaunchType::UserInitiated);
606 });
607 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { 603 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
608 emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path); 604 emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path);
609 }); 605 });
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 563a3a35b..79f9c7ec0 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -106,8 +106,7 @@ public:
106 static const QStringList supported_file_extensions; 106 static const QStringList supported_file_extensions;
107 107
108signals: 108signals:
109 void BootGame(const QString& game_path, u64 program_id, std::size_t program_index, 109 void BootGame(const QString& game_path, StartGameType type);
110 StartGameType type, AmLaunchType launch_type);
111 void GameChosen(const QString& game_path, const u64 title_id = 0); 110 void GameChosen(const QString& game_path, const u64 title_id = 0);
112 void OpenFolderRequested(u64 program_id, GameListOpenTarget target, 111 void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
113 const std::string& game_path); 112 const std::string& game_path);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 782bcbb61..303d84a1f 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -8,6 +8,7 @@
8#include <iostream> 8#include <iostream>
9#include <memory> 9#include <memory>
10#include <thread> 10#include <thread>
11#include "core/hle/service/am/applet_manager.h"
11#include "core/loader/nca.h" 12#include "core/loader/nca.h"
12#include "core/tools/renderdoc.h" 13#include "core/tools/renderdoc.h"
13 14
@@ -39,13 +40,14 @@
39#include "core/file_sys/vfs/vfs_real.h" 40#include "core/file_sys/vfs/vfs_real.h"
40#include "core/frontend/applets/cabinet.h" 41#include "core/frontend/applets/cabinet.h"
41#include "core/frontend/applets/controller.h" 42#include "core/frontend/applets/controller.h"
42#include "core/frontend/applets/general_frontend.h" 43#include "core/frontend/applets/general.h"
43#include "core/frontend/applets/mii_edit.h" 44#include "core/frontend/applets/mii_edit.h"
44#include "core/frontend/applets/software_keyboard.h" 45#include "core/frontend/applets/software_keyboard.h"
45#include "core/hle/service/acc/profile_manager.h" 46#include "core/hle/service/acc/profile_manager.h"
46#include "core/hle/service/am/applet_ae.h" 47#include "core/hle/service/am/applet_ae.h"
48#include "core/hle/service/am/applet_message_queue.h"
47#include "core/hle/service/am/applet_oe.h" 49#include "core/hle/service/am/applet_oe.h"
48#include "core/hle/service/am/applets/applets.h" 50#include "core/hle/service/am/frontend/applets.h"
49#include "core/hle/service/set/system_settings_server.h" 51#include "core/hle/service/set/system_settings_server.h"
50#include "frontend_common/content_manager.h" 52#include "frontend_common/content_manager.h"
51#include "hid_core/frontend/emulated_controller.h" 53#include "hid_core/frontend/emulated_controller.h"
@@ -568,7 +570,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
568 } 570 }
569 571
570 if (!game_path.isEmpty()) { 572 if (!game_path.isEmpty()) {
571 BootGame(game_path); 573 BootGame(game_path, ApplicationAppletParameters());
572 } 574 }
573} 575}
574 576
@@ -630,13 +632,14 @@ void GMainWindow::RegisterMetaTypes() {
630 qRegisterMetaType<Core::Frontend::InlineAppearParameters>( 632 qRegisterMetaType<Core::Frontend::InlineAppearParameters>(
631 "Core::Frontend::InlineAppearParameters"); 633 "Core::Frontend::InlineAppearParameters");
632 qRegisterMetaType<Core::Frontend::InlineTextParameters>("Core::Frontend::InlineTextParameters"); 634 qRegisterMetaType<Core::Frontend::InlineTextParameters>("Core::Frontend::InlineTextParameters");
633 qRegisterMetaType<Service::AM::Applets::SwkbdResult>("Service::AM::Applets::SwkbdResult"); 635 qRegisterMetaType<Service::AM::Frontend::SwkbdResult>("Service::AM::Frontend::SwkbdResult");
634 qRegisterMetaType<Service::AM::Applets::SwkbdTextCheckResult>( 636 qRegisterMetaType<Service::AM::Frontend::SwkbdTextCheckResult>(
635 "Service::AM::Applets::SwkbdTextCheckResult"); 637 "Service::AM::Frontend::SwkbdTextCheckResult");
636 qRegisterMetaType<Service::AM::Applets::SwkbdReplyType>("Service::AM::Applets::SwkbdReplyType"); 638 qRegisterMetaType<Service::AM::Frontend::SwkbdReplyType>(
639 "Service::AM::Frontend::SwkbdReplyType");
637 640
638 // Web Browser Applet 641 // Web Browser Applet
639 qRegisterMetaType<Service::AM::Applets::WebExitReason>("Service::AM::Applets::WebExitReason"); 642 qRegisterMetaType<Service::AM::Frontend::WebExitReason>("Service::AM::Frontend::WebExitReason");
640 643
641 // Register loader types 644 // Register loader types
642 qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus"); 645 qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus");
@@ -746,7 +749,7 @@ void GMainWindow::SoftwareKeyboardInitialize(
746 if (is_inline) { 749 if (is_inline) {
747 connect( 750 connect(
748 software_keyboard, &QtSoftwareKeyboardDialog::SubmitInlineText, this, 751 software_keyboard, &QtSoftwareKeyboardDialog::SubmitInlineText, this,
749 [this](Service::AM::Applets::SwkbdReplyType reply_type, std::u16string submitted_text, 752 [this](Service::AM::Frontend::SwkbdReplyType reply_type, std::u16string submitted_text,
750 s32 cursor_position) { 753 s32 cursor_position) {
751 emit SoftwareKeyboardSubmitInlineText(reply_type, submitted_text, cursor_position); 754 emit SoftwareKeyboardSubmitInlineText(reply_type, submitted_text, cursor_position);
752 }, 755 },
@@ -754,7 +757,7 @@ void GMainWindow::SoftwareKeyboardInitialize(
754 } else { 757 } else {
755 connect( 758 connect(
756 software_keyboard, &QtSoftwareKeyboardDialog::SubmitNormalText, this, 759 software_keyboard, &QtSoftwareKeyboardDialog::SubmitNormalText, this,
757 [this](Service::AM::Applets::SwkbdResult result, std::u16string submitted_text, 760 [this](Service::AM::Frontend::SwkbdResult result, std::u16string submitted_text,
758 bool confirmed) { 761 bool confirmed) {
759 emit SoftwareKeyboardSubmitNormalText(result, submitted_text, confirmed); 762 emit SoftwareKeyboardSubmitNormalText(result, submitted_text, confirmed);
760 }, 763 },
@@ -781,7 +784,7 @@ void GMainWindow::SoftwareKeyboardShowNormal() {
781} 784}
782 785
783void GMainWindow::SoftwareKeyboardShowTextCheck( 786void GMainWindow::SoftwareKeyboardShowTextCheck(
784 Service::AM::Applets::SwkbdTextCheckResult text_check_result, 787 Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
785 std::u16string text_check_message) { 788 std::u16string text_check_message) {
786 if (!software_keyboard) { 789 if (!software_keyboard) {
787 LOG_ERROR(Frontend, "The software keyboard is not initialized!"); 790 LOG_ERROR(Frontend, "The software keyboard is not initialized!");
@@ -852,7 +855,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
852 855
853 // Raw input breaks with the web applet, Disable web applets if enabled 856 // Raw input breaks with the web applet, Disable web applets if enabled
854 if (UISettings::values.disable_web_applet || Settings::values.enable_raw_input) { 857 if (UISettings::values.disable_web_applet || Settings::values.enable_raw_input) {
855 emit WebBrowserClosed(Service::AM::Applets::WebExitReason::WindowClosed, 858 emit WebBrowserClosed(Service::AM::Frontend::WebExitReason::WindowClosed,
856 "http://localhost/"); 859 "http://localhost/");
857 return; 860 return;
858 } 861 }
@@ -940,7 +943,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
940 if (variant.toBool()) { 943 if (variant.toBool()) {
941 web_applet->SetFinished(true); 944 web_applet->SetFinished(true);
942 web_applet->SetExitReason( 945 web_applet->SetExitReason(
943 Service::AM::Applets::WebExitReason::EndButtonPressed); 946 Service::AM::Frontend::WebExitReason::EndButtonPressed);
944 } 947 }
945 }); 948 });
946 949
@@ -950,7 +953,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
950 if (web_applet->GetCurrentURL().contains(QStringLiteral("localhost"))) { 953 if (web_applet->GetCurrentURL().contains(QStringLiteral("localhost"))) {
951 if (!web_applet->IsFinished()) { 954 if (!web_applet->IsFinished()) {
952 web_applet->SetFinished(true); 955 web_applet->SetFinished(true);
953 web_applet->SetExitReason(Service::AM::Applets::WebExitReason::CallbackURL); 956 web_applet->SetExitReason(Service::AM::Frontend::WebExitReason::CallbackURL);
954 } 957 }
955 958
956 web_applet->SetLastURL(web_applet->GetCurrentURL().toStdString()); 959 web_applet->SetLastURL(web_applet->GetCurrentURL().toStdString());
@@ -983,7 +986,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
983#else 986#else
984 987
985 // Utilize the same fallback as the default web browser applet. 988 // Utilize the same fallback as the default web browser applet.
986 emit WebBrowserClosed(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/"); 989 emit WebBrowserClosed(Service::AM::Frontend::WebExitReason::WindowClosed, "http://localhost/");
987 990
988#endif 991#endif
989} 992}
@@ -991,7 +994,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
991void GMainWindow::WebBrowserRequestExit() { 994void GMainWindow::WebBrowserRequestExit() {
992#ifdef YUZU_USE_QT_WEB_ENGINE 995#ifdef YUZU_USE_QT_WEB_ENGINE
993 if (web_applet) { 996 if (web_applet) {
994 web_applet->SetExitReason(Service::AM::Applets::WebExitReason::ExitRequested); 997 web_applet->SetExitReason(Service::AM::Frontend::WebExitReason::ExitRequested);
995 web_applet->SetFinished(true); 998 web_applet->SetFinished(true);
996 } 999 }
997#endif 1000#endif
@@ -1472,7 +1475,7 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
1472} 1475}
1473 1476
1474void GMainWindow::ConnectWidgetEvents() { 1477void GMainWindow::ConnectWidgetEvents() {
1475 connect(game_list, &GameList::BootGame, this, &GMainWindow::BootGame); 1478 connect(game_list, &GameList::BootGame, this, &GMainWindow::BootGameFromList);
1476 connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); 1479 connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile);
1477 connect(game_list, &GameList::OpenDirectory, this, &GMainWindow::OnGameListOpenDirectory); 1480 connect(game_list, &GameList::OpenDirectory, this, &GMainWindow::OnGameListOpenDirectory);
1478 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); 1481 connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
@@ -1760,8 +1763,7 @@ void GMainWindow::AllowOSSleep() {
1760#endif 1763#endif
1761} 1764}
1762 1765
1763bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index, 1766bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletParameters params) {
1764 AmLaunchType launch_type) {
1765 // Shutdown previous session if the emu thread is still active... 1767 // Shutdown previous session if the emu thread is still active...
1766 if (emu_thread != nullptr) { 1768 if (emu_thread != nullptr) {
1767 ShutdownGame(); 1769 ShutdownGame();
@@ -1773,11 +1775,11 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1773 1775
1774 system->SetFilesystem(vfs); 1776 system->SetFilesystem(vfs);
1775 1777
1776 if (launch_type == AmLaunchType::UserInitiated) { 1778 if (params.launch_type == Service::AM::LaunchType::FrontendInitiated) {
1777 system->GetUserChannel().clear(); 1779 system->GetUserChannel().clear();
1778 } 1780 }
1779 1781
1780 system->SetAppletFrontendSet({ 1782 system->SetFrontendAppletSet({
1781 std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings 1783 std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings
1782 (UISettings::values.controller_applet_disabled.GetValue() == true) 1784 (UISettings::values.controller_applet_disabled.GetValue() == true)
1783 ? nullptr 1785 ? nullptr
@@ -1792,7 +1794,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1792 }); 1794 });
1793 1795
1794 const Core::SystemResultStatus result{ 1796 const Core::SystemResultStatus result{
1795 system->Load(*render_window, filename.toStdString(), program_id, program_index)}; 1797 system->Load(*render_window, filename.toStdString(), params)};
1796 1798
1797 const auto drd_callout = (UISettings::values.callout_flags.GetValue() & 1799 const auto drd_callout = (UISettings::values.callout_flags.GetValue() &
1798 static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0; 1800 static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
@@ -1915,12 +1917,12 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
1915 } 1917 }
1916} 1918}
1917 1919
1918void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, 1920void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletParameters params,
1919 StartGameType type, AmLaunchType launch_type) { 1921 StartGameType type) {
1920 LOG_INFO(Frontend, "yuzu starting..."); 1922 LOG_INFO(Frontend, "yuzu starting...");
1921 1923
1922 if (program_id == 0 || 1924 if (params.program_id == 0 ||
1923 program_id > static_cast<u64>(Service::AM::Applets::AppletProgramId::MaxProgramId)) { 1925 params.program_id > static_cast<u64>(Service::AM::AppletProgramId::MaxProgramId)) {
1924 StoreRecentFile(filename); // Put the filename on top of the list 1926 StoreRecentFile(filename); // Put the filename on top of the list
1925 } 1927 }
1926 1928
@@ -1935,7 +1937,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1935 1937
1936 ConfigureFilesystemProvider(filename.toStdString()); 1938 ConfigureFilesystemProvider(filename.toStdString());
1937 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData()); 1939 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
1938 const auto loader = Loader::GetLoader(*system, v_file, program_id, program_index); 1940 const auto loader = Loader::GetLoader(*system, v_file, params.program_id, params.program_index);
1939 1941
1940 if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success && 1942 if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success &&
1941 type == StartGameType::Normal) { 1943 type == StartGameType::Normal) {
@@ -1954,10 +1956,10 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1954 1956
1955 if (UISettings::values.select_user_on_boot && !user_flag_cmd_line) { 1957 if (UISettings::values.select_user_on_boot && !user_flag_cmd_line) {
1956 const Core::Frontend::ProfileSelectParameters parameters{ 1958 const Core::Frontend::ProfileSelectParameters parameters{
1957 .mode = Service::AM::Applets::UiMode::UserSelector, 1959 .mode = Service::AM::Frontend::UiMode::UserSelector,
1958 .invalid_uid_list = {}, 1960 .invalid_uid_list = {},
1959 .display_options = {}, 1961 .display_options = {},
1960 .purpose = Service::AM::Applets::UserSelectionPurpose::General, 1962 .purpose = Service::AM::Frontend::UserSelectionPurpose::General,
1961 }; 1963 };
1962 if (SelectAndSetCurrentUser(parameters) == false) { 1964 if (SelectAndSetCurrentUser(parameters) == false) {
1963 return; 1965 return;
@@ -1969,7 +1971,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1969 // behavior of asking. 1971 // behavior of asking.
1970 user_flag_cmd_line = false; 1972 user_flag_cmd_line = false;
1971 1973
1972 if (!LoadROM(filename, program_id, program_index, launch_type)) { 1974 if (!LoadROM(filename, params)) {
1973 return; 1975 return;
1974 } 1976 }
1975 1977
@@ -2059,6 +2061,10 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
2059 OnStartGame(); 2061 OnStartGame();
2060} 2062}
2061 2063
2064void GMainWindow::BootGameFromList(const QString& filename, StartGameType with_config) {
2065 BootGame(filename, ApplicationAppletParameters(), with_config);
2066}
2067
2062bool GMainWindow::OnShutdownBegin() { 2068bool GMainWindow::OnShutdownBegin() {
2063 if (!emulation_running) { 2069 if (!emulation_running) {
2064 return false; 2070 return false;
@@ -2160,7 +2166,7 @@ void GMainWindow::OnEmulationStopped() {
2160 OnTasStateChanged(); 2166 OnTasStateChanged();
2161 render_window->FinalizeCamera(); 2167 render_window->FinalizeCamera();
2162 2168
2163 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::None); 2169 system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::None);
2164 2170
2165 // Enable all controllers 2171 // Enable all controllers
2166 system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); 2172 system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
@@ -2239,7 +2245,10 @@ void GMainWindow::UpdateRecentFiles() {
2239} 2245}
2240 2246
2241void GMainWindow::OnGameListLoadFile(QString game_path, u64 program_id) { 2247void GMainWindow::OnGameListLoadFile(QString game_path, u64 program_id) {
2242 BootGame(game_path, program_id); 2248 auto params = ApplicationAppletParameters();
2249 params.program_id = program_id;
2250
2251 BootGame(game_path, params);
2243} 2252}
2244 2253
2245void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, 2254void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
@@ -2280,10 +2289,10 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
2280 // User save data 2289 // User save data
2281 const auto select_profile = [this] { 2290 const auto select_profile = [this] {
2282 const Core::Frontend::ProfileSelectParameters parameters{ 2291 const Core::Frontend::ProfileSelectParameters parameters{
2283 .mode = Service::AM::Applets::UiMode::UserSelector, 2292 .mode = Service::AM::Frontend::UiMode::UserSelector,
2284 .invalid_uid_list = {}, 2293 .invalid_uid_list = {},
2285 .display_options = {}, 2294 .display_options = {},
2286 .purpose = Service::AM::Applets::UserSelectionPurpose::General, 2295 .purpose = Service::AM::Frontend::UserSelectionPurpose::General,
2287 }; 2296 };
2288 QtProfileSelectionDialog dialog(*system, this, parameters); 2297 QtProfileSelectionDialog dialog(*system, this, parameters);
2289 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | 2298 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
@@ -3171,7 +3180,7 @@ void GMainWindow::OnMenuLoadFile() {
3171 } 3180 }
3172 3181
3173 UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); 3182 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
3174 BootGame(filename); 3183 BootGame(filename, ApplicationAppletParameters());
3175} 3184}
3176 3185
3177void GMainWindow::OnMenuLoadFolder() { 3186void GMainWindow::OnMenuLoadFolder() {
@@ -3185,7 +3194,7 @@ void GMainWindow::OnMenuLoadFolder() {
3185 const QDir dir{dir_path}; 3194 const QDir dir{dir_path};
3186 const QStringList matching_main = dir.entryList({QStringLiteral("main")}, QDir::Files); 3195 const QStringList matching_main = dir.entryList({QStringLiteral("main")}, QDir::Files);
3187 if (matching_main.size() == 1) { 3196 if (matching_main.size() == 1) {
3188 BootGame(dir.path() + QDir::separator() + matching_main[0]); 3197 BootGame(dir.path() + QDir::separator() + matching_main[0], ApplicationAppletParameters());
3189 } else { 3198 } else {
3190 QMessageBox::warning(this, tr("Invalid Directory Selected"), 3199 QMessageBox::warning(this, tr("Invalid Directory Selected"),
3191 tr("The directory you have selected does not contain a 'main' file.")); 3200 tr("The directory you have selected does not contain a 'main' file."));
@@ -3379,7 +3388,7 @@ void GMainWindow::OnMenuRecentFile() {
3379 3388
3380 const QString filename = action->data().toString(); 3389 const QString filename = action->data().toString();
3381 if (QFileInfo::exists(filename)) { 3390 if (QFileInfo::exists(filename)) {
3382 BootGame(filename); 3391 BootGame(filename, ApplicationAppletParameters());
3383 } else { 3392 } else {
3384 // Display an error message and remove the file from the list. 3393 // Display an error message and remove the file from the list.
3385 QMessageBox::information(this, tr("File not found"), 3394 QMessageBox::information(this, tr("File not found"),
@@ -3417,7 +3426,7 @@ void GMainWindow::OnRestartGame() {
3417 // Make a copy since ShutdownGame edits game_path 3426 // Make a copy since ShutdownGame edits game_path
3418 const auto current_game = QString(current_game_path); 3427 const auto current_game = QString(current_game_path);
3419 ShutdownGame(); 3428 ShutdownGame();
3420 BootGame(current_game); 3429 BootGame(current_game, ApplicationAppletParameters());
3421 } 3430 }
3422} 3431}
3423 3432
@@ -3485,8 +3494,11 @@ void GMainWindow::OnLoadComplete() {
3485 3494
3486void GMainWindow::OnExecuteProgram(std::size_t program_index) { 3495void GMainWindow::OnExecuteProgram(std::size_t program_index) {
3487 ShutdownGame(); 3496 ShutdownGame();
3488 BootGame(last_filename_booted, 0, program_index, StartGameType::Normal, 3497
3489 AmLaunchType::ApplicationInitiated); 3498 auto params = ApplicationAppletParameters();
3499 params.program_index = static_cast<s32>(program_index);
3500 params.launch_type = Service::AM::LaunchType::ApplicationInitiated;
3501 BootGame(last_filename_booted, params);
3490} 3502}
3491 3503
3492void GMainWindow::OnExit() { 3504void GMainWindow::OnExit() {
@@ -4153,7 +4165,7 @@ void GMainWindow::OnToggleStatusBar() {
4153} 4165}
4154 4166
4155void GMainWindow::OnAlbum() { 4167void GMainWindow::OnAlbum() {
4156 constexpr u64 AlbumId = static_cast<u64>(Service::AM::Applets::AppletProgramId::PhotoViewer); 4168 constexpr u64 AlbumId = static_cast<u64>(Service::AM::AppletProgramId::PhotoViewer);
4157 auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); 4169 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4158 if (!bis_system) { 4170 if (!bis_system) {
4159 QMessageBox::warning(this, tr("No firmware available"), 4171 QMessageBox::warning(this, tr("No firmware available"),
@@ -4168,15 +4180,15 @@ void GMainWindow::OnAlbum() {
4168 return; 4180 return;
4169 } 4181 }
4170 4182
4171 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer); 4183 system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::PhotoViewer);
4172 4184
4173 const auto filename = QString::fromStdString(album_nca->GetFullPath()); 4185 const auto filename = QString::fromStdString(album_nca->GetFullPath());
4174 UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); 4186 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4175 BootGame(filename, AlbumId); 4187 BootGame(filename, LibraryAppletParameters(AlbumId, Service::AM::AppletId::PhotoViewer));
4176} 4188}
4177 4189
4178void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { 4190void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
4179 constexpr u64 CabinetId = static_cast<u64>(Service::AM::Applets::AppletProgramId::Cabinet); 4191 constexpr u64 CabinetId = static_cast<u64>(Service::AM::AppletProgramId::Cabinet);
4180 auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); 4192 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4181 if (!bis_system) { 4193 if (!bis_system) {
4182 QMessageBox::warning(this, tr("No firmware available"), 4194 QMessageBox::warning(this, tr("No firmware available"),
@@ -4191,16 +4203,16 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
4191 return; 4203 return;
4192 } 4204 }
4193 4205
4194 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Cabinet); 4206 system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Cabinet);
4195 system->GetAppletManager().SetCabinetMode(mode); 4207 system->GetFrontendAppletHolder().SetCabinetMode(mode);
4196 4208
4197 const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); 4209 const auto filename = QString::fromStdString(cabinet_nca->GetFullPath());
4198 UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); 4210 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4199 BootGame(filename, CabinetId); 4211 BootGame(filename, LibraryAppletParameters(CabinetId, Service::AM::AppletId::Cabinet));
4200} 4212}
4201 4213
4202void GMainWindow::OnMiiEdit() { 4214void GMainWindow::OnMiiEdit() {
4203 constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit); 4215 constexpr u64 MiiEditId = static_cast<u64>(Service::AM::AppletProgramId::MiiEdit);
4204 auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); 4216 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4205 if (!bis_system) { 4217 if (!bis_system) {
4206 QMessageBox::warning(this, tr("No firmware available"), 4218 QMessageBox::warning(this, tr("No firmware available"),
@@ -4215,16 +4227,15 @@ void GMainWindow::OnMiiEdit() {
4215 return; 4227 return;
4216 } 4228 }
4217 4229
4218 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit); 4230 system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::MiiEdit);
4219 4231
4220 const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); 4232 const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath()));
4221 UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); 4233 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4222 BootGame(filename, MiiEditId); 4234 BootGame(filename, LibraryAppletParameters(MiiEditId, Service::AM::AppletId::MiiEdit));
4223} 4235}
4224 4236
4225void GMainWindow::OnOpenControllerMenu() { 4237void GMainWindow::OnOpenControllerMenu() {
4226 constexpr u64 ControllerAppletId = 4238 constexpr u64 ControllerAppletId = static_cast<u64>(Service::AM::AppletProgramId::Controller);
4227 static_cast<u64>(Service::AM::Applets::AppletProgramId::Controller);
4228 auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); 4239 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4229 if (!bis_system) { 4240 if (!bis_system) {
4230 QMessageBox::warning(this, tr("No firmware available"), 4241 QMessageBox::warning(this, tr("No firmware available"),
@@ -4240,11 +4251,12 @@ void GMainWindow::OnOpenControllerMenu() {
4240 return; 4251 return;
4241 } 4252 }
4242 4253
4243 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Controller); 4254 system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Controller);
4244 4255
4245 const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath())); 4256 const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath()));
4246 UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); 4257 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4247 BootGame(filename, ControllerAppletId); 4258 BootGame(filename,
4259 LibraryAppletParameters(ControllerAppletId, Service::AM::AppletId::Controller));
4248} 4260}
4249 4261
4250void GMainWindow::OnCaptureScreenshot() { 4262void GMainWindow::OnCaptureScreenshot() {
@@ -4564,7 +4576,7 @@ void GMainWindow::OnCheckFirmwareDecryption() {
4564} 4576}
4565 4577
4566bool GMainWindow::CheckFirmwarePresence() { 4578bool GMainWindow::CheckFirmwarePresence() {
4567 constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit); 4579 constexpr u64 MiiEditId = static_cast<u64>(Service::AM::AppletProgramId::MiiEdit);
4568 4580
4569 auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); 4581 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4570 if (!bis_system) { 4582 if (!bis_system) {
@@ -4727,7 +4739,7 @@ bool GMainWindow::DropAction(QDropEvent* event) {
4727 } else { 4739 } else {
4728 // Game 4740 // Game
4729 if (ConfirmChangeGame()) { 4741 if (ConfirmChangeGame()) {
4730 BootGame(filename); 4742 BootGame(filename, ApplicationAppletParameters());
4731 } 4743 }
4732 } 4744 }
4733 return true; 4745 return true;
@@ -4771,36 +4783,12 @@ void GMainWindow::RequestGameExit() {
4771 return; 4783 return;
4772 } 4784 }
4773 4785
4774 auto& sm{system->ServiceManager()};
4775 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
4776 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
4777 bool has_signalled = false;
4778
4779 system->SetExitRequested(true); 4786 system->SetExitRequested(true);
4780 4787 system->GetAppletManager().RequestExit();
4781 if (applet_oe != nullptr) {
4782 applet_oe->GetMessageQueue()->RequestExit();
4783 has_signalled = true;
4784 }
4785
4786 if (applet_ae != nullptr && !has_signalled) {
4787 applet_ae->GetMessageQueue()->RequestExit();
4788 }
4789} 4788}
4790 4789
4791void GMainWindow::RequestGameResume() { 4790void GMainWindow::RequestGameResume() {
4792 auto& sm{system->ServiceManager()}; 4791 system->GetAppletManager().RequestResume();
4793 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
4794 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
4795
4796 if (applet_oe != nullptr) {
4797 applet_oe->GetMessageQueue()->RequestResume();
4798 return;
4799 }
4800
4801 if (applet_ae != nullptr) {
4802 applet_ae->GetMessageQueue()->RequestResume();
4803 }
4804} 4792}
4805 4793
4806void GMainWindow::filterBarSetChecked(bool state) { 4794void GMainWindow::filterBarSetChecked(bool state) {
@@ -4942,6 +4930,22 @@ void GMainWindow::changeEvent(QEvent* event) {
4942 QWidget::changeEvent(event); 4930 QWidget::changeEvent(event);
4943} 4931}
4944 4932
4933Service::AM::FrontendAppletParameters GMainWindow::ApplicationAppletParameters() {
4934 return Service::AM::FrontendAppletParameters{
4935 .applet_id = Service::AM::AppletId::Application,
4936 .applet_type = Service::AM::AppletType::Application,
4937 };
4938}
4939
4940Service::AM::FrontendAppletParameters GMainWindow::LibraryAppletParameters(
4941 u64 program_id, Service::AM::AppletId applet_id) {
4942 return Service::AM::FrontendAppletParameters{
4943 .program_id = program_id,
4944 .applet_id = applet_id,
4945 .applet_type = Service::AM::AppletType::LibraryApplet,
4946 };
4947}
4948
4945void VolumeButton::wheelEvent(QWheelEvent* event) { 4949void VolumeButton::wheelEvent(QWheelEvent* event) {
4946 4950
4947 int num_degrees = event->angleDelta().y() / 8; 4951 int num_degrees = event->angleDelta().y() / 8;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 6b72094ff..aba61e388 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -64,11 +64,6 @@ enum class StartGameType {
64 Global, // Only uses global configuration 64 Global, // Only uses global configuration
65}; 65};
66 66
67enum class AmLaunchType {
68 UserInitiated,
69 ApplicationInitiated,
70};
71
72namespace Core { 67namespace Core {
73enum class SystemResultStatus : u32; 68enum class SystemResultStatus : u32;
74class System; 69class System;
@@ -101,12 +96,17 @@ namespace InputCommon {
101class InputSubsystem; 96class InputSubsystem;
102} 97}
103 98
104namespace Service::AM::Applets { 99namespace Service::AM {
100struct FrontendAppletParameters;
101enum class AppletId : u32;
102} // namespace Service::AM
103
104namespace Service::AM::Frontend {
105enum class SwkbdResult : u32; 105enum class SwkbdResult : u32;
106enum class SwkbdTextCheckResult : u32; 106enum class SwkbdTextCheckResult : u32;
107enum class SwkbdReplyType : u32; 107enum class SwkbdReplyType : u32;
108enum class WebExitReason : u32; 108enum class WebExitReason : u32;
109} // namespace Service::AM::Applets 109} // namespace Service::AM::Frontend
110 110
111namespace Service::NFC { 111namespace Service::NFC {
112class NfcDevice; 112class NfcDevice;
@@ -204,13 +204,13 @@ signals:
204 204
205 void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid); 205 void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
206 206
207 void SoftwareKeyboardSubmitNormalText(Service::AM::Applets::SwkbdResult result, 207 void SoftwareKeyboardSubmitNormalText(Service::AM::Frontend::SwkbdResult result,
208 std::u16string submitted_text, bool confirmed); 208 std::u16string submitted_text, bool confirmed);
209 void SoftwareKeyboardSubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, 209 void SoftwareKeyboardSubmitInlineText(Service::AM::Frontend::SwkbdReplyType reply_type,
210 std::u16string submitted_text, s32 cursor_position); 210 std::u16string submitted_text, s32 cursor_position);
211 211
212 void WebBrowserExtractOfflineRomFS(); 212 void WebBrowserExtractOfflineRomFS();
213 void WebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url); 213 void WebBrowserClosed(Service::AM::Frontend::WebExitReason exit_reason, std::string last_url);
214 214
215 void SigInterrupt(); 215 void SigInterrupt();
216 216
@@ -228,8 +228,9 @@ public slots:
228 void SoftwareKeyboardInitialize( 228 void SoftwareKeyboardInitialize(
229 bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters); 229 bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters);
230 void SoftwareKeyboardShowNormal(); 230 void SoftwareKeyboardShowNormal();
231 void SoftwareKeyboardShowTextCheck(Service::AM::Applets::SwkbdTextCheckResult text_check_result, 231 void SoftwareKeyboardShowTextCheck(
232 std::u16string text_check_message); 232 Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
233 std::u16string text_check_message);
233 void SoftwareKeyboardShowInline(Core::Frontend::InlineAppearParameters appear_parameters); 234 void SoftwareKeyboardShowInline(Core::Frontend::InlineAppearParameters appear_parameters);
234 void SoftwareKeyboardHideInline(); 235 void SoftwareKeyboardHideInline();
235 void SoftwareKeyboardInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters); 236 void SoftwareKeyboardInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters);
@@ -267,11 +268,10 @@ private:
267 void PreventOSSleep(); 268 void PreventOSSleep();
268 void AllowOSSleep(); 269 void AllowOSSleep();
269 270
270 bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index, 271 bool LoadROM(const QString& filename, Service::AM::FrontendAppletParameters params);
271 AmLaunchType launch_type); 272 void BootGame(const QString& filename, Service::AM::FrontendAppletParameters params,
272 void BootGame(const QString& filename, u64 program_id = 0, std::size_t program_index = 0, 273 StartGameType with_config = StartGameType::Normal);
273 StartGameType with_config = StartGameType::Normal, 274 void BootGameFromList(const QString& filename, StartGameType with_config);
274 AmLaunchType launch_type = AmLaunchType::UserInitiated);
275 void ShutdownGame(); 275 void ShutdownGame();
276 276
277 void ShowTelemetryCallout(); 277 void ShowTelemetryCallout();
@@ -324,6 +324,10 @@ private:
324 void SetGamemodeEnabled(bool state); 324 void SetGamemodeEnabled(bool state);
325#endif 325#endif
326 326
327 Service::AM::FrontendAppletParameters ApplicationAppletParameters();
328 Service::AM::FrontendAppletParameters LibraryAppletParameters(u64 program_id,
329 Service::AM::AppletId applet_id);
330
327private slots: 331private slots:
328 void OnStartGame(); 332 void OnStartGame();
329 void OnRestartGame(); 333 void OnRestartGame();
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c39ace2ec..3b321dad1 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -26,6 +26,7 @@
26#include "core/crypto/key_manager.h" 26#include "core/crypto/key_manager.h"
27#include "core/file_sys/registered_cache.h" 27#include "core/file_sys/registered_cache.h"
28#include "core/file_sys/vfs/vfs_real.h" 28#include "core/file_sys/vfs/vfs_real.h"
29#include "core/hle/service/am/applet_manager.h"
29#include "core/hle/service/filesystem/filesystem.h" 30#include "core/hle/service/filesystem/filesystem.h"
30#include "core/loader/loader.h" 31#include "core/loader/loader.h"
31#include "core/telemetry_session.h" 32#include "core/telemetry_session.h"
@@ -366,7 +367,10 @@ int main(int argc, char** argv) {
366 system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); 367 system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
367 system.GetUserChannel().clear(); 368 system.GetUserChannel().clear();
368 369
369 const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath)}; 370 Service::AM::FrontendAppletParameters load_parameters{
371 .applet_id = Service::AM::AppletId::Application,
372 };
373 const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath, load_parameters)};
370 374
371 switch (load_result) { 375 switch (load_result) {
372 case Core::SystemResultStatus::ErrorGetLoader: 376 case Core::SystemResultStatus::ErrorGetLoader: