summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug-report-feature-request.md (renamed from .github/ISSUE_TEMPLATE.md)11
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml8
-rw-r--r--.gitmodules9
-rw-r--r--CMakeLists.txt34
-rw-r--r--CMakeModules/CopyYuzuQt5Deps.cmake1
-rw-r--r--CMakeModules/GenerateSCMRev.cmake2
-rw-r--r--dist/qt_themes/qdarkstyle/style.qss4
-rw-r--r--dist/yuzu.manifest80
-rw-r--r--externals/CMakeLists.txt27
m---------externals/Vulkan-Headers0
m---------externals/dynarmic0
-rw-r--r--externals/find-modules/FindLibUSB.cmake43
m---------externals/libressl0
m---------externals/libusb0
-rw-r--r--externals/opus/CMakeLists.txt254
m---------externals/opus/opus0
m---------externals/sirit0
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/audio_core/audio_renderer.cpp19
-rw-r--r--src/audio_core/audio_renderer.h13
-rw-r--r--src/audio_core/cubeb_sink.cpp2
-rw-r--r--src/audio_core/stream.cpp23
-rw-r--r--src/audio_core/stream.h9
-rw-r--r--src/common/CMakeLists.txt15
-rw-r--r--src/common/atomic_ops.cpp70
-rw-r--r--src/common/atomic_ops.h17
-rw-r--r--src/common/fiber.cpp222
-rw-r--r--src/common/fiber.h92
-rw-r--r--src/common/memory_detect.cpp23
-rw-r--r--src/common/spin_lock.cpp54
-rw-r--r--src/common/spin_lock.h26
-rw-r--r--src/common/telemetry.cpp1
-rw-r--r--src/common/thread.cpp52
-rw-r--r--src/common/thread.h13
-rw-r--r--src/common/uint128.cpp26
-rw-r--r--src/common/uint128.h3
-rw-r--r--src/common/wall_clock.cpp91
-rw-r--r--src/common/wall_clock.h53
-rw-r--r--src/common/x64/cpu_detect.cpp38
-rw-r--r--src/common/x64/cpu_detect.h13
-rw-r--r--src/common/x64/native_clock.cpp103
-rw-r--r--src/common/x64/native_clock.h48
-rw-r--r--src/common/x64/xbyak_abi.h95
-rw-r--r--src/core/CMakeLists.txt16
-rw-r--r--src/core/arm/arm_interface.cpp57
-rw-r--r--src/core/arm/arm_interface.h20
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp27
-rw-r--r--src/core/arm/cpu_interrupt_handler.h39
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp103
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h12
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp110
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h26
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp81
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.h126
-rw-r--r--src/core/arm/dynarmic/arm_exclusive_monitor.cpp76
-rw-r--r--src/core/arm/dynarmic/arm_exclusive_monitor.h48
-rw-r--r--src/core/arm/exclusive_monitor.cpp2
-rw-r--r--src/core/arm/exclusive_monitor.h6
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp19
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h5
-rw-r--r--src/core/core.cpp130
-rw-r--r--src/core/core.h48
-rw-r--r--src/core/core_manager.cpp67
-rw-r--r--src/core/core_manager.h63
-rw-r--r--src/core/core_timing.cpp254
-rw-r--r--src/core/core_timing.h123
-rw-r--r--src/core/core_timing_util.cpp44
-rw-r--r--src/core/core_timing_util.h18
-rw-r--r--src/core/cpu_manager.cpp368
-rw-r--r--src/core/cpu_manager.h80
-rw-r--r--src/core/crypto/key_manager.cpp12
-rw-r--r--src/core/crypto/key_manager.h13
-rw-r--r--src/core/file_sys/bis_factory.cpp2
-rw-r--r--src/core/file_sys/card_image.cpp4
-rw-r--r--src/core/file_sys/card_image.h2
-rw-r--r--src/core/file_sys/content_archive.cpp5
-rw-r--r--src/core/file_sys/content_archive.h5
-rw-r--r--src/core/file_sys/registered_cache.cpp6
-rw-r--r--src/core/file_sys/registered_cache.h2
-rw-r--r--src/core/file_sys/submission_package.cpp4
-rw-r--r--src/core/file_sys/submission_package.h2
-rw-r--r--src/core/file_sys/system_archive/mii_model.cpp2
-rw-r--r--src/core/file_sys/system_archive/shared_font.cpp2
-rw-r--r--src/core/file_sys/xts_archive.h2
-rw-r--r--src/core/frontend/framebuffer_layout.cpp2
-rw-r--r--src/core/gdbstub/gdbstub.cpp1
-rw-r--r--src/core/hardware_properties.h4
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp212
-rw-r--r--src/core/hle/kernel/address_arbiter.h3
-rw-r--r--src/core/hle/kernel/client_port.cpp2
-rw-r--r--src/core/hle/kernel/errors.h1
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp87
-rw-r--r--src/core/hle/kernel/kernel.cpp255
-rw-r--r--src/core/hle/kernel/kernel.h39
-rw-r--r--src/core/hle/kernel/memory/memory_manager.cpp5
-rw-r--r--src/core/hle/kernel/mutex.cpp118
-rw-r--r--src/core/hle/kernel/mutex.h4
-rw-r--r--src/core/hle/kernel/physical_core.cpp52
-rw-r--r--src/core/hle/kernel/physical_core.h44
-rw-r--r--src/core/hle/kernel/process.cpp31
-rw-r--r--src/core/hle/kernel/readable_event.cpp5
-rw-r--r--src/core/hle/kernel/resource_limit.cpp6
-rw-r--r--src/core/hle/kernel/scheduler.cpp571
-rw-r--r--src/core/hle/kernel/scheduler.h123
-rw-r--r--src/core/hle/kernel/server_session.cpp16
-rw-r--r--src/core/hle/kernel/svc.cpp464
-rw-r--r--src/core/hle/kernel/svc_wrap.h137
-rw-r--r--src/core/hle/kernel/synchronization.cpp137
-rw-r--r--src/core/hle/kernel/synchronization_object.cpp64
-rw-r--r--src/core/hle/kernel/synchronization_object.h18
-rw-r--r--src/core/hle/kernel/thread.cpp424
-rw-r--r--src/core/hle/kernel/thread.h277
-rw-r--r--src/core/hle/kernel/time_manager.cpp23
-rw-r--r--src/core/hle/kernel/time_manager.h4
-rw-r--r--src/core/hle/service/acc/acc.cpp350
-rw-r--r--src/core/hle/service/acc/acc.h1
-rw-r--r--src/core/hle/service/acc/acc_aa.cpp4
-rw-r--r--src/core/hle/service/acc/acc_su.cpp34
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp18
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp29
-rw-r--r--src/core/hle/service/am/am.cpp80
-rw-r--r--src/core/hle/service/am/am.h3
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp6
-rw-r--r--src/core/hle/service/am/spsm.cpp16
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp1
-rw-r--r--src/core/hle/service/bcat/bcat.cpp2
-rw-r--r--src/core/hle/service/bcat/module.cpp3
-rw-r--r--src/core/hle/service/bpc/bpc.cpp20
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp167
-rw-r--r--src/core/hle/service/btm/btm.cpp147
-rw-r--r--src/core/hle/service/caps/caps.cpp2
-rw-r--r--src/core/hle/service/caps/caps.h76
-rw-r--r--src/core/hle/service/caps/caps_a.cpp2
-rw-r--r--src/core/hle/service/caps/caps_a.h2
-rw-r--r--src/core/hle/service/caps/caps_c.cpp2
-rw-r--r--src/core/hle/service/caps/caps_c.h2
-rw-r--r--src/core/hle/service/caps/caps_sc.cpp2
-rw-r--r--src/core/hle/service/caps/caps_sc.h2
-rw-r--r--src/core/hle/service/caps/caps_ss.cpp2
-rw-r--r--src/core/hle/service/caps/caps_ss.h2
-rw-r--r--src/core/hle/service/caps/caps_su.cpp2
-rw-r--r--src/core/hle/service/caps/caps_su.h2
-rw-r--r--src/core/hle/service/caps/caps_u.cpp26
-rw-r--r--src/core/hle/service/caps/caps_u.h2
-rw-r--r--src/core/hle/service/es/es.cpp49
-rw-r--r--src/core/hle/service/eupld/eupld.cpp1
-rw-r--r--src/core/hle/service/friend/friend.cpp6
-rw-r--r--src/core/hle/service/grc/grc.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp10
-rw-r--r--src/core/hle/service/hid/controllers/npad.h10
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp4
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp2
-rw-r--r--src/core/hle/service/hid/hid.cpp164
-rw-r--r--src/core/hle/service/hid/hid.h16
-rw-r--r--src/core/hle/service/hid/irs.cpp2
-rw-r--r--src/core/hle/service/lbl/lbl.cpp1
-rw-r--r--src/core/hle/service/ldn/ldn.cpp1
-rw-r--r--src/core/hle/service/ldr/ldr.cpp105
-rw-r--r--src/core/hle/service/lm/manager.cpp3
-rw-r--r--src/core/hle/service/mig/mig.cpp6
-rw-r--r--src/core/hle/service/mm/mm_u.cpp32
-rw-r--r--src/core/hle/service/ncm/ncm.cpp20
-rw-r--r--src/core/hle/service/nfc/nfc.cpp6
-rw-r--r--src/core/hle/service/ns/ns.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp28
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h18
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp64
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h21
-rw-r--r--src/core/hle/service/prepo/prepo.cpp7
-rw-r--r--src/core/hle/service/set/set.cpp78
-rw-r--r--src/core/hle/service/set/set.h2
-rw-r--r--src/core/hle/service/sm/sm.cpp2
-rw-r--r--src/core/hle/service/spl/module.cpp2
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.cpp5
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.cpp5
-rw-r--r--src/core/hle/service/time/time.cpp5
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp3
-rw-r--r--src/core/hle/service/vi/vi.cpp22
-rw-r--r--src/core/memory.cpp116
-rw-r--r--src/core/memory.h67
-rw-r--r--src/core/memory/cheat_engine.cpp8
-rw-r--r--src/core/perf_stats.cpp5
-rw-r--r--src/core/settings.cpp89
-rw-r--r--src/core/settings.h126
-rw-r--r--src/core/telemetry_session.cpp27
-rw-r--r--src/core/tools/freezer.cpp8
-rw-r--r--src/input_common/CMakeLists.txt6
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp398
-rw-r--r--src/input_common/gcadapter/gc_adapter.h161
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp272
-rw-r--r--src/input_common/gcadapter/gc_poller.h67
-rw-r--r--src/input_common/keyboard.cpp2
-rw-r--r--src/input_common/main.cpp24
-rw-r--r--src/input_common/main.h5
-rw-r--r--src/input_common/motion_emu.cpp2
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/fibers.cpp358
-rw-r--r--src/tests/core/core_timing.cpp182
-rw-r--r--src/video_core/CMakeLists.txt17
-rw-r--r--src/video_core/buffer_cache/buffer_block.h27
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h280
-rw-r--r--src/video_core/compatible_formats.cpp162
-rw-r--r--src/video_core/compatible_formats.h32
-rw-r--r--src/video_core/engines/const_buffer_engine_interface.h1
-rw-r--r--src/video_core/engines/kepler_compute.cpp5
-rw-r--r--src/video_core/engines/kepler_compute.h2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp26
-rw-r--r--src/video_core/engines/maxwell_3d.h27
-rw-r--r--src/video_core/engines/shader_bytecode.h8
-rw-r--r--src/video_core/gpu.cpp7
-rw-r--r--src/video_core/gpu.h6
-rw-r--r--src/video_core/gpu_asynch.cpp9
-rw-r--r--src/video_core/gpu_asynch.h2
-rw-r--r--src/video_core/gpu_synch.cpp8
-rw-r--r--src/video_core/gpu_synch.h2
-rw-r--r--src/video_core/gpu_thread.cpp7
-rw-r--r--src/video_core/macro/macro.cpp91
-rw-r--r--src/video_core/macro/macro.h141
-rw-r--r--src/video_core/macro/macro_hle.cpp113
-rw-r--r--src/video_core/macro/macro_hle.h44
-rw-r--r--src/video_core/macro/macro_interpreter.cpp (renamed from src/video_core/macro_interpreter.cpp)199
-rw-r--r--src/video_core/macro/macro_interpreter.h (renamed from src/video_core/macro_interpreter.h)51
-rw-r--r--src/video_core/macro/macro_jit_x64.cpp621
-rw-r--r--src/video_core/macro/macro_jit_x64.h98
-rw-r--r--src/video_core/memory_manager.cpp40
-rw-r--r--src/video_core/memory_manager.h12
-rw-r--r--src/video_core/query_cache.h12
-rw-r--r--src/video_core/rasterizer_cache.cpp7
-rw-r--r--src/video_core/rasterizer_cache.h253
-rw-r--r--src/video_core/renderer_base.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_arb_decompiler.cpp2073
-rw-r--r--src/video_core/renderer_opengl/gl_arb_decompiler.h29
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp78
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h50
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp122
-rw-r--r--src/video_core/renderer_opengl/gl_device.h27
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp302
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h25
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp99
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h52
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp163
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp66
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h1
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp64
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.h25
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp56
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h6
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h120
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp28
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h3
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp116
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h207
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp178
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp34
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp97
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h42
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp169
-rw-r--r--src/video_core/renderer_vulkan/vk_device.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp89
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp103
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h41
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp455
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h36
-rw-r--r--src/video_core/renderer_vulkan/vk_sampler_cache.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp167
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.h12
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp68
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h50
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp80
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h33
-rw-r--r--src/video_core/renderer_vulkan/vk_update_descriptor.cpp36
-rw-r--r--src/video_core/renderer_vulkan/vk_update_descriptor.h32
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp29
-rw-r--r--src/video_core/renderer_vulkan/wrapper.h60
-rw-r--r--src/video_core/shader/decode/half_set.cpp88
-rw-r--r--src/video_core/shader/decode/image.cpp26
-rw-r--r--src/video_core/shader/decode/other.cpp2
-rw-r--r--src/video_core/shader/decode/texture.cpp55
-rw-r--r--src/video_core/shader/memory_util.cpp4
-rw-r--r--src/video_core/shader/node.h75
-rw-r--r--src/video_core/shader/node_helper.h2
-rw-r--r--src/video_core/shader/registry.cpp20
-rw-r--r--src/video_core/shader/registry.h35
-rw-r--r--src/video_core/shader/shader_ir.h14
-rw-r--r--src/video_core/shader/track.cpp78
-rw-r--r--src/video_core/shader_cache.h240
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp3
-rw-r--r--src/video_core/texture_cache/surface_base.cpp10
-rw-r--r--src/video_core/texture_cache/surface_base.h13
-rw-r--r--src/video_core/texture_cache/surface_params.cpp19
-rw-r--r--src/video_core/texture_cache/texture_cache.h268
-rw-r--r--src/video_core/textures/texture.cpp2
-rw-r--r--src/video_core/video_core.cpp8
-rw-r--r--src/yuzu/CMakeLists.txt17
-rw-r--r--src/yuzu/bootmanager.cpp82
-rw-r--r--src/yuzu/bootmanager.h8
-rw-r--r--src/yuzu/configuration/config.cpp362
-rw-r--r--src/yuzu/configuration/config.h29
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp76
-rw-r--r--src/yuzu/configuration/configuration_shared.h36
-rw-r--r--src/yuzu/configuration/configure_audio.cpp82
-rw-r--r--src/yuzu/configuration/configure_audio.h2
-rw-r--r--src/yuzu/configuration/configure_audio.ui50
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui13
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp2
-rw-r--r--src/yuzu/configuration/configure_general.cpp78
-rw-r--r--src/yuzu/configuration/configure_general.h2
-rw-r--r--src/yuzu/configuration/configure_general.ui7
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp201
-rw-r--r--src/yuzu/configuration/configure_graphics.h2
-rw-r--r--src/yuzu/configuration/configure_graphics.ui70
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp107
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h2
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp63
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp140
-rw-r--r--src/yuzu/configuration/configure_per_game.h51
-rw-r--r--src/yuzu/configuration/configure_per_game.ui350
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp (renamed from src/yuzu/configuration/configure_per_general.cpp)95
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.h (renamed from src/yuzu/configuration/configure_per_general.h)16
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.ui38
-rw-r--r--src/yuzu/configuration/configure_per_general.ui276
-rw-r--r--src/yuzu/configuration/configure_service.cpp6
-rw-r--r--src/yuzu/configuration/configure_system.cpp190
-rw-r--r--src/yuzu/configuration/configure_system.h2
-rw-r--r--src/yuzu/debugger/wait_tree.cpp54
-rw-r--r--src/yuzu/main.cpp210
-rw-r--r--src/yuzu/main.h9
-rw-r--r--src/yuzu/main.ui18
-rw-r--r--src/yuzu/yuzu.rc2
-rw-r--r--src/yuzu_cmd/config.cpp87
-rw-r--r--src/yuzu_cmd/default_ini.h7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp5
-rw-r--r--src/yuzu_cmd/yuzu.cpp7
-rw-r--r--src/yuzu_cmd/yuzu.rc2
-rw-r--r--src/yuzu_tester/config.cpp65
-rw-r--r--src/yuzu_tester/default_ini.h5
-rw-r--r--src/yuzu_tester/service/yuzutest.cpp2
-rw-r--r--src/yuzu_tester/yuzu.cpp5
-rw-r--r--src/yuzu_tester/yuzu.rc2
351 files changed, 16551 insertions, 5621 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/bug-report-feature-request.md
index 70e1bba67..5706243bb 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE/bug-report-feature-request.md
@@ -1,4 +1,13 @@
1<!-- 1---
2name: Bug Report / Feature Request
3about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better.
4title: ''
5labels: ''
6assignees: ''
7
8---
9
10<!---
2Please keep in mind yuzu is EXPERIMENTAL SOFTWARE. 11Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
3 12
4Please read the FAQ: 13Please read the FAQ:
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..52faafad3
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
1blank_issues_enabled: false
2contact_links:
3 - name: yuzu Discord
4 url: https://discord.com/invite/u77vRWY
5 about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed.
6 - name: Community forums
7 url: https://community.citra-emu.org
8 about: This is an alternative place for tech support, however helpers there are not as active.
diff --git a/.gitmodules b/.gitmodules
index 2ec9dda62..79028bbb5 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -13,6 +13,9 @@
13[submodule "soundtouch"] 13[submodule "soundtouch"]
14 path = externals/soundtouch 14 path = externals/soundtouch
15 url = https://github.com/citra-emu/ext-soundtouch.git 15 url = https://github.com/citra-emu/ext-soundtouch.git
16[submodule "libressl"]
17 path = externals/libressl
18 url = https://github.com/citra-emu/ext-libressl-portable.git
16[submodule "discord-rpc"] 19[submodule "discord-rpc"]
17 path = externals/discord-rpc 20 path = externals/discord-rpc
18 url = https://github.com/discordapp/discord-rpc.git 21 url = https://github.com/discordapp/discord-rpc.git
@@ -31,3 +34,9 @@
31[submodule "xbyak"] 34[submodule "xbyak"]
32 path = externals/xbyak 35 path = externals/xbyak
33 url = https://github.com/herumi/xbyak.git 36 url = https://github.com/herumi/xbyak.git
37[submodule "externals/libusb"]
38 path = externals/libusb
39 url = https://github.com/ameerj/libusb
40[submodule "opus"]
41 path = externals/opus/opus
42 url = https://github.com/xiph/opus.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 61321bf0a..1c0e49c03 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
1cmake_minimum_required(VERSION 3.11) 1cmake_minimum_required(VERSION 3.15)
2 2
3list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") 3list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
4list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules") 4list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
@@ -13,7 +13,7 @@ project(yuzu)
13option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) 13option(ENABLE_SDL2 "Enable the SDL2 frontend" ON)
14 14
15option(ENABLE_QT "Enable the Qt frontend" ON) 15option(ENABLE_QT "Enable the Qt frontend" ON)
16CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" OFF "ENABLE_QT;MSVC" OFF) 16CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF)
17 17
18option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) 18option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
19 19
@@ -151,14 +151,11 @@ macro(yuzu_find_packages)
151 # Cmake Pkg Prefix Version Conan Pkg 151 # Cmake Pkg Prefix Version Conan Pkg
152 "Boost 1.71 boost/1.72.0" 152 "Boost 1.71 boost/1.72.0"
153 "Catch2 2.11 catch2/2.11.0" 153 "Catch2 2.11 catch2/2.11.0"
154 "fmt 6.2 fmt/6.2.0" 154 "fmt 7.0 fmt/7.0.1"
155 "OpenSSL 1.1 openssl/1.1.1f"
156 # can't use until https://github.com/bincrafters/community/issues/1173 155 # can't use until https://github.com/bincrafters/community/issues/1173
157 #"libzip 1.5 libzip/1.5.2@bincrafters/stable" 156 #"libzip 1.5 libzip/1.5.2@bincrafters/stable"
158 "lz4 1.8 lz4/1.9.2" 157 "lz4 1.8 lz4/1.9.2"
159 "nlohmann_json 3.7 nlohmann_json/3.7.3" 158 "nlohmann_json 3.7 nlohmann_json/3.7.3"
160 # we need to be careful as the version check might be broken https://github.com/xiph/opus/issues/110
161 "opus 1.3 opus/1.3.1"
162 "ZLIB 1.2 zlib/1.2.11" 159 "ZLIB 1.2 zlib/1.2.11"
163 "zstd 1.4 zstd/1.4.4" 160 "zstd 1.4 zstd/1.4.4"
164 ) 161 )
@@ -214,7 +211,10 @@ if(ENABLE_QT)
214 211
215 set(QT_PREFIX_HINT HINTS "${QT_PREFIX}") 212 set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
216 endif() 213 endif()
217 find_package(Qt5 5.9 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) 214 find_package(Qt5 5.9 COMPONENTS Widgets ${QT_PREFIX_HINT})
215 if (YUZU_USE_QT_WEB_ENGINE)
216 find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
217 endif()
218 if (NOT Qt5_FOUND) 218 if (NOT Qt5_FOUND)
219 list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable") 219 list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable")
220 endif() 220 endif()
@@ -287,7 +287,7 @@ if (CONAN_REQUIRED_LIBS)
287 if(ENABLE_QT) 287 if(ENABLE_QT)
288 list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}") 288 list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
289 list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}") 289 list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}")
290 find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets OpenGL) 290 find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets)
291 if (YUZU_USE_QT_WEB_ENGINE) 291 if (YUZU_USE_QT_WEB_ENGINE)
292 find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets) 292 find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
293 endif() 293 endif()
@@ -312,15 +312,6 @@ elseif (TARGET Boost::boost)
312 add_library(boost ALIAS Boost::boost) 312 add_library(boost ALIAS Boost::boost)
313endif() 313endif()
314 314
315if (NOT TARGET OpenSSL::SSL)
316 set_target_properties(OpenSSL::OpenSSL PROPERTIES IMPORTED_GLOBAL TRUE)
317 add_library(OpenSSL::SSL ALIAS OpenSSL::OpenSSL)
318endif()
319if (NOT TARGET OpenSSL::Crypto)
320 set_target_properties(OpenSSL::OpenSSL PROPERTIES IMPORTED_GLOBAL TRUE)
321 add_library(OpenSSL::Crypto ALIAS OpenSSL::OpenSSL)
322endif()
323
324if (TARGET sdl2::sdl2) 315if (TARGET sdl2::sdl2)
325 # imported from the conan generated sdl2Config.cmake 316 # imported from the conan generated sdl2Config.cmake
326 set_target_properties(sdl2::sdl2 PROPERTIES IMPORTED_GLOBAL TRUE) 317 set_target_properties(sdl2::sdl2 PROPERTIES IMPORTED_GLOBAL TRUE)
@@ -338,6 +329,15 @@ elseif(SDL2_FOUND)
338 target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}") 329 target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}")
339endif() 330endif()
340 331
332# Ensure libusb is properly configured (based on dolphin libusb include)
333include(FindPkgConfig)
334find_package(LibUSB)
335if (NOT LIBUSB_FOUND)
336 add_subdirectory(externals/libusb)
337 set(LIBUSB_LIBRARIES usb)
338endif()
339
340
341# Prefer the -pthread flag on Linux. 341# Prefer the -pthread flag on Linux.
342set(THREADS_PREFER_PTHREAD_FLAG ON) 342set(THREADS_PREFER_PTHREAD_FLAG ON)
343find_package(Threads REQUIRED) 343find_package(Threads REQUIRED)
diff --git a/CMakeModules/CopyYuzuQt5Deps.cmake b/CMakeModules/CopyYuzuQt5Deps.cmake
index 2598b9b60..59343b1ca 100644
--- a/CMakeModules/CopyYuzuQt5Deps.cmake
+++ b/CMakeModules/CopyYuzuQt5Deps.cmake
@@ -15,7 +15,6 @@ function(copy_yuzu_Qt5_deps target_dir)
15 icuuc*.dll 15 icuuc*.dll
16 Qt5Core$<$<CONFIG:Debug>:d>.* 16 Qt5Core$<$<CONFIG:Debug>:d>.*
17 Qt5Gui$<$<CONFIG:Debug>:d>.* 17 Qt5Gui$<$<CONFIG:Debug>:d>.*
18 Qt5OpenGL$<$<CONFIG:Debug>:d>.*
19 Qt5Widgets$<$<CONFIG:Debug>:d>.* 18 Qt5Widgets$<$<CONFIG:Debug>:d>.*
20 ) 19 )
21 20
diff --git a/CMakeModules/GenerateSCMRev.cmake b/CMakeModules/GenerateSCMRev.cmake
index 83e4e9df2..311ba1c2e 100644
--- a/CMakeModules/GenerateSCMRev.cmake
+++ b/CMakeModules/GenerateSCMRev.cmake
@@ -51,6 +51,8 @@ endif()
51# The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR) 51# The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR)
52set(VIDEO_CORE "${SRC_DIR}/src/video_core") 52set(VIDEO_CORE "${SRC_DIR}/src/video_core")
53set(HASH_FILES 53set(HASH_FILES
54 "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.cpp"
55 "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.h"
54 "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp" 56 "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp"
55 "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h" 57 "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h"
56 "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp" 58 "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss
index 7d088a719..2d5c9761f 100644
--- a/dist/qt_themes/qdarkstyle/style.qss
+++ b/dist/qt_themes/qdarkstyle/style.qss
@@ -673,10 +673,6 @@ QTabWidget::pane {
673 border-bottom-left-radius: 2px; 673 border-bottom-left-radius: 2px;
674} 674}
675 675
676QTabWidget::tab-bar {
677 overflow: visible;
678}
679
680QTabBar { 676QTabBar {
681 qproperty-drawBase: 0; 677 qproperty-drawBase: 0;
682 border-radius: 3px; 678 border-radius: 3px;
diff --git a/dist/yuzu.manifest b/dist/yuzu.manifest
index fd30b656f..038edff23 100644
--- a/dist/yuzu.manifest
+++ b/dist/yuzu.manifest
@@ -1,24 +1,58 @@
1<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 1<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 2<assembly manifestVersion="1.0"
3 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> 3 xmlns="urn:schemas-microsoft-com:asm.v1"
4 <security> 4 xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
5 <requestedPrivileges> 5 <asmv3:application>
6 <requestedExecutionLevel level="asInvoker" uiAccess="false"/> 6 <asmv3:windowsSettings>
7 </requestedPrivileges> 7 <!-- Windows 7/8/8.1/10 -->
8 </security> 8 <dpiAware
9 </trustInfo> 9 xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
10 <application xmlns="urn:schemas-microsoft-com:asm.v3"> 10 true/pm
11 <windowsSettings> 11 </dpiAware>
12 <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware> 12 <!-- Windows 10, version 1607 or later -->
13 <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware> 13 <dpiAwareness
14 </windowsSettings> 14 xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
15 </application> 15 PerMonitorV2
16 <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> 16 </dpiAwareness>
17 <application> 17 <!-- Windows 10, version 1703 or later -->
18 <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> 18 <gdiScaling
19 <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> 19 xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">
20 <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> 20 true
21 <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> 21 </gdiScaling>
22 </application> 22 <ws2:longPathAware
23 </compatibility> 23 xmlns:ws3="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
24</assembly> \ No newline at end of file 24 true
25 </ws2:longPathAware>
26 </asmv3:windowsSettings>
27 </asmv3:application>
28 <compatibility
29 xmlns="urn:schemas-microsoft-com:compatibility.v1">
30 <application>
31 <!-- Windows 10 -->
32 <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
33 <!-- Windows 8.1 -->
34 <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
35 <!-- Windows 8 -->
36 <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
37 <!-- Windows 7 -->
38 <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
39 </application>
40 </compatibility>
41 <trustInfo
42 xmlns="urn:schemas-microsoft-com:asm.v3">
43 <security>
44 <requestedPrivileges>
45 <!--
46 UAC settings:
47 - app should run at same integrity level as calling process
48 - app does not need to manipulate windows belonging to
49 higher-integrity-level processes
50 -->
51 <requestedExecutionLevel
52 level="asInvoker"
53 uiAccess="false"
54 />
55 </requestedPrivileges>
56 </security>
57 </trustInfo>
58</assembly>
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index df7a5e0a9..d1dcc403b 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -4,6 +4,13 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules")
4list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/externals/find-modules") 4list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/externals/find-modules")
5include(DownloadExternals) 5include(DownloadExternals)
6 6
7# xbyak
8if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
9 add_library(xbyak INTERFACE)
10 target_include_directories(xbyak SYSTEM INTERFACE ./xbyak/xbyak)
11 target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
12endif()
13
7# Catch 14# Catch
8add_library(catch-single-include INTERFACE) 15add_library(catch-single-include INTERFACE)
9target_include_directories(catch-single-include INTERFACE catch/single_include) 16target_include_directories(catch-single-include INTERFACE catch/single_include)
@@ -66,6 +73,15 @@ if (NOT LIBZIP_FOUND)
66endif() 73endif()
67 74
68if (ENABLE_WEB_SERVICE) 75if (ENABLE_WEB_SERVICE)
76 # LibreSSL
77 set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
78 add_subdirectory(libressl EXCLUDE_FROM_ALL)
79 target_include_directories(ssl INTERFACE ./libressl/include)
80 target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
81 get_directory_property(OPENSSL_LIBRARIES
82 DIRECTORY libressl
83 DEFINITION OPENSSL_LIBS)
84
69 # lurlparser 85 # lurlparser
70 add_subdirectory(lurlparser EXCLUDE_FROM_ALL) 86 add_subdirectory(lurlparser EXCLUDE_FROM_ALL)
71 87
@@ -73,13 +89,8 @@ if (ENABLE_WEB_SERVICE)
73 add_library(httplib INTERFACE) 89 add_library(httplib INTERFACE)
74 target_include_directories(httplib INTERFACE ./httplib) 90 target_include_directories(httplib INTERFACE ./httplib)
75 target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT) 91 target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
76 target_link_libraries(httplib INTERFACE OpenSSL::SSL OpenSSL::Crypto) 92 target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
77endif() 93endif()
78 94
79if (NOT TARGET xbyak) 95# Opus
80 if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) 96add_subdirectory(opus)
81 add_library(xbyak INTERFACE)
82 target_include_directories(xbyak SYSTEM INTERFACE ./xbyak/xbyak)
83 target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
84 endif()
85endif()
diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers
Subproject 9250d5ae8f50202005233dc0512a1d460c8b483 Subproject 8188e3fbbc105591064093440f88081fb957d4f
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject e7166e8ba74d7b9c85e87afc0aaf667e7e84cfe Subproject 4f967387c07365b7ea35d2fa3e19b7df8872a09
diff --git a/externals/find-modules/FindLibUSB.cmake b/externals/find-modules/FindLibUSB.cmake
new file mode 100644
index 000000000..dec0b98b0
--- /dev/null
+++ b/externals/find-modules/FindLibUSB.cmake
@@ -0,0 +1,43 @@
1# - Find libusb-1.0 library
2# This module defines
3# LIBUSB_INCLUDE_DIR, where to find bluetooth.h
4# LIBUSB_LIBRARIES, the libraries needed to use libusb-1.0.
5# LIBUSB_FOUND, If false, do not try to use libusb-1.0.
6#
7# Copyright (c) 2009, Michal Cihar, <michal@cihar.com>
8#
9# vim: expandtab sw=4 ts=4 sts=4:
10
11if(ANDROID)
12 set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
13 message(STATUS "libusb-1.0 not found.")
14elseif (NOT LIBUSB_FOUND)
15 pkg_check_modules (LIBUSB_PKG libusb-1.0)
16
17 find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h
18 PATHS
19 ${LIBUSB_PKG_INCLUDE_DIRS}
20 /usr/include/libusb-1.0
21 /usr/include
22 /usr/local/include/libusb-1.0
23 /usr/local/include
24 )
25
26 find_library(LIBUSB_LIBRARIES NAMES usb-1.0 usb
27 PATHS
28 ${LIBUSB_PKG_LIBRARY_DIRS}
29 /usr/lib
30 /usr/local/lib
31 )
32
33 if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
34 set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found")
35 message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}")
36 else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
37 set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
38 message(STATUS "libusb-1.0 not found.")
39 endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
40
41 mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES)
42endif ()
43
diff --git a/externals/libressl b/externals/libressl
new file mode 160000
Subproject 7d01cb01cb1a926ecb4c9c98b107ef3c26f59df
diff --git a/externals/libusb b/externals/libusb
new file mode 160000
Subproject 3406d72cda879f8792a88bf5f6bd0b7a65636f7
diff --git a/externals/opus/CMakeLists.txt b/externals/opus/CMakeLists.txt
new file mode 100644
index 000000000..94a86551f
--- /dev/null
+++ b/externals/opus/CMakeLists.txt
@@ -0,0 +1,254 @@
1cmake_minimum_required(VERSION 3.8)
2
3project(opus)
4
5option(OPUS_STACK_PROTECTOR "Use stack protection" OFF)
6option(OPUS_USE_ALLOCA "Use alloca for stack arrays (on non-C99 compilers)" OFF)
7option(OPUS_CUSTOM_MODES "Enable non-Opus modes, e.g. 44.1 kHz & 2^n frames" OFF)
8option(OPUS_FIXED_POINT "Compile as fixed-point (for machines without a fast enough FPU)" OFF)
9option(OPUS_ENABLE_FLOAT_API "Compile with the floating point API (for machines with float library" ON)
10
11include(opus/opus_functions.cmake)
12
13if(OPUS_STACK_PROTECTOR)
14 if(NOT MSVC) # GC on by default on MSVC
15 check_and_set_flag(STACK_PROTECTION_STRONG -fstack-protector-strong)
16 endif()
17else()
18 if(MSVC)
19 check_and_set_flag(BUFFER_SECURITY_CHECK /GS-)
20 endif()
21endif()
22
23add_library(opus STATIC
24 # CELT sources
25 opus/celt/bands.c
26 opus/celt/celt.c
27 opus/celt/celt_decoder.c
28 opus/celt/celt_encoder.c
29 opus/celt/celt_lpc.c
30 opus/celt/cwrs.c
31 opus/celt/entcode.c
32 opus/celt/entdec.c
33 opus/celt/entenc.c
34 opus/celt/kiss_fft.c
35 opus/celt/laplace.c
36 opus/celt/mathops.c
37 opus/celt/mdct.c
38 opus/celt/modes.c
39 opus/celt/pitch.c
40 opus/celt/quant_bands.c
41 opus/celt/rate.c
42 opus/celt/vq.c
43
44 # SILK sources
45 opus/silk/A2NLSF.c
46 opus/silk/CNG.c
47 opus/silk/HP_variable_cutoff.c
48 opus/silk/LPC_analysis_filter.c
49 opus/silk/LPC_fit.c
50 opus/silk/LPC_inv_pred_gain.c
51 opus/silk/LP_variable_cutoff.c
52 opus/silk/NLSF2A.c
53 opus/silk/NLSF_VQ.c
54 opus/silk/NLSF_VQ_weights_laroia.c
55 opus/silk/NLSF_decode.c
56 opus/silk/NLSF_del_dec_quant.c
57 opus/silk/NLSF_encode.c
58 opus/silk/NLSF_stabilize.c
59 opus/silk/NLSF_unpack.c
60 opus/silk/NSQ.c
61 opus/silk/NSQ_del_dec.c
62 opus/silk/PLC.c
63 opus/silk/VAD.c
64 opus/silk/VQ_WMat_EC.c
65 opus/silk/ana_filt_bank_1.c
66 opus/silk/biquad_alt.c
67 opus/silk/bwexpander.c
68 opus/silk/bwexpander_32.c
69 opus/silk/check_control_input.c
70 opus/silk/code_signs.c
71 opus/silk/control_SNR.c
72 opus/silk/control_audio_bandwidth.c
73 opus/silk/control_codec.c
74 opus/silk/dec_API.c
75 opus/silk/decode_core.c
76 opus/silk/decode_frame.c
77 opus/silk/decode_indices.c
78 opus/silk/decode_parameters.c
79 opus/silk/decode_pitch.c
80 opus/silk/decode_pulses.c
81 opus/silk/decoder_set_fs.c
82 opus/silk/enc_API.c
83 opus/silk/encode_indices.c
84 opus/silk/encode_pulses.c
85 opus/silk/gain_quant.c
86 opus/silk/init_decoder.c
87 opus/silk/init_encoder.c
88 opus/silk/inner_prod_aligned.c
89 opus/silk/interpolate.c
90 opus/silk/lin2log.c
91 opus/silk/log2lin.c
92 opus/silk/pitch_est_tables.c
93 opus/silk/process_NLSFs.c
94 opus/silk/quant_LTP_gains.c
95 opus/silk/resampler.c
96 opus/silk/resampler_down2.c
97 opus/silk/resampler_down2_3.c
98 opus/silk/resampler_private_AR2.c
99 opus/silk/resampler_private_IIR_FIR.c
100 opus/silk/resampler_private_down_FIR.c
101 opus/silk/resampler_private_up2_HQ.c
102 opus/silk/resampler_rom.c
103 opus/silk/shell_coder.c
104 opus/silk/sigm_Q15.c
105 opus/silk/sort.c
106 opus/silk/stereo_LR_to_MS.c
107 opus/silk/stereo_MS_to_LR.c
108 opus/silk/stereo_decode_pred.c
109 opus/silk/stereo_encode_pred.c
110 opus/silk/stereo_find_predictor.c
111 opus/silk/stereo_quant_pred.c
112 opus/silk/sum_sqr_shift.c
113 opus/silk/table_LSF_cos.c
114 opus/silk/tables_LTP.c
115 opus/silk/tables_NLSF_CB_NB_MB.c
116 opus/silk/tables_NLSF_CB_WB.c
117 opus/silk/tables_gain.c
118 opus/silk/tables_other.c
119 opus/silk/tables_pitch_lag.c
120 opus/silk/tables_pulses_per_block.c
121
122 # Opus sources
123 opus/src/analysis.c
124 opus/src/mapping_matrix.c
125 opus/src/mlp.c
126 opus/src/mlp_data.c
127 opus/src/opus.c
128 opus/src/opus_decoder.c
129 opus/src/opus_encoder.c
130 opus/src/opus_multistream.c
131 opus/src/opus_multistream_decoder.c
132 opus/src/opus_multistream_encoder.c
133 opus/src/opus_projection_decoder.c
134 opus/src/opus_projection_encoder.c
135 opus/src/repacketizer.c
136)
137
138if (DEBUG)
139 target_sources(opus PRIVATE opus/silk/debug.c)
140endif()
141
142if (OPUS_FIXED_POINT)
143 target_sources(opus PRIVATE
144 opus/silk/fixed/LTP_analysis_filter_FIX.c
145 opus/silk/fixed/LTP_scale_ctrl_FIX.c
146 opus/silk/fixed/apply_sine_window_FIX.c
147 opus/silk/fixed/autocorr_FIX.c
148 opus/silk/fixed/burg_modified_FIX.c
149 opus/silk/fixed/corrMatrix_FIX.c
150 opus/silk/fixed/encode_frame_FIX.c
151 opus/silk/fixed/find_LPC_FIX.c
152 opus/silk/fixed/find_LTP_FIX.c
153 opus/silk/fixed/find_pitch_lags_FIX.c
154 opus/silk/fixed/find_pred_coefs_FIX.c
155 opus/silk/fixed/k2a_FIX.c
156 opus/silk/fixed/k2a_Q16_FIX.c
157 opus/silk/fixed/noise_shape_analysis_FIX.c
158 opus/silk/fixed/pitch_analysis_core_FIX.c
159 opus/silk/fixed/prefilter_FIX.c
160 opus/silk/fixed/process_gains_FIX.c
161 opus/silk/fixed/regularize_correlations_FIX.c
162 opus/silk/fixed/residual_energy16_FIX.c
163 opus/silk/fixed/residual_energy_FIX.c
164 opus/silk/fixed/schur64_FIX.c
165 opus/silk/fixed/schur_FIX.c
166 opus/silk/fixed/solve_LS_FIX.c
167 opus/silk/fixed/vector_ops_FIX.c
168 opus/silk/fixed/warped_autocorrelation_FIX.c
169 )
170else()
171 target_sources(opus PRIVATE
172 opus/silk/float/LPC_analysis_filter_FLP.c
173 opus/silk/float/LPC_inv_pred_gain_FLP.c
174 opus/silk/float/LTP_analysis_filter_FLP.c
175 opus/silk/float/LTP_scale_ctrl_FLP.c
176 opus/silk/float/apply_sine_window_FLP.c
177 opus/silk/float/autocorrelation_FLP.c
178 opus/silk/float/burg_modified_FLP.c
179 opus/silk/float/bwexpander_FLP.c
180 opus/silk/float/corrMatrix_FLP.c
181 opus/silk/float/encode_frame_FLP.c
182 opus/silk/float/energy_FLP.c
183 opus/silk/float/find_LPC_FLP.c
184 opus/silk/float/find_LTP_FLP.c
185 opus/silk/float/find_pitch_lags_FLP.c
186 opus/silk/float/find_pred_coefs_FLP.c
187 opus/silk/float/inner_product_FLP.c
188 opus/silk/float/k2a_FLP.c
189 opus/silk/float/noise_shape_analysis_FLP.c
190 opus/silk/float/pitch_analysis_core_FLP.c
191 opus/silk/float/process_gains_FLP.c
192 opus/silk/float/regularize_correlations_FLP.c
193 opus/silk/float/residual_energy_FLP.c
194 opus/silk/float/scale_copy_vector_FLP.c
195 opus/silk/float/scale_vector_FLP.c
196 opus/silk/float/schur_FLP.c
197 opus/silk/float/sort_FLP.c
198 opus/silk/float/warped_autocorrelation_FLP.c
199 opus/silk/float/wrappers_FLP.c
200 )
201endif()
202
203target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING)
204
205if(NOT MSVC)
206 if(MINGW)
207 target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=0)
208 else()
209 target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=2)
210 endif()
211endif()
212
213# It is strongly recommended to uncomment one of these VAR_ARRAYS: Use C99
214# variable-length arrays for stack allocation USE_ALLOCA: Use alloca() for stack
215# allocation If none is defined, then the fallback is a non-threadsafe global
216# array
217if(OPUS_USE_ALLOCA OR MSVC)
218 target_compile_definitions(opus PRIVATE USE_ALLOCA)
219else()
220 target_compile_definitions(opus PRIVATE VAR_ARRAYS)
221endif()
222
223if(OPUS_CUSTOM_MODES)
224 target_compile_definitions(opus PRIVATE CUSTOM_MODES)
225endif()
226
227if(NOT OPUS_ENABLE_FLOAT_API)
228 target_compile_definitions(opus PRIVATE DISABLE_FLOAT_API)
229endif()
230
231target_compile_definitions(opus
232PUBLIC
233 -DOPUS_VERSION="\\"1.3.1\\""
234
235PRIVATE
236 # Use C99 intrinsics to speed up float-to-int conversion
237 HAVE_LRINTF
238)
239
240if (FIXED_POINT)
241 target_compile_definitions(opus PRIVATE -DFIXED_POINT=1 -DDISABLE_FLOAT_API)
242endif()
243
244target_include_directories(opus
245PUBLIC
246 opus/include
247
248PRIVATE
249 opus/celt
250 opus/silk
251 opus/silk/fixed
252 opus/silk/float
253 opus/src
254)
diff --git a/externals/opus/opus b/externals/opus/opus
new file mode 160000
Subproject ad8fe90db79b7d2a135e3dfd2ed6631b0c5662a
diff --git a/externals/sirit b/externals/sirit
Subproject a62c5bbc100a5e5a31ea0ccc4a78d8fa6a4167c Subproject eefca56afd49379bdebc97ded8b480839f93088
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3a57356ab..1e977e8a8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -62,6 +62,10 @@ else()
62 -Wno-unused-parameter 62 -Wno-unused-parameter
63 ) 63 )
64 64
65 if (ARCHITECTURE_x86_64)
66 add_compile_options("-mcx16")
67 endif()
68
65 if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) 69 if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
66 add_compile_options("-stdlib=libc++") 70 add_compile_options("-stdlib=libc++")
67 endif() 71 endif()
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 50846a854..d64452617 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -180,11 +180,12 @@ ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector<
180 180
181 // Copy output header 181 // Copy output header
182 UpdateDataHeader response_data{worker_params}; 182 UpdateDataHeader response_data{worker_params};
183 std::vector<u8> output_params(response_data.total_size);
184 if (behavior_info.IsElapsedFrameCountSupported()) { 183 if (behavior_info.IsElapsedFrameCountSupported()) {
185 response_data.frame_count = 0x10; 184 response_data.render_info = sizeof(RendererInfo);
186 response_data.total_size += 0x10; 185 response_data.total_size += sizeof(RendererInfo);
187 } 186 }
187
188 std::vector<u8> output_params(response_data.total_size);
188 std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader)); 189 std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
189 190
190 // Copy output memory pool entries 191 // Copy output memory pool entries
@@ -219,6 +220,17 @@ ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector<
219 return Audren::ERR_INVALID_PARAMETERS; 220 return Audren::ERR_INVALID_PARAMETERS;
220 } 221 }
221 222
223 if (behavior_info.IsElapsedFrameCountSupported()) {
224 const std::size_t renderer_info_offset{
225 sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size +
226 response_data.effects_size + response_data.sinks_size +
227 response_data.performance_manager_size + response_data.behavior_size};
228 RendererInfo renderer_info{};
229 renderer_info.elasped_frame_count = elapsed_frame_count;
230 std::memcpy(output_params.data() + renderer_info_offset, &renderer_info,
231 sizeof(RendererInfo));
232 }
233
222 return MakeResult(output_params); 234 return MakeResult(output_params);
223} 235}
224 236
@@ -447,6 +459,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
447 } 459 }
448 } 460 }
449 audio_out->QueueBuffer(stream, tag, std::move(buffer)); 461 audio_out->QueueBuffer(stream, tag, std::move(buffer));
462 elapsed_frame_count++;
450} 463}
451 464
452void AudioRenderer::ReleaseAndQueueBuffers() { 465void AudioRenderer::ReleaseAndQueueBuffers() {
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 1f9114c07..f0b691a86 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -196,6 +196,12 @@ struct EffectOutStatus {
196}; 196};
197static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size"); 197static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size");
198 198
199struct RendererInfo {
200 u64_le elasped_frame_count{};
201 INSERT_PADDING_WORDS(2);
202};
203static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
204
199struct UpdateDataHeader { 205struct UpdateDataHeader {
200 UpdateDataHeader() {} 206 UpdateDataHeader() {}
201 207
@@ -209,7 +215,7 @@ struct UpdateDataHeader {
209 mixes_size = 0x0; 215 mixes_size = 0x0;
210 sinks_size = config.sink_count * 0x20; 216 sinks_size = config.sink_count * 0x20;
211 performance_manager_size = 0x10; 217 performance_manager_size = 0x10;
212 frame_count = 0; 218 render_info = 0;
213 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size + 219 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
214 effects_size + sinks_size + performance_manager_size; 220 effects_size + sinks_size + performance_manager_size;
215 } 221 }
@@ -223,8 +229,8 @@ struct UpdateDataHeader {
223 u32_le mixes_size{}; 229 u32_le mixes_size{};
224 u32_le sinks_size{}; 230 u32_le sinks_size{};
225 u32_le performance_manager_size{}; 231 u32_le performance_manager_size{};
226 INSERT_PADDING_WORDS(1); 232 u32_le splitter_size{};
227 u32_le frame_count{}; 233 u32_le render_info{};
228 INSERT_PADDING_WORDS(4); 234 INSERT_PADDING_WORDS(4);
229 u32_le total_size{}; 235 u32_le total_size{};
230}; 236};
@@ -258,6 +264,7 @@ private:
258 std::unique_ptr<AudioOut> audio_out; 264 std::unique_ptr<AudioOut> audio_out;
259 StreamPtr stream; 265 StreamPtr stream;
260 Core::Memory::Memory& memory; 266 Core::Memory::Memory& memory;
267 std::size_t elapsed_frame_count{};
261}; 268};
262 269
263} // namespace AudioCore 270} // namespace AudioCore
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index c4e0e30fe..41bf5cd4d 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -193,7 +193,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
193 const std::size_t samples_to_write = num_channels * num_frames; 193 const std::size_t samples_to_write = num_channels * num_frames;
194 std::size_t samples_written; 194 std::size_t samples_written;
195 195
196 if (Settings::values.enable_audio_stretching) { 196 if (Settings::values.enable_audio_stretching.GetValue()) {
197 const std::vector<s16> in{impl->queue.Pop()}; 197 const std::vector<s16> in{impl->queue.Pop()};
198 const std::size_t num_in{in.size() / num_channels}; 198 const std::size_t num_in{in.size() / num_channels};
199 s16* const out{reinterpret_cast<s16*>(buffer)}; 199 s16* const out{reinterpret_cast<s16*>(buffer)};
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 4ca98f8ea..aab3e979a 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -38,7 +38,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo
38 sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { 38 sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
39 39
40 release_event = Core::Timing::CreateEvent( 40 release_event = Core::Timing::CreateEvent(
41 name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); }); 41 name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(cycles_late); });
42} 42}
43 43
44void Stream::Play() { 44void Stream::Play() {
@@ -59,15 +59,15 @@ Stream::State Stream::GetState() const {
59 return state; 59 return state;
60} 60}
61 61
62s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { 62s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const {
63 const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; 63 const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
64 const auto us = 64 const auto ns =
65 std::chrono::microseconds((static_cast<u64>(num_samples) * 1000000) / sample_rate); 65 std::chrono::nanoseconds((static_cast<u64>(num_samples) * 1000000000ULL) / sample_rate);
66 return Core::Timing::usToCycles(us); 66 return ns.count();
67} 67}
68 68
69static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) { 69static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
70 const float volume{std::clamp(Settings::values.volume - (1.0f - game_volume), 0.0f, 1.0f)}; 70 const float volume{std::clamp(Settings::Volume() - (1.0f - game_volume), 0.0f, 1.0f)};
71 71
72 if (volume == 1.0f) { 72 if (volume == 1.0f) {
73 return; 73 return;
@@ -80,7 +80,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
80 } 80 }
81} 81}
82 82
83void Stream::PlayNextBuffer() { 83void Stream::PlayNextBuffer(s64 cycles_late) {
84 if (!IsPlaying()) { 84 if (!IsPlaying()) {
85 // Ensure we are in playing state before playing the next buffer 85 // Ensure we are in playing state before playing the next buffer
86 sink_stream.Flush(); 86 sink_stream.Flush();
@@ -105,14 +105,17 @@ void Stream::PlayNextBuffer() {
105 105
106 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); 106 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
107 107
108 core_timing.ScheduleEvent(GetBufferReleaseCycles(*active_buffer), release_event, {}); 108 core_timing.ScheduleEvent(
109 GetBufferReleaseNS(*active_buffer) -
110 (Settings::values.enable_audio_stretching.GetValue() ? 0 : cycles_late),
111 release_event, {});
109} 112}
110 113
111void Stream::ReleaseActiveBuffer() { 114void Stream::ReleaseActiveBuffer(s64 cycles_late) {
112 ASSERT(active_buffer); 115 ASSERT(active_buffer);
113 released_buffers.push(std::move(active_buffer)); 116 released_buffers.push(std::move(active_buffer));
114 release_callback(); 117 release_callback();
115 PlayNextBuffer(); 118 PlayNextBuffer(cycles_late);
116} 119}
117 120
118bool Stream::QueueBuffer(BufferPtr&& buffer) { 121bool Stream::QueueBuffer(BufferPtr&& buffer) {
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index 1708a4d98..524376257 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -90,13 +90,16 @@ public:
90 90
91private: 91private:
92 /// Plays the next queued buffer in the audio stream, starting playback if necessary 92 /// Plays the next queued buffer in the audio stream, starting playback if necessary
93 void PlayNextBuffer(); 93 void PlayNextBuffer(s64 cycles_late = 0);
94 94
95 /// Releases the actively playing buffer, signalling that it has been completed 95 /// Releases the actively playing buffer, signalling that it has been completed
96 void ReleaseActiveBuffer(); 96 void ReleaseActiveBuffer(s64 cycles_late = 0);
97 97
98 /// Gets the number of core cycles when the specified buffer will be released 98 /// Gets the number of core cycles when the specified buffer will be released
99 s64 GetBufferReleaseCycles(const Buffer& buffer) const; 99 s64 GetBufferReleaseNS(const Buffer& buffer) const;
100
101 /// Gets the number of core cycles when the specified buffer will be released
102 s64 GetBufferReleaseNSHostTiming(const Buffer& buffer) const;
100 103
101 u32 sample_rate; ///< Sample rate of the stream 104 u32 sample_rate; ///< Sample rate of the stream
102 Format format; ///< Format of the stream 105 Format format; ///< Format of the stream
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 24b7a083c..d120c8d3d 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -32,6 +32,8 @@ add_custom_command(OUTPUT scm_rev.cpp
32 DEPENDS 32 DEPENDS
33 # WARNING! It was too much work to try and make a common location for this list, 33 # WARNING! It was too much work to try and make a common location for this list,
34 # so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well 34 # so if you need to change it, please update CMakeModules/GenerateSCMRev.cmake as well
35 "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.cpp"
36 "${VIDEO_CORE}/renderer_opengl/gl_arb_decompiler.h"
35 "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp" 37 "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.cpp"
36 "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h" 38 "${VIDEO_CORE}/renderer_opengl/gl_shader_cache.h"
37 "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp" 39 "${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.cpp"
@@ -96,6 +98,8 @@ add_library(common STATIC
96 algorithm.h 98 algorithm.h
97 alignment.h 99 alignment.h
98 assert.h 100 assert.h
101 atomic_ops.cpp
102 atomic_ops.h
99 detached_tasks.cpp 103 detached_tasks.cpp
100 detached_tasks.h 104 detached_tasks.h
101 bit_field.h 105 bit_field.h
@@ -108,6 +112,8 @@ add_library(common STATIC
108 common_types.h 112 common_types.h
109 dynamic_library.cpp 113 dynamic_library.cpp
110 dynamic_library.h 114 dynamic_library.h
115 fiber.cpp
116 fiber.h
111 file_util.cpp 117 file_util.cpp
112 file_util.h 118 file_util.h
113 hash.h 119 hash.h
@@ -141,6 +147,8 @@ add_library(common STATIC
141 scm_rev.cpp 147 scm_rev.cpp
142 scm_rev.h 148 scm_rev.h
143 scope_exit.h 149 scope_exit.h
150 spin_lock.cpp
151 spin_lock.h
144 string_util.cpp 152 string_util.cpp
145 string_util.h 153 string_util.h
146 swap.h 154 swap.h
@@ -161,6 +169,8 @@ add_library(common STATIC
161 vector_math.h 169 vector_math.h
162 virtual_buffer.cpp 170 virtual_buffer.cpp
163 virtual_buffer.h 171 virtual_buffer.h
172 wall_clock.cpp
173 wall_clock.h
164 web_result.h 174 web_result.h
165 zstd_compression.cpp 175 zstd_compression.cpp
166 zstd_compression.h 176 zstd_compression.h
@@ -171,12 +181,15 @@ if(ARCHITECTURE_x86_64)
171 PRIVATE 181 PRIVATE
172 x64/cpu_detect.cpp 182 x64/cpu_detect.cpp
173 x64/cpu_detect.h 183 x64/cpu_detect.h
184 x64/native_clock.cpp
185 x64/native_clock.h
174 x64/xbyak_abi.h 186 x64/xbyak_abi.h
175 x64/xbyak_util.h 187 x64/xbyak_util.h
176 ) 188 )
177endif() 189endif()
178 190
179create_target_directory_groups(common) 191create_target_directory_groups(common)
192find_package(Boost 1.71 COMPONENTS context headers REQUIRED)
180 193
181target_link_libraries(common PUBLIC Boost::boost fmt::fmt microprofile) 194target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile)
182target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak) 195target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak)
diff --git a/src/common/atomic_ops.cpp b/src/common/atomic_ops.cpp
new file mode 100644
index 000000000..1098e21ff
--- /dev/null
+++ b/src/common/atomic_ops.cpp
@@ -0,0 +1,70 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6
7#include "common/atomic_ops.h"
8
9#if _MSC_VER
10#include <intrin.h>
11#endif
12
13namespace Common {
14
15#if _MSC_VER
16
17bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) {
18 u8 result = _InterlockedCompareExchange8((char*)pointer, value, expected);
19 return result == expected;
20}
21
22bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) {
23 u16 result = _InterlockedCompareExchange16((short*)pointer, value, expected);
24 return result == expected;
25}
26
27bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) {
28 u32 result = _InterlockedCompareExchange((long*)pointer, value, expected);
29 return result == expected;
30}
31
32bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) {
33 u64 result = _InterlockedCompareExchange64((__int64*)pointer, value, expected);
34 return result == expected;
35}
36
37bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) {
38 return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0],
39 (__int64*)expected.data()) != 0;
40}
41
42#else
43
44bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) {
45 return __sync_bool_compare_and_swap(pointer, expected, value);
46}
47
48bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) {
49 return __sync_bool_compare_and_swap(pointer, expected, value);
50}
51
52bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) {
53 return __sync_bool_compare_and_swap(pointer, expected, value);
54}
55
56bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) {
57 return __sync_bool_compare_and_swap(pointer, expected, value);
58}
59
60bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) {
61 unsigned __int128 value_a;
62 unsigned __int128 expected_a;
63 std::memcpy(&value_a, value.data(), sizeof(u128));
64 std::memcpy(&expected_a, expected.data(), sizeof(u128));
65 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
66}
67
68#endif
69
70} // namespace Common
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h
new file mode 100644
index 000000000..e6181d521
--- /dev/null
+++ b/src/common/atomic_ops.h
@@ -0,0 +1,17 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace Common {
10
11bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected);
12bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected);
13bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected);
14bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected);
15bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected);
16
17} // namespace Common
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
new file mode 100644
index 000000000..1c1d09ccb
--- /dev/null
+++ b/src/common/fiber.cpp
@@ -0,0 +1,222 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/fiber.h"
7#if defined(_WIN32) || defined(WIN32)
8#include <windows.h>
9#else
10#include <boost/context/detail/fcontext.hpp>
11#endif
12
13namespace Common {
14
15constexpr std::size_t default_stack_size = 256 * 1024; // 256kb
16
17#if defined(_WIN32) || defined(WIN32)
18
19struct Fiber::FiberImpl {
20 LPVOID handle = nullptr;
21 LPVOID rewind_handle = nullptr;
22};
23
24void Fiber::Start() {
25 ASSERT(previous_fiber != nullptr);
26 previous_fiber->guard.unlock();
27 previous_fiber.reset();
28 entry_point(start_parameter);
29 UNREACHABLE();
30}
31
32void Fiber::OnRewind() {
33 ASSERT(impl->handle != nullptr);
34 DeleteFiber(impl->handle);
35 impl->handle = impl->rewind_handle;
36 impl->rewind_handle = nullptr;
37 rewind_point(rewind_parameter);
38 UNREACHABLE();
39}
40
41void Fiber::FiberStartFunc(void* fiber_parameter) {
42 auto fiber = static_cast<Fiber*>(fiber_parameter);
43 fiber->Start();
44}
45
46void Fiber::RewindStartFunc(void* fiber_parameter) {
47 auto fiber = static_cast<Fiber*>(fiber_parameter);
48 fiber->OnRewind();
49}
50
51Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
52 : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} {
53 impl = std::make_unique<FiberImpl>();
54 impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
55}
56
57Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
58
59Fiber::~Fiber() {
60 if (released) {
61 return;
62 }
63 // Make sure the Fiber is not being used
64 const bool locked = guard.try_lock();
65 ASSERT_MSG(locked, "Destroying a fiber that's still running");
66 if (locked) {
67 guard.unlock();
68 }
69 DeleteFiber(impl->handle);
70}
71
72void Fiber::Exit() {
73 ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
74 if (!is_thread_fiber) {
75 return;
76 }
77 ConvertFiberToThread();
78 guard.unlock();
79 released = true;
80}
81
82void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) {
83 rewind_point = std::move(rewind_func);
84 rewind_parameter = start_parameter;
85}
86
87void Fiber::Rewind() {
88 ASSERT(rewind_point);
89 ASSERT(impl->rewind_handle == nullptr);
90 impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
91 SwitchToFiber(impl->rewind_handle);
92}
93
94void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) {
95 ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
96 ASSERT_MSG(to != nullptr, "Next fiber is null!");
97 to->guard.lock();
98 to->previous_fiber = from;
99 SwitchToFiber(to->impl->handle);
100 ASSERT(from->previous_fiber != nullptr);
101 from->previous_fiber->guard.unlock();
102 from->previous_fiber.reset();
103}
104
105std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
106 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
107 fiber->guard.lock();
108 fiber->impl->handle = ConvertThreadToFiber(nullptr);
109 fiber->is_thread_fiber = true;
110 return fiber;
111}
112
113#else
114
115struct Fiber::FiberImpl {
116 alignas(64) std::array<u8, default_stack_size> stack;
117 alignas(64) std::array<u8, default_stack_size> rewind_stack;
118 u8* stack_limit;
119 u8* rewind_stack_limit;
120 boost::context::detail::fcontext_t context;
121 boost::context::detail::fcontext_t rewind_context;
122};
123
124void Fiber::Start(boost::context::detail::transfer_t& transfer) {
125 ASSERT(previous_fiber != nullptr);
126 previous_fiber->impl->context = transfer.fctx;
127 previous_fiber->guard.unlock();
128 previous_fiber.reset();
129 entry_point(start_parameter);
130 UNREACHABLE();
131}
132
133void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transfer) {
134 ASSERT(impl->context != nullptr);
135 impl->context = impl->rewind_context;
136 impl->rewind_context = nullptr;
137 u8* tmp = impl->stack_limit;
138 impl->stack_limit = impl->rewind_stack_limit;
139 impl->rewind_stack_limit = tmp;
140 rewind_point(rewind_parameter);
141 UNREACHABLE();
142}
143
144void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
145 auto fiber = static_cast<Fiber*>(transfer.data);
146 fiber->Start(transfer);
147}
148
149void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
150 auto fiber = static_cast<Fiber*>(transfer.data);
151 fiber->OnRewind(transfer);
152}
153
154Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
155 : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} {
156 impl = std::make_unique<FiberImpl>();
157 impl->stack_limit = impl->stack.data();
158 impl->rewind_stack_limit = impl->rewind_stack.data();
159 u8* stack_base = impl->stack_limit + default_stack_size;
160 impl->context =
161 boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
162}
163
164void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) {
165 rewind_point = std::move(rewind_func);
166 rewind_parameter = start_parameter;
167}
168
169Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
170
171Fiber::~Fiber() {
172 if (released) {
173 return;
174 }
175 // Make sure the Fiber is not being used
176 const bool locked = guard.try_lock();
177 ASSERT_MSG(locked, "Destroying a fiber that's still running");
178 if (locked) {
179 guard.unlock();
180 }
181}
182
183void Fiber::Exit() {
184
185 ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
186 if (!is_thread_fiber) {
187 return;
188 }
189 guard.unlock();
190 released = true;
191}
192
193void Fiber::Rewind() {
194 ASSERT(rewind_point);
195 ASSERT(impl->rewind_context == nullptr);
196 u8* stack_base = impl->rewind_stack_limit + default_stack_size;
197 impl->rewind_context =
198 boost::context::detail::make_fcontext(stack_base, impl->stack.size(), RewindStartFunc);
199 boost::context::detail::jump_fcontext(impl->rewind_context, this);
200}
201
202void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) {
203 ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
204 ASSERT_MSG(to != nullptr, "Next fiber is null!");
205 to->guard.lock();
206 to->previous_fiber = from;
207 auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
208 ASSERT(from->previous_fiber != nullptr);
209 from->previous_fiber->impl->context = transfer.fctx;
210 from->previous_fiber->guard.unlock();
211 from->previous_fiber.reset();
212}
213
214std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
215 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
216 fiber->guard.lock();
217 fiber->is_thread_fiber = true;
218 return fiber;
219}
220
221#endif
222} // namespace Common
diff --git a/src/common/fiber.h b/src/common/fiber.h
new file mode 100644
index 000000000..dafc1100e
--- /dev/null
+++ b/src/common/fiber.h
@@ -0,0 +1,92 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <memory>
9
10#include "common/common_types.h"
11#include "common/spin_lock.h"
12
13#if !defined(_WIN32) && !defined(WIN32)
14namespace boost::context::detail {
15struct transfer_t;
16}
17#endif
18
19namespace Common {
20
21/**
22 * Fiber class
23 * a fiber is a userspace thread with it's own context. They can be used to
24 * implement coroutines, emulated threading systems and certain asynchronous
25 * patterns.
26 *
27 * This class implements fibers at a low level, thus allowing greater freedom
28 * to implement such patterns. This fiber class is 'threadsafe' only one fiber
29 * can be running at a time and threads will be locked while trying to yield to
30 * a running fiber until it yields. WARNING exchanging two running fibers between
31 * threads will cause a deadlock. In order to prevent a deadlock, each thread should
32 * have an intermediary fiber, you switch to the intermediary fiber of the current
33 * thread and then from it switch to the expected fiber. This way you can exchange
34 * 2 fibers within 2 different threads.
35 */
36class Fiber {
37public:
38 Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter);
39 ~Fiber();
40
41 Fiber(const Fiber&) = delete;
42 Fiber& operator=(const Fiber&) = delete;
43
44 Fiber(Fiber&&) = default;
45 Fiber& operator=(Fiber&&) = default;
46
47 /// Yields control from Fiber 'from' to Fiber 'to'
48 /// Fiber 'from' must be the currently running fiber.
49 static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to);
50 static std::shared_ptr<Fiber> ThreadToFiber();
51
52 void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter);
53
54 void Rewind();
55
56 /// Only call from main thread's fiber
57 void Exit();
58
59 /// Changes the start parameter of the fiber. Has no effect if the fiber already started
60 void SetStartParameter(void* new_parameter) {
61 start_parameter = new_parameter;
62 }
63
64private:
65 Fiber();
66
67#if defined(_WIN32) || defined(WIN32)
68 void OnRewind();
69 void Start();
70 static void FiberStartFunc(void* fiber_parameter);
71 static void RewindStartFunc(void* fiber_parameter);
72#else
73 void OnRewind(boost::context::detail::transfer_t& transfer);
74 void Start(boost::context::detail::transfer_t& transfer);
75 static void FiberStartFunc(boost::context::detail::transfer_t transfer);
76 static void RewindStartFunc(boost::context::detail::transfer_t transfer);
77#endif
78
79 struct FiberImpl;
80
81 SpinLock guard{};
82 std::function<void(void*)> entry_point;
83 std::function<void(void*)> rewind_point;
84 void* rewind_parameter{};
85 void* start_parameter{};
86 std::shared_ptr<Fiber> previous_fiber;
87 std::unique_ptr<FiberImpl> impl;
88 bool is_thread_fiber{};
89 bool released{};
90};
91
92} // namespace Common
diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp
index 3fdc309a2..8cff6ec37 100644
--- a/src/common/memory_detect.cpp
+++ b/src/common/memory_detect.cpp
@@ -9,10 +9,12 @@
9// clang-format on 9// clang-format on
10#else 10#else
11#include <sys/types.h> 11#include <sys/types.h>
12#ifdef __APPLE__ 12#if defined(__APPLE__) || defined(__FreeBSD__)
13#include <sys/sysctl.h> 13#include <sys/sysctl.h>
14#else 14#elif defined(__linux__)
15#include <sys/sysinfo.h> 15#include <sys/sysinfo.h>
16#else
17#include <unistd.h>
16#endif 18#endif
17#endif 19#endif
18 20
@@ -38,15 +40,26 @@ static MemoryInfo Detect() {
38 // hw and vm are defined in sysctl.h 40 // hw and vm are defined in sysctl.h
39 // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471 41 // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471
40 // sysctlbyname(const char *, void *, size_t *, void *, size_t); 42 // sysctlbyname(const char *, void *, size_t *, void *, size_t);
41 sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, NULL, 0); 43 sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, nullptr, 0);
42 sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, NULL, 0); 44 sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, nullptr, 0);
43 mem_info.TotalPhysicalMemory = ramsize; 45 mem_info.TotalPhysicalMemory = ramsize;
44 mem_info.TotalSwapMemory = vmusage.xsu_total; 46 mem_info.TotalSwapMemory = vmusage.xsu_total;
45#else 47#elif defined(__FreeBSD__)
48 u_long physmem, swap_total;
49 std::size_t sizeof_u_long = sizeof(u_long);
50 // sysctlbyname(const char *, void *, size_t *, const void *, size_t);
51 sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, nullptr, 0);
52 sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, nullptr, 0);
53 mem_info.TotalPhysicalMemory = physmem;
54 mem_info.TotalSwapMemory = swap_total;
55#elif defined(__linux__)
46 struct sysinfo meminfo; 56 struct sysinfo meminfo;
47 sysinfo(&meminfo); 57 sysinfo(&meminfo);
48 mem_info.TotalPhysicalMemory = meminfo.totalram; 58 mem_info.TotalPhysicalMemory = meminfo.totalram;
49 mem_info.TotalSwapMemory = meminfo.totalswap; 59 mem_info.TotalSwapMemory = meminfo.totalswap;
60#else
61 mem_info.TotalPhysicalMemory = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE);
62 mem_info.TotalSwapMemory = 0;
50#endif 63#endif
51 64
52 return mem_info; 65 return mem_info;
diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp
new file mode 100644
index 000000000..c1524220f
--- /dev/null
+++ b/src/common/spin_lock.cpp
@@ -0,0 +1,54 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/spin_lock.h"
6
7#if _MSC_VER
8#include <intrin.h>
9#if _M_AMD64
10#define __x86_64__ 1
11#endif
12#if _M_ARM64
13#define __aarch64__ 1
14#endif
15#else
16#if __x86_64__
17#include <xmmintrin.h>
18#endif
19#endif
20
21namespace {
22
23void ThreadPause() {
24#if __x86_64__
25 _mm_pause();
26#elif __aarch64__ && _MSC_VER
27 __yield();
28#elif __aarch64__
29 asm("yield");
30#endif
31}
32
33} // Anonymous namespace
34
35namespace Common {
36
37void SpinLock::lock() {
38 while (lck.test_and_set(std::memory_order_acquire)) {
39 ThreadPause();
40 }
41}
42
43void SpinLock::unlock() {
44 lck.clear(std::memory_order_release);
45}
46
47bool SpinLock::try_lock() {
48 if (lck.test_and_set(std::memory_order_acquire)) {
49 return false;
50 }
51 return true;
52}
53
54} // namespace Common
diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h
new file mode 100644
index 000000000..1df5528c4
--- /dev/null
+++ b/src/common/spin_lock.h
@@ -0,0 +1,26 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8
9namespace Common {
10
11/**
12 * SpinLock class
13 * a lock similar to mutex that forces a thread to spin wait instead calling the
14 * supervisor. Should be used on short sequences of code.
15 */
16class SpinLock {
17public:
18 void lock();
19 void unlock();
20 bool try_lock();
21
22private:
23 std::atomic_flag lck = ATOMIC_FLAG_INIT;
24};
25
26} // namespace Common
diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp
index 200c6489a..16d42facd 100644
--- a/src/common/telemetry.cpp
+++ b/src/common/telemetry.cpp
@@ -60,6 +60,7 @@ void AppendCPUInfo(FieldCollection& fc) {
60 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); 60 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes);
61 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); 61 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx);
62 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); 62 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2);
63 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512);
63 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); 64 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1);
64 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); 65 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2);
65 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); 66 fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma);
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 0cd2d10bf..8e5935e6a 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -25,6 +25,52 @@
25 25
26namespace Common { 26namespace Common {
27 27
28#ifdef _WIN32
29
30void SetCurrentThreadPriority(ThreadPriority new_priority) {
31 auto handle = GetCurrentThread();
32 int windows_priority = 0;
33 switch (new_priority) {
34 case ThreadPriority::Low:
35 windows_priority = THREAD_PRIORITY_BELOW_NORMAL;
36 break;
37 case ThreadPriority::Normal:
38 windows_priority = THREAD_PRIORITY_NORMAL;
39 break;
40 case ThreadPriority::High:
41 windows_priority = THREAD_PRIORITY_ABOVE_NORMAL;
42 break;
43 case ThreadPriority::VeryHigh:
44 windows_priority = THREAD_PRIORITY_HIGHEST;
45 break;
46 default:
47 windows_priority = THREAD_PRIORITY_NORMAL;
48 break;
49 }
50 SetThreadPriority(handle, windows_priority);
51}
52
53#else
54
55void SetCurrentThreadPriority(ThreadPriority new_priority) {
56 pthread_t this_thread = pthread_self();
57
58 s32 max_prio = sched_get_priority_max(SCHED_OTHER);
59 s32 min_prio = sched_get_priority_min(SCHED_OTHER);
60 u32 level = static_cast<u32>(new_priority) + 1;
61
62 struct sched_param params;
63 if (max_prio > min_prio) {
64 params.sched_priority = min_prio + ((max_prio - min_prio) * level) / 4;
65 } else {
66 params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4;
67 }
68
69 pthread_setschedparam(this_thread, SCHED_OTHER, &params);
70}
71
72#endif
73
28#ifdef _MSC_VER 74#ifdef _MSC_VER
29 75
30// Sets the debugger-visible name of the current thread. 76// Sets the debugger-visible name of the current thread.
@@ -70,6 +116,12 @@ void SetCurrentThreadName(const char* name) {
70} 116}
71#endif 117#endif
72 118
119#if defined(_WIN32)
120void SetCurrentThreadName(const char* name) {
121 // Do Nothing on MingW
122}
123#endif
124
73#endif 125#endif
74 126
75} // namespace Common 127} // namespace Common
diff --git a/src/common/thread.h b/src/common/thread.h
index 2fc071685..52b359413 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -9,6 +9,7 @@
9#include <cstddef> 9#include <cstddef>
10#include <mutex> 10#include <mutex>
11#include <thread> 11#include <thread>
12#include "common/common_types.h"
12 13
13namespace Common { 14namespace Common {
14 15
@@ -28,8 +29,7 @@ public:
28 is_set = false; 29 is_set = false;
29 } 30 }
30 31
31 template <class Duration> 32 bool WaitFor(const std::chrono::nanoseconds& time) {
32 bool WaitFor(const std::chrono::duration<Duration>& time) {
33 std::unique_lock lk{mutex}; 33 std::unique_lock lk{mutex};
34 if (!condvar.wait_for(lk, time, [this] { return is_set; })) 34 if (!condvar.wait_for(lk, time, [this] { return is_set; }))
35 return false; 35 return false;
@@ -86,6 +86,15 @@ private:
86 std::size_t generation = 0; // Incremented once each time the barrier is used 86 std::size_t generation = 0; // Incremented once each time the barrier is used
87}; 87};
88 88
89enum class ThreadPriority : u32 {
90 Low = 0,
91 Normal = 1,
92 High = 2,
93 VeryHigh = 3,
94};
95
96void SetCurrentThreadPriority(ThreadPriority new_priority);
97
89void SetCurrentThreadName(const char* name); 98void SetCurrentThreadName(const char* name);
90 99
91} // namespace Common 100} // namespace Common
diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp
index 32bf56730..16bf7c828 100644
--- a/src/common/uint128.cpp
+++ b/src/common/uint128.cpp
@@ -6,12 +6,38 @@
6#include <intrin.h> 6#include <intrin.h>
7 7
8#pragma intrinsic(_umul128) 8#pragma intrinsic(_umul128)
9#pragma intrinsic(_udiv128)
9#endif 10#endif
10#include <cstring> 11#include <cstring>
11#include "common/uint128.h" 12#include "common/uint128.h"
12 13
13namespace Common { 14namespace Common {
14 15
16#ifdef _MSC_VER
17
18u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
19 u128 r{};
20 r[0] = _umul128(a, b, &r[1]);
21 u64 remainder;
22#if _MSC_VER < 1923
23 return udiv128(r[1], r[0], d, &remainder);
24#else
25 return _udiv128(r[1], r[0], d, &remainder);
26#endif
27}
28
29#else
30
31u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
32 const u64 diva = a / d;
33 const u64 moda = a % d;
34 const u64 divb = b / d;
35 const u64 modb = b % d;
36 return diva * b + moda * divb + moda * modb / d;
37}
38
39#endif
40
15u128 Multiply64Into128(u64 a, u64 b) { 41u128 Multiply64Into128(u64 a, u64 b) {
16 u128 result; 42 u128 result;
17#ifdef _MSC_VER 43#ifdef _MSC_VER
diff --git a/src/common/uint128.h b/src/common/uint128.h
index a3be2a2cb..503cd2d0c 100644
--- a/src/common/uint128.h
+++ b/src/common/uint128.h
@@ -9,6 +9,9 @@
9 9
10namespace Common { 10namespace Common {
11 11
12// This function multiplies 2 u64 values and divides it by a u64 value.
13u64 MultiplyAndDivide64(u64 a, u64 b, u64 d);
14
12// This function multiplies 2 u64 values and produces a u128 value; 15// This function multiplies 2 u64 values and produces a u128 value;
13u128 Multiply64Into128(u64 a, u64 b); 16u128 Multiply64Into128(u64 a, u64 b);
14 17
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
new file mode 100644
index 000000000..3afbdb898
--- /dev/null
+++ b/src/common/wall_clock.cpp
@@ -0,0 +1,91 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/uint128.h"
6#include "common/wall_clock.h"
7
8#ifdef ARCHITECTURE_x86_64
9#include "common/x64/cpu_detect.h"
10#include "common/x64/native_clock.h"
11#endif
12
13namespace Common {
14
15using base_timer = std::chrono::steady_clock;
16using base_time_point = std::chrono::time_point<base_timer>;
17
18class StandardWallClock : public WallClock {
19public:
20 StandardWallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency)
21 : WallClock(emulated_cpu_frequency, emulated_clock_frequency, false) {
22 start_time = base_timer::now();
23 }
24
25 std::chrono::nanoseconds GetTimeNS() override {
26 base_time_point current = base_timer::now();
27 auto elapsed = current - start_time;
28 return std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed);
29 }
30
31 std::chrono::microseconds GetTimeUS() override {
32 base_time_point current = base_timer::now();
33 auto elapsed = current - start_time;
34 return std::chrono::duration_cast<std::chrono::microseconds>(elapsed);
35 }
36
37 std::chrono::milliseconds GetTimeMS() override {
38 base_time_point current = base_timer::now();
39 auto elapsed = current - start_time;
40 return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed);
41 }
42
43 u64 GetClockCycles() override {
44 std::chrono::nanoseconds time_now = GetTimeNS();
45 const u128 temporary =
46 Common::Multiply64Into128(time_now.count(), emulated_clock_frequency);
47 return Common::Divide128On32(temporary, 1000000000).first;
48 }
49
50 u64 GetCPUCycles() override {
51 std::chrono::nanoseconds time_now = GetTimeNS();
52 const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency);
53 return Common::Divide128On32(temporary, 1000000000).first;
54 }
55
56 void Pause(bool is_paused) override {
57 // Do nothing in this clock type.
58 }
59
60private:
61 base_time_point start_time;
62};
63
64#ifdef ARCHITECTURE_x86_64
65
66std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
67 u32 emulated_clock_frequency) {
68 const auto& caps = GetCPUCaps();
69 u64 rtsc_frequency = 0;
70 if (caps.invariant_tsc) {
71 rtsc_frequency = EstimateRDTSCFrequency();
72 }
73 if (rtsc_frequency == 0) {
74 return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
75 emulated_clock_frequency);
76 } else {
77 return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency,
78 rtsc_frequency);
79 }
80}
81
82#else
83
84std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
85 u32 emulated_clock_frequency) {
86 return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
87}
88
89#endif
90
91} // namespace Common
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
new file mode 100644
index 000000000..367d72134
--- /dev/null
+++ b/src/common/wall_clock.h
@@ -0,0 +1,53 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <chrono>
8#include <memory>
9
10#include "common/common_types.h"
11
12namespace Common {
13
14class WallClock {
15public:
16 /// Returns current wall time in nanoseconds
17 virtual std::chrono::nanoseconds GetTimeNS() = 0;
18
19 /// Returns current wall time in microseconds
20 virtual std::chrono::microseconds GetTimeUS() = 0;
21
22 /// Returns current wall time in milliseconds
23 virtual std::chrono::milliseconds GetTimeMS() = 0;
24
25 /// Returns current wall time in emulated clock cycles
26 virtual u64 GetClockCycles() = 0;
27
28 /// Returns current wall time in emulated cpu cycles
29 virtual u64 GetCPUCycles() = 0;
30
31 virtual void Pause(bool is_paused) = 0;
32
33 /// Tells if the wall clock, uses the host CPU's hardware clock
34 bool IsNative() const {
35 return is_native;
36 }
37
38protected:
39 WallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, bool is_native)
40 : emulated_cpu_frequency{emulated_cpu_frequency},
41 emulated_clock_frequency{emulated_clock_frequency}, is_native{is_native} {}
42
43 u64 emulated_cpu_frequency;
44 u64 emulated_clock_frequency;
45
46private:
47 bool is_native;
48};
49
50std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
51 u32 emulated_clock_frequency);
52
53} // namespace Common
diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp
index c9349a6b4..fccd2eee5 100644
--- a/src/common/x64/cpu_detect.cpp
+++ b/src/common/x64/cpu_detect.cpp
@@ -62,6 +62,17 @@ static CPUCaps Detect() {
62 std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int)); 62 std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
63 std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int)); 63 std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
64 std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int)); 64 std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
65 if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69)
66 caps.manufacturer = Manufacturer::Intel;
67 else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65)
68 caps.manufacturer = Manufacturer::AMD;
69 else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e)
70 caps.manufacturer = Manufacturer::Hygon;
71 else
72 caps.manufacturer = Manufacturer::Unknown;
73
74 u32 family = {};
75 u32 model = {};
65 76
66 __cpuid(cpu_id, 0x80000000); 77 __cpuid(cpu_id, 0x80000000);
67 78
@@ -73,6 +84,14 @@ static CPUCaps Detect() {
73 // Detect family and other miscellaneous features 84 // Detect family and other miscellaneous features
74 if (max_std_fn >= 1) { 85 if (max_std_fn >= 1) {
75 __cpuid(cpu_id, 0x00000001); 86 __cpuid(cpu_id, 0x00000001);
87 family = (cpu_id[0] >> 8) & 0xf;
88 model = (cpu_id[0] >> 4) & 0xf;
89 if (family == 0xf) {
90 family += (cpu_id[0] >> 20) & 0xff;
91 }
92 if (family >= 6) {
93 model += ((cpu_id[0] >> 16) & 0xf) << 4;
94 }
76 95
77 if ((cpu_id[3] >> 25) & 1) 96 if ((cpu_id[3] >> 25) & 1)
78 caps.sse = true; 97 caps.sse = true;
@@ -110,6 +129,11 @@ static CPUCaps Detect() {
110 caps.bmi1 = true; 129 caps.bmi1 = true;
111 if ((cpu_id[1] >> 8) & 1) 130 if ((cpu_id[1] >> 8) & 1)
112 caps.bmi2 = true; 131 caps.bmi2 = true;
132 // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP)
133 if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 &&
134 (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) {
135 caps.avx512 = caps.avx2;
136 }
113 } 137 }
114 } 138 }
115 139
@@ -130,6 +154,20 @@ static CPUCaps Detect() {
130 caps.fma4 = true; 154 caps.fma4 = true;
131 } 155 }
132 156
157 if (max_ex_fn >= 0x80000007) {
158 __cpuid(cpu_id, 0x80000007);
159 if (cpu_id[3] & (1 << 8)) {
160 caps.invariant_tsc = true;
161 }
162 }
163
164 if (max_std_fn >= 0x16) {
165 __cpuid(cpu_id, 0x16);
166 caps.base_frequency = cpu_id[0];
167 caps.max_frequency = cpu_id[1];
168 caps.bus_frequency = cpu_id[2];
169 }
170
133 return caps; 171 return caps;
134} 172}
135 173
diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h
index 20f2ba234..e3b63302e 100644
--- a/src/common/x64/cpu_detect.h
+++ b/src/common/x64/cpu_detect.h
@@ -6,8 +6,16 @@
6 6
7namespace Common { 7namespace Common {
8 8
9enum class Manufacturer : u32 {
10 Intel = 0,
11 AMD = 1,
12 Hygon = 2,
13 Unknown = 3,
14};
15
9/// x86/x64 CPU capabilities that may be detected by this module 16/// x86/x64 CPU capabilities that may be detected by this module
10struct CPUCaps { 17struct CPUCaps {
18 Manufacturer manufacturer;
11 char cpu_string[0x21]; 19 char cpu_string[0x21];
12 char brand_string[0x41]; 20 char brand_string[0x41];
13 bool sse; 21 bool sse;
@@ -19,11 +27,16 @@ struct CPUCaps {
19 bool lzcnt; 27 bool lzcnt;
20 bool avx; 28 bool avx;
21 bool avx2; 29 bool avx2;
30 bool avx512;
22 bool bmi1; 31 bool bmi1;
23 bool bmi2; 32 bool bmi2;
24 bool fma; 33 bool fma;
25 bool fma4; 34 bool fma4;
26 bool aes; 35 bool aes;
36 bool invariant_tsc;
37 u32 base_frequency;
38 u32 max_frequency;
39 u32 bus_frequency;
27}; 40};
28 41
29/** 42/**
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
new file mode 100644
index 000000000..424b39b1f
--- /dev/null
+++ b/src/common/x64/native_clock.cpp
@@ -0,0 +1,103 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <mutex>
7#include <thread>
8
9#ifdef _MSC_VER
10#include <intrin.h>
11#else
12#include <x86intrin.h>
13#endif
14
15#include "common/uint128.h"
16#include "common/x64/native_clock.h"
17
18namespace Common {
19
20u64 EstimateRDTSCFrequency() {
21 const auto milli_10 = std::chrono::milliseconds{10};
22 // get current time
23 _mm_mfence();
24 const u64 tscStart = __rdtsc();
25 const auto startTime = std::chrono::high_resolution_clock::now();
26 // wait roughly 3 seconds
27 while (true) {
28 auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(
29 std::chrono::high_resolution_clock::now() - startTime);
30 if (milli.count() >= 3000)
31 break;
32 std::this_thread::sleep_for(milli_10);
33 }
34 const auto endTime = std::chrono::high_resolution_clock::now();
35 _mm_mfence();
36 const u64 tscEnd = __rdtsc();
37 // calculate difference
38 const u64 timer_diff =
39 std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime).count();
40 const u64 tsc_diff = tscEnd - tscStart;
41 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
42 return tsc_freq;
43}
44
45namespace X64 {
46NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency,
47 u64 rtsc_frequency)
48 : WallClock(emulated_cpu_frequency, emulated_clock_frequency, true), rtsc_frequency{
49 rtsc_frequency} {
50 _mm_mfence();
51 last_measure = __rdtsc();
52 accumulated_ticks = 0U;
53}
54
55u64 NativeClock::GetRTSC() {
56 std::scoped_lock scope{rtsc_serialize};
57 _mm_mfence();
58 const u64 current_measure = __rdtsc();
59 u64 diff = current_measure - last_measure;
60 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
61 if (current_measure > last_measure) {
62 last_measure = current_measure;
63 }
64 accumulated_ticks += diff;
65 /// The clock cannot be more precise than the guest timer, remove the lower bits
66 return accumulated_ticks & inaccuracy_mask;
67}
68
69void NativeClock::Pause(bool is_paused) {
70 if (!is_paused) {
71 _mm_mfence();
72 last_measure = __rdtsc();
73 }
74}
75
76std::chrono::nanoseconds NativeClock::GetTimeNS() {
77 const u64 rtsc_value = GetRTSC();
78 return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)};
79}
80
81std::chrono::microseconds NativeClock::GetTimeUS() {
82 const u64 rtsc_value = GetRTSC();
83 return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)};
84}
85
86std::chrono::milliseconds NativeClock::GetTimeMS() {
87 const u64 rtsc_value = GetRTSC();
88 return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)};
89}
90
91u64 NativeClock::GetClockCycles() {
92 const u64 rtsc_value = GetRTSC();
93 return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency);
94}
95
96u64 NativeClock::GetCPUCycles() {
97 const u64 rtsc_value = GetRTSC();
98 return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency);
99}
100
101} // namespace X64
102
103} // namespace Common
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
new file mode 100644
index 000000000..891a3bbfd
--- /dev/null
+++ b/src/common/x64/native_clock.h
@@ -0,0 +1,48 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <optional>
8
9#include "common/spin_lock.h"
10#include "common/wall_clock.h"
11
12namespace Common {
13
14namespace X64 {
15class NativeClock : public WallClock {
16public:
17 NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, u64 rtsc_frequency);
18
19 std::chrono::nanoseconds GetTimeNS() override;
20
21 std::chrono::microseconds GetTimeUS() override;
22
23 std::chrono::milliseconds GetTimeMS() override;
24
25 u64 GetClockCycles() override;
26
27 u64 GetCPUCycles() override;
28
29 void Pause(bool is_paused) override;
30
31private:
32 u64 GetRTSC();
33
34 /// value used to reduce the native clocks accuracy as some apss rely on
35 /// undefined behavior where the level of accuracy in the clock shouldn't
36 /// be higher.
37 static constexpr u64 inaccuracy_mask = ~(0x400 - 1);
38
39 SpinLock rtsc_serialize{};
40 u64 last_measure{};
41 u64 accumulated_ticks{};
42 u64 rtsc_frequency;
43};
44} // namespace X64
45
46u64 EstimateRDTSCFrequency();
47
48} // namespace Common
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
index 794da8a52..a5f5d4fc1 100644
--- a/src/common/x64/xbyak_abi.h
+++ b/src/common/x64/xbyak_abi.h
@@ -11,7 +11,7 @@
11 11
12namespace Common::X64 { 12namespace Common::X64 {
13 13
14inline int RegToIndex(const Xbyak::Reg& reg) { 14inline std::size_t RegToIndex(const Xbyak::Reg& reg) {
15 using Kind = Xbyak::Reg::Kind; 15 using Kind = Xbyak::Reg::Kind;
16 ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0, 16 ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
17 "RegSet only support GPRs and XMM registers."); 17 "RegSet only support GPRs and XMM registers.");
@@ -19,17 +19,17 @@ inline int RegToIndex(const Xbyak::Reg& reg) {
19 return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16); 19 return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16);
20} 20}
21 21
22inline Xbyak::Reg64 IndexToReg64(int reg_index) { 22inline Xbyak::Reg64 IndexToReg64(std::size_t reg_index) {
23 ASSERT(reg_index < 16); 23 ASSERT(reg_index < 16);
24 return Xbyak::Reg64(reg_index); 24 return Xbyak::Reg64(static_cast<int>(reg_index));
25} 25}
26 26
27inline Xbyak::Xmm IndexToXmm(int reg_index) { 27inline Xbyak::Xmm IndexToXmm(std::size_t reg_index) {
28 ASSERT(reg_index >= 16 && reg_index < 32); 28 ASSERT(reg_index >= 16 && reg_index < 32);
29 return Xbyak::Xmm(reg_index - 16); 29 return Xbyak::Xmm(static_cast<int>(reg_index - 16));
30} 30}
31 31
32inline Xbyak::Reg IndexToReg(int reg_index) { 32inline Xbyak::Reg IndexToReg(std::size_t reg_index) {
33 if (reg_index < 16) { 33 if (reg_index < 16) {
34 return IndexToReg64(reg_index); 34 return IndexToReg64(reg_index);
35 } else { 35 } else {
@@ -151,9 +151,13 @@ constexpr size_t ABI_SHADOW_SPACE = 0;
151 151
152#endif 152#endif
153 153
154inline void ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alignment, 154struct ABIFrameInfo {
155 size_t needed_frame_size, s32* out_subtraction, 155 s32 subtraction;
156 s32* out_xmm_offset) { 156 s32 xmm_offset;
157};
158
159inline ABIFrameInfo ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alignment,
160 size_t needed_frame_size) {
157 const auto count = (regs & ABI_ALL_GPRS).count(); 161 const auto count = (regs & ABI_ALL_GPRS).count();
158 rsp_alignment -= count * 8; 162 rsp_alignment -= count * 8;
159 size_t subtraction = 0; 163 size_t subtraction = 0;
@@ -170,33 +174,28 @@ inline void ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alignment,
170 rsp_alignment -= subtraction; 174 rsp_alignment -= subtraction;
171 subtraction += rsp_alignment & 0xF; 175 subtraction += rsp_alignment & 0xF;
172 176
173 *out_subtraction = (s32)subtraction; 177 return ABIFrameInfo{static_cast<s32>(subtraction),
174 *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction); 178 static_cast<s32>(subtraction - xmm_base_subtraction)};
175} 179}
176 180
177inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, 181inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
178 size_t rsp_alignment, size_t needed_frame_size = 0) { 182 size_t rsp_alignment, size_t needed_frame_size = 0) {
179 s32 subtraction, xmm_offset; 183 auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
180 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); 184
181 for (std::size_t i = 0; i < regs.size(); ++i) { 185 for (std::size_t i = 0; i < regs.size(); ++i) {
182 if (regs[i] && ABI_ALL_GPRS[i]) { 186 if (regs[i] && ABI_ALL_GPRS[i]) {
183 code.push(IndexToReg64(static_cast<int>(i))); 187 code.push(IndexToReg64(i));
184 } 188 }
185 } 189 }
186 if (subtraction != 0) {
187 code.sub(code.rsp, subtraction);
188 }
189 190
190 for (int i = 0; i < regs.count(); i++) { 191 if (frame_info.subtraction != 0) {
191 if (regs.test(i) & ABI_ALL_GPRS.test(i)) { 192 code.sub(code.rsp, frame_info.subtraction);
192 code.push(IndexToReg64(i));
193 }
194 } 193 }
195 194
196 for (std::size_t i = 0; i < regs.size(); ++i) { 195 for (std::size_t i = 0; i < regs.size(); ++i) {
197 if (regs[i] && ABI_ALL_XMMS[i]) { 196 if (regs[i] && ABI_ALL_XMMS[i]) {
198 code.movaps(code.xword[code.rsp + xmm_offset], IndexToXmm(static_cast<int>(i))); 197 code.movaps(code.xword[code.rsp + frame_info.xmm_offset], IndexToXmm(i));
199 xmm_offset += 0x10; 198 frame_info.xmm_offset += 0x10;
200 } 199 }
201 } 200 }
202 201
@@ -205,59 +204,23 @@ inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::b
205 204
206inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, 205inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
207 size_t rsp_alignment, size_t needed_frame_size = 0) { 206 size_t rsp_alignment, size_t needed_frame_size = 0) {
208 s32 subtraction, xmm_offset; 207 auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
209 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
210 208
211 for (std::size_t i = 0; i < regs.size(); ++i) { 209 for (std::size_t i = 0; i < regs.size(); ++i) {
212 if (regs[i] && ABI_ALL_XMMS[i]) { 210 if (regs[i] && ABI_ALL_XMMS[i]) {
213 code.movaps(IndexToXmm(static_cast<int>(i)), code.xword[code.rsp + xmm_offset]); 211 code.movaps(IndexToXmm(i), code.xword[code.rsp + frame_info.xmm_offset]);
214 xmm_offset += 0x10; 212 frame_info.xmm_offset += 0x10;
215 } 213 }
216 } 214 }
217 215
218 if (subtraction != 0) { 216 if (frame_info.subtraction != 0) {
219 code.add(code.rsp, subtraction); 217 code.add(code.rsp, frame_info.subtraction);
220 } 218 }
221 219
222 // GPRs need to be popped in reverse order 220 // GPRs need to be popped in reverse order
223 for (int i = 15; i >= 0; i--) { 221 for (std::size_t j = 0; j < regs.size(); ++j) {
224 if (regs[i]) { 222 const std::size_t i = regs.size() - j - 1;
225 code.pop(IndexToReg64(i));
226 }
227 }
228}
229
230inline size_t ABI_PushRegistersAndAdjustStackGPS(Xbyak::CodeGenerator& code, std::bitset<32> regs,
231 size_t rsp_alignment,
232 size_t needed_frame_size = 0) {
233 s32 subtraction, xmm_offset;
234 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
235
236 for (std::size_t i = 0; i < regs.size(); ++i) {
237 if (regs[i] && ABI_ALL_GPRS[i]) { 223 if (regs[i] && ABI_ALL_GPRS[i]) {
238 code.push(IndexToReg64(static_cast<int>(i)));
239 }
240 }
241
242 if (subtraction != 0) {
243 code.sub(code.rsp, subtraction);
244 }
245
246 return ABI_SHADOW_SPACE;
247}
248
249inline void ABI_PopRegistersAndAdjustStackGPS(Xbyak::CodeGenerator& code, std::bitset<32> regs,
250 size_t rsp_alignment, size_t needed_frame_size = 0) {
251 s32 subtraction, xmm_offset;
252 ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
253
254 if (subtraction != 0) {
255 code.add(code.rsp, subtraction);
256 }
257
258 // GPRs need to be popped in reverse order
259 for (int i = 15; i >= 0; i--) {
260 if (regs[i]) {
261 code.pop(IndexToReg64(i)); 224 code.pop(IndexToReg64(i));
262 } 225 }
263 } 226 }
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 47418006b..d1f173f42 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -7,6 +7,16 @@ endif()
7add_library(core STATIC 7add_library(core STATIC
8 arm/arm_interface.h 8 arm/arm_interface.h
9 arm/arm_interface.cpp 9 arm/arm_interface.cpp
10 arm/cpu_interrupt_handler.cpp
11 arm/cpu_interrupt_handler.h
12 arm/dynarmic/arm_dynarmic_32.cpp
13 arm/dynarmic/arm_dynarmic_32.h
14 arm/dynarmic/arm_dynarmic_64.cpp
15 arm/dynarmic/arm_dynarmic_64.h
16 arm/dynarmic/arm_dynarmic_cp15.cpp
17 arm/dynarmic/arm_dynarmic_cp15.h
18 arm/dynarmic/arm_exclusive_monitor.cpp
19 arm/dynarmic/arm_exclusive_monitor.h
10 arm/exclusive_monitor.cpp 20 arm/exclusive_monitor.cpp
11 arm/exclusive_monitor.h 21 arm/exclusive_monitor.h
12 arm/unicorn/arm_unicorn.cpp 22 arm/unicorn/arm_unicorn.cpp
@@ -15,8 +25,6 @@ add_library(core STATIC
15 constants.h 25 constants.h
16 core.cpp 26 core.cpp
17 core.h 27 core.h
18 core_manager.cpp
19 core_manager.h
20 core_timing.cpp 28 core_timing.cpp
21 core_timing.h 29 core_timing.h
22 core_timing_util.cpp 30 core_timing_util.cpp
@@ -606,11 +614,11 @@ endif()
606create_target_directory_groups(core) 614create_target_directory_groups(core)
607 615
608target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 616target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
609target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus unicorn) 617target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus unicorn zip)
610 618
611if (YUZU_ENABLE_BOXCAT) 619if (YUZU_ENABLE_BOXCAT)
612 target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT) 620 target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT)
613 target_link_libraries(core PRIVATE httplib nlohmann_json::nlohmann_json zip) 621 target_link_libraries(core PRIVATE httplib nlohmann_json::nlohmann_json)
614endif() 622endif()
615 623
616if (ENABLE_WEB_SERVICE) 624if (ENABLE_WEB_SERVICE)
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index d079a1bc8..d2295ed90 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -139,6 +139,63 @@ std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_addr
139 139
140constexpr u64 SEGMENT_BASE = 0x7100000000ull; 140constexpr u64 SEGMENT_BASE = 0x7100000000ull;
141 141
142std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
143 System& system, const ThreadContext64& ctx) {
144 std::vector<BacktraceEntry> out;
145 auto& memory = system.Memory();
146
147 auto fp = ctx.cpu_registers[29];
148 auto lr = ctx.cpu_registers[30];
149 while (true) {
150 out.push_back({"", 0, lr, 0});
151 if (!fp) {
152 break;
153 }
154 lr = memory.Read64(fp + 8) - 4;
155 fp = memory.Read64(fp);
156 }
157
158 std::map<VAddr, std::string> modules;
159 auto& loader{system.GetAppLoader()};
160 if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
161 return {};
162 }
163
164 std::map<std::string, Symbols> symbols;
165 for (const auto& module : modules) {
166 symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
167 }
168
169 for (auto& entry : out) {
170 VAddr base = 0;
171 for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
172 const auto& module{*iter};
173 if (entry.original_address >= module.first) {
174 entry.module = module.second;
175 base = module.first;
176 break;
177 }
178 }
179
180 entry.offset = entry.original_address - base;
181 entry.address = SEGMENT_BASE + entry.offset;
182
183 if (entry.module.empty())
184 entry.module = "unknown";
185
186 const auto symbol_set = symbols.find(entry.module);
187 if (symbol_set != symbols.end()) {
188 const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
189 if (symbol.has_value()) {
190 // TODO(DarkLordZach): Add demangling of symbol names.
191 entry.name = *symbol;
192 }
193 }
194 }
195
196 return out;
197}
198
142std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { 199std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
143 std::vector<BacktraceEntry> out; 200 std::vector<BacktraceEntry> out;
144 auto& memory = system.Memory(); 201 auto& memory = system.Memory();
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index cb2e640e2..1f24051e4 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <vector> 8#include <vector>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hardware_properties.h"
10 11
11namespace Common { 12namespace Common {
12struct PageTable; 13struct PageTable;
@@ -18,25 +19,29 @@ enum class VMAPermission : u8;
18 19
19namespace Core { 20namespace Core {
20class System; 21class System;
22class CPUInterruptHandler;
23
24using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
21 25
22/// Generic ARMv8 CPU interface 26/// Generic ARMv8 CPU interface
23class ARM_Interface : NonCopyable { 27class ARM_Interface : NonCopyable {
24public: 28public:
25 explicit ARM_Interface(System& system_) : system{system_} {} 29 explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers, bool uses_wall_clock)
30 : system{system_}, interrupt_handlers{interrupt_handlers}, uses_wall_clock{
31 uses_wall_clock} {}
26 virtual ~ARM_Interface() = default; 32 virtual ~ARM_Interface() = default;
27 33
28 struct ThreadContext32 { 34 struct ThreadContext32 {
29 std::array<u32, 16> cpu_registers{}; 35 std::array<u32, 16> cpu_registers{};
36 std::array<u32, 64> extension_registers{};
30 u32 cpsr{}; 37 u32 cpsr{};
31 std::array<u8, 4> padding{};
32 std::array<u64, 32> fprs{};
33 u32 fpscr{}; 38 u32 fpscr{};
34 u32 fpexc{}; 39 u32 fpexc{};
35 u32 tpidr{}; 40 u32 tpidr{};
36 }; 41 };
37 // Internally within the kernel, it expects the AArch32 version of the 42 // Internally within the kernel, it expects the AArch32 version of the
38 // thread context to be 344 bytes in size. 43 // thread context to be 344 bytes in size.
39 static_assert(sizeof(ThreadContext32) == 0x158); 44 static_assert(sizeof(ThreadContext32) == 0x150);
40 45
41 struct ThreadContext64 { 46 struct ThreadContext64 {
42 std::array<u64, 31> cpu_registers{}; 47 std::array<u64, 31> cpu_registers{};
@@ -143,6 +148,8 @@ public:
143 */ 148 */
144 virtual void SetTPIDR_EL0(u64 value) = 0; 149 virtual void SetTPIDR_EL0(u64 value) = 0;
145 150
151 virtual void ChangeProcessorID(std::size_t new_core_id) = 0;
152
146 virtual void SaveContext(ThreadContext32& ctx) = 0; 153 virtual void SaveContext(ThreadContext32& ctx) = 0;
147 virtual void SaveContext(ThreadContext64& ctx) = 0; 154 virtual void SaveContext(ThreadContext64& ctx) = 0;
148 virtual void LoadContext(const ThreadContext32& ctx) = 0; 155 virtual void LoadContext(const ThreadContext32& ctx) = 0;
@@ -162,6 +169,9 @@ public:
162 std::string name; 169 std::string name;
163 }; 170 };
164 171
172 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
173 const ThreadContext64& ctx);
174
165 std::vector<BacktraceEntry> GetBacktrace() const; 175 std::vector<BacktraceEntry> GetBacktrace() const;
166 176
167 /// fp (= r29) points to the last frame record. 177 /// fp (= r29) points to the last frame record.
@@ -175,6 +185,8 @@ public:
175protected: 185protected:
176 /// System context that this ARM interface is running under. 186 /// System context that this ARM interface is running under.
177 System& system; 187 System& system;
188 CPUInterrupts& interrupt_handlers;
189 bool uses_wall_clock;
178}; 190};
179 191
180} // namespace Core 192} // namespace Core
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
new file mode 100644
index 000000000..df0350881
--- /dev/null
+++ b/src/core/arm/cpu_interrupt_handler.cpp
@@ -0,0 +1,27 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/thread.h"
6#include "core/arm/cpu_interrupt_handler.h"
7
8namespace Core {
9
10CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} {
11 interrupt_event = std::make_unique<Common::Event>();
12}
13
14CPUInterruptHandler::~CPUInterruptHandler() = default;
15
16void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
17 if (is_interrupted_) {
18 interrupt_event->Set();
19 }
20 this->is_interrupted = is_interrupted_;
21}
22
23void CPUInterruptHandler::AwaitInterrupt() {
24 interrupt_event->Wait();
25}
26
27} // namespace Core
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
new file mode 100644
index 000000000..3d062d326
--- /dev/null
+++ b/src/core/arm/cpu_interrupt_handler.h
@@ -0,0 +1,39 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9namespace Common {
10class Event;
11}
12
13namespace Core {
14
15class CPUInterruptHandler {
16public:
17 CPUInterruptHandler();
18 ~CPUInterruptHandler();
19
20 CPUInterruptHandler(const CPUInterruptHandler&) = delete;
21 CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete;
22
23 CPUInterruptHandler(CPUInterruptHandler&&) = default;
24 CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default;
25
26 bool IsInterrupted() const {
27 return is_interrupted;
28 }
29
30 void SetInterrupt(bool is_interrupted);
31
32 void AwaitInterrupt();
33
34private:
35 bool is_interrupted{};
36 std::unique_ptr<Common::Event> interrupt_event;
37};
38
39} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 9bc86e3b9..0d4ab95b7 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -7,15 +7,17 @@
7#include <dynarmic/A32/a32.h> 7#include <dynarmic/A32/a32.h>
8#include <dynarmic/A32/config.h> 8#include <dynarmic/A32/config.h>
9#include <dynarmic/A32/context.h> 9#include <dynarmic/A32/context.h>
10#include "common/microprofile.h" 10#include "common/logging/log.h"
11#include "common/page_table.h"
12#include "core/arm/cpu_interrupt_handler.h"
11#include "core/arm/dynarmic/arm_dynarmic_32.h" 13#include "core/arm/dynarmic/arm_dynarmic_32.h"
12#include "core/arm/dynarmic/arm_dynarmic_64.h"
13#include "core/arm/dynarmic/arm_dynarmic_cp15.h" 14#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
15#include "core/arm/dynarmic/arm_exclusive_monitor.h"
14#include "core/core.h" 16#include "core/core.h"
15#include "core/core_manager.h"
16#include "core/core_timing.h" 17#include "core/core_timing.h"
17#include "core/hle/kernel/svc.h" 18#include "core/hle/kernel/svc.h"
18#include "core/memory.h" 19#include "core/memory.h"
20#include "core/settings.h"
19 21
20namespace Core { 22namespace Core {
21 23
@@ -49,8 +51,22 @@ public:
49 parent.system.Memory().Write64(vaddr, value); 51 parent.system.Memory().Write64(vaddr, value);
50 } 52 }
51 53
54 bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override {
55 return parent.system.Memory().WriteExclusive8(vaddr, value, expected);
56 }
57 bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override {
58 return parent.system.Memory().WriteExclusive16(vaddr, value, expected);
59 }
60 bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override {
61 return parent.system.Memory().WriteExclusive32(vaddr, value, expected);
62 }
63 bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override {
64 return parent.system.Memory().WriteExclusive64(vaddr, value, expected);
65 }
66
52 void InterpreterFallback(u32 pc, std::size_t num_instructions) override { 67 void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
53 UNIMPLEMENTED(); 68 UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
69 MemoryReadCode(pc));
54 } 70 }
55 71
56 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 72 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
@@ -61,7 +77,7 @@ public:
61 case Dynarmic::A32::Exception::Breakpoint: 77 case Dynarmic::A32::Exception::Breakpoint:
62 break; 78 break;
63 } 79 }
64 LOG_CRITICAL(HW_GPU, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 80 LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
65 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 81 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
66 UNIMPLEMENTED(); 82 UNIMPLEMENTED();
67 } 83 }
@@ -71,26 +87,36 @@ public:
71 } 87 }
72 88
73 void AddTicks(u64 ticks) override { 89 void AddTicks(u64 ticks) override {
90 if (parent.uses_wall_clock) {
91 return;
92 }
74 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a 93 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
75 // rough approximation of the amount of executed ticks in the system, it may be thrown off 94 // rough approximation of the amount of executed ticks in the system, it may be thrown off
76 // if not all cores are doing a similar amount of work. Instead of doing this, we should 95 // if not all cores are doing a similar amount of work. Instead of doing this, we should
77 // device a way so that timing is consistent across all cores without increasing the ticks 4 96 // device a way so that timing is consistent across all cores without increasing the ticks 4
78 // times. 97 // times.
79 u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; 98 u64 amortized_ticks =
99 (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES;
80 // Always execute at least one tick. 100 // Always execute at least one tick.
81 amortized_ticks = std::max<u64>(amortized_ticks, 1); 101 amortized_ticks = std::max<u64>(amortized_ticks, 1);
82 102
83 parent.system.CoreTiming().AddTicks(amortized_ticks); 103 parent.system.CoreTiming().AddTicks(amortized_ticks);
84 num_interpreted_instructions = 0; 104 num_interpreted_instructions = 0;
85 } 105 }
106
86 u64 GetTicksRemaining() override { 107 u64 GetTicksRemaining() override {
87 return std::max(parent.system.CoreTiming().GetDowncount(), {}); 108 if (parent.uses_wall_clock) {
109 if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) {
110 return minimum_run_cycles;
111 }
112 return 0U;
113 }
114 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
88 } 115 }
89 116
90 ARM_Dynarmic_32& parent; 117 ARM_Dynarmic_32& parent;
91 std::size_t num_interpreted_instructions{}; 118 std::size_t num_interpreted_instructions{};
92 u64 tpidrro_el0{}; 119 static constexpr u64 minimum_run_cycles = 1000U;
93 u64 tpidr_el0{};
94}; 120};
95 121
96std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table, 122std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table,
@@ -99,26 +125,46 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
99 config.callbacks = cb.get(); 125 config.callbacks = cb.get();
100 // TODO(bunnei): Implement page table for 32-bit 126 // TODO(bunnei): Implement page table for 32-bit
101 // config.page_table = &page_table.pointers; 127 // config.page_table = &page_table.pointers;
102 config.coprocessors[15] = std::make_shared<DynarmicCP15>((u32*)&CP15_regs[0]); 128 config.coprocessors[15] = cp15;
103 config.define_unpredictable_behaviour = true; 129 config.define_unpredictable_behaviour = true;
130 static constexpr std::size_t PAGE_BITS = 12;
131 static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS);
132 config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
133 page_table.pointers.data());
134 config.absolute_offset_page_table = true;
135 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
136 config.only_detect_misalignment_via_page_table_on_page_boundary = true;
137
138 // Multi-process state
139 config.processor_id = core_index;
140 config.global_monitor = &exclusive_monitor.monitor;
141
142 // Timing
143 config.wall_clock_cntpct = uses_wall_clock;
144
145 // Optimizations
146 if (Settings::values.disable_cpu_opt) {
147 config.enable_optimizations = false;
148 config.enable_fast_dispatch = false;
149 }
150
104 return std::make_unique<Dynarmic::A32::Jit>(config); 151 return std::make_unique<Dynarmic::A32::Jit>(config);
105} 152}
106 153
107MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_32, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
108
109void ARM_Dynarmic_32::Run() { 154void ARM_Dynarmic_32::Run() {
110 MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_32);
111 jit->Run(); 155 jit->Run();
112} 156}
113 157
114void ARM_Dynarmic_32::Step() { 158void ARM_Dynarmic_32::Step() {
115 cb->InterpreterFallback(jit->Regs()[15], 1); 159 jit->Step();
116} 160}
117 161
118ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, 162ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers,
163 bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor,
119 std::size_t core_index) 164 std::size_t core_index)
120 : ARM_Interface{system}, 165 : ARM_Interface{system, interrupt_handlers, uses_wall_clock},
121 cb(std::make_unique<DynarmicCallbacks32>(*this)), core_index{core_index}, 166 cb(std::make_unique<DynarmicCallbacks32>(*this)),
167 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index},
122 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} 168 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
123 169
124ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; 170ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
@@ -154,32 +200,40 @@ void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) {
154} 200}
155 201
156u64 ARM_Dynarmic_32::GetTlsAddress() const { 202u64 ARM_Dynarmic_32::GetTlsAddress() const {
157 return CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)]; 203 return cp15->uro;
158} 204}
159 205
160void ARM_Dynarmic_32::SetTlsAddress(VAddr address) { 206void ARM_Dynarmic_32::SetTlsAddress(VAddr address) {
161 CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)] = static_cast<u32>(address); 207 cp15->uro = static_cast<u32>(address);
162} 208}
163 209
164u64 ARM_Dynarmic_32::GetTPIDR_EL0() const { 210u64 ARM_Dynarmic_32::GetTPIDR_EL0() const {
165 return cb->tpidr_el0; 211 return cp15->uprw;
166} 212}
167 213
168void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { 214void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
169 cb->tpidr_el0 = value; 215 cp15->uprw = static_cast<u32>(value);
216}
217
218void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) {
219 jit->ChangeProcessorID(new_core_id);
170} 220}
171 221
172void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { 222void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
173 Dynarmic::A32::Context context; 223 Dynarmic::A32::Context context;
174 jit->SaveContext(context); 224 jit->SaveContext(context);
175 ctx.cpu_registers = context.Regs(); 225 ctx.cpu_registers = context.Regs();
226 ctx.extension_registers = context.ExtRegs();
176 ctx.cpsr = context.Cpsr(); 227 ctx.cpsr = context.Cpsr();
228 ctx.fpscr = context.Fpscr();
177} 229}
178 230
179void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { 231void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
180 Dynarmic::A32::Context context; 232 Dynarmic::A32::Context context;
181 context.Regs() = ctx.cpu_registers; 233 context.Regs() = ctx.cpu_registers;
234 context.ExtRegs() = ctx.extension_registers;
182 context.SetCpsr(ctx.cpsr); 235 context.SetCpsr(ctx.cpsr);
236 context.SetFpscr(ctx.fpscr);
183 jit->LoadContext(context); 237 jit->LoadContext(context);
184} 238}
185 239
@@ -188,10 +242,15 @@ void ARM_Dynarmic_32::PrepareReschedule() {
188} 242}
189 243
190void ARM_Dynarmic_32::ClearInstructionCache() { 244void ARM_Dynarmic_32::ClearInstructionCache() {
245 if (!jit) {
246 return;
247 }
191 jit->ClearCache(); 248 jit->ClearCache();
192} 249}
193 250
194void ARM_Dynarmic_32::ClearExclusiveState() {} 251void ARM_Dynarmic_32::ClearExclusiveState() {
252 jit->ClearExclusiveState();
253}
195 254
196void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, 255void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
197 std::size_t new_address_space_size_in_bits) { 256 std::size_t new_address_space_size_in_bits) {
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 8ba9cea8f..2bab31b92 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -9,7 +9,7 @@
9 9
10#include <dynarmic/A32/a32.h> 10#include <dynarmic/A32/a32.h>
11#include <dynarmic/A64/a64.h> 11#include <dynarmic/A64/a64.h>
12#include <dynarmic/A64/exclusive_monitor.h> 12#include <dynarmic/exclusive_monitor.h>
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/hash.h" 14#include "common/hash.h"
15#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
@@ -21,13 +21,16 @@ class Memory;
21 21
22namespace Core { 22namespace Core {
23 23
24class CPUInterruptHandler;
24class DynarmicCallbacks32; 25class DynarmicCallbacks32;
26class DynarmicCP15;
25class DynarmicExclusiveMonitor; 27class DynarmicExclusiveMonitor;
26class System; 28class System;
27 29
28class ARM_Dynarmic_32 final : public ARM_Interface { 30class ARM_Dynarmic_32 final : public ARM_Interface {
29public: 31public:
30 ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); 32 ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock,
33 ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
31 ~ARM_Dynarmic_32() override; 34 ~ARM_Dynarmic_32() override;
32 35
33 void SetPC(u64 pc) override; 36 void SetPC(u64 pc) override;
@@ -44,6 +47,7 @@ public:
44 void SetTlsAddress(VAddr address) override; 47 void SetTlsAddress(VAddr address) override;
45 void SetTPIDR_EL0(u64 value) override; 48 void SetTPIDR_EL0(u64 value) override;
46 u64 GetTPIDR_EL0() const override; 49 u64 GetTPIDR_EL0() const override;
50 void ChangeProcessorID(std::size_t new_core_id) override;
47 51
48 void SaveContext(ThreadContext32& ctx) override; 52 void SaveContext(ThreadContext32& ctx) override;
49 void SaveContext(ThreadContext64& ctx) override {} 53 void SaveContext(ThreadContext64& ctx) override {}
@@ -66,12 +70,14 @@ private:
66 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>; 70 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
67 71
68 friend class DynarmicCallbacks32; 72 friend class DynarmicCallbacks32;
73 friend class DynarmicCP15;
74
69 std::unique_ptr<DynarmicCallbacks32> cb; 75 std::unique_ptr<DynarmicCallbacks32> cb;
70 JitCacheType jit_cache; 76 JitCacheType jit_cache;
71 std::shared_ptr<Dynarmic::A32::Jit> jit; 77 std::shared_ptr<Dynarmic::A32::Jit> jit;
78 std::shared_ptr<DynarmicCP15> cp15;
72 std::size_t core_index; 79 std::size_t core_index;
73 DynarmicExclusiveMonitor& exclusive_monitor; 80 DynarmicExclusiveMonitor& exclusive_monitor;
74 std::array<u32, 84> CP15_regs{};
75}; 81};
76 82
77} // namespace Core 83} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 337b97be9..790981034 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -7,11 +7,11 @@
7#include <dynarmic/A64/a64.h> 7#include <dynarmic/A64/a64.h>
8#include <dynarmic/A64/config.h> 8#include <dynarmic/A64/config.h>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/microprofile.h"
11#include "common/page_table.h" 10#include "common/page_table.h"
11#include "core/arm/cpu_interrupt_handler.h"
12#include "core/arm/dynarmic/arm_dynarmic_64.h" 12#include "core/arm/dynarmic/arm_dynarmic_64.h"
13#include "core/arm/dynarmic/arm_exclusive_monitor.h"
13#include "core/core.h" 14#include "core/core.h"
14#include "core/core_manager.h"
15#include "core/core_timing.h" 15#include "core/core_timing.h"
16#include "core/core_timing_util.h" 16#include "core/core_timing_util.h"
17#include "core/gdbstub/gdbstub.h" 17#include "core/gdbstub/gdbstub.h"
@@ -65,6 +65,22 @@ public:
65 memory.Write64(vaddr + 8, value[1]); 65 memory.Write64(vaddr + 8, value[1]);
66 } 66 }
67 67
68 bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override {
69 return parent.system.Memory().WriteExclusive8(vaddr, value, expected);
70 }
71 bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override {
72 return parent.system.Memory().WriteExclusive16(vaddr, value, expected);
73 }
74 bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override {
75 return parent.system.Memory().WriteExclusive32(vaddr, value, expected);
76 }
77 bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override {
78 return parent.system.Memory().WriteExclusive64(vaddr, value, expected);
79 }
80 bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override {
81 return parent.system.Memory().WriteExclusive128(vaddr, value, expected);
82 }
83
68 void InterpreterFallback(u64 pc, std::size_t num_instructions) override { 84 void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
69 LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, 85 LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc,
70 num_instructions, MemoryReadCode(pc)); 86 num_instructions, MemoryReadCode(pc));
@@ -98,8 +114,8 @@ public:
98 } 114 }
99 [[fallthrough]]; 115 [[fallthrough]];
100 default: 116 default:
101 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})", 117 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
102 static_cast<std::size_t>(exception), pc); 118 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
103 } 119 }
104 } 120 }
105 121
@@ -108,29 +124,42 @@ public:
108 } 124 }
109 125
110 void AddTicks(u64 ticks) override { 126 void AddTicks(u64 ticks) override {
127 if (parent.uses_wall_clock) {
128 return;
129 }
111 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a 130 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
112 // rough approximation of the amount of executed ticks in the system, it may be thrown off 131 // rough approximation of the amount of executed ticks in the system, it may be thrown off
113 // if not all cores are doing a similar amount of work. Instead of doing this, we should 132 // if not all cores are doing a similar amount of work. Instead of doing this, we should
114 // device a way so that timing is consistent across all cores without increasing the ticks 4 133 // device a way so that timing is consistent across all cores without increasing the ticks 4
115 // times. 134 // times.
116 u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES; 135 u64 amortized_ticks =
136 (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES;
117 // Always execute at least one tick. 137 // Always execute at least one tick.
118 amortized_ticks = std::max<u64>(amortized_ticks, 1); 138 amortized_ticks = std::max<u64>(amortized_ticks, 1);
119 139
120 parent.system.CoreTiming().AddTicks(amortized_ticks); 140 parent.system.CoreTiming().AddTicks(amortized_ticks);
121 num_interpreted_instructions = 0; 141 num_interpreted_instructions = 0;
122 } 142 }
143
123 u64 GetTicksRemaining() override { 144 u64 GetTicksRemaining() override {
124 return std::max(parent.system.CoreTiming().GetDowncount(), s64{0}); 145 if (parent.uses_wall_clock) {
146 if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) {
147 return minimum_run_cycles;
148 }
149 return 0U;
150 }
151 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
125 } 152 }
153
126 u64 GetCNTPCT() override { 154 u64 GetCNTPCT() override {
127 return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); 155 return parent.system.CoreTiming().GetClockTicks();
128 } 156 }
129 157
130 ARM_Dynarmic_64& parent; 158 ARM_Dynarmic_64& parent;
131 std::size_t num_interpreted_instructions = 0; 159 std::size_t num_interpreted_instructions = 0;
132 u64 tpidrro_el0 = 0; 160 u64 tpidrro_el0 = 0;
133 u64 tpidr_el0 = 0; 161 u64 tpidr_el0 = 0;
162 static constexpr u64 minimum_run_cycles = 1000U;
134}; 163};
135 164
136std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table, 165std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table,
@@ -168,14 +197,13 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
168 config.enable_fast_dispatch = false; 197 config.enable_fast_dispatch = false;
169 } 198 }
170 199
200 // Timing
201 config.wall_clock_cntpct = uses_wall_clock;
202
171 return std::make_shared<Dynarmic::A64::Jit>(config); 203 return std::make_shared<Dynarmic::A64::Jit>(config);
172} 204}
173 205
174MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_64, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
175
176void ARM_Dynarmic_64::Run() { 206void ARM_Dynarmic_64::Run() {
177 MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_64);
178
179 jit->Run(); 207 jit->Run();
180} 208}
181 209
@@ -183,11 +211,16 @@ void ARM_Dynarmic_64::Step() {
183 cb->InterpreterFallback(jit->GetPC(), 1); 211 cb->InterpreterFallback(jit->GetPC(), 1);
184} 212}
185 213
186ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, 214ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers,
215 bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor,
187 std::size_t core_index) 216 std::size_t core_index)
188 : ARM_Interface{system}, cb(std::make_unique<DynarmicCallbacks64>(*this)), 217 : ARM_Interface{system, interrupt_handlers, uses_wall_clock},
189 inner_unicorn{system, ARM_Unicorn::Arch::AArch64}, core_index{core_index}, 218 cb(std::make_unique<DynarmicCallbacks64>(*this)), inner_unicorn{system, interrupt_handlers,
190 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} 219 uses_wall_clock,
220 ARM_Unicorn::Arch::AArch64,
221 core_index},
222 core_index{core_index}, exclusive_monitor{
223 dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
191 224
192ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; 225ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
193 226
@@ -239,6 +272,10 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
239 cb->tpidr_el0 = value; 272 cb->tpidr_el0 = value;
240} 273}
241 274
275void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) {
276 jit->ChangeProcessorID(new_core_id);
277}
278
242void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { 279void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
243 ctx.cpu_registers = jit->GetRegisters(); 280 ctx.cpu_registers = jit->GetRegisters();
244 ctx.sp = jit->GetSP(); 281 ctx.sp = jit->GetSP();
@@ -266,6 +303,9 @@ void ARM_Dynarmic_64::PrepareReschedule() {
266} 303}
267 304
268void ARM_Dynarmic_64::ClearInstructionCache() { 305void ARM_Dynarmic_64::ClearInstructionCache() {
306 if (!jit) {
307 return;
308 }
269 jit->ClearCache(); 309 jit->ClearCache();
270} 310}
271 311
@@ -285,44 +325,4 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
285 jit_cache.emplace(key, jit); 325 jit_cache.emplace(key, jit);
286} 326}
287 327
288DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count)
289 : monitor(core_count), memory{memory} {}
290
291DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
292
293void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) {
294 // Size doesn't actually matter.
295 monitor.Mark(core_index, addr, 16);
296}
297
298void DynarmicExclusiveMonitor::ClearExclusive() {
299 monitor.Clear();
300}
301
302bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
303 return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&] { memory.Write8(vaddr, value); });
304}
305
306bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) {
307 return monitor.DoExclusiveOperation(core_index, vaddr, 2,
308 [&] { memory.Write16(vaddr, value); });
309}
310
311bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) {
312 return monitor.DoExclusiveOperation(core_index, vaddr, 4,
313 [&] { memory.Write32(vaddr, value); });
314}
315
316bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) {
317 return monitor.DoExclusiveOperation(core_index, vaddr, 8,
318 [&] { memory.Write64(vaddr, value); });
319}
320
321bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) {
322 return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
323 memory.Write64(vaddr + 0, value[0]);
324 memory.Write64(vaddr + 8, value[1]);
325 });
326}
327
328} // namespace Core 328} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 647cecaf0..403c55961 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -8,7 +8,6 @@
8#include <unordered_map> 8#include <unordered_map>
9 9
10#include <dynarmic/A64/a64.h> 10#include <dynarmic/A64/a64.h>
11#include <dynarmic/A64/exclusive_monitor.h>
12#include "common/common_types.h" 11#include "common/common_types.h"
13#include "common/hash.h" 12#include "common/hash.h"
14#include "core/arm/arm_interface.h" 13#include "core/arm/arm_interface.h"
@@ -22,12 +21,14 @@ class Memory;
22namespace Core { 21namespace Core {
23 22
24class DynarmicCallbacks64; 23class DynarmicCallbacks64;
24class CPUInterruptHandler;
25class DynarmicExclusiveMonitor; 25class DynarmicExclusiveMonitor;
26class System; 26class System;
27 27
28class ARM_Dynarmic_64 final : public ARM_Interface { 28class ARM_Dynarmic_64 final : public ARM_Interface {
29public: 29public:
30 ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); 30 ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock,
31 ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
31 ~ARM_Dynarmic_64() override; 32 ~ARM_Dynarmic_64() override;
32 33
33 void SetPC(u64 pc) override; 34 void SetPC(u64 pc) override;
@@ -44,6 +45,7 @@ public:
44 void SetTlsAddress(VAddr address) override; 45 void SetTlsAddress(VAddr address) override;
45 void SetTPIDR_EL0(u64 value) override; 46 void SetTPIDR_EL0(u64 value) override;
46 u64 GetTPIDR_EL0() const override; 47 u64 GetTPIDR_EL0() const override;
48 void ChangeProcessorID(std::size_t new_core_id) override;
47 49
48 void SaveContext(ThreadContext32& ctx) override {} 50 void SaveContext(ThreadContext32& ctx) override {}
49 void SaveContext(ThreadContext64& ctx) override; 51 void SaveContext(ThreadContext64& ctx) override;
@@ -75,24 +77,4 @@ private:
75 DynarmicExclusiveMonitor& exclusive_monitor; 77 DynarmicExclusiveMonitor& exclusive_monitor;
76}; 78};
77 79
78class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
79public:
80 explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count);
81 ~DynarmicExclusiveMonitor() override;
82
83 void SetExclusive(std::size_t core_index, VAddr addr) override;
84 void ClearExclusive() override;
85
86 bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override;
87 bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override;
88 bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override;
89 bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override;
90 bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override;
91
92private:
93 friend class ARM_Dynarmic_64;
94 Dynarmic::A64::ExclusiveMonitor monitor;
95 Core::Memory::Memory& memory;
96};
97
98} // namespace Core 80} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index 3fdcdebde..54556e0f9 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -2,79 +2,132 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <fmt/format.h>
6#include "common/logging/log.h"
7#include "core/arm/dynarmic/arm_dynarmic_32.h"
5#include "core/arm/dynarmic/arm_dynarmic_cp15.h" 8#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
9#include "core/core.h"
10#include "core/core_timing.h"
11#include "core/core_timing_util.h"
6 12
7using Callback = Dynarmic::A32::Coprocessor::Callback; 13using Callback = Dynarmic::A32::Coprocessor::Callback;
8using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord; 14using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
9using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords; 15using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
10 16
17template <>
18struct fmt::formatter<Dynarmic::A32::CoprocReg> {
19 constexpr auto parse(format_parse_context& ctx) {
20 return ctx.begin();
21 }
22 template <typename FormatContext>
23 auto format(const Dynarmic::A32::CoprocReg& reg, FormatContext& ctx) {
24 return format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
25 }
26};
27
28namespace Core {
29
30static u32 dummy_value;
31
11std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigned opc1, 32std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigned opc1,
12 CoprocReg CRd, CoprocReg CRn, 33 CoprocReg CRd, CoprocReg CRn,
13 CoprocReg CRm, unsigned opc2) { 34 CoprocReg CRm, unsigned opc2) {
35 LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn,
36 CRm, opc2);
14 return {}; 37 return {};
15} 38}
16 39
17CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, 40CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
18 CoprocReg CRm, unsigned opc2) { 41 CoprocReg CRm, unsigned opc2) {
19 // TODO(merry): Privileged CP15 registers
20
21 if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C5 && opc2 == 4) { 42 if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C5 && opc2 == 4) {
43 // CP15_FLUSH_PREFETCH_BUFFER
22 // This is a dummy write, we ignore the value written here. 44 // This is a dummy write, we ignore the value written here.
23 return &CP15[static_cast<std::size_t>(CP15Register::CP15_FLUSH_PREFETCH_BUFFER)]; 45 return &dummy_value;
24 } 46 }
25 47
26 if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C10) { 48 if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C10) {
27 switch (opc2) { 49 switch (opc2) {
28 case 4: 50 case 4:
51 // CP15_DATA_SYNC_BARRIER
29 // This is a dummy write, we ignore the value written here. 52 // This is a dummy write, we ignore the value written here.
30 return &CP15[static_cast<std::size_t>(CP15Register::CP15_DATA_SYNC_BARRIER)]; 53 return &dummy_value;
31 case 5: 54 case 5:
55 // CP15_DATA_MEMORY_BARRIER
32 // This is a dummy write, we ignore the value written here. 56 // This is a dummy write, we ignore the value written here.
33 return &CP15[static_cast<std::size_t>(CP15Register::CP15_DATA_MEMORY_BARRIER)]; 57 return &dummy_value;
34 default:
35 return {};
36 } 58 }
37 } 59 }
38 60
39 if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0 && opc2 == 2) { 61 if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0 && opc2 == 2) {
40 return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_UPRW)]; 62 // CP15_THREAD_UPRW
63 return &uprw;
41 } 64 }
42 65
66 LOG_CRITICAL(Core_ARM, "CP15: mcr{} p15, {}, <Rt>, {}, {}, {}", two ? "2" : "", opc1, CRn, CRm,
67 opc2);
43 return {}; 68 return {};
44} 69}
45 70
46CallbackOrAccessTwoWords DynarmicCP15::CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) { 71CallbackOrAccessTwoWords DynarmicCP15::CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) {
72 LOG_CRITICAL(Core_ARM, "CP15: mcrr{} p15, {}, <Rt>, <Rt2>, {}", two ? "2" : "", opc, CRm);
47 return {}; 73 return {};
48} 74}
49 75
50CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn, 76CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn,
51 CoprocReg CRm, unsigned opc2) { 77 CoprocReg CRm, unsigned opc2) {
52 // TODO(merry): Privileged CP15 registers
53
54 if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0) { 78 if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0) {
55 switch (opc2) { 79 switch (opc2) {
56 case 2: 80 case 2:
57 return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_UPRW)]; 81 // CP15_THREAD_UPRW
82 return &uprw;
58 case 3: 83 case 3:
59 return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)]; 84 // CP15_THREAD_URO
60 default: 85 return &uro;
61 return {};
62 } 86 }
63 } 87 }
64 88
89 LOG_CRITICAL(Core_ARM, "CP15: mrc{} p15, {}, <Rt>, {}, {}, {}", two ? "2" : "", opc1, CRn, CRm,
90 opc2);
65 return {}; 91 return {};
66} 92}
67 93
68CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) { 94CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {
95 if (!two && opc == 0 && CRm == CoprocReg::C14) {
96 // CNTPCT
97 const auto callback = static_cast<u64 (*)(Dynarmic::A32::Jit*, void*, u32, u32)>(
98 [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 {
99 ARM_Dynarmic_32& parent = *(ARM_Dynarmic_32*)arg;
100 return parent.system.CoreTiming().GetClockTicks();
101 });
102 return Dynarmic::A32::Coprocessor::Callback{callback, (void*)&parent};
103 }
104
105 LOG_CRITICAL(Core_ARM, "CP15: mrrc{} p15, {}, <Rt>, <Rt2>, {}", two ? "2" : "", opc, CRm);
69 return {}; 106 return {};
70} 107}
71 108
72std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd, 109std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd,
73 std::optional<u8> option) { 110 std::optional<u8> option) {
111 if (option) {
112 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...], {}", two ? "2" : "",
113 long_transfer ? "l" : "", CRd, *option);
114 } else {
115 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
116 long_transfer ? "l" : "", CRd);
117 }
74 return {}; 118 return {};
75} 119}
76 120
77std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, 121std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
78 std::optional<u8> option) { 122 std::optional<u8> option) {
123 if (option) {
124 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...], {}", two ? "2" : "",
125 long_transfer ? "l" : "", CRd, *option);
126 } else {
127 LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
128 long_transfer ? "l" : "", CRd);
129 }
79 return {}; 130 return {};
80} 131}
132
133} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
index 07bcde5f9..7356d252e 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
@@ -10,128 +10,15 @@
10#include <dynarmic/A32/coprocessor.h> 10#include <dynarmic/A32/coprocessor.h>
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13enum class CP15Register { 13namespace Core {
14 // c0 - Information registers
15 CP15_MAIN_ID,
16 CP15_CACHE_TYPE,
17 CP15_TCM_STATUS,
18 CP15_TLB_TYPE,
19 CP15_CPU_ID,
20 CP15_PROCESSOR_FEATURE_0,
21 CP15_PROCESSOR_FEATURE_1,
22 CP15_DEBUG_FEATURE_0,
23 CP15_AUXILIARY_FEATURE_0,
24 CP15_MEMORY_MODEL_FEATURE_0,
25 CP15_MEMORY_MODEL_FEATURE_1,
26 CP15_MEMORY_MODEL_FEATURE_2,
27 CP15_MEMORY_MODEL_FEATURE_3,
28 CP15_ISA_FEATURE_0,
29 CP15_ISA_FEATURE_1,
30 CP15_ISA_FEATURE_2,
31 CP15_ISA_FEATURE_3,
32 CP15_ISA_FEATURE_4,
33 14
34 // c1 - Control registers 15class ARM_Dynarmic_32;
35 CP15_CONTROL,
36 CP15_AUXILIARY_CONTROL,
37 CP15_COPROCESSOR_ACCESS_CONTROL,
38
39 // c2 - Translation table registers
40 CP15_TRANSLATION_BASE_TABLE_0,
41 CP15_TRANSLATION_BASE_TABLE_1,
42 CP15_TRANSLATION_BASE_CONTROL,
43 CP15_DOMAIN_ACCESS_CONTROL,
44 CP15_RESERVED,
45
46 // c5 - Fault status registers
47 CP15_FAULT_STATUS,
48 CP15_INSTR_FAULT_STATUS,
49 CP15_COMBINED_DATA_FSR = CP15_FAULT_STATUS,
50 CP15_INST_FSR,
51
52 // c6 - Fault Address registers
53 CP15_FAULT_ADDRESS,
54 CP15_COMBINED_DATA_FAR = CP15_FAULT_ADDRESS,
55 CP15_WFAR,
56 CP15_IFAR,
57
58 // c7 - Cache operation registers
59 CP15_WAIT_FOR_INTERRUPT,
60 CP15_PHYS_ADDRESS,
61 CP15_INVALIDATE_INSTR_CACHE,
62 CP15_INVALIDATE_INSTR_CACHE_USING_MVA,
63 CP15_INVALIDATE_INSTR_CACHE_USING_INDEX,
64 CP15_FLUSH_PREFETCH_BUFFER,
65 CP15_FLUSH_BRANCH_TARGET_CACHE,
66 CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY,
67 CP15_INVALIDATE_DATA_CACHE,
68 CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
69 CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
70 CP15_INVALIDATE_DATA_AND_INSTR_CACHE,
71 CP15_CLEAN_DATA_CACHE,
72 CP15_CLEAN_DATA_CACHE_LINE_USING_MVA,
73 CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX,
74 CP15_DATA_SYNC_BARRIER,
75 CP15_DATA_MEMORY_BARRIER,
76 CP15_CLEAN_AND_INVALIDATE_DATA_CACHE,
77 CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
78 CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
79
80 // c8 - TLB operations
81 CP15_INVALIDATE_ITLB,
82 CP15_INVALIDATE_ITLB_SINGLE_ENTRY,
83 CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH,
84 CP15_INVALIDATE_ITLB_ENTRY_ON_MVA,
85 CP15_INVALIDATE_DTLB,
86 CP15_INVALIDATE_DTLB_SINGLE_ENTRY,
87 CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH,
88 CP15_INVALIDATE_DTLB_ENTRY_ON_MVA,
89 CP15_INVALIDATE_UTLB,
90 CP15_INVALIDATE_UTLB_SINGLE_ENTRY,
91 CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH,
92 CP15_INVALIDATE_UTLB_ENTRY_ON_MVA,
93
94 // c9 - Data cache lockdown register
95 CP15_DATA_CACHE_LOCKDOWN,
96
97 // c10 - TLB/Memory map registers
98 CP15_TLB_LOCKDOWN,
99 CP15_PRIMARY_REGION_REMAP,
100 CP15_NORMAL_REGION_REMAP,
101
102 // c13 - Thread related registers
103 CP15_PID,
104 CP15_CONTEXT_ID,
105 CP15_THREAD_UPRW, // Thread ID register - User/Privileged Read/Write
106 CP15_THREAD_URO, // Thread ID register - User Read Only (Privileged R/W)
107 CP15_THREAD_PRW, // Thread ID register - Privileged R/W only.
108
109 // c15 - Performance and TLB lockdown registers
110 CP15_PERFORMANCE_MONITOR_CONTROL,
111 CP15_CYCLE_COUNTER,
112 CP15_COUNT_0,
113 CP15_COUNT_1,
114 CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY,
115 CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY,
116 CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS,
117 CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS,
118 CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE,
119 CP15_TLB_DEBUG_CONTROL,
120
121 // Skyeye defined
122 CP15_TLB_FAULT_ADDR,
123 CP15_TLB_FAULT_STATUS,
124
125 // Not an actual register.
126 // All registers should be defined above this.
127 CP15_REGISTER_COUNT,
128};
129 16
130class DynarmicCP15 final : public Dynarmic::A32::Coprocessor { 17class DynarmicCP15 final : public Dynarmic::A32::Coprocessor {
131public: 18public:
132 using CoprocReg = Dynarmic::A32::CoprocReg; 19 using CoprocReg = Dynarmic::A32::CoprocReg;
133 20
134 explicit DynarmicCP15(u32* cp15) : CP15(cp15){}; 21 explicit DynarmicCP15(ARM_Dynarmic_32& parent) : parent(parent) {}
135 22
136 std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd, 23 std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd,
137 CoprocReg CRn, CoprocReg CRm, 24 CoprocReg CRn, CoprocReg CRm,
@@ -147,6 +34,9 @@ public:
147 std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, 34 std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
148 std::optional<u8> option) override; 35 std::optional<u8> option) override;
149 36
150private: 37 ARM_Dynarmic_32& parent;
151 u32* CP15{}; 38 u32 uprw;
39 u32 uro;
152}; 40};
41
42} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp
new file mode 100644
index 000000000..4e209f6a5
--- /dev/null
+++ b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp
@@ -0,0 +1,76 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cinttypes>
6#include <memory>
7#include "core/arm/dynarmic/arm_exclusive_monitor.h"
8#include "core/memory.h"
9
10namespace Core {
11
12DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count)
13 : monitor(core_count), memory{memory} {}
14
15DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
16
17u8 DynarmicExclusiveMonitor::ExclusiveRead8(std::size_t core_index, VAddr addr) {
18 return monitor.ReadAndMark<u8>(core_index, addr, [&]() -> u8 { return memory.Read8(addr); });
19}
20
21u16 DynarmicExclusiveMonitor::ExclusiveRead16(std::size_t core_index, VAddr addr) {
22 return monitor.ReadAndMark<u16>(core_index, addr, [&]() -> u16 { return memory.Read16(addr); });
23}
24
25u32 DynarmicExclusiveMonitor::ExclusiveRead32(std::size_t core_index, VAddr addr) {
26 return monitor.ReadAndMark<u32>(core_index, addr, [&]() -> u32 { return memory.Read32(addr); });
27}
28
29u64 DynarmicExclusiveMonitor::ExclusiveRead64(std::size_t core_index, VAddr addr) {
30 return monitor.ReadAndMark<u64>(core_index, addr, [&]() -> u64 { return memory.Read64(addr); });
31}
32
33u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr addr) {
34 return monitor.ReadAndMark<u128>(core_index, addr, [&]() -> u128 {
35 u128 result;
36 result[0] = memory.Read64(addr);
37 result[1] = memory.Read64(addr + 8);
38 return result;
39 });
40}
41
42void DynarmicExclusiveMonitor::ClearExclusive() {
43 monitor.Clear();
44}
45
46bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
47 return monitor.DoExclusiveOperation<u8>(core_index, vaddr, [&](u8 expected) -> bool {
48 return memory.WriteExclusive8(vaddr, value, expected);
49 });
50}
51
52bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) {
53 return monitor.DoExclusiveOperation<u16>(core_index, vaddr, [&](u16 expected) -> bool {
54 return memory.WriteExclusive16(vaddr, value, expected);
55 });
56}
57
58bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) {
59 return monitor.DoExclusiveOperation<u32>(core_index, vaddr, [&](u32 expected) -> bool {
60 return memory.WriteExclusive32(vaddr, value, expected);
61 });
62}
63
64bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) {
65 return monitor.DoExclusiveOperation<u64>(core_index, vaddr, [&](u64 expected) -> bool {
66 return memory.WriteExclusive64(vaddr, value, expected);
67 });
68}
69
70bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) {
71 return monitor.DoExclusiveOperation<u128>(core_index, vaddr, [&](u128 expected) -> bool {
72 return memory.WriteExclusive128(vaddr, value, expected);
73 });
74}
75
76} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h
new file mode 100644
index 000000000..964f4a55d
--- /dev/null
+++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h
@@ -0,0 +1,48 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <unordered_map>
9
10#include <dynarmic/exclusive_monitor.h>
11
12#include "common/common_types.h"
13#include "core/arm/dynarmic/arm_dynarmic_32.h"
14#include "core/arm/dynarmic/arm_dynarmic_64.h"
15#include "core/arm/exclusive_monitor.h"
16
17namespace Core::Memory {
18class Memory;
19}
20
21namespace Core {
22
23class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
24public:
25 explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count);
26 ~DynarmicExclusiveMonitor() override;
27
28 u8 ExclusiveRead8(std::size_t core_index, VAddr addr) override;
29 u16 ExclusiveRead16(std::size_t core_index, VAddr addr) override;
30 u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override;
31 u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override;
32 u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override;
33 void ClearExclusive() override;
34
35 bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override;
36 bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override;
37 bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override;
38 bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override;
39 bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override;
40
41private:
42 friend class ARM_Dynarmic_32;
43 friend class ARM_Dynarmic_64;
44 Dynarmic::ExclusiveMonitor monitor;
45 Core::Memory::Memory& memory;
46};
47
48} // namespace Core
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp
index b32401e0b..d8cba369d 100644
--- a/src/core/arm/exclusive_monitor.cpp
+++ b/src/core/arm/exclusive_monitor.cpp
@@ -3,7 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#ifdef ARCHITECTURE_x86_64 5#ifdef ARCHITECTURE_x86_64
6#include "core/arm/dynarmic/arm_dynarmic_64.h" 6#include "core/arm/dynarmic/arm_exclusive_monitor.h"
7#endif 7#endif
8#include "core/arm/exclusive_monitor.h" 8#include "core/arm/exclusive_monitor.h"
9#include "core/memory.h" 9#include "core/memory.h"
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h
index ccd73b80f..62f6e6023 100644
--- a/src/core/arm/exclusive_monitor.h
+++ b/src/core/arm/exclusive_monitor.h
@@ -18,7 +18,11 @@ class ExclusiveMonitor {
18public: 18public:
19 virtual ~ExclusiveMonitor(); 19 virtual ~ExclusiveMonitor();
20 20
21 virtual void SetExclusive(std::size_t core_index, VAddr addr) = 0; 21 virtual u8 ExclusiveRead8(std::size_t core_index, VAddr addr) = 0;
22 virtual u16 ExclusiveRead16(std::size_t core_index, VAddr addr) = 0;
23 virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0;
24 virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0;
25 virtual u128 ExclusiveRead128(std::size_t core_index, VAddr addr) = 0;
22 virtual void ClearExclusive() = 0; 26 virtual void ClearExclusive() = 0;
23 27
24 virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; 28 virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0;
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index e40e9626a..1df3f3ed1 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -6,6 +6,7 @@
6#include <unicorn/arm64.h> 6#include <unicorn/arm64.h>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/microprofile.h" 8#include "common/microprofile.h"
9#include "core/arm/cpu_interrupt_handler.h"
9#include "core/arm/unicorn/arm_unicorn.h" 10#include "core/arm/unicorn/arm_unicorn.h"
10#include "core/core.h" 11#include "core/core.h"
11#include "core/core_timing.h" 12#include "core/core_timing.h"
@@ -62,7 +63,9 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
62 return false; 63 return false;
63} 64}
64 65
65ARM_Unicorn::ARM_Unicorn(System& system, Arch architecture) : ARM_Interface{system} { 66ARM_Unicorn::ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock,
67 Arch architecture, std::size_t core_index)
68 : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, core_index{core_index} {
66 const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; 69 const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64;
67 CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); 70 CHECKED(uc_open(arch, UC_MODE_ARM, &uc));
68 71
@@ -156,12 +159,20 @@ void ARM_Unicorn::SetTPIDR_EL0(u64 value) {
156 CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value)); 159 CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value));
157} 160}
158 161
162void ARM_Unicorn::ChangeProcessorID(std::size_t new_core_id) {
163 core_index = new_core_id;
164}
165
159void ARM_Unicorn::Run() { 166void ARM_Unicorn::Run() {
160 if (GDBStub::IsServerEnabled()) { 167 if (GDBStub::IsServerEnabled()) {
161 ExecuteInstructions(std::max(4000000U, 0U)); 168 ExecuteInstructions(std::max(4000000U, 0U));
162 } else { 169 } else {
163 ExecuteInstructions( 170 while (true) {
164 std::max(std::size_t(system.CoreTiming().GetDowncount()), std::size_t{0})); 171 if (interrupt_handlers[core_index].IsInterrupted()) {
172 return;
173 }
174 ExecuteInstructions(10);
175 }
165 } 176 }
166} 177}
167 178
@@ -183,8 +194,6 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
183 UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data())); 194 UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data()));
184 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); 195 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
185 CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size())); 196 CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size()));
186
187 system.CoreTiming().AddTicks(num_instructions);
188 if (GDBStub::IsServerEnabled()) { 197 if (GDBStub::IsServerEnabled()) {
189 if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { 198 if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
190 uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); 199 uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index 725c65085..810aff311 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -20,7 +20,8 @@ public:
20 AArch64, // 64-bit ARM 20 AArch64, // 64-bit ARM
21 }; 21 };
22 22
23 explicit ARM_Unicorn(System& system, Arch architecture); 23 explicit ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock,
24 Arch architecture, std::size_t core_index);
24 ~ARM_Unicorn() override; 25 ~ARM_Unicorn() override;
25 26
26 void SetPC(u64 pc) override; 27 void SetPC(u64 pc) override;
@@ -35,6 +36,7 @@ public:
35 void SetTlsAddress(VAddr address) override; 36 void SetTlsAddress(VAddr address) override;
36 void SetTPIDR_EL0(u64 value) override; 37 void SetTPIDR_EL0(u64 value) override;
37 u64 GetTPIDR_EL0() const override; 38 u64 GetTPIDR_EL0() const override;
39 void ChangeProcessorID(std::size_t new_core_id) override;
38 void PrepareReschedule() override; 40 void PrepareReschedule() override;
39 void ClearExclusiveState() override; 41 void ClearExclusiveState() override;
40 void ExecuteInstructions(std::size_t num_instructions); 42 void ExecuteInstructions(std::size_t num_instructions);
@@ -55,6 +57,7 @@ private:
55 uc_engine* uc{}; 57 uc_engine* uc{};
56 GDBStub::BreakpointAddress last_bkpt{}; 58 GDBStub::BreakpointAddress last_bkpt{};
57 bool last_bkpt_hit = false; 59 bool last_bkpt_hit = false;
60 std::size_t core_index;
58}; 61};
59 62
60} // namespace Core 63} // namespace Core
diff --git a/src/core/core.cpp b/src/core/core.cpp
index f9f8a3000..69a1aa0a5 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -8,10 +8,10 @@
8 8
9#include "common/file_util.h" 9#include "common/file_util.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/microprofile.h"
11#include "common/string_util.h" 12#include "common/string_util.h"
12#include "core/arm/exclusive_monitor.h" 13#include "core/arm/exclusive_monitor.h"
13#include "core/core.h" 14#include "core/core.h"
14#include "core/core_manager.h"
15#include "core/core_timing.h" 15#include "core/core_timing.h"
16#include "core/cpu_manager.h" 16#include "core/cpu_manager.h"
17#include "core/device_memory.h" 17#include "core/device_memory.h"
@@ -51,6 +51,11 @@
51#include "video_core/renderer_base.h" 51#include "video_core/renderer_base.h"
52#include "video_core/video_core.h" 52#include "video_core/video_core.h"
53 53
54MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU0, "ARM JIT", "Dynarmic CPU 0", MP_RGB(255, 64, 64));
55MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU1, "ARM JIT", "Dynarmic CPU 1", MP_RGB(255, 64, 64));
56MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU2, "ARM JIT", "Dynarmic CPU 2", MP_RGB(255, 64, 64));
57MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_CPU3, "ARM JIT", "Dynarmic CPU 3", MP_RGB(255, 64, 64));
58
54namespace Core { 59namespace Core {
55 60
56namespace { 61namespace {
@@ -117,23 +122,22 @@ struct System::Impl {
117 : kernel{system}, fs_controller{system}, memory{system}, 122 : kernel{system}, fs_controller{system}, memory{system},
118 cpu_manager{system}, reporter{system}, applet_manager{system} {} 123 cpu_manager{system}, reporter{system}, applet_manager{system} {}
119 124
120 CoreManager& CurrentCoreManager() { 125 ResultStatus Run() {
121 return cpu_manager.GetCurrentCoreManager(); 126 status = ResultStatus::Success;
122 }
123 127
124 Kernel::PhysicalCore& CurrentPhysicalCore() { 128 kernel.Suspend(false);
125 const auto index = cpu_manager.GetActiveCoreIndex(); 129 core_timing.SyncPause(false);
126 return kernel.PhysicalCore(index); 130 cpu_manager.Pause(false);
127 }
128 131
129 Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) { 132 return status;
130 return kernel.PhysicalCore(index);
131 } 133 }
132 134
133 ResultStatus RunLoop(bool tight_loop) { 135 ResultStatus Pause() {
134 status = ResultStatus::Success; 136 status = ResultStatus::Success;
135 137
136 cpu_manager.RunLoop(tight_loop); 138 core_timing.SyncPause(true);
139 kernel.Suspend(true);
140 cpu_manager.Pause(true);
137 141
138 return status; 142 return status;
139 } 143 }
@@ -143,14 +147,22 @@ struct System::Impl {
143 147
144 device_memory = std::make_unique<Core::DeviceMemory>(system); 148 device_memory = std::make_unique<Core::DeviceMemory>(system);
145 149
146 core_timing.Initialize(); 150 is_multicore = Settings::values.use_multi_core.GetValue();
151 is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();
152
153 kernel.SetMulticore(is_multicore);
154 cpu_manager.SetMulticore(is_multicore);
155 cpu_manager.SetAsyncGpu(is_async_gpu);
156 core_timing.SetMulticore(is_multicore);
157
158 core_timing.Initialize([&system]() { system.RegisterHostThread(); });
147 kernel.Initialize(); 159 kernel.Initialize();
148 cpu_manager.Initialize(); 160 cpu_manager.Initialize();
149 161
150 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( 162 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
151 std::chrono::system_clock::now().time_since_epoch()); 163 std::chrono::system_clock::now().time_since_epoch());
152 Settings::values.custom_rtc_differential = 164 Settings::values.custom_rtc_differential =
153 Settings::values.custom_rtc.value_or(current_time) - current_time; 165 Settings::values.custom_rtc.GetValue().value_or(current_time) - current_time;
154 166
155 // Create a default fs if one doesn't already exist. 167 // Create a default fs if one doesn't already exist.
156 if (virtual_filesystem == nullptr) 168 if (virtual_filesystem == nullptr)
@@ -180,6 +192,11 @@ struct System::Impl {
180 is_powered_on = true; 192 is_powered_on = true;
181 exit_lock = false; 193 exit_lock = false;
182 194
195 microprofile_dynarmic[0] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU0);
196 microprofile_dynarmic[1] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU1);
197 microprofile_dynarmic[2] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU2);
198 microprofile_dynarmic[3] = MICROPROFILE_TOKEN(ARM_Jit_Dynarmic_CPU3);
199
183 LOG_DEBUG(Core, "Initialized OK"); 200 LOG_DEBUG(Core, "Initialized OK");
184 201
185 return ResultStatus::Success; 202 return ResultStatus::Success;
@@ -277,8 +294,6 @@ struct System::Impl {
277 service_manager.reset(); 294 service_manager.reset();
278 cheat_engine.reset(); 295 cheat_engine.reset();
279 telemetry_session.reset(); 296 telemetry_session.reset();
280 perf_stats.reset();
281 gpu_core.reset();
282 device_memory.reset(); 297 device_memory.reset();
283 298
284 // Close all CPU/threading state 299 // Close all CPU/threading state
@@ -290,6 +305,8 @@ struct System::Impl {
290 305
291 // Close app loader 306 // Close app loader
292 app_loader.reset(); 307 app_loader.reset();
308 gpu_core.reset();
309 perf_stats.reset();
293 310
294 // Clear all applets 311 // Clear all applets
295 applet_manager.ClearAll(); 312 applet_manager.ClearAll();
@@ -382,25 +399,35 @@ struct System::Impl {
382 399
383 std::unique_ptr<Core::PerfStats> perf_stats; 400 std::unique_ptr<Core::PerfStats> perf_stats;
384 Core::FrameLimiter frame_limiter; 401 Core::FrameLimiter frame_limiter;
402
403 bool is_multicore{};
404 bool is_async_gpu{};
405
406 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
407 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{};
385}; 408};
386 409
387System::System() : impl{std::make_unique<Impl>(*this)} {} 410System::System() : impl{std::make_unique<Impl>(*this)} {}
388System::~System() = default; 411System::~System() = default;
389 412
390CoreManager& System::CurrentCoreManager() { 413CpuManager& System::GetCpuManager() {
391 return impl->CurrentCoreManager(); 414 return impl->cpu_manager;
415}
416
417const CpuManager& System::GetCpuManager() const {
418 return impl->cpu_manager;
392} 419}
393 420
394const CoreManager& System::CurrentCoreManager() const { 421System::ResultStatus System::Run() {
395 return impl->CurrentCoreManager(); 422 return impl->Run();
396} 423}
397 424
398System::ResultStatus System::RunLoop(bool tight_loop) { 425System::ResultStatus System::Pause() {
399 return impl->RunLoop(tight_loop); 426 return impl->Pause();
400} 427}
401 428
402System::ResultStatus System::SingleStep() { 429System::ResultStatus System::SingleStep() {
403 return RunLoop(false); 430 return ResultStatus::Success;
404} 431}
405 432
406void System::InvalidateCpuInstructionCaches() { 433void System::InvalidateCpuInstructionCaches() {
@@ -416,7 +443,7 @@ bool System::IsPoweredOn() const {
416} 443}
417 444
418void System::PrepareReschedule() { 445void System::PrepareReschedule() {
419 impl->CurrentPhysicalCore().Stop(); 446 // Deprecated, does nothing, kept for backward compatibility.
420} 447}
421 448
422void System::PrepareReschedule(const u32 core_index) { 449void System::PrepareReschedule(const u32 core_index) {
@@ -436,31 +463,41 @@ const TelemetrySession& System::TelemetrySession() const {
436} 463}
437 464
438ARM_Interface& System::CurrentArmInterface() { 465ARM_Interface& System::CurrentArmInterface() {
439 return impl->CurrentPhysicalCore().ArmInterface(); 466 return impl->kernel.CurrentScheduler().GetCurrentThread()->ArmInterface();
440} 467}
441 468
442const ARM_Interface& System::CurrentArmInterface() const { 469const ARM_Interface& System::CurrentArmInterface() const {
443 return impl->CurrentPhysicalCore().ArmInterface(); 470 return impl->kernel.CurrentScheduler().GetCurrentThread()->ArmInterface();
444} 471}
445 472
446std::size_t System::CurrentCoreIndex() const { 473std::size_t System::CurrentCoreIndex() const {
447 return impl->cpu_manager.GetActiveCoreIndex(); 474 std::size_t core = impl->kernel.GetCurrentHostThreadID();
475 ASSERT(core < Core::Hardware::NUM_CPU_CORES);
476 return core;
448} 477}
449 478
450Kernel::Scheduler& System::CurrentScheduler() { 479Kernel::Scheduler& System::CurrentScheduler() {
451 return impl->CurrentPhysicalCore().Scheduler(); 480 return impl->kernel.CurrentScheduler();
452} 481}
453 482
454const Kernel::Scheduler& System::CurrentScheduler() const { 483const Kernel::Scheduler& System::CurrentScheduler() const {
455 return impl->CurrentPhysicalCore().Scheduler(); 484 return impl->kernel.CurrentScheduler();
485}
486
487Kernel::PhysicalCore& System::CurrentPhysicalCore() {
488 return impl->kernel.CurrentPhysicalCore();
489}
490
491const Kernel::PhysicalCore& System::CurrentPhysicalCore() const {
492 return impl->kernel.CurrentPhysicalCore();
456} 493}
457 494
458Kernel::Scheduler& System::Scheduler(std::size_t core_index) { 495Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
459 return impl->GetPhysicalCore(core_index).Scheduler(); 496 return impl->kernel.Scheduler(core_index);
460} 497}
461 498
462const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { 499const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const {
463 return impl->GetPhysicalCore(core_index).Scheduler(); 500 return impl->kernel.Scheduler(core_index);
464} 501}
465 502
466/// Gets the global scheduler 503/// Gets the global scheduler
@@ -490,20 +527,15 @@ const Kernel::Process* System::CurrentProcess() const {
490} 527}
491 528
492ARM_Interface& System::ArmInterface(std::size_t core_index) { 529ARM_Interface& System::ArmInterface(std::size_t core_index) {
493 return impl->GetPhysicalCore(core_index).ArmInterface(); 530 auto* thread = impl->kernel.Scheduler(core_index).GetCurrentThread();
531 ASSERT(thread && !thread->IsHLEThread());
532 return thread->ArmInterface();
494} 533}
495 534
496const ARM_Interface& System::ArmInterface(std::size_t core_index) const { 535const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
497 return impl->GetPhysicalCore(core_index).ArmInterface(); 536 auto* thread = impl->kernel.Scheduler(core_index).GetCurrentThread();
498} 537 ASSERT(thread && !thread->IsHLEThread());
499 538 return thread->ArmInterface();
500CoreManager& System::GetCoreManager(std::size_t core_index) {
501 return impl->cpu_manager.GetCoreManager(core_index);
502}
503
504const CoreManager& System::GetCoreManager(std::size_t core_index) const {
505 ASSERT(core_index < NUM_CPU_CORES);
506 return impl->cpu_manager.GetCoreManager(core_index);
507} 539}
508 540
509ExclusiveMonitor& System::Monitor() { 541ExclusiveMonitor& System::Monitor() {
@@ -722,4 +754,18 @@ void System::RegisterHostThread() {
722 impl->kernel.RegisterHostThread(); 754 impl->kernel.RegisterHostThread();
723} 755}
724 756
757void System::EnterDynarmicProfile() {
758 std::size_t core = impl->kernel.GetCurrentHostThreadID();
759 impl->dynarmic_ticks[core] = MicroProfileEnter(impl->microprofile_dynarmic[core]);
760}
761
762void System::ExitDynarmicProfile() {
763 std::size_t core = impl->kernel.GetCurrentHostThreadID();
764 MicroProfileLeave(impl->microprofile_dynarmic[core], impl->dynarmic_ticks[core]);
765}
766
767bool System::IsMulticore() const {
768 return impl->is_multicore;
769}
770
725} // namespace Core 771} // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index acc53d6a1..5c6cfbffe 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -27,6 +27,7 @@ class VfsFilesystem;
27namespace Kernel { 27namespace Kernel {
28class GlobalScheduler; 28class GlobalScheduler;
29class KernelCore; 29class KernelCore;
30class PhysicalCore;
30class Process; 31class Process;
31class Scheduler; 32class Scheduler;
32} // namespace Kernel 33} // namespace Kernel
@@ -90,7 +91,7 @@ class InterruptManager;
90namespace Core { 91namespace Core {
91 92
92class ARM_Interface; 93class ARM_Interface;
93class CoreManager; 94class CpuManager;
94class DeviceMemory; 95class DeviceMemory;
95class ExclusiveMonitor; 96class ExclusiveMonitor;
96class FrameLimiter; 97class FrameLimiter;
@@ -136,16 +137,16 @@ public:
136 }; 137 };
137 138
138 /** 139 /**
139 * Run the core CPU loop 140 * Run the OS and Application
140 * This function runs the core for the specified number of CPU instructions before trying to 141 * This function will start emulation and run the relevant devices
141 * update hardware. This is much faster than SingleStep (and should be equivalent), as the CPU 142 */
142 * is not required to do a full dispatch with each instruction. NOTE: the number of instructions 143 ResultStatus Run();
143 * requested is not guaranteed to run, as this will be interrupted preemptively if a hardware 144
144 * update is requested (e.g. on a thread switch). 145 /**
145 * @param tight_loop If false, the CPU single-steps. 146 * Pause the OS and Application
146 * @return Result status, indicating whether or not the operation succeeded. 147 * This function will pause emulation and stop the relevant devices
147 */ 148 */
148 ResultStatus RunLoop(bool tight_loop = true); 149 ResultStatus Pause();
149 150
150 /** 151 /**
151 * Step the CPU one instruction 152 * Step the CPU one instruction
@@ -209,17 +210,21 @@ public:
209 /// Gets the scheduler for the CPU core that is currently running 210 /// Gets the scheduler for the CPU core that is currently running
210 const Kernel::Scheduler& CurrentScheduler() const; 211 const Kernel::Scheduler& CurrentScheduler() const;
211 212
213 /// Gets the physical core for the CPU core that is currently running
214 Kernel::PhysicalCore& CurrentPhysicalCore();
215
216 /// Gets the physical core for the CPU core that is currently running
217 const Kernel::PhysicalCore& CurrentPhysicalCore() const;
218
212 /// Gets a reference to an ARM interface for the CPU core with the specified index 219 /// Gets a reference to an ARM interface for the CPU core with the specified index
213 ARM_Interface& ArmInterface(std::size_t core_index); 220 ARM_Interface& ArmInterface(std::size_t core_index);
214 221
215 /// Gets a const reference to an ARM interface from the CPU core with the specified index 222 /// Gets a const reference to an ARM interface from the CPU core with the specified index
216 const ARM_Interface& ArmInterface(std::size_t core_index) const; 223 const ARM_Interface& ArmInterface(std::size_t core_index) const;
217 224
218 /// Gets a CPU interface to the CPU core with the specified index 225 CpuManager& GetCpuManager();
219 CoreManager& GetCoreManager(std::size_t core_index);
220 226
221 /// Gets a CPU interface to the CPU core with the specified index 227 const CpuManager& GetCpuManager() const;
222 const CoreManager& GetCoreManager(std::size_t core_index) const;
223 228
224 /// Gets a reference to the exclusive monitor 229 /// Gets a reference to the exclusive monitor
225 ExclusiveMonitor& Monitor(); 230 ExclusiveMonitor& Monitor();
@@ -370,14 +375,17 @@ public:
370 /// Register a host thread as an auxiliary thread. 375 /// Register a host thread as an auxiliary thread.
371 void RegisterHostThread(); 376 void RegisterHostThread();
372 377
373private: 378 /// Enter Dynarmic Microprofile
374 System(); 379 void EnterDynarmicProfile();
380
381 /// Exit Dynarmic Microprofile
382 void ExitDynarmicProfile();
375 383
376 /// Returns the currently running CPU core 384 /// Tells if system is running on multicore.
377 CoreManager& CurrentCoreManager(); 385 bool IsMulticore() const;
378 386
379 /// Returns the currently running CPU core 387private:
380 const CoreManager& CurrentCoreManager() const; 388 System();
381 389
382 /** 390 /**
383 * Initialize the emulated system. 391 * Initialize the emulated system.
diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp
deleted file mode 100644
index b6b797c80..000000000
--- a/src/core/core_manager.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <condition_variable>
6#include <mutex>
7
8#include "common/logging/log.h"
9#include "core/arm/exclusive_monitor.h"
10#include "core/arm/unicorn/arm_unicorn.h"
11#include "core/core.h"
12#include "core/core_manager.h"
13#include "core/core_timing.h"
14#include "core/hle/kernel/kernel.h"
15#include "core/hle/kernel/physical_core.h"
16#include "core/hle/kernel/scheduler.h"
17#include "core/hle/kernel/thread.h"
18#include "core/hle/lock.h"
19#include "core/settings.h"
20
21namespace Core {
22
23CoreManager::CoreManager(System& system, std::size_t core_index)
24 : global_scheduler{system.GlobalScheduler()}, physical_core{system.Kernel().PhysicalCore(
25 core_index)},
26 core_timing{system.CoreTiming()}, core_index{core_index} {}
27
28CoreManager::~CoreManager() = default;
29
30void CoreManager::RunLoop(bool tight_loop) {
31 Reschedule();
32
33 // If we don't have a currently active thread then don't execute instructions,
34 // instead advance to the next event and try to yield to the next thread
35 if (Kernel::GetCurrentThread() == nullptr) {
36 LOG_TRACE(Core, "Core-{} idling", core_index);
37 core_timing.Idle();
38 } else {
39 if (tight_loop) {
40 physical_core.Run();
41 } else {
42 physical_core.Step();
43 }
44 }
45 core_timing.Advance();
46
47 Reschedule();
48}
49
50void CoreManager::SingleStep() {
51 return RunLoop(false);
52}
53
54void CoreManager::PrepareReschedule() {
55 physical_core.Stop();
56}
57
58void CoreManager::Reschedule() {
59 // Lock the global kernel mutex when we manipulate the HLE state
60 std::lock_guard lock(HLE::g_hle_lock);
61
62 global_scheduler.SelectThread(core_index);
63
64 physical_core.Scheduler().TryDoContextSwitch();
65}
66
67} // namespace Core
diff --git a/src/core/core_manager.h b/src/core/core_manager.h
deleted file mode 100644
index d525de00a..000000000
--- a/src/core/core_manager.h
+++ /dev/null
@@ -1,63 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8#include <cstddef>
9#include <memory>
10#include "common/common_types.h"
11
12namespace Kernel {
13class GlobalScheduler;
14class PhysicalCore;
15} // namespace Kernel
16
17namespace Core {
18class System;
19}
20
21namespace Core::Timing {
22class CoreTiming;
23}
24
25namespace Core::Memory {
26class Memory;
27}
28
29namespace Core {
30
31constexpr unsigned NUM_CPU_CORES{4};
32
33class CoreManager {
34public:
35 CoreManager(System& system, std::size_t core_index);
36 ~CoreManager();
37
38 void RunLoop(bool tight_loop = true);
39
40 void SingleStep();
41
42 void PrepareReschedule();
43
44 bool IsMainCore() const {
45 return core_index == 0;
46 }
47
48 std::size_t CoreIndex() const {
49 return core_index;
50 }
51
52private:
53 void Reschedule();
54
55 Kernel::GlobalScheduler& global_scheduler;
56 Kernel::PhysicalCore& physical_core;
57 Timing::CoreTiming& core_timing;
58
59 std::atomic<bool> reschedule_pending = false;
60 std::size_t core_index;
61};
62
63} // namespace Core
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 46d4178c4..a63e60461 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -1,29 +1,27 @@
1// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2+ 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core_timing.h"
6
7#include <algorithm> 5#include <algorithm>
8#include <mutex> 6#include <mutex>
9#include <string> 7#include <string>
10#include <tuple> 8#include <tuple>
11 9
12#include "common/assert.h" 10#include "common/assert.h"
13#include "common/thread.h" 11#include "common/microprofile.h"
12#include "core/core_timing.h"
14#include "core/core_timing_util.h" 13#include "core/core_timing_util.h"
15#include "core/hardware_properties.h"
16 14
17namespace Core::Timing { 15namespace Core::Timing {
18 16
19constexpr int MAX_SLICE_LENGTH = 10000; 17constexpr u64 MAX_SLICE_LENGTH = 4000;
20 18
21std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { 19std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
22 return std::make_shared<EventType>(std::move(callback), std::move(name)); 20 return std::make_shared<EventType>(std::move(callback), std::move(name));
23} 21}
24 22
25struct CoreTiming::Event { 23struct CoreTiming::Event {
26 s64 time; 24 u64 time;
27 u64 fifo_order; 25 u64 fifo_order;
28 u64 userdata; 26 u64 userdata;
29 std::weak_ptr<EventType> type; 27 std::weak_ptr<EventType> type;
@@ -39,51 +37,90 @@ struct CoreTiming::Event {
39 } 37 }
40}; 38};
41 39
42CoreTiming::CoreTiming() = default; 40CoreTiming::CoreTiming() {
43CoreTiming::~CoreTiming() = default; 41 clock =
42 Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ);
43}
44 44
45void CoreTiming::Initialize() { 45CoreTiming::~CoreTiming() = default;
46 downcounts.fill(MAX_SLICE_LENGTH);
47 time_slice.fill(MAX_SLICE_LENGTH);
48 slice_length = MAX_SLICE_LENGTH;
49 global_timer = 0;
50 idled_cycles = 0;
51 current_context = 0;
52 46
53 // The time between CoreTiming being initialized and the first call to Advance() is considered 47void CoreTiming::ThreadEntry(CoreTiming& instance) {
54 // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before 48 constexpr char name[] = "yuzu:HostTiming";
55 // executing the first cycle of each slice to prepare the slice length and downcount for 49 MicroProfileOnThreadCreate(name);
56 // that slice. 50 Common::SetCurrentThreadName(name);
57 is_global_timer_sane = true; 51 Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
52 instance.on_thread_init();
53 instance.ThreadLoop();
54}
58 55
56void CoreTiming::Initialize(std::function<void(void)>&& on_thread_init_) {
57 on_thread_init = std::move(on_thread_init_);
59 event_fifo_id = 0; 58 event_fifo_id = 0;
60 59 shutting_down = false;
60 ticks = 0;
61 const auto empty_timed_callback = [](u64, s64) {}; 61 const auto empty_timed_callback = [](u64, s64) {};
62 ev_lost = CreateEvent("_lost_event", empty_timed_callback); 62 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
63 if (is_multicore) {
64 timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
65 }
63} 66}
64 67
65void CoreTiming::Shutdown() { 68void CoreTiming::Shutdown() {
69 paused = true;
70 shutting_down = true;
71 pause_event.Set();
72 event.Set();
73 if (timer_thread) {
74 timer_thread->join();
75 }
66 ClearPendingEvents(); 76 ClearPendingEvents();
77 timer_thread.reset();
78 has_started = false;
67} 79}
68 80
69void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type, 81void CoreTiming::Pause(bool is_paused) {
70 u64 userdata) { 82 paused = is_paused;
71 std::lock_guard guard{inner_mutex}; 83 pause_event.Set();
72 const s64 timeout = GetTicks() + cycles_into_future; 84}
73 85
74 // If this event needs to be scheduled before the next advance(), force one early 86void CoreTiming::SyncPause(bool is_paused) {
75 if (!is_global_timer_sane) { 87 if (is_paused == paused && paused_set == paused) {
76 ForceExceptionCheck(cycles_into_future); 88 return;
89 }
90 Pause(is_paused);
91 if (timer_thread) {
92 if (!is_paused) {
93 pause_event.Set();
94 }
95 event.Set();
96 while (paused_set != is_paused)
97 ;
77 } 98 }
99}
78 100
79 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); 101bool CoreTiming::IsRunning() const {
102 return !paused_set;
103}
80 104
81 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 105bool CoreTiming::HasPendingEvents() const {
106 return !(wait_set && event_queue.empty());
82} 107}
83 108
84void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { 109void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type,
85 std::lock_guard guard{inner_mutex}; 110 u64 userdata) {
111 {
112 std::scoped_lock scope{basic_lock};
113 const u64 timeout = static_cast<u64>(GetGlobalTimeNs().count() + ns_into_future);
114
115 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
86 116
117 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
118 }
119 event.Set();
120}
121
122void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) {
123 std::scoped_lock scope{basic_lock};
87 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 124 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
88 return e.type.lock().get() == event_type.get() && e.userdata == userdata; 125 return e.type.lock().get() == event_type.get() && e.userdata == userdata;
89 }); 126 });
@@ -95,21 +132,39 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u
95 } 132 }
96} 133}
97 134
98u64 CoreTiming::GetTicks() const { 135void CoreTiming::AddTicks(u64 ticks) {
99 u64 ticks = static_cast<u64>(global_timer); 136 this->ticks += ticks;
100 if (!is_global_timer_sane) { 137 downcount -= ticks;
101 ticks += accumulated_ticks; 138}
139
140void CoreTiming::Idle() {
141 if (!event_queue.empty()) {
142 const u64 next_event_time = event_queue.front().time;
143 const u64 next_ticks = nsToCycles(std::chrono::nanoseconds(next_event_time)) + 10U;
144 if (next_ticks > ticks) {
145 ticks = next_ticks;
146 }
147 return;
102 } 148 }
103 return ticks; 149 ticks += 1000U;
104} 150}
105 151
106u64 CoreTiming::GetIdleTicks() const { 152void CoreTiming::ResetTicks() {
107 return static_cast<u64>(idled_cycles); 153 downcount = MAX_SLICE_LENGTH;
108} 154}
109 155
110void CoreTiming::AddTicks(u64 ticks) { 156u64 CoreTiming::GetCPUTicks() const {
111 accumulated_ticks += ticks; 157 if (is_multicore) {
112 downcounts[current_context] -= static_cast<s64>(ticks); 158 return clock->GetCPUCycles();
159 }
160 return ticks;
161}
162
163u64 CoreTiming::GetClockTicks() const {
164 if (is_multicore) {
165 return clock->GetClockCycles();
166 }
167 return CpuCyclesToClockCycles(ticks);
113} 168}
114 169
115void CoreTiming::ClearPendingEvents() { 170void CoreTiming::ClearPendingEvents() {
@@ -117,7 +172,7 @@ void CoreTiming::ClearPendingEvents() {
117} 172}
118 173
119void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { 174void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
120 std::lock_guard guard{inner_mutex}; 175 std::scoped_lock lock{basic_lock};
121 176
122 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 177 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
123 return e.type.lock().get() == event_type.get(); 178 return e.type.lock().get() == event_type.get();
@@ -130,97 +185,68 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
130 } 185 }
131} 186}
132 187
133void CoreTiming::ForceExceptionCheck(s64 cycles) { 188std::optional<s64> CoreTiming::Advance() {
134 cycles = std::max<s64>(0, cycles); 189 std::scoped_lock lock{advance_lock, basic_lock};
135 if (downcounts[current_context] <= cycles) { 190 global_timer = GetGlobalTimeNs().count();
136 return;
137 }
138
139 // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int
140 // here. Account for cycles already executed by adjusting the g.slice_length
141 downcounts[current_context] = static_cast<int>(cycles);
142}
143
144std::optional<u64> CoreTiming::NextAvailableCore(const s64 needed_ticks) const {
145 const u64 original_context = current_context;
146 u64 next_context = (original_context + 1) % num_cpu_cores;
147 while (next_context != original_context) {
148 if (time_slice[next_context] >= needed_ticks) {
149 return {next_context};
150 } else if (time_slice[next_context] >= 0) {
151 return std::nullopt;
152 }
153 next_context = (next_context + 1) % num_cpu_cores;
154 }
155 return std::nullopt;
156}
157
158void CoreTiming::Advance() {
159 std::unique_lock<std::mutex> guard(inner_mutex);
160
161 const u64 cycles_executed = accumulated_ticks;
162 time_slice[current_context] = std::max<s64>(0, time_slice[current_context] - accumulated_ticks);
163 global_timer += cycles_executed;
164
165 is_global_timer_sane = true;
166 191
167 while (!event_queue.empty() && event_queue.front().time <= global_timer) { 192 while (!event_queue.empty() && event_queue.front().time <= global_timer) {
168 Event evt = std::move(event_queue.front()); 193 Event evt = std::move(event_queue.front());
169 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 194 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
170 event_queue.pop_back(); 195 event_queue.pop_back();
171 inner_mutex.unlock(); 196 basic_lock.unlock();
172 197
173 if (auto event_type{evt.type.lock()}) { 198 if (auto event_type{evt.type.lock()}) {
174 event_type->callback(evt.userdata, global_timer - evt.time); 199 event_type->callback(evt.userdata, global_timer - evt.time);
175 } 200 }
176 201
177 inner_mutex.lock(); 202 basic_lock.lock();
203 global_timer = GetGlobalTimeNs().count();
178 } 204 }
179 205
180 is_global_timer_sane = false;
181
182 // Still events left (scheduled in the future)
183 if (!event_queue.empty()) { 206 if (!event_queue.empty()) {
184 const s64 needed_ticks = 207 const s64 next_time = event_queue.front().time - global_timer;
185 std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); 208 return next_time;
186 const auto next_core = NextAvailableCore(needed_ticks); 209 } else {
187 if (next_core) { 210 return std::nullopt;
188 downcounts[*next_core] = needed_ticks;
189 }
190 } 211 }
191
192 accumulated_ticks = 0;
193
194 downcounts[current_context] = time_slice[current_context];
195} 212}
196 213
197void CoreTiming::ResetRun() { 214void CoreTiming::ThreadLoop() {
198 downcounts.fill(MAX_SLICE_LENGTH); 215 has_started = true;
199 time_slice.fill(MAX_SLICE_LENGTH); 216 while (!shutting_down) {
200 current_context = 0; 217 while (!paused) {
201 // Still events left (scheduled in the future) 218 paused_set = false;
202 if (!event_queue.empty()) { 219 const auto next_time = Advance();
203 const s64 needed_ticks = 220 if (next_time) {
204 std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); 221 if (*next_time > 0) {
205 downcounts[current_context] = needed_ticks; 222 std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time);
223 event.WaitFor(next_time_ns);
224 }
225 } else {
226 wait_set = true;
227 event.Wait();
228 }
229 wait_set = false;
230 }
231 paused_set = true;
232 clock->Pause(true);
233 pause_event.Wait();
234 clock->Pause(false);
206 } 235 }
207
208 is_global_timer_sane = false;
209 accumulated_ticks = 0;
210} 236}
211 237
212void CoreTiming::Idle() { 238std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
213 accumulated_ticks += downcounts[current_context]; 239 if (is_multicore) {
214 idled_cycles += downcounts[current_context]; 240 return clock->GetTimeNS();
215 downcounts[current_context] = 0; 241 }
242 return CyclesToNs(ticks);
216} 243}
217 244
218std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { 245std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
219 return std::chrono::microseconds{GetTicks() * 1000000 / Hardware::BASE_CLOCK_RATE}; 246 if (is_multicore) {
220} 247 return clock->GetTimeUS();
221 248 }
222s64 CoreTiming::GetDowncount() const { 249 return CyclesToUs(ticks);
223 return downcounts[current_context];
224} 250}
225 251
226} // namespace Core::Timing 252} // namespace Core::Timing
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index d50f4eb8a..72faaab64 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -1,19 +1,25 @@
1// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2+ 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <chrono> 8#include <chrono>
8#include <functional> 9#include <functional>
9#include <memory> 10#include <memory>
10#include <mutex> 11#include <mutex>
11#include <optional> 12#include <optional>
12#include <string> 13#include <string>
14#include <thread>
13#include <vector> 15#include <vector>
14 16
15#include "common/common_types.h" 17#include "common/common_types.h"
18#include "common/spin_lock.h"
19#include "common/thread.h"
16#include "common/threadsafe_queue.h" 20#include "common/threadsafe_queue.h"
21#include "common/wall_clock.h"
22#include "core/hardware_properties.h"
17 23
18namespace Core::Timing { 24namespace Core::Timing {
19 25
@@ -56,16 +62,40 @@ public:
56 62
57 /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is 63 /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
58 /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. 64 /// required to end slice - 1 and start slice 0 before the first cycle of code is executed.
59 void Initialize(); 65 void Initialize(std::function<void(void)>&& on_thread_init_);
60 66
61 /// Tears down all timing related functionality. 67 /// Tears down all timing related functionality.
62 void Shutdown(); 68 void Shutdown();
63 69
64 /// After the first Advance, the slice lengths and the downcount will be reduced whenever an 70 /// Sets if emulation is multicore or single core, must be set before Initialize
65 /// event is scheduled earlier than the current values. 71 void SetMulticore(bool is_multicore) {
66 /// 72 this->is_multicore = is_multicore;
67 /// Scheduling from a callback will not update the downcount until the Advance() completes. 73 }
68 void ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type, 74
75 /// Check if it's using host timing.
76 bool IsHostTiming() const {
77 return is_multicore;
78 }
79
80 /// Pauses/Unpauses the execution of the timer thread.
81 void Pause(bool is_paused);
82
83 /// Pauses/Unpauses the execution of the timer thread and waits until paused.
84 void SyncPause(bool is_paused);
85
86 /// Checks if core timing is running.
87 bool IsRunning() const;
88
89 /// Checks if the timer thread has started.
90 bool HasStarted() const {
91 return has_started;
92 }
93
94 /// Checks if there are any pending time events.
95 bool HasPendingEvents() const;
96
97 /// Schedules an event in core timing
98 void ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type,
69 u64 userdata = 0); 99 u64 userdata = 0);
70 100
71 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); 101 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata);
@@ -73,41 +103,30 @@ public:
73 /// We only permit one event of each type in the queue at a time. 103 /// We only permit one event of each type in the queue at a time.
74 void RemoveEvent(const std::shared_ptr<EventType>& event_type); 104 void RemoveEvent(const std::shared_ptr<EventType>& event_type);
75 105
76 void ForceExceptionCheck(s64 cycles);
77
78 /// This should only be called from the emu thread, if you are calling it any other thread,
79 /// you are doing something evil
80 u64 GetTicks() const;
81
82 u64 GetIdleTicks() const;
83
84 void AddTicks(u64 ticks); 106 void AddTicks(u64 ticks);
85 107
86 /// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends 108 void ResetTicks();
87 /// the previous timing slice and begins the next one, you must Advance from the previous
88 /// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
89 /// Advance() is required to initialize the slice length before the first cycle of emulated
90 /// instructions is executed.
91 void Advance();
92 109
93 /// Pretend that the main CPU has executed enough cycles to reach the next event.
94 void Idle(); 110 void Idle();
95 111
96 std::chrono::microseconds GetGlobalTimeUs() const; 112 s64 GetDowncount() const {
113 return downcount;
114 }
97 115
98 void ResetRun(); 116 /// Returns current time in emulated CPU cycles
117 u64 GetCPUTicks() const;
99 118
100 s64 GetDowncount() const; 119 /// Returns current time in emulated in Clock cycles
120 u64 GetClockTicks() const;
101 121
102 void SwitchContext(u64 new_context) { 122 /// Returns current time in microseconds.
103 current_context = new_context; 123 std::chrono::microseconds GetGlobalTimeUs() const;
104 }
105 124
106 bool CanCurrentContextRun() const { 125 /// Returns current time in nanoseconds.
107 return time_slice[current_context] > 0; 126 std::chrono::nanoseconds GetGlobalTimeNs() const;
108 }
109 127
110 std::optional<u64> NextAvailableCore(const s64 needed_ticks) const; 128 /// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
129 std::optional<s64> Advance();
111 130
112private: 131private:
113 struct Event; 132 struct Event;
@@ -115,21 +134,14 @@ private:
115 /// Clear all pending events. This should ONLY be done on exit. 134 /// Clear all pending events. This should ONLY be done on exit.
116 void ClearPendingEvents(); 135 void ClearPendingEvents();
117 136
118 static constexpr u64 num_cpu_cores = 4; 137 static void ThreadEntry(CoreTiming& instance);
138 void ThreadLoop();
119 139
120 s64 global_timer = 0; 140 std::unique_ptr<Common::WallClock> clock;
121 s64 idled_cycles = 0;
122 s64 slice_length = 0;
123 u64 accumulated_ticks = 0;
124 std::array<s64, num_cpu_cores> downcounts{};
125 // Slice of time assigned to each core per run.
126 std::array<s64, num_cpu_cores> time_slice{};
127 u64 current_context = 0;
128 141
129 // Are we in a function that has been called from Advance() 142 u64 global_timer = 0;
130 // If events are scheduled from a function that gets called from Advance(), 143
131 // don't change slice_length and downcount. 144 std::chrono::nanoseconds start_point;
132 bool is_global_timer_sane = false;
133 145
134 // The queue is a min-heap using std::make_heap/push_heap/pop_heap. 146 // The queue is a min-heap using std::make_heap/push_heap/pop_heap.
135 // We don't use std::priority_queue because we need to be able to serialize, unserialize and 147 // We don't use std::priority_queue because we need to be able to serialize, unserialize and
@@ -139,8 +151,23 @@ private:
139 u64 event_fifo_id = 0; 151 u64 event_fifo_id = 0;
140 152
141 std::shared_ptr<EventType> ev_lost; 153 std::shared_ptr<EventType> ev_lost;
142 154 Common::Event event{};
143 std::mutex inner_mutex; 155 Common::Event pause_event{};
156 Common::SpinLock basic_lock{};
157 Common::SpinLock advance_lock{};
158 std::unique_ptr<std::thread> timer_thread;
159 std::atomic<bool> paused{};
160 std::atomic<bool> paused_set{};
161 std::atomic<bool> wait_set{};
162 std::atomic<bool> shutting_down{};
163 std::atomic<bool> has_started{};
164 std::function<void(void)> on_thread_init{};
165
166 bool is_multicore{};
167
168 /// Cycle timing
169 u64 ticks{};
170 s64 downcount{};
144}; 171};
145 172
146/// Creates a core timing event with the given name and callback. 173/// Creates a core timing event with the given name and callback.
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp
index de50d3b14..aefc63663 100644
--- a/src/core/core_timing_util.cpp
+++ b/src/core/core_timing_util.cpp
@@ -38,15 +38,23 @@ s64 usToCycles(std::chrono::microseconds us) {
38} 38}
39 39
40s64 nsToCycles(std::chrono::nanoseconds ns) { 40s64 nsToCycles(std::chrono::nanoseconds ns) {
41 if (static_cast<u64>(ns.count() / 1000000000) > MAX_VALUE_TO_MULTIPLY) { 41 const u128 temporal = Common::Multiply64Into128(ns.count(), Hardware::BASE_CLOCK_RATE);
42 LOG_ERROR(Core_Timing, "Integer overflow, use max value"); 42 return Common::Divide128On32(temporal, static_cast<u32>(1000000000)).first;
43 return std::numeric_limits<s64>::max(); 43}
44 } 44
45 if (static_cast<u64>(ns.count()) > MAX_VALUE_TO_MULTIPLY) { 45u64 msToClockCycles(std::chrono::milliseconds ns) {
46 LOG_DEBUG(Core_Timing, "Time very big, do rounding"); 46 const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
47 return Hardware::BASE_CLOCK_RATE * (ns.count() / 1000000000); 47 return Common::Divide128On32(temp, 1000).first;
48 } 48}
49 return (Hardware::BASE_CLOCK_RATE * ns.count()) / 1000000000; 49
50u64 usToClockCycles(std::chrono::microseconds ns) {
51 const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
52 return Common::Divide128On32(temp, 1000000).first;
53}
54
55u64 nsToClockCycles(std::chrono::nanoseconds ns) {
56 const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
57 return Common::Divide128On32(temp, 1000000000).first;
50} 58}
51 59
52u64 CpuCyclesToClockCycles(u64 ticks) { 60u64 CpuCyclesToClockCycles(u64 ticks) {
@@ -54,4 +62,22 @@ u64 CpuCyclesToClockCycles(u64 ticks) {
54 return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; 62 return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
55} 63}
56 64
65std::chrono::milliseconds CyclesToMs(s64 cycles) {
66 const u128 temporal = Common::Multiply64Into128(cycles, 1000);
67 u64 ms = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
68 return std::chrono::milliseconds(ms);
69}
70
71std::chrono::nanoseconds CyclesToNs(s64 cycles) {
72 const u128 temporal = Common::Multiply64Into128(cycles, 1000000000);
73 u64 ns = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
74 return std::chrono::nanoseconds(ns);
75}
76
77std::chrono::microseconds CyclesToUs(s64 cycles) {
78 const u128 temporal = Common::Multiply64Into128(cycles, 1000000);
79 u64 us = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
80 return std::chrono::microseconds(us);
81}
82
57} // namespace Core::Timing 83} // namespace Core::Timing
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index addc72b19..2ed979e14 100644
--- a/src/core/core_timing_util.h
+++ b/src/core/core_timing_util.h
@@ -13,18 +13,12 @@ namespace Core::Timing {
13s64 msToCycles(std::chrono::milliseconds ms); 13s64 msToCycles(std::chrono::milliseconds ms);
14s64 usToCycles(std::chrono::microseconds us); 14s64 usToCycles(std::chrono::microseconds us);
15s64 nsToCycles(std::chrono::nanoseconds ns); 15s64 nsToCycles(std::chrono::nanoseconds ns);
16 16u64 msToClockCycles(std::chrono::milliseconds ns);
17inline std::chrono::milliseconds CyclesToMs(s64 cycles) { 17u64 usToClockCycles(std::chrono::microseconds ns);
18 return std::chrono::milliseconds(cycles * 1000 / Hardware::BASE_CLOCK_RATE); 18u64 nsToClockCycles(std::chrono::nanoseconds ns);
19} 19std::chrono::milliseconds CyclesToMs(s64 cycles);
20 20std::chrono::nanoseconds CyclesToNs(s64 cycles);
21inline std::chrono::nanoseconds CyclesToNs(s64 cycles) { 21std::chrono::microseconds CyclesToUs(s64 cycles);
22 return std::chrono::nanoseconds(cycles * 1000000000 / Hardware::BASE_CLOCK_RATE);
23}
24
25inline std::chrono::microseconds CyclesToUs(s64 cycles) {
26 return std::chrono::microseconds(cycles * 1000000 / Hardware::BASE_CLOCK_RATE);
27}
28 22
29u64 CpuCyclesToClockCycles(u64 ticks); 23u64 CpuCyclesToClockCycles(u64 ticks);
30 24
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 70ddbdcca..32afcf3ae 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -2,80 +2,372 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/fiber.h"
6#include "common/microprofile.h"
7#include "common/thread.h"
5#include "core/arm/exclusive_monitor.h" 8#include "core/arm/exclusive_monitor.h"
6#include "core/core.h" 9#include "core/core.h"
7#include "core/core_manager.h"
8#include "core/core_timing.h" 10#include "core/core_timing.h"
9#include "core/cpu_manager.h" 11#include "core/cpu_manager.h"
10#include "core/gdbstub/gdbstub.h" 12#include "core/gdbstub/gdbstub.h"
13#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/physical_core.h"
15#include "core/hle/kernel/scheduler.h"
16#include "core/hle/kernel/thread.h"
17#include "video_core/gpu.h"
11 18
12namespace Core { 19namespace Core {
13 20
14CpuManager::CpuManager(System& system) : system{system} {} 21CpuManager::CpuManager(System& system) : system{system} {}
15CpuManager::~CpuManager() = default; 22CpuManager::~CpuManager() = default;
16 23
24void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) {
25 cpu_manager.RunThread(core);
26}
27
17void CpuManager::Initialize() { 28void CpuManager::Initialize() {
18 for (std::size_t index = 0; index < core_managers.size(); ++index) { 29 running_mode = true;
19 core_managers[index] = std::make_unique<CoreManager>(system, index); 30 if (is_multicore) {
31 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
32 core_data[core].host_thread =
33 std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
34 }
35 } else {
36 core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0);
20 } 37 }
21} 38}
22 39
23void CpuManager::Shutdown() { 40void CpuManager::Shutdown() {
24 for (auto& cpu_core : core_managers) { 41 running_mode = false;
25 cpu_core.reset(); 42 Pause(false);
43 if (is_multicore) {
44 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
45 core_data[core].host_thread->join();
46 core_data[core].host_thread.reset();
47 }
48 } else {
49 core_data[0].host_thread->join();
50 core_data[0].host_thread.reset();
26 } 51 }
27} 52}
28 53
29CoreManager& CpuManager::GetCoreManager(std::size_t index) { 54std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
30 return *core_managers.at(index); 55 return std::function<void(void*)>(GuestThreadFunction);
31} 56}
32 57
33const CoreManager& CpuManager::GetCoreManager(std::size_t index) const { 58std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
34 return *core_managers.at(index); 59 return std::function<void(void*)>(IdleThreadFunction);
35} 60}
36 61
37CoreManager& CpuManager::GetCurrentCoreManager() { 62std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
38 // Otherwise, use single-threaded mode active_core variable 63 return std::function<void(void*)>(SuspendThreadFunction);
39 return *core_managers[active_core];
40} 64}
41 65
42const CoreManager& CpuManager::GetCurrentCoreManager() const { 66void CpuManager::GuestThreadFunction(void* cpu_manager_) {
43 // Otherwise, use single-threaded mode active_core variable 67 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
44 return *core_managers[active_core]; 68 if (cpu_manager->is_multicore) {
69 cpu_manager->MultiCoreRunGuestThread();
70 } else {
71 cpu_manager->SingleCoreRunGuestThread();
72 }
45} 73}
46 74
47void CpuManager::RunLoop(bool tight_loop) { 75void CpuManager::GuestRewindFunction(void* cpu_manager_) {
48 if (GDBStub::IsServerEnabled()) { 76 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
49 GDBStub::HandlePacket(); 77 if (cpu_manager->is_multicore) {
78 cpu_manager->MultiCoreRunGuestLoop();
79 } else {
80 cpu_manager->SingleCoreRunGuestLoop();
81 }
82}
50 83
51 // If the loop is halted and we want to step, use a tiny (1) number of instructions to 84void CpuManager::IdleThreadFunction(void* cpu_manager_) {
52 // execute. Otherwise, get out of the loop function. 85 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
53 if (GDBStub::GetCpuHaltFlag()) { 86 if (cpu_manager->is_multicore) {
54 if (GDBStub::GetCpuStepFlag()) { 87 cpu_manager->MultiCoreRunIdleThread();
55 tight_loop = false; 88 } else {
56 } else { 89 cpu_manager->SingleCoreRunIdleThread();
57 return; 90 }
91}
92
93void CpuManager::SuspendThreadFunction(void* cpu_manager_) {
94 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
95 if (cpu_manager->is_multicore) {
96 cpu_manager->MultiCoreRunSuspendThread();
97 } else {
98 cpu_manager->SingleCoreRunSuspendThread();
99 }
100}
101
102void* CpuManager::GetStartFuncParamater() {
103 return static_cast<void*>(this);
104}
105
106///////////////////////////////////////////////////////////////////////////////
107/// MultiCore ///
108///////////////////////////////////////////////////////////////////////////////
109
110void CpuManager::MultiCoreRunGuestThread() {
111 auto& kernel = system.Kernel();
112 {
113 auto& sched = kernel.CurrentScheduler();
114 sched.OnThreadStart();
115 }
116 MultiCoreRunGuestLoop();
117}
118
119void CpuManager::MultiCoreRunGuestLoop() {
120 auto& kernel = system.Kernel();
121 auto* thread = kernel.CurrentScheduler().GetCurrentThread();
122 while (true) {
123 auto* physical_core = &kernel.CurrentPhysicalCore();
124 auto& arm_interface = thread->ArmInterface();
125 system.EnterDynarmicProfile();
126 while (!physical_core->IsInterrupted()) {
127 arm_interface.Run();
128 physical_core = &kernel.CurrentPhysicalCore();
129 }
130 system.ExitDynarmicProfile();
131 arm_interface.ClearExclusiveState();
132 auto& scheduler = kernel.CurrentScheduler();
133 scheduler.TryDoContextSwitch();
134 }
135}
136
137void CpuManager::MultiCoreRunIdleThread() {
138 auto& kernel = system.Kernel();
139 while (true) {
140 auto& physical_core = kernel.CurrentPhysicalCore();
141 physical_core.Idle();
142 auto& scheduler = kernel.CurrentScheduler();
143 scheduler.TryDoContextSwitch();
144 }
145}
146
147void CpuManager::MultiCoreRunSuspendThread() {
148 auto& kernel = system.Kernel();
149 {
150 auto& sched = kernel.CurrentScheduler();
151 sched.OnThreadStart();
152 }
153 while (true) {
154 auto core = kernel.GetCurrentHostThreadID();
155 auto& scheduler = kernel.CurrentScheduler();
156 Kernel::Thread* current_thread = scheduler.GetCurrentThread();
157 Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
158 ASSERT(scheduler.ContextSwitchPending());
159 ASSERT(core == kernel.GetCurrentHostThreadID());
160 scheduler.TryDoContextSwitch();
161 }
162}
163
164void CpuManager::MultiCorePause(bool paused) {
165 if (!paused) {
166 bool all_not_barrier = false;
167 while (!all_not_barrier) {
168 all_not_barrier = true;
169 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
170 all_not_barrier &=
171 !core_data[core].is_running.load() && core_data[core].initialized.load();
172 }
173 }
174 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
175 core_data[core].enter_barrier->Set();
176 }
177 if (paused_state.load()) {
178 bool all_barrier = false;
179 while (!all_barrier) {
180 all_barrier = true;
181 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
182 all_barrier &=
183 core_data[core].is_paused.load() && core_data[core].initialized.load();
184 }
185 }
186 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
187 core_data[core].exit_barrier->Set();
188 }
189 }
190 } else {
191 /// Wait until all cores are paused.
192 bool all_barrier = false;
193 while (!all_barrier) {
194 all_barrier = true;
195 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
196 all_barrier &=
197 core_data[core].is_paused.load() && core_data[core].initialized.load();
58 } 198 }
59 } 199 }
200 /// Don't release the barrier
60 } 201 }
202 paused_state = paused;
203}
204
205///////////////////////////////////////////////////////////////////////////////
206/// SingleCore ///
207///////////////////////////////////////////////////////////////////////////////
61 208
62 auto& core_timing = system.CoreTiming(); 209void CpuManager::SingleCoreRunGuestThread() {
63 core_timing.ResetRun(); 210 auto& kernel = system.Kernel();
64 bool keep_running{}; 211 {
65 do { 212 auto& sched = kernel.CurrentScheduler();
66 keep_running = false; 213 sched.OnThreadStart();
67 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { 214 }
68 core_timing.SwitchContext(active_core); 215 SingleCoreRunGuestLoop();
69 if (core_timing.CanCurrentContextRun()) { 216}
70 core_managers[active_core]->RunLoop(tight_loop); 217
218void CpuManager::SingleCoreRunGuestLoop() {
219 auto& kernel = system.Kernel();
220 auto* thread = kernel.CurrentScheduler().GetCurrentThread();
221 while (true) {
222 auto* physical_core = &kernel.CurrentPhysicalCore();
223 auto& arm_interface = thread->ArmInterface();
224 system.EnterDynarmicProfile();
225 if (!physical_core->IsInterrupted()) {
226 arm_interface.Run();
227 physical_core = &kernel.CurrentPhysicalCore();
228 }
229 system.ExitDynarmicProfile();
230 thread->SetPhantomMode(true);
231 system.CoreTiming().Advance();
232 thread->SetPhantomMode(false);
233 arm_interface.ClearExclusiveState();
234 PreemptSingleCore();
235 auto& scheduler = kernel.Scheduler(current_core);
236 scheduler.TryDoContextSwitch();
237 }
238}
239
240void CpuManager::SingleCoreRunIdleThread() {
241 auto& kernel = system.Kernel();
242 while (true) {
243 auto& physical_core = kernel.CurrentPhysicalCore();
244 PreemptSingleCore(false);
245 system.CoreTiming().AddTicks(1000U);
246 idle_count++;
247 auto& scheduler = physical_core.Scheduler();
248 scheduler.TryDoContextSwitch();
249 }
250}
251
252void CpuManager::SingleCoreRunSuspendThread() {
253 auto& kernel = system.Kernel();
254 {
255 auto& sched = kernel.CurrentScheduler();
256 sched.OnThreadStart();
257 }
258 while (true) {
259 auto core = kernel.GetCurrentHostThreadID();
260 auto& scheduler = kernel.CurrentScheduler();
261 Kernel::Thread* current_thread = scheduler.GetCurrentThread();
262 Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
263 ASSERT(scheduler.ContextSwitchPending());
264 ASSERT(core == kernel.GetCurrentHostThreadID());
265 scheduler.TryDoContextSwitch();
266 }
267}
268
269void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
270 std::size_t old_core = current_core;
271 auto& scheduler = system.Kernel().Scheduler(old_core);
272 Kernel::Thread* current_thread = scheduler.GetCurrentThread();
273 if (idle_count >= 4 || from_running_enviroment) {
274 if (!from_running_enviroment) {
275 system.CoreTiming().Idle();
276 idle_count = 0;
277 }
278 current_thread->SetPhantomMode(true);
279 system.CoreTiming().Advance();
280 current_thread->SetPhantomMode(false);
281 }
282 current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
283 system.CoreTiming().ResetTicks();
284 scheduler.Unload();
285 auto& next_scheduler = system.Kernel().Scheduler(current_core);
286 Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
287 /// May have changed scheduler
288 auto& current_scheduler = system.Kernel().Scheduler(current_core);
289 current_scheduler.Reload();
290 auto* currrent_thread2 = current_scheduler.GetCurrentThread();
291 if (!currrent_thread2->IsIdleThread()) {
292 idle_count = 0;
293 }
294}
295
296void CpuManager::SingleCorePause(bool paused) {
297 if (!paused) {
298 bool all_not_barrier = false;
299 while (!all_not_barrier) {
300 all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load();
301 }
302 core_data[0].enter_barrier->Set();
303 if (paused_state.load()) {
304 bool all_barrier = false;
305 while (!all_barrier) {
306 all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
71 } 307 }
72 keep_running |= core_timing.CanCurrentContextRun(); 308 core_data[0].exit_barrier->Set();
73 } 309 }
74 } while (keep_running); 310 } else {
311 /// Wait until all cores are paused.
312 bool all_barrier = false;
313 while (!all_barrier) {
314 all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
315 }
316 /// Don't release the barrier
317 }
318 paused_state = paused;
319}
320
321void CpuManager::Pause(bool paused) {
322 if (is_multicore) {
323 MultiCorePause(paused);
324 } else {
325 SingleCorePause(paused);
326 }
327}
75 328
76 if (GDBStub::IsServerEnabled()) { 329void CpuManager::RunThread(std::size_t core) {
77 GDBStub::SetCpuStepFlag(false); 330 /// Initialization
331 system.RegisterCoreThread(core);
332 std::string name;
333 if (is_multicore) {
334 name = "yuzu:CoreCPUThread_" + std::to_string(core);
335 } else {
336 name = "yuzu:CPUThread";
337 }
338 MicroProfileOnThreadCreate(name.c_str());
339 Common::SetCurrentThreadName(name.c_str());
340 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
341 auto& data = core_data[core];
342 data.enter_barrier = std::make_unique<Common::Event>();
343 data.exit_barrier = std::make_unique<Common::Event>();
344 data.host_context = Common::Fiber::ThreadToFiber();
345 data.is_running = false;
346 data.initialized = true;
347 const bool sc_sync = !is_async_gpu && !is_multicore;
348 bool sc_sync_first_use = sc_sync;
349 /// Running
350 while (running_mode) {
351 data.is_running = false;
352 data.enter_barrier->Wait();
353 if (sc_sync_first_use) {
354 system.GPU().ObtainContext();
355 sc_sync_first_use = false;
356 }
357 auto& scheduler = system.Kernel().CurrentScheduler();
358 Kernel::Thread* current_thread = scheduler.GetCurrentThread();
359 data.is_running = true;
360 Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext());
361 data.is_running = false;
362 data.is_paused = true;
363 data.exit_barrier->Wait();
364 data.is_paused = false;
78 } 365 }
366 /// Time to cleanup
367 data.host_context->Exit();
368 data.enter_barrier.reset();
369 data.exit_barrier.reset();
370 data.initialized = false;
79} 371}
80 372
81} // namespace Core 373} // namespace Core
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 97554d1bb..35929ed94 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -5,12 +5,19 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
9#include <functional>
8#include <memory> 10#include <memory>
11#include <thread>
9#include "core/hardware_properties.h" 12#include "core/hardware_properties.h"
10 13
14namespace Common {
15class Event;
16class Fiber;
17} // namespace Common
18
11namespace Core { 19namespace Core {
12 20
13class CoreManager;
14class System; 21class System;
15 22
16class CpuManager { 23class CpuManager {
@@ -24,24 +31,75 @@ public:
24 CpuManager& operator=(const CpuManager&) = delete; 31 CpuManager& operator=(const CpuManager&) = delete;
25 CpuManager& operator=(CpuManager&&) = delete; 32 CpuManager& operator=(CpuManager&&) = delete;
26 33
34 /// Sets if emulation is multicore or single core, must be set before Initialize
35 void SetMulticore(bool is_multicore) {
36 this->is_multicore = is_multicore;
37 }
38
39 /// Sets if emulation is using an asynchronous GPU.
40 void SetAsyncGpu(bool is_async_gpu) {
41 this->is_async_gpu = is_async_gpu;
42 }
43
27 void Initialize(); 44 void Initialize();
28 void Shutdown(); 45 void Shutdown();
29 46
30 CoreManager& GetCoreManager(std::size_t index); 47 void Pause(bool paused);
31 const CoreManager& GetCoreManager(std::size_t index) const;
32 48
33 CoreManager& GetCurrentCoreManager(); 49 std::function<void(void*)> GetGuestThreadStartFunc();
34 const CoreManager& GetCurrentCoreManager() const; 50 std::function<void(void*)> GetIdleThreadStartFunc();
51 std::function<void(void*)> GetSuspendThreadStartFunc();
52 void* GetStartFuncParamater();
35 53
36 std::size_t GetActiveCoreIndex() const { 54 void PreemptSingleCore(bool from_running_enviroment = true);
37 return active_core;
38 }
39 55
40 void RunLoop(bool tight_loop); 56 std::size_t CurrentCore() const {
57 return current_core.load();
58 }
41 59
42private: 60private:
43 std::array<std::unique_ptr<CoreManager>, Hardware::NUM_CPU_CORES> core_managers; 61 static void GuestThreadFunction(void* cpu_manager);
44 std::size_t active_core{}; ///< Active core, only used in single thread mode 62 static void GuestRewindFunction(void* cpu_manager);
63 static void IdleThreadFunction(void* cpu_manager);
64 static void SuspendThreadFunction(void* cpu_manager);
65
66 void MultiCoreRunGuestThread();
67 void MultiCoreRunGuestLoop();
68 void MultiCoreRunIdleThread();
69 void MultiCoreRunSuspendThread();
70 void MultiCorePause(bool paused);
71
72 void SingleCoreRunGuestThread();
73 void SingleCoreRunGuestLoop();
74 void SingleCoreRunIdleThread();
75 void SingleCoreRunSuspendThread();
76 void SingleCorePause(bool paused);
77
78 static void ThreadStart(CpuManager& cpu_manager, std::size_t core);
79
80 void RunThread(std::size_t core);
81
82 struct CoreData {
83 std::shared_ptr<Common::Fiber> host_context;
84 std::unique_ptr<Common::Event> enter_barrier;
85 std::unique_ptr<Common::Event> exit_barrier;
86 std::atomic<bool> is_running;
87 std::atomic<bool> is_paused;
88 std::atomic<bool> initialized;
89 std::unique_ptr<std::thread> host_thread;
90 };
91
92 std::atomic<bool> running_mode{};
93 std::atomic<bool> paused_state{};
94
95 std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
96
97 bool is_async_gpu{};
98 bool is_multicore{};
99 std::atomic<std::size_t> current_core{};
100 std::size_t preemption_count{};
101 std::size_t idle_count{};
102 static constexpr std::size_t max_cycle_runs = 5;
45 103
46 System& system; 104 System& system;
47}; 105};
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 8997c7082..f87fe0abc 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -695,8 +695,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
695} 695}
696 696
697void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { 697void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
698 if (s128_keys.find({id, field1, field2}) != s128_keys.end()) 698 if (s128_keys.find({id, field1, field2}) != s128_keys.end() || key == Key128{}) {
699 return; 699 return;
700 }
700 if (id == S128KeyType::Titlekey) { 701 if (id == S128KeyType::Titlekey) {
701 Key128 rights_id; 702 Key128 rights_id;
702 std::memcpy(rights_id.data(), &field2, sizeof(u64)); 703 std::memcpy(rights_id.data(), &field2, sizeof(u64));
@@ -716,8 +717,9 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
716 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == 717 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
717 std::tie(id, field1, field2); 718 std::tie(id, field1, field2);
718 }); 719 });
719 if (iter2 != s128_file_id.end()) 720 if (iter2 != s128_file_id.end()) {
720 WriteKeyToFile(category, iter2->first, key); 721 WriteKeyToFile(category, iter2->first, key);
722 }
721 723
722 // Variable cases 724 // Variable cases
723 if (id == S128KeyType::KeyArea) { 725 if (id == S128KeyType::KeyArea) {
@@ -745,16 +747,18 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
745} 747}
746 748
747void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { 749void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
748 if (s256_keys.find({id, field1, field2}) != s256_keys.end()) 750 if (s256_keys.find({id, field1, field2}) != s256_keys.end() || key == Key256{}) {
749 return; 751 return;
752 }
750 const auto iter = std::find_if( 753 const auto iter = std::find_if(
751 s256_file_id.begin(), s256_file_id.end(), 754 s256_file_id.begin(), s256_file_id.end(),
752 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) { 755 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
753 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == 756 return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
754 std::tie(id, field1, field2); 757 std::tie(id, field1, field2);
755 }); 758 });
756 if (iter != s256_file_id.end()) 759 if (iter != s256_file_id.end()) {
757 WriteKeyToFile(KeyCategory::Standard, iter->first, key); 760 WriteKeyToFile(KeyCategory::Standard, iter->first, key);
761 }
758 s256_keys[{id, field1, field2}] = key; 762 s256_keys[{id, field1, field2}] = key;
759} 763}
760 764
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 7265c4171..9269a73f2 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -223,7 +223,16 @@ bool operator<(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
223 223
224class KeyManager { 224class KeyManager {
225public: 225public:
226 KeyManager(); 226 static KeyManager& Instance() {
227 static KeyManager instance;
228 return instance;
229 }
230
231 KeyManager(const KeyManager&) = delete;
232 KeyManager& operator=(const KeyManager&) = delete;
233
234 KeyManager(KeyManager&&) = delete;
235 KeyManager& operator=(KeyManager&&) = delete;
227 236
228 bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; 237 bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
229 bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; 238 bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
@@ -257,6 +266,8 @@ public:
257 bool AddTicketPersonalized(Ticket raw); 266 bool AddTicketPersonalized(Ticket raw);
258 267
259private: 268private:
269 KeyManager();
270
260 std::map<KeyIndex<S128KeyType>, Key128> s128_keys; 271 std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
261 std::map<KeyIndex<S256KeyType>, Key256> s256_keys; 272 std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
262 273
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 0af44f340..8935a62c3 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -79,7 +79,7 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
79} 79}
80 80
81VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { 81VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
82 Core::Crypto::KeyManager keys; 82 auto& keys = Core::Crypto::KeyManager::Instance();
83 Core::Crypto::PartitionDataManager pdm{ 83 Core::Crypto::PartitionDataManager pdm{
84 Core::System::GetInstance().GetFilesystem()->OpenDirectory( 84 Core::System::GetInstance().GetFilesystem()->OpenDirectory(
85 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; 85 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 07d0c8d5d..664a47e7f 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -178,7 +178,7 @@ u32 XCI::GetSystemUpdateVersion() {
178 return 0; 178 return 0;
179 179
180 for (const auto& file : update->GetFiles()) { 180 for (const auto& file : update->GetFiles()) {
181 NCA nca{file, nullptr, 0, keys}; 181 NCA nca{file, nullptr, 0};
182 182
183 if (nca.GetStatus() != Loader::ResultStatus::Success) 183 if (nca.GetStatus() != Loader::ResultStatus::Success)
184 continue; 184 continue;
@@ -286,7 +286,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
286 continue; 286 continue;
287 } 287 }
288 288
289 auto nca = std::make_shared<NCA>(file, nullptr, 0, keys); 289 auto nca = std::make_shared<NCA>(file, nullptr, 0);
290 if (nca->IsUpdate()) { 290 if (nca->IsUpdate()) {
291 continue; 291 continue;
292 } 292 }
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index c2ee0ea99..e1b136426 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -140,6 +140,6 @@ private:
140 140
141 u64 update_normal_partition_end; 141 u64 update_normal_partition_end;
142 142
143 Core::Crypto::KeyManager keys; 143 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
144}; 144};
145} // namespace FileSys 145} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index b8bbdd1ef..473245d5a 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -118,9 +118,8 @@ static bool IsValidNCA(const NCAHeader& header) {
118 return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); 118 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
119} 119}
120 120
121NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset, 121NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
122 Core::Crypto::KeyManager keys_) 122 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
123 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) {
124 if (file == nullptr) { 123 if (file == nullptr) {
125 status = Loader::ResultStatus::ErrorNullFile; 124 status = Loader::ResultStatus::ErrorNullFile;
126 return; 125 return;
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index e249079b5..d25cbcf91 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -99,8 +99,7 @@ inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) {
99class NCA : public ReadOnlyVfsDirectory { 99class NCA : public ReadOnlyVfsDirectory {
100public: 100public:
101 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, 101 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
102 u64 bktr_base_ivfc_offset = 0, 102 u64 bktr_base_ivfc_offset = 0);
103 Core::Crypto::KeyManager keys = Core::Crypto::KeyManager());
104 ~NCA() override; 103 ~NCA() override;
105 104
106 Loader::ResultStatus GetStatus() const; 105 Loader::ResultStatus GetStatus() const;
@@ -159,7 +158,7 @@ private:
159 bool encrypted = false; 158 bool encrypted = false;
160 bool is_update = false; 159 bool is_update = false;
161 160
162 Core::Crypto::KeyManager keys; 161 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
163}; 162};
164 163
165} // namespace FileSys 164} // namespace FileSys
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index ba5f76288..27c1b0233 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -408,7 +408,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
408 408
409 if (file == nullptr) 409 if (file == nullptr)
410 continue; 410 continue;
411 const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys); 411 const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0);
412 if (nca->GetStatus() != Loader::ResultStatus::Success || 412 if (nca->GetStatus() != Loader::ResultStatus::Success ||
413 nca->GetType() != NCAContentType::Meta) { 413 nca->GetType() != NCAContentType::Meta) {
414 continue; 414 continue;
@@ -486,7 +486,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
486 const auto raw = GetEntryRaw(title_id, type); 486 const auto raw = GetEntryRaw(title_id, type);
487 if (raw == nullptr) 487 if (raw == nullptr)
488 return nullptr; 488 return nullptr;
489 return std::make_unique<NCA>(raw, nullptr, 0, keys); 489 return std::make_unique<NCA>(raw, nullptr, 0);
490} 490}
491 491
492template <typename T> 492template <typename T>
@@ -865,7 +865,7 @@ std::unique_ptr<NCA> ManualContentProvider::GetEntry(u64 title_id, ContentRecord
865 const auto res = GetEntryRaw(title_id, type); 865 const auto res = GetEntryRaw(title_id, type);
866 if (res == nullptr) 866 if (res == nullptr)
867 return nullptr; 867 return nullptr;
868 return std::make_unique<NCA>(res, nullptr, 0, keys); 868 return std::make_unique<NCA>(res, nullptr, 0);
869} 869}
870 870
871std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter( 871std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter(
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index d1eec240e..f339cd17b 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -88,7 +88,7 @@ public:
88 88
89protected: 89protected:
90 // A single instance of KeyManager to be used by GetEntry() 90 // A single instance of KeyManager to be used by GetEntry()
91 Core::Crypto::KeyManager keys; 91 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
92}; 92};
93 93
94class PlaceholderCache { 94class PlaceholderCache {
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index ef3084681..175a8266a 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -21,7 +21,7 @@
21namespace FileSys { 21namespace FileSys {
22namespace { 22namespace {
23void SetTicketKeys(const std::vector<VirtualFile>& files) { 23void SetTicketKeys(const std::vector<VirtualFile>& files) {
24 Core::Crypto::KeyManager keys; 24 auto& keys = Core::Crypto::KeyManager::Instance();
25 25
26 for (const auto& ticket_file : files) { 26 for (const auto& ticket_file : files) {
27 if (ticket_file == nullptr) { 27 if (ticket_file == nullptr) {
@@ -285,7 +285,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
285 continue; 285 continue;
286 } 286 }
287 287
288 auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0, keys); 288 auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0);
289 if (next_nca->GetType() == NCAContentType::Program) { 289 if (next_nca->GetType() == NCAContentType::Program) {
290 program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); 290 program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
291 } 291 }
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index ee9b6ce17..cf89de6a9 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -73,7 +73,7 @@ private:
73 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; 73 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
74 std::vector<VirtualFile> ticket_files; 74 std::vector<VirtualFile> ticket_files;
75 75
76 Core::Crypto::KeyManager keys; 76 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
77 77
78 VirtualFile romfs; 78 VirtualFile romfs;
79 VirtualDir exefs; 79 VirtualDir exefs;
diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp
index 6a9add87c..61bb67945 100644
--- a/src/core/file_sys/system_archive/mii_model.cpp
+++ b/src/core/file_sys/system_archive/mii_model.cpp
@@ -40,7 +40,7 @@ VirtualDir MiiModel() {
40 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>( 40 out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>(
41 MiiModelData::SHAPE_MID, "ShapeMid.dat")); 41 MiiModelData::SHAPE_MID, "ShapeMid.dat"));
42 42
43 return std::move(out); 43 return out;
44} 44}
45 45
46} // namespace FileSys::SystemArchive 46} // namespace FileSys::SystemArchive
diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp
index 2c05eb42e..c5cdf7d9b 100644
--- a/src/core/file_sys/system_archive/shared_font.cpp
+++ b/src/core/file_sys/system_archive/shared_font.cpp
@@ -23,7 +23,7 @@ VirtualFile PackBFTTF(const std::array<u8, Size>& data, const std::string& name)
23 23
24 std::vector<u8> bfttf(Size + sizeof(u64)); 24 std::vector<u8> bfttf(Size + sizeof(u64));
25 25
26 u64 offset = 0; 26 size_t offset = 0;
27 Service::NS::EncryptSharedFont(vec, bfttf, offset); 27 Service::NS::EncryptSharedFont(vec, bfttf, offset);
28 return std::make_shared<VectorVfsFile>(std::move(bfttf), name); 28 return std::make_shared<VectorVfsFile>(std::move(bfttf), name);
29} 29}
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h
index 7704dee90..563531bb6 100644
--- a/src/core/file_sys/xts_archive.h
+++ b/src/core/file_sys/xts_archive.h
@@ -62,6 +62,6 @@ private:
62 62
63 VirtualFile dec_file; 63 VirtualFile dec_file;
64 64
65 Core::Crypto::KeyManager keys; 65 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
66}; 66};
67} // namespace FileSys 67} // namespace FileSys
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index d0c43447c..c1fbc235b 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -29,7 +29,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
29 29
30 const float window_aspect_ratio = static_cast<float>(height) / width; 30 const float window_aspect_ratio = static_cast<float>(height) / width;
31 const float emulation_aspect_ratio = EmulationAspectRatio( 31 const float emulation_aspect_ratio = EmulationAspectRatio(
32 static_cast<AspectRatio>(Settings::values.aspect_ratio), window_aspect_ratio); 32 static_cast<AspectRatio>(Settings::values.aspect_ratio.GetValue()), window_aspect_ratio);
33 33
34 const Common::Rectangle<u32> screen_window_area{0, 0, width, height}; 34 const Common::Rectangle<u32> screen_window_area{0, 0, width, height};
35 Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio); 35 Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 70c0f8b80..79f22a403 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -35,7 +35,6 @@
35#include "common/swap.h" 35#include "common/swap.h"
36#include "core/arm/arm_interface.h" 36#include "core/arm/arm_interface.h"
37#include "core/core.h" 37#include "core/core.h"
38#include "core/core_manager.h"
39#include "core/gdbstub/gdbstub.h" 38#include "core/gdbstub/gdbstub.h"
40#include "core/hle/kernel/memory/page_table.h" 39#include "core/hle/kernel/memory/page_table.h"
41#include "core/hle/kernel/process.h" 40#include "core/hle/kernel/process.h"
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h
index b04e046ed..456b41e1b 100644
--- a/src/core/hardware_properties.h
+++ b/src/core/hardware_properties.h
@@ -42,6 +42,10 @@ struct EmuThreadHandle {
42 constexpr u32 invalid_handle = 0xFFFFFFFF; 42 constexpr u32 invalid_handle = 0xFFFFFFFF;
43 return {invalid_handle, invalid_handle}; 43 return {invalid_handle, invalid_handle};
44 } 44 }
45
46 bool IsInvalid() const {
47 return (*this) == InvalidHandle();
48 }
45}; 49};
46 50
47} // namespace Core 51} // namespace Core
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 8475b698c..4d2a9b35d 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -7,11 +7,15 @@
7 7
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/arm/exclusive_monitor.h"
10#include "core/core.h" 11#include "core/core.h"
11#include "core/hle/kernel/address_arbiter.h" 12#include "core/hle/kernel/address_arbiter.h"
12#include "core/hle/kernel/errors.h" 13#include "core/hle/kernel/errors.h"
14#include "core/hle/kernel/handle_table.h"
15#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/scheduler.h" 16#include "core/hle/kernel/scheduler.h"
14#include "core/hle/kernel/thread.h" 17#include "core/hle/kernel/thread.h"
18#include "core/hle/kernel/time_manager.h"
15#include "core/hle/result.h" 19#include "core/hle/result.h"
16#include "core/memory.h" 20#include "core/memory.h"
17 21
@@ -20,6 +24,7 @@ namespace Kernel {
20// Wake up num_to_wake (or all) threads in a vector. 24// Wake up num_to_wake (or all) threads in a vector.
21void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, 25void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads,
22 s32 num_to_wake) { 26 s32 num_to_wake) {
27 auto& time_manager = system.Kernel().TimeManager();
23 // Only process up to 'target' threads, unless 'target' is <= 0, in which case process 28 // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
24 // them all. 29 // them all.
25 std::size_t last = waiting_threads.size(); 30 std::size_t last = waiting_threads.size();
@@ -29,12 +34,10 @@ void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& wai
29 34
30 // Signal the waiting threads. 35 // Signal the waiting threads.
31 for (std::size_t i = 0; i < last; i++) { 36 for (std::size_t i = 0; i < last; i++) {
32 ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb); 37 waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
33 waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
34 RemoveThread(waiting_threads[i]); 38 RemoveThread(waiting_threads[i]);
35 waiting_threads[i]->SetArbiterWaitAddress(0); 39 waiting_threads[i]->WaitForArbitration(false);
36 waiting_threads[i]->ResumeFromWait(); 40 waiting_threads[i]->ResumeFromWait();
37 system.PrepareReschedule(waiting_threads[i]->GetProcessorID());
38 } 41 }
39} 42}
40 43
@@ -56,6 +59,7 @@ ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 v
56} 59}
57 60
58ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { 61ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
62 SchedulerLock lock(system.Kernel());
59 const std::vector<std::shared_ptr<Thread>> waiting_threads = 63 const std::vector<std::shared_ptr<Thread>> waiting_threads =
60 GetThreadsWaitingOnAddress(address); 64 GetThreadsWaitingOnAddress(address);
61 WakeThreads(waiting_threads, num_to_wake); 65 WakeThreads(waiting_threads, num_to_wake);
@@ -64,6 +68,7 @@ ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
64 68
65ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, 69ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
66 s32 num_to_wake) { 70 s32 num_to_wake) {
71 SchedulerLock lock(system.Kernel());
67 auto& memory = system.Memory(); 72 auto& memory = system.Memory();
68 73
69 // Ensure that we can write to the address. 74 // Ensure that we can write to the address.
@@ -71,16 +76,24 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32
71 return ERR_INVALID_ADDRESS_STATE; 76 return ERR_INVALID_ADDRESS_STATE;
72 } 77 }
73 78
74 if (static_cast<s32>(memory.Read32(address)) != value) { 79 const std::size_t current_core = system.CurrentCoreIndex();
75 return ERR_INVALID_STATE; 80 auto& monitor = system.Monitor();
76 } 81 u32 current_value;
82 do {
83 current_value = monitor.ExclusiveRead32(current_core, address);
84
85 if (current_value != value) {
86 return ERR_INVALID_STATE;
87 }
88 current_value++;
89 } while (!monitor.ExclusiveWrite32(current_core, address, current_value));
77 90
78 memory.Write32(address, static_cast<u32>(value + 1));
79 return SignalToAddressOnly(address, num_to_wake); 91 return SignalToAddressOnly(address, num_to_wake);
80} 92}
81 93
82ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, 94ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
83 s32 num_to_wake) { 95 s32 num_to_wake) {
96 SchedulerLock lock(system.Kernel());
84 auto& memory = system.Memory(); 97 auto& memory = system.Memory();
85 98
86 // Ensure that we can write to the address. 99 // Ensure that we can write to the address.
@@ -92,29 +105,33 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
92 const std::vector<std::shared_ptr<Thread>> waiting_threads = 105 const std::vector<std::shared_ptr<Thread>> waiting_threads =
93 GetThreadsWaitingOnAddress(address); 106 GetThreadsWaitingOnAddress(address);
94 107
95 // Determine the modified value depending on the waiting count. 108 const std::size_t current_core = system.CurrentCoreIndex();
109 auto& monitor = system.Monitor();
96 s32 updated_value; 110 s32 updated_value;
97 if (num_to_wake <= 0) { 111 do {
98 if (waiting_threads.empty()) { 112 updated_value = monitor.ExclusiveRead32(current_core, address);
99 updated_value = value + 1; 113
100 } else { 114 if (updated_value != value) {
101 updated_value = value - 1; 115 return ERR_INVALID_STATE;
102 } 116 }
103 } else { 117 // Determine the modified value depending on the waiting count.
104 if (waiting_threads.empty()) { 118 if (num_to_wake <= 0) {
105 updated_value = value + 1; 119 if (waiting_threads.empty()) {
106 } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) { 120 updated_value = value + 1;
107 updated_value = value - 1; 121 } else {
122 updated_value = value - 1;
123 }
108 } else { 124 } else {
109 updated_value = value; 125 if (waiting_threads.empty()) {
126 updated_value = value + 1;
127 } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
128 updated_value = value - 1;
129 } else {
130 updated_value = value;
131 }
110 } 132 }
111 } 133 } while (!monitor.ExclusiveWrite32(current_core, address, updated_value));
112 134
113 if (static_cast<s32>(memory.Read32(address)) != value) {
114 return ERR_INVALID_STATE;
115 }
116
117 memory.Write32(address, static_cast<u32>(updated_value));
118 WakeThreads(waiting_threads, num_to_wake); 135 WakeThreads(waiting_threads, num_to_wake);
119 return RESULT_SUCCESS; 136 return RESULT_SUCCESS;
120} 137}
@@ -136,60 +153,127 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s
136ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, 153ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
137 bool should_decrement) { 154 bool should_decrement) {
138 auto& memory = system.Memory(); 155 auto& memory = system.Memory();
156 auto& kernel = system.Kernel();
157 Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
139 158
140 // Ensure that we can read the address. 159 Handle event_handle = InvalidHandle;
141 if (!memory.IsValidVirtualAddress(address)) { 160 {
142 return ERR_INVALID_ADDRESS_STATE; 161 SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout);
143 } 162
163 if (current_thread->IsPendingTermination()) {
164 lock.CancelSleep();
165 return ERR_THREAD_TERMINATING;
166 }
167
168 // Ensure that we can read the address.
169 if (!memory.IsValidVirtualAddress(address)) {
170 lock.CancelSleep();
171 return ERR_INVALID_ADDRESS_STATE;
172 }
173
174 s32 current_value = static_cast<s32>(memory.Read32(address));
175 if (current_value >= value) {
176 lock.CancelSleep();
177 return ERR_INVALID_STATE;
178 }
179
180 current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
181
182 s32 decrement_value;
183
184 const std::size_t current_core = system.CurrentCoreIndex();
185 auto& monitor = system.Monitor();
186 do {
187 current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address));
188 if (should_decrement) {
189 decrement_value = current_value - 1;
190 } else {
191 decrement_value = current_value;
192 }
193 } while (
194 !monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value)));
195
196 // Short-circuit without rescheduling, if timeout is zero.
197 if (timeout == 0) {
198 lock.CancelSleep();
199 return RESULT_TIMEOUT;
200 }
144 201
145 const s32 cur_value = static_cast<s32>(memory.Read32(address)); 202 current_thread->SetArbiterWaitAddress(address);
146 if (cur_value >= value) { 203 InsertThread(SharedFrom(current_thread));
147 return ERR_INVALID_STATE; 204 current_thread->SetStatus(ThreadStatus::WaitArb);
205 current_thread->WaitForArbitration(true);
148 } 206 }
149 207
150 if (should_decrement) { 208 if (event_handle != InvalidHandle) {
151 memory.Write32(address, static_cast<u32>(cur_value - 1)); 209 auto& time_manager = kernel.TimeManager();
210 time_manager.UnscheduleTimeEvent(event_handle);
152 } 211 }
153 212
154 // Short-circuit without rescheduling, if timeout is zero. 213 {
155 if (timeout == 0) { 214 SchedulerLock lock(kernel);
156 return RESULT_TIMEOUT; 215 if (current_thread->IsWaitingForArbitration()) {
216 RemoveThread(SharedFrom(current_thread));
217 current_thread->WaitForArbitration(false);
218 }
157 } 219 }
158 220
159 return WaitForAddressImpl(address, timeout); 221 return current_thread->GetSignalingResult();
160} 222}
161 223
162ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { 224ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
163 auto& memory = system.Memory(); 225 auto& memory = system.Memory();
226 auto& kernel = system.Kernel();
227 Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
164 228
165 // Ensure that we can read the address. 229 Handle event_handle = InvalidHandle;
166 if (!memory.IsValidVirtualAddress(address)) { 230 {
167 return ERR_INVALID_ADDRESS_STATE; 231 SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout);
168 } 232
233 if (current_thread->IsPendingTermination()) {
234 lock.CancelSleep();
235 return ERR_THREAD_TERMINATING;
236 }
237
238 // Ensure that we can read the address.
239 if (!memory.IsValidVirtualAddress(address)) {
240 lock.CancelSleep();
241 return ERR_INVALID_ADDRESS_STATE;
242 }
169 243
170 // Only wait for the address if equal. 244 s32 current_value = static_cast<s32>(memory.Read32(address));
171 if (static_cast<s32>(memory.Read32(address)) != value) { 245 if (current_value != value) {
172 return ERR_INVALID_STATE; 246 lock.CancelSleep();
247 return ERR_INVALID_STATE;
248 }
249
250 // Short-circuit without rescheduling, if timeout is zero.
251 if (timeout == 0) {
252 lock.CancelSleep();
253 return RESULT_TIMEOUT;
254 }
255
256 current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
257 current_thread->SetArbiterWaitAddress(address);
258 InsertThread(SharedFrom(current_thread));
259 current_thread->SetStatus(ThreadStatus::WaitArb);
260 current_thread->WaitForArbitration(true);
173 } 261 }
174 262
175 // Short-circuit without rescheduling if timeout is zero. 263 if (event_handle != InvalidHandle) {
176 if (timeout == 0) { 264 auto& time_manager = kernel.TimeManager();
177 return RESULT_TIMEOUT; 265 time_manager.UnscheduleTimeEvent(event_handle);
178 } 266 }
179 267
180 return WaitForAddressImpl(address, timeout); 268 {
181} 269 SchedulerLock lock(kernel);
270 if (current_thread->IsWaitingForArbitration()) {
271 RemoveThread(SharedFrom(current_thread));
272 current_thread->WaitForArbitration(false);
273 }
274 }
182 275
183ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) { 276 return current_thread->GetSignalingResult();
184 Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
185 current_thread->SetArbiterWaitAddress(address);
186 InsertThread(SharedFrom(current_thread));
187 current_thread->SetStatus(ThreadStatus::WaitArb);
188 current_thread->InvalidateWakeupCallback();
189 current_thread->WakeAfterDelay(timeout);
190
191 system.PrepareReschedule(current_thread->GetProcessorID());
192 return RESULT_TIMEOUT;
193} 277}
194 278
195void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) { 279void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) {
@@ -221,9 +305,9 @@ void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) {
221 const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(), 305 const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(),
222 [&thread](const auto& entry) { return thread == entry; }); 306 [&thread](const auto& entry) { return thread == entry; });
223 307
224 ASSERT(iter != thread_list.cend()); 308 if (iter != thread_list.cend()) {
225 309 thread_list.erase(iter);
226 thread_list.erase(iter); 310 }
227} 311}
228 312
229std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress( 313std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index f958eee5a..0b05d533c 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -73,9 +73,6 @@ private:
73 /// Waits on an address if the value passed is equal to the argument value. 73 /// Waits on an address if the value passed is equal to the argument value.
74 ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); 74 ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
75 75
76 // Waits on the given address with a timeout in nanoseconds
77 ResultCode WaitForAddressImpl(VAddr address, s64 timeout);
78
79 /// Wake up num_to_wake (or all) threads in a vector. 76 /// Wake up num_to_wake (or all) threads in a vector.
80 void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake); 77 void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake);
81 78
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index 5498fd313..8aff2227a 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -34,7 +34,7 @@ ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() {
34 } 34 }
35 35
36 // Wake the threads waiting on the ServerPort 36 // Wake the threads waiting on the ServerPort
37 server_port->WakeupAllWaitingThreads(); 37 server_port->Signal();
38 38
39 return MakeResult(std::move(client)); 39 return MakeResult(std::move(client));
40} 40}
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 29bfa3621..d4e5d88cf 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -12,6 +12,7 @@ namespace Kernel {
12 12
13constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; 13constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
14constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; 14constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
15constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59};
15constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; 16constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
16constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; 17constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
17constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103}; 18constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103};
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index ba0eac4c2..9277b5d08 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -14,14 +14,17 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "core/hle/ipc_helpers.h" 16#include "core/hle/ipc_helpers.h"
17#include "core/hle/kernel/errors.h"
17#include "core/hle/kernel/handle_table.h" 18#include "core/hle/kernel/handle_table.h"
18#include "core/hle/kernel/hle_ipc.h" 19#include "core/hle/kernel/hle_ipc.h"
19#include "core/hle/kernel/kernel.h" 20#include "core/hle/kernel/kernel.h"
20#include "core/hle/kernel/object.h" 21#include "core/hle/kernel/object.h"
21#include "core/hle/kernel/process.h" 22#include "core/hle/kernel/process.h"
22#include "core/hle/kernel/readable_event.h" 23#include "core/hle/kernel/readable_event.h"
24#include "core/hle/kernel/scheduler.h"
23#include "core/hle/kernel/server_session.h" 25#include "core/hle/kernel/server_session.h"
24#include "core/hle/kernel/thread.h" 26#include "core/hle/kernel/thread.h"
27#include "core/hle/kernel/time_manager.h"
25#include "core/hle/kernel/writable_event.h" 28#include "core/hle/kernel/writable_event.h"
26#include "core/memory.h" 29#include "core/memory.h"
27 30
@@ -46,15 +49,6 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread(
46 const std::string& reason, u64 timeout, WakeupCallback&& callback, 49 const std::string& reason, u64 timeout, WakeupCallback&& callback,
47 std::shared_ptr<WritableEvent> writable_event) { 50 std::shared_ptr<WritableEvent> writable_event) {
48 // Put the client thread to sleep until the wait event is signaled or the timeout expires. 51 // Put the client thread to sleep until the wait event is signaled or the timeout expires.
49 thread->SetWakeupCallback(
50 [context = *this, callback](ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
51 std::shared_ptr<SynchronizationObject> object,
52 std::size_t index) mutable -> bool {
53 ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent);
54 callback(thread, context, reason);
55 context.WriteToOutgoingCommandBuffer(*thread);
56 return true;
57 });
58 52
59 if (!writable_event) { 53 if (!writable_event) {
60 // Create event if not provided 54 // Create event if not provided
@@ -62,14 +56,26 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread(
62 writable_event = pair.writable; 56 writable_event = pair.writable;
63 } 57 }
64 58
65 const auto readable_event{writable_event->GetReadableEvent()}; 59 {
66 writable_event->Clear(); 60 Handle event_handle = InvalidHandle;
67 thread->SetStatus(ThreadStatus::WaitHLEEvent); 61 SchedulerLockAndSleep lock(kernel, event_handle, thread.get(), timeout);
68 thread->SetSynchronizationObjects({readable_event}); 62 thread->SetHLECallback(
69 readable_event->AddWaitingThread(thread); 63 [context = *this, callback](std::shared_ptr<Thread> thread) mutable -> bool {
70 64 ThreadWakeupReason reason = thread->GetSignalingResult() == RESULT_TIMEOUT
71 if (timeout > 0) { 65 ? ThreadWakeupReason::Timeout
72 thread->WakeAfterDelay(timeout); 66 : ThreadWakeupReason::Signal;
67 callback(thread, context, reason);
68 context.WriteToOutgoingCommandBuffer(*thread);
69 return true;
70 });
71 const auto readable_event{writable_event->GetReadableEvent()};
72 writable_event->Clear();
73 thread->SetHLESyncObject(readable_event.get());
74 thread->SetStatus(ThreadStatus::WaitHLEEvent);
75 thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
76 readable_event->AddWaitingThread(thread);
77 lock.Release();
78 thread->SetHLETimeEvent(event_handle);
73 } 79 }
74 80
75 is_thread_waiting = true; 81 is_thread_waiting = true;
@@ -282,18 +288,18 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
282} 288}
283 289
284std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { 290std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
285 std::vector<u8> buffer; 291 std::vector<u8> buffer{};
286 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && 292 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
287 BufferDescriptorA()[buffer_index].Size()}; 293 BufferDescriptorA()[buffer_index].Size()};
288 294
289 if (is_buffer_a) { 295 if (is_buffer_a) {
290 ASSERT_MSG(BufferDescriptorA().size() > buffer_index, 296 ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return buffer; },
291 "BufferDescriptorA invalid buffer_index {}", buffer_index); 297 "BufferDescriptorA invalid buffer_index {}", buffer_index);
292 buffer.resize(BufferDescriptorA()[buffer_index].Size()); 298 buffer.resize(BufferDescriptorA()[buffer_index].Size());
293 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); 299 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
294 } else { 300 } else {
295 ASSERT_MSG(BufferDescriptorX().size() > buffer_index, 301 ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return buffer; },
296 "BufferDescriptorX invalid buffer_index {}", buffer_index); 302 "BufferDescriptorX invalid buffer_index {}", buffer_index);
297 buffer.resize(BufferDescriptorX()[buffer_index].Size()); 303 buffer.resize(BufferDescriptorX()[buffer_index].Size());
298 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); 304 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
299 } 305 }
@@ -318,16 +324,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
318 } 324 }
319 325
320 if (is_buffer_b) { 326 if (is_buffer_b) {
321 ASSERT_MSG(BufferDescriptorB().size() > buffer_index, 327 ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index &&
322 "BufferDescriptorB invalid buffer_index {}", buffer_index); 328 BufferDescriptorB()[buffer_index].Size() >= size,
323 ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size, 329 { return 0; }, "BufferDescriptorB is invalid, index={}, size={}",
324 "BufferDescriptorB buffer_index {} is not large enough", buffer_index); 330 buffer_index, size);
325 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); 331 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
326 } else { 332 } else {
327 ASSERT_MSG(BufferDescriptorC().size() > buffer_index, 333 ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index &&
328 "BufferDescriptorC invalid buffer_index {}", buffer_index); 334 BufferDescriptorC()[buffer_index].Size() >= size,
329 ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size, 335 { return 0; }, "BufferDescriptorC is invalid, index={}, size={}",
330 "BufferDescriptorC buffer_index {} is not large enough", buffer_index); 336 buffer_index, size);
331 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); 337 memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
332 } 338 }
333 339
@@ -338,16 +344,12 @@ std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const
338 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && 344 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
339 BufferDescriptorA()[buffer_index].Size()}; 345 BufferDescriptorA()[buffer_index].Size()};
340 if (is_buffer_a) { 346 if (is_buffer_a) {
341 ASSERT_MSG(BufferDescriptorA().size() > buffer_index, 347 ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return 0; },
342 "BufferDescriptorA invalid buffer_index {}", buffer_index); 348 "BufferDescriptorA invalid buffer_index {}", buffer_index);
343 ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0,
344 "BufferDescriptorA buffer_index {} is empty", buffer_index);
345 return BufferDescriptorA()[buffer_index].Size(); 349 return BufferDescriptorA()[buffer_index].Size();
346 } else { 350 } else {
347 ASSERT_MSG(BufferDescriptorX().size() > buffer_index, 351 ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return 0; },
348 "BufferDescriptorX invalid buffer_index {}", buffer_index); 352 "BufferDescriptorX invalid buffer_index {}", buffer_index);
349 ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0,
350 "BufferDescriptorX buffer_index {} is empty", buffer_index);
351 return BufferDescriptorX()[buffer_index].Size(); 353 return BufferDescriptorX()[buffer_index].Size();
352 } 354 }
353} 355}
@@ -356,14 +358,15 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons
356 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && 358 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
357 BufferDescriptorB()[buffer_index].Size()}; 359 BufferDescriptorB()[buffer_index].Size()};
358 if (is_buffer_b) { 360 if (is_buffer_b) {
359 ASSERT_MSG(BufferDescriptorB().size() > buffer_index, 361 ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index, { return 0; },
360 "BufferDescriptorB invalid buffer_index {}", buffer_index); 362 "BufferDescriptorB invalid buffer_index {}", buffer_index);
361 return BufferDescriptorB()[buffer_index].Size(); 363 return BufferDescriptorB()[buffer_index].Size();
362 } else { 364 } else {
363 ASSERT_MSG(BufferDescriptorC().size() > buffer_index, 365 ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index, { return 0; },
364 "BufferDescriptorC invalid buffer_index {}", buffer_index); 366 "BufferDescriptorC invalid buffer_index {}", buffer_index);
365 return BufferDescriptorC()[buffer_index].Size(); 367 return BufferDescriptorC()[buffer_index].Size();
366 } 368 }
369 return 0;
367} 370}
368 371
369std::string HLERequestContext::Description() const { 372std::string HLERequestContext::Description() const {
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 7655382fa..1f2af7a1b 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
5#include <atomic> 6#include <atomic>
6#include <bitset> 7#include <bitset>
7#include <functional> 8#include <functional>
@@ -13,11 +14,15 @@
13 14
14#include "common/assert.h" 15#include "common/assert.h"
15#include "common/logging/log.h" 16#include "common/logging/log.h"
17#include "common/microprofile.h"
18#include "common/thread.h"
16#include "core/arm/arm_interface.h" 19#include "core/arm/arm_interface.h"
20#include "core/arm/cpu_interrupt_handler.h"
17#include "core/arm/exclusive_monitor.h" 21#include "core/arm/exclusive_monitor.h"
18#include "core/core.h" 22#include "core/core.h"
19#include "core/core_timing.h" 23#include "core/core_timing.h"
20#include "core/core_timing_util.h" 24#include "core/core_timing_util.h"
25#include "core/cpu_manager.h"
21#include "core/device_memory.h" 26#include "core/device_memory.h"
22#include "core/hardware_properties.h" 27#include "core/hardware_properties.h"
23#include "core/hle/kernel/client_port.h" 28#include "core/hle/kernel/client_port.h"
@@ -39,85 +44,28 @@
39#include "core/hle/result.h" 44#include "core/hle/result.h"
40#include "core/memory.h" 45#include "core/memory.h"
41 46
42namespace Kernel { 47MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
43
44/**
45 * Callback that will wake up the thread it was scheduled for
46 * @param thread_handle The handle of the thread that's been awoken
47 * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
48 */
49static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_late) {
50 const auto proper_handle = static_cast<Handle>(thread_handle);
51 const auto& system = Core::System::GetInstance();
52
53 // Lock the global kernel mutex when we enter the kernel HLE.
54 std::lock_guard lock{HLE::g_hle_lock};
55
56 std::shared_ptr<Thread> thread =
57 system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
58 if (thread == nullptr) {
59 LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
60 return;
61 }
62
63 bool resume = true;
64
65 if (thread->GetStatus() == ThreadStatus::WaitSynch ||
66 thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
67 // Remove the thread from each of its waiting objects' waitlists
68 for (const auto& object : thread->GetSynchronizationObjects()) {
69 object->RemoveWaitingThread(thread);
70 }
71 thread->ClearSynchronizationObjects();
72
73 // Invoke the wakeup callback before clearing the wait objects
74 if (thread->HasWakeupCallback()) {
75 resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
76 }
77 } else if (thread->GetStatus() == ThreadStatus::WaitMutex ||
78 thread->GetStatus() == ThreadStatus::WaitCondVar) {
79 thread->SetMutexWaitAddress(0);
80 thread->SetWaitHandle(0);
81 if (thread->GetStatus() == ThreadStatus::WaitCondVar) {
82 thread->GetOwnerProcess()->RemoveConditionVariableThread(thread);
83 thread->SetCondVarWaitAddress(0);
84 }
85
86 auto* const lock_owner = thread->GetLockOwner();
87 // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
88 // and don't have a lock owner unless SignalProcessWideKey was called first and the thread
89 // wasn't awakened due to the mutex already being acquired.
90 if (lock_owner != nullptr) {
91 lock_owner->RemoveMutexWaiter(thread);
92 }
93 }
94 48
95 if (thread->GetStatus() == ThreadStatus::WaitArb) { 49namespace Kernel {
96 auto& address_arbiter = thread->GetOwnerProcess()->GetAddressArbiter();
97 address_arbiter.HandleWakeupThread(thread);
98 }
99
100 if (resume) {
101 if (thread->GetStatus() == ThreadStatus::WaitCondVar ||
102 thread->GetStatus() == ThreadStatus::WaitArb) {
103 thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
104 }
105 thread->ResumeFromWait();
106 }
107}
108 50
109struct KernelCore::Impl { 51struct KernelCore::Impl {
110 explicit Impl(Core::System& system, KernelCore& kernel) 52 explicit Impl(Core::System& system, KernelCore& kernel)
111 : global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {} 53 : global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {}
112 54
55 void SetMulticore(bool is_multicore) {
56 this->is_multicore = is_multicore;
57 }
58
113 void Initialize(KernelCore& kernel) { 59 void Initialize(KernelCore& kernel) {
114 Shutdown(); 60 Shutdown();
61 RegisterHostThread();
115 62
116 InitializePhysicalCores(); 63 InitializePhysicalCores();
117 InitializeSystemResourceLimit(kernel); 64 InitializeSystemResourceLimit(kernel);
118 InitializeMemoryLayout(); 65 InitializeMemoryLayout();
119 InitializeThreads(); 66 InitializePreemption(kernel);
120 InitializePreemption(); 67 InitializeSchedulers();
68 InitializeSuspendThreads();
121 } 69 }
122 70
123 void Shutdown() { 71 void Shutdown() {
@@ -126,13 +74,26 @@ struct KernelCore::Impl {
126 next_user_process_id = Process::ProcessIDMin; 74 next_user_process_id = Process::ProcessIDMin;
127 next_thread_id = 1; 75 next_thread_id = 1;
128 76
77 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
78 if (suspend_threads[i]) {
79 suspend_threads[i].reset();
80 }
81 }
82
83 for (std::size_t i = 0; i < cores.size(); i++) {
84 cores[i].Shutdown();
85 schedulers[i].reset();
86 }
87 cores.clear();
88
89 registered_core_threads.reset();
90
129 process_list.clear(); 91 process_list.clear();
130 current_process = nullptr; 92 current_process = nullptr;
131 93
132 system_resource_limit = nullptr; 94 system_resource_limit = nullptr;
133 95
134 global_handle_table.Clear(); 96 global_handle_table.Clear();
135 thread_wakeup_event_type = nullptr;
136 preemption_event = nullptr; 97 preemption_event = nullptr;
137 98
138 global_scheduler.Shutdown(); 99 global_scheduler.Shutdown();
@@ -145,13 +106,21 @@ struct KernelCore::Impl {
145 cores.clear(); 106 cores.clear();
146 107
147 exclusive_monitor.reset(); 108 exclusive_monitor.reset();
109 host_thread_ids.clear();
148 } 110 }
149 111
150 void InitializePhysicalCores() { 112 void InitializePhysicalCores() {
151 exclusive_monitor = 113 exclusive_monitor =
152 Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); 114 Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
153 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 115 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
154 cores.emplace_back(system, i, *exclusive_monitor); 116 schedulers[i] = std::make_unique<Kernel::Scheduler>(system, i);
117 cores.emplace_back(system, i, *schedulers[i], interrupts[i]);
118 }
119 }
120
121 void InitializeSchedulers() {
122 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
123 cores[i].Scheduler().Initialize();
155 } 124 }
156 } 125 }
157 126
@@ -173,15 +142,13 @@ struct KernelCore::Impl {
173 } 142 }
174 } 143 }
175 144
176 void InitializeThreads() { 145 void InitializePreemption(KernelCore& kernel) {
177 thread_wakeup_event_type = 146 preemption_event = Core::Timing::CreateEvent(
178 Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback); 147 "PreemptionCallback", [this, &kernel](u64 userdata, s64 cycles_late) {
179 } 148 {
180 149 SchedulerLock lock(kernel);
181 void InitializePreemption() { 150 global_scheduler.PreemptThreads();
182 preemption_event = 151 }
183 Core::Timing::CreateEvent("PreemptionCallback", [this](u64 userdata, s64 cycles_late) {
184 global_scheduler.PreemptThreads();
185 s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); 152 s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10));
186 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 153 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
187 }); 154 });
@@ -190,6 +157,20 @@ struct KernelCore::Impl {
190 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 157 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
191 } 158 }
192 159
160 void InitializeSuspendThreads() {
161 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
162 std::string name = "Suspend Thread Id:" + std::to_string(i);
163 std::function<void(void*)> init_func =
164 system.GetCpuManager().GetSuspendThreadStartFunc();
165 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
166 ThreadType type =
167 static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND);
168 auto thread_res = Thread::Create(system, type, name, 0, 0, 0, static_cast<u32>(i), 0,
169 nullptr, std::move(init_func), init_func_parameter);
170 suspend_threads[i] = std::move(thread_res).Unwrap();
171 }
172 }
173
193 void MakeCurrentProcess(Process* process) { 174 void MakeCurrentProcess(Process* process) {
194 current_process = process; 175 current_process = process;
195 176
@@ -197,15 +178,17 @@ struct KernelCore::Impl {
197 return; 178 return;
198 } 179 }
199 180
200 for (auto& core : cores) { 181 u32 core_id = GetCurrentHostThreadID();
201 core.SetIs64Bit(process->Is64BitProcess()); 182 if (core_id < Core::Hardware::NUM_CPU_CORES) {
183 system.Memory().SetCurrentPageTable(*process, core_id);
202 } 184 }
203
204 system.Memory().SetCurrentPageTable(*process);
205 } 185 }
206 186
207 void RegisterCoreThread(std::size_t core_id) { 187 void RegisterCoreThread(std::size_t core_id) {
208 std::unique_lock lock{register_thread_mutex}; 188 std::unique_lock lock{register_thread_mutex};
189 if (!is_multicore) {
190 single_core_thread_id = std::this_thread::get_id();
191 }
209 const std::thread::id this_id = std::this_thread::get_id(); 192 const std::thread::id this_id = std::this_thread::get_id();
210 const auto it = host_thread_ids.find(this_id); 193 const auto it = host_thread_ids.find(this_id);
211 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); 194 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
@@ -219,12 +202,19 @@ struct KernelCore::Impl {
219 std::unique_lock lock{register_thread_mutex}; 202 std::unique_lock lock{register_thread_mutex};
220 const std::thread::id this_id = std::this_thread::get_id(); 203 const std::thread::id this_id = std::this_thread::get_id();
221 const auto it = host_thread_ids.find(this_id); 204 const auto it = host_thread_ids.find(this_id);
222 ASSERT(it == host_thread_ids.end()); 205 if (it != host_thread_ids.end()) {
206 return;
207 }
223 host_thread_ids[this_id] = registered_thread_ids++; 208 host_thread_ids[this_id] = registered_thread_ids++;
224 } 209 }
225 210
226 u32 GetCurrentHostThreadID() const { 211 u32 GetCurrentHostThreadID() const {
227 const std::thread::id this_id = std::this_thread::get_id(); 212 const std::thread::id this_id = std::this_thread::get_id();
213 if (!is_multicore) {
214 if (single_core_thread_id == this_id) {
215 return static_cast<u32>(system.GetCpuManager().CurrentCore());
216 }
217 }
228 const auto it = host_thread_ids.find(this_id); 218 const auto it = host_thread_ids.find(this_id);
229 if (it == host_thread_ids.end()) { 219 if (it == host_thread_ids.end()) {
230 return Core::INVALID_HOST_THREAD_ID; 220 return Core::INVALID_HOST_THREAD_ID;
@@ -240,7 +230,7 @@ struct KernelCore::Impl {
240 } 230 }
241 const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler(); 231 const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler();
242 const Kernel::Thread* current = sched.GetCurrentThread(); 232 const Kernel::Thread* current = sched.GetCurrentThread();
243 if (current != nullptr) { 233 if (current != nullptr && !current->IsPhantomMode()) {
244 result.guest_handle = current->GetGlobalHandle(); 234 result.guest_handle = current->GetGlobalHandle();
245 } else { 235 } else {
246 result.guest_handle = InvalidHandle; 236 result.guest_handle = InvalidHandle;
@@ -313,7 +303,6 @@ struct KernelCore::Impl {
313 303
314 std::shared_ptr<ResourceLimit> system_resource_limit; 304 std::shared_ptr<ResourceLimit> system_resource_limit;
315 305
316 std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type;
317 std::shared_ptr<Core::Timing::EventType> preemption_event; 306 std::shared_ptr<Core::Timing::EventType> preemption_event;
318 307
319 // This is the kernel's handle table or supervisor handle table which 308 // This is the kernel's handle table or supervisor handle table which
@@ -343,6 +332,15 @@ struct KernelCore::Impl {
343 std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; 332 std::shared_ptr<Kernel::SharedMemory> irs_shared_mem;
344 std::shared_ptr<Kernel::SharedMemory> time_shared_mem; 333 std::shared_ptr<Kernel::SharedMemory> time_shared_mem;
345 334
335 std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
336 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
337 std::array<std::unique_ptr<Kernel::Scheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
338
339 bool is_multicore{};
340 std::thread::id single_core_thread_id{};
341
342 std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
343
346 // System context 344 // System context
347 Core::System& system; 345 Core::System& system;
348}; 346};
@@ -352,6 +350,10 @@ KernelCore::~KernelCore() {
352 Shutdown(); 350 Shutdown();
353} 351}
354 352
353void KernelCore::SetMulticore(bool is_multicore) {
354 impl->SetMulticore(is_multicore);
355}
356
355void KernelCore::Initialize() { 357void KernelCore::Initialize() {
356 impl->Initialize(*this); 358 impl->Initialize(*this);
357} 359}
@@ -397,11 +399,11 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const {
397} 399}
398 400
399Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) { 401Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) {
400 return impl->cores[id].Scheduler(); 402 return *impl->schedulers[id];
401} 403}
402 404
403const Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) const { 405const Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) const {
404 return impl->cores[id].Scheduler(); 406 return *impl->schedulers[id];
405} 407}
406 408
407Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { 409Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) {
@@ -412,6 +414,39 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
412 return impl->cores[id]; 414 return impl->cores[id];
413} 415}
414 416
417Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
418 u32 core_id = impl->GetCurrentHostThreadID();
419 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
420 return impl->cores[core_id];
421}
422
423const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
424 u32 core_id = impl->GetCurrentHostThreadID();
425 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
426 return impl->cores[core_id];
427}
428
429Kernel::Scheduler& KernelCore::CurrentScheduler() {
430 u32 core_id = impl->GetCurrentHostThreadID();
431 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
432 return *impl->schedulers[core_id];
433}
434
435const Kernel::Scheduler& KernelCore::CurrentScheduler() const {
436 u32 core_id = impl->GetCurrentHostThreadID();
437 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
438 return *impl->schedulers[core_id];
439}
440
441std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() {
442 return impl->interrupts;
443}
444
445const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts()
446 const {
447 return impl->interrupts;
448}
449
415Kernel::Synchronization& KernelCore::Synchronization() { 450Kernel::Synchronization& KernelCore::Synchronization() {
416 return impl->synchronization; 451 return impl->synchronization;
417} 452}
@@ -437,15 +472,17 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
437} 472}
438 473
439void KernelCore::InvalidateAllInstructionCaches() { 474void KernelCore::InvalidateAllInstructionCaches() {
440 for (std::size_t i = 0; i < impl->global_scheduler.CpuCoresCount(); i++) { 475 auto& threads = GlobalScheduler().GetThreadList();
441 PhysicalCore(i).ArmInterface().ClearInstructionCache(); 476 for (auto& thread : threads) {
477 if (!thread->IsHLEThread()) {
478 auto& arm_interface = thread->ArmInterface();
479 arm_interface.ClearInstructionCache();
480 }
442 } 481 }
443} 482}
444 483
445void KernelCore::PrepareReschedule(std::size_t id) { 484void KernelCore::PrepareReschedule(std::size_t id) {
446 if (id < impl->global_scheduler.CpuCoresCount()) { 485 // TODO: Reimplement, this
447 impl->cores[id].Stop();
448 }
449} 486}
450 487
451void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) { 488void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) {
@@ -481,10 +518,6 @@ u64 KernelCore::CreateNewUserProcessID() {
481 return impl->next_user_process_id++; 518 return impl->next_user_process_id++;
482} 519}
483 520
484const std::shared_ptr<Core::Timing::EventType>& KernelCore::ThreadWakeupCallbackEventType() const {
485 return impl->thread_wakeup_event_type;
486}
487
488Kernel::HandleTable& KernelCore::GlobalHandleTable() { 521Kernel::HandleTable& KernelCore::GlobalHandleTable() {
489 return impl->global_handle_table; 522 return impl->global_handle_table;
490} 523}
@@ -557,4 +590,34 @@ const Kernel::SharedMemory& KernelCore::GetTimeSharedMem() const {
557 return *impl->time_shared_mem; 590 return *impl->time_shared_mem;
558} 591}
559 592
593void KernelCore::Suspend(bool in_suspention) {
594 const bool should_suspend = exception_exited || in_suspention;
595 {
596 SchedulerLock lock(*this);
597 ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep;
598 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
599 impl->suspend_threads[i]->SetStatus(status);
600 }
601 }
602}
603
604bool KernelCore::IsMulticore() const {
605 return impl->is_multicore;
606}
607
608void KernelCore::ExceptionalExit() {
609 exception_exited = true;
610 Suspend(true);
611}
612
613void KernelCore::EnterSVCProfile() {
614 std::size_t core = impl->GetCurrentHostThreadID();
615 impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
616}
617
618void KernelCore::ExitSVCProfile() {
619 std::size_t core = impl->GetCurrentHostThreadID();
620 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
621}
622
560} // namespace Kernel 623} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 83de1f542..49bd47e89 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -4,15 +4,17 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <memory> 8#include <memory>
8#include <string> 9#include <string>
9#include <unordered_map> 10#include <unordered_map>
10#include <vector> 11#include <vector>
12#include "core/hardware_properties.h"
11#include "core/hle/kernel/memory/memory_types.h" 13#include "core/hle/kernel/memory/memory_types.h"
12#include "core/hle/kernel/object.h" 14#include "core/hle/kernel/object.h"
13 15
14namespace Core { 16namespace Core {
15struct EmuThreadHandle; 17class CPUInterruptHandler;
16class ExclusiveMonitor; 18class ExclusiveMonitor;
17class System; 19class System;
18} // namespace Core 20} // namespace Core
@@ -65,6 +67,9 @@ public:
65 KernelCore(KernelCore&&) = delete; 67 KernelCore(KernelCore&&) = delete;
66 KernelCore& operator=(KernelCore&&) = delete; 68 KernelCore& operator=(KernelCore&&) = delete;
67 69
70 /// Sets if emulation is multicore or single core, must be set before Initialize
71 void SetMulticore(bool is_multicore);
72
68 /// Resets the kernel to a clean slate for use. 73 /// Resets the kernel to a clean slate for use.
69 void Initialize(); 74 void Initialize();
70 75
@@ -110,6 +115,18 @@ public:
110 /// Gets the an instance of the respective physical CPU core. 115 /// Gets the an instance of the respective physical CPU core.
111 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; 116 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
112 117
118 /// Gets the sole instance of the Scheduler at the current running core.
119 Kernel::Scheduler& CurrentScheduler();
120
121 /// Gets the sole instance of the Scheduler at the current running core.
122 const Kernel::Scheduler& CurrentScheduler() const;
123
124 /// Gets the an instance of the current physical CPU core.
125 Kernel::PhysicalCore& CurrentPhysicalCore();
126
127 /// Gets the an instance of the current physical CPU core.
128 const Kernel::PhysicalCore& CurrentPhysicalCore() const;
129
113 /// Gets the an instance of the Synchronization Interface. 130 /// Gets the an instance of the Synchronization Interface.
114 Kernel::Synchronization& Synchronization(); 131 Kernel::Synchronization& Synchronization();
115 132
@@ -129,6 +146,10 @@ public:
129 146
130 const Core::ExclusiveMonitor& GetExclusiveMonitor() const; 147 const Core::ExclusiveMonitor& GetExclusiveMonitor() const;
131 148
149 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts();
150
151 const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const;
152
132 void InvalidateAllInstructionCaches(); 153 void InvalidateAllInstructionCaches();
133 154
134 /// Adds a port to the named port table 155 /// Adds a port to the named port table
@@ -191,6 +212,18 @@ public:
191 /// Gets the shared memory object for Time services. 212 /// Gets the shared memory object for Time services.
192 const Kernel::SharedMemory& GetTimeSharedMem() const; 213 const Kernel::SharedMemory& GetTimeSharedMem() const;
193 214
215 /// Suspend/unsuspend the OS.
216 void Suspend(bool in_suspention);
217
218 /// Exceptional exit the OS.
219 void ExceptionalExit();
220
221 bool IsMulticore() const;
222
223 void EnterSVCProfile();
224
225 void ExitSVCProfile();
226
194private: 227private:
195 friend class Object; 228 friend class Object;
196 friend class Process; 229 friend class Process;
@@ -208,9 +241,6 @@ private:
208 /// Creates a new thread ID, incrementing the internal thread ID counter. 241 /// Creates a new thread ID, incrementing the internal thread ID counter.
209 u64 CreateNewThreadID(); 242 u64 CreateNewThreadID();
210 243
211 /// Retrieves the event type used for thread wakeup callbacks.
212 const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const;
213
214 /// Provides a reference to the global handle table. 244 /// Provides a reference to the global handle table.
215 Kernel::HandleTable& GlobalHandleTable(); 245 Kernel::HandleTable& GlobalHandleTable();
216 246
@@ -219,6 +249,7 @@ private:
219 249
220 struct Impl; 250 struct Impl;
221 std::unique_ptr<Impl> impl; 251 std::unique_ptr<Impl> impl;
252 bool exception_exited{};
222}; 253};
223 254
224} // namespace Kernel 255} // namespace Kernel
diff --git a/src/core/hle/kernel/memory/memory_manager.cpp b/src/core/hle/kernel/memory/memory_manager.cpp
index 6b432e1b2..acf13585c 100644
--- a/src/core/hle/kernel/memory/memory_manager.cpp
+++ b/src/core/hle/kernel/memory/memory_manager.cpp
@@ -104,7 +104,7 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
104 // Ensure that we don't leave anything un-freed 104 // Ensure that we don't leave anything un-freed
105 auto group_guard = detail::ScopeExit([&] { 105 auto group_guard = detail::ScopeExit([&] {
106 for (const auto& it : page_list.Nodes()) { 106 for (const auto& it : page_list.Nodes()) {
107 const auto min_num_pages{std::min( 107 const auto min_num_pages{std::min<size_t>(
108 it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; 108 it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)};
109 chosen_manager.Free(it.GetAddress(), min_num_pages); 109 chosen_manager.Free(it.GetAddress(), min_num_pages);
110 } 110 }
@@ -139,7 +139,6 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
139 } 139 }
140 140
141 // Only succeed if we allocated as many pages as we wanted 141 // Only succeed if we allocated as many pages as we wanted
142 ASSERT(num_pages >= 0);
143 if (num_pages) { 142 if (num_pages) {
144 return ERR_OUT_OF_MEMORY; 143 return ERR_OUT_OF_MEMORY;
145 } 144 }
@@ -165,7 +164,7 @@ ResultCode MemoryManager::Free(PageLinkedList& page_list, std::size_t num_pages,
165 164
166 // Free all of the pages 165 // Free all of the pages
167 for (const auto& it : page_list.Nodes()) { 166 for (const auto& it : page_list.Nodes()) {
168 const auto min_num_pages{std::min( 167 const auto min_num_pages{std::min<size_t>(
169 it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; 168 it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)};
170 chosen_manager.Free(it.GetAddress(), min_num_pages); 169 chosen_manager.Free(it.GetAddress(), min_num_pages);
171 } 170 }
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 7869eb32b..8f6c944d1 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -34,8 +34,6 @@ static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThr
34 if (thread->GetMutexWaitAddress() != mutex_addr) 34 if (thread->GetMutexWaitAddress() != mutex_addr)
35 continue; 35 continue;
36 36
37 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
38
39 ++num_waiters; 37 ++num_waiters;
40 if (highest_priority_thread == nullptr || 38 if (highest_priority_thread == nullptr ||
41 thread->GetPriority() < highest_priority_thread->GetPriority()) { 39 thread->GetPriority() < highest_priority_thread->GetPriority()) {
@@ -49,6 +47,7 @@ static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThr
49/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. 47/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
50static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread, 48static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread,
51 std::shared_ptr<Thread> new_owner) { 49 std::shared_ptr<Thread> new_owner) {
50 current_thread->RemoveMutexWaiter(new_owner);
52 const auto threads = current_thread->GetMutexWaitingThreads(); 51 const auto threads = current_thread->GetMutexWaitingThreads();
53 for (const auto& thread : threads) { 52 for (const auto& thread : threads) {
54 if (thread->GetMutexWaitAddress() != mutex_addr) 53 if (thread->GetMutexWaitAddress() != mutex_addr)
@@ -72,85 +71,100 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
72 return ERR_INVALID_ADDRESS; 71 return ERR_INVALID_ADDRESS;
73 } 72 }
74 73
75 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 74 auto& kernel = system.Kernel();
76 std::shared_ptr<Thread> current_thread = 75 std::shared_ptr<Thread> current_thread =
77 SharedFrom(system.CurrentScheduler().GetCurrentThread()); 76 SharedFrom(kernel.CurrentScheduler().GetCurrentThread());
78 std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); 77 {
79 std::shared_ptr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle); 78 SchedulerLock lock(kernel);
79 // The mutex address must be 4-byte aligned
80 if ((address % sizeof(u32)) != 0) {
81 return ERR_INVALID_ADDRESS;
82 }
80 83
81 // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another 84 const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
82 // thread. 85 std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
83 ASSERT(requesting_thread == current_thread); 86 std::shared_ptr<Thread> requesting_thread =
87 handle_table.Get<Thread>(requesting_thread_handle);
84 88
85 const u32 addr_value = system.Memory().Read32(address); 89 // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of
90 // another thread.
91 ASSERT(requesting_thread == current_thread);
86 92
87 // If the mutex isn't being held, just return success. 93 current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
88 if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
89 return RESULT_SUCCESS;
90 }
91 94
92 if (holding_thread == nullptr) { 95 const u32 addr_value = system.Memory().Read32(address);
93 LOG_ERROR(Kernel, "Holding thread does not exist! thread_handle={:08X}", 96
94 holding_thread_handle); 97 // If the mutex isn't being held, just return success.
95 return ERR_INVALID_HANDLE; 98 if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
96 } 99 return RESULT_SUCCESS;
100 }
97 101
98 // Wait until the mutex is released 102 if (holding_thread == nullptr) {
99 current_thread->SetMutexWaitAddress(address); 103 return ERR_INVALID_HANDLE;
100 current_thread->SetWaitHandle(requesting_thread_handle); 104 }
101 105
102 current_thread->SetStatus(ThreadStatus::WaitMutex); 106 // Wait until the mutex is released
103 current_thread->InvalidateWakeupCallback(); 107 current_thread->SetMutexWaitAddress(address);
108 current_thread->SetWaitHandle(requesting_thread_handle);
104 109
105 // Update the lock holder thread's priority to prevent priority inversion. 110 current_thread->SetStatus(ThreadStatus::WaitMutex);
106 holding_thread->AddMutexWaiter(current_thread);
107 111
108 system.PrepareReschedule(); 112 // Update the lock holder thread's priority to prevent priority inversion.
113 holding_thread->AddMutexWaiter(current_thread);
114 }
109 115
110 return RESULT_SUCCESS; 116 {
117 SchedulerLock lock(kernel);
118 auto* owner = current_thread->GetLockOwner();
119 if (owner != nullptr) {
120 owner->RemoveMutexWaiter(current_thread);
121 }
122 }
123 return current_thread->GetSignalingResult();
111} 124}
112 125
113ResultCode Mutex::Release(VAddr address) { 126std::pair<ResultCode, std::shared_ptr<Thread>> Mutex::Unlock(std::shared_ptr<Thread> owner,
127 VAddr address) {
114 // The mutex address must be 4-byte aligned 128 // The mutex address must be 4-byte aligned
115 if ((address % sizeof(u32)) != 0) { 129 if ((address % sizeof(u32)) != 0) {
116 LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); 130 LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address);
117 return ERR_INVALID_ADDRESS; 131 return {ERR_INVALID_ADDRESS, nullptr};
118 } 132 }
119 133
120 std::shared_ptr<Thread> current_thread = 134 auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address);
121 SharedFrom(system.CurrentScheduler().GetCurrentThread()); 135 if (new_owner == nullptr) {
122 auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
123
124 // There are no more threads waiting for the mutex, release it completely.
125 if (thread == nullptr) {
126 system.Memory().Write32(address, 0); 136 system.Memory().Write32(address, 0);
127 return RESULT_SUCCESS; 137 return {RESULT_SUCCESS, nullptr};
128 } 138 }
129
130 // Transfer the ownership of the mutex from the previous owner to the new one. 139 // Transfer the ownership of the mutex from the previous owner to the new one.
131 TransferMutexOwnership(address, current_thread, thread); 140 TransferMutexOwnership(address, owner, new_owner);
132 141 u32 mutex_value = new_owner->GetWaitHandle();
133 u32 mutex_value = thread->GetWaitHandle();
134
135 if (num_waiters >= 2) { 142 if (num_waiters >= 2) {
136 // Notify the guest that there are still some threads waiting for the mutex 143 // Notify the guest that there are still some threads waiting for the mutex
137 mutex_value |= Mutex::MutexHasWaitersFlag; 144 mutex_value |= Mutex::MutexHasWaitersFlag;
138 } 145 }
146 new_owner->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
147 new_owner->SetLockOwner(nullptr);
148 new_owner->ResumeFromWait();
139 149
140 // Grant the mutex to the next waiting thread and resume it.
141 system.Memory().Write32(address, mutex_value); 150 system.Memory().Write32(address, mutex_value);
151 return {RESULT_SUCCESS, new_owner};
152}
142 153
143 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); 154ResultCode Mutex::Release(VAddr address) {
144 thread->ResumeFromWait(); 155 auto& kernel = system.Kernel();
156 SchedulerLock lock(kernel);
145 157
146 thread->SetLockOwner(nullptr); 158 std::shared_ptr<Thread> current_thread =
147 thread->SetCondVarWaitAddress(0); 159 SharedFrom(kernel.CurrentScheduler().GetCurrentThread());
148 thread->SetMutexWaitAddress(0);
149 thread->SetWaitHandle(0);
150 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
151 160
152 system.PrepareReschedule(); 161 auto [result, new_owner] = Unlock(current_thread, address);
153 162
154 return RESULT_SUCCESS; 163 if (result != RESULT_SUCCESS && new_owner != nullptr) {
164 new_owner->SetSynchronizationResults(nullptr, result);
165 }
166
167 return result;
155} 168}
169
156} // namespace Kernel 170} // namespace Kernel
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index b904de2e8..3b81dc3df 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -28,6 +28,10 @@ public:
28 ResultCode TryAcquire(VAddr address, Handle holding_thread_handle, 28 ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
29 Handle requesting_thread_handle); 29 Handle requesting_thread_handle);
30 30
31 /// Unlocks a mutex for owner at address
32 std::pair<ResultCode, std::shared_ptr<Thread>> Unlock(std::shared_ptr<Thread> owner,
33 VAddr address);
34
31 /// Releases the mutex at the specified address. 35 /// Releases the mutex at the specified address.
32 ResultCode Release(VAddr address); 36 ResultCode Release(VAddr address);
33 37
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index a15011076..c6bbdb080 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -2,12 +2,15 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h"
5#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/spin_lock.h"
6#include "core/arm/arm_interface.h" 8#include "core/arm/arm_interface.h"
7#ifdef ARCHITECTURE_x86_64 9#ifdef ARCHITECTURE_x86_64
8#include "core/arm/dynarmic/arm_dynarmic_32.h" 10#include "core/arm/dynarmic/arm_dynarmic_32.h"
9#include "core/arm/dynarmic/arm_dynarmic_64.h" 11#include "core/arm/dynarmic/arm_dynarmic_64.h"
10#endif 12#endif
13#include "core/arm/cpu_interrupt_handler.h"
11#include "core/arm/exclusive_monitor.h" 14#include "core/arm/exclusive_monitor.h"
12#include "core/arm/unicorn/arm_unicorn.h" 15#include "core/arm/unicorn/arm_unicorn.h"
13#include "core/core.h" 16#include "core/core.h"
@@ -17,50 +20,37 @@
17 20
18namespace Kernel { 21namespace Kernel {
19 22
20PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, 23PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler,
21 Core::ExclusiveMonitor& exclusive_monitor) 24 Core::CPUInterruptHandler& interrupt_handler)
22 : core_index{id} { 25 : interrupt_handler{interrupt_handler}, core_index{id}, scheduler{scheduler} {
23#ifdef ARCHITECTURE_x86_64
24 arm_interface_32 =
25 std::make_unique<Core::ARM_Dynarmic_32>(system, exclusive_monitor, core_index);
26 arm_interface_64 =
27 std::make_unique<Core::ARM_Dynarmic_64>(system, exclusive_monitor, core_index);
28
29#else
30 using Core::ARM_Unicorn;
31 arm_interface_32 = std::make_unique<ARM_Unicorn>(system, ARM_Unicorn::Arch::AArch32);
32 arm_interface_64 = std::make_unique<ARM_Unicorn>(system, ARM_Unicorn::Arch::AArch64);
33 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
34#endif
35 26
36 scheduler = std::make_unique<Kernel::Scheduler>(system, core_index); 27 guard = std::make_unique<Common::SpinLock>();
37} 28}
38 29
39PhysicalCore::~PhysicalCore() = default; 30PhysicalCore::~PhysicalCore() = default;
40 31
41void PhysicalCore::Run() { 32void PhysicalCore::Idle() {
42 arm_interface->Run(); 33 interrupt_handler.AwaitInterrupt();
43 arm_interface->ClearExclusiveState();
44} 34}
45 35
46void PhysicalCore::Step() { 36void PhysicalCore::Shutdown() {
47 arm_interface->Step(); 37 scheduler.Shutdown();
48} 38}
49 39
50void PhysicalCore::Stop() { 40bool PhysicalCore::IsInterrupted() const {
51 arm_interface->PrepareReschedule(); 41 return interrupt_handler.IsInterrupted();
52} 42}
53 43
54void PhysicalCore::Shutdown() { 44void PhysicalCore::Interrupt() {
55 scheduler->Shutdown(); 45 guard->lock();
46 interrupt_handler.SetInterrupt(true);
47 guard->unlock();
56} 48}
57 49
58void PhysicalCore::SetIs64Bit(bool is_64_bit) { 50void PhysicalCore::ClearInterrupt() {
59 if (is_64_bit) { 51 guard->lock();
60 arm_interface = arm_interface_64.get(); 52 interrupt_handler.SetInterrupt(false);
61 } else { 53 guard->unlock();
62 arm_interface = arm_interface_32.get();
63 }
64} 54}
65 55
66} // namespace Kernel 56} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 3269166be..d7a7a951c 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -7,12 +7,17 @@
7#include <cstddef> 7#include <cstddef>
8#include <memory> 8#include <memory>
9 9
10namespace Common {
11class SpinLock;
12}
13
10namespace Kernel { 14namespace Kernel {
11class Scheduler; 15class Scheduler;
12} // namespace Kernel 16} // namespace Kernel
13 17
14namespace Core { 18namespace Core {
15class ARM_Interface; 19class ARM_Interface;
20class CPUInterruptHandler;
16class ExclusiveMonitor; 21class ExclusiveMonitor;
17class System; 22class System;
18} // namespace Core 23} // namespace Core
@@ -21,7 +26,8 @@ namespace Kernel {
21 26
22class PhysicalCore { 27class PhysicalCore {
23public: 28public:
24 PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor); 29 PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler,
30 Core::CPUInterruptHandler& interrupt_handler);
25 ~PhysicalCore(); 31 ~PhysicalCore();
26 32
27 PhysicalCore(const PhysicalCore&) = delete; 33 PhysicalCore(const PhysicalCore&) = delete;
@@ -30,23 +36,18 @@ public:
30 PhysicalCore(PhysicalCore&&) = default; 36 PhysicalCore(PhysicalCore&&) = default;
31 PhysicalCore& operator=(PhysicalCore&&) = default; 37 PhysicalCore& operator=(PhysicalCore&&) = default;
32 38
33 /// Execute current jit state 39 void Idle();
34 void Run(); 40 /// Interrupt this physical core.
35 /// Execute a single instruction in current jit. 41 void Interrupt();
36 void Step();
37 /// Stop JIT execution/exit
38 void Stop();
39 42
40 // Shutdown this physical core. 43 /// Clear this core's interrupt
41 void Shutdown(); 44 void ClearInterrupt();
42 45
43 Core::ARM_Interface& ArmInterface() { 46 /// Check if this core is interrupted
44 return *arm_interface; 47 bool IsInterrupted() const;
45 }
46 48
47 const Core::ARM_Interface& ArmInterface() const { 49 // Shutdown this physical core.
48 return *arm_interface; 50 void Shutdown();
49 }
50 51
51 bool IsMainCore() const { 52 bool IsMainCore() const {
52 return core_index == 0; 53 return core_index == 0;
@@ -61,21 +62,18 @@ public:
61 } 62 }
62 63
63 Kernel::Scheduler& Scheduler() { 64 Kernel::Scheduler& Scheduler() {
64 return *scheduler; 65 return scheduler;
65 } 66 }
66 67
67 const Kernel::Scheduler& Scheduler() const { 68 const Kernel::Scheduler& Scheduler() const {
68 return *scheduler; 69 return scheduler;
69 } 70 }
70 71
71 void SetIs64Bit(bool is_64_bit);
72
73private: 72private:
73 Core::CPUInterruptHandler& interrupt_handler;
74 std::size_t core_index; 74 std::size_t core_index;
75 std::unique_ptr<Core::ARM_Interface> arm_interface_32; 75 Kernel::Scheduler& scheduler;
76 std::unique_ptr<Core::ARM_Interface> arm_interface_64; 76 std::unique_ptr<Common::SpinLock> guard;
77 std::unique_ptr<Kernel::Scheduler> scheduler;
78 Core::ARM_Interface* arm_interface{};
79}; 77};
80 78
81} // namespace Kernel 79} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 36724569f..c6fcb56ad 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -22,6 +22,7 @@
22#include "core/hle/kernel/resource_limit.h" 22#include "core/hle/kernel/resource_limit.h"
23#include "core/hle/kernel/scheduler.h" 23#include "core/hle/kernel/scheduler.h"
24#include "core/hle/kernel/thread.h" 24#include "core/hle/kernel/thread.h"
25#include "core/hle/lock.h"
25#include "core/memory.h" 26#include "core/memory.h"
26#include "core/settings.h" 27#include "core/settings.h"
27 28
@@ -30,14 +31,15 @@ namespace {
30/** 31/**
31 * Sets up the primary application thread 32 * Sets up the primary application thread
32 * 33 *
34 * @param system The system instance to create the main thread under.
33 * @param owner_process The parent process for the main thread 35 * @param owner_process The parent process for the main thread
34 * @param kernel The kernel instance to create the main thread under.
35 * @param priority The priority to give the main thread 36 * @param priority The priority to give the main thread
36 */ 37 */
37void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, VAddr stack_top) { 38void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
38 const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); 39 const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
39 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, 40 ThreadType type = THREADTYPE_USER;
40 owner_process.GetIdealCore(), stack_top, owner_process); 41 auto thread_res = Thread::Create(system, type, "main", entry_point, priority, 0,
42 owner_process.GetIdealCore(), stack_top, &owner_process);
41 43
42 std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap(); 44 std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap();
43 45
@@ -48,8 +50,12 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, V
48 thread->GetContext32().cpu_registers[1] = thread_handle; 50 thread->GetContext32().cpu_registers[1] = thread_handle;
49 thread->GetContext64().cpu_registers[1] = thread_handle; 51 thread->GetContext64().cpu_registers[1] = thread_handle;
50 52
53 auto& kernel = system.Kernel();
51 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires 54 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
52 thread->ResumeFromWait(); 55 {
56 SchedulerLock lock{kernel};
57 thread->SetStatus(ThreadStatus::Ready);
58 }
53} 59}
54} // Anonymous namespace 60} // Anonymous namespace
55 61
@@ -117,7 +123,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
117 : kernel.CreateNewUserProcessID(); 123 : kernel.CreateNewUserProcessID();
118 process->capabilities.InitializeForMetadatalessProcess(); 124 process->capabilities.InitializeForMetadatalessProcess();
119 125
120 std::mt19937 rng(Settings::values.rng_seed.value_or(0)); 126 std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0));
121 std::uniform_int_distribution<u64> distribution; 127 std::uniform_int_distribution<u64> distribution;
122 std::generate(process->random_entropy.begin(), process->random_entropy.end(), 128 std::generate(process->random_entropy.begin(), process->random_entropy.end(),
123 [&] { return distribution(rng); }); 129 [&] { return distribution(rng); });
@@ -132,7 +138,8 @@ std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const {
132 138
133u64 Process::GetTotalPhysicalMemoryAvailable() const { 139u64 Process::GetTotalPhysicalMemoryAvailable() const {
134 const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) + 140 const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) +
135 page_table->GetTotalHeapSize() + image_size + main_thread_stack_size}; 141 page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
142 main_thread_stack_size};
136 143
137 if (capacity < memory_usage_capacity) { 144 if (capacity < memory_usage_capacity) {
138 return capacity; 145 return capacity;
@@ -146,7 +153,8 @@ u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
146} 153}
147 154
148u64 Process::GetTotalPhysicalMemoryUsed() const { 155u64 Process::GetTotalPhysicalMemoryUsed() const {
149 return image_size + main_thread_stack_size + page_table->GetTotalHeapSize(); 156 return image_size + main_thread_stack_size + page_table->GetTotalHeapSize() +
157 GetSystemResourceSize();
150} 158}
151 159
152u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { 160u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
@@ -180,7 +188,6 @@ void Process::RemoveConditionVariableThread(std::shared_ptr<Thread> thread) {
180 } 188 }
181 ++it; 189 ++it;
182 } 190 }
183 UNREACHABLE();
184} 191}
185 192
186std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads( 193std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads(
@@ -205,6 +212,7 @@ void Process::UnregisterThread(const Thread* thread) {
205} 212}
206 213
207ResultCode Process::ClearSignalState() { 214ResultCode Process::ClearSignalState() {
215 SchedulerLock lock(system.Kernel());
208 if (status == ProcessStatus::Exited) { 216 if (status == ProcessStatus::Exited) {
209 LOG_ERROR(Kernel, "called on a terminated process instance."); 217 LOG_ERROR(Kernel, "called on a terminated process instance.");
210 return ERR_INVALID_STATE; 218 return ERR_INVALID_STATE;
@@ -292,7 +300,7 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) {
292 300
293 ChangeStatus(ProcessStatus::Running); 301 ChangeStatus(ProcessStatus::Running);
294 302
295 SetupMainThread(*this, kernel, main_thread_priority, main_thread_stack_top); 303 SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top);
296 resource_limit->Reserve(ResourceType::Threads, 1); 304 resource_limit->Reserve(ResourceType::Threads, 1);
297 resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size); 305 resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size);
298} 306}
@@ -338,6 +346,7 @@ static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
338} 346}
339 347
340VAddr Process::CreateTLSRegion() { 348VAddr Process::CreateTLSRegion() {
349 SchedulerLock lock(system.Kernel());
341 if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; 350 if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)};
342 tls_page_iter != tls_pages.cend()) { 351 tls_page_iter != tls_pages.cend()) {
343 return *tls_page_iter->ReserveSlot(); 352 return *tls_page_iter->ReserveSlot();
@@ -368,6 +377,7 @@ VAddr Process::CreateTLSRegion() {
368} 377}
369 378
370void Process::FreeTLSRegion(VAddr tls_address) { 379void Process::FreeTLSRegion(VAddr tls_address) {
380 SchedulerLock lock(system.Kernel());
371 const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); 381 const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE);
372 auto iter = 382 auto iter =
373 std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { 383 std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
@@ -382,6 +392,7 @@ void Process::FreeTLSRegion(VAddr tls_address) {
382} 392}
383 393
384void Process::LoadModule(CodeSet code_set, VAddr base_addr) { 394void Process::LoadModule(CodeSet code_set, VAddr base_addr) {
395 std::lock_guard lock{HLE::g_hle_lock};
385 const auto ReprotectSegment = [&](const CodeSet::Segment& segment, 396 const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
386 Memory::MemoryPermission permission) { 397 Memory::MemoryPermission permission) {
387 page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission); 398 page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission);
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp
index 00860fcbd..6e286419e 100644
--- a/src/core/hle/kernel/readable_event.cpp
+++ b/src/core/hle/kernel/readable_event.cpp
@@ -6,8 +6,10 @@
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/kernel/errors.h" 8#include "core/hle/kernel/errors.h"
9#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/object.h" 10#include "core/hle/kernel/object.h"
10#include "core/hle/kernel/readable_event.h" 11#include "core/hle/kernel/readable_event.h"
12#include "core/hle/kernel/scheduler.h"
11#include "core/hle/kernel/thread.h" 13#include "core/hle/kernel/thread.h"
12 14
13namespace Kernel { 15namespace Kernel {
@@ -37,8 +39,9 @@ void ReadableEvent::Clear() {
37} 39}
38 40
39ResultCode ReadableEvent::Reset() { 41ResultCode ReadableEvent::Reset() {
42 SchedulerLock lock(kernel);
40 if (!is_signaled) { 43 if (!is_signaled) {
41 LOG_ERROR(Kernel, "Handle is not signaled! object_id={}, object_type={}, object_name={}", 44 LOG_TRACE(Kernel, "Handle is not signaled! object_id={}, object_type={}, object_name={}",
42 GetObjectId(), GetTypeName(), GetName()); 45 GetObjectId(), GetTypeName(), GetName());
43 return ERR_INVALID_STATE; 46 return ERR_INVALID_STATE;
44 } 47 }
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index d9beaa3a4..212e442f4 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -24,13 +24,9 @@ bool ResourceLimit::Reserve(ResourceType resource, s64 amount, u64 timeout) {
24 const std::size_t index{ResourceTypeToIndex(resource)}; 24 const std::size_t index{ResourceTypeToIndex(resource)};
25 25
26 s64 new_value = current[index] + amount; 26 s64 new_value = current[index] + amount;
27 while (new_value > limit[index] && available[index] + amount <= limit[index]) { 27 if (new_value > limit[index] && available[index] + amount <= limit[index]) {
28 // TODO(bunnei): This is wrong for multicore, we should wait the calling thread for timeout 28 // TODO(bunnei): This is wrong for multicore, we should wait the calling thread for timeout
29 new_value = current[index] + amount; 29 new_value = current[index] + amount;
30
31 if (timeout >= 0) {
32 break;
33 }
34 } 30 }
35 31
36 if (new_value <= limit[index]) { 32 if (new_value <= limit[index]) {
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 1140c72a3..7b929781c 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -6,16 +6,21 @@
6// licensed under GPLv2 or later under exception provided by the author. 6// licensed under GPLv2 or later under exception provided by the author.
7 7
8#include <algorithm> 8#include <algorithm>
9#include <mutex>
9#include <set> 10#include <set>
10#include <unordered_set> 11#include <unordered_set>
11#include <utility> 12#include <utility>
12 13
13#include "common/assert.h" 14#include "common/assert.h"
15#include "common/bit_util.h"
16#include "common/fiber.h"
14#include "common/logging/log.h" 17#include "common/logging/log.h"
15#include "core/arm/arm_interface.h" 18#include "core/arm/arm_interface.h"
16#include "core/core.h" 19#include "core/core.h"
17#include "core/core_timing.h" 20#include "core/core_timing.h"
21#include "core/cpu_manager.h"
18#include "core/hle/kernel/kernel.h" 22#include "core/hle/kernel/kernel.h"
23#include "core/hle/kernel/physical_core.h"
19#include "core/hle/kernel/process.h" 24#include "core/hle/kernel/process.h"
20#include "core/hle/kernel/scheduler.h" 25#include "core/hle/kernel/scheduler.h"
21#include "core/hle/kernel/time_manager.h" 26#include "core/hle/kernel/time_manager.h"
@@ -27,103 +32,148 @@ GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {}
27GlobalScheduler::~GlobalScheduler() = default; 32GlobalScheduler::~GlobalScheduler() = default;
28 33
29void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) { 34void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) {
35 std::scoped_lock lock{global_list_guard};
30 thread_list.push_back(std::move(thread)); 36 thread_list.push_back(std::move(thread));
31} 37}
32 38
33void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) { 39void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
40 std::scoped_lock lock{global_list_guard};
34 thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), 41 thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
35 thread_list.end()); 42 thread_list.end());
36} 43}
37 44
38void GlobalScheduler::UnloadThread(std::size_t core) { 45u32 GlobalScheduler::SelectThreads() {
39 Scheduler& sched = kernel.Scheduler(core); 46 ASSERT(is_locked);
40 sched.UnloadThread();
41}
42
43void GlobalScheduler::SelectThread(std::size_t core) {
44 const auto update_thread = [](Thread* thread, Scheduler& sched) { 47 const auto update_thread = [](Thread* thread, Scheduler& sched) {
45 if (thread != sched.selected_thread.get()) { 48 std::scoped_lock lock{sched.guard};
49 if (thread != sched.selected_thread_set.get()) {
46 if (thread == nullptr) { 50 if (thread == nullptr) {
47 ++sched.idle_selection_count; 51 ++sched.idle_selection_count;
48 } 52 }
49 sched.selected_thread = SharedFrom(thread); 53 sched.selected_thread_set = SharedFrom(thread);
50 } 54 }
51 sched.is_context_switch_pending = sched.selected_thread != sched.current_thread; 55 const bool reschedule_pending =
56 sched.is_context_switch_pending || (sched.selected_thread_set != sched.current_thread);
57 sched.is_context_switch_pending = reschedule_pending;
52 std::atomic_thread_fence(std::memory_order_seq_cst); 58 std::atomic_thread_fence(std::memory_order_seq_cst);
59 return reschedule_pending;
53 }; 60 };
54 Scheduler& sched = kernel.Scheduler(core); 61 if (!is_reselection_pending.load()) {
55 Thread* current_thread = nullptr; 62 return 0;
56 // Step 1: Get top thread in schedule queue.
57 current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front();
58 if (current_thread) {
59 update_thread(current_thread, sched);
60 return;
61 } 63 }
62 // Step 2: Try selecting a suggested thread. 64 std::array<Thread*, Core::Hardware::NUM_CPU_CORES> top_threads{};
63 Thread* winner = nullptr; 65
64 std::set<s32> sug_cores; 66 u32 idle_cores{};
65 for (auto thread : suggested_queue[core]) { 67
66 s32 this_core = thread->GetProcessorID(); 68 // Step 1: Get top thread in schedule queue.
67 Thread* thread_on_core = nullptr; 69 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
68 if (this_core >= 0) { 70 Thread* top_thread =
69 thread_on_core = scheduled_queue[this_core].front(); 71 scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front();
70 } 72 if (top_thread != nullptr) {
71 if (this_core < 0 || thread != thread_on_core) { 73 // TODO(Blinkhawk): Implement Thread Pinning
72 winner = thread; 74 } else {
73 break; 75 idle_cores |= (1ul << core);
74 } 76 }
75 sug_cores.insert(this_core); 77 top_threads[core] = top_thread;
76 } 78 }
77 // if we got a suggested thread, select it, else do a second pass. 79
78 if (winner && winner->GetPriority() > 2) { 80 while (idle_cores != 0) {
79 if (winner->IsRunning()) { 81 u32 core_id = Common::CountTrailingZeroes32(idle_cores);
80 UnloadThread(static_cast<u32>(winner->GetProcessorID())); 82
83 if (!suggested_queue[core_id].empty()) {
84 std::array<s32, Core::Hardware::NUM_CPU_CORES> migration_candidates{};
85 std::size_t num_candidates = 0;
86 auto iter = suggested_queue[core_id].begin();
87 Thread* suggested = nullptr;
88 // Step 2: Try selecting a suggested thread.
89 while (iter != suggested_queue[core_id].end()) {
90 suggested = *iter;
91 iter++;
92 s32 suggested_core_id = suggested->GetProcessorID();
93 Thread* top_thread =
94 suggested_core_id >= 0 ? top_threads[suggested_core_id] : nullptr;
95 if (top_thread != suggested) {
96 if (top_thread != nullptr &&
97 top_thread->GetPriority() < THREADPRIO_MAX_CORE_MIGRATION) {
98 suggested = nullptr;
99 break;
100 // There's a too high thread to do core migration, cancel
101 }
102 TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id), suggested);
103 break;
104 }
105 suggested = nullptr;
106 migration_candidates[num_candidates++] = suggested_core_id;
107 }
108 // Step 3: Select a suggested thread from another core
109 if (suggested == nullptr) {
110 for (std::size_t i = 0; i < num_candidates; i++) {
111 s32 candidate_core = migration_candidates[i];
112 suggested = top_threads[candidate_core];
113 auto it = scheduled_queue[candidate_core].begin();
114 it++;
115 Thread* next = it != scheduled_queue[candidate_core].end() ? *it : nullptr;
116 if (next != nullptr) {
117 TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id),
118 suggested);
119 top_threads[candidate_core] = next;
120 break;
121 } else {
122 suggested = nullptr;
123 }
124 }
125 }
126 top_threads[core_id] = suggested;
81 } 127 }
82 TransferToCore(winner->GetPriority(), static_cast<s32>(core), winner); 128
83 update_thread(winner, sched); 129 idle_cores &= ~(1ul << core_id);
84 return;
85 } 130 }
86 // Step 3: Select a suggested thread from another core 131 u32 cores_needing_context_switch{};
87 for (auto& src_core : sug_cores) { 132 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
88 auto it = scheduled_queue[src_core].begin(); 133 Scheduler& sched = kernel.Scheduler(core);
89 it++; 134 ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core);
90 if (it != scheduled_queue[src_core].end()) { 135 if (update_thread(top_threads[core], sched)) {
91 Thread* thread_on_core = scheduled_queue[src_core].front(); 136 cores_needing_context_switch |= (1ul << core);
92 Thread* to_change = *it;
93 if (thread_on_core->IsRunning() || to_change->IsRunning()) {
94 UnloadThread(static_cast<u32>(src_core));
95 }
96 TransferToCore(thread_on_core->GetPriority(), static_cast<s32>(core), thread_on_core);
97 current_thread = thread_on_core;
98 break;
99 } 137 }
100 } 138 }
101 update_thread(current_thread, sched); 139 return cores_needing_context_switch;
102} 140}
103 141
104bool GlobalScheduler::YieldThread(Thread* yielding_thread) { 142bool GlobalScheduler::YieldThread(Thread* yielding_thread) {
143 ASSERT(is_locked);
105 // Note: caller should use critical section, etc. 144 // Note: caller should use critical section, etc.
145 if (!yielding_thread->IsRunnable()) {
146 // Normally this case shouldn't happen except for SetThreadActivity.
147 is_reselection_pending.store(true, std::memory_order_release);
148 return false;
149 }
106 const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); 150 const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
107 const u32 priority = yielding_thread->GetPriority(); 151 const u32 priority = yielding_thread->GetPriority();
108 152
109 // Yield the thread 153 // Yield the thread
110 const Thread* const winner = scheduled_queue[core_id].front(priority); 154 Reschedule(priority, core_id, yielding_thread);
111 ASSERT_MSG(yielding_thread == winner, "Thread yielding without being in front"); 155 const Thread* const winner = scheduled_queue[core_id].front();
112 scheduled_queue[core_id].yield(priority); 156 if (kernel.GetCurrentHostThreadID() != core_id) {
157 is_reselection_pending.store(true, std::memory_order_release);
158 }
113 159
114 return AskForReselectionOrMarkRedundant(yielding_thread, winner); 160 return AskForReselectionOrMarkRedundant(yielding_thread, winner);
115} 161}
116 162
117bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { 163bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) {
164 ASSERT(is_locked);
118 // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, 165 // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
119 // etc. 166 // etc.
167 if (!yielding_thread->IsRunnable()) {
168 // Normally this case shouldn't happen except for SetThreadActivity.
169 is_reselection_pending.store(true, std::memory_order_release);
170 return false;
171 }
120 const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); 172 const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
121 const u32 priority = yielding_thread->GetPriority(); 173 const u32 priority = yielding_thread->GetPriority();
122 174
123 // Yield the thread 175 // Yield the thread
124 ASSERT_MSG(yielding_thread == scheduled_queue[core_id].front(priority), 176 Reschedule(priority, core_id, yielding_thread);
125 "Thread yielding without being in front");
126 scheduled_queue[core_id].yield(priority);
127 177
128 std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads; 178 std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads;
129 for (std::size_t i = 0; i < current_threads.size(); i++) { 179 for (std::size_t i = 0; i < current_threads.size(); i++) {
@@ -153,21 +203,28 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) {
153 203
154 if (winner != nullptr) { 204 if (winner != nullptr) {
155 if (winner != yielding_thread) { 205 if (winner != yielding_thread) {
156 if (winner->IsRunning()) {
157 UnloadThread(static_cast<u32>(winner->GetProcessorID()));
158 }
159 TransferToCore(winner->GetPriority(), s32(core_id), winner); 206 TransferToCore(winner->GetPriority(), s32(core_id), winner);
160 } 207 }
161 } else { 208 } else {
162 winner = next_thread; 209 winner = next_thread;
163 } 210 }
164 211
212 if (kernel.GetCurrentHostThreadID() != core_id) {
213 is_reselection_pending.store(true, std::memory_order_release);
214 }
215
165 return AskForReselectionOrMarkRedundant(yielding_thread, winner); 216 return AskForReselectionOrMarkRedundant(yielding_thread, winner);
166} 217}
167 218
168bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) { 219bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) {
220 ASSERT(is_locked);
169 // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section, 221 // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
170 // etc. 222 // etc.
223 if (!yielding_thread->IsRunnable()) {
224 // Normally this case shouldn't happen except for SetThreadActivity.
225 is_reselection_pending.store(true, std::memory_order_release);
226 return false;
227 }
171 Thread* winner = nullptr; 228 Thread* winner = nullptr;
172 const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID()); 229 const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
173 230
@@ -195,25 +252,31 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread
195 } 252 }
196 if (winner != nullptr) { 253 if (winner != nullptr) {
197 if (winner != yielding_thread) { 254 if (winner != yielding_thread) {
198 if (winner->IsRunning()) {
199 UnloadThread(static_cast<u32>(winner->GetProcessorID()));
200 }
201 TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner); 255 TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner);
202 } 256 }
203 } else { 257 } else {
204 winner = yielding_thread; 258 winner = yielding_thread;
205 } 259 }
260 } else {
261 winner = scheduled_queue[core_id].front();
262 }
263
264 if (kernel.GetCurrentHostThreadID() != core_id) {
265 is_reselection_pending.store(true, std::memory_order_release);
206 } 266 }
207 267
208 return AskForReselectionOrMarkRedundant(yielding_thread, winner); 268 return AskForReselectionOrMarkRedundant(yielding_thread, winner);
209} 269}
210 270
211void GlobalScheduler::PreemptThreads() { 271void GlobalScheduler::PreemptThreads() {
272 ASSERT(is_locked);
212 for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 273 for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
213 const u32 priority = preemption_priorities[core_id]; 274 const u32 priority = preemption_priorities[core_id];
214 275
215 if (scheduled_queue[core_id].size(priority) > 0) { 276 if (scheduled_queue[core_id].size(priority) > 0) {
216 scheduled_queue[core_id].front(priority)->IncrementYieldCount(); 277 if (scheduled_queue[core_id].size(priority) > 1) {
278 scheduled_queue[core_id].front(priority)->IncrementYieldCount();
279 }
217 scheduled_queue[core_id].yield(priority); 280 scheduled_queue[core_id].yield(priority);
218 if (scheduled_queue[core_id].size(priority) > 1) { 281 if (scheduled_queue[core_id].size(priority) > 1) {
219 scheduled_queue[core_id].front(priority)->IncrementYieldCount(); 282 scheduled_queue[core_id].front(priority)->IncrementYieldCount();
@@ -247,9 +310,6 @@ void GlobalScheduler::PreemptThreads() {
247 } 310 }
248 311
249 if (winner != nullptr) { 312 if (winner != nullptr) {
250 if (winner->IsRunning()) {
251 UnloadThread(static_cast<u32>(winner->GetProcessorID()));
252 }
253 TransferToCore(winner->GetPriority(), s32(core_id), winner); 313 TransferToCore(winner->GetPriority(), s32(core_id), winner);
254 current_thread = 314 current_thread =
255 winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread; 315 winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread;
@@ -280,9 +340,6 @@ void GlobalScheduler::PreemptThreads() {
280 } 340 }
281 341
282 if (winner != nullptr) { 342 if (winner != nullptr) {
283 if (winner->IsRunning()) {
284 UnloadThread(static_cast<u32>(winner->GetProcessorID()));
285 }
286 TransferToCore(winner->GetPriority(), s32(core_id), winner); 343 TransferToCore(winner->GetPriority(), s32(core_id), winner);
287 current_thread = winner; 344 current_thread = winner;
288 } 345 }
@@ -292,34 +349,65 @@ void GlobalScheduler::PreemptThreads() {
292 } 349 }
293} 350}
294 351
352void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule,
353 Core::EmuThreadHandle global_thread) {
354 u32 current_core = global_thread.host_handle;
355 bool must_context_switch = global_thread.guest_handle != InvalidHandle &&
356 (current_core < Core::Hardware::NUM_CPU_CORES);
357 while (cores_pending_reschedule != 0) {
358 u32 core = Common::CountTrailingZeroes32(cores_pending_reschedule);
359 ASSERT(core < Core::Hardware::NUM_CPU_CORES);
360 if (!must_context_switch || core != current_core) {
361 auto& phys_core = kernel.PhysicalCore(core);
362 phys_core.Interrupt();
363 } else {
364 must_context_switch = true;
365 }
366 cores_pending_reschedule &= ~(1ul << core);
367 }
368 if (must_context_switch) {
369 auto& core_scheduler = kernel.CurrentScheduler();
370 kernel.ExitSVCProfile();
371 core_scheduler.TryDoContextSwitch();
372 kernel.EnterSVCProfile();
373 }
374}
375
295void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) { 376void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) {
377 ASSERT(is_locked);
296 suggested_queue[core].add(thread, priority); 378 suggested_queue[core].add(thread, priority);
297} 379}
298 380
299void GlobalScheduler::Unsuggest(u32 priority, std::size_t core, Thread* thread) { 381void GlobalScheduler::Unsuggest(u32 priority, std::size_t core, Thread* thread) {
382 ASSERT(is_locked);
300 suggested_queue[core].remove(thread, priority); 383 suggested_queue[core].remove(thread, priority);
301} 384}
302 385
303void GlobalScheduler::Schedule(u32 priority, std::size_t core, Thread* thread) { 386void GlobalScheduler::Schedule(u32 priority, std::size_t core, Thread* thread) {
387 ASSERT(is_locked);
304 ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core."); 388 ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core.");
305 scheduled_queue[core].add(thread, priority); 389 scheduled_queue[core].add(thread, priority);
306} 390}
307 391
308void GlobalScheduler::SchedulePrepend(u32 priority, std::size_t core, Thread* thread) { 392void GlobalScheduler::SchedulePrepend(u32 priority, std::size_t core, Thread* thread) {
393 ASSERT(is_locked);
309 ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core."); 394 ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core.");
310 scheduled_queue[core].add(thread, priority, false); 395 scheduled_queue[core].add(thread, priority, false);
311} 396}
312 397
313void GlobalScheduler::Reschedule(u32 priority, std::size_t core, Thread* thread) { 398void GlobalScheduler::Reschedule(u32 priority, std::size_t core, Thread* thread) {
399 ASSERT(is_locked);
314 scheduled_queue[core].remove(thread, priority); 400 scheduled_queue[core].remove(thread, priority);
315 scheduled_queue[core].add(thread, priority); 401 scheduled_queue[core].add(thread, priority);
316} 402}
317 403
318void GlobalScheduler::Unschedule(u32 priority, std::size_t core, Thread* thread) { 404void GlobalScheduler::Unschedule(u32 priority, std::size_t core, Thread* thread) {
405 ASSERT(is_locked);
319 scheduled_queue[core].remove(thread, priority); 406 scheduled_queue[core].remove(thread, priority);
320} 407}
321 408
322void GlobalScheduler::TransferToCore(u32 priority, s32 destination_core, Thread* thread) { 409void GlobalScheduler::TransferToCore(u32 priority, s32 destination_core, Thread* thread) {
410 ASSERT(is_locked);
323 const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT; 411 const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT;
324 const s32 source_core = thread->GetProcessorID(); 412 const s32 source_core = thread->GetProcessorID();
325 if (source_core == destination_core || !schedulable) { 413 if (source_core == destination_core || !schedulable) {
@@ -349,6 +437,108 @@ bool GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread,
349 } 437 }
350} 438}
351 439
440void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) {
441 if (old_flags == thread->scheduling_state) {
442 return;
443 }
444 ASSERT(is_locked);
445
446 if (old_flags == static_cast<u32>(ThreadSchedStatus::Runnable)) {
447 // In this case the thread was running, now it's pausing/exitting
448 if (thread->processor_id >= 0) {
449 Unschedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
450 }
451
452 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
453 if (core != static_cast<u32>(thread->processor_id) &&
454 ((thread->affinity_mask >> core) & 1) != 0) {
455 Unsuggest(thread->current_priority, core, thread);
456 }
457 }
458 } else if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) {
459 // The thread is now set to running from being stopped
460 if (thread->processor_id >= 0) {
461 Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
462 }
463
464 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
465 if (core != static_cast<u32>(thread->processor_id) &&
466 ((thread->affinity_mask >> core) & 1) != 0) {
467 Suggest(thread->current_priority, core, thread);
468 }
469 }
470 }
471
472 SetReselectionPending();
473}
474
475void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priority) {
476 if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable)) {
477 return;
478 }
479 ASSERT(is_locked);
480 if (thread->processor_id >= 0) {
481 Unschedule(old_priority, static_cast<u32>(thread->processor_id), thread);
482 }
483
484 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
485 if (core != static_cast<u32>(thread->processor_id) &&
486 ((thread->affinity_mask >> core) & 1) != 0) {
487 Unsuggest(old_priority, core, thread);
488 }
489 }
490
491 if (thread->processor_id >= 0) {
492 if (thread == kernel.CurrentScheduler().GetCurrentThread()) {
493 SchedulePrepend(thread->current_priority, static_cast<u32>(thread->processor_id),
494 thread);
495 } else {
496 Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
497 }
498 }
499
500 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
501 if (core != static_cast<u32>(thread->processor_id) &&
502 ((thread->affinity_mask >> core) & 1) != 0) {
503 Suggest(thread->current_priority, core, thread);
504 }
505 }
506 thread->IncrementYieldCount();
507 SetReselectionPending();
508}
509
510void GlobalScheduler::AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask,
511 s32 old_core) {
512 if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable) ||
513 thread->current_priority >= THREADPRIO_COUNT) {
514 return;
515 }
516 ASSERT(is_locked);
517
518 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
519 if (((old_affinity_mask >> core) & 1) != 0) {
520 if (core == static_cast<u32>(old_core)) {
521 Unschedule(thread->current_priority, core, thread);
522 } else {
523 Unsuggest(thread->current_priority, core, thread);
524 }
525 }
526 }
527
528 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
529 if (((thread->affinity_mask >> core) & 1) != 0) {
530 if (core == static_cast<u32>(thread->processor_id)) {
531 Schedule(thread->current_priority, core, thread);
532 } else {
533 Suggest(thread->current_priority, core, thread);
534 }
535 }
536 }
537
538 thread->IncrementYieldCount();
539 SetReselectionPending();
540}
541
352void GlobalScheduler::Shutdown() { 542void GlobalScheduler::Shutdown() {
353 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 543 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
354 scheduled_queue[core].clear(); 544 scheduled_queue[core].clear();
@@ -359,10 +549,12 @@ void GlobalScheduler::Shutdown() {
359 549
360void GlobalScheduler::Lock() { 550void GlobalScheduler::Lock() {
361 Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID(); 551 Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID();
552 ASSERT(!current_thread.IsInvalid());
362 if (current_thread == current_owner) { 553 if (current_thread == current_owner) {
363 ++scope_lock; 554 ++scope_lock;
364 } else { 555 } else {
365 inner_lock.lock(); 556 inner_lock.lock();
557 is_locked = true;
366 current_owner = current_thread; 558 current_owner = current_thread;
367 ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle()); 559 ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle());
368 scope_lock = 1; 560 scope_lock = 1;
@@ -374,17 +566,18 @@ void GlobalScheduler::Unlock() {
374 ASSERT(scope_lock > 0); 566 ASSERT(scope_lock > 0);
375 return; 567 return;
376 } 568 }
377 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 569 u32 cores_pending_reschedule = SelectThreads();
378 SelectThread(i); 570 Core::EmuThreadHandle leaving_thread = current_owner;
379 }
380 current_owner = Core::EmuThreadHandle::InvalidHandle(); 571 current_owner = Core::EmuThreadHandle::InvalidHandle();
381 scope_lock = 1; 572 scope_lock = 1;
573 is_locked = false;
382 inner_lock.unlock(); 574 inner_lock.unlock();
383 // TODO(Blinkhawk): Setup the interrupts and change context on current core. 575 EnableInterruptAndSchedule(cores_pending_reschedule, leaving_thread);
384} 576}
385 577
386Scheduler::Scheduler(Core::System& system, std::size_t core_id) 578Scheduler::Scheduler(Core::System& system, std::size_t core_id) : system(system), core_id(core_id) {
387 : system{system}, core_id{core_id} {} 579 switch_fiber = std::make_shared<Common::Fiber>(std::function<void(void*)>(OnSwitch), this);
580}
388 581
389Scheduler::~Scheduler() = default; 582Scheduler::~Scheduler() = default;
390 583
@@ -393,56 +586,128 @@ bool Scheduler::HaveReadyThreads() const {
393} 586}
394 587
395Thread* Scheduler::GetCurrentThread() const { 588Thread* Scheduler::GetCurrentThread() const {
396 return current_thread.get(); 589 if (current_thread) {
590 return current_thread.get();
591 }
592 return idle_thread.get();
397} 593}
398 594
399Thread* Scheduler::GetSelectedThread() const { 595Thread* Scheduler::GetSelectedThread() const {
400 return selected_thread.get(); 596 return selected_thread.get();
401} 597}
402 598
403void Scheduler::SelectThreads() {
404 system.GlobalScheduler().SelectThread(core_id);
405}
406
407u64 Scheduler::GetLastContextSwitchTicks() const { 599u64 Scheduler::GetLastContextSwitchTicks() const {
408 return last_context_switch_time; 600 return last_context_switch_time;
409} 601}
410 602
411void Scheduler::TryDoContextSwitch() { 603void Scheduler::TryDoContextSwitch() {
604 auto& phys_core = system.Kernel().CurrentPhysicalCore();
605 if (phys_core.IsInterrupted()) {
606 phys_core.ClearInterrupt();
607 }
608 guard.lock();
412 if (is_context_switch_pending) { 609 if (is_context_switch_pending) {
413 SwitchContext(); 610 SwitchContext();
611 } else {
612 guard.unlock();
414 } 613 }
415} 614}
416 615
417void Scheduler::UnloadThread() { 616void Scheduler::OnThreadStart() {
418 Thread* const previous_thread = GetCurrentThread(); 617 SwitchContextStep2();
419 Process* const previous_process = system.Kernel().CurrentProcess(); 618}
420 619
421 UpdateLastContextSwitchTime(previous_thread, previous_process); 620void Scheduler::Unload() {
621 Thread* thread = current_thread.get();
622 if (thread) {
623 thread->SetContinuousOnSVC(false);
624 thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
625 thread->SetIsRunning(false);
626 if (!thread->IsHLEThread() && !thread->HasExited()) {
627 Core::ARM_Interface& cpu_core = thread->ArmInterface();
628 cpu_core.SaveContext(thread->GetContext32());
629 cpu_core.SaveContext(thread->GetContext64());
630 // Save the TPIDR_EL0 system register in case it was modified.
631 thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
632 cpu_core.ClearExclusiveState();
633 }
634 thread->context_guard.unlock();
635 }
636}
422 637
423 // Save context for previous thread 638void Scheduler::Reload() {
424 if (previous_thread) { 639 Thread* thread = current_thread.get();
425 system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32()); 640 if (thread) {
426 system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64()); 641 ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
427 // Save the TPIDR_EL0 system register in case it was modified. 642 "Thread must be runnable.");
428 previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0()); 643
644 // Cancel any outstanding wakeup events for this thread
645 thread->SetIsRunning(true);
646 thread->SetWasRunning(false);
647 thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
429 648
430 if (previous_thread->GetStatus() == ThreadStatus::Running) { 649 auto* const thread_owner_process = thread->GetOwnerProcess();
431 // This is only the case when a reschedule is triggered without the current thread 650 if (thread_owner_process != nullptr) {
432 // yielding execution (i.e. an event triggered, system core time-sliced, etc) 651 system.Kernel().MakeCurrentProcess(thread_owner_process);
433 previous_thread->SetStatus(ThreadStatus::Ready); 652 }
653 if (!thread->IsHLEThread()) {
654 Core::ARM_Interface& cpu_core = thread->ArmInterface();
655 cpu_core.LoadContext(thread->GetContext32());
656 cpu_core.LoadContext(thread->GetContext64());
657 cpu_core.SetTlsAddress(thread->GetTLSAddress());
658 cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
659 cpu_core.ChangeProcessorID(this->core_id);
660 cpu_core.ClearExclusiveState();
434 } 661 }
435 previous_thread->SetIsRunning(false);
436 } 662 }
437 current_thread = nullptr; 663}
664
665void Scheduler::SwitchContextStep2() {
666 Thread* previous_thread = current_thread_prev.get();
667 Thread* new_thread = selected_thread.get();
668
669 // Load context of new thread
670 Process* const previous_process =
671 previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr;
672
673 if (new_thread) {
674 ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
675 "Thread must be runnable.");
676
677 // Cancel any outstanding wakeup events for this thread
678 new_thread->SetIsRunning(true);
679 new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
680 new_thread->SetWasRunning(false);
681
682 auto* const thread_owner_process = current_thread->GetOwnerProcess();
683 if (thread_owner_process != nullptr) {
684 system.Kernel().MakeCurrentProcess(thread_owner_process);
685 }
686 if (!new_thread->IsHLEThread()) {
687 Core::ARM_Interface& cpu_core = new_thread->ArmInterface();
688 cpu_core.LoadContext(new_thread->GetContext32());
689 cpu_core.LoadContext(new_thread->GetContext64());
690 cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
691 cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
692 cpu_core.ChangeProcessorID(this->core_id);
693 cpu_core.ClearExclusiveState();
694 }
695 }
696
697 TryDoContextSwitch();
438} 698}
439 699
440void Scheduler::SwitchContext() { 700void Scheduler::SwitchContext() {
441 Thread* const previous_thread = GetCurrentThread(); 701 current_thread_prev = current_thread;
442 Thread* const new_thread = GetSelectedThread(); 702 selected_thread = selected_thread_set;
703 Thread* previous_thread = current_thread_prev.get();
704 Thread* new_thread = selected_thread.get();
705 current_thread = selected_thread;
443 706
444 is_context_switch_pending = false; 707 is_context_switch_pending = false;
708
445 if (new_thread == previous_thread) { 709 if (new_thread == previous_thread) {
710 guard.unlock();
446 return; 711 return;
447 } 712 }
448 713
@@ -452,51 +717,76 @@ void Scheduler::SwitchContext() {
452 717
453 // Save context for previous thread 718 // Save context for previous thread
454 if (previous_thread) { 719 if (previous_thread) {
455 system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32()); 720 if (new_thread != nullptr && new_thread->IsSuspendThread()) {
456 system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64()); 721 previous_thread->SetWasRunning(true);
457 // Save the TPIDR_EL0 system register in case it was modified.
458 previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0());
459
460 if (previous_thread->GetStatus() == ThreadStatus::Running) {
461 // This is only the case when a reschedule is triggered without the current thread
462 // yielding execution (i.e. an event triggered, system core time-sliced, etc)
463 previous_thread->SetStatus(ThreadStatus::Ready);
464 } 722 }
723 previous_thread->SetContinuousOnSVC(false);
724 previous_thread->last_running_ticks = system.CoreTiming().GetCPUTicks();
465 previous_thread->SetIsRunning(false); 725 previous_thread->SetIsRunning(false);
726 if (!previous_thread->IsHLEThread() && !previous_thread->HasExited()) {
727 Core::ARM_Interface& cpu_core = previous_thread->ArmInterface();
728 cpu_core.SaveContext(previous_thread->GetContext32());
729 cpu_core.SaveContext(previous_thread->GetContext64());
730 // Save the TPIDR_EL0 system register in case it was modified.
731 previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
732 cpu_core.ClearExclusiveState();
733 }
734 previous_thread->context_guard.unlock();
466 } 735 }
467 736
468 // Load context of new thread 737 std::shared_ptr<Common::Fiber>* old_context;
469 if (new_thread) { 738 if (previous_thread != nullptr) {
470 ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id), 739 old_context = &previous_thread->GetHostContext();
471 "Thread must be assigned to this core."); 740 } else {
472 ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready, 741 old_context = &idle_thread->GetHostContext();
473 "Thread must be ready to become running."); 742 }
743 guard.unlock();
474 744
475 // Cancel any outstanding wakeup events for this thread 745 Common::Fiber::YieldTo(*old_context, switch_fiber);
476 new_thread->CancelWakeupTimer(); 746 /// When a thread wakes up, the scheduler may have changed to other in another core.
477 current_thread = SharedFrom(new_thread); 747 auto& next_scheduler = system.Kernel().CurrentScheduler();
478 new_thread->SetStatus(ThreadStatus::Running); 748 next_scheduler.SwitchContextStep2();
479 new_thread->SetIsRunning(true); 749}
480 750
481 auto* const thread_owner_process = current_thread->GetOwnerProcess(); 751void Scheduler::OnSwitch(void* this_scheduler) {
482 if (previous_process != thread_owner_process) { 752 Scheduler* sched = static_cast<Scheduler*>(this_scheduler);
483 system.Kernel().MakeCurrentProcess(thread_owner_process); 753 sched->SwitchToCurrent();
484 } 754}
485 755
486 system.ArmInterface(core_id).LoadContext(new_thread->GetContext32()); 756void Scheduler::SwitchToCurrent() {
487 system.ArmInterface(core_id).LoadContext(new_thread->GetContext64()); 757 while (true) {
488 system.ArmInterface(core_id).SetTlsAddress(new_thread->GetTLSAddress()); 758 {
489 system.ArmInterface(core_id).SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); 759 std::scoped_lock lock{guard};
490 } else { 760 selected_thread = selected_thread_set;
491 current_thread = nullptr; 761 current_thread = selected_thread;
492 // Note: We do not reset the current process and current page table when idling because 762 is_context_switch_pending = false;
493 // technically we haven't changed processes, our threads are just paused. 763 }
764 while (!is_context_switch_pending) {
765 if (current_thread != nullptr && !current_thread->IsHLEThread()) {
766 current_thread->context_guard.lock();
767 if (!current_thread->IsRunnable()) {
768 current_thread->context_guard.unlock();
769 break;
770 }
771 if (current_thread->GetProcessorID() != core_id) {
772 current_thread->context_guard.unlock();
773 break;
774 }
775 }
776 std::shared_ptr<Common::Fiber>* next_context;
777 if (current_thread != nullptr) {
778 next_context = &current_thread->GetHostContext();
779 } else {
780 next_context = &idle_thread->GetHostContext();
781 }
782 Common::Fiber::YieldTo(switch_fiber, *next_context);
783 }
494 } 784 }
495} 785}
496 786
497void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { 787void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
498 const u64 prev_switch_ticks = last_context_switch_time; 788 const u64 prev_switch_ticks = last_context_switch_time;
499 const u64 most_recent_switch_ticks = system.CoreTiming().GetTicks(); 789 const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
500 const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; 790 const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
501 791
502 if (thread != nullptr) { 792 if (thread != nullptr) {
@@ -510,6 +800,16 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
510 last_context_switch_time = most_recent_switch_ticks; 800 last_context_switch_time = most_recent_switch_ticks;
511} 801}
512 802
803void Scheduler::Initialize() {
804 std::string name = "Idle Thread Id:" + std::to_string(core_id);
805 std::function<void(void*)> init_func = system.GetCpuManager().GetIdleThreadStartFunc();
806 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
807 ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE);
808 auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0,
809 nullptr, std::move(init_func), init_func_parameter);
810 idle_thread = std::move(thread_res).Unwrap();
811}
812
513void Scheduler::Shutdown() { 813void Scheduler::Shutdown() {
514 current_thread = nullptr; 814 current_thread = nullptr;
515 selected_thread = nullptr; 815 selected_thread = nullptr;
@@ -538,4 +838,13 @@ SchedulerLockAndSleep::~SchedulerLockAndSleep() {
538 time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds); 838 time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds);
539} 839}
540 840
841void SchedulerLockAndSleep::Release() {
842 if (sleep_cancelled) {
843 return;
844 }
845 auto& time_manager = kernel.TimeManager();
846 time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds);
847 sleep_cancelled = true;
848}
849
541} // namespace Kernel 850} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 07df33f9c..b3b4b5169 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -11,9 +11,14 @@
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/multi_level_queue.h" 13#include "common/multi_level_queue.h"
14#include "common/spin_lock.h"
14#include "core/hardware_properties.h" 15#include "core/hardware_properties.h"
15#include "core/hle/kernel/thread.h" 16#include "core/hle/kernel/thread.h"
16 17
18namespace Common {
19class Fiber;
20}
21
17namespace Core { 22namespace Core {
18class ARM_Interface; 23class ARM_Interface;
19class System; 24class System;
@@ -41,41 +46,17 @@ public:
41 return thread_list; 46 return thread_list;
42 } 47 }
43 48
44 /** 49 /// Notify the scheduler a thread's status has changed.
45 * Add a thread to the suggested queue of a cpu core. Suggested threads may be 50 void AdjustSchedulingOnStatus(Thread* thread, u32 old_flags);
46 * picked if no thread is scheduled to run on the core.
47 */
48 void Suggest(u32 priority, std::size_t core, Thread* thread);
49
50 /**
51 * Remove a thread to the suggested queue of a cpu core. Suggested threads may be
52 * picked if no thread is scheduled to run on the core.
53 */
54 void Unsuggest(u32 priority, std::size_t core, Thread* thread);
55
56 /**
57 * Add a thread to the scheduling queue of a cpu core. The thread is added at the
58 * back the queue in its priority level.
59 */
60 void Schedule(u32 priority, std::size_t core, Thread* thread);
61
62 /**
63 * Add a thread to the scheduling queue of a cpu core. The thread is added at the
64 * front the queue in its priority level.
65 */
66 void SchedulePrepend(u32 priority, std::size_t core, Thread* thread);
67 51
68 /// Reschedule an already scheduled thread based on a new priority 52 /// Notify the scheduler a thread's priority has changed.
69 void Reschedule(u32 priority, std::size_t core, Thread* thread); 53 void AdjustSchedulingOnPriority(Thread* thread, u32 old_priority);
70
71 /// Unschedules a thread.
72 void Unschedule(u32 priority, std::size_t core, Thread* thread);
73 54
74 /// Selects a core and forces it to unload its current thread's context 55 /// Notify the scheduler a thread's core and/or affinity mask has changed.
75 void UnloadThread(std::size_t core); 56 void AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, s32 old_core);
76 57
77 /** 58 /**
78 * Takes care of selecting the new scheduled thread in three steps: 59 * Takes care of selecting the new scheduled threads in three steps:
79 * 60 *
80 * 1. First a thread is selected from the top of the priority queue. If no thread 61 * 1. First a thread is selected from the top of the priority queue. If no thread
81 * is obtained then we move to step two, else we are done. 62 * is obtained then we move to step two, else we are done.
@@ -85,8 +66,10 @@ public:
85 * 66 *
86 * 3. Third is no suggested thread is found, we do a second pass and pick a running 67 * 3. Third is no suggested thread is found, we do a second pass and pick a running
87 * thread in another core and swap it with its current thread. 68 * thread in another core and swap it with its current thread.
69 *
70 * returns the cores needing scheduling.
88 */ 71 */
89 void SelectThread(std::size_t core); 72 u32 SelectThreads();
90 73
91 bool HaveReadyThreads(std::size_t core_id) const { 74 bool HaveReadyThreads(std::size_t core_id) const {
92 return !scheduled_queue[core_id].empty(); 75 return !scheduled_queue[core_id].empty();
@@ -149,6 +132,40 @@ private:
149 /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling 132 /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling
150 /// and reschedules current core if needed. 133 /// and reschedules current core if needed.
151 void Unlock(); 134 void Unlock();
135
136 void EnableInterruptAndSchedule(u32 cores_pending_reschedule,
137 Core::EmuThreadHandle global_thread);
138
139 /**
140 * Add a thread to the suggested queue of a cpu core. Suggested threads may be
141 * picked if no thread is scheduled to run on the core.
142 */
143 void Suggest(u32 priority, std::size_t core, Thread* thread);
144
145 /**
146 * Remove a thread to the suggested queue of a cpu core. Suggested threads may be
147 * picked if no thread is scheduled to run on the core.
148 */
149 void Unsuggest(u32 priority, std::size_t core, Thread* thread);
150
151 /**
152 * Add a thread to the scheduling queue of a cpu core. The thread is added at the
153 * back the queue in its priority level.
154 */
155 void Schedule(u32 priority, std::size_t core, Thread* thread);
156
157 /**
158 * Add a thread to the scheduling queue of a cpu core. The thread is added at the
159 * front the queue in its priority level.
160 */
161 void SchedulePrepend(u32 priority, std::size_t core, Thread* thread);
162
163 /// Reschedule an already scheduled thread based on a new priority
164 void Reschedule(u32 priority, std::size_t core, Thread* thread);
165
166 /// Unschedules a thread.
167 void Unschedule(u32 priority, std::size_t core, Thread* thread);
168
152 /** 169 /**
153 * Transfers a thread into an specific core. If the destination_core is -1 170 * Transfers a thread into an specific core. If the destination_core is -1
154 * it will be unscheduled from its source code and added into its suggested 171 * it will be unscheduled from its source code and added into its suggested
@@ -170,10 +187,13 @@ private:
170 std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62}; 187 std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62};
171 188
172 /// Scheduler lock mechanisms. 189 /// Scheduler lock mechanisms.
173 std::mutex inner_lock{}; // TODO(Blinkhawk): Replace for a SpinLock 190 bool is_locked{};
191 Common::SpinLock inner_lock{};
174 std::atomic<s64> scope_lock{}; 192 std::atomic<s64> scope_lock{};
175 Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; 193 Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()};
176 194
195 Common::SpinLock global_list_guard{};
196
177 /// Lists all thread ids that aren't deleted/etc. 197 /// Lists all thread ids that aren't deleted/etc.
178 std::vector<std::shared_ptr<Thread>> thread_list; 198 std::vector<std::shared_ptr<Thread>> thread_list;
179 KernelCore& kernel; 199 KernelCore& kernel;
@@ -190,11 +210,11 @@ public:
190 /// Reschedules to the next available thread (call after current thread is suspended) 210 /// Reschedules to the next available thread (call after current thread is suspended)
191 void TryDoContextSwitch(); 211 void TryDoContextSwitch();
192 212
193 /// Unloads currently running thread 213 /// The next two are for SingleCore Only.
194 void UnloadThread(); 214 /// Unload current thread before preempting core.
195 215 void Unload();
196 /// Select the threads in top of the scheduling multilist. 216 /// Reload current thread after core preemption.
197 void SelectThreads(); 217 void Reload();
198 218
199 /// Gets the current running thread 219 /// Gets the current running thread
200 Thread* GetCurrentThread() const; 220 Thread* GetCurrentThread() const;
@@ -209,15 +229,30 @@ public:
209 return is_context_switch_pending; 229 return is_context_switch_pending;
210 } 230 }
211 231
232 void Initialize();
233
212 /// Shutdowns the scheduler. 234 /// Shutdowns the scheduler.
213 void Shutdown(); 235 void Shutdown();
214 236
237 void OnThreadStart();
238
239 std::shared_ptr<Common::Fiber>& ControlContext() {
240 return switch_fiber;
241 }
242
243 const std::shared_ptr<Common::Fiber>& ControlContext() const {
244 return switch_fiber;
245 }
246
215private: 247private:
216 friend class GlobalScheduler; 248 friend class GlobalScheduler;
217 249
218 /// Switches the CPU's active thread context to that of the specified thread 250 /// Switches the CPU's active thread context to that of the specified thread
219 void SwitchContext(); 251 void SwitchContext();
220 252
253 /// When a thread wakes up, it must run this through it's new scheduler
254 void SwitchContextStep2();
255
221 /** 256 /**
222 * Called on every context switch to update the internal timestamp 257 * Called on every context switch to update the internal timestamp
223 * This also updates the running time ticks for the given thread and 258 * This also updates the running time ticks for the given thread and
@@ -231,14 +266,24 @@ private:
231 */ 266 */
232 void UpdateLastContextSwitchTime(Thread* thread, Process* process); 267 void UpdateLastContextSwitchTime(Thread* thread, Process* process);
233 268
269 static void OnSwitch(void* this_scheduler);
270 void SwitchToCurrent();
271
234 std::shared_ptr<Thread> current_thread = nullptr; 272 std::shared_ptr<Thread> current_thread = nullptr;
235 std::shared_ptr<Thread> selected_thread = nullptr; 273 std::shared_ptr<Thread> selected_thread = nullptr;
274 std::shared_ptr<Thread> current_thread_prev = nullptr;
275 std::shared_ptr<Thread> selected_thread_set = nullptr;
276 std::shared_ptr<Thread> idle_thread = nullptr;
277
278 std::shared_ptr<Common::Fiber> switch_fiber = nullptr;
236 279
237 Core::System& system; 280 Core::System& system;
238 u64 last_context_switch_time = 0; 281 u64 last_context_switch_time = 0;
239 u64 idle_selection_count = 0; 282 u64 idle_selection_count = 0;
240 const std::size_t core_id; 283 const std::size_t core_id;
241 284
285 Common::SpinLock guard{};
286
242 bool is_context_switch_pending = false; 287 bool is_context_switch_pending = false;
243}; 288};
244 289
@@ -261,6 +306,8 @@ public:
261 sleep_cancelled = true; 306 sleep_cancelled = true;
262 } 307 }
263 308
309 void Release();
310
264private: 311private:
265 Handle& event_handle; 312 Handle& event_handle;
266 Thread* time_task; 313 Thread* time_task;
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 25438b86b..7b23a6889 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -17,6 +17,7 @@
17#include "core/hle/kernel/hle_ipc.h" 17#include "core/hle/kernel/hle_ipc.h"
18#include "core/hle/kernel/kernel.h" 18#include "core/hle/kernel/kernel.h"
19#include "core/hle/kernel/process.h" 19#include "core/hle/kernel/process.h"
20#include "core/hle/kernel/scheduler.h"
20#include "core/hle/kernel/server_session.h" 21#include "core/hle/kernel/server_session.h"
21#include "core/hle/kernel/session.h" 22#include "core/hle/kernel/session.h"
22#include "core/hle/kernel/thread.h" 23#include "core/hle/kernel/thread.h"
@@ -168,9 +169,12 @@ ResultCode ServerSession::CompleteSyncRequest() {
168 } 169 }
169 170
170 // Some service requests require the thread to block 171 // Some service requests require the thread to block
171 if (!context.IsThreadWaiting()) { 172 {
172 context.GetThread().ResumeFromWait(); 173 SchedulerLock lock(kernel);
173 context.GetThread().SetWaitSynchronizationResult(result); 174 if (!context.IsThreadWaiting()) {
175 context.GetThread().ResumeFromWait();
176 context.GetThread().SetSynchronizationResults(nullptr, result);
177 }
174 } 178 }
175 179
176 request_queue.Pop(); 180 request_queue.Pop();
@@ -180,8 +184,10 @@ ResultCode ServerSession::CompleteSyncRequest() {
180 184
181ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, 185ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
182 Core::Memory::Memory& memory) { 186 Core::Memory::Memory& memory) {
183 Core::System::GetInstance().CoreTiming().ScheduleEvent(20000, request_event, {}); 187 ResultCode result = QueueSyncRequest(std::move(thread), memory);
184 return QueueSyncRequest(std::move(thread), memory); 188 const u64 delay = kernel.IsMulticore() ? 0U : 20000U;
189 Core::System::GetInstance().CoreTiming().ScheduleEvent(delay, request_event, {});
190 return result;
185} 191}
186 192
187} // namespace Kernel 193} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 4ae4529f5..5db19dcf3 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -10,14 +10,15 @@
10 10
11#include "common/alignment.h" 11#include "common/alignment.h"
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/fiber.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14#include "common/microprofile.h" 15#include "common/microprofile.h"
15#include "common/string_util.h" 16#include "common/string_util.h"
16#include "core/arm/exclusive_monitor.h" 17#include "core/arm/exclusive_monitor.h"
17#include "core/core.h" 18#include "core/core.h"
18#include "core/core_manager.h"
19#include "core/core_timing.h" 19#include "core/core_timing.h"
20#include "core/core_timing_util.h" 20#include "core/core_timing_util.h"
21#include "core/cpu_manager.h"
21#include "core/hle/kernel/address_arbiter.h" 22#include "core/hle/kernel/address_arbiter.h"
22#include "core/hle/kernel/client_port.h" 23#include "core/hle/kernel/client_port.h"
23#include "core/hle/kernel/client_session.h" 24#include "core/hle/kernel/client_session.h"
@@ -27,6 +28,7 @@
27#include "core/hle/kernel/memory/memory_block.h" 28#include "core/hle/kernel/memory/memory_block.h"
28#include "core/hle/kernel/memory/page_table.h" 29#include "core/hle/kernel/memory/page_table.h"
29#include "core/hle/kernel/mutex.h" 30#include "core/hle/kernel/mutex.h"
31#include "core/hle/kernel/physical_core.h"
30#include "core/hle/kernel/process.h" 32#include "core/hle/kernel/process.h"
31#include "core/hle/kernel/readable_event.h" 33#include "core/hle/kernel/readable_event.h"
32#include "core/hle/kernel/resource_limit.h" 34#include "core/hle/kernel/resource_limit.h"
@@ -37,6 +39,7 @@
37#include "core/hle/kernel/svc_wrap.h" 39#include "core/hle/kernel/svc_wrap.h"
38#include "core/hle/kernel/synchronization.h" 40#include "core/hle/kernel/synchronization.h"
39#include "core/hle/kernel/thread.h" 41#include "core/hle/kernel/thread.h"
42#include "core/hle/kernel/time_manager.h"
40#include "core/hle/kernel/transfer_memory.h" 43#include "core/hle/kernel/transfer_memory.h"
41#include "core/hle/kernel/writable_event.h" 44#include "core/hle/kernel/writable_event.h"
42#include "core/hle/lock.h" 45#include "core/hle/lock.h"
@@ -133,6 +136,7 @@ enum class ResourceLimitValueType {
133 136
134ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, 137ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit,
135 u32 resource_type, ResourceLimitValueType value_type) { 138 u32 resource_type, ResourceLimitValueType value_type) {
139 std::lock_guard lock{HLE::g_hle_lock};
136 const auto type = static_cast<ResourceType>(resource_type); 140 const auto type = static_cast<ResourceType>(resource_type);
137 if (!IsValidResourceType(type)) { 141 if (!IsValidResourceType(type)) {
138 LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); 142 LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
@@ -160,6 +164,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_
160 164
161/// Set the process heap to a given Size. It can both extend and shrink the heap. 165/// Set the process heap to a given Size. It can both extend and shrink the heap.
162static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) { 166static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) {
167 std::lock_guard lock{HLE::g_hle_lock};
163 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); 168 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
164 169
165 // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB. 170 // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB.
@@ -190,6 +195,7 @@ static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_s
190 195
191static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, 196static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
192 u32 attribute) { 197 u32 attribute) {
198 std::lock_guard lock{HLE::g_hle_lock};
193 LOG_DEBUG(Kernel_SVC, 199 LOG_DEBUG(Kernel_SVC,
194 "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, 200 "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
195 size, mask, attribute); 201 size, mask, attribute);
@@ -226,8 +232,15 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si
226 static_cast<Memory::MemoryAttribute>(attribute)); 232 static_cast<Memory::MemoryAttribute>(attribute));
227} 233}
228 234
235static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
236 u32 attribute) {
237 return SetMemoryAttribute(system, static_cast<VAddr>(address), static_cast<std::size_t>(size),
238 mask, attribute);
239}
240
229/// Maps a memory range into a different range. 241/// Maps a memory range into a different range.
230static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { 242static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
243 std::lock_guard lock{HLE::g_hle_lock};
231 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 244 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
232 src_addr, size); 245 src_addr, size);
233 246
@@ -241,8 +254,14 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr
241 return page_table.Map(dst_addr, src_addr, size); 254 return page_table.Map(dst_addr, src_addr, size);
242} 255}
243 256
257static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
258 return MapMemory(system, static_cast<VAddr>(dst_addr), static_cast<VAddr>(src_addr),
259 static_cast<std::size_t>(size));
260}
261
244/// Unmaps a region that was previously mapped with svcMapMemory 262/// Unmaps a region that was previously mapped with svcMapMemory
245static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { 263static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
264 std::lock_guard lock{HLE::g_hle_lock};
246 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 265 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
247 src_addr, size); 266 src_addr, size);
248 267
@@ -256,9 +275,15 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad
256 return page_table.Unmap(dst_addr, src_addr, size); 275 return page_table.Unmap(dst_addr, src_addr, size);
257} 276}
258 277
278static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
279 return UnmapMemory(system, static_cast<VAddr>(dst_addr), static_cast<VAddr>(src_addr),
280 static_cast<std::size_t>(size));
281}
282
259/// Connect to an OS service given the port name, returns the handle to the port to out 283/// Connect to an OS service given the port name, returns the handle to the port to out
260static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, 284static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
261 VAddr port_name_address) { 285 VAddr port_name_address) {
286 std::lock_guard lock{HLE::g_hle_lock};
262 auto& memory = system.Memory(); 287 auto& memory = system.Memory();
263 288
264 if (!memory.IsValidVirtualAddress(port_name_address)) { 289 if (!memory.IsValidVirtualAddress(port_name_address)) {
@@ -317,11 +342,30 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
317 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); 342 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
318 343
319 auto thread = system.CurrentScheduler().GetCurrentThread(); 344 auto thread = system.CurrentScheduler().GetCurrentThread();
320 thread->InvalidateWakeupCallback(); 345 {
321 thread->SetStatus(ThreadStatus::WaitIPC); 346 SchedulerLock lock(system.Kernel());
322 system.PrepareReschedule(thread->GetProcessorID()); 347 thread->InvalidateHLECallback();
348 thread->SetStatus(ThreadStatus::WaitIPC);
349 session->SendSyncRequest(SharedFrom(thread), system.Memory());
350 }
351
352 if (thread->HasHLECallback()) {
353 Handle event_handle = thread->GetHLETimeEvent();
354 if (event_handle != InvalidHandle) {
355 auto& time_manager = system.Kernel().TimeManager();
356 time_manager.UnscheduleTimeEvent(event_handle);
357 }
358
359 {
360 SchedulerLock lock(system.Kernel());
361 auto* sync_object = thread->GetHLESyncObject();
362 sync_object->RemoveWaitingThread(SharedFrom(thread));
363 }
364
365 thread->InvokeHLECallback(SharedFrom(thread));
366 }
323 367
324 return session->SendSyncRequest(SharedFrom(thread), system.Memory()); 368 return thread->GetSignalingResult();
325} 369}
326 370
327static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { 371static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
@@ -383,6 +427,15 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
383 return ERR_INVALID_HANDLE; 427 return ERR_INVALID_HANDLE;
384} 428}
385 429
430static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32* process_id_high,
431 Handle handle) {
432 u64 process_id{};
433 const auto result = GetProcessId(system, &process_id, handle);
434 *process_id_low = static_cast<u32>(process_id);
435 *process_id_high = static_cast<u32>(process_id >> 32);
436 return result;
437}
438
386/// Wait for the given handles to synchronize, timeout after the specified nanoseconds 439/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
387static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, 440static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address,
388 u64 handle_count, s64 nano_seconds) { 441 u64 handle_count, s64 nano_seconds) {
@@ -447,10 +500,13 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand
447 } 500 }
448 501
449 thread->CancelWait(); 502 thread->CancelWait();
450 system.PrepareReschedule(thread->GetProcessorID());
451 return RESULT_SUCCESS; 503 return RESULT_SUCCESS;
452} 504}
453 505
506static ResultCode CancelSynchronization32(Core::System& system, Handle thread_handle) {
507 return CancelSynchronization(system, thread_handle);
508}
509
454/// Attempts to locks a mutex, creating it if it does not already exist 510/// Attempts to locks a mutex, creating it if it does not already exist
455static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, 511static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle,
456 VAddr mutex_addr, Handle requesting_thread_handle) { 512 VAddr mutex_addr, Handle requesting_thread_handle) {
@@ -475,6 +531,12 @@ static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_hand
475 requesting_thread_handle); 531 requesting_thread_handle);
476} 532}
477 533
534static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle,
535 u32 mutex_addr, Handle requesting_thread_handle) {
536 return ArbitrateLock(system, holding_thread_handle, static_cast<VAddr>(mutex_addr),
537 requesting_thread_handle);
538}
539
478/// Unlock a mutex 540/// Unlock a mutex
479static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { 541static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {
480 LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); 542 LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr);
@@ -494,6 +556,10 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {
494 return current_process->GetMutex().Release(mutex_addr); 556 return current_process->GetMutex().Release(mutex_addr);
495} 557}
496 558
559static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) {
560 return ArbitrateUnlock(system, static_cast<VAddr>(mutex_addr));
561}
562
497enum class BreakType : u32 { 563enum class BreakType : u32 {
498 Panic = 0, 564 Panic = 0,
499 AssertionFailed = 1, 565 AssertionFailed = 1,
@@ -594,6 +660,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
594 info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); 660 info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
595 661
596 if (!break_reason.signal_debugger) { 662 if (!break_reason.signal_debugger) {
663 SchedulerLock lock(system.Kernel());
597 LOG_CRITICAL( 664 LOG_CRITICAL(
598 Debug_Emulated, 665 Debug_Emulated,
599 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 666 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
@@ -605,14 +672,16 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
605 const auto thread_processor_id = current_thread->GetProcessorID(); 672 const auto thread_processor_id = current_thread->GetProcessorID();
606 system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); 673 system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
607 674
608 system.Kernel().CurrentProcess()->PrepareForTermination();
609
610 // Kill the current thread 675 // Kill the current thread
676 system.Kernel().ExceptionalExit();
611 current_thread->Stop(); 677 current_thread->Stop();
612 system.PrepareReschedule();
613 } 678 }
614} 679}
615 680
681static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
682 Break(system, reason, static_cast<u64>(info1), static_cast<u64>(info2));
683}
684
616/// Used to output a message on a debug hardware unit - does nothing on a retail unit 685/// Used to output a message on a debug hardware unit - does nothing on a retail unit
617static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { 686static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) {
618 if (len == 0) { 687 if (len == 0) {
@@ -627,6 +696,7 @@ static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr addre
627/// Gets system/memory information for the current process 696/// Gets system/memory information for the current process
628static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle, 697static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle,
629 u64 info_sub_id) { 698 u64 info_sub_id) {
699 std::lock_guard lock{HLE::g_hle_lock};
630 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, 700 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
631 info_sub_id, handle); 701 info_sub_id, handle);
632 702
@@ -863,9 +933,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
863 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { 933 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
864 const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); 934 const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
865 935
866 out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks); 936 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
867 } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { 937 } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
868 out_ticks = core_timing.GetTicks() - prev_ctx_ticks; 938 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
869 } 939 }
870 940
871 *result = out_ticks; 941 *result = out_ticks;
@@ -892,6 +962,7 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h
892 962
893/// Maps memory at a desired address 963/// Maps memory at a desired address
894static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { 964static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
965 std::lock_guard lock{HLE::g_hle_lock};
895 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); 966 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
896 967
897 if (!Common::Is4KBAligned(addr)) { 968 if (!Common::Is4KBAligned(addr)) {
@@ -939,8 +1010,13 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size)
939 return page_table.MapPhysicalMemory(addr, size); 1010 return page_table.MapPhysicalMemory(addr, size);
940} 1011}
941 1012
1013static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
1014 return MapPhysicalMemory(system, static_cast<VAddr>(addr), static_cast<std::size_t>(size));
1015}
1016
942/// Unmaps memory previously mapped via MapPhysicalMemory 1017/// Unmaps memory previously mapped via MapPhysicalMemory
943static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { 1018static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
1019 std::lock_guard lock{HLE::g_hle_lock};
944 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); 1020 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
945 1021
946 if (!Common::Is4KBAligned(addr)) { 1022 if (!Common::Is4KBAligned(addr)) {
@@ -988,6 +1064,10 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size
988 return page_table.UnmapPhysicalMemory(addr, size); 1064 return page_table.UnmapPhysicalMemory(addr, size);
989} 1065}
990 1066
1067static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
1068 return UnmapPhysicalMemory(system, static_cast<VAddr>(addr), static_cast<std::size_t>(size));
1069}
1070
991/// Sets the thread activity 1071/// Sets the thread activity
992static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { 1072static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
993 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); 1073 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
@@ -1017,10 +1097,11 @@ static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 act
1017 return ERR_BUSY; 1097 return ERR_BUSY;
1018 } 1098 }
1019 1099
1020 thread->SetActivity(static_cast<ThreadActivity>(activity)); 1100 return thread->SetActivity(static_cast<ThreadActivity>(activity));
1101}
1021 1102
1022 system.PrepareReschedule(thread->GetProcessorID()); 1103static ResultCode SetThreadActivity32(Core::System& system, Handle handle, u32 activity) {
1023 return RESULT_SUCCESS; 1104 return SetThreadActivity(system, handle, activity);
1024} 1105}
1025 1106
1026/// Gets the thread context 1107/// Gets the thread context
@@ -1064,6 +1145,10 @@ static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, H
1064 return RESULT_SUCCESS; 1145 return RESULT_SUCCESS;
1065} 1146}
1066 1147
1148static ResultCode GetThreadContext32(Core::System& system, u32 thread_context, Handle handle) {
1149 return GetThreadContext(system, static_cast<VAddr>(thread_context), handle);
1150}
1151
1067/// Gets the priority for the specified thread 1152/// Gets the priority for the specified thread
1068static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) { 1153static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) {
1069 LOG_TRACE(Kernel_SVC, "called"); 1154 LOG_TRACE(Kernel_SVC, "called");
@@ -1071,6 +1156,7 @@ static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle
1071 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 1156 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1072 const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle); 1157 const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle);
1073 if (!thread) { 1158 if (!thread) {
1159 *priority = 0;
1074 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); 1160 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
1075 return ERR_INVALID_HANDLE; 1161 return ERR_INVALID_HANDLE;
1076 } 1162 }
@@ -1105,18 +1191,26 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri
1105 1191
1106 thread->SetPriority(priority); 1192 thread->SetPriority(priority);
1107 1193
1108 system.PrepareReschedule(thread->GetProcessorID());
1109 return RESULT_SUCCESS; 1194 return RESULT_SUCCESS;
1110} 1195}
1111 1196
1197static ResultCode SetThreadPriority32(Core::System& system, Handle handle, u32 priority) {
1198 return SetThreadPriority(system, handle, priority);
1199}
1200
1112/// Get which CPU core is executing the current thread 1201/// Get which CPU core is executing the current thread
1113static u32 GetCurrentProcessorNumber(Core::System& system) { 1202static u32 GetCurrentProcessorNumber(Core::System& system) {
1114 LOG_TRACE(Kernel_SVC, "called"); 1203 LOG_TRACE(Kernel_SVC, "called");
1115 return system.CurrentScheduler().GetCurrentThread()->GetProcessorID(); 1204 return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex());
1205}
1206
1207static u32 GetCurrentProcessorNumber32(Core::System& system) {
1208 return GetCurrentProcessorNumber(system);
1116} 1209}
1117 1210
1118static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, 1211static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr,
1119 u64 size, u32 permissions) { 1212 u64 size, u32 permissions) {
1213 std::lock_guard lock{HLE::g_hle_lock};
1120 LOG_TRACE(Kernel_SVC, 1214 LOG_TRACE(Kernel_SVC,
1121 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", 1215 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
1122 shared_memory_handle, addr, size, permissions); 1216 shared_memory_handle, addr, size, permissions);
@@ -1187,9 +1281,16 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han
1187 return shared_memory->Map(*current_process, addr, size, permission_type); 1281 return shared_memory->Map(*current_process, addr, size, permission_type);
1188} 1282}
1189 1283
1284static ResultCode MapSharedMemory32(Core::System& system, Handle shared_memory_handle, u32 addr,
1285 u32 size, u32 permissions) {
1286 return MapSharedMemory(system, shared_memory_handle, static_cast<VAddr>(addr),
1287 static_cast<std::size_t>(size), permissions);
1288}
1289
1190static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, 1290static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
1191 VAddr page_info_address, Handle process_handle, 1291 VAddr page_info_address, Handle process_handle,
1192 VAddr address) { 1292 VAddr address) {
1293 std::lock_guard lock{HLE::g_hle_lock};
1193 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); 1294 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
1194 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 1295 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1195 std::shared_ptr<Process> process = handle_table.Get<Process>(process_handle); 1296 std::shared_ptr<Process> process = handle_table.Get<Process>(process_handle);
@@ -1372,6 +1473,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
1372/// Exits the current process 1473/// Exits the current process
1373static void ExitProcess(Core::System& system) { 1474static void ExitProcess(Core::System& system) {
1374 auto* current_process = system.Kernel().CurrentProcess(); 1475 auto* current_process = system.Kernel().CurrentProcess();
1476 UNIMPLEMENTED();
1375 1477
1376 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); 1478 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
1377 ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, 1479 ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
@@ -1381,8 +1483,10 @@ static void ExitProcess(Core::System& system) {
1381 1483
1382 // Kill the current thread 1484 // Kill the current thread
1383 system.CurrentScheduler().GetCurrentThread()->Stop(); 1485 system.CurrentScheduler().GetCurrentThread()->Stop();
1486}
1384 1487
1385 system.PrepareReschedule(); 1488static void ExitProcess32(Core::System& system) {
1489 ExitProcess(system);
1386} 1490}
1387 1491
1388/// Creates a new thread 1492/// Creates a new thread
@@ -1428,9 +1532,10 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
1428 1532
1429 ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); 1533 ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1));
1430 1534
1535 ThreadType type = THREADTYPE_USER;
1431 CASCADE_RESULT(std::shared_ptr<Thread> thread, 1536 CASCADE_RESULT(std::shared_ptr<Thread> thread,
1432 Thread::Create(kernel, "", entry_point, priority, arg, processor_id, stack_top, 1537 Thread::Create(system, type, "", entry_point, priority, arg, processor_id,
1433 *current_process)); 1538 stack_top, current_process));
1434 1539
1435 const auto new_thread_handle = current_process->GetHandleTable().Create(thread); 1540 const auto new_thread_handle = current_process->GetHandleTable().Create(thread);
1436 if (new_thread_handle.Failed()) { 1541 if (new_thread_handle.Failed()) {
@@ -1444,11 +1549,15 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
1444 thread->SetName( 1549 thread->SetName(
1445 fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle)); 1550 fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle));
1446 1551
1447 system.PrepareReschedule(thread->GetProcessorID());
1448
1449 return RESULT_SUCCESS; 1552 return RESULT_SUCCESS;
1450} 1553}
1451 1554
1555static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 priority,
1556 u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) {
1557 return CreateThread(system, out_handle, static_cast<VAddr>(entry_point), static_cast<u64>(arg),
1558 static_cast<VAddr>(stack_top), priority, processor_id);
1559}
1560
1452/// Starts the thread for the provided handle 1561/// Starts the thread for the provided handle
1453static ResultCode StartThread(Core::System& system, Handle thread_handle) { 1562static ResultCode StartThread(Core::System& system, Handle thread_handle) {
1454 LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 1563 LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
@@ -1463,13 +1572,11 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
1463 1572
1464 ASSERT(thread->GetStatus() == ThreadStatus::Dormant); 1573 ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
1465 1574
1466 thread->ResumeFromWait(); 1575 return thread->Start();
1467 1576}
1468 if (thread->GetStatus() == ThreadStatus::Ready) {
1469 system.PrepareReschedule(thread->GetProcessorID());
1470 }
1471 1577
1472 return RESULT_SUCCESS; 1578static ResultCode StartThread32(Core::System& system, Handle thread_handle) {
1579 return StartThread(system, thread_handle);
1473} 1580}
1474 1581
1475/// Called when a thread exits 1582/// Called when a thread exits
@@ -1477,9 +1584,12 @@ static void ExitThread(Core::System& system) {
1477 LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); 1584 LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
1478 1585
1479 auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); 1586 auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
1480 current_thread->Stop();
1481 system.GlobalScheduler().RemoveThread(SharedFrom(current_thread)); 1587 system.GlobalScheduler().RemoveThread(SharedFrom(current_thread));
1482 system.PrepareReschedule(); 1588 current_thread->Stop();
1589}
1590
1591static void ExitThread32(Core::System& system) {
1592 ExitThread(system);
1483} 1593}
1484 1594
1485/// Sleep the current thread 1595/// Sleep the current thread
@@ -1498,15 +1608,21 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
1498 1608
1499 if (nanoseconds <= 0) { 1609 if (nanoseconds <= 0) {
1500 switch (static_cast<SleepType>(nanoseconds)) { 1610 switch (static_cast<SleepType>(nanoseconds)) {
1501 case SleepType::YieldWithoutLoadBalancing: 1611 case SleepType::YieldWithoutLoadBalancing: {
1502 is_redundant = current_thread->YieldSimple(); 1612 auto pair = current_thread->YieldSimple();
1613 is_redundant = pair.second;
1503 break; 1614 break;
1504 case SleepType::YieldWithLoadBalancing: 1615 }
1505 is_redundant = current_thread->YieldAndBalanceLoad(); 1616 case SleepType::YieldWithLoadBalancing: {
1617 auto pair = current_thread->YieldAndBalanceLoad();
1618 is_redundant = pair.second;
1506 break; 1619 break;
1507 case SleepType::YieldAndWaitForLoadBalancing: 1620 }
1508 is_redundant = current_thread->YieldAndWaitForLoadBalancing(); 1621 case SleepType::YieldAndWaitForLoadBalancing: {
1622 auto pair = current_thread->YieldAndWaitForLoadBalancing();
1623 is_redundant = pair.second;
1509 break; 1624 break;
1625 }
1510 default: 1626 default:
1511 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); 1627 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
1512 } 1628 }
@@ -1514,13 +1630,18 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
1514 current_thread->Sleep(nanoseconds); 1630 current_thread->Sleep(nanoseconds);
1515 } 1631 }
1516 1632
1517 if (is_redundant) { 1633 if (is_redundant && !system.Kernel().IsMulticore()) {
1518 // If it's redundant, the core is pretty much idle. Some games keep idling 1634 system.Kernel().ExitSVCProfile();
1519 // a core while it's doing nothing, we advance timing to avoid costly continuous 1635 system.CoreTiming().AddTicks(1000U);
1520 // calls. 1636 system.GetCpuManager().PreemptSingleCore();
1521 system.CoreTiming().AddTicks(2000); 1637 system.Kernel().EnterSVCProfile();
1522 } 1638 }
1523 system.PrepareReschedule(current_thread->GetProcessorID()); 1639}
1640
1641static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) {
1642 const s64 nanoseconds = static_cast<s64>(static_cast<u64>(nanoseconds_low) |
1643 (static_cast<u64>(nanoseconds_high) << 32));
1644 SleepThread(system, nanoseconds);
1524} 1645}
1525 1646
1526/// Wait process wide key atomic 1647/// Wait process wide key atomic
@@ -1547,31 +1668,69 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
1547 } 1668 }
1548 1669
1549 ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); 1670 ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
1550 1671 auto& kernel = system.Kernel();
1672 Handle event_handle;
1673 Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
1551 auto* const current_process = system.Kernel().CurrentProcess(); 1674 auto* const current_process = system.Kernel().CurrentProcess();
1552 const auto& handle_table = current_process->GetHandleTable(); 1675 {
1553 std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); 1676 SchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds);
1554 ASSERT(thread); 1677 const auto& handle_table = current_process->GetHandleTable();
1678 std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1679 ASSERT(thread);
1680
1681 current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
1682
1683 if (thread->IsPendingTermination()) {
1684 lock.CancelSleep();
1685 return ERR_THREAD_TERMINATING;
1686 }
1687
1688 const auto release_result = current_process->GetMutex().Release(mutex_addr);
1689 if (release_result.IsError()) {
1690 lock.CancelSleep();
1691 return release_result;
1692 }
1693
1694 if (nano_seconds == 0) {
1695 lock.CancelSleep();
1696 return RESULT_TIMEOUT;
1697 }
1555 1698
1556 const auto release_result = current_process->GetMutex().Release(mutex_addr); 1699 current_thread->SetCondVarWaitAddress(condition_variable_addr);
1557 if (release_result.IsError()) { 1700 current_thread->SetMutexWaitAddress(mutex_addr);
1558 return release_result; 1701 current_thread->SetWaitHandle(thread_handle);
1702 current_thread->SetStatus(ThreadStatus::WaitCondVar);
1703 current_process->InsertConditionVariableThread(SharedFrom(current_thread));
1559 } 1704 }
1560 1705
1561 Thread* current_thread = system.CurrentScheduler().GetCurrentThread(); 1706 if (event_handle != InvalidHandle) {
1562 current_thread->SetCondVarWaitAddress(condition_variable_addr); 1707 auto& time_manager = kernel.TimeManager();
1563 current_thread->SetMutexWaitAddress(mutex_addr); 1708 time_manager.UnscheduleTimeEvent(event_handle);
1564 current_thread->SetWaitHandle(thread_handle); 1709 }
1565 current_thread->SetStatus(ThreadStatus::WaitCondVar); 1710
1566 current_thread->InvalidateWakeupCallback(); 1711 {
1567 current_process->InsertConditionVariableThread(SharedFrom(current_thread)); 1712 SchedulerLock lock(kernel);
1568 1713
1569 current_thread->WakeAfterDelay(nano_seconds); 1714 auto* owner = current_thread->GetLockOwner();
1715 if (owner != nullptr) {
1716 owner->RemoveMutexWaiter(SharedFrom(current_thread));
1717 }
1570 1718
1719 current_process->RemoveConditionVariableThread(SharedFrom(current_thread));
1720 }
1571 // Note: Deliberately don't attempt to inherit the lock owner's priority. 1721 // Note: Deliberately don't attempt to inherit the lock owner's priority.
1572 1722
1573 system.PrepareReschedule(current_thread->GetProcessorID()); 1723 return current_thread->GetSignalingResult();
1574 return RESULT_SUCCESS; 1724}
1725
1726static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr,
1727 u32 condition_variable_addr, Handle thread_handle,
1728 u32 nanoseconds_low, u32 nanoseconds_high) {
1729 const s64 nanoseconds =
1730 static_cast<s64>(nanoseconds_low | (static_cast<u64>(nanoseconds_high) << 32));
1731 return WaitProcessWideKeyAtomic(system, static_cast<VAddr>(mutex_addr),
1732 static_cast<VAddr>(condition_variable_addr), thread_handle,
1733 nanoseconds);
1575} 1734}
1576 1735
1577/// Signal process wide key 1736/// Signal process wide key
@@ -1582,7 +1741,9 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
1582 ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); 1741 ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
1583 1742
1584 // Retrieve a list of all threads that are waiting for this condition variable. 1743 // Retrieve a list of all threads that are waiting for this condition variable.
1585 auto* const current_process = system.Kernel().CurrentProcess(); 1744 auto& kernel = system.Kernel();
1745 SchedulerLock lock(kernel);
1746 auto* const current_process = kernel.CurrentProcess();
1586 std::vector<std::shared_ptr<Thread>> waiting_threads = 1747 std::vector<std::shared_ptr<Thread>> waiting_threads =
1587 current_process->GetConditionVariableThreads(condition_variable_addr); 1748 current_process->GetConditionVariableThreads(condition_variable_addr);
1588 1749
@@ -1591,7 +1752,7 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
1591 std::size_t last = waiting_threads.size(); 1752 std::size_t last = waiting_threads.size();
1592 if (target > 0) 1753 if (target > 0)
1593 last = std::min(waiting_threads.size(), static_cast<std::size_t>(target)); 1754 last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
1594 1755 auto& time_manager = kernel.TimeManager();
1595 for (std::size_t index = 0; index < last; ++index) { 1756 for (std::size_t index = 0; index < last; ++index) {
1596 auto& thread = waiting_threads[index]; 1757 auto& thread = waiting_threads[index];
1597 1758
@@ -1599,7 +1760,6 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
1599 1760
1600 // liberate Cond Var Thread. 1761 // liberate Cond Var Thread.
1601 current_process->RemoveConditionVariableThread(thread); 1762 current_process->RemoveConditionVariableThread(thread);
1602 thread->SetCondVarWaitAddress(0);
1603 1763
1604 const std::size_t current_core = system.CurrentCoreIndex(); 1764 const std::size_t current_core = system.CurrentCoreIndex();
1605 auto& monitor = system.Monitor(); 1765 auto& monitor = system.Monitor();
@@ -1610,10 +1770,8 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
1610 u32 update_val = 0; 1770 u32 update_val = 0;
1611 const VAddr mutex_address = thread->GetMutexWaitAddress(); 1771 const VAddr mutex_address = thread->GetMutexWaitAddress();
1612 do { 1772 do {
1613 monitor.SetExclusive(current_core, mutex_address);
1614
1615 // If the mutex is not yet acquired, acquire it. 1773 // If the mutex is not yet acquired, acquire it.
1616 mutex_val = memory.Read32(mutex_address); 1774 mutex_val = monitor.ExclusiveRead32(current_core, mutex_address);
1617 1775
1618 if (mutex_val != 0) { 1776 if (mutex_val != 0) {
1619 update_val = mutex_val | Mutex::MutexHasWaitersFlag; 1777 update_val = mutex_val | Mutex::MutexHasWaitersFlag;
@@ -1621,33 +1779,28 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
1621 update_val = thread->GetWaitHandle(); 1779 update_val = thread->GetWaitHandle();
1622 } 1780 }
1623 } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val)); 1781 } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val));
1782 monitor.ClearExclusive();
1624 if (mutex_val == 0) { 1783 if (mutex_val == 0) {
1625 // We were able to acquire the mutex, resume this thread. 1784 // We were able to acquire the mutex, resume this thread.
1626 ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar);
1627 thread->ResumeFromWait();
1628
1629 auto* const lock_owner = thread->GetLockOwner(); 1785 auto* const lock_owner = thread->GetLockOwner();
1630 if (lock_owner != nullptr) { 1786 if (lock_owner != nullptr) {
1631 lock_owner->RemoveMutexWaiter(thread); 1787 lock_owner->RemoveMutexWaiter(thread);
1632 } 1788 }
1633 1789
1634 thread->SetLockOwner(nullptr); 1790 thread->SetLockOwner(nullptr);
1635 thread->SetMutexWaitAddress(0); 1791 thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
1636 thread->SetWaitHandle(0); 1792 thread->ResumeFromWait();
1637 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
1638 system.PrepareReschedule(thread->GetProcessorID());
1639 } else { 1793 } else {
1640 // The mutex is already owned by some other thread, make this thread wait on it. 1794 // The mutex is already owned by some other thread, make this thread wait on it.
1641 const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); 1795 const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
1642 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 1796 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1643 auto owner = handle_table.Get<Thread>(owner_handle); 1797 auto owner = handle_table.Get<Thread>(owner_handle);
1644 ASSERT(owner); 1798 ASSERT(owner);
1645 ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); 1799 if (thread->GetStatus() == ThreadStatus::WaitCondVar) {
1646 thread->InvalidateWakeupCallback(); 1800 thread->SetStatus(ThreadStatus::WaitMutex);
1647 thread->SetStatus(ThreadStatus::WaitMutex); 1801 }
1648 1802
1649 owner->AddMutexWaiter(thread); 1803 owner->AddMutexWaiter(thread);
1650 system.PrepareReschedule(thread->GetProcessorID());
1651 } 1804 }
1652 } 1805 }
1653} 1806}
@@ -1678,12 +1831,15 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type,
1678 auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); 1831 auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
1679 const ResultCode result = 1832 const ResultCode result =
1680 address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); 1833 address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
1681 if (result == RESULT_SUCCESS) {
1682 system.PrepareReschedule();
1683 }
1684 return result; 1834 return result;
1685} 1835}
1686 1836
1837static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value,
1838 u32 timeout_low, u32 timeout_high) {
1839 s64 timeout = static_cast<s64>(timeout_low | (static_cast<u64>(timeout_high) << 32));
1840 return WaitForAddress(system, static_cast<VAddr>(address), type, value, timeout);
1841}
1842
1687// Signals to an address (via Address Arbiter) 1843// Signals to an address (via Address Arbiter)
1688static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, 1844static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value,
1689 s32 num_to_wake) { 1845 s32 num_to_wake) {
@@ -1707,6 +1863,11 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type,
1707 return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); 1863 return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake);
1708} 1864}
1709 1865
1866static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value,
1867 s32 num_to_wake) {
1868 return SignalToAddress(system, static_cast<VAddr>(address), type, value, num_to_wake);
1869}
1870
1710static void KernelDebug([[maybe_unused]] Core::System& system, 1871static void KernelDebug([[maybe_unused]] Core::System& system,
1711 [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1, 1872 [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1,
1712 [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) { 1873 [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) {
@@ -1725,14 +1886,21 @@ static u64 GetSystemTick(Core::System& system) {
1725 auto& core_timing = system.CoreTiming(); 1886 auto& core_timing = system.CoreTiming();
1726 1887
1727 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) 1888 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
1728 const u64 result{Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks())}; 1889 const u64 result{system.CoreTiming().GetClockTicks()};
1729 1890
1730 // Advance time to defeat dumb games that busy-wait for the frame to end. 1891 if (!system.Kernel().IsMulticore()) {
1731 core_timing.AddTicks(400); 1892 core_timing.AddTicks(400U);
1893 }
1732 1894
1733 return result; 1895 return result;
1734} 1896}
1735 1897
1898static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) {
1899 u64 time = GetSystemTick(system);
1900 *time_low = static_cast<u32>(time);
1901 *time_high = static_cast<u32>(time >> 32);
1902}
1903
1736/// Close a handle 1904/// Close a handle
1737static ResultCode CloseHandle(Core::System& system, Handle handle) { 1905static ResultCode CloseHandle(Core::System& system, Handle handle) {
1738 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); 1906 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
@@ -1765,9 +1933,14 @@ static ResultCode ResetSignal(Core::System& system, Handle handle) {
1765 return ERR_INVALID_HANDLE; 1933 return ERR_INVALID_HANDLE;
1766} 1934}
1767 1935
1936static ResultCode ResetSignal32(Core::System& system, Handle handle) {
1937 return ResetSignal(system, handle);
1938}
1939
1768/// Creates a TransferMemory object 1940/// Creates a TransferMemory object
1769static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size, 1941static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size,
1770 u32 permissions) { 1942 u32 permissions) {
1943 std::lock_guard lock{HLE::g_hle_lock};
1771 LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, 1944 LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
1772 permissions); 1945 permissions);
1773 1946
@@ -1812,6 +1985,12 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
1812 return RESULT_SUCCESS; 1985 return RESULT_SUCCESS;
1813} 1986}
1814 1987
1988static ResultCode CreateTransferMemory32(Core::System& system, Handle* handle, u32 addr, u32 size,
1989 u32 permissions) {
1990 return CreateTransferMemory(system, handle, static_cast<VAddr>(addr),
1991 static_cast<std::size_t>(size), permissions);
1992}
1993
1815static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, 1994static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core,
1816 u64* mask) { 1995 u64* mask) {
1817 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); 1996 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
@@ -1821,6 +2000,8 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle,
1821 if (!thread) { 2000 if (!thread) {
1822 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", 2001 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
1823 thread_handle); 2002 thread_handle);
2003 *core = 0;
2004 *mask = 0;
1824 return ERR_INVALID_HANDLE; 2005 return ERR_INVALID_HANDLE;
1825 } 2006 }
1826 2007
@@ -1830,6 +2011,15 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle,
1830 return RESULT_SUCCESS; 2011 return RESULT_SUCCESS;
1831} 2012}
1832 2013
2014static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, u32* core,
2015 u32* mask_low, u32* mask_high) {
2016 u64 mask{};
2017 const auto result = GetThreadCoreMask(system, thread_handle, core, &mask);
2018 *mask_high = static_cast<u32>(mask >> 32);
2019 *mask_low = static_cast<u32>(mask);
2020 return result;
2021}
2022
1833static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, 2023static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core,
1834 u64 affinity_mask) { 2024 u64 affinity_mask) {
1835 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}", 2025 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}",
@@ -1861,7 +2051,7 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,
1861 return ERR_INVALID_COMBINATION; 2051 return ERR_INVALID_COMBINATION;
1862 } 2052 }
1863 2053
1864 if (core < Core::NUM_CPU_CORES) { 2054 if (core < Core::Hardware::NUM_CPU_CORES) {
1865 if ((affinity_mask & (1ULL << core)) == 0) { 2055 if ((affinity_mask & (1ULL << core)) == 0) {
1866 LOG_ERROR(Kernel_SVC, 2056 LOG_ERROR(Kernel_SVC,
1867 "Core is not enabled for the current mask, core={}, mask={:016X}", core, 2057 "Core is not enabled for the current mask, core={}, mask={:016X}", core,
@@ -1883,11 +2073,14 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,
1883 return ERR_INVALID_HANDLE; 2073 return ERR_INVALID_HANDLE;
1884 } 2074 }
1885 2075
1886 system.PrepareReschedule(thread->GetProcessorID()); 2076 return thread->SetCoreAndAffinityMask(core, affinity_mask);
1887 thread->ChangeCore(core, affinity_mask); 2077}
1888 system.PrepareReschedule(thread->GetProcessorID());
1889 2078
1890 return RESULT_SUCCESS; 2079static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core,
2080 u32 affinity_mask_low, u32 affinity_mask_high) {
2081 const u64 affinity_mask =
2082 static_cast<u64>(affinity_mask_low) | (static_cast<u64>(affinity_mask_high) << 32);
2083 return SetThreadCoreMask(system, thread_handle, core, affinity_mask);
1891} 2084}
1892 2085
1893static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { 2086static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) {
@@ -1918,6 +2111,10 @@ static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle
1918 return RESULT_SUCCESS; 2111 return RESULT_SUCCESS;
1919} 2112}
1920 2113
2114static ResultCode CreateEvent32(Core::System& system, Handle* write_handle, Handle* read_handle) {
2115 return CreateEvent(system, write_handle, read_handle);
2116}
2117
1921static ResultCode ClearEvent(Core::System& system, Handle handle) { 2118static ResultCode ClearEvent(Core::System& system, Handle handle) {
1922 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); 2119 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
1923 2120
@@ -1939,6 +2136,10 @@ static ResultCode ClearEvent(Core::System& system, Handle handle) {
1939 return ERR_INVALID_HANDLE; 2136 return ERR_INVALID_HANDLE;
1940} 2137}
1941 2138
2139static ResultCode ClearEvent32(Core::System& system, Handle handle) {
2140 return ClearEvent(system, handle);
2141}
2142
1942static ResultCode SignalEvent(Core::System& system, Handle handle) { 2143static ResultCode SignalEvent(Core::System& system, Handle handle) {
1943 LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); 2144 LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle);
1944 2145
@@ -1951,10 +2152,13 @@ static ResultCode SignalEvent(Core::System& system, Handle handle) {
1951 } 2152 }
1952 2153
1953 writable_event->Signal(); 2154 writable_event->Signal();
1954 system.PrepareReschedule();
1955 return RESULT_SUCCESS; 2155 return RESULT_SUCCESS;
1956} 2156}
1957 2157
2158static ResultCode SignalEvent32(Core::System& system, Handle handle) {
2159 return SignalEvent(system, handle);
2160}
2161
1958static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { 2162static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
1959 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); 2163 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
1960 2164
@@ -1982,6 +2186,7 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_
1982} 2186}
1983 2187
1984static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) { 2188static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) {
2189 std::lock_guard lock{HLE::g_hle_lock};
1985 LOG_DEBUG(Kernel_SVC, "called"); 2190 LOG_DEBUG(Kernel_SVC, "called");
1986 2191
1987 auto& kernel = system.Kernel(); 2192 auto& kernel = system.Kernel();
@@ -2139,6 +2344,15 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
2139 return RESULT_SUCCESS; 2344 return RESULT_SUCCESS;
2140} 2345}
2141 2346
2347static ResultCode FlushProcessDataCache32(Core::System& system, Handle handle, u32 address,
2348 u32 size) {
2349 // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a nope
2350 // as all emulation is done in the same cache level in host architecture, thus data cache
2351 // does not need flushing.
2352 LOG_DEBUG(Kernel_SVC, "called");
2353 return RESULT_SUCCESS;
2354}
2355
2142namespace { 2356namespace {
2143struct FunctionDef { 2357struct FunctionDef {
2144 using Func = void(Core::System&); 2358 using Func = void(Core::System&);
@@ -2153,57 +2367,57 @@ static const FunctionDef SVC_Table_32[] = {
2153 {0x00, nullptr, "Unknown"}, 2367 {0x00, nullptr, "Unknown"},
2154 {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"}, 2368 {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"},
2155 {0x02, nullptr, "Unknown"}, 2369 {0x02, nullptr, "Unknown"},
2156 {0x03, nullptr, "SetMemoryAttribute32"}, 2370 {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"},
2157 {0x04, nullptr, "MapMemory32"}, 2371 {0x04, SvcWrap32<MapMemory32>, "MapMemory32"},
2158 {0x05, nullptr, "UnmapMemory32"}, 2372 {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"},
2159 {0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"}, 2373 {0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"},
2160 {0x07, nullptr, "ExitProcess32"}, 2374 {0x07, SvcWrap32<ExitProcess32>, "ExitProcess32"},
2161 {0x08, nullptr, "CreateThread32"}, 2375 {0x08, SvcWrap32<CreateThread32>, "CreateThread32"},
2162 {0x09, nullptr, "StartThread32"}, 2376 {0x09, SvcWrap32<StartThread32>, "StartThread32"},
2163 {0x0a, nullptr, "ExitThread32"}, 2377 {0x0a, SvcWrap32<ExitThread32>, "ExitThread32"},
2164 {0x0b, nullptr, "SleepThread32"}, 2378 {0x0b, SvcWrap32<SleepThread32>, "SleepThread32"},
2165 {0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"}, 2379 {0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"},
2166 {0x0d, nullptr, "SetThreadPriority32"}, 2380 {0x0d, SvcWrap32<SetThreadPriority32>, "SetThreadPriority32"},
2167 {0x0e, nullptr, "GetThreadCoreMask32"}, 2381 {0x0e, SvcWrap32<GetThreadCoreMask32>, "GetThreadCoreMask32"},
2168 {0x0f, nullptr, "SetThreadCoreMask32"}, 2382 {0x0f, SvcWrap32<SetThreadCoreMask32>, "SetThreadCoreMask32"},
2169 {0x10, nullptr, "GetCurrentProcessorNumber32"}, 2383 {0x10, SvcWrap32<GetCurrentProcessorNumber32>, "GetCurrentProcessorNumber32"},
2170 {0x11, nullptr, "SignalEvent32"}, 2384 {0x11, SvcWrap32<SignalEvent32>, "SignalEvent32"},
2171 {0x12, nullptr, "ClearEvent32"}, 2385 {0x12, SvcWrap32<ClearEvent32>, "ClearEvent32"},
2172 {0x13, nullptr, "MapSharedMemory32"}, 2386 {0x13, SvcWrap32<MapSharedMemory32>, "MapSharedMemory32"},
2173 {0x14, nullptr, "UnmapSharedMemory32"}, 2387 {0x14, nullptr, "UnmapSharedMemory32"},
2174 {0x15, nullptr, "CreateTransferMemory32"}, 2388 {0x15, SvcWrap32<CreateTransferMemory32>, "CreateTransferMemory32"},
2175 {0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"}, 2389 {0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"},
2176 {0x17, nullptr, "ResetSignal32"}, 2390 {0x17, SvcWrap32<ResetSignal32>, "ResetSignal32"},
2177 {0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"}, 2391 {0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"},
2178 {0x19, nullptr, "CancelSynchronization32"}, 2392 {0x19, SvcWrap32<CancelSynchronization32>, "CancelSynchronization32"},
2179 {0x1a, nullptr, "ArbitrateLock32"}, 2393 {0x1a, SvcWrap32<ArbitrateLock32>, "ArbitrateLock32"},
2180 {0x1b, nullptr, "ArbitrateUnlock32"}, 2394 {0x1b, SvcWrap32<ArbitrateUnlock32>, "ArbitrateUnlock32"},
2181 {0x1c, nullptr, "WaitProcessWideKeyAtomic32"}, 2395 {0x1c, SvcWrap32<WaitProcessWideKeyAtomic32>, "WaitProcessWideKeyAtomic32"},
2182 {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"}, 2396 {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"},
2183 {0x1e, nullptr, "GetSystemTick32"}, 2397 {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"},
2184 {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"}, 2398 {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"},
2185 {0x20, nullptr, "Unknown"}, 2399 {0x20, nullptr, "Unknown"},
2186 {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"}, 2400 {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"},
2187 {0x22, nullptr, "SendSyncRequestWithUserBuffer32"}, 2401 {0x22, nullptr, "SendSyncRequestWithUserBuffer32"},
2188 {0x23, nullptr, "Unknown"}, 2402 {0x23, nullptr, "Unknown"},
2189 {0x24, nullptr, "GetProcessId32"}, 2403 {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"},
2190 {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"}, 2404 {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"},
2191 {0x26, nullptr, "Break32"}, 2405 {0x26, SvcWrap32<Break32>, "Break32"},
2192 {0x27, nullptr, "OutputDebugString32"}, 2406 {0x27, nullptr, "OutputDebugString32"},
2193 {0x28, nullptr, "Unknown"}, 2407 {0x28, nullptr, "Unknown"},
2194 {0x29, SvcWrap32<GetInfo32>, "GetInfo32"}, 2408 {0x29, SvcWrap32<GetInfo32>, "GetInfo32"},
2195 {0x2a, nullptr, "Unknown"}, 2409 {0x2a, nullptr, "Unknown"},
2196 {0x2b, nullptr, "Unknown"}, 2410 {0x2b, nullptr, "Unknown"},
2197 {0x2c, nullptr, "MapPhysicalMemory32"}, 2411 {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"},
2198 {0x2d, nullptr, "UnmapPhysicalMemory32"}, 2412 {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"},
2199 {0x2e, nullptr, "Unknown"}, 2413 {0x2e, nullptr, "Unknown"},
2200 {0x2f, nullptr, "Unknown"}, 2414 {0x2f, nullptr, "Unknown"},
2201 {0x30, nullptr, "Unknown"}, 2415 {0x30, nullptr, "Unknown"},
2202 {0x31, nullptr, "Unknown"}, 2416 {0x31, nullptr, "Unknown"},
2203 {0x32, nullptr, "SetThreadActivity32"}, 2417 {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"},
2204 {0x33, nullptr, "GetThreadContext32"}, 2418 {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"},
2205 {0x34, nullptr, "WaitForAddress32"}, 2419 {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"},
2206 {0x35, nullptr, "SignalToAddress32"}, 2420 {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"},
2207 {0x36, nullptr, "Unknown"}, 2421 {0x36, nullptr, "Unknown"},
2208 {0x37, nullptr, "Unknown"}, 2422 {0x37, nullptr, "Unknown"},
2209 {0x38, nullptr, "Unknown"}, 2423 {0x38, nullptr, "Unknown"},
@@ -2219,7 +2433,7 @@ static const FunctionDef SVC_Table_32[] = {
2219 {0x42, nullptr, "Unknown"}, 2433 {0x42, nullptr, "Unknown"},
2220 {0x43, nullptr, "ReplyAndReceive32"}, 2434 {0x43, nullptr, "ReplyAndReceive32"},
2221 {0x44, nullptr, "Unknown"}, 2435 {0x44, nullptr, "Unknown"},
2222 {0x45, nullptr, "CreateEvent32"}, 2436 {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"},
2223 {0x46, nullptr, "Unknown"}, 2437 {0x46, nullptr, "Unknown"},
2224 {0x47, nullptr, "Unknown"}, 2438 {0x47, nullptr, "Unknown"},
2225 {0x48, nullptr, "Unknown"}, 2439 {0x48, nullptr, "Unknown"},
@@ -2245,7 +2459,7 @@ static const FunctionDef SVC_Table_32[] = {
2245 {0x5c, nullptr, "Unknown"}, 2459 {0x5c, nullptr, "Unknown"},
2246 {0x5d, nullptr, "Unknown"}, 2460 {0x5d, nullptr, "Unknown"},
2247 {0x5e, nullptr, "Unknown"}, 2461 {0x5e, nullptr, "Unknown"},
2248 {0x5F, nullptr, "FlushProcessDataCache32"}, 2462 {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"},
2249 {0x60, nullptr, "Unknown"}, 2463 {0x60, nullptr, "Unknown"},
2250 {0x61, nullptr, "Unknown"}, 2464 {0x61, nullptr, "Unknown"},
2251 {0x62, nullptr, "Unknown"}, 2465 {0x62, nullptr, "Unknown"},
@@ -2423,13 +2637,10 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) {
2423 return &SVC_Table_64[func_num]; 2637 return &SVC_Table_64[func_num];
2424} 2638}
2425 2639
2426MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
2427
2428void Call(Core::System& system, u32 immediate) { 2640void Call(Core::System& system, u32 immediate) {
2429 MICROPROFILE_SCOPE(Kernel_SVC); 2641 system.ExitDynarmicProfile();
2430 2642 auto& kernel = system.Kernel();
2431 // Lock the global kernel mutex when we enter the kernel HLE. 2643 kernel.EnterSVCProfile();
2432 std::lock_guard lock{HLE::g_hle_lock};
2433 2644
2434 const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) 2645 const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
2435 : GetSVCInfo32(immediate); 2646 : GetSVCInfo32(immediate);
@@ -2442,6 +2653,9 @@ void Call(Core::System& system, u32 immediate) {
2442 } else { 2653 } else {
2443 LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); 2654 LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate);
2444 } 2655 }
2656
2657 kernel.ExitSVCProfile();
2658 system.EnterDynarmicProfile();
2445} 2659}
2446 2660
2447} // namespace Kernel::Svc 2661} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 7d735e3fa..0b6dd9df0 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -350,13 +350,50 @@ void SvcWrap64(Core::System& system) {
350 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)); 350 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2));
351} 351}
352 352
353// Used by QueryMemory32 353// Used by QueryMemory32, ArbitrateLock32
354template <ResultCode func(Core::System&, u32, u32, u32)> 354template <ResultCode func(Core::System&, u32, u32, u32)>
355void SvcWrap32(Core::System& system) { 355void SvcWrap32(Core::System& system) {
356 FuncReturn32(system, 356 FuncReturn32(system,
357 func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw); 357 func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw);
358} 358}
359 359
360// Used by Break32
361template <void func(Core::System&, u32, u32, u32)>
362void SvcWrap32(Core::System& system) {
363 func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2));
364}
365
366// Used by ExitProcess32, ExitThread32
367template <void func(Core::System&)>
368void SvcWrap32(Core::System& system) {
369 func(system);
370}
371
372// Used by GetCurrentProcessorNumber32
373template <u32 func(Core::System&)>
374void SvcWrap32(Core::System& system) {
375 FuncReturn32(system, func(system));
376}
377
378// Used by SleepThread32
379template <void func(Core::System&, u32, u32)>
380void SvcWrap32(Core::System& system) {
381 func(system, Param32(system, 0), Param32(system, 1));
382}
383
384// Used by CreateThread32
385template <ResultCode func(Core::System&, Handle*, u32, u32, u32, u32, s32)>
386void SvcWrap32(Core::System& system) {
387 Handle param_1 = 0;
388
389 const u32 retval = func(system, &param_1, Param32(system, 0), Param32(system, 1),
390 Param32(system, 2), Param32(system, 3), Param32(system, 4))
391 .raw;
392
393 system.CurrentArmInterface().SetReg(1, param_1);
394 FuncReturn(system, retval);
395}
396
360// Used by GetInfo32 397// Used by GetInfo32
361template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)> 398template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)>
362void SvcWrap32(Core::System& system) { 399void SvcWrap32(Core::System& system) {
@@ -393,18 +430,114 @@ void SvcWrap32(Core::System& system) {
393 FuncReturn(system, retval); 430 FuncReturn(system, retval);
394} 431}
395 432
433// Used by GetSystemTick32
434template <void func(Core::System&, u32*, u32*)>
435void SvcWrap32(Core::System& system) {
436 u32 param_1 = 0;
437 u32 param_2 = 0;
438
439 func(system, &param_1, &param_2);
440 system.CurrentArmInterface().SetReg(0, param_1);
441 system.CurrentArmInterface().SetReg(1, param_2);
442}
443
444// Used by CreateEvent32
445template <ResultCode func(Core::System&, Handle*, Handle*)>
446void SvcWrap32(Core::System& system) {
447 Handle param_1 = 0;
448 Handle param_2 = 0;
449
450 const u32 retval = func(system, &param_1, &param_2).raw;
451 system.CurrentArmInterface().SetReg(1, param_1);
452 system.CurrentArmInterface().SetReg(2, param_2);
453 FuncReturn(system, retval);
454}
455
456// Used by GetThreadId32
457template <ResultCode func(Core::System&, Handle, u32*, u32*, u32*)>
458void SvcWrap32(Core::System& system) {
459 u32 param_1 = 0;
460 u32 param_2 = 0;
461 u32 param_3 = 0;
462
463 const u32 retval = func(system, Param32(system, 2), &param_1, &param_2, &param_3).raw;
464 system.CurrentArmInterface().SetReg(1, param_1);
465 system.CurrentArmInterface().SetReg(2, param_2);
466 system.CurrentArmInterface().SetReg(3, param_3);
467 FuncReturn(system, retval);
468}
469
396// Used by SignalProcessWideKey32 470// Used by SignalProcessWideKey32
397template <void func(Core::System&, u32, s32)> 471template <void func(Core::System&, u32, s32)>
398void SvcWrap32(Core::System& system) { 472void SvcWrap32(Core::System& system) {
399 func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1))); 473 func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1)));
400} 474}
401 475
402// Used by SendSyncRequest32 476// Used by SetThreadPriority32
477template <ResultCode func(Core::System&, Handle, u32)>
478void SvcWrap32(Core::System& system) {
479 const u32 retval =
480 func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw;
481 FuncReturn(system, retval);
482}
483
484// Used by SetThreadCoreMask32
485template <ResultCode func(Core::System&, Handle, u32, u32, u32)>
486void SvcWrap32(Core::System& system) {
487 const u32 retval =
488 func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
489 static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
490 .raw;
491 FuncReturn(system, retval);
492}
493
494// Used by WaitProcessWideKeyAtomic32
495template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)>
496void SvcWrap32(Core::System& system) {
497 const u32 retval =
498 func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
499 static_cast<Handle>(Param(system, 2)), static_cast<u32>(Param(system, 3)),
500 static_cast<u32>(Param(system, 4)))
501 .raw;
502 FuncReturn(system, retval);
503}
504
505// Used by WaitForAddress32
506template <ResultCode func(Core::System&, u32, u32, s32, u32, u32)>
507void SvcWrap32(Core::System& system) {
508 const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
509 static_cast<u32>(Param(system, 1)), static_cast<s32>(Param(system, 2)),
510 static_cast<u32>(Param(system, 3)), static_cast<u32>(Param(system, 4)))
511 .raw;
512 FuncReturn(system, retval);
513}
514
515// Used by SignalToAddress32
516template <ResultCode func(Core::System&, u32, u32, s32, s32)>
517void SvcWrap32(Core::System& system) {
518 const u32 retval =
519 func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
520 static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
521 .raw;
522 FuncReturn(system, retval);
523}
524
525// Used by SendSyncRequest32, ArbitrateUnlock32
403template <ResultCode func(Core::System&, u32)> 526template <ResultCode func(Core::System&, u32)>
404void SvcWrap32(Core::System& system) { 527void SvcWrap32(Core::System& system) {
405 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); 528 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
406} 529}
407 530
531// Used by CreateTransferMemory32
532template <ResultCode func(Core::System&, Handle*, u32, u32, u32)>
533void SvcWrap32(Core::System& system) {
534 Handle handle = 0;
535 const u32 retval =
536 func(system, &handle, Param32(system, 1), Param32(system, 2), Param32(system, 3)).raw;
537 system.CurrentArmInterface().SetReg(1, handle);
538 FuncReturn(system, retval);
539}
540
408// Used by WaitSynchronization32 541// Used by WaitSynchronization32
409template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)> 542template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)>
410void SvcWrap32(Core::System& system) { 543void SvcWrap32(Core::System& system) {
diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp
index dc37fad1a..851b702a5 100644
--- a/src/core/hle/kernel/synchronization.cpp
+++ b/src/core/hle/kernel/synchronization.cpp
@@ -10,78 +10,107 @@
10#include "core/hle/kernel/synchronization.h" 10#include "core/hle/kernel/synchronization.h"
11#include "core/hle/kernel/synchronization_object.h" 11#include "core/hle/kernel/synchronization_object.h"
12#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
13#include "core/hle/kernel/time_manager.h"
13 14
14namespace Kernel { 15namespace Kernel {
15 16
16/// Default thread wakeup callback for WaitSynchronization
17static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
18 std::shared_ptr<SynchronizationObject> object,
19 std::size_t index) {
20 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
21
22 if (reason == ThreadWakeupReason::Timeout) {
23 thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
24 return true;
25 }
26
27 ASSERT(reason == ThreadWakeupReason::Signal);
28 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
29 thread->SetWaitSynchronizationOutput(static_cast<u32>(index));
30 return true;
31}
32
33Synchronization::Synchronization(Core::System& system) : system{system} {} 17Synchronization::Synchronization(Core::System& system) : system{system} {}
34 18
35void Synchronization::SignalObject(SynchronizationObject& obj) const { 19void Synchronization::SignalObject(SynchronizationObject& obj) const {
20 auto& kernel = system.Kernel();
21 SchedulerLock lock(kernel);
22 auto& time_manager = kernel.TimeManager();
36 if (obj.IsSignaled()) { 23 if (obj.IsSignaled()) {
37 obj.WakeupAllWaitingThreads(); 24 for (auto thread : obj.GetWaitingThreads()) {
25 if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) {
26 if (thread->GetStatus() != ThreadStatus::WaitHLEEvent) {
27 ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
28 ASSERT(thread->IsWaitingSync());
29 }
30 thread->SetSynchronizationResults(&obj, RESULT_SUCCESS);
31 thread->ResumeFromWait();
32 }
33 }
34 obj.ClearWaitingThreads();
38 } 35 }
39} 36}
40 37
41std::pair<ResultCode, Handle> Synchronization::WaitFor( 38std::pair<ResultCode, Handle> Synchronization::WaitFor(
42 std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) { 39 std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) {
40 auto& kernel = system.Kernel();
43 auto* const thread = system.CurrentScheduler().GetCurrentThread(); 41 auto* const thread = system.CurrentScheduler().GetCurrentThread();
44 // Find the first object that is acquirable in the provided list of objects 42 Handle event_handle = InvalidHandle;
45 const auto itr = std::find_if(sync_objects.begin(), sync_objects.end(), 43 {
46 [thread](const std::shared_ptr<SynchronizationObject>& object) { 44 SchedulerLockAndSleep lock(kernel, event_handle, thread, nano_seconds);
47 return object->IsSignaled(); 45 const auto itr =
48 }); 46 std::find_if(sync_objects.begin(), sync_objects.end(),
49 47 [thread](const std::shared_ptr<SynchronizationObject>& object) {
50 if (itr != sync_objects.end()) { 48 return object->IsSignaled();
51 // We found a ready object, acquire it and set the result value 49 });
52 SynchronizationObject* object = itr->get(); 50
53 object->Acquire(thread); 51 if (itr != sync_objects.end()) {
54 const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); 52 // We found a ready object, acquire it and set the result value
55 return {RESULT_SUCCESS, index}; 53 SynchronizationObject* object = itr->get();
54 object->Acquire(thread);
55 const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr));
56 lock.CancelSleep();
57 return {RESULT_SUCCESS, index};
58 }
59
60 if (nano_seconds == 0) {
61 lock.CancelSleep();
62 return {RESULT_TIMEOUT, InvalidHandle};
63 }
64
65 if (thread->IsPendingTermination()) {
66 lock.CancelSleep();
67 return {ERR_THREAD_TERMINATING, InvalidHandle};
68 }
69
70 if (thread->IsSyncCancelled()) {
71 thread->SetSyncCancelled(false);
72 lock.CancelSleep();
73 return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle};
74 }
75
76 for (auto& object : sync_objects) {
77 object->AddWaitingThread(SharedFrom(thread));
78 }
79
80 thread->SetSynchronizationObjects(&sync_objects);
81 thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
82 thread->SetStatus(ThreadStatus::WaitSynch);
83 thread->SetWaitingSync(true);
56 } 84 }
85 thread->SetWaitingSync(false);
57 86
58 // No objects were ready to be acquired, prepare to suspend the thread. 87 if (event_handle != InvalidHandle) {
59 88 auto& time_manager = kernel.TimeManager();
60 // If a timeout value of 0 was provided, just return the Timeout error code instead of 89 time_manager.UnscheduleTimeEvent(event_handle);
61 // suspending the thread.
62 if (nano_seconds == 0) {
63 return {RESULT_TIMEOUT, InvalidHandle};
64 } 90 }
65 91
66 if (thread->IsSyncCancelled()) { 92 {
67 thread->SetSyncCancelled(false); 93 SchedulerLock lock(kernel);
68 return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; 94 ResultCode signaling_result = thread->GetSignalingResult();
95 SynchronizationObject* signaling_object = thread->GetSignalingObject();
96 thread->SetSynchronizationObjects(nullptr);
97 auto shared_thread = SharedFrom(thread);
98 for (auto& obj : sync_objects) {
99 obj->RemoveWaitingThread(shared_thread);
100 }
101 if (signaling_object != nullptr) {
102 const auto itr = std::find_if(
103 sync_objects.begin(), sync_objects.end(),
104 [signaling_object](const std::shared_ptr<SynchronizationObject>& object) {
105 return object.get() == signaling_object;
106 });
107 ASSERT(itr != sync_objects.end());
108 signaling_object->Acquire(thread);
109 const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr));
110 return {signaling_result, index};
111 }
112 return {signaling_result, -1};
69 } 113 }
70
71 for (auto& object : sync_objects) {
72 object->AddWaitingThread(SharedFrom(thread));
73 }
74
75 thread->SetSynchronizationObjects(std::move(sync_objects));
76 thread->SetStatus(ThreadStatus::WaitSynch);
77
78 // Create an event to wake the thread up after the specified nanosecond delay has passed
79 thread->WakeAfterDelay(nano_seconds);
80 thread->SetWakeupCallback(DefaultThreadWakeupCallback);
81
82 system.PrepareReschedule(thread->GetProcessorID());
83
84 return {RESULT_TIMEOUT, InvalidHandle};
85} 114}
86 115
87} // namespace Kernel 116} // namespace Kernel
diff --git a/src/core/hle/kernel/synchronization_object.cpp b/src/core/hle/kernel/synchronization_object.cpp
index 43f3eef18..ba4d39157 100644
--- a/src/core/hle/kernel/synchronization_object.cpp
+++ b/src/core/hle/kernel/synchronization_object.cpp
@@ -38,68 +38,8 @@ void SynchronizationObject::RemoveWaitingThread(std::shared_ptr<Thread> thread)
38 waiting_threads.erase(itr); 38 waiting_threads.erase(itr);
39} 39}
40 40
41std::shared_ptr<Thread> SynchronizationObject::GetHighestPriorityReadyThread() const { 41void SynchronizationObject::ClearWaitingThreads() {
42 Thread* candidate = nullptr; 42 waiting_threads.clear();
43 u32 candidate_priority = THREADPRIO_LOWEST + 1;
44
45 for (const auto& thread : waiting_threads) {
46 const ThreadStatus thread_status = thread->GetStatus();
47
48 // The list of waiting threads must not contain threads that are not waiting to be awakened.
49 ASSERT_MSG(thread_status == ThreadStatus::WaitSynch ||
50 thread_status == ThreadStatus::WaitHLEEvent,
51 "Inconsistent thread statuses in waiting_threads");
52
53 if (thread->GetPriority() >= candidate_priority)
54 continue;
55
56 if (ShouldWait(thread.get()))
57 continue;
58
59 candidate = thread.get();
60 candidate_priority = thread->GetPriority();
61 }
62
63 return SharedFrom(candidate);
64}
65
66void SynchronizationObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) {
67 ASSERT(!ShouldWait(thread.get()));
68
69 if (!thread) {
70 return;
71 }
72
73 if (thread->IsSleepingOnWait()) {
74 for (const auto& object : thread->GetSynchronizationObjects()) {
75 ASSERT(!object->ShouldWait(thread.get()));
76 object->Acquire(thread.get());
77 }
78 } else {
79 Acquire(thread.get());
80 }
81
82 const std::size_t index = thread->GetSynchronizationObjectIndex(SharedFrom(this));
83
84 thread->ClearSynchronizationObjects();
85
86 thread->CancelWakeupTimer();
87
88 bool resume = true;
89 if (thread->HasWakeupCallback()) {
90 resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, SharedFrom(this),
91 index);
92 }
93 if (resume) {
94 thread->ResumeFromWait();
95 kernel.PrepareReschedule(thread->GetProcessorID());
96 }
97}
98
99void SynchronizationObject::WakeupAllWaitingThreads() {
100 while (auto thread = GetHighestPriorityReadyThread()) {
101 WakeupWaitingThread(thread);
102 }
103} 43}
104 44
105const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const { 45const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const {
diff --git a/src/core/hle/kernel/synchronization_object.h b/src/core/hle/kernel/synchronization_object.h
index 741c31faf..f89b24204 100644
--- a/src/core/hle/kernel/synchronization_object.h
+++ b/src/core/hle/kernel/synchronization_object.h
@@ -12,6 +12,7 @@
12namespace Kernel { 12namespace Kernel {
13 13
14class KernelCore; 14class KernelCore;
15class Synchronization;
15class Thread; 16class Thread;
16 17
17/// Class that represents a Kernel object that a thread can be waiting on 18/// Class that represents a Kernel object that a thread can be waiting on
@@ -49,24 +50,11 @@ public:
49 */ 50 */
50 void RemoveWaitingThread(std::shared_ptr<Thread> thread); 51 void RemoveWaitingThread(std::shared_ptr<Thread> thread);
51 52
52 /**
53 * Wake up all threads waiting on this object that can be awoken, in priority order,
54 * and set the synchronization result and output of the thread.
55 */
56 void WakeupAllWaitingThreads();
57
58 /**
59 * Wakes up a single thread waiting on this object.
60 * @param thread Thread that is waiting on this object to wakeup.
61 */
62 void WakeupWaitingThread(std::shared_ptr<Thread> thread);
63
64 /// Obtains the highest priority thread that is ready to run from this object's waiting list.
65 std::shared_ptr<Thread> GetHighestPriorityReadyThread() const;
66
67 /// Get a const reference to the waiting threads list for debug use 53 /// Get a const reference to the waiting threads list for debug use
68 const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; 54 const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const;
69 55
56 void ClearWaitingThreads();
57
70protected: 58protected:
71 bool is_signaled{}; // Tells if this sync object is signalled; 59 bool is_signaled{}; // Tells if this sync object is signalled;
72 60
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index db7f379ac..2b1092697 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -9,12 +9,21 @@
9 9
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/fiber.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/thread_queue_list.h" 14#include "common/thread_queue_list.h"
14#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
16#ifdef ARCHITECTURE_x86_64
17#include "core/arm/dynarmic/arm_dynarmic_32.h"
18#include "core/arm/dynarmic/arm_dynarmic_64.h"
19#endif
20#include "core/arm/cpu_interrupt_handler.h"
21#include "core/arm/exclusive_monitor.h"
22#include "core/arm/unicorn/arm_unicorn.h"
15#include "core/core.h" 23#include "core/core.h"
16#include "core/core_timing.h" 24#include "core/core_timing.h"
17#include "core/core_timing_util.h" 25#include "core/core_timing_util.h"
26#include "core/cpu_manager.h"
18#include "core/hardware_properties.h" 27#include "core/hardware_properties.h"
19#include "core/hle/kernel/errors.h" 28#include "core/hle/kernel/errors.h"
20#include "core/hle/kernel/handle_table.h" 29#include "core/hle/kernel/handle_table.h"
@@ -23,6 +32,7 @@
23#include "core/hle/kernel/process.h" 32#include "core/hle/kernel/process.h"
24#include "core/hle/kernel/scheduler.h" 33#include "core/hle/kernel/scheduler.h"
25#include "core/hle/kernel/thread.h" 34#include "core/hle/kernel/thread.h"
35#include "core/hle/kernel/time_manager.h"
26#include "core/hle/result.h" 36#include "core/hle/result.h"
27#include "core/memory.h" 37#include "core/memory.h"
28 38
@@ -44,46 +54,26 @@ Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {}
44Thread::~Thread() = default; 54Thread::~Thread() = default;
45 55
46void Thread::Stop() { 56void Thread::Stop() {
47 // Cancel any outstanding wakeup events for this thread 57 {
48 Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), 58 SchedulerLock lock(kernel);
49 global_handle); 59 SetStatus(ThreadStatus::Dead);
50 kernel.GlobalHandleTable().Close(global_handle); 60 Signal();
51 global_handle = 0; 61 kernel.GlobalHandleTable().Close(global_handle);
52 SetStatus(ThreadStatus::Dead);
53 Signal();
54
55 // Clean up any dangling references in objects that this thread was waiting for
56 for (auto& wait_object : wait_objects) {
57 wait_object->RemoveWaitingThread(SharedFrom(this));
58 }
59 wait_objects.clear();
60
61 owner_process->UnregisterThread(this);
62
63 // Mark the TLS slot in the thread's page as free.
64 owner_process->FreeTLSRegion(tls_address);
65}
66
67void Thread::WakeAfterDelay(s64 nanoseconds) {
68 // Don't schedule a wakeup if the thread wants to wait forever
69 if (nanoseconds == -1)
70 return;
71 62
72 // This function might be called from any thread so we have to be cautious and use the 63 if (owner_process) {
73 // thread-safe version of ScheduleEvent. 64 owner_process->UnregisterThread(this);
74 const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
75 Core::System::GetInstance().CoreTiming().ScheduleEvent(
76 cycles, kernel.ThreadWakeupCallbackEventType(), global_handle);
77}
78 65
79void Thread::CancelWakeupTimer() { 66 // Mark the TLS slot in the thread's page as free.
80 Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), 67 owner_process->FreeTLSRegion(tls_address);
81 global_handle); 68 }
69 arm_interface.reset();
70 has_exited = true;
71 }
72 global_handle = 0;
82} 73}
83 74
84void Thread::ResumeFromWait() { 75void Thread::ResumeFromWait() {
85 ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); 76 SchedulerLock lock(kernel);
86
87 switch (status) { 77 switch (status) {
88 case ThreadStatus::Paused: 78 case ThreadStatus::Paused:
89 case ThreadStatus::WaitSynch: 79 case ThreadStatus::WaitSynch:
@@ -99,7 +89,7 @@ void Thread::ResumeFromWait() {
99 case ThreadStatus::Ready: 89 case ThreadStatus::Ready:
100 // The thread's wakeup callback must have already been cleared when the thread was first 90 // The thread's wakeup callback must have already been cleared when the thread was first
101 // awoken. 91 // awoken.
102 ASSERT(wakeup_callback == nullptr); 92 ASSERT(hle_callback == nullptr);
103 // If the thread is waiting on multiple wait objects, it might be awoken more than once 93 // If the thread is waiting on multiple wait objects, it might be awoken more than once
104 // before actually resuming. We can ignore subsequent wakeups if the thread status has 94 // before actually resuming. We can ignore subsequent wakeups if the thread status has
105 // already been set to ThreadStatus::Ready. 95 // already been set to ThreadStatus::Ready.
@@ -115,24 +105,31 @@ void Thread::ResumeFromWait() {
115 return; 105 return;
116 } 106 }
117 107
118 wakeup_callback = nullptr; 108 SetStatus(ThreadStatus::Ready);
109}
110
111void Thread::OnWakeUp() {
112 SchedulerLock lock(kernel);
119 113
120 if (activity == ThreadActivity::Paused) { 114 SetStatus(ThreadStatus::Ready);
121 SetStatus(ThreadStatus::Paused); 115}
122 return;
123 }
124 116
117ResultCode Thread::Start() {
118 SchedulerLock lock(kernel);
125 SetStatus(ThreadStatus::Ready); 119 SetStatus(ThreadStatus::Ready);
120 return RESULT_SUCCESS;
126} 121}
127 122
128void Thread::CancelWait() { 123void Thread::CancelWait() {
129 if (GetSchedulingStatus() != ThreadSchedStatus::Paused) { 124 SchedulerLock lock(kernel);
125 if (GetSchedulingStatus() != ThreadSchedStatus::Paused || !is_waiting_on_sync) {
130 is_sync_cancelled = true; 126 is_sync_cancelled = true;
131 return; 127 return;
132 } 128 }
129 // TODO(Blinkhawk): Implement cancel of server session
133 is_sync_cancelled = false; 130 is_sync_cancelled = false;
134 SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED); 131 SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED);
135 ResumeFromWait(); 132 SetStatus(ThreadStatus::Ready);
136} 133}
137 134
138static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, 135static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top,
@@ -153,12 +150,29 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
153 context.fpcr = 0; 150 context.fpcr = 0;
154} 151}
155 152
156ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::string name, 153std::shared_ptr<Common::Fiber>& Thread::GetHostContext() {
157 VAddr entry_point, u32 priority, u64 arg, 154 return host_context;
158 s32 processor_id, VAddr stack_top, 155}
159 Process& owner_process) { 156
157ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags,
158 std::string name, VAddr entry_point, u32 priority,
159 u64 arg, s32 processor_id, VAddr stack_top,
160 Process* owner_process) {
161 std::function<void(void*)> init_func = system.GetCpuManager().GetGuestThreadStartFunc();
162 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
163 return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
164 owner_process, std::move(init_func), init_func_parameter);
165}
166
167ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags,
168 std::string name, VAddr entry_point, u32 priority,
169 u64 arg, s32 processor_id, VAddr stack_top,
170 Process* owner_process,
171 std::function<void(void*)>&& thread_start_func,
172 void* thread_start_parameter) {
173 auto& kernel = system.Kernel();
160 // Check if priority is in ranged. Lowest priority -> highest priority id. 174 // Check if priority is in ranged. Lowest priority -> highest priority id.
161 if (priority > THREADPRIO_LOWEST) { 175 if (priority > THREADPRIO_LOWEST && ((type_flags & THREADTYPE_IDLE) == 0)) {
162 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); 176 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
163 return ERR_INVALID_THREAD_PRIORITY; 177 return ERR_INVALID_THREAD_PRIORITY;
164 } 178 }
@@ -168,11 +182,12 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
168 return ERR_INVALID_PROCESSOR_ID; 182 return ERR_INVALID_PROCESSOR_ID;
169 } 183 }
170 184
171 auto& system = Core::System::GetInstance(); 185 if (owner_process) {
172 if (!system.Memory().IsValidVirtualAddress(owner_process, entry_point)) { 186 if (!system.Memory().IsValidVirtualAddress(*owner_process, entry_point)) {
173 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); 187 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
174 // TODO (bunnei): Find the correct error code to use here 188 // TODO (bunnei): Find the correct error code to use here
175 return RESULT_UNKNOWN; 189 return RESULT_UNKNOWN;
190 }
176 } 191 }
177 192
178 std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); 193 std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel);
@@ -183,51 +198,82 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
183 thread->stack_top = stack_top; 198 thread->stack_top = stack_top;
184 thread->tpidr_el0 = 0; 199 thread->tpidr_el0 = 0;
185 thread->nominal_priority = thread->current_priority = priority; 200 thread->nominal_priority = thread->current_priority = priority;
186 thread->last_running_ticks = system.CoreTiming().GetTicks(); 201 thread->last_running_ticks = 0;
187 thread->processor_id = processor_id; 202 thread->processor_id = processor_id;
188 thread->ideal_core = processor_id; 203 thread->ideal_core = processor_id;
189 thread->affinity_mask = 1ULL << processor_id; 204 thread->affinity_mask = 1ULL << processor_id;
190 thread->wait_objects.clear(); 205 thread->wait_objects = nullptr;
191 thread->mutex_wait_address = 0; 206 thread->mutex_wait_address = 0;
192 thread->condvar_wait_address = 0; 207 thread->condvar_wait_address = 0;
193 thread->wait_handle = 0; 208 thread->wait_handle = 0;
194 thread->name = std::move(name); 209 thread->name = std::move(name);
195 thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); 210 thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap();
196 thread->owner_process = &owner_process; 211 thread->owner_process = owner_process;
197 auto& scheduler = kernel.GlobalScheduler(); 212 thread->type = type_flags;
198 scheduler.AddThread(thread); 213 if ((type_flags & THREADTYPE_IDLE) == 0) {
199 thread->tls_address = thread->owner_process->CreateTLSRegion(); 214 auto& scheduler = kernel.GlobalScheduler();
200 215 scheduler.AddThread(thread);
201 thread->owner_process->RegisterThread(thread.get()); 216 }
217 if (owner_process) {
218 thread->tls_address = thread->owner_process->CreateTLSRegion();
219 thread->owner_process->RegisterThread(thread.get());
220 } else {
221 thread->tls_address = 0;
222 }
223 // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
224 // to initialize the context
225 thread->arm_interface.reset();
226 if ((type_flags & THREADTYPE_HLE) == 0) {
227#ifdef ARCHITECTURE_x86_64
228 if (owner_process && !owner_process->Is64BitProcess()) {
229 thread->arm_interface = std::make_unique<Core::ARM_Dynarmic_32>(
230 system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(),
231 processor_id);
232 } else {
233 thread->arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
234 system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(),
235 processor_id);
236 }
202 237
203 ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top), 238#else
204 static_cast<u32>(entry_point), static_cast<u32>(arg)); 239 if (owner_process && !owner_process->Is64BitProcess()) {
205 ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); 240 thread->arm_interface = std::make_shared<Core::ARM_Unicorn>(
241 system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch32,
242 processor_id);
243 } else {
244 thread->arm_interface = std::make_shared<Core::ARM_Unicorn>(
245 system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch64,
246 processor_id);
247 }
248 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
249#endif
250 ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top),
251 static_cast<u32>(entry_point), static_cast<u32>(arg));
252 ResetThreadContext64(thread->context_64, stack_top, entry_point, arg);
253 }
254 thread->host_context =
255 std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
206 256
207 return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); 257 return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
208} 258}
209 259
210void Thread::SetPriority(u32 priority) { 260void Thread::SetPriority(u32 priority) {
261 SchedulerLock lock(kernel);
211 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, 262 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
212 "Invalid priority value."); 263 "Invalid priority value.");
213 nominal_priority = priority; 264 nominal_priority = priority;
214 UpdatePriority(); 265 UpdatePriority();
215} 266}
216 267
217void Thread::SetWaitSynchronizationResult(ResultCode result) { 268void Thread::SetSynchronizationResults(SynchronizationObject* object, ResultCode result) {
218 context_32.cpu_registers[0] = result.raw; 269 signaling_object = object;
219 context_64.cpu_registers[0] = result.raw; 270 signaling_result = result;
220}
221
222void Thread::SetWaitSynchronizationOutput(s32 output) {
223 context_32.cpu_registers[1] = output;
224 context_64.cpu_registers[1] = output;
225} 271}
226 272
227s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const { 273s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const {
228 ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); 274 ASSERT_MSG(!wait_objects->empty(), "Thread is not waiting for anything");
229 const auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); 275 const auto match = std::find(wait_objects->rbegin(), wait_objects->rend(), object);
230 return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1); 276 return static_cast<s32>(std::distance(match, wait_objects->rend()) - 1);
231} 277}
232 278
233VAddr Thread::GetCommandBufferAddress() const { 279VAddr Thread::GetCommandBufferAddress() const {
@@ -236,6 +282,14 @@ VAddr Thread::GetCommandBufferAddress() const {
236 return GetTLSAddress() + command_header_offset; 282 return GetTLSAddress() + command_header_offset;
237} 283}
238 284
285Core::ARM_Interface& Thread::ArmInterface() {
286 return *arm_interface;
287}
288
289const Core::ARM_Interface& Thread::ArmInterface() const {
290 return *arm_interface;
291}
292
239void Thread::SetStatus(ThreadStatus new_status) { 293void Thread::SetStatus(ThreadStatus new_status) {
240 if (new_status == status) { 294 if (new_status == status) {
241 return; 295 return;
@@ -257,10 +311,6 @@ void Thread::SetStatus(ThreadStatus new_status) {
257 break; 311 break;
258 } 312 }
259 313
260 if (status == ThreadStatus::Running) {
261 last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
262 }
263
264 status = new_status; 314 status = new_status;
265} 315}
266 316
@@ -341,75 +391,116 @@ void Thread::UpdatePriority() {
341 lock_owner->UpdatePriority(); 391 lock_owner->UpdatePriority();
342} 392}
343 393
344void Thread::ChangeCore(u32 core, u64 mask) {
345 SetCoreAndAffinityMask(core, mask);
346}
347
348bool Thread::AllSynchronizationObjectsReady() const { 394bool Thread::AllSynchronizationObjectsReady() const {
349 return std::none_of(wait_objects.begin(), wait_objects.end(), 395 return std::none_of(wait_objects->begin(), wait_objects->end(),
350 [this](const std::shared_ptr<SynchronizationObject>& object) { 396 [this](const std::shared_ptr<SynchronizationObject>& object) {
351 return object->ShouldWait(this); 397 return object->ShouldWait(this);
352 }); 398 });
353} 399}
354 400
355bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, 401bool Thread::InvokeHLECallback(std::shared_ptr<Thread> thread) {
356 std::shared_ptr<SynchronizationObject> object, 402 ASSERT(hle_callback);
357 std::size_t index) { 403 return hle_callback(std::move(thread));
358 ASSERT(wakeup_callback);
359 return wakeup_callback(reason, std::move(thread), std::move(object), index);
360} 404}
361 405
362void Thread::SetActivity(ThreadActivity value) { 406ResultCode Thread::SetActivity(ThreadActivity value) {
363 activity = value; 407 SchedulerLock lock(kernel);
408
409 auto sched_status = GetSchedulingStatus();
410
411 if (sched_status != ThreadSchedStatus::Runnable && sched_status != ThreadSchedStatus::Paused) {
412 return ERR_INVALID_STATE;
413 }
414
415 if (IsPendingTermination()) {
416 return RESULT_SUCCESS;
417 }
364 418
365 if (value == ThreadActivity::Paused) { 419 if (value == ThreadActivity::Paused) {
366 // Set status if not waiting 420 if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) != 0) {
367 if (status == ThreadStatus::Ready || status == ThreadStatus::Running) { 421 return ERR_INVALID_STATE;
368 SetStatus(ThreadStatus::Paused); 422 }
369 kernel.PrepareReschedule(processor_id); 423 AddSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag);
424 } else {
425 if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) == 0) {
426 return ERR_INVALID_STATE;
370 } 427 }
371 } else if (status == ThreadStatus::Paused) { 428 RemoveSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag);
372 // Ready to reschedule
373 ResumeFromWait();
374 } 429 }
430 return RESULT_SUCCESS;
375} 431}
376 432
377void Thread::Sleep(s64 nanoseconds) { 433ResultCode Thread::Sleep(s64 nanoseconds) {
378 // Sleep current thread and check for next thread to schedule 434 Handle event_handle{};
379 SetStatus(ThreadStatus::WaitSleep); 435 {
436 SchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds);
437 SetStatus(ThreadStatus::WaitSleep);
438 }
380 439
381 // Create an event to wake the thread up after the specified nanosecond delay has passed 440 if (event_handle != InvalidHandle) {
382 WakeAfterDelay(nanoseconds); 441 auto& time_manager = kernel.TimeManager();
442 time_manager.UnscheduleTimeEvent(event_handle);
443 }
444 return RESULT_SUCCESS;
445}
446
447std::pair<ResultCode, bool> Thread::YieldSimple() {
448 bool is_redundant = false;
449 {
450 SchedulerLock lock(kernel);
451 is_redundant = kernel.GlobalScheduler().YieldThread(this);
452 }
453 return {RESULT_SUCCESS, is_redundant};
454}
455
456std::pair<ResultCode, bool> Thread::YieldAndBalanceLoad() {
457 bool is_redundant = false;
458 {
459 SchedulerLock lock(kernel);
460 is_redundant = kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this);
461 }
462 return {RESULT_SUCCESS, is_redundant};
383} 463}
384 464
385bool Thread::YieldSimple() { 465std::pair<ResultCode, bool> Thread::YieldAndWaitForLoadBalancing() {
386 auto& scheduler = kernel.GlobalScheduler(); 466 bool is_redundant = false;
387 return scheduler.YieldThread(this); 467 {
468 SchedulerLock lock(kernel);
469 is_redundant = kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this);
470 }
471 return {RESULT_SUCCESS, is_redundant};
388} 472}
389 473
390bool Thread::YieldAndBalanceLoad() { 474void Thread::AddSchedulingFlag(ThreadSchedFlags flag) {
391 auto& scheduler = kernel.GlobalScheduler(); 475 const u32 old_state = scheduling_state;
392 return scheduler.YieldThreadAndBalanceLoad(this); 476 pausing_state |= static_cast<u32>(flag);
477 const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus());
478 scheduling_state = base_scheduling | pausing_state;
479 kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state);
393} 480}
394 481
395bool Thread::YieldAndWaitForLoadBalancing() { 482void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) {
396 auto& scheduler = kernel.GlobalScheduler(); 483 const u32 old_state = scheduling_state;
397 return scheduler.YieldThreadAndWaitForLoadBalancing(this); 484 pausing_state &= ~static_cast<u32>(flag);
485 const u32 base_scheduling = static_cast<u32>(GetSchedulingStatus());
486 scheduling_state = base_scheduling | pausing_state;
487 kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state);
398} 488}
399 489
400void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { 490void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) {
401 const u32 old_flags = scheduling_state; 491 const u32 old_state = scheduling_state;
402 scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) | 492 scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) |
403 static_cast<u32>(new_status); 493 static_cast<u32>(new_status);
404 AdjustSchedulingOnStatus(old_flags); 494 kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_state);
405} 495}
406 496
407void Thread::SetCurrentPriority(u32 new_priority) { 497void Thread::SetCurrentPriority(u32 new_priority) {
408 const u32 old_priority = std::exchange(current_priority, new_priority); 498 const u32 old_priority = std::exchange(current_priority, new_priority);
409 AdjustSchedulingOnPriority(old_priority); 499 kernel.GlobalScheduler().AdjustSchedulingOnPriority(this, old_priority);
410} 500}
411 501
412ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { 502ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
503 SchedulerLock lock(kernel);
413 const auto HighestSetCore = [](u64 mask, u32 max_cores) { 504 const auto HighestSetCore = [](u64 mask, u32 max_cores) {
414 for (s32 core = static_cast<s32>(max_cores - 1); core >= 0; core--) { 505 for (s32 core = static_cast<s32>(max_cores - 1); core >= 0; core--) {
415 if (((mask >> core) & 1) != 0) { 506 if (((mask >> core) & 1) != 0) {
@@ -443,111 +534,12 @@ ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
443 processor_id = ideal_core; 534 processor_id = ideal_core;
444 } 535 }
445 } 536 }
446 AdjustSchedulingOnAffinity(old_affinity_mask, old_core); 537 kernel.GlobalScheduler().AdjustSchedulingOnAffinity(this, old_affinity_mask, old_core);
447 } 538 }
448 } 539 }
449 return RESULT_SUCCESS; 540 return RESULT_SUCCESS;
450} 541}
451 542
452void Thread::AdjustSchedulingOnStatus(u32 old_flags) {
453 if (old_flags == scheduling_state) {
454 return;
455 }
456
457 auto& scheduler = kernel.GlobalScheduler();
458 if (static_cast<ThreadSchedStatus>(old_flags & static_cast<u32>(ThreadSchedMasks::LowMask)) ==
459 ThreadSchedStatus::Runnable) {
460 // In this case the thread was running, now it's pausing/exitting
461 if (processor_id >= 0) {
462 scheduler.Unschedule(current_priority, static_cast<u32>(processor_id), this);
463 }
464
465 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
466 if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) {
467 scheduler.Unsuggest(current_priority, core, this);
468 }
469 }
470 } else if (GetSchedulingStatus() == ThreadSchedStatus::Runnable) {
471 // The thread is now set to running from being stopped
472 if (processor_id >= 0) {
473 scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this);
474 }
475
476 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
477 if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) {
478 scheduler.Suggest(current_priority, core, this);
479 }
480 }
481 }
482
483 scheduler.SetReselectionPending();
484}
485
486void Thread::AdjustSchedulingOnPriority(u32 old_priority) {
487 if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) {
488 return;
489 }
490 auto& scheduler = kernel.GlobalScheduler();
491 if (processor_id >= 0) {
492 scheduler.Unschedule(old_priority, static_cast<u32>(processor_id), this);
493 }
494
495 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
496 if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) {
497 scheduler.Unsuggest(old_priority, core, this);
498 }
499 }
500
501 // Add thread to the new priority queues.
502 Thread* current_thread = GetCurrentThread();
503
504 if (processor_id >= 0) {
505 if (current_thread == this) {
506 scheduler.SchedulePrepend(current_priority, static_cast<u32>(processor_id), this);
507 } else {
508 scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this);
509 }
510 }
511
512 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
513 if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) {
514 scheduler.Suggest(current_priority, core, this);
515 }
516 }
517
518 scheduler.SetReselectionPending();
519}
520
521void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) {
522 auto& scheduler = kernel.GlobalScheduler();
523 if (GetSchedulingStatus() != ThreadSchedStatus::Runnable ||
524 current_priority >= THREADPRIO_COUNT) {
525 return;
526 }
527
528 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
529 if (((old_affinity_mask >> core) & 1) != 0) {
530 if (core == static_cast<u32>(old_core)) {
531 scheduler.Unschedule(current_priority, core, this);
532 } else {
533 scheduler.Unsuggest(current_priority, core, this);
534 }
535 }
536 }
537
538 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
539 if (((affinity_mask >> core) & 1) != 0) {
540 if (core == static_cast<u32>(processor_id)) {
541 scheduler.Schedule(current_priority, core, this);
542 } else {
543 scheduler.Suggest(current_priority, core, this);
544 }
545 }
546 }
547
548 scheduler.SetReselectionPending();
549}
550
551//////////////////////////////////////////////////////////////////////////////////////////////////// 543////////////////////////////////////////////////////////////////////////////////////////////////////
552 544
553/** 545/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 23fdef8a4..c0342c462 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -6,26 +6,47 @@
6 6
7#include <functional> 7#include <functional>
8#include <string> 8#include <string>
9#include <utility>
9#include <vector> 10#include <vector>
10 11
11#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/spin_lock.h"
12#include "core/arm/arm_interface.h" 14#include "core/arm/arm_interface.h"
13#include "core/hle/kernel/object.h" 15#include "core/hle/kernel/object.h"
14#include "core/hle/kernel/synchronization_object.h" 16#include "core/hle/kernel/synchronization_object.h"
15#include "core/hle/result.h" 17#include "core/hle/result.h"
16 18
19namespace Common {
20class Fiber;
21}
22
23namespace Core {
24class ARM_Interface;
25class System;
26} // namespace Core
27
17namespace Kernel { 28namespace Kernel {
18 29
30class GlobalScheduler;
19class KernelCore; 31class KernelCore;
20class Process; 32class Process;
21class Scheduler; 33class Scheduler;
22 34
23enum ThreadPriority : u32 { 35enum ThreadPriority : u32 {
24 THREADPRIO_HIGHEST = 0, ///< Highest thread priority 36 THREADPRIO_HIGHEST = 0, ///< Highest thread priority
25 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps 37 THREADPRIO_MAX_CORE_MIGRATION = 2, ///< Highest priority for a core migration
26 THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps 38 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
27 THREADPRIO_LOWEST = 63, ///< Lowest thread priority 39 THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
28 THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. 40 THREADPRIO_LOWEST = 63, ///< Lowest thread priority
41 THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities.
42};
43
44enum ThreadType : u32 {
45 THREADTYPE_USER = 0x1,
46 THREADTYPE_KERNEL = 0x2,
47 THREADTYPE_HLE = 0x4,
48 THREADTYPE_IDLE = 0x8,
49 THREADTYPE_SUSPEND = 0x10,
29}; 50};
30 51
31enum ThreadProcessorId : s32 { 52enum ThreadProcessorId : s32 {
@@ -107,26 +128,45 @@ public:
107 128
108 using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>; 129 using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>;
109 130
110 using WakeupCallback = 131 using HLECallback = std::function<bool(std::shared_ptr<Thread> thread)>;
111 std::function<bool(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, 132
112 std::shared_ptr<SynchronizationObject> object, std::size_t index)>; 133 /**
134 * Creates and returns a new thread. The new thread is immediately scheduled
135 * @param system The instance of the whole system
136 * @param name The friendly name desired for the thread
137 * @param entry_point The address at which the thread should start execution
138 * @param priority The thread's priority
139 * @param arg User data to pass to the thread
140 * @param processor_id The ID(s) of the processors on which the thread is desired to be run
141 * @param stack_top The address of the thread's stack top
142 * @param owner_process The parent process for the thread, if null, it's a kernel thread
143 * @return A shared pointer to the newly created thread
144 */
145 static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags,
146 std::string name, VAddr entry_point,
147 u32 priority, u64 arg, s32 processor_id,
148 VAddr stack_top, Process* owner_process);
113 149
114 /** 150 /**
115 * Creates and returns a new thread. The new thread is immediately scheduled 151 * Creates and returns a new thread. The new thread is immediately scheduled
116 * @param kernel The kernel instance this thread will be created under. 152 * @param system The instance of the whole system
117 * @param name The friendly name desired for the thread 153 * @param name The friendly name desired for the thread
118 * @param entry_point The address at which the thread should start execution 154 * @param entry_point The address at which the thread should start execution
119 * @param priority The thread's priority 155 * @param priority The thread's priority
120 * @param arg User data to pass to the thread 156 * @param arg User data to pass to the thread
121 * @param processor_id The ID(s) of the processors on which the thread is desired to be run 157 * @param processor_id The ID(s) of the processors on which the thread is desired to be run
122 * @param stack_top The address of the thread's stack top 158 * @param stack_top The address of the thread's stack top
123 * @param owner_process The parent process for the thread 159 * @param owner_process The parent process for the thread, if null, it's a kernel thread
160 * @param thread_start_func The function where the host context will start.
161 * @param thread_start_parameter The parameter which will passed to host context on init
124 * @return A shared pointer to the newly created thread 162 * @return A shared pointer to the newly created thread
125 */ 163 */
126 static ResultVal<std::shared_ptr<Thread>> Create(KernelCore& kernel, std::string name, 164 static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags,
127 VAddr entry_point, u32 priority, u64 arg, 165 std::string name, VAddr entry_point,
128 s32 processor_id, VAddr stack_top, 166 u32 priority, u64 arg, s32 processor_id,
129 Process& owner_process); 167 VAddr stack_top, Process* owner_process,
168 std::function<void(void*)>&& thread_start_func,
169 void* thread_start_parameter);
130 170
131 std::string GetName() const override { 171 std::string GetName() const override {
132 return name; 172 return name;
@@ -181,7 +221,7 @@ public:
181 void UpdatePriority(); 221 void UpdatePriority();
182 222
183 /// Changes the core that the thread is running or scheduled to run on. 223 /// Changes the core that the thread is running or scheduled to run on.
184 void ChangeCore(u32 core, u64 mask); 224 ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
185 225
186 /** 226 /**
187 * Gets the thread's thread ID 227 * Gets the thread's thread ID
@@ -194,6 +234,10 @@ public:
194 /// Resumes a thread from waiting 234 /// Resumes a thread from waiting
195 void ResumeFromWait(); 235 void ResumeFromWait();
196 236
237 void OnWakeUp();
238
239 ResultCode Start();
240
197 /// Cancels a waiting operation that this thread may or may not be within. 241 /// Cancels a waiting operation that this thread may or may not be within.
198 /// 242 ///
199 /// When the thread is within a waiting state, this will set the thread's 243 /// When the thread is within a waiting state, this will set the thread's
@@ -202,26 +246,19 @@ public:
202 /// 246 ///
203 void CancelWait(); 247 void CancelWait();
204 248
205 /** 249 void SetSynchronizationResults(SynchronizationObject* object, ResultCode result);
206 * Schedules an event to wake up the specified thread after the specified delay
207 * @param nanoseconds The time this thread will be allowed to sleep for
208 */
209 void WakeAfterDelay(s64 nanoseconds);
210 250
211 /// Cancel any outstanding wakeup events for this thread 251 Core::ARM_Interface& ArmInterface();
212 void CancelWakeupTimer();
213 252
214 /** 253 const Core::ARM_Interface& ArmInterface() const;
215 * Sets the result after the thread awakens (from svcWaitSynchronization)
216 * @param result Value to set to the returned result
217 */
218 void SetWaitSynchronizationResult(ResultCode result);
219 254
220 /** 255 SynchronizationObject* GetSignalingObject() const {
221 * Sets the output parameter value after the thread awakens (from svcWaitSynchronization) 256 return signaling_object;
222 * @param output Value to set to the output parameter 257 }
223 */ 258
224 void SetWaitSynchronizationOutput(s32 output); 259 ResultCode GetSignalingResult() const {
260 return signaling_result;
261 }
225 262
226 /** 263 /**
227 * Retrieves the index that this particular object occupies in the list of objects 264 * Retrieves the index that this particular object occupies in the list of objects
@@ -269,11 +306,6 @@ public:
269 */ 306 */
270 VAddr GetCommandBufferAddress() const; 307 VAddr GetCommandBufferAddress() const;
271 308
272 /// Returns whether this thread is waiting on objects from a WaitSynchronization call.
273 bool IsSleepingOnWait() const {
274 return status == ThreadStatus::WaitSynch;
275 }
276
277 ThreadContext32& GetContext32() { 309 ThreadContext32& GetContext32() {
278 return context_32; 310 return context_32;
279 } 311 }
@@ -290,6 +322,28 @@ public:
290 return context_64; 322 return context_64;
291 } 323 }
292 324
325 bool IsHLEThread() const {
326 return (type & THREADTYPE_HLE) != 0;
327 }
328
329 bool IsSuspendThread() const {
330 return (type & THREADTYPE_SUSPEND) != 0;
331 }
332
333 bool IsIdleThread() const {
334 return (type & THREADTYPE_IDLE) != 0;
335 }
336
337 bool WasRunning() const {
338 return was_running;
339 }
340
341 void SetWasRunning(bool value) {
342 was_running = value;
343 }
344
345 std::shared_ptr<Common::Fiber>& GetHostContext();
346
293 ThreadStatus GetStatus() const { 347 ThreadStatus GetStatus() const {
294 return status; 348 return status;
295 } 349 }
@@ -325,18 +379,18 @@ public:
325 } 379 }
326 380
327 const ThreadSynchronizationObjects& GetSynchronizationObjects() const { 381 const ThreadSynchronizationObjects& GetSynchronizationObjects() const {
328 return wait_objects; 382 return *wait_objects;
329 } 383 }
330 384
331 void SetSynchronizationObjects(ThreadSynchronizationObjects objects) { 385 void SetSynchronizationObjects(ThreadSynchronizationObjects* objects) {
332 wait_objects = std::move(objects); 386 wait_objects = objects;
333 } 387 }
334 388
335 void ClearSynchronizationObjects() { 389 void ClearSynchronizationObjects() {
336 for (const auto& waiting_object : wait_objects) { 390 for (const auto& waiting_object : *wait_objects) {
337 waiting_object->RemoveWaitingThread(SharedFrom(this)); 391 waiting_object->RemoveWaitingThread(SharedFrom(this));
338 } 392 }
339 wait_objects.clear(); 393 wait_objects->clear();
340 } 394 }
341 395
342 /// Determines whether all the objects this thread is waiting on are ready. 396 /// Determines whether all the objects this thread is waiting on are ready.
@@ -386,26 +440,35 @@ public:
386 arb_wait_address = address; 440 arb_wait_address = address;
387 } 441 }
388 442
389 bool HasWakeupCallback() const { 443 bool HasHLECallback() const {
390 return wakeup_callback != nullptr; 444 return hle_callback != nullptr;
391 } 445 }
392 446
393 void SetWakeupCallback(WakeupCallback callback) { 447 void SetHLECallback(HLECallback callback) {
394 wakeup_callback = std::move(callback); 448 hle_callback = std::move(callback);
395 } 449 }
396 450
397 void InvalidateWakeupCallback() { 451 void SetHLETimeEvent(Handle time_event) {
398 SetWakeupCallback(nullptr); 452 hle_time_event = time_event;
399 } 453 }
400 454
401 /** 455 void SetHLESyncObject(SynchronizationObject* object) {
402 * Invokes the thread's wakeup callback. 456 hle_object = object;
403 * 457 }
404 * @pre A valid wakeup callback has been set. Violating this precondition 458
405 * will cause an assertion to trigger. 459 Handle GetHLETimeEvent() const {
406 */ 460 return hle_time_event;
407 bool InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, 461 }
408 std::shared_ptr<SynchronizationObject> object, std::size_t index); 462
463 SynchronizationObject* GetHLESyncObject() const {
464 return hle_object;
465 }
466
467 void InvalidateHLECallback() {
468 SetHLECallback(nullptr);
469 }
470
471 bool InvokeHLECallback(std::shared_ptr<Thread> thread);
409 472
410 u32 GetIdealCore() const { 473 u32 GetIdealCore() const {
411 return ideal_core; 474 return ideal_core;
@@ -415,23 +478,19 @@ public:
415 return affinity_mask; 478 return affinity_mask;
416 } 479 }
417 480
418 ThreadActivity GetActivity() const { 481 ResultCode SetActivity(ThreadActivity value);
419 return activity;
420 }
421
422 void SetActivity(ThreadActivity value);
423 482
424 /// Sleeps this thread for the given amount of nanoseconds. 483 /// Sleeps this thread for the given amount of nanoseconds.
425 void Sleep(s64 nanoseconds); 484 ResultCode Sleep(s64 nanoseconds);
426 485
427 /// Yields this thread without rebalancing loads. 486 /// Yields this thread without rebalancing loads.
428 bool YieldSimple(); 487 std::pair<ResultCode, bool> YieldSimple();
429 488
430 /// Yields this thread and does a load rebalancing. 489 /// Yields this thread and does a load rebalancing.
431 bool YieldAndBalanceLoad(); 490 std::pair<ResultCode, bool> YieldAndBalanceLoad();
432 491
433 /// Yields this thread and if the core is left idle, loads are rebalanced 492 /// Yields this thread and if the core is left idle, loads are rebalanced
434 bool YieldAndWaitForLoadBalancing(); 493 std::pair<ResultCode, bool> YieldAndWaitForLoadBalancing();
435 494
436 void IncrementYieldCount() { 495 void IncrementYieldCount() {
437 yield_count++; 496 yield_count++;
@@ -446,6 +505,10 @@ public:
446 static_cast<u32>(ThreadSchedMasks::LowMask)); 505 static_cast<u32>(ThreadSchedMasks::LowMask));
447 } 506 }
448 507
508 bool IsRunnable() const {
509 return scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable);
510 }
511
449 bool IsRunning() const { 512 bool IsRunning() const {
450 return is_running; 513 return is_running;
451 } 514 }
@@ -466,17 +529,67 @@ public:
466 return global_handle; 529 return global_handle;
467 } 530 }
468 531
532 bool IsWaitingForArbitration() const {
533 return waiting_for_arbitration;
534 }
535
536 void WaitForArbitration(bool set) {
537 waiting_for_arbitration = set;
538 }
539
540 bool IsWaitingSync() const {
541 return is_waiting_on_sync;
542 }
543
544 void SetWaitingSync(bool is_waiting) {
545 is_waiting_on_sync = is_waiting;
546 }
547
548 bool IsPendingTermination() const {
549 return will_be_terminated || GetSchedulingStatus() == ThreadSchedStatus::Exited;
550 }
551
552 bool IsPaused() const {
553 return pausing_state != 0;
554 }
555
556 bool IsContinuousOnSVC() const {
557 return is_continuous_on_svc;
558 }
559
560 void SetContinuousOnSVC(bool is_continuous) {
561 is_continuous_on_svc = is_continuous;
562 }
563
564 bool IsPhantomMode() const {
565 return is_phantom_mode;
566 }
567
568 void SetPhantomMode(bool phantom) {
569 is_phantom_mode = phantom;
570 }
571
572 bool HasExited() const {
573 return has_exited;
574 }
575
469private: 576private:
577 friend class GlobalScheduler;
578 friend class Scheduler;
579
470 void SetSchedulingStatus(ThreadSchedStatus new_status); 580 void SetSchedulingStatus(ThreadSchedStatus new_status);
581 void AddSchedulingFlag(ThreadSchedFlags flag);
582 void RemoveSchedulingFlag(ThreadSchedFlags flag);
583
471 void SetCurrentPriority(u32 new_priority); 584 void SetCurrentPriority(u32 new_priority);
472 ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
473 585
474 void AdjustSchedulingOnStatus(u32 old_flags);
475 void AdjustSchedulingOnPriority(u32 old_priority);
476 void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); 586 void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core);
477 587
588 Common::SpinLock context_guard{};
478 ThreadContext32 context_32{}; 589 ThreadContext32 context_32{};
479 ThreadContext64 context_64{}; 590 ThreadContext64 context_64{};
591 std::unique_ptr<Core::ARM_Interface> arm_interface{};
592 std::shared_ptr<Common::Fiber> host_context{};
480 593
481 u64 thread_id = 0; 594 u64 thread_id = 0;
482 595
@@ -485,6 +598,8 @@ private:
485 VAddr entry_point = 0; 598 VAddr entry_point = 0;
486 VAddr stack_top = 0; 599 VAddr stack_top = 0;
487 600
601 ThreadType type;
602
488 /// Nominal thread priority, as set by the emulated application. 603 /// Nominal thread priority, as set by the emulated application.
489 /// The nominal priority is the thread priority without priority 604 /// The nominal priority is the thread priority without priority
490 /// inheritance taken into account. 605 /// inheritance taken into account.
@@ -509,7 +624,10 @@ private:
509 624
510 /// Objects that the thread is waiting on, in the same order as they were 625 /// Objects that the thread is waiting on, in the same order as they were
511 /// passed to WaitSynchronization. 626 /// passed to WaitSynchronization.
512 ThreadSynchronizationObjects wait_objects; 627 ThreadSynchronizationObjects* wait_objects;
628
629 SynchronizationObject* signaling_object;
630 ResultCode signaling_result{RESULT_SUCCESS};
513 631
514 /// List of threads that are waiting for a mutex that is held by this thread. 632 /// List of threads that are waiting for a mutex that is held by this thread.
515 MutexWaitingThreads wait_mutex_threads; 633 MutexWaitingThreads wait_mutex_threads;
@@ -526,30 +644,39 @@ private:
526 644
527 /// If waiting for an AddressArbiter, this is the address being waited on. 645 /// If waiting for an AddressArbiter, this is the address being waited on.
528 VAddr arb_wait_address{0}; 646 VAddr arb_wait_address{0};
647 bool waiting_for_arbitration{};
529 648
530 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. 649 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
531 Handle global_handle = 0; 650 Handle global_handle = 0;
532 651
533 /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread 652 /// Callback for HLE Events
534 /// was waiting via WaitSynchronization then the object will be the last object that became 653 HLECallback hle_callback;
535 /// available. In case of a timeout, the object will be nullptr. 654 Handle hle_time_event;
536 WakeupCallback wakeup_callback; 655 SynchronizationObject* hle_object;
537 656
538 Scheduler* scheduler = nullptr; 657 Scheduler* scheduler = nullptr;
539 658
540 u32 ideal_core{0xFFFFFFFF}; 659 u32 ideal_core{0xFFFFFFFF};
541 u64 affinity_mask{0x1}; 660 u64 affinity_mask{0x1};
542 661
543 ThreadActivity activity = ThreadActivity::Normal;
544
545 s32 ideal_core_override = -1; 662 s32 ideal_core_override = -1;
546 u64 affinity_mask_override = 0x1; 663 u64 affinity_mask_override = 0x1;
547 u32 affinity_override_count = 0; 664 u32 affinity_override_count = 0;
548 665
549 u32 scheduling_state = 0; 666 u32 scheduling_state = 0;
667 u32 pausing_state = 0;
550 bool is_running = false; 668 bool is_running = false;
669 bool is_waiting_on_sync = false;
551 bool is_sync_cancelled = false; 670 bool is_sync_cancelled = false;
552 671
672 bool is_continuous_on_svc = false;
673
674 bool will_be_terminated = false;
675 bool is_phantom_mode = false;
676 bool has_exited = false;
677
678 bool was_running = false;
679
553 std::string name; 680 std::string name;
554}; 681};
555 682
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 21b290468..941305e8e 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -8,30 +8,37 @@
8#include "core/core_timing_util.h" 8#include "core/core_timing_util.h"
9#include "core/hle/kernel/handle_table.h" 9#include "core/hle/kernel/handle_table.h"
10#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/scheduler.h"
11#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
12#include "core/hle/kernel/time_manager.h" 13#include "core/hle/kernel/time_manager.h"
13 14
14namespace Kernel { 15namespace Kernel {
15 16
16TimeManager::TimeManager(Core::System& system) : system{system} { 17TimeManager::TimeManager(Core::System& system_) : system{system_} {
17 time_manager_event_type = Core::Timing::CreateEvent( 18 time_manager_event_type = Core::Timing::CreateEvent(
18 "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) { 19 "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) {
20 SchedulerLock lock(system.Kernel());
19 Handle proper_handle = static_cast<Handle>(thread_handle); 21 Handle proper_handle = static_cast<Handle>(thread_handle);
22 if (cancelled_events[proper_handle]) {
23 return;
24 }
20 std::shared_ptr<Thread> thread = 25 std::shared_ptr<Thread> thread =
21 this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); 26 this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
22 thread->ResumeFromWait(); 27 thread->OnWakeUp();
23 }); 28 });
24} 29}
25 30
26void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) { 31void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) {
32 event_handle = timetask->GetGlobalHandle();
27 if (nanoseconds > 0) { 33 if (nanoseconds > 0) {
28 ASSERT(timetask); 34 ASSERT(timetask);
29 event_handle = timetask->GetGlobalHandle(); 35 ASSERT(timetask->GetStatus() != ThreadStatus::Ready);
30 const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); 36 ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex);
31 system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle); 37 system.CoreTiming().ScheduleEvent(nanoseconds, time_manager_event_type, event_handle);
32 } else { 38 } else {
33 event_handle = InvalidHandle; 39 event_handle = InvalidHandle;
34 } 40 }
41 cancelled_events[event_handle] = false;
35} 42}
36 43
37void TimeManager::UnscheduleTimeEvent(Handle event_handle) { 44void TimeManager::UnscheduleTimeEvent(Handle event_handle) {
@@ -39,6 +46,12 @@ void TimeManager::UnscheduleTimeEvent(Handle event_handle) {
39 return; 46 return;
40 } 47 }
41 system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle); 48 system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle);
49 cancelled_events[event_handle] = true;
50}
51
52void TimeManager::CancelTimeEvent(Thread* time_task) {
53 Handle event_handle = time_task->GetGlobalHandle();
54 UnscheduleTimeEvent(event_handle);
42} 55}
43 56
44} // namespace Kernel 57} // namespace Kernel
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h
index eaec486d1..307a18765 100644
--- a/src/core/hle/kernel/time_manager.h
+++ b/src/core/hle/kernel/time_manager.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <unordered_map>
8 9
9#include "core/hle/kernel/object.h" 10#include "core/hle/kernel/object.h"
10 11
@@ -35,9 +36,12 @@ public:
35 /// Unschedule an existing time event 36 /// Unschedule an existing time event
36 void UnscheduleTimeEvent(Handle event_handle); 37 void UnscheduleTimeEvent(Handle event_handle);
37 38
39 void CancelTimeEvent(Thread* time_task);
40
38private: 41private:
39 Core::System& system; 42 Core::System& system;
40 std::shared_ptr<Core::Timing::EventType> time_manager_event_type; 43 std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
44 std::unordered_map<Handle, bool> cancelled_events;
41}; 45};
42 46
43} // namespace Kernel 47} // namespace Kernel
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 630a8b048..8ac856ec3 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -44,6 +44,218 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) {
44 return static_cast<u32>(std::min(size, max_jpeg_image_size)); 44 return static_cast<u32>(std::min(size, max_jpeg_image_size));
45} 45}
46 46
47class IManagerForSystemService final : public ServiceFramework<IManagerForSystemService> {
48public:
49 explicit IManagerForSystemService(Common::UUID user_id)
50 : ServiceFramework("IManagerForSystemService") {
51 // clang-format off
52 static const FunctionInfo functions[] = {
53 {0, nullptr, "CheckAvailability"},
54 {1, nullptr, "GetAccountId"},
55 {2, nullptr, "EnsureIdTokenCacheAsync"},
56 {3, nullptr, "LoadIdTokenCache"},
57 {100, nullptr, "SetSystemProgramIdentification"},
58 {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+
59 {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+
60 {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+
61 {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0
62 {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+
63 {120, nullptr, "GetNintendoAccountId"},
64 {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+
65 {130, nullptr, "GetNintendoAccountUserResourceCache"},
66 {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"},
67 {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"},
68 {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+
69 {134, nullptr, "RefreshNintendoAccountVerificationUrlCache"}, // 9.0.0+
70 {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+
71 {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+
72 {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+
73 {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+
74 {150, nullptr, "CreateAuthorizationRequest"},
75 };
76 // clang-format on
77
78 RegisterHandlers(functions);
79 }
80};
81
82// 3.0.0+
83class IFloatingRegistrationRequest final : public ServiceFramework<IFloatingRegistrationRequest> {
84public:
85 explicit IFloatingRegistrationRequest(Common::UUID user_id)
86 : ServiceFramework("IFloatingRegistrationRequest") {
87 // clang-format off
88 static const FunctionInfo functions[] = {
89 {0, nullptr, "GetSessionId"},
90 {12, nullptr, "GetAccountId"},
91 {13, nullptr, "GetLinkedNintendoAccountId"},
92 {14, nullptr, "GetNickname"},
93 {15, nullptr, "GetProfileImage"},
94 {21, nullptr, "LoadIdTokenCache"},
95 {100, nullptr, "RegisterUser"}, // [1.0.0-3.0.2] RegisterAsync
96 {101, nullptr, "RegisterUserWithUid"}, // [1.0.0-3.0.2] RegisterWithUidAsync
97 {102, nullptr, "RegisterNetworkServiceAccountAsync"}, // 4.0.0+
98 {103, nullptr, "RegisterNetworkServiceAccountWithUidAsync"}, // 4.0.0+
99 {110, nullptr, "SetSystemProgramIdentification"},
100 {111, nullptr, "EnsureIdTokenCacheAsync"},
101 };
102 // clang-format on
103
104 RegisterHandlers(functions);
105 }
106};
107
108class IAdministrator final : public ServiceFramework<IAdministrator> {
109public:
110 explicit IAdministrator(Common::UUID user_id) : ServiceFramework("IAdministrator") {
111 // clang-format off
112 static const FunctionInfo functions[] = {
113 {0, nullptr, "CheckAvailability"},
114 {1, nullptr, "GetAccountId"},
115 {2, nullptr, "EnsureIdTokenCacheAsync"},
116 {3, nullptr, "LoadIdTokenCache"},
117 {100, nullptr, "SetSystemProgramIdentification"},
118 {101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+
119 {110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+
120 {111, nullptr, "InvalidateServiceEntryRequirementCache"}, // 4.0.0+
121 {112, nullptr, "InvalidateTokenCache"}, // 4.0.0 - 6.2.0
122 {113, nullptr, "GetServiceEntryRequirementCacheForOnlinePlay"}, // 6.1.0+
123 {120, nullptr, "GetNintendoAccountId"},
124 {121, nullptr, "CalculateNintendoAccountAuthenticationFingerprint"}, // 9.0.0+
125 {130, nullptr, "GetNintendoAccountUserResourceCache"},
126 {131, nullptr, "RefreshNintendoAccountUserResourceCacheAsync"},
127 {132, nullptr, "RefreshNintendoAccountUserResourceCacheAsyncIfSecondsElapsed"},
128 {133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+
129 {134, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsync"}, // 9.0.0+
130 {135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+
131 {140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+
132 {141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+
133 {142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+
134 {150, nullptr, "CreateAuthorizationRequest"},
135 {200, nullptr, "IsRegistered"},
136 {201, nullptr, "RegisterAsync"},
137 {202, nullptr, "UnregisterAsync"},
138 {203, nullptr, "DeleteRegistrationInfoLocally"},
139 {220, nullptr, "SynchronizeProfileAsync"},
140 {221, nullptr, "UploadProfileAsync"},
141 {222, nullptr, "SynchronizaProfileAsyncIfSecondsElapsed"},
142 {250, nullptr, "IsLinkedWithNintendoAccount"},
143 {251, nullptr, "CreateProcedureToLinkWithNintendoAccount"},
144 {252, nullptr, "ResumeProcedureToLinkWithNintendoAccount"},
145 {255, nullptr, "CreateProcedureToUpdateLinkageStateOfNintendoAccount"},
146 {256, nullptr, "ResumeProcedureToUpdateLinkageStateOfNintendoAccount"},
147 {260, nullptr, "CreateProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+
148 {261, nullptr, "ResumeProcedureToLinkNnidWithNintendoAccount"}, // 3.0.0+
149 {280, nullptr, "ProxyProcedureToAcquireApplicationAuthorizationForNintendoAccount"},
150 {290, nullptr, "GetRequestForNintendoAccountUserResourceView"}, // 8.0.0+
151 {300, nullptr, "TryRecoverNintendoAccountUserStateAsync"}, // 6.0.0+
152 {400, nullptr, "IsServiceEntryRequirementCacheRefreshRequiredForOnlinePlay"}, // 6.1.0+
153 {401, nullptr, "RefreshServiceEntryRequirementCacheForOnlinePlayAsync"}, // 6.1.0+
154 {900, nullptr, "GetAuthenticationInfoForWin"}, // 9.0.0+
155 {901, nullptr, "ImportAsyncForWin"}, // 9.0.0+
156 {997, nullptr, "DebugUnlinkNintendoAccountAsync"},
157 {998, nullptr, "DebugSetAvailabilityErrorDetail"},
158 };
159 // clang-format on
160
161 RegisterHandlers(functions);
162 }
163};
164
165class IAuthorizationRequest final : public ServiceFramework<IAuthorizationRequest> {
166public:
167 explicit IAuthorizationRequest(Common::UUID user_id)
168 : ServiceFramework("IAuthorizationRequest") {
169 // clang-format off
170 static const FunctionInfo functions[] = {
171 {0, nullptr, "GetSessionId"},
172 {10, nullptr, "InvokeWithoutInteractionAsync"},
173 {19, nullptr, "IsAuthorized"},
174 {20, nullptr, "GetAuthorizationCode"},
175 {21, nullptr, "GetIdToken"},
176 {22, nullptr, "GetState"},
177 };
178 // clang-format on
179
180 RegisterHandlers(functions);
181 }
182};
183
184class IOAuthProcedure final : public ServiceFramework<IOAuthProcedure> {
185public:
186 explicit IOAuthProcedure(Common::UUID user_id) : ServiceFramework("IOAuthProcedure") {
187 // clang-format off
188 static const FunctionInfo functions[] = {
189 {0, nullptr, "PrepareAsync"},
190 {1, nullptr, "GetRequest"},
191 {2, nullptr, "ApplyResponse"},
192 {3, nullptr, "ApplyResponseAsync"},
193 {10, nullptr, "Suspend"},
194 };
195 // clang-format on
196
197 RegisterHandlers(functions);
198 }
199};
200
201// 3.0.0+
202class IOAuthProcedureForExternalNsa final : public ServiceFramework<IOAuthProcedureForExternalNsa> {
203public:
204 explicit IOAuthProcedureForExternalNsa(Common::UUID user_id)
205 : ServiceFramework("IOAuthProcedureForExternalNsa") {
206 // clang-format off
207 static const FunctionInfo functions[] = {
208 {0, nullptr, "PrepareAsync"},
209 {1, nullptr, "GetRequest"},
210 {2, nullptr, "ApplyResponse"},
211 {3, nullptr, "ApplyResponseAsync"},
212 {10, nullptr, "Suspend"},
213 {100, nullptr, "GetAccountId"},
214 {101, nullptr, "GetLinkedNintendoAccountId"},
215 {102, nullptr, "GetNickname"},
216 {103, nullptr, "GetProfileImage"},
217 };
218 // clang-format on
219
220 RegisterHandlers(functions);
221 }
222};
223
224class IOAuthProcedureForNintendoAccountLinkage final
225 : public ServiceFramework<IOAuthProcedureForNintendoAccountLinkage> {
226public:
227 explicit IOAuthProcedureForNintendoAccountLinkage(Common::UUID user_id)
228 : ServiceFramework("IOAuthProcedureForNintendoAccountLinkage") {
229 // clang-format off
230 static const FunctionInfo functions[] = {
231 {0, nullptr, "PrepareAsync"},
232 {1, nullptr, "GetRequest"},
233 {2, nullptr, "ApplyResponse"},
234 {3, nullptr, "ApplyResponseAsync"},
235 {10, nullptr, "Suspend"},
236 {100, nullptr, "GetRequestWithTheme"},
237 {101, nullptr, "IsNetworkServiceAccountReplaced"},
238 {199, nullptr, "GetUrlForIntroductionOfExtraMembership"}, // 2.0.0 - 5.1.0
239 };
240 // clang-format on
241
242 RegisterHandlers(functions);
243 }
244};
245
246class INotifier final : public ServiceFramework<INotifier> {
247public:
248 explicit INotifier(Common::UUID user_id) : ServiceFramework("INotifier") {
249 // clang-format off
250 static const FunctionInfo functions[] = {
251 {0, nullptr, "GetSystemEvent"},
252 };
253 // clang-format on
254
255 RegisterHandlers(functions);
256 }
257};
258
47class IProfileCommon : public ServiceFramework<IProfileCommon> { 259class IProfileCommon : public ServiceFramework<IProfileCommon> {
48public: 260public:
49 explicit IProfileCommon(const char* name, bool editor_commands, Common::UUID user_id, 261 explicit IProfileCommon(const char* name, bool editor_commands, Common::UUID user_id,
@@ -226,6 +438,54 @@ public:
226 : IProfileCommon("IProfileEditor", true, user_id, profile_manager) {} 438 : IProfileCommon("IProfileEditor", true, user_id, profile_manager) {}
227}; 439};
228 440
441class IAsyncContext final : public ServiceFramework<IAsyncContext> {
442public:
443 explicit IAsyncContext(Common::UUID user_id) : ServiceFramework("IAsyncContext") {
444 // clang-format off
445 static const FunctionInfo functions[] = {
446 {0, nullptr, "GetSystemEvent"},
447 {1, nullptr, "Cancel"},
448 {2, nullptr, "HasDone"},
449 {3, nullptr, "GetResult"},
450 };
451 // clang-format on
452
453 RegisterHandlers(functions);
454 }
455};
456
457class ISessionObject final : public ServiceFramework<ISessionObject> {
458public:
459 explicit ISessionObject(Common::UUID user_id) : ServiceFramework("ISessionObject") {
460 // clang-format off
461 static const FunctionInfo functions[] = {
462 {999, nullptr, "Dummy"},
463 };
464 // clang-format on
465
466 RegisterHandlers(functions);
467 }
468};
469
470class IGuestLoginRequest final : public ServiceFramework<IGuestLoginRequest> {
471public:
472 explicit IGuestLoginRequest(Common::UUID) : ServiceFramework("IGuestLoginRequest") {
473 // clang-format off
474 static const FunctionInfo functions[] = {
475 {0, nullptr, "GetSessionId"},
476 {11, nullptr, "Unknown"}, // 1.0.0 - 2.3.0 (the name is blank on Switchbrew)
477 {12, nullptr, "GetAccountId"},
478 {13, nullptr, "GetLinkedNintendoAccountId"},
479 {14, nullptr, "GetNickname"},
480 {15, nullptr, "GetProfileImage"},
481 {21, nullptr, "LoadIdTokenCache"}, // 3.0.0+
482 };
483 // clang-format on
484
485 RegisterHandlers(functions);
486 }
487};
488
229class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { 489class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
230public: 490public:
231 explicit IManagerForApplication(Common::UUID user_id) 491 explicit IManagerForApplication(Common::UUID user_id)
@@ -265,6 +525,87 @@ private:
265 Common::UUID user_id; 525 Common::UUID user_id;
266}; 526};
267 527
528// 6.0.0+
529class IAsyncNetworkServiceLicenseKindContext final
530 : public ServiceFramework<IAsyncNetworkServiceLicenseKindContext> {
531public:
532 explicit IAsyncNetworkServiceLicenseKindContext(Common::UUID user_id)
533 : ServiceFramework("IAsyncNetworkServiceLicenseKindContext") {
534 // clang-format off
535 static const FunctionInfo functions[] = {
536 {0, nullptr, "GetSystemEvent"},
537 {1, nullptr, "Cancel"},
538 {2, nullptr, "HasDone"},
539 {3, nullptr, "GetResult"},
540 {4, nullptr, "GetNetworkServiceLicenseKind"},
541 };
542 // clang-format on
543
544 RegisterHandlers(functions);
545 }
546};
547
548// 8.0.0+
549class IOAuthProcedureForUserRegistration final
550 : public ServiceFramework<IOAuthProcedureForUserRegistration> {
551public:
552 explicit IOAuthProcedureForUserRegistration(Common::UUID user_id)
553 : ServiceFramework("IOAuthProcedureForUserRegistration") {
554 // clang-format off
555 static const FunctionInfo functions[] = {
556 {0, nullptr, "PrepareAsync"},
557 {1, nullptr, "GetRequest"},
558 {2, nullptr, "ApplyResponse"},
559 {3, nullptr, "ApplyResponseAsync"},
560 {10, nullptr, "Suspend"},
561 {100, nullptr, "GetAccountId"},
562 {101, nullptr, "GetLinkedNintendoAccountId"},
563 {102, nullptr, "GetNickname"},
564 {103, nullptr, "GetProfileImage"},
565 {110, nullptr, "RegisterUserAsync"},
566 {111, nullptr, "GetUid"},
567 };
568 // clang-format on
569
570 RegisterHandlers(functions);
571 }
572};
573
574class DAUTH_O final : public ServiceFramework<DAUTH_O> {
575public:
576 explicit DAUTH_O(Common::UUID) : ServiceFramework("dauth:o") {
577 // clang-format off
578 static const FunctionInfo functions[] = {
579 {0, nullptr, "EnsureAuthenticationTokenCacheAsync"}, // [5.0.0-5.1.0] GeneratePostData
580 {1, nullptr, "LoadAuthenticationTokenCache"}, // 6.0.0+
581 {2, nullptr, "InvalidateAuthenticationTokenCache"}, // 6.0.0+
582 {10, nullptr, "EnsureEdgeTokenCacheAsync"}, // 6.0.0+
583 {11, nullptr, "LoadEdgeTokenCache"}, // 6.0.0+
584 {12, nullptr, "InvalidateEdgeTokenCache"}, // 6.0.0+
585 };
586 // clang-format on
587
588 RegisterHandlers(functions);
589 }
590};
591
592// 6.0.0+
593class IAsyncResult final : public ServiceFramework<IAsyncResult> {
594public:
595 explicit IAsyncResult(Common::UUID user_id) : ServiceFramework("IAsyncResult") {
596 // clang-format off
597 static const FunctionInfo functions[] = {
598 {0, nullptr, "GetResult"},
599 {1, nullptr, "Cancel"},
600 {2, nullptr, "IsAvailable"},
601 {3, nullptr, "GetSystemEvent"},
602 };
603 // clang-format on
604
605 RegisterHandlers(functions);
606 }
607};
608
268void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { 609void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
269 LOG_DEBUG(Service_ACC, "called"); 610 LOG_DEBUG(Service_ACC, "called");
270 IPC::ResponseBuilder rb{ctx, 3}; 611 IPC::ResponseBuilder rb{ctx, 3};
@@ -435,6 +776,15 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) {
435 rb.Push(RESULT_SUCCESS); 776 rb.Push(RESULT_SUCCESS);
436} 777}
437 778
779void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) {
780 LOG_WARNING(Service_ACC, "(STUBBED) called");
781
782 // TODO(ogniK): Handle open contexts
783 ctx.WriteBuffer(profile_manager->GetOpenUsers());
784 IPC::ResponseBuilder rb{ctx, 2};
785 rb.Push(RESULT_SUCCESS);
786}
787
438void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) { 788void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
439 LOG_DEBUG(Service_ACC, "called"); 789 LOG_DEBUG(Service_ACC, "called");
440 // A u8 is passed into this function which we can safely ignore. It's to determine if we have 790 // A u8 is passed into this function which we can safely ignore. It's to determine if we have
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index 74ca39d6e..d4c6395c6 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -34,6 +34,7 @@ public:
34 void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); 34 void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
35 void GetProfileEditor(Kernel::HLERequestContext& ctx); 35 void GetProfileEditor(Kernel::HLERequestContext& ctx);
36 void ListQualifiedUsers(Kernel::HLERequestContext& ctx); 36 void ListQualifiedUsers(Kernel::HLERequestContext& ctx);
37 void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);
37 38
38 private: 39 private:
39 ResultCode InitializeApplicationInfoBase(); 40 ResultCode InitializeApplicationInfoBase();
diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp
index 3bac6bcd1..51f119b12 100644
--- a/src/core/hle/service/acc/acc_aa.cpp
+++ b/src/core/hle/service/acc/acc_aa.cpp
@@ -13,8 +13,8 @@ ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
13 {0, nullptr, "EnsureCacheAsync"}, 13 {0, nullptr, "EnsureCacheAsync"},
14 {1, nullptr, "LoadCache"}, 14 {1, nullptr, "LoadCache"},
15 {2, nullptr, "GetDeviceAccountId"}, 15 {2, nullptr, "GetDeviceAccountId"},
16 {50, nullptr, "RegisterNotificationTokenAsync"}, 16 {50, nullptr, "RegisterNotificationTokenAsync"}, // 1.0.0 - 6.2.0
17 {51, nullptr, "UnregisterNotificationTokenAsync"}, 17 {51, nullptr, "UnregisterNotificationTokenAsync"}, // 1.0.0 - 6.2.0
18 }; 18 };
19 RegisterHandlers(functions); 19 RegisterHandlers(functions);
20} 20}
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index 2eefc6df5..d2bb8c2c8 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -17,28 +17,28 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
17 {3, &ACC_SU::ListOpenUsers, "ListOpenUsers"}, 17 {3, &ACC_SU::ListOpenUsers, "ListOpenUsers"},
18 {4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"}, 18 {4, &ACC_SU::GetLastOpenedUser, "GetLastOpenedUser"},
19 {5, &ACC_SU::GetProfile, "GetProfile"}, 19 {5, &ACC_SU::GetProfile, "GetProfile"},
20 {6, nullptr, "GetProfileDigest"}, 20 {6, nullptr, "GetProfileDigest"}, // 3.0.0+
21 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 21 {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
22 {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 22 {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
23 {60, nullptr, "ListOpenContextStoredUsers"}, 23 {60, &ACC_SU::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
24 {99, nullptr, "DebugActivateOpenContextRetention"}, 24 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+
25 {100, nullptr, "GetUserRegistrationNotifier"}, 25 {100, nullptr, "GetUserRegistrationNotifier"},
26 {101, nullptr, "GetUserStateChangeNotifier"}, 26 {101, nullptr, "GetUserStateChangeNotifier"},
27 {102, nullptr, "GetBaasAccountManagerForSystemService"}, 27 {102, nullptr, "GetBaasAccountManagerForSystemService"},
28 {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, 28 {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"},
29 {104, nullptr, "GetProfileUpdateNotifier"}, 29 {104, nullptr, "GetProfileUpdateNotifier"},
30 {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, 30 {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+
31 {106, nullptr, "GetProfileSyncNotifier"}, 31 {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+
32 {110, nullptr, "StoreSaveDataThumbnail"}, 32 {110, nullptr, "StoreSaveDataThumbnail"},
33 {111, nullptr, "ClearSaveDataThumbnail"}, 33 {111, nullptr, "ClearSaveDataThumbnail"},
34 {112, nullptr, "LoadSaveDataThumbnail"}, 34 {112, nullptr, "LoadSaveDataThumbnail"},
35 {113, nullptr, "GetSaveDataThumbnailExistence"}, 35 {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+
36 {120, nullptr, "ListOpenUsersInApplication"}, 36 {120, nullptr, "ListOpenUsersInApplication"}, // 10.0.0+
37 {130, nullptr, "ActivateOpenContextRetention"}, 37 {130, nullptr, "ActivateOpenContextRetention"}, // 6.0.0+
38 {140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"}, 38 {140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
39 {150, nullptr, "AuthenticateApplicationAsync"}, 39 {150, nullptr, "AuthenticateApplicationAsync"}, // 10.0.0+
40 {190, nullptr, "GetUserLastOpenedApplication"}, 40 {190, nullptr, "GetUserLastOpenedApplication"}, // 1.0.0 - 9.2.0
41 {191, nullptr, "ActivateOpenContextHolder"}, 41 {191, nullptr, "ActivateOpenContextHolder"}, // 7.0.0+
42 {200, nullptr, "BeginUserRegistration"}, 42 {200, nullptr, "BeginUserRegistration"},
43 {201, nullptr, "CompleteUserRegistration"}, 43 {201, nullptr, "CompleteUserRegistration"},
44 {202, nullptr, "CancelUserRegistration"}, 44 {202, nullptr, "CancelUserRegistration"},
@@ -46,15 +46,15 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
46 {204, nullptr, "SetUserPosition"}, 46 {204, nullptr, "SetUserPosition"},
47 {205, &ACC_SU::GetProfileEditor, "GetProfileEditor"}, 47 {205, &ACC_SU::GetProfileEditor, "GetProfileEditor"},
48 {206, nullptr, "CompleteUserRegistrationForcibly"}, 48 {206, nullptr, "CompleteUserRegistrationForcibly"},
49 {210, nullptr, "CreateFloatingRegistrationRequest"}, 49 {210, nullptr, "CreateFloatingRegistrationRequest"}, // 3.0.0+
50 {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, 50 {211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"}, // 8.0.0+
51 {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, 51 {212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"}, // 8.0.0+
52 {230, nullptr, "AuthenticateServiceAsync"}, 52 {230, nullptr, "AuthenticateServiceAsync"},
53 {250, nullptr, "GetBaasAccountAdministrator"}, 53 {250, nullptr, "GetBaasAccountAdministrator"},
54 {290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"}, 54 {290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"},
55 {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, 55 {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, // 3.0.0+
56 {299, nullptr, "SuspendBackgroundDaemon"}, 56 {299, nullptr, "SuspendBackgroundDaemon"},
57 {997, nullptr, "DebugInvalidateTokenCacheForUser"}, 57 {997, nullptr, "DebugInvalidateTokenCacheForUser"}, // 3.0.0+
58 {998, nullptr, "DebugSetUserStateClose"}, 58 {998, nullptr, "DebugSetUserStateClose"},
59 {999, nullptr, "DebugSetUserStateOpen"}, 59 {999, nullptr, "DebugSetUserStateOpen"},
60 }; 60 };
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index fb4e7e772..cb44e06b7 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -17,23 +17,23 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
17 {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"}, 17 {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"},
18 {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, 18 {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"},
19 {5, &ACC_U0::GetProfile, "GetProfile"}, 19 {5, &ACC_U0::GetProfile, "GetProfile"},
20 {6, nullptr, "GetProfileDigest"}, 20 {6, nullptr, "GetProfileDigest"}, // 3.0.0+
21 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 21 {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
22 {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 22 {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
23 {60, nullptr, "ListOpenContextStoredUsers"}, 23 {60, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
24 {99, nullptr, "DebugActivateOpenContextRetention"}, 24 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+
25 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, 25 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
26 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, 26 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
27 {102, nullptr, "AuthenticateApplicationAsync"}, 27 {102, nullptr, "AuthenticateApplicationAsync"},
28 {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, 28 {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+
29 {110, nullptr, "StoreSaveDataThumbnail"}, 29 {110, nullptr, "StoreSaveDataThumbnail"},
30 {111, nullptr, "ClearSaveDataThumbnail"}, 30 {111, nullptr, "ClearSaveDataThumbnail"},
31 {120, nullptr, "CreateGuestLoginRequest"}, 31 {120, nullptr, "CreateGuestLoginRequest"},
32 {130, nullptr, "LoadOpenContext"}, 32 {130, nullptr, "LoadOpenContext"}, // 5.0.0+
33 {131, nullptr, "ListOpenContextStoredUsers"}, 33 {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+
34 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, 34 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+
35 {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, 35 {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
36 {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, 36 {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+
37 }; 37 };
38 // clang-format on 38 // clang-format on
39 39
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index 9f29cdc82..a4aa5316a 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -17,28 +17,29 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
17 {3, &ACC_U1::ListOpenUsers, "ListOpenUsers"}, 17 {3, &ACC_U1::ListOpenUsers, "ListOpenUsers"},
18 {4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"}, 18 {4, &ACC_U1::GetLastOpenedUser, "GetLastOpenedUser"},
19 {5, &ACC_U1::GetProfile, "GetProfile"}, 19 {5, &ACC_U1::GetProfile, "GetProfile"},
20 {6, nullptr, "GetProfileDigest"}, 20 {6, nullptr, "GetProfileDigest"}, // 3.0.0+
21 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, 21 {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
22 {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 22 {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
23 {60, nullptr, "ListOpenContextStoredUsers"}, 23 {60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0
24 {99, nullptr, "DebugActivateOpenContextRetention"}, 24 {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+
25 {100, nullptr, "GetUserRegistrationNotifier"}, 25 {100, nullptr, "GetUserRegistrationNotifier"},
26 {101, nullptr, "GetUserStateChangeNotifier"}, 26 {101, nullptr, "GetUserStateChangeNotifier"},
27 {102, nullptr, "GetBaasAccountManagerForSystemService"}, 27 {102, nullptr, "GetBaasAccountManagerForSystemService"},
28 {103, nullptr, "GetProfileUpdateNotifier"}, 28 {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"},
29 {104, nullptr, "CheckNetworkServiceAvailabilityAsync"}, 29 {104, nullptr, "GetProfileUpdateNotifier"},
30 {105, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, 30 {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+
31 {106, nullptr, "GetProfileSyncNotifier"}, 31 {106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+
32 {110, nullptr, "StoreSaveDataThumbnail"}, 32 {110, nullptr, "StoreSaveDataThumbnail"},
33 {111, nullptr, "ClearSaveDataThumbnail"}, 33 {111, nullptr, "ClearSaveDataThumbnail"},
34 {112, nullptr, "LoadSaveDataThumbnail"}, 34 {112, nullptr, "LoadSaveDataThumbnail"},
35 {113, nullptr, "GetSaveDataThumbnailExistence"}, 35 {113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+
36 {130, nullptr, "ActivateOpenContextRetention"}, 36 {120, nullptr, "ListOpenUsersInApplication"}, // 10.0.0+
37 {140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"}, 37 {130, nullptr, "ActivateOpenContextRetention"}, // 6.0.0+
38 {150, nullptr, "AuthenticateApplicationAsync"}, 38 {140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
39 {190, nullptr, "GetUserLastOpenedApplication"}, 39 {150, nullptr, "AuthenticateApplicationAsync"}, // 10.0.0+
40 {191, nullptr, "ActivateOpenContextHolder"}, 40 {190, nullptr, "GetUserLastOpenedApplication"}, // 1.0.0 - 9.2.0
41 {997, nullptr, "DebugInvalidateTokenCacheForUser"}, 41 {191, nullptr, "ActivateOpenContextHolder"}, // 7.0.0+
42 {997, nullptr, "DebugInvalidateTokenCacheForUser"}, // 3.0.0+
42 {998, nullptr, "DebugSetUserStateClose"}, 43 {998, nullptr, "DebugSetUserStateClose"},
43 {999, nullptr, "DebugSetUserStateOpen"}, 44 {999, nullptr, "DebugSetUserStateOpen"},
44 }; 45 };
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 4df74c4f9..256449aa7 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -10,6 +10,7 @@
10#include "core/core.h" 10#include "core/core.h"
11#include "core/file_sys/control_metadata.h" 11#include "core/file_sys/control_metadata.h"
12#include "core/file_sys/patch_manager.h" 12#include "core/file_sys/patch_manager.h"
13#include "core/file_sys/registered_cache.h"
13#include "core/file_sys/savedata_factory.h" 14#include "core/file_sys/savedata_factory.h"
14#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
15#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/kernel.h"
@@ -68,6 +69,7 @@ IWindowController::IWindowController(Core::System& system_)
68 static const FunctionInfo functions[] = { 69 static const FunctionInfo functions[] = {
69 {0, nullptr, "CreateWindow"}, 70 {0, nullptr, "CreateWindow"},
70 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, 71 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
72 {2, nullptr, "GetAppletResourceUserIdOfCallerApplet"},
71 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, 73 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
72 {11, nullptr, "ReleaseForegroundRights"}, 74 {11, nullptr, "ReleaseForegroundRights"},
73 {12, nullptr, "RejectToChangeIntoBackground"}, 75 {12, nullptr, "RejectToChangeIntoBackground"},
@@ -189,8 +191,8 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
189 {5, nullptr, "GetLastForegroundCaptureImageEx"}, 191 {5, nullptr, "GetLastForegroundCaptureImageEx"},
190 {6, nullptr, "GetLastApplicationCaptureImageEx"}, 192 {6, nullptr, "GetLastApplicationCaptureImageEx"},
191 {7, nullptr, "GetCallerAppletCaptureImageEx"}, 193 {7, nullptr, "GetCallerAppletCaptureImageEx"},
192 {8, nullptr, "TakeScreenShotOfOwnLayer"}, // 2.0.0+ 194 {8, nullptr, "TakeScreenShotOfOwnLayer"},
193 {9, nullptr, "CopyBetweenCaptureBuffers"}, // 5.0.0+ 195 {9, nullptr, "CopyBetweenCaptureBuffers"},
194 {10, nullptr, "AcquireLastApplicationCaptureBuffer"}, 196 {10, nullptr, "AcquireLastApplicationCaptureBuffer"},
195 {11, nullptr, "ReleaseLastApplicationCaptureBuffer"}, 197 {11, nullptr, "ReleaseLastApplicationCaptureBuffer"},
196 {12, nullptr, "AcquireLastForegroundCaptureBuffer"}, 198 {12, nullptr, "AcquireLastForegroundCaptureBuffer"},
@@ -200,17 +202,14 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
200 {16, nullptr, "AcquireLastApplicationCaptureBufferEx"}, 202 {16, nullptr, "AcquireLastApplicationCaptureBufferEx"},
201 {17, nullptr, "AcquireLastForegroundCaptureBufferEx"}, 203 {17, nullptr, "AcquireLastForegroundCaptureBufferEx"},
202 {18, nullptr, "AcquireCallerAppletCaptureBufferEx"}, 204 {18, nullptr, "AcquireCallerAppletCaptureBufferEx"},
203 // 2.0.0+
204 {20, nullptr, "ClearCaptureBuffer"}, 205 {20, nullptr, "ClearCaptureBuffer"},
205 {21, nullptr, "ClearAppletTransitionBuffer"}, 206 {21, nullptr, "ClearAppletTransitionBuffer"},
206 // 4.0.0+
207 {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"}, 207 {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"},
208 {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, 208 {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"},
209 {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"}, 209 {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"},
210 {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, 210 {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
211 {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, 211 {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
212 {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, 212 {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
213 // 6.0.0+
214 {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, 213 {28, nullptr, "TakeScreenShotOfOwnLayerEx"},
215 }; 214 };
216 // clang-format on 215 // clang-format on
@@ -225,7 +224,7 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
225 static const FunctionInfo functions[] = { 224 static const FunctionInfo functions[] = {
226 {0, nullptr, "NotifyMessageToHomeMenuForDebug"}, 225 {0, nullptr, "NotifyMessageToHomeMenuForDebug"},
227 {1, nullptr, "OpenMainApplication"}, 226 {1, nullptr, "OpenMainApplication"},
228 {10, nullptr, "EmulateButtonEvent"}, 227 {10, nullptr, "PerformSystemButtonPressing"},
229 {20, nullptr, "InvalidateTransitionLayer"}, 228 {20, nullptr, "InvalidateTransitionLayer"},
230 {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, 229 {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
231 {40, nullptr, "GetAppletResourceUsageInfo"}, 230 {40, nullptr, "GetAppletResourceUsageInfo"},
@@ -267,13 +266,13 @@ ISelfController::ISelfController(Core::System& system,
267 {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, 266 {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"},
268 {17, nullptr, "SetControllerFirmwareUpdateSection"}, 267 {17, nullptr, "SetControllerFirmwareUpdateSection"},
269 {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, 268 {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
270 {19, &ISelfController::SetScreenShotImageOrientation, "SetScreenShotImageOrientation"}, 269 {19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"},
271 {20, nullptr, "SetDesirableKeyboardLayout"}, 270 {20, nullptr, "SetDesirableKeyboardLayout"},
272 {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, 271 {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
273 {41, nullptr, "IsSystemBufferSharingEnabled"}, 272 {41, nullptr, "IsSystemBufferSharingEnabled"},
274 {42, nullptr, "GetSystemSharedLayerHandle"}, 273 {42, nullptr, "GetSystemSharedLayerHandle"},
275 {43, nullptr, "GetSystemSharedBufferHandle"}, 274 {43, nullptr, "GetSystemSharedBufferHandle"},
276 {44, nullptr, "CreateManagedDisplaySeparableLayer"}, 275 {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
277 {45, nullptr, "SetManagedDisplayLayerSeparationMode"}, 276 {45, nullptr, "SetManagedDisplayLayerSeparationMode"},
278 {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, 277 {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
279 {51, nullptr, "ApproveToDisplay"}, 278 {51, nullptr, "ApproveToDisplay"},
@@ -443,7 +442,7 @@ void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext&
443 rb.Push(RESULT_SUCCESS); 442 rb.Push(RESULT_SUCCESS);
444} 443}
445 444
446void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) { 445void ISelfController::SetAlbumImageOrientation(Kernel::HLERequestContext& ctx) {
447 LOG_WARNING(Service_AM, "(STUBBED) called"); 446 LOG_WARNING(Service_AM, "(STUBBED) called");
448 447
449 IPC::ResponseBuilder rb{ctx, 2}; 448 IPC::ResponseBuilder rb{ctx, 2};
@@ -463,6 +462,24 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
463 rb.Push(*layer_id); 462 rb.Push(*layer_id);
464} 463}
465 464
465void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx) {
466 LOG_WARNING(Service_AM, "(STUBBED) called");
467
468 // TODO(Subv): Find out how AM determines the display to use, for now just
469 // create the layer in the Default display.
470 // This calls nn::vi::CreateRecordingLayer() which creates another layer.
471 // Currently we do not support more than 1 layer per display, output 1 layer id for now.
472 // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
473 // side effects.
474 // TODO: Support multiple layers
475 const auto display_id = nvflinger->OpenDisplay("Default");
476 const auto layer_id = nvflinger->CreateLayer(*display_id);
477
478 IPC::ResponseBuilder rb{ctx, 4};
479 rb.Push(RESULT_SUCCESS);
480 rb.Push(*layer_id);
481}
482
466void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) { 483void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) {
467 LOG_WARNING(Service_AM, "(STUBBED) called"); 484 LOG_WARNING(Service_AM, "(STUBBED) called");
468 485
@@ -607,6 +624,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system,
607 {20, nullptr, "PushToGeneralChannel"}, 624 {20, nullptr, "PushToGeneralChannel"},
608 {30, nullptr, "GetHomeButtonReaderLockAccessor"}, 625 {30, nullptr, "GetHomeButtonReaderLockAccessor"},
609 {31, nullptr, "GetReaderLockAccessorEx"}, 626 {31, nullptr, "GetReaderLockAccessorEx"},
627 {32, nullptr, "GetWriterLockAccessorEx"},
610 {40, nullptr, "GetCradleFwVersion"}, 628 {40, nullptr, "GetCradleFwVersion"},
611 {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, 629 {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
612 {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"}, 630 {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
@@ -731,14 +749,14 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
731 749
732 if (Settings::values.use_docked_mode) { 750 if (Settings::values.use_docked_mode) {
733 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * 751 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
734 static_cast<u32>(Settings::values.resolution_factor)); 752 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
735 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * 753 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
736 static_cast<u32>(Settings::values.resolution_factor)); 754 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
737 } else { 755 } else {
738 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) * 756 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
739 static_cast<u32>(Settings::values.resolution_factor)); 757 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
740 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) * 758 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
741 static_cast<u32>(Settings::values.resolution_factor)); 759 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
742 } 760 }
743} 761}
744 762
@@ -842,7 +860,7 @@ public:
842 {110, nullptr, "NeedsToExitProcess"}, 860 {110, nullptr, "NeedsToExitProcess"},
843 {120, nullptr, "GetLibraryAppletInfo"}, 861 {120, nullptr, "GetLibraryAppletInfo"},
844 {150, nullptr, "RequestForAppletToGetForeground"}, 862 {150, nullptr, "RequestForAppletToGetForeground"},
845 {160, nullptr, "GetIndirectLayerConsumerHandle"}, 863 {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"},
846 }; 864 };
847 // clang-format on 865 // clang-format on
848 866
@@ -961,6 +979,18 @@ private:
961 rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent()); 979 rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
962 } 980 }
963 981
982 void GetIndirectLayerConsumerHandle(Kernel::HLERequestContext& ctx) {
983 LOG_WARNING(Service_AM, "(STUBBED) called");
984
985 // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is
986 // actually used anywhere
987 constexpr u64 handle = 0xdeadbeef;
988
989 IPC::ResponseBuilder rb{ctx, 4};
990 rb.Push(RESULT_SUCCESS);
991 rb.Push(handle);
992 }
993
964 std::shared_ptr<Applets::Applet> applet; 994 std::shared_ptr<Applets::Applet> applet;
965}; 995};
966 996
@@ -1132,6 +1162,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1132 {24, nullptr, "GetLaunchStorageInfoForDebug"}, 1162 {24, nullptr, "GetLaunchStorageInfoForDebug"},
1133 {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, 1163 {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
1134 {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, 1164 {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
1165 {27, nullptr, "CreateCacheStorage"},
1135 {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, 1166 {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
1136 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, 1167 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
1137 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, 1168 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
@@ -1157,6 +1188,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1157 {120, nullptr, "ExecuteProgram"}, 1188 {120, nullptr, "ExecuteProgram"},
1158 {121, nullptr, "ClearUserChannel"}, 1189 {121, nullptr, "ClearUserChannel"},
1159 {122, nullptr, "UnpopToUserChannel"}, 1190 {122, nullptr, "UnpopToUserChannel"},
1191 {123, nullptr, "GetPreviousProgramIndex"},
1192 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
1160 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, 1193 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
1161 {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, 1194 {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
1162 {141, nullptr, "TryPopFromFriendInvitationStorageChannel"}, 1195 {141, nullptr, "TryPopFromFriendInvitationStorageChannel"},
@@ -1339,14 +1372,25 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
1339 1372
1340 std::array<u8, 0x10> version_string{}; 1373 std::array<u8, 0x10> version_string{};
1341 1374
1342 FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; 1375 const auto res = [this] {
1343 const auto res = pm.GetControlMetadata(); 1376 const auto title_id = system.CurrentProcess()->GetTitleID();
1377
1378 FileSys::PatchManager pm{title_id};
1379 auto res = pm.GetControlMetadata();
1380 if (res.first != nullptr) {
1381 return res;
1382 }
1383
1384 FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
1385 return pm_update.GetControlMetadata();
1386 }();
1387
1344 if (res.first != nullptr) { 1388 if (res.first != nullptr) {
1345 const auto& version = res.first->GetVersionString(); 1389 const auto& version = res.first->GetVersionString();
1346 std::copy(version.begin(), version.end(), version_string.begin()); 1390 std::copy(version.begin(), version.end(), version_string.begin());
1347 } else { 1391 } else {
1348 constexpr u128 default_version = {1, 0}; 1392 constexpr char default_version[]{"1.0.0"};
1349 std::memcpy(version_string.data(), default_version.data(), sizeof(u128)); 1393 std::memcpy(version_string.data(), default_version, sizeof(default_version));
1350 } 1394 }
1351 1395
1352 IPC::ResponseBuilder rb{ctx, 6}; 1396 IPC::ResponseBuilder rb{ctx, 6};
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 469f7f814..6cfb11b48 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -138,8 +138,9 @@ private:
138 void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); 138 void SetFocusHandlingMode(Kernel::HLERequestContext& ctx);
139 void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx); 139 void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx);
140 void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx); 140 void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
141 void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx); 141 void SetAlbumImageOrientation(Kernel::HLERequestContext& ctx);
142 void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); 142 void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
143 void CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx);
143 void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); 144 void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
144 void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); 145 void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
145 void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); 146 void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index 54e63c138..fbe3686ae 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -30,7 +30,7 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
30 config.sub_text.size()); 30 config.sub_text.size());
31 params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(), 31 params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(),
32 config.guide_text.size()); 32 config.guide_text.size());
33 params.initial_text = initial_text; 33 params.initial_text = std::move(initial_text);
34 params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit; 34 params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit;
35 params.password = static_cast<bool>(config.is_password); 35 params.password = static_cast<bool>(config.is_password);
36 params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position); 36 params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position);
@@ -60,7 +60,7 @@ void SoftwareKeyboard::Initialize() {
60 std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); 60 std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
61 61
62 const auto work_buffer_storage = broker.PopNormalDataToApplet(); 62 const auto work_buffer_storage = broker.PopNormalDataToApplet();
63 ASSERT(work_buffer_storage != nullptr); 63 ASSERT_OR_EXECUTE(work_buffer_storage != nullptr, { return; });
64 const auto& work_buffer = work_buffer_storage->GetData(); 64 const auto& work_buffer = work_buffer_storage->GetData();
65 65
66 if (config.initial_string_size == 0) 66 if (config.initial_string_size == 0)
@@ -109,7 +109,7 @@ void SoftwareKeyboard::Execute() {
109 109
110 const auto parameters = ConvertToFrontendParameters(config, initial_text); 110 const auto parameters = ConvertToFrontendParameters(config, initial_text);
111 111
112 frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); }, 112 frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(std::move(text)); },
113 parameters); 113 parameters);
114} 114}
115 115
diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp
index 003ee8667..f27729ce7 100644
--- a/src/core/hle/service/am/spsm.cpp
+++ b/src/core/hle/service/am/spsm.cpp
@@ -10,17 +10,17 @@ SPSM::SPSM() : ServiceFramework{"spsm"} {
10 // clang-format off 10 // clang-format off
11 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
12 {0, nullptr, "GetState"}, 12 {0, nullptr, "GetState"},
13 {1, nullptr, "SleepSystemAndWaitAwake"}, 13 {1, nullptr, "EnterSleep"},
14 {2, nullptr, "Unknown1"}, 14 {2, nullptr, "GetLastWakeReason"},
15 {3, nullptr, "Unknown2"}, 15 {3, nullptr, "Shutdown"},
16 {4, nullptr, "GetNotificationMessageEventHandle"}, 16 {4, nullptr, "GetNotificationMessageEventHandle"},
17 {5, nullptr, "Unknown3"}, 17 {5, nullptr, "ReceiveNotificationMessage"},
18 {6, nullptr, "Unknown4"}, 18 {6, nullptr, "AnalyzeLogForLastSleepWakeSequence"},
19 {7, nullptr, "Unknown5"}, 19 {7, nullptr, "ResetEventLog"},
20 {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"}, 20 {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"},
21 {9, nullptr, "ChangeHomeButtonLongPressingTime"}, 21 {9, nullptr, "ChangeHomeButtonLongPressingTime"},
22 {10, nullptr, "Unknown6"}, 22 {10, nullptr, "PutErrorState"},
23 {11, nullptr, "Unknown7"}, 23 {11, nullptr, "InvalidateCurrentHomeButtonPressing"},
24 }; 24 };
25 // clang-format on 25 // clang-format on
26 26
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 4227a4adf..8e79f707b 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -60,6 +60,7 @@ AOC_U::AOC_U(Core::System& system)
60 {6, nullptr, "PrepareAddOnContentByApplicationId"}, 60 {6, nullptr, "PrepareAddOnContentByApplicationId"},
61 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, 61 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
62 {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, 62 {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
63 {9, nullptr, "GetAddOnContentLostErrorCode"},
63 {100, nullptr, "CreateEcPurchasedEventManager"}, 64 {100, nullptr, "CreateEcPurchasedEventManager"},
64 {101, nullptr, "CreatePermanentEcPurchasedEventManager"}, 65 {101, nullptr, "CreatePermanentEcPurchasedEventManager"},
65 }; 66 };
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp
index 8bb2528c9..b31766212 100644
--- a/src/core/hle/service/bcat/bcat.cpp
+++ b/src/core/hle/service/bcat/bcat.cpp
@@ -14,6 +14,8 @@ BCAT::BCAT(Core::System& system, std::shared_ptr<Module> module,
14 {0, &BCAT::CreateBcatService, "CreateBcatService"}, 14 {0, &BCAT::CreateBcatService, "CreateBcatService"},
15 {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"}, 15 {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"},
16 {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"}, 16 {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"},
17 {3, nullptr, "CreateDeliveryCacheProgressService"},
18 {4, nullptr, "CreateDeliveryCacheProgressServiceWithApplicationId"},
17 }; 19 };
18 // clang-format on 20 // clang-format on
19 RegisterHandlers(functions); 21 RegisterHandlers(functions);
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index 34aba7a27..603b64d4f 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -143,10 +143,13 @@ public:
143 {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, 143 {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
144 {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, 144 {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
145 {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, 145 {30100, &IBcatService::SetPassphrase, "SetPassphrase"},
146 {30101, nullptr, "Unknown"},
147 {30102, nullptr, "Unknown2"},
146 {30200, nullptr, "RegisterBackgroundDeliveryTask"}, 148 {30200, nullptr, "RegisterBackgroundDeliveryTask"},
147 {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, 149 {30201, nullptr, "UnregisterBackgroundDeliveryTask"},
148 {30202, nullptr, "BlockDeliveryTask"}, 150 {30202, nullptr, "BlockDeliveryTask"},
149 {30203, nullptr, "UnblockDeliveryTask"}, 151 {30203, nullptr, "UnblockDeliveryTask"},
152 {30210, nullptr, "SetDeliveryTaskTimer"},
150 {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"}, 153 {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"},
151 {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, 154 {90100, nullptr, "EnumerateBackgroundDeliveryTask"},
152 {90200, nullptr, "GetDeliveryList"}, 155 {90200, nullptr, "GetDeliveryList"},
diff --git a/src/core/hle/service/bpc/bpc.cpp b/src/core/hle/service/bpc/bpc.cpp
index 1c1ecdb60..fac6b2f9c 100644
--- a/src/core/hle/service/bpc/bpc.cpp
+++ b/src/core/hle/service/bpc/bpc.cpp
@@ -23,9 +23,14 @@ public:
23 {5, nullptr, "GetBoardPowerControlEvent"}, 23 {5, nullptr, "GetBoardPowerControlEvent"},
24 {6, nullptr, "GetSleepButtonState"}, 24 {6, nullptr, "GetSleepButtonState"},
25 {7, nullptr, "GetPowerEvent"}, 25 {7, nullptr, "GetPowerEvent"},
26 {8, nullptr, "Unknown1"}, 26 {8, nullptr, "CreateWakeupTimer"},
27 {9, nullptr, "Unknown2"}, 27 {9, nullptr, "CancelWakeupTimer"},
28 {10, nullptr, "Unknown3"}, 28 {10, nullptr, "EnableWakeupTimerOnDevice"},
29 {11, nullptr, "CreateWakeupTimerEx"},
30 {12, nullptr, "GetLastEnabledWakeupTimerType"},
31 {13, nullptr, "CleanAllWakeupTimers"},
32 {14, nullptr, "Unknown"},
33 {15, nullptr, "Unknown2"},
29 }; 34 };
30 // clang-format on 35 // clang-format on
31 36
@@ -38,10 +43,11 @@ public:
38 explicit BPC_R() : ServiceFramework{"bpc:r"} { 43 explicit BPC_R() : ServiceFramework{"bpc:r"} {
39 // clang-format off 44 // clang-format off
40 static const FunctionInfo functions[] = { 45 static const FunctionInfo functions[] = {
41 {0, nullptr, "GetExternalRtcValue"}, 46 {0, nullptr, "GetRtcTime"},
42 {1, nullptr, "SetExternalRtcValue"}, 47 {1, nullptr, "SetRtcTime"},
43 {2, nullptr, "ReadExternalRtcResetFlag"}, 48 {2, nullptr, "GetRtcResetDetected"},
44 {3, nullptr, "ClearExternalRtcResetFlag"}, 49 {3, nullptr, "ClearRtcResetDetected"},
50 {4, nullptr, "SetUpRtcResetOnShutdown"},
45 }; 51 };
46 // clang-format on 52 // clang-format on
47 53
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index 40a06c9fd..f311afa2f 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -58,102 +58,103 @@ public:
58 {1, nullptr, "InitializeBluetooth"}, 58 {1, nullptr, "InitializeBluetooth"},
59 {2, nullptr, "EnableBluetooth"}, 59 {2, nullptr, "EnableBluetooth"},
60 {3, nullptr, "DisableBluetooth"}, 60 {3, nullptr, "DisableBluetooth"},
61 {4, nullptr, "CleanupBluetooth"}, 61 {4, nullptr, "FinalizeBluetooth"},
62 {5, nullptr, "GetAdapterProperties"}, 62 {5, nullptr, "GetAdapterProperties"},
63 {6, nullptr, "GetAdapterProperty"}, 63 {6, nullptr, "GetAdapterProperty"},
64 {7, nullptr, "SetAdapterProperty"}, 64 {7, nullptr, "SetAdapterProperty"},
65 {8, nullptr, "StartDiscovery"}, 65 {8, nullptr, "StartInquiry"},
66 {9, nullptr, "CancelDiscovery"}, 66 {9, nullptr, "StopInquiry"},
67 {10, nullptr, "CreateBond"}, 67 {10, nullptr, "CreateBond"},
68 {11, nullptr, "RemoveBond"}, 68 {11, nullptr, "RemoveBond"},
69 {12, nullptr, "CancelBond"}, 69 {12, nullptr, "CancelBond"},
70 {13, nullptr, "PinReply"}, 70 {13, nullptr, "RespondToPinRequest"},
71 {14, nullptr, "SspReply"}, 71 {14, nullptr, "RespondToSspRequest"},
72 {15, nullptr, "GetEventInfo"}, 72 {15, nullptr, "GetEventInfo"},
73 {16, nullptr, "InitializeHid"}, 73 {16, nullptr, "InitializeHid"},
74 {17, nullptr, "HidConnect"}, 74 {17, nullptr, "OpenHidConnection"},
75 {18, nullptr, "HidDisconnect"}, 75 {18, nullptr, "CloseHidConnection"},
76 {19, nullptr, "HidSendData"}, 76 {19, nullptr, "WriteHidData"},
77 {20, nullptr, "HidSendData2"}, 77 {20, nullptr, "WriteHidData2"},
78 {21, nullptr, "HidSetReport"}, 78 {21, nullptr, "SetHidReport"},
79 {22, nullptr, "HidGetReport"}, 79 {22, nullptr, "GetHidReport"},
80 {23, nullptr, "HidWakeController"}, 80 {23, nullptr, "TriggerConnection"},
81 {24, nullptr, "HidAddPairedDevice"}, 81 {24, nullptr, "AddPairedDeviceInfo"},
82 {25, nullptr, "HidGetPairedDevice"}, 82 {25, nullptr, "GetPairedDeviceInfo"},
83 {26, nullptr, "CleanupHid"}, 83 {26, nullptr, "FinalizeHid"},
84 {27, nullptr, "HidGetEventInfo"}, 84 {27, nullptr, "GetHidEventInfo"},
85 {28, nullptr, "ExtSetTsi"}, 85 {28, nullptr, "SetTsi"},
86 {29, nullptr, "ExtSetBurstMode"}, 86 {29, nullptr, "EnableBurstMode"},
87 {30, nullptr, "ExtSetZeroRetran"}, 87 {30, nullptr, "SetZeroRetransmission"},
88 {31, nullptr, "ExtSetMcMode"}, 88 {31, nullptr, "EnableMcMode"},
89 {32, nullptr, "ExtStartLlrMode"}, 89 {32, nullptr, "EnableLlrScan"},
90 {33, nullptr, "ExtExitLlrMode"}, 90 {33, nullptr, "DisableLlrScan"},
91 {34, nullptr, "ExtSetRadio"}, 91 {34, nullptr, "EnableRadio"},
92 {35, nullptr, "ExtSetVisibility"}, 92 {35, nullptr, "SetVisibility"},
93 {36, nullptr, "ExtSetTbfcScan"}, 93 {36, nullptr, "EnableTbfcScan"},
94 {37, nullptr, "RegisterHidReportEvent"}, 94 {37, nullptr, "RegisterHidReportEvent"},
95 {38, nullptr, "HidGetReportEventInfo"}, 95 {38, nullptr, "GetHidReportEventInfo"},
96 {39, nullptr, "GetLatestPlr"}, 96 {39, nullptr, "GetLatestPlr"},
97 {40, nullptr, "ExtGetPendingConnections"}, 97 {40, nullptr, "GetPendingConnections"},
98 {41, nullptr, "GetChannelMap"}, 98 {41, nullptr, "GetChannelMap"},
99 {42, nullptr, "EnableBluetoothBoostSetting"}, 99 {42, nullptr, "EnableTxPowerBoostSetting"},
100 {43, nullptr, "IsBluetoothBoostSettingEnabled"}, 100 {43, nullptr, "IsTxPowerBoostSettingEnabled"},
101 {44, nullptr, "EnableBluetoothAfhSetting"}, 101 {44, nullptr, "EnableAfhSetting"},
102 {45, nullptr, "IsBluetoothAfhSettingEnabled"}, 102 {45, nullptr, "IsAfhSettingEnabled"},
103 {46, nullptr, "InitializeBluetoothLe"}, 103 {46, nullptr, "InitializeBle"},
104 {47, nullptr, "EnableBluetoothLe"}, 104 {47, nullptr, "EnableBle"},
105 {48, nullptr, "DisableBluetoothLe"}, 105 {48, nullptr, "DisableBle"},
106 {49, nullptr, "CleanupBluetoothLe"}, 106 {49, nullptr, "FinalizeBle"},
107 {50, nullptr, "SetLeVisibility"}, 107 {50, nullptr, "SetBleVisibility"},
108 {51, nullptr, "SetLeConnectionParameter"}, 108 {51, nullptr, "SetBleConnectionParameter"},
109 {52, nullptr, "SetLeDefaultConnectionParameter"}, 109 {52, nullptr, "SetBleDefaultConnectionParameter"},
110 {53, nullptr, "SetLeAdvertiseData"}, 110 {53, nullptr, "SetBleAdvertiseData"},
111 {54, nullptr, "SetLeAdvertiseParameter"}, 111 {54, nullptr, "SetBleAdvertiseParameter"},
112 {55, nullptr, "StartLeScan"}, 112 {55, nullptr, "StartBleScan"},
113 {56, nullptr, "StopLeScan"}, 113 {56, nullptr, "StopBleScan"},
114 {57, nullptr, "AddLeScanFilterCondition"}, 114 {57, nullptr, "AddBleScanFilterCondition"},
115 {58, nullptr, "DeleteLeScanFilterCondition"}, 115 {58, nullptr, "DeleteBleScanFilterCondition"},
116 {59, nullptr, "DeleteLeScanFilter"}, 116 {59, nullptr, "DeleteBleScanFilter"},
117 {60, nullptr, "ClearLeScanFilters"}, 117 {60, nullptr, "ClearBleScanFilters"},
118 {61, nullptr, "EnableLeScanFilter"}, 118 {61, nullptr, "EnableBleScanFilter"},
119 {62, nullptr, "RegisterLeClient"}, 119 {62, nullptr, "RegisterGattClient"},
120 {63, nullptr, "UnregisterLeClient"}, 120 {63, nullptr, "UnregisterGattClient"},
121 {64, nullptr, "UnregisterLeClientAll"}, 121 {64, nullptr, "UnregisterAllGattClients"},
122 {65, nullptr, "LeClientConnect"}, 122 {65, nullptr, "ConnectGattServer"},
123 {66, nullptr, "LeClientCancelConnection"}, 123 {66, nullptr, "CancelConnectGattServer"},
124 {67, nullptr, "LeClientDisconnect"}, 124 {67, nullptr, "DisconnectGattServer"},
125 {68, nullptr, "LeClientGetAttributes"}, 125 {68, nullptr, "GetGattAttribute"},
126 {69, nullptr, "LeClientDiscoverService"}, 126 {69, nullptr, "GetGattService"},
127 {70, nullptr, "LeClientConfigureMtu"}, 127 {70, nullptr, "ConfigureAttMtu"},
128 {71, nullptr, "RegisterLeServer"}, 128 {71, nullptr, "RegisterGattServer"},
129 {72, nullptr, "UnregisterLeServer"}, 129 {72, nullptr, "UnregisterGattServer"},
130 {73, nullptr, "LeServerConnect"}, 130 {73, nullptr, "ConnectGattClient"},
131 {74, nullptr, "LeServerDisconnect"}, 131 {74, nullptr, "DisconnectGattClient"},
132 {75, nullptr, "CreateLeService"}, 132 {75, nullptr, "AddGattService"},
133 {76, nullptr, "StartLeService"}, 133 {76, nullptr, "EnableGattService"},
134 {77, nullptr, "AddLeCharacteristic"}, 134 {77, nullptr, "AddGattCharacteristic"},
135 {78, nullptr, "AddLeDescriptor"}, 135 {78, nullptr, "AddGattDescriptor"},
136 {79, nullptr, "GetLeCoreEventInfo"}, 136 {79, nullptr, "GetBleManagedEventInfo"},
137 {80, nullptr, "LeGetFirstCharacteristic"}, 137 {80, nullptr, "GetGattFirstCharacteristic"},
138 {81, nullptr, "LeGetNextCharacteristic"}, 138 {81, nullptr, "GetGattNextCharacteristic"},
139 {82, nullptr, "LeGetFirstDescriptor"}, 139 {82, nullptr, "GetGattFirstDescriptor"},
140 {83, nullptr, "LeGetNextDescriptor"}, 140 {83, nullptr, "GetGattNextDescriptor"},
141 {84, nullptr, "RegisterLeCoreDataPath"}, 141 {84, nullptr, "RegisterGattManagedDataPath"},
142 {85, nullptr, "UnregisterLeCoreDataPath"}, 142 {85, nullptr, "UnregisterGattManagedDataPath"},
143 {86, nullptr, "RegisterLeHidDataPath"}, 143 {86, nullptr, "RegisterGattHidDataPath"},
144 {87, nullptr, "UnregisterLeHidDataPath"}, 144 {87, nullptr, "UnregisterGattHidDataPath"},
145 {88, nullptr, "RegisterLeDataPath"}, 145 {88, nullptr, "RegisterGattDataPath"},
146 {89, nullptr, "UnregisterLeDataPath"}, 146 {89, nullptr, "UnregisterGattDataPath"},
147 {90, nullptr, "LeClientReadCharacteristic"}, 147 {90, nullptr, "ReadGattCharacteristic"},
148 {91, nullptr, "LeClientReadDescriptor"}, 148 {91, nullptr, "ReadGattDescriptor"},
149 {92, nullptr, "LeClientWriteCharacteristic"}, 149 {92, nullptr, "WriteGattCharacteristic"},
150 {93, nullptr, "LeClientWriteDescriptor"}, 150 {93, nullptr, "WriteGattDescriptor"},
151 {94, nullptr, "LeClientRegisterNotification"}, 151 {94, nullptr, "RegisterGattNotification"},
152 {95, nullptr, "LeClientDeregisterNotification"}, 152 {95, nullptr, "UnregisterGattNotification"},
153 {96, nullptr, "GetLeHidEventInfo"}, 153 {96, nullptr, "GetLeHidEventInfo"},
154 {97, nullptr, "RegisterBleHidEvent"}, 154 {97, nullptr, "RegisterBleHidEvent"},
155 {98, nullptr, "SetLeScanParameter"}, 155 {98, nullptr, "SetBleScanParameter"},
156 {256, nullptr, "GetIsManufacturingMode"}, 156 {99, nullptr, "MoveToSecondaryPiconet"},
157 {256, nullptr, "IsManufacturingMode"},
157 {257, nullptr, "EmulateBluetoothCrash"}, 158 {257, nullptr, "EmulateBluetoothCrash"},
158 {258, nullptr, "GetBleChannelMap"}, 159 {258, nullptr, "GetBleChannelMap"},
159 }; 160 };
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index 251b3c9df..0d251c6d0 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -132,66 +132,71 @@ public:
132 explicit BTM() : ServiceFramework{"btm"} { 132 explicit BTM() : ServiceFramework{"btm"} {
133 // clang-format off 133 // clang-format off
134 static const FunctionInfo functions[] = { 134 static const FunctionInfo functions[] = {
135 {0, nullptr, "Unknown1"}, 135 {0, nullptr, "GetState"},
136 {1, nullptr, "Unknown2"}, 136 {1, nullptr, "GetHostDeviceProperty"},
137 {2, nullptr, "RegisterSystemEventForConnectedDeviceCondition"}, 137 {2, nullptr, "AcquireDeviceConditionEvent"},
138 {3, nullptr, "Unknown3"}, 138 {3, nullptr, "GetDeviceCondition"},
139 {4, nullptr, "Unknown4"}, 139 {4, nullptr, "SetBurstMode"},
140 {5, nullptr, "Unknown5"}, 140 {5, nullptr, "SetSlotMode"},
141 {6, nullptr, "Unknown6"}, 141 {6, nullptr, "SetBluetoothMode"},
142 {7, nullptr, "Unknown7"}, 142 {7, nullptr, "SetWlanMode"},
143 {8, nullptr, "RegisterSystemEventForRegisteredDeviceInfo"}, 143 {8, nullptr, "AcquireDeviceInfoEvent"},
144 {9, nullptr, "Unknown8"}, 144 {9, nullptr, "GetDeviceInfo"},
145 {10, nullptr, "Unknown9"}, 145 {10, nullptr, "AddDeviceInfo"},
146 {11, nullptr, "Unknown10"}, 146 {11, nullptr, "RemoveDeviceInfo"},
147 {12, nullptr, "Unknown11"}, 147 {12, nullptr, "IncreaseDeviceInfoOrder"},
148 {13, nullptr, "Unknown12"}, 148 {13, nullptr, "LlrNotify"},
149 {14, nullptr, "EnableRadio"}, 149 {14, nullptr, "EnableRadio"},
150 {15, nullptr, "DisableRadio"}, 150 {15, nullptr, "DisableRadio"},
151 {16, nullptr, "Unknown13"}, 151 {16, nullptr, "HidDisconnect"},
152 {17, nullptr, "Unknown14"}, 152 {17, nullptr, "HidSetRetransmissionMode"},
153 {18, nullptr, "Unknown15"}, 153 {18, nullptr, "AcquireAwakeReqEvent"},
154 {19, nullptr, "Unknown16"}, 154 {19, nullptr, "AcquireLlrStateEvent"},
155 {20, nullptr, "Unknown17"}, 155 {20, nullptr, "IsLlrStarted"},
156 {21, nullptr, "Unknown18"}, 156 {21, nullptr, "EnableSlotSaving"},
157 {22, nullptr, "Unknown19"}, 157 {22, nullptr, "ProtectDeviceInfo"},
158 {23, nullptr, "Unknown20"}, 158 {23, nullptr, "AcquireBleScanEvent"},
159 {24, nullptr, "Unknown21"}, 159 {24, nullptr, "GetBleScanParameterGeneral"},
160 {25, nullptr, "Unknown22"}, 160 {25, nullptr, "GetBleScanParameterSmartDevice"},
161 {26, nullptr, "Unknown23"}, 161 {26, nullptr, "StartBleScanForGeneral"},
162 {27, nullptr, "Unknown24"}, 162 {27, nullptr, "StopBleScanForGeneral"},
163 {28, nullptr, "Unknown25"}, 163 {28, nullptr, "GetBleScanResultsForGeneral"},
164 {29, nullptr, "Unknown26"}, 164 {29, nullptr, "StartBleScanForPairedDevice"},
165 {30, nullptr, "Unknown27"}, 165 {30, nullptr, "StopBleScanForPairedDevice"},
166 {31, nullptr, "Unknown28"}, 166 {31, nullptr, "StartBleScanForSmartDevice"},
167 {32, nullptr, "Unknown29"}, 167 {32, nullptr, "StopBleScanForSmartDevice"},
168 {33, nullptr, "Unknown30"}, 168 {33, nullptr, "GetBleScanResultsForSmartDevice"},
169 {34, nullptr, "Unknown31"}, 169 {34, nullptr, "AcquireBleConnectionEvent"},
170 {35, nullptr, "Unknown32"}, 170 {35, nullptr, "BleConnect"},
171 {36, nullptr, "Unknown33"}, 171 {36, nullptr, "BleOverrideConnection"},
172 {37, nullptr, "Unknown34"}, 172 {37, nullptr, "BleDisconnect"},
173 {38, nullptr, "Unknown35"}, 173 {38, nullptr, "BleGetConnectionState"},
174 {39, nullptr, "Unknown36"}, 174 {39, nullptr, "BleGetGattClientConditionList"},
175 {40, nullptr, "Unknown37"}, 175 {40, nullptr, "AcquireBlePairingEvent"},
176 {41, nullptr, "Unknown38"}, 176 {41, nullptr, "BlePairDevice"},
177 {42, nullptr, "Unknown39"}, 177 {42, nullptr, "BleUnpairDeviceOnBoth"},
178 {43, nullptr, "Unknown40"}, 178 {43, nullptr, "BleUnpairDevice"},
179 {44, nullptr, "Unknown41"}, 179 {44, nullptr, "BleGetPairedAddresses"},
180 {45, nullptr, "Unknown42"}, 180 {45, nullptr, "AcquireBleServiceDiscoveryEvent"},
181 {46, nullptr, "Unknown43"}, 181 {46, nullptr, "GetGattServices"},
182 {47, nullptr, "Unknown44"}, 182 {47, nullptr, "GetGattService"},
183 {48, nullptr, "Unknown45"}, 183 {48, nullptr, "GetGattIncludedServices"},
184 {49, nullptr, "Unknown46"}, 184 {49, nullptr, "GetBelongingService"},
185 {50, nullptr, "Unknown47"}, 185 {50, nullptr, "GetGattCharacteristics"},
186 {51, nullptr, "Unknown48"}, 186 {51, nullptr, "GetGattDescriptors"},
187 {52, nullptr, "Unknown49"}, 187 {52, nullptr, "AcquireBleMtuConfigEvent"},
188 {53, nullptr, "Unknown50"}, 188 {53, nullptr, "ConfigureBleMtu"},
189 {54, nullptr, "Unknown51"}, 189 {54, nullptr, "GetBleMtu"},
190 {55, nullptr, "Unknown52"}, 190 {55, nullptr, "RegisterBleGattDataPath"},
191 {56, nullptr, "Unknown53"}, 191 {56, nullptr, "UnregisterBleGattDataPath"},
192 {57, nullptr, "Unknown54"}, 192 {57, nullptr, "RegisterAppletResourceUserId"},
193 {58, nullptr, "Unknown55"}, 193 {58, nullptr, "UnregisterAppletResourceUserId"},
194 {59, nullptr, "Unknown56"}, 194 {59, nullptr, "SetAppletResourceUserId"},
195 {60, nullptr, "Unknown60"},
196 {61, nullptr, "Unknown61"},
197 {62, nullptr, "Unknown62"},
198 {63, nullptr, "Unknown63"},
199 {64, nullptr, "Unknown64"},
195 }; 200 };
196 // clang-format on 201 // clang-format on
197 202
@@ -204,19 +209,19 @@ public:
204 explicit BTM_DBG() : ServiceFramework{"btm:dbg"} { 209 explicit BTM_DBG() : ServiceFramework{"btm:dbg"} {
205 // clang-format off 210 // clang-format off
206 static const FunctionInfo functions[] = { 211 static const FunctionInfo functions[] = {
207 {0, nullptr, "RegisterSystemEventForDiscovery"}, 212 {0, nullptr, "AcquireDiscoveryEvent"},
208 {1, nullptr, "Unknown1"}, 213 {1, nullptr, "StartDiscovery"},
209 {2, nullptr, "Unknown2"}, 214 {2, nullptr, "CancelDiscovery"},
210 {3, nullptr, "Unknown3"}, 215 {3, nullptr, "GetDeviceProperty"},
211 {4, nullptr, "Unknown4"}, 216 {4, nullptr, "CreateBond"},
212 {5, nullptr, "Unknown5"}, 217 {5, nullptr, "CancelBond"},
213 {6, nullptr, "Unknown6"}, 218 {6, nullptr, "SetTsiMode"},
214 {7, nullptr, "Unknown7"}, 219 {7, nullptr, "GeneralTest"},
215 {8, nullptr, "Unknown8"}, 220 {8, nullptr, "HidConnect"},
216 {9, nullptr, "Unknown9"}, 221 {9, nullptr, "GeneralGet"},
217 {10, nullptr, "Unknown10"}, 222 {10, nullptr, "GetGattClientDisconnectionReason"},
218 {11, nullptr, "Unknown11"}, 223 {11, nullptr, "GetBleConnectionParameter"},
219 {12, nullptr, "Unknown11"}, 224 {12, nullptr, "GetBleConnectionParameterRequest"},
220 }; 225 };
221 // clang-format on 226 // clang-format on
222 227
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp
index 26c8a7081..ba5749b84 100644
--- a/src/core/hle/service/caps/caps.cpp
+++ b/src/core/hle/service/caps/caps.cpp
@@ -1,4 +1,4 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h
index fc70a4c27..b8c67b6e2 100644
--- a/src/core/hle/service/caps/caps.h
+++ b/src/core/hle/service/caps/caps.h
@@ -1,4 +1,4 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
@@ -12,73 +12,79 @@ class ServiceManager;
12 12
13namespace Service::Capture { 13namespace Service::Capture {
14 14
15enum AlbumImageOrientation { 15enum class AlbumImageOrientation {
16 Orientation0 = 0, 16 Orientation0 = 0,
17 Orientation1 = 1, 17 Orientation1 = 1,
18 Orientation2 = 2, 18 Orientation2 = 2,
19 Orientation3 = 3, 19 Orientation3 = 3,
20}; 20};
21 21
22enum AlbumReportOption { 22enum class AlbumReportOption {
23 Disable = 0, 23 Disable = 0,
24 Enable = 1, 24 Enable = 1,
25}; 25};
26 26
27enum ContentType : u8 { 27enum class ContentType : u8 {
28 Screenshot = 0, 28 Screenshot = 0,
29 Movie = 1, 29 Movie = 1,
30 ExtraMovie = 3, 30 ExtraMovie = 3,
31}; 31};
32 32
33enum AlbumStorage : u8 { 33enum class AlbumStorage : u8 {
34 NAND = 0, 34 NAND = 0,
35 SD = 1, 35 SD = 1,
36}; 36};
37 37
38struct AlbumFileDateTime { 38struct AlbumFileDateTime {
39 u16 year; 39 s16 year{};
40 u8 month; 40 s8 month{};
41 u8 day; 41 s8 day{};
42 u8 hour; 42 s8 hour{};
43 u8 minute; 43 s8 minute{};
44 u8 second; 44 s8 second{};
45 u8 uid; 45 s8 uid{};
46}; 46};
47static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size.");
47 48
48struct AlbumEntry { 49struct AlbumEntry {
49 u64 size; 50 u64 size{};
50 u64 application_id; 51 u64 application_id{};
51 AlbumFileDateTime datetime; 52 AlbumFileDateTime datetime{};
52 AlbumStorage storage; 53 AlbumStorage storage{};
53 ContentType content; 54 ContentType content{};
54 u8 padding[6]; 55 INSERT_PADDING_BYTES(6);
55}; 56};
57static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size.");
56 58
57struct AlbumFileEntry { 59struct AlbumFileEntry {
58 u64 size; 60 u64 size{}; // Size of the entry
59 u64 hash; 61 u64 hash{}; // AES256 with hardcoded key over AlbumEntry
60 AlbumFileDateTime datetime; 62 AlbumFileDateTime datetime{};
61 AlbumStorage storage; 63 AlbumStorage storage{};
62 ContentType content; 64 ContentType content{};
63 u8 padding[5]; 65 INSERT_PADDING_BYTES(5);
64 u8 unknown; 66 u8 unknown{1}; // Set to 1 on official SW
65}; 67};
68static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size.");
66 69
67struct ApplicationAlbumEntry { 70struct ApplicationAlbumEntry {
68 u64 size; 71 u64 size{}; // Size of the entry
69 u64 hash; 72 u64 hash{}; // AES256 with hardcoded key over AlbumEntry
70 AlbumFileDateTime datetime; 73 AlbumFileDateTime datetime{};
71 AlbumStorage storage; 74 AlbumStorage storage{};
72 ContentType content; 75 ContentType content{};
73 u8 padding[5]; 76 INSERT_PADDING_BYTES(5);
74 u8 unknown; 77 u8 unknown{1}; // Set to 1 on official SW
75}; 78};
79static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size.");
76 80
77struct ApplicationAlbumFileEntry { 81struct ApplicationAlbumFileEntry {
78 ApplicationAlbumEntry entry; 82 ApplicationAlbumEntry entry{};
79 AlbumFileDateTime datetime; 83 AlbumFileDateTime datetime{};
80 u64 unknown; 84 u64 unknown{};
81}; 85};
86static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30,
87 "ApplicationAlbumFileEntry has incorrect size.");
82 88
83/// Registers all Capture services with the specified service manager. 89/// Registers all Capture services with the specified service manager.
84void InstallInterfaces(SM::ServiceManager& sm); 90void InstallInterfaces(SM::ServiceManager& sm);
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp
index 88a3fdc05..a0a3b2ae3 100644
--- a/src/core/hle/service/caps/caps_a.cpp
+++ b/src/core/hle/service/caps/caps_a.cpp
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h
index 8de832491..cb93aad5b 100644
--- a/src/core/hle/service/caps/caps_a.h
+++ b/src/core/hle/service/caps/caps_a.h
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp
index ea6452ffa..ab17a187e 100644
--- a/src/core/hle/service/caps/caps_c.cpp
+++ b/src/core/hle/service/caps/caps_c.cpp
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h
index d07cdb441..a9d028689 100644
--- a/src/core/hle/service/caps/caps_c.h
+++ b/src/core/hle/service/caps/caps_c.h
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp
index d01a8a58e..822ee96c8 100644
--- a/src/core/hle/service/caps/caps_sc.cpp
+++ b/src/core/hle/service/caps/caps_sc.cpp
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h
index 9ba372f7a..ac3e929ca 100644
--- a/src/core/hle/service/caps/caps_sc.h
+++ b/src/core/hle/service/caps/caps_sc.h
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp
index eaa3a7494..24dc716e7 100644
--- a/src/core/hle/service/caps/caps_ss.cpp
+++ b/src/core/hle/service/caps/caps_ss.cpp
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h
index e258a6925..450686e4f 100644
--- a/src/core/hle/service/caps/caps_ss.h
+++ b/src/core/hle/service/caps/caps_ss.h
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp
index e8b0698e8..fffb2ecf9 100644
--- a/src/core/hle/service/caps/caps_su.cpp
+++ b/src/core/hle/service/caps/caps_su.cpp
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h
index c494d7c84..62c9603a9 100644
--- a/src/core/hle/service/caps/caps_su.h
+++ b/src/core/hle/service/caps/caps_su.h
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp
index 78bab6ed8..f36d8de2d 100644
--- a/src/core/hle/service/caps/caps_u.cpp
+++ b/src/core/hle/service/caps/caps_u.cpp
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
@@ -58,19 +58,25 @@ void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& c
58 // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total 58 // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total
59 // output entries (which is copied to a s32 by official SW). 59 // output entries (which is copied to a s32 by official SW).
60 IPC::RequestParser rp{ctx}; 60 IPC::RequestParser rp{ctx};
61 [[maybe_unused]] const auto application_album_file_entries = rp.PopRaw<std::array<u8, 0x30>>(); 61 const auto pid{rp.Pop<s32>()};
62 const auto pid = rp.Pop<s32>(); 62 const auto content_type{rp.PopEnum<ContentType>()};
63 const auto content_type = rp.PopRaw<ContentType>(); 63 const auto start_posix_time{rp.Pop<s64>()};
64 [[maybe_unused]] const auto start_datetime = rp.PopRaw<AlbumFileDateTime>(); 64 const auto end_posix_time{rp.Pop<s64>()};
65 [[maybe_unused]] const auto end_datetime = rp.PopRaw<AlbumFileDateTime>(); 65 const auto applet_resource_user_id{rp.Pop<u64>()};
66 const auto applet_resource_user_id = rp.Pop<u64>(); 66
67 // TODO: Update this when we implement the album.
68 // Currently we do not have a method of accessing album entries, set this to 0 for now.
69 constexpr s32 total_entries{0};
70
67 LOG_WARNING(Service_Capture, 71 LOG_WARNING(Service_Capture,
68 "(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid, 72 "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
69 content_type, applet_resource_user_id); 73 "end_posix_time={}, applet_resource_user_id={}, total_entries={}",
74 pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
75 total_entries);
70 76
71 IPC::ResponseBuilder rb{ctx, 3}; 77 IPC::ResponseBuilder rb{ctx, 3};
72 rb.Push(RESULT_SUCCESS); 78 rb.Push(RESULT_SUCCESS);
73 rb.Push<s32>(0); 79 rb.Push(total_entries);
74} 80}
75 81
76} // namespace Service::Capture 82} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h
index e6e0716ff..689364de4 100644
--- a/src/core/hle/service/caps/caps_u.h
+++ b/src/core/hle/service/caps/caps_u.h
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index f8e9df4b1..a41c73c48 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -27,8 +27,8 @@ public:
27 {8, &ETicket::GetTitleKey, "GetTitleKey"}, 27 {8, &ETicket::GetTitleKey, "GetTitleKey"},
28 {9, &ETicket::CountCommonTicket, "CountCommonTicket"}, 28 {9, &ETicket::CountCommonTicket, "CountCommonTicket"},
29 {10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"}, 29 {10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"},
30 {11, &ETicket::ListCommonTicket, "ListCommonTicket"}, 30 {11, &ETicket::ListCommonTicketRightsIds, "ListCommonTicketRightsIds"},
31 {12, &ETicket::ListPersonalizedTicket, "ListPersonalizedTicket"}, 31 {12, &ETicket::ListPersonalizedTicketRightsIds, "ListPersonalizedTicketRightsIds"},
32 {13, nullptr, "ListMissingPersonalizedTicket"}, 32 {13, nullptr, "ListMissingPersonalizedTicket"},
33 {14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"}, 33 {14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"},
34 {15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"}, 34 {15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"},
@@ -55,7 +55,46 @@ public:
55 {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"}, 55 {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"},
56 {37, nullptr, "OwnTicket2"}, 56 {37, nullptr, "OwnTicket2"},
57 {38, nullptr, "OwnTicket3"}, 57 {38, nullptr, "OwnTicket3"},
58 {501, nullptr, "Unknown501"},
59 {502, nullptr, "Unknown502"},
58 {503, nullptr, "GetTitleKey"}, 60 {503, nullptr, "GetTitleKey"},
61 {504, nullptr, "Unknown504"},
62 {508, nullptr, "Unknown508"},
63 {509, nullptr, "Unknown509"},
64 {510, nullptr, "Unknown510"},
65 {511, nullptr, "Unknown511"},
66 {1001, nullptr, "Unknown1001"},
67 {1002, nullptr, "Unknown1001"},
68 {1003, nullptr, "Unknown1003"},
69 {1004, nullptr, "Unknown1004"},
70 {1005, nullptr, "Unknown1005"},
71 {1006, nullptr, "Unknown1006"},
72 {1007, nullptr, "Unknown1007"},
73 {1009, nullptr, "Unknown1009"},
74 {1010, nullptr, "Unknown1010"},
75 {1011, nullptr, "Unknown1011"},
76 {1012, nullptr, "Unknown1012"},
77 {1013, nullptr, "Unknown1013"},
78 {1014, nullptr, "Unknown1014"},
79 {1015, nullptr, "Unknown1015"},
80 {1016, nullptr, "Unknown1016"},
81 {1017, nullptr, "Unknown1017"},
82 {1018, nullptr, "Unknown1018"},
83 {1019, nullptr, "Unknown1019"},
84 {1020, nullptr, "Unknown1020"},
85 {1021, nullptr, "Unknown1021"},
86 {1501, nullptr, "Unknown1501"},
87 {1502, nullptr, "Unknown1502"},
88 {1503, nullptr, "Unknown1503"},
89 {1504, nullptr, "Unknown1504"},
90 {1505, nullptr, "Unknown1505"},
91 {2000, nullptr, "Unknown2000"},
92 {2001, nullptr, "Unknown2001"},
93 {2100, nullptr, "Unknown2100"},
94 {2501, nullptr, "Unknown2501"},
95 {2502, nullptr, "Unknown2502"},
96 {3001, nullptr, "Unknown3001"},
97 {3002, nullptr, "Unknown3002"},
59 }; 98 };
60 // clang-format on 99 // clang-format on
61 RegisterHandlers(functions); 100 RegisterHandlers(functions);
@@ -147,7 +186,7 @@ private:
147 rb.Push<u32>(count); 186 rb.Push<u32>(count);
148 } 187 }
149 188
150 void ListCommonTicket(Kernel::HLERequestContext& ctx) { 189 void ListCommonTicketRightsIds(Kernel::HLERequestContext& ctx) {
151 u32 out_entries; 190 u32 out_entries;
152 if (keys.GetCommonTickets().empty()) 191 if (keys.GetCommonTickets().empty())
153 out_entries = 0; 192 out_entries = 0;
@@ -170,7 +209,7 @@ private:
170 rb.Push<u32>(out_entries); 209 rb.Push<u32>(out_entries);
171 } 210 }
172 211
173 void ListPersonalizedTicket(Kernel::HLERequestContext& ctx) { 212 void ListPersonalizedTicketRightsIds(Kernel::HLERequestContext& ctx) {
174 u32 out_entries; 213 u32 out_entries;
175 if (keys.GetPersonalizedTickets().empty()) 214 if (keys.GetPersonalizedTickets().empty())
176 out_entries = 0; 215 out_entries = 0;
@@ -263,7 +302,7 @@ private:
263 rb.Push<u64>(write_size); 302 rb.Push<u64>(write_size);
264 } 303 }
265 304
266 Core::Crypto::KeyManager keys; 305 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
267}; 306};
268 307
269void InstallInterfaces(SM::ServiceManager& service_manager) { 308void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/eupld/eupld.cpp b/src/core/hle/service/eupld/eupld.cpp
index 2df30acee..0d6d244f4 100644
--- a/src/core/hle/service/eupld/eupld.cpp
+++ b/src/core/hle/service/eupld/eupld.cpp
@@ -19,6 +19,7 @@ public:
19 {1, nullptr, "ImportCrt"}, 19 {1, nullptr, "ImportCrt"},
20 {2, nullptr, "ImportPki"}, 20 {2, nullptr, "ImportPki"},
21 {3, nullptr, "SetAutoUpload"}, 21 {3, nullptr, "SetAutoUpload"},
22 {4, nullptr, "GetAutoUpload"},
22 }; 23 };
23 // clang-format on 24 // clang-format on
24 25
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 68f259b70..b7adaffc7 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -25,9 +25,13 @@ public:
25 {10101, &IFriendService::GetFriendList, "GetFriendList"}, 25 {10101, &IFriendService::GetFriendList, "GetFriendList"},
26 {10102, nullptr, "UpdateFriendInfo"}, 26 {10102, nullptr, "UpdateFriendInfo"},
27 {10110, nullptr, "GetFriendProfileImage"}, 27 {10110, nullptr, "GetFriendProfileImage"},
28 {10120, nullptr, "Unknown10120"},
29 {10121, nullptr, "Unknown10121"},
28 {10200, nullptr, "SendFriendRequestForApplication"}, 30 {10200, nullptr, "SendFriendRequestForApplication"},
29 {10211, nullptr, "AddFacedFriendRequestForApplication"}, 31 {10211, nullptr, "AddFacedFriendRequestForApplication"},
30 {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"}, 32 {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
33 {10420, nullptr, "Unknown10420"},
34 {10421, nullptr, "Unknown10421"},
31 {10500, nullptr, "GetProfileList"}, 35 {10500, nullptr, "GetProfileList"},
32 {10600, nullptr, "DeclareOpenOnlinePlaySession"}, 36 {10600, nullptr, "DeclareOpenOnlinePlaySession"},
33 {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"}, 37 {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"},
@@ -97,6 +101,8 @@ public:
97 {30900, nullptr, "SendFriendInvitation"}, 101 {30900, nullptr, "SendFriendInvitation"},
98 {30910, nullptr, "ReadFriendInvitation"}, 102 {30910, nullptr, "ReadFriendInvitation"},
99 {30911, nullptr, "ReadAllFriendInvitations"}, 103 {30911, nullptr, "ReadAllFriendInvitations"},
104 {40100, nullptr, "Unknown40100"},
105 {40400, nullptr, "Unknown40400"},
100 {49900, nullptr, "DeleteNetworkServiceAccountCache"}, 106 {49900, nullptr, "DeleteNetworkServiceAccountCache"},
101 }; 107 };
102 // clang-format on 108 // clang-format on
diff --git a/src/core/hle/service/grc/grc.cpp b/src/core/hle/service/grc/grc.cpp
index 24910ac6c..401e0b208 100644
--- a/src/core/hle/service/grc/grc.cpp
+++ b/src/core/hle/service/grc/grc.cpp
@@ -17,6 +17,9 @@ public:
17 static const FunctionInfo functions[] = { 17 static const FunctionInfo functions[] = {
18 {1, nullptr, "OpenContinuousRecorder"}, 18 {1, nullptr, "OpenContinuousRecorder"},
19 {2, nullptr, "OpenGameMovieTrimmer"}, 19 {2, nullptr, "OpenGameMovieTrimmer"},
20 {3, nullptr, "OpenOffscreenRecorder"},
21 {101, nullptr, "CreateMovieMaker"},
22 {9903, nullptr, "SetOffscreenRecordingMarker"}
20 }; 23 };
21 // clang-format on 24 // clang-format on
22 25
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 1f2131ec8..cb35919e9 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -23,7 +23,7 @@ void Controller_DebugPad::OnRelease() {}
23 23
24void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 24void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
25 std::size_t size) { 25 std::size_t size) {
26 shared_memory.header.timestamp = core_timing.GetTicks(); 26 shared_memory.header.timestamp = core_timing.GetCPUTicks();
27 shared_memory.header.total_entry_count = 17; 27 shared_memory.header.total_entry_count = 17;
28 28
29 if (!IsControllerActivated()) { 29 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 6e990dd00..b7b7bfeae 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -19,7 +19,7 @@ void Controller_Gesture::OnRelease() {}
19 19
20void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 20void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 21 std::size_t size) {
22 shared_memory.header.timestamp = core_timing.GetTicks(); 22 shared_memory.header.timestamp = core_timing.GetCPUTicks();
23 shared_memory.header.total_entry_count = 17; 23 shared_memory.header.total_entry_count = 17;
24 24
25 if (!IsControllerActivated()) { 25 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 9a8d354ba..feae89525 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -21,7 +21,7 @@ void Controller_Keyboard::OnRelease() {}
21 21
22void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 22void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
23 std::size_t size) { 23 std::size_t size) {
24 shared_memory.header.timestamp = core_timing.GetTicks(); 24 shared_memory.header.timestamp = core_timing.GetCPUTicks();
25 shared_memory.header.total_entry_count = 17; 25 shared_memory.header.total_entry_count = 17;
26 26
27 if (!IsControllerActivated()) { 27 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 93d88ea50..ac40989c5 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -19,7 +19,7 @@ void Controller_Mouse::OnRelease() {}
19 19
20void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 20void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 21 std::size_t size) {
22 shared_memory.header.timestamp = core_timing.GetTicks(); 22 shared_memory.header.timestamp = core_timing.GetCPUTicks();
23 shared_memory.header.total_entry_count = 17; 23 shared_memory.header.total_entry_count = 17;
24 24
25 if (!IsControllerActivated()) { 25 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index c55d900e2..ef67ad690 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -328,7 +328,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
328 const auto& last_entry = 328 const auto& last_entry =
329 main_controller->npad[main_controller->common.last_entry_index]; 329 main_controller->npad[main_controller->common.last_entry_index];
330 330
331 main_controller->common.timestamp = core_timing.GetTicks(); 331 main_controller->common.timestamp = core_timing.GetCPUTicks();
332 main_controller->common.last_entry_index = 332 main_controller->common.last_entry_index =
333 (main_controller->common.last_entry_index + 1) % 17; 333 (main_controller->common.last_entry_index + 1) % 17;
334 334
@@ -566,6 +566,14 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) {
566 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; 566 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
567} 567}
568 568
569void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
570 gyroscope_zero_drift_mode = drift_mode;
571}
572
573Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode() const {
574 return gyroscope_zero_drift_mode;
575}
576
569void Controller_NPad::StartLRAssignmentMode() { 577void Controller_NPad::StartLRAssignmentMode() {
570 // Nothing internally is used for lr assignment mode. Since we have the ability to set the 578 // Nothing internally is used for lr assignment mode. Since we have the ability to set the
571 // controller types from boot, it doesn't really matter about showing a selection screen 579 // controller types from boot, it doesn't really matter about showing a selection screen
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 931f03430..5d4c58a43 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -58,6 +58,12 @@ public:
58 }; 58 };
59 static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size"); 59 static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
60 60
61 enum class GyroscopeZeroDriftMode : u32 {
62 Loose = 0,
63 Standard = 1,
64 Tight = 2,
65 };
66
61 enum class NpadHoldType : u64 { 67 enum class NpadHoldType : u64 {
62 Vertical = 0, 68 Vertical = 0,
63 Horizontal = 1, 69 Horizontal = 1,
@@ -117,6 +123,8 @@ public:
117 123
118 void ConnectNPad(u32 npad_id); 124 void ConnectNPad(u32 npad_id);
119 void DisconnectNPad(u32 npad_id); 125 void DisconnectNPad(u32 npad_id);
126 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
127 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
120 LedPattern GetLedPattern(u32 npad_id); 128 LedPattern GetLedPattern(u32 npad_id);
121 void SetVibrationEnabled(bool can_vibrate); 129 void SetVibrationEnabled(bool can_vibrate);
122 bool IsVibrationEnabled() const; 130 bool IsVibrationEnabled() const;
@@ -324,8 +332,8 @@ private:
324 std::array<Kernel::EventPair, 10> styleset_changed_events; 332 std::array<Kernel::EventPair, 10> styleset_changed_events;
325 Vibration last_processed_vibration{}; 333 Vibration last_processed_vibration{};
326 std::array<ControllerHolder, 10> connected_controllers{}; 334 std::array<ControllerHolder, 10> connected_controllers{};
335 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
327 bool can_controllers_vibrate{true}; 336 bool can_controllers_vibrate{true};
328
329 std::array<ControllerPad, 10> npad_pad_states{}; 337 std::array<ControllerPad, 10> npad_pad_states{};
330 bool is_in_lr_assignment_mode{false}; 338 bool is_in_lr_assignment_mode{false};
331 Core::System& system; 339 Core::System& system;
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 9e527d176..e7483bfa2 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -23,7 +23,7 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
23 } 23 }
24 24
25 CommonHeader header{}; 25 CommonHeader header{};
26 header.timestamp = core_timing.GetTicks(); 26 header.timestamp = core_timing.GetCPUTicks();
27 header.total_entry_count = 17; 27 header.total_entry_count = 17;
28 header.entry_count = 0; 28 header.entry_count = 0;
29 header.last_entry_index = 0; 29 header.last_entry_index = 0;
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 1c6e55566..e326f8f5c 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -22,7 +22,7 @@ void Controller_Touchscreen::OnRelease() {}
22 22
23void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 23void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
24 std::size_t size) { 24 std::size_t size) {
25 shared_memory.header.timestamp = core_timing.GetTicks(); 25 shared_memory.header.timestamp = core_timing.GetCPUTicks();
26 shared_memory.header.total_entry_count = 17; 26 shared_memory.header.total_entry_count = 17;
27 27
28 if (!IsControllerActivated()) { 28 if (!IsControllerActivated()) {
@@ -49,7 +49,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
49 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; 49 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
50 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; 50 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
51 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; 51 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
52 const u64 tick = core_timing.GetTicks(); 52 const u64 tick = core_timing.GetCPUTicks();
53 touch_entry.delta_time = tick - last_touch; 53 touch_entry.delta_time = tick - last_touch;
54 last_touch = tick; 54 last_touch = tick;
55 touch_entry.finger = Settings::values.touchscreen.finger; 55 touch_entry.finger = Settings::values.touchscreen.finger;
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 27511b27b..2503ef241 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -20,7 +20,7 @@ void Controller_XPad::OnRelease() {}
20void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 20void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 21 std::size_t size) {
22 for (auto& xpad_entry : shared_memory.shared_memory_entries) { 22 for (auto& xpad_entry : shared_memory.shared_memory_entries) {
23 xpad_entry.header.timestamp = core_timing.GetTicks(); 23 xpad_entry.header.timestamp = core_timing.GetCPUTicks();
24 xpad_entry.header.total_entry_count = 17; 24 xpad_entry.header.total_entry_count = 17;
25 25
26 if (!IsControllerActivated()) { 26 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index c84cb1483..e9020e0dc 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -39,11 +39,9 @@ namespace Service::HID {
39 39
40// Updating period for each HID device. 40// Updating period for each HID device.
41// TODO(ogniK): Find actual polling rate of hid 41// TODO(ogniK): Find actual polling rate of hid
42constexpr s64 pad_update_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 66); 42constexpr s64 pad_update_ticks = static_cast<s64>(1000000000 / 66);
43[[maybe_unused]] constexpr s64 accelerometer_update_ticks = 43[[maybe_unused]] constexpr s64 accelerometer_update_ticks = static_cast<s64>(1000000000 / 100);
44 static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100); 44[[maybe_unused]] constexpr s64 gyroscope_update_ticks = static_cast<s64>(1000000000 / 100);
45[[maybe_unused]] constexpr s64 gyroscope_update_ticks =
46 static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100);
47constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 45constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
48 46
49IAppletResource::IAppletResource(Core::System& system) 47IAppletResource::IAppletResource(Core::System& system)
@@ -78,8 +76,8 @@ IAppletResource::IAppletResource(Core::System& system)
78 76
79 // Register update callbacks 77 // Register update callbacks
80 pad_update_event = 78 pad_update_event =
81 Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { 79 Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 ns_late) {
82 UpdateControllers(userdata, cycles_late); 80 UpdateControllers(userdata, ns_late);
83 }); 81 });
84 82
85 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 83 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
@@ -109,7 +107,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
109 rb.PushCopyObjects(shared_mem); 107 rb.PushCopyObjects(shared_mem);
110} 108}
111 109
112void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { 110void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) {
113 auto& core_timing = system.CoreTiming(); 111 auto& core_timing = system.CoreTiming();
114 112
115 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); 113 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
@@ -120,7 +118,7 @@ void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) {
120 controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); 118 controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
121 } 119 }
122 120
123 core_timing.ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); 121 core_timing.ScheduleEvent(pad_update_ticks - ns_late, pad_update_event);
124} 122}
125 123
126class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { 124class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
@@ -161,7 +159,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
161 {40, nullptr, "AcquireXpadIdEventHandle"}, 159 {40, nullptr, "AcquireXpadIdEventHandle"},
162 {41, nullptr, "ReleaseXpadIdEventHandle"}, 160 {41, nullptr, "ReleaseXpadIdEventHandle"},
163 {51, &Hid::ActivateXpad, "ActivateXpad"}, 161 {51, &Hid::ActivateXpad, "ActivateXpad"},
164 {55, nullptr, "GetXpadIds"}, 162 {55, &Hid::GetXpadIDs, "GetXpadIds"},
165 {56, nullptr, "ActivateJoyXpad"}, 163 {56, nullptr, "ActivateJoyXpad"},
166 {58, nullptr, "GetJoyXpadLifoHandle"}, 164 {58, nullptr, "GetJoyXpadLifoHandle"},
167 {59, nullptr, "GetJoyXpadIds"}, 165 {59, nullptr, "GetJoyXpadIds"},
@@ -185,8 +183,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
185 {77, nullptr, "GetAccelerometerPlayMode"}, 183 {77, nullptr, "GetAccelerometerPlayMode"},
186 {78, nullptr, "ResetAccelerometerPlayMode"}, 184 {78, nullptr, "ResetAccelerometerPlayMode"},
187 {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, 185 {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
188 {80, nullptr, "GetGyroscopeZeroDriftMode"}, 186 {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"},
189 {81, nullptr, "ResetGyroscopeZeroDriftMode"}, 187 {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
190 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, 188 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
191 {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"}, 189 {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"},
192 {91, &Hid::ActivateGesture, "ActivateGesture"}, 190 {91, &Hid::ActivateGesture, "ActivateGesture"},
@@ -230,15 +228,15 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
230 {211, nullptr, "IsVibrationDeviceMounted"}, 228 {211, nullptr, "IsVibrationDeviceMounted"},
231 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, 229 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
232 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, 230 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
233 {302, nullptr, "StopConsoleSixAxisSensor"}, 231 {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
234 {303, nullptr, "ActivateSevenSixAxisSensor"}, 232 {303, &Hid::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"},
235 {304, nullptr, "StartSevenSixAxisSensor"}, 233 {304, &Hid::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"},
236 {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, 234 {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"},
237 {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, 235 {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
238 {307, nullptr, "FinalizeSevenSixAxisSensor"}, 236 {307, &Hid::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"},
239 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, 237 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
240 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, 238 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
241 {310, nullptr, "ResetSevenSixAxisSensorTimestamp"}, 239 {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"},
242 {400, nullptr, "IsUsbFullKeyControllerEnabled"}, 240 {400, nullptr, "IsUsbFullKeyControllerEnabled"},
243 {401, nullptr, "EnableUsbFullKeyController"}, 241 {401, nullptr, "EnableUsbFullKeyController"},
244 {402, nullptr, "IsUsbFullKeyControllerConnected"}, 242 {402, nullptr, "IsUsbFullKeyControllerConnected"},
@@ -319,6 +317,17 @@ void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
319 rb.Push(RESULT_SUCCESS); 317 rb.Push(RESULT_SUCCESS);
320} 318}
321 319
320void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
321 IPC::RequestParser rp{ctx};
322 const auto applet_resource_user_id{rp.Pop<u64>()};
323
324 LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id);
325
326 IPC::ResponseBuilder rb{ctx, 3};
327 rb.Push(RESULT_SUCCESS);
328 rb.Push(0);
329}
330
322void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { 331void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) {
323 IPC::RequestParser rp{ctx}; 332 IPC::RequestParser rp{ctx};
324 const auto applet_resource_user_id{rp.Pop<u64>()}; 333 const auto applet_resource_user_id{rp.Pop<u64>()};
@@ -363,6 +372,15 @@ void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) {
363 rb.Push(RESULT_SUCCESS); 372 rb.Push(RESULT_SUCCESS);
364} 373}
365 374
375void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) {
376 IPC::RequestParser rp{ctx};
377 const auto flags{rp.Pop<u32>()};
378 LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags);
379
380 IPC::ResponseBuilder rb{ctx, 2};
381 rb.Push(RESULT_SUCCESS);
382}
383
366void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { 384void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
367 IPC::RequestParser rp{ctx}; 385 IPC::RequestParser rp{ctx};
368 const auto unknown{rp.Pop<u32>()}; 386 const auto unknown{rp.Pop<u32>()};
@@ -402,15 +420,59 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
402 rb.Push(RESULT_SUCCESS); 420 rb.Push(RESULT_SUCCESS);
403} 421}
404 422
423void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
424 IPC::RequestParser rp{ctx};
425 const auto handle{rp.Pop<u32>()};
426 const auto applet_resource_user_id{rp.Pop<u64>()};
427
428 LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
429 applet_resource_user_id);
430
431 IPC::ResponseBuilder rb{ctx, 2};
432 rb.Push(RESULT_SUCCESS);
433}
434
405void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 435void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
406 IPC::RequestParser rp{ctx}; 436 IPC::RequestParser rp{ctx};
407 const auto handle{rp.Pop<u32>()}; 437 const auto handle{rp.Pop<u32>()};
408 const auto drift_mode{rp.Pop<u32>()}; 438 const auto drift_mode{rp.Pop<u32>()};
409 const auto applet_resource_user_id{rp.Pop<u64>()}; 439 const auto applet_resource_user_id{rp.Pop<u64>()};
410 440
411 LOG_WARNING(Service_HID, 441 applet_resource->GetController<Controller_NPad>(HidController::NPad)
412 "(STUBBED) called, handle={}, drift_mode={}, applet_resource_user_id={}", handle, 442 .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode{drift_mode});
413 drift_mode, applet_resource_user_id); 443
444 LOG_DEBUG(Service_HID, "called, handle={}, drift_mode={}, applet_resource_user_id={}", handle,
445 drift_mode, applet_resource_user_id);
446
447 IPC::ResponseBuilder rb{ctx, 2};
448 rb.Push(RESULT_SUCCESS);
449}
450
451void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
452 IPC::RequestParser rp{ctx};
453 const auto handle{rp.Pop<u32>()};
454 const auto applet_resource_user_id{rp.Pop<u64>()};
455
456 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
457 applet_resource_user_id);
458
459 IPC::ResponseBuilder rb{ctx, 3};
460 rb.Push(RESULT_SUCCESS);
461 rb.Push<u32>(
462 static_cast<u32>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
463 .GetGyroscopeZeroDriftMode()));
464}
465
466void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
467 IPC::RequestParser rp{ctx};
468 const auto handle{rp.Pop<u32>()};
469 const auto applet_resource_user_id{rp.Pop<u64>()};
470
471 applet_resource->GetController<Controller_NPad>(HidController::NPad)
472 .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard);
473
474 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
475 applet_resource_user_id);
414 476
415 IPC::ResponseBuilder rb{ctx, 2}; 477 IPC::ResponseBuilder rb{ctx, 2};
416 rb.Push(RESULT_SUCCESS); 478 rb.Push(RESULT_SUCCESS);
@@ -821,33 +883,35 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
821 rb.Push(RESULT_SUCCESS); 883 rb.Push(RESULT_SUCCESS);
822} 884}
823 885
824void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { 886void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
825 IPC::RequestParser rp{ctx}; 887 IPC::RequestParser rp{ctx};
826 const auto handle{rp.Pop<u32>()}; 888 const auto handle{rp.Pop<u32>()};
889 const auto applet_resource_user_id{rp.Pop<u64>()};
827 890
828 LOG_WARNING(Service_HID, "(STUBBED) called, handle={}", handle); 891 LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
892 applet_resource_user_id);
829 893
830 IPC::ResponseBuilder rb{ctx, 2}; 894 IPC::ResponseBuilder rb{ctx, 2};
831 rb.Push(RESULT_SUCCESS); 895 rb.Push(RESULT_SUCCESS);
832} 896}
833 897
834void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { 898void Hid::ActivateSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
835 IPC::RequestParser rp{ctx}; 899 IPC::RequestParser rp{ctx};
836 const auto applet_resource_user_id{rp.Pop<u64>()}; 900 const auto applet_resource_user_id{rp.Pop<u64>()};
837 const auto unknown{rp.Pop<u32>()};
838 901
839 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, unknown={}", 902 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
840 applet_resource_user_id, unknown); 903 applet_resource_user_id);
841 904
842 IPC::ResponseBuilder rb{ctx, 2}; 905 IPC::ResponseBuilder rb{ctx, 2};
843 rb.Push(RESULT_SUCCESS); 906 rb.Push(RESULT_SUCCESS);
844} 907}
845 908
846void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { 909void Hid::StartSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
847 IPC::RequestParser rp{ctx}; 910 IPC::RequestParser rp{ctx};
848 const auto unknown{rp.Pop<u32>()}; 911 const auto applet_resource_user_id{rp.Pop<u64>()};
849 912
850 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}", unknown); 913 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
914 applet_resource_user_id);
851 915
852 IPC::ResponseBuilder rb{ctx, 2}; 916 IPC::ResponseBuilder rb{ctx, 2};
853 rb.Push(RESULT_SUCCESS); 917 rb.Push(RESULT_SUCCESS);
@@ -871,10 +935,46 @@ void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
871 rb.Push(RESULT_SUCCESS); 935 rb.Push(RESULT_SUCCESS);
872} 936}
873 937
874void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) { 938void Hid::FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
875 IPC::RequestParser rp{ctx}; 939 IPC::RequestParser rp{ctx};
876 const auto flags{rp.Pop<u32>()}; 940 const auto applet_resource_user_id{rp.Pop<u64>()};
877 LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); 941
942 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
943 applet_resource_user_id);
944
945 IPC::ResponseBuilder rb{ctx, 2};
946 rb.Push(RESULT_SUCCESS);
947}
948
949void Hid::ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx) {
950 IPC::RequestParser rp{ctx};
951 const auto applet_resource_user_id{rp.Pop<u64>()};
952
953 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
954 applet_resource_user_id);
955
956 IPC::ResponseBuilder rb{ctx, 2};
957 rb.Push(RESULT_SUCCESS);
958}
959
960void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
961 IPC::RequestParser rp{ctx};
962 const auto applet_resource_user_id{rp.Pop<u64>()};
963 const auto is_palma_all_connectable{rp.Pop<bool>()};
964
965 LOG_WARNING(Service_HID,
966 "(STUBBED) called, applet_resource_user_id={}, is_palma_all_connectable={}",
967 applet_resource_user_id, is_palma_all_connectable);
968
969 IPC::ResponseBuilder rb{ctx, 2};
970 rb.Push(RESULT_SUCCESS);
971}
972
973void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
974 IPC::RequestParser rp{ctx};
975 const auto palma_boost_mode{rp.Pop<bool>()};
976
977 LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode);
878 978
879 IPC::ResponseBuilder rb{ctx, 2}; 979 IPC::ResponseBuilder rb{ctx, 2};
880 rb.Push(RESULT_SUCCESS); 980 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index c8ed4ad8b..6fb048360 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -86,14 +86,19 @@ public:
86private: 86private:
87 void CreateAppletResource(Kernel::HLERequestContext& ctx); 87 void CreateAppletResource(Kernel::HLERequestContext& ctx);
88 void ActivateXpad(Kernel::HLERequestContext& ctx); 88 void ActivateXpad(Kernel::HLERequestContext& ctx);
89 void GetXpadIDs(Kernel::HLERequestContext& ctx);
89 void ActivateDebugPad(Kernel::HLERequestContext& ctx); 90 void ActivateDebugPad(Kernel::HLERequestContext& ctx);
90 void ActivateTouchScreen(Kernel::HLERequestContext& ctx); 91 void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
91 void ActivateMouse(Kernel::HLERequestContext& ctx); 92 void ActivateMouse(Kernel::HLERequestContext& ctx);
92 void ActivateKeyboard(Kernel::HLERequestContext& ctx); 93 void ActivateKeyboard(Kernel::HLERequestContext& ctx);
94 void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx);
93 void ActivateGesture(Kernel::HLERequestContext& ctx); 95 void ActivateGesture(Kernel::HLERequestContext& ctx);
94 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); 96 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
95 void StartSixAxisSensor(Kernel::HLERequestContext& ctx); 97 void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
98 void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
96 void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); 99 void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
100 void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
101 void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
97 void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); 102 void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
98 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); 103 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
99 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); 104 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
@@ -125,12 +130,15 @@ private:
125 void IsVibrationPermitted(Kernel::HLERequestContext& ctx); 130 void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
126 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); 131 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
127 void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); 132 void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
128 void StopSixAxisSensor(Kernel::HLERequestContext& ctx); 133 void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
129 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); 134 void ActivateSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
130 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); 135 void StartSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
131 void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx); 136 void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
132 void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); 137 void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
133 void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx); 138 void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
139 void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx);
140 void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
141 void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
134 142
135 std::shared_ptr<IAppletResource> applet_resource; 143 std::shared_ptr<IAppletResource> applet_resource;
136 Core::System& system; 144 Core::System& system;
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 36ed6f7da..e82fd031b 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
98 98
99 IPC::ResponseBuilder rb{ctx, 5}; 99 IPC::ResponseBuilder rb{ctx, 5};
100 rb.Push(RESULT_SUCCESS); 100 rb.Push(RESULT_SUCCESS);
101 rb.PushRaw<u64>(system.CoreTiming().GetTicks()); 101 rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks());
102 rb.PushRaw<u32>(0); 102 rb.PushRaw<u32>(0);
103} 103}
104 104
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp
index e8f9f2d29..17350b403 100644
--- a/src/core/hle/service/lbl/lbl.cpp
+++ b/src/core/hle/service/lbl/lbl.cpp
@@ -47,6 +47,7 @@ public:
47 {26, &LBL::EnableVrMode, "EnableVrMode"}, 47 {26, &LBL::EnableVrMode, "EnableVrMode"},
48 {27, &LBL::DisableVrMode, "DisableVrMode"}, 48 {27, &LBL::DisableVrMode, "DisableVrMode"},
49 {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"}, 49 {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"},
50 {29, nullptr, "IsAutoBrightnessControlSupported"},
50 }; 51 };
51 // clang-format on 52 // clang-format on
52 53
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 92adde6d4..49972cd69 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -69,6 +69,7 @@ public:
69 {101, nullptr, "GetNetworkInfoLatestUpdate"}, 69 {101, nullptr, "GetNetworkInfoLatestUpdate"},
70 {102, nullptr, "Scan"}, 70 {102, nullptr, "Scan"},
71 {103, nullptr, "ScanPrivate"}, 71 {103, nullptr, "ScanPrivate"},
72 {104, nullptr, "SetWirelessControllerRestriction"},
72 {200, nullptr, "OpenAccessPoint"}, 73 {200, nullptr, "OpenAccessPoint"},
73 {201, nullptr, "CloseAccessPoint"}, 74 {201, nullptr, "CloseAccessPoint"},
74 {202, nullptr, "CreateNetwork"}, 75 {202, nullptr, "CreateNetwork"},
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 6ad3be1b3..64a526b9e 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -39,42 +39,61 @@ constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
39constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; 39constexpr std::size_t MAXIMUM_LOADED_RO{0x40};
40constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; 40constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200};
41 41
42constexpr std::size_t TEXT_INDEX{0};
43constexpr std::size_t RO_INDEX{1};
44constexpr std::size_t DATA_INDEX{2};
45
46struct NRRCertification {
47 u64_le application_id_mask;
48 u64_le application_id_pattern;
49 INSERT_PADDING_BYTES(0x10);
50 std::array<u8, 0x100> public_key; // Also known as modulus
51 std::array<u8, 0x100> signature;
52};
53static_assert(sizeof(NRRCertification) == 0x220, "NRRCertification has invalid size.");
54
42struct NRRHeader { 55struct NRRHeader {
43 u32_le magic; 56 u32_le magic;
44 INSERT_PADDING_BYTES(12); 57 u32_le certification_signature_key_generation; // 9.0.0+
45 u64_le title_id_mask; 58 INSERT_PADDING_WORDS(2);
46 u64_le title_id_pattern; 59 NRRCertification certification;
47 INSERT_PADDING_BYTES(16); 60 std::array<u8, 0x100> signature;
48 std::array<u8, 0x100> modulus; 61 u64_le application_id;
49 std::array<u8, 0x100> signature_1;
50 std::array<u8, 0x100> signature_2;
51 u64_le title_id;
52 u32_le size; 62 u32_le size;
53 INSERT_PADDING_BYTES(4); 63 u8 nrr_kind; // 7.0.0+
64 INSERT_PADDING_BYTES(3);
54 u32_le hash_offset; 65 u32_le hash_offset;
55 u32_le hash_count; 66 u32_le hash_count;
56 INSERT_PADDING_BYTES(8); 67 INSERT_PADDING_WORDS(2);
68};
69static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has invalid size.");
70
71struct SegmentHeader {
72 u32_le memory_offset;
73 u32_le memory_size;
57}; 74};
58static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size."); 75static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size.");
59 76
60struct NROHeader { 77struct NROHeader {
78 // Switchbrew calls this "Start" (0x10)
61 INSERT_PADDING_WORDS(1); 79 INSERT_PADDING_WORDS(1);
62 u32_le mod_offset; 80 u32_le mod_offset;
63 INSERT_PADDING_WORDS(2); 81 INSERT_PADDING_WORDS(2);
82
83 // Switchbrew calls this "Header" (0x70)
64 u32_le magic; 84 u32_le magic;
65 u32_le version; 85 u32_le version;
66 u32_le nro_size; 86 u32_le nro_size;
67 u32_le flags; 87 u32_le flags;
68 u32_le text_offset; 88 // .text, .ro, .data
69 u32_le text_size; 89 std::array<SegmentHeader, 3> segment_headers;
70 u32_le ro_offset;
71 u32_le ro_size;
72 u32_le rw_offset;
73 u32_le rw_size;
74 u32_le bss_size; 90 u32_le bss_size;
75 INSERT_PADDING_WORDS(1); 91 INSERT_PADDING_WORDS(1);
76 std::array<u8, 0x20> build_id; 92 std::array<u8, 0x20> build_id;
77 INSERT_PADDING_BYTES(0x20); 93 u32_le dso_handle_offset;
94 INSERT_PADDING_WORDS(1);
95 // .apiInfo, .dynstr, .dynsym
96 std::array<SegmentHeader, 3> segment_headers_2;
78}; 97};
79static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); 98static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size.");
80 99
@@ -91,6 +110,7 @@ struct NROInfo {
91 std::size_t data_size{}; 110 std::size_t data_size{};
92 VAddr src_addr{}; 111 VAddr src_addr{};
93}; 112};
113static_assert(sizeof(NROInfo) == 0x60, "NROInfo has invalid size.");
94 114
95class DebugMonitor final : public ServiceFramework<DebugMonitor> { 115class DebugMonitor final : public ServiceFramework<DebugMonitor> {
96public: 116public:
@@ -226,11 +246,11 @@ public:
226 return; 246 return;
227 } 247 }
228 248
229 if (system.CurrentProcess()->GetTitleID() != header.title_id) { 249 if (system.CurrentProcess()->GetTitleID() != header.application_id) {
230 LOG_ERROR(Service_LDR, 250 LOG_ERROR(Service_LDR,
231 "Attempting to load NRR with title ID other than current process. (actual " 251 "Attempting to load NRR with title ID other than current process. (actual "
232 "{:016X})!", 252 "{:016X})!",
233 header.title_id); 253 header.application_id);
234 IPC::ResponseBuilder rb{ctx, 2}; 254 IPC::ResponseBuilder rb{ctx, 2};
235 rb.Push(ERROR_INVALID_NRR); 255 rb.Push(ERROR_INVALID_NRR);
236 return; 256 return;
@@ -348,10 +368,10 @@ public:
348 368
349 ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, 369 ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr,
350 VAddr start) const { 370 VAddr start) const {
351 const VAddr text_start{start + nro_header.text_offset}; 371 const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset};
352 const VAddr ro_start{start + nro_header.ro_offset}; 372 const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset};
353 const VAddr data_start{start + nro_header.rw_offset}; 373 const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset};
354 const VAddr bss_start{data_start + nro_header.rw_size}; 374 const VAddr bss_start{data_start + nro_header.segment_headers[DATA_INDEX].memory_size};
355 const VAddr bss_end_addr{ 375 const VAddr bss_end_addr{
356 Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; 376 Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)};
357 377
@@ -360,9 +380,12 @@ public:
360 system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); 380 system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size());
361 system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); 381 system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size());
362 }}; 382 }};
363 CopyCode(nro_addr + nro_header.text_offset, text_start, nro_header.text_size); 383 CopyCode(nro_addr + nro_header.segment_headers[TEXT_INDEX].memory_offset, text_start,
364 CopyCode(nro_addr + nro_header.ro_offset, ro_start, nro_header.ro_size); 384 nro_header.segment_headers[TEXT_INDEX].memory_size);
365 CopyCode(nro_addr + nro_header.rw_offset, data_start, nro_header.rw_size); 385 CopyCode(nro_addr + nro_header.segment_headers[RO_INDEX].memory_offset, ro_start,
386 nro_header.segment_headers[RO_INDEX].memory_size);
387 CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start,
388 nro_header.segment_headers[DATA_INDEX].memory_size);
366 389
367 CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( 390 CASCADE_CODE(process->PageTable().SetCodeMemoryPermission(
368 text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); 391 text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute));
@@ -484,9 +507,11 @@ public:
484 } 507 }
485 508
486 // Track the loaded NRO 509 // Track the loaded NRO
487 nro.insert_or_assign(*map_result, NROInfo{hash, *map_result, nro_size, bss_address, 510 nro.insert_or_assign(*map_result,
488 bss_size, header.text_size, header.ro_size, 511 NROInfo{hash, *map_result, nro_size, bss_address, bss_size,
489 header.rw_size, nro_address}); 512 header.segment_headers[TEXT_INDEX].memory_size,
513 header.segment_headers[RO_INDEX].memory_size,
514 header.segment_headers[DATA_INDEX].memory_size, nro_address});
490 515
491 // Invalidate JIT caches for the newly mapped process code 516 // Invalidate JIT caches for the newly mapped process code
492 system.InvalidateCpuInstructionCaches(); 517 system.InvalidateCpuInstructionCaches();
@@ -584,11 +609,21 @@ private:
584 static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { 609 static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
585 return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && 610 return header.magic == Common::MakeMagic('N', 'R', 'O', '0') &&
586 header.nro_size == nro_size && header.bss_size == bss_size && 611 header.nro_size == nro_size && header.bss_size == bss_size &&
587 header.ro_offset == header.text_offset + header.text_size && 612
588 header.rw_offset == header.ro_offset + header.ro_size && 613 header.segment_headers[RO_INDEX].memory_offset ==
589 nro_size == header.rw_offset + header.rw_size && 614 header.segment_headers[TEXT_INDEX].memory_offset +
590 Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && 615 header.segment_headers[TEXT_INDEX].memory_size &&
591 Common::Is4KBAligned(header.rw_size); 616
617 header.segment_headers[DATA_INDEX].memory_offset ==
618 header.segment_headers[RO_INDEX].memory_offset +
619 header.segment_headers[RO_INDEX].memory_size &&
620
621 nro_size == header.segment_headers[DATA_INDEX].memory_offset +
622 header.segment_headers[DATA_INDEX].memory_size &&
623
624 Common::Is4KBAligned(header.segment_headers[TEXT_INDEX].memory_size) &&
625 Common::Is4KBAligned(header.segment_headers[RO_INDEX].memory_size) &&
626 Common::Is4KBAligned(header.segment_headers[DATA_INDEX].memory_size);
592 } 627 }
593 Core::System& system; 628 Core::System& system;
594}; 629};
diff --git a/src/core/hle/service/lm/manager.cpp b/src/core/hle/service/lm/manager.cpp
index b67081b86..3ee2374e7 100644
--- a/src/core/hle/service/lm/manager.cpp
+++ b/src/core/hle/service/lm/manager.cpp
@@ -86,7 +86,8 @@ std::string FormatField(Field type, const std::vector<u8>& data) {
86 return Common::StringFromFixedZeroTerminatedBuffer( 86 return Common::StringFromFixedZeroTerminatedBuffer(
87 reinterpret_cast<const char*>(data.data()), data.size()); 87 reinterpret_cast<const char*>(data.data()), data.size());
88 default: 88 default:
89 UNIMPLEMENTED(); 89 UNIMPLEMENTED_MSG("Unimplemented field type={}", type);
90 return "";
90 } 91 }
91} 92}
92 93
diff --git a/src/core/hle/service/mig/mig.cpp b/src/core/hle/service/mig/mig.cpp
index d16367f2c..113a4665c 100644
--- a/src/core/hle/service/mig/mig.cpp
+++ b/src/core/hle/service/mig/mig.cpp
@@ -20,6 +20,12 @@ public:
20 {101, nullptr, "ResumeServer"}, 20 {101, nullptr, "ResumeServer"},
21 {200, nullptr, "CreateClient"}, 21 {200, nullptr, "CreateClient"},
22 {201, nullptr, "ResumeClient"}, 22 {201, nullptr, "ResumeClient"},
23 {1001, nullptr, "Unknown1001"},
24 {1010, nullptr, "Unknown1010"},
25 {1100, nullptr, "Unknown1100"},
26 {1101, nullptr, "Unknown1101"},
27 {1200, nullptr, "Unknown1200"},
28 {1201, nullptr, "Unknown1201"}
23 }; 29 };
24 // clang-format on 30 // clang-format on
25 31
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index def63dc8a..25c24e537 100644
--- a/src/core/hle/service/mm/mm_u.cpp
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -14,14 +14,14 @@ public:
14 explicit MM_U() : ServiceFramework{"mm:u"} { 14 explicit MM_U() : ServiceFramework{"mm:u"} {
15 // clang-format off 15 // clang-format off
16 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
17 {0, &MM_U::Initialize, "Initialize"}, 17 {0, &MM_U::InitializeOld, "InitializeOld"},
18 {1, &MM_U::Finalize, "Finalize"}, 18 {1, &MM_U::FinalizeOld, "FinalizeOld"},
19 {2, &MM_U::SetAndWait, "SetAndWait"}, 19 {2, &MM_U::SetAndWaitOld, "SetAndWaitOld"},
20 {3, &MM_U::Get, "Get"}, 20 {3, &MM_U::GetOld, "GetOld"},
21 {4, &MM_U::InitializeWithId, "InitializeWithId"}, 21 {4, &MM_U::Initialize, "Initialize"},
22 {5, &MM_U::FinalizeWithId, "FinalizeWithId"}, 22 {5, &MM_U::Finalize, "Finalize"},
23 {6, &MM_U::SetAndWaitWithId, "SetAndWaitWithId"}, 23 {6, &MM_U::SetAndWait, "SetAndWait"},
24 {7, &MM_U::GetWithId, "GetWithId"}, 24 {7, &MM_U::Get, "Get"},
25 }; 25 };
26 // clang-format on 26 // clang-format on
27 27
@@ -29,21 +29,21 @@ public:
29 } 29 }
30 30
31private: 31private:
32 void Initialize(Kernel::HLERequestContext& ctx) { 32 void InitializeOld(Kernel::HLERequestContext& ctx) {
33 LOG_WARNING(Service_MM, "(STUBBED) called"); 33 LOG_WARNING(Service_MM, "(STUBBED) called");
34 34
35 IPC::ResponseBuilder rb{ctx, 2}; 35 IPC::ResponseBuilder rb{ctx, 2};
36 rb.Push(RESULT_SUCCESS); 36 rb.Push(RESULT_SUCCESS);
37 } 37 }
38 38
39 void Finalize(Kernel::HLERequestContext& ctx) { 39 void FinalizeOld(Kernel::HLERequestContext& ctx) {
40 LOG_WARNING(Service_MM, "(STUBBED) called"); 40 LOG_WARNING(Service_MM, "(STUBBED) called");
41 41
42 IPC::ResponseBuilder rb{ctx, 2}; 42 IPC::ResponseBuilder rb{ctx, 2};
43 rb.Push(RESULT_SUCCESS); 43 rb.Push(RESULT_SUCCESS);
44 } 44 }
45 45
46 void SetAndWait(Kernel::HLERequestContext& ctx) { 46 void SetAndWaitOld(Kernel::HLERequestContext& ctx) {
47 IPC::RequestParser rp{ctx}; 47 IPC::RequestParser rp{ctx};
48 min = rp.Pop<u32>(); 48 min = rp.Pop<u32>();
49 max = rp.Pop<u32>(); 49 max = rp.Pop<u32>();
@@ -54,7 +54,7 @@ private:
54 rb.Push(RESULT_SUCCESS); 54 rb.Push(RESULT_SUCCESS);
55 } 55 }
56 56
57 void Get(Kernel::HLERequestContext& ctx) { 57 void GetOld(Kernel::HLERequestContext& ctx) {
58 LOG_WARNING(Service_MM, "(STUBBED) called"); 58 LOG_WARNING(Service_MM, "(STUBBED) called");
59 59
60 IPC::ResponseBuilder rb{ctx, 3}; 60 IPC::ResponseBuilder rb{ctx, 3};
@@ -62,7 +62,7 @@ private:
62 rb.Push(current); 62 rb.Push(current);
63 } 63 }
64 64
65 void InitializeWithId(Kernel::HLERequestContext& ctx) { 65 void Initialize(Kernel::HLERequestContext& ctx) {
66 LOG_WARNING(Service_MM, "(STUBBED) called"); 66 LOG_WARNING(Service_MM, "(STUBBED) called");
67 67
68 IPC::ResponseBuilder rb{ctx, 3}; 68 IPC::ResponseBuilder rb{ctx, 3};
@@ -70,14 +70,14 @@ private:
70 rb.Push<u32>(id); // Any non zero value 70 rb.Push<u32>(id); // Any non zero value
71 } 71 }
72 72
73 void FinalizeWithId(Kernel::HLERequestContext& ctx) { 73 void Finalize(Kernel::HLERequestContext& ctx) {
74 LOG_WARNING(Service_MM, "(STUBBED) called"); 74 LOG_WARNING(Service_MM, "(STUBBED) called");
75 75
76 IPC::ResponseBuilder rb{ctx, 2}; 76 IPC::ResponseBuilder rb{ctx, 2};
77 rb.Push(RESULT_SUCCESS); 77 rb.Push(RESULT_SUCCESS);
78 } 78 }
79 79
80 void SetAndWaitWithId(Kernel::HLERequestContext& ctx) { 80 void SetAndWait(Kernel::HLERequestContext& ctx) {
81 IPC::RequestParser rp{ctx}; 81 IPC::RequestParser rp{ctx};
82 u32 input_id = rp.Pop<u32>(); 82 u32 input_id = rp.Pop<u32>();
83 min = rp.Pop<u32>(); 83 min = rp.Pop<u32>();
@@ -90,7 +90,7 @@ private:
90 rb.Push(RESULT_SUCCESS); 90 rb.Push(RESULT_SUCCESS);
91 } 91 }
92 92
93 void GetWithId(Kernel::HLERequestContext& ctx) { 93 void Get(Kernel::HLERequestContext& ctx) {
94 LOG_WARNING(Service_MM, "(STUBBED) called"); 94 LOG_WARNING(Service_MM, "(STUBBED) called");
95 95
96 IPC::ResponseBuilder rb{ctx, 3}; 96 IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp
index ec9aae04a..e38dea1f4 100644
--- a/src/core/hle/service/ncm/ncm.cpp
+++ b/src/core/hle/service/ncm/ncm.cpp
@@ -28,16 +28,16 @@ public:
28 {7, nullptr, "ResolveApplicationLegalInformationPath"}, 28 {7, nullptr, "ResolveApplicationLegalInformationPath"},
29 {8, nullptr, "RedirectApplicationLegalInformationPath"}, 29 {8, nullptr, "RedirectApplicationLegalInformationPath"},
30 {9, nullptr, "Refresh"}, 30 {9, nullptr, "Refresh"},
31 {10, nullptr, "RedirectProgramPath2"}, 31 {10, nullptr, "RedirectApplicationProgramPath"},
32 {11, nullptr, "Refresh2"}, 32 {11, nullptr, "ClearApplicationRedirection"},
33 {12, nullptr, "DeleteProgramPath"}, 33 {12, nullptr, "EraseProgramRedirection"},
34 {13, nullptr, "DeleteApplicationControlPath"}, 34 {13, nullptr, "EraseApplicationControlRedirection"},
35 {14, nullptr, "DeleteApplicationHtmlDocumentPath"}, 35 {14, nullptr, "EraseApplicationHtmlDocumentRedirection"},
36 {15, nullptr, "DeleteApplicationLegalInformationPath"}, 36 {15, nullptr, "EraseApplicationLegalInformationRedirection"},
37 {16, nullptr, ""}, 37 {16, nullptr, "ResolveProgramPathForDebug"},
38 {17, nullptr, ""}, 38 {17, nullptr, "RedirectProgramPathForDebug"},
39 {18, nullptr, ""}, 39 {18, nullptr, "RedirectApplicationProgramPathForDebug"},
40 {19, nullptr, ""}, 40 {19, nullptr, "EraseProgramRedirectionForDebug"},
41 }; 41 };
42 // clang-format on 42 // clang-format on
43 43
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index b7b34ce7e..780ea30fe 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -198,9 +198,9 @@ public:
198 static const FunctionInfo functions[] = { 198 static const FunctionInfo functions[] = {
199 {0, nullptr, "Initialize"}, 199 {0, nullptr, "Initialize"},
200 {1, nullptr, "Finalize"}, 200 {1, nullptr, "Finalize"},
201 {2, nullptr, "GetState"}, 201 {2, nullptr, "GetStateOld"},
202 {3, nullptr, "IsNfcEnabled"}, 202 {3, nullptr, "IsNfcEnabledOld"},
203 {100, nullptr, "SetNfcEnabled"}, 203 {100, nullptr, "SetNfcEnabledOld"},
204 {400, nullptr, "InitializeSystem"}, 204 {400, nullptr, "InitializeSystem"},
205 {401, nullptr, "FinalizeSystem"}, 205 {401, nullptr, "FinalizeSystem"},
206 {402, nullptr, "GetState"}, 206 {402, nullptr, "GetState"},
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 3e4dd2f7a..886450be2 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -366,7 +366,8 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
366 LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages); 366 LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
367 367
368 // Get language code from settings 368 // Get language code from settings
369 const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index); 369 const auto language_code =
370 Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue());
370 371
371 // Convert to application language, get priority list 372 // Convert to application language, get priority list
372 const auto application_language = ConvertToApplicationLanguage(language_code); 373 const auto application_language = ConvertToApplicationLanguage(language_code);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index cc2192e5c..fba89e7a6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -25,7 +25,7 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
25 case IoctlCommand::IocGetCharacteristicsCommand: 25 case IoctlCommand::IocGetCharacteristicsCommand:
26 return GetCharacteristics(input, output, output2, version); 26 return GetCharacteristics(input, output, output2, version);
27 case IoctlCommand::IocGetTPCMasksCommand: 27 case IoctlCommand::IocGetTPCMasksCommand:
28 return GetTPCMasks(input, output); 28 return GetTPCMasks(input, output, output2, version);
29 case IoctlCommand::IocGetActiveSlotMaskCommand: 29 case IoctlCommand::IocGetActiveSlotMaskCommand:
30 return GetActiveSlotMask(input, output); 30 return GetActiveSlotMask(input, output);
31 case IoctlCommand::IocZcullGetCtxSizeCommand: 31 case IoctlCommand::IocZcullGetCtxSizeCommand:
@@ -98,17 +98,22 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
98 return 0; 98 return 0;
99} 99}
100 100
101u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) { 101u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
102 std::vector<u8>& output2, IoctlVersion version) {
102 IoctlGpuGetTpcMasksArgs params{}; 103 IoctlGpuGetTpcMasksArgs params{};
103 std::memcpy(&params, input.data(), input.size()); 104 std::memcpy(&params, input.data(), input.size());
104 LOG_INFO(Service_NVDRV, "called, mask=0x{:X}, mask_buf_addr=0x{:X}", params.mask_buf_size, 105 LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
105 params.mask_buf_addr); 106 if (params.mask_buffer_size != 0) {
106 // TODO(ogniK): Confirm value on hardware 107 params.tcp_mask = 3;
107 if (params.mask_buf_size) 108 }
108 params.tpc_mask_size = 4 * 1; // 4 * num_gpc 109
109 else 110 if (version == IoctlVersion::Version3) {
110 params.tpc_mask_size = 0; 111 std::memcpy(output.data(), input.data(), output.size());
111 std::memcpy(output.data(), &params, sizeof(params)); 112 std::memcpy(output2.data(), &params.tcp_mask, output2.size());
113 } else {
114 std::memcpy(output.data(), &params, output.size());
115 }
116
112 return 0; 117 return 0;
113} 118}
114 119
@@ -195,8 +200,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
195 200
196 IoctlGetGpuTime params{}; 201 IoctlGetGpuTime params{};
197 std::memcpy(&params, input.data(), input.size()); 202 std::memcpy(&params, input.data(), input.size());
198 const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks()); 203 params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count());
199 params.gpu_time = static_cast<u64_le>(ns.count());
200 std::memcpy(output.data(), &params, output.size()); 204 std::memcpy(output.data(), &params, output.size());
201 return 0; 205 return 0;
202} 206}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 07b644ec5..ef60f72ce 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -92,16 +92,11 @@ private:
92 "IoctlCharacteristics is incorrect size"); 92 "IoctlCharacteristics is incorrect size");
93 93
94 struct IoctlGpuGetTpcMasksArgs { 94 struct IoctlGpuGetTpcMasksArgs {
95 /// [in] TPC mask buffer size reserved by userspace. Should be at least 95 u32_le mask_buffer_size{};
96 /// sizeof(__u32) * fls(gpc_mask) to receive TPC mask for each GPC. 96 INSERT_PADDING_WORDS(1);
97 /// [out] full kernel buffer size 97 u64_le mask_buffer_address{};
98 u32_le mask_buf_size; 98 u32_le tcp_mask{};
99 u32_le reserved; 99 INSERT_PADDING_WORDS(1);
100
101 /// [in] pointer to TPC mask buffer. It will receive one 32-bit TPC mask per GPC or 0 if
102 /// GPC is not enabled or not present. This parameter is ignored if mask_buf_size is 0.
103 u64_le mask_buf_addr;
104 u64_le tpc_mask_size; // Nintendo add this?
105 }; 100 };
106 static_assert(sizeof(IoctlGpuGetTpcMasksArgs) == 24, 101 static_assert(sizeof(IoctlGpuGetTpcMasksArgs) == 24,
107 "IoctlGpuGetTpcMasksArgs is incorrect size"); 102 "IoctlGpuGetTpcMasksArgs is incorrect size");
@@ -166,7 +161,8 @@ private:
166 161
167 u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, 162 u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
168 std::vector<u8>& output2, IoctlVersion version); 163 std::vector<u8>& output2, IoctlVersion version);
169 u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); 164 u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, std::vector<u8>& output2,
165 IoctlVersion version);
170 u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); 166 u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
171 u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); 167 u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
172 u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); 168 u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 437bc5dee..2f44d3779 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -9,6 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/microprofile.h" 10#include "common/microprofile.h"
11#include "common/scope_exit.h" 11#include "common/scope_exit.h"
12#include "common/thread.h"
12#include "core/core.h" 13#include "core/core.h"
13#include "core/core_timing.h" 14#include "core/core_timing.h"
14#include "core/core_timing_util.h" 15#include "core/core_timing_util.h"
@@ -27,8 +28,35 @@
27 28
28namespace Service::NVFlinger { 29namespace Service::NVFlinger {
29 30
30constexpr s64 frame_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); 31constexpr s64 frame_ticks = static_cast<s64>(1000000000 / 60);
31constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 30); 32constexpr s64 frame_ticks_30fps = static_cast<s64>(1000000000 / 30);
33
34void NVFlinger::VSyncThread(NVFlinger& nv_flinger) {
35 nv_flinger.SplitVSync();
36}
37
38void NVFlinger::SplitVSync() {
39 system.RegisterHostThread();
40 std::string name = "yuzu:VSyncThread";
41 MicroProfileOnThreadCreate(name.c_str());
42 Common::SetCurrentThreadName(name.c_str());
43 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
44 s64 delay = 0;
45 while (is_running) {
46 guard->lock();
47 const s64 time_start = system.CoreTiming().GetGlobalTimeNs().count();
48 Compose();
49 const auto ticks = GetNextTicks();
50 const s64 time_end = system.CoreTiming().GetGlobalTimeNs().count();
51 const s64 time_passed = time_end - time_start;
52 const s64 next_time = std::max<s64>(0, ticks - time_passed - delay);
53 guard->unlock();
54 if (next_time > 0) {
55 wait_event->WaitFor(std::chrono::nanoseconds{next_time});
56 }
57 delay = (system.CoreTiming().GetGlobalTimeNs().count() - time_end) - next_time;
58 }
59}
32 60
33NVFlinger::NVFlinger(Core::System& system) : system(system) { 61NVFlinger::NVFlinger(Core::System& system) : system(system) {
34 displays.emplace_back(0, "Default", system); 62 displays.emplace_back(0, "Default", system);
@@ -36,22 +64,36 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) {
36 displays.emplace_back(2, "Edid", system); 64 displays.emplace_back(2, "Edid", system);
37 displays.emplace_back(3, "Internal", system); 65 displays.emplace_back(3, "Internal", system);
38 displays.emplace_back(4, "Null", system); 66 displays.emplace_back(4, "Null", system);
67 guard = std::make_shared<std::mutex>();
39 68
40 // Schedule the screen composition events 69 // Schedule the screen composition events
41 composition_event = 70 composition_event =
42 Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { 71 Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 ns_late) {
72 Lock();
43 Compose(); 73 Compose();
44 const auto ticks = 74 const auto ticks = GetNextTicks();
45 Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); 75 this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - ns_late),
46 this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late),
47 composition_event); 76 composition_event);
48 }); 77 });
49 78 if (system.IsMulticore()) {
50 system.CoreTiming().ScheduleEvent(frame_ticks, composition_event); 79 is_running = true;
80 wait_event = std::make_unique<Common::Event>();
81 vsync_thread = std::make_unique<std::thread>(VSyncThread, std::ref(*this));
82 } else {
83 system.CoreTiming().ScheduleEvent(frame_ticks, composition_event);
84 }
51} 85}
52 86
53NVFlinger::~NVFlinger() { 87NVFlinger::~NVFlinger() {
54 system.CoreTiming().UnscheduleEvent(composition_event, 0); 88 if (system.IsMulticore()) {
89 is_running = false;
90 wait_event->Set();
91 vsync_thread->join();
92 vsync_thread.reset();
93 wait_event.reset();
94 } else {
95 system.CoreTiming().UnscheduleEvent(composition_event, 0);
96 }
55} 97}
56 98
57void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 99void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
@@ -199,10 +241,12 @@ void NVFlinger::Compose() {
199 241
200 auto& gpu = system.GPU(); 242 auto& gpu = system.GPU();
201 const auto& multi_fence = buffer->get().multi_fence; 243 const auto& multi_fence = buffer->get().multi_fence;
244 guard->unlock();
202 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { 245 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
203 const auto& fence = multi_fence.fences[fence_id]; 246 const auto& fence = multi_fence.fences[fence_id];
204 gpu.WaitFence(fence.id, fence.value); 247 gpu.WaitFence(fence.id, fence.value);
205 } 248 }
249 guard->lock();
206 250
207 MicroProfileFlip(); 251 MicroProfileFlip();
208 252
@@ -223,7 +267,7 @@ void NVFlinger::Compose() {
223 267
224s64 NVFlinger::GetNextTicks() const { 268s64 NVFlinger::GetNextTicks() const {
225 constexpr s64 max_hertz = 120LL; 269 constexpr s64 max_hertz = 120LL;
226 return (Core::Hardware::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz; 270 return (1000000000 * (1LL << swap_interval)) / max_hertz;
227} 271}
228 272
229} // namespace Service::NVFlinger 273} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 57a21f33b..e4959a9af 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -4,15 +4,22 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
9#include <mutex>
8#include <optional> 10#include <optional>
9#include <string> 11#include <string>
10#include <string_view> 12#include <string_view>
13#include <thread>
11#include <vector> 14#include <vector>
12 15
13#include "common/common_types.h" 16#include "common/common_types.h"
14#include "core/hle/kernel/object.h" 17#include "core/hle/kernel/object.h"
15 18
19namespace Common {
20class Event;
21} // namespace Common
22
16namespace Core::Timing { 23namespace Core::Timing {
17class CoreTiming; 24class CoreTiming;
18struct EventType; 25struct EventType;
@@ -79,6 +86,10 @@ public:
79 86
80 s64 GetNextTicks() const; 87 s64 GetNextTicks() const;
81 88
89 std::unique_lock<std::mutex> Lock() {
90 return std::unique_lock{*guard};
91 }
92
82private: 93private:
83 /// Finds the display identified by the specified ID. 94 /// Finds the display identified by the specified ID.
84 VI::Display* FindDisplay(u64 display_id); 95 VI::Display* FindDisplay(u64 display_id);
@@ -92,6 +103,10 @@ private:
92 /// Finds the layer identified by the specified ID in the desired display. 103 /// Finds the layer identified by the specified ID in the desired display.
93 const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; 104 const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const;
94 105
106 static void VSyncThread(NVFlinger& nv_flinger);
107
108 void SplitVSync();
109
95 std::shared_ptr<Nvidia::Module> nvdrv; 110 std::shared_ptr<Nvidia::Module> nvdrv;
96 111
97 std::vector<VI::Display> displays; 112 std::vector<VI::Display> displays;
@@ -108,7 +123,13 @@ private:
108 /// Event that handles screen composition. 123 /// Event that handles screen composition.
109 std::shared_ptr<Core::Timing::EventType> composition_event; 124 std::shared_ptr<Core::Timing::EventType> composition_event;
110 125
126 std::shared_ptr<std::mutex> guard;
127
111 Core::System& system; 128 Core::System& system;
129
130 std::unique_ptr<std::thread> vsync_thread;
131 std::unique_ptr<Common::Event> wait_event;
132 std::atomic<bool> is_running{};
112}; 133};
113 134
114} // namespace Service::NVFlinger 135} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 9d36ea0d0..cde3312da 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -80,8 +80,13 @@ private:
80 const auto user_id = rp.PopRaw<u128>(); 80 const auto user_id = rp.PopRaw<u128>();
81 const auto process_id = rp.PopRaw<u64>(); 81 const auto process_id = rp.PopRaw<u64>();
82 std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; 82 std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
83
83 if constexpr (Type == Core::Reporter::PlayReportType::Old2) { 84 if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
84 data.emplace_back(ctx.ReadBuffer(1)); 85 const auto read_buffer_count =
86 ctx.BufferDescriptorX().size() + ctx.BufferDescriptorA().size();
87 if (read_buffer_count > 1) {
88 data.emplace_back(ctx.ReadBuffer(1));
89 }
85 } 90 }
86 91
87 LOG_DEBUG( 92 LOG_DEBUG(
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index f3b4b286c..34fe2fd82 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array>
6#include <chrono> 7#include <chrono>
7#include "common/logging/log.h" 8#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
@@ -31,6 +32,44 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
31 LanguageCode::ZH_HANT, 32 LanguageCode::ZH_HANT,
32}}; 33}};
33 34
35enum class KeyboardLayout : u64 {
36 Japanese = 0,
37 EnglishUs = 1,
38 EnglishUsInternational = 2,
39 EnglishUk = 3,
40 French = 4,
41 FrenchCa = 5,
42 Spanish = 6,
43 SpanishLatin = 7,
44 German = 8,
45 Italian = 9,
46 Portuguese = 10,
47 Russian = 11,
48 Korean = 12,
49 ChineseSimplified = 13,
50 ChineseTraditional = 14,
51};
52
53constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{
54 {LanguageCode::JA, KeyboardLayout::Japanese},
55 {LanguageCode::EN_US, KeyboardLayout::EnglishUs},
56 {LanguageCode::FR, KeyboardLayout::French},
57 {LanguageCode::DE, KeyboardLayout::German},
58 {LanguageCode::IT, KeyboardLayout::Italian},
59 {LanguageCode::ES, KeyboardLayout::Spanish},
60 {LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified},
61 {LanguageCode::KO, KeyboardLayout::Korean},
62 {LanguageCode::NL, KeyboardLayout::EnglishUsInternational},
63 {LanguageCode::PT, KeyboardLayout::Portuguese},
64 {LanguageCode::RU, KeyboardLayout::Russian},
65 {LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional},
66 {LanguageCode::EN_GB, KeyboardLayout::EnglishUk},
67 {LanguageCode::FR_CA, KeyboardLayout::FrenchCa},
68 {LanguageCode::ES_419, KeyboardLayout::SpanishLatin},
69 {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified},
70 {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional},
71}};
72
34constexpr std::size_t pre4_0_0_max_entries = 15; 73constexpr std::size_t pre4_0_0_max_entries = 15;
35constexpr std::size_t post4_0_0_max_entries = 17; 74constexpr std::size_t post4_0_0_max_entries = 17;
36 75
@@ -50,6 +89,25 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m
50 ctx.WriteBuffer(available_language_codes.data(), copy_size); 89 ctx.WriteBuffer(available_language_codes.data(), copy_size);
51 PushResponseLanguageCode(ctx, copy_amount); 90 PushResponseLanguageCode(ctx, copy_amount);
52} 91}
92
93void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
94 const auto language_code = available_language_codes[Settings::values.language_index.GetValue()];
95 const auto key_code =
96 std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
97 [=](const auto& element) { return element.first == language_code; });
98 KeyboardLayout layout = KeyboardLayout::EnglishUs;
99 if (key_code == language_to_layout.cend()) {
100 LOG_ERROR(Service_SET,
101 "Could not find keyboard layout for language index {}, defaulting to English us",
102 Settings::values.language_index.GetValue());
103 } else {
104 layout = key_code->second;
105 }
106
107 IPC::ResponseBuilder rb{ctx, 2};
108 rb.Push(RESULT_SUCCESS);
109 ctx.WriteBuffer(&layout, sizeof(KeyboardLayout));
110}
53} // Anonymous namespace 111} // Anonymous namespace
54 112
55LanguageCode GetLanguageCodeFromIndex(std::size_t index) { 113LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
@@ -105,11 +163,11 @@ void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
105} 163}
106 164
107void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { 165void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
108 LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index); 166 LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue());
109 167
110 IPC::ResponseBuilder rb{ctx, 4}; 168 IPC::ResponseBuilder rb{ctx, 4};
111 rb.Push(RESULT_SUCCESS); 169 rb.Push(RESULT_SUCCESS);
112 rb.PushEnum(available_language_codes[Settings::values.language_index]); 170 rb.PushEnum(available_language_codes[Settings::values.language_index.GetValue()]);
113} 171}
114 172
115void SET::GetRegionCode(Kernel::HLERequestContext& ctx) { 173void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
@@ -117,7 +175,17 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
117 175
118 IPC::ResponseBuilder rb{ctx, 3}; 176 IPC::ResponseBuilder rb{ctx, 3};
119 rb.Push(RESULT_SUCCESS); 177 rb.Push(RESULT_SUCCESS);
120 rb.Push(Settings::values.region_index); 178 rb.Push(Settings::values.region_index.GetValue());
179}
180
181void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) {
182 LOG_DEBUG(Service_SET, "Called {}", ctx.Description());
183 GetKeyCodeMapImpl(ctx);
184}
185
186void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) {
187 LOG_DEBUG(Service_SET, "Called {}", ctx.Description());
188 GetKeyCodeMapImpl(ctx);
121} 189}
122 190
123SET::SET() : ServiceFramework("set") { 191SET::SET() : ServiceFramework("set") {
@@ -130,9 +198,9 @@ SET::SET() : ServiceFramework("set") {
130 {4, &SET::GetRegionCode, "GetRegionCode"}, 198 {4, &SET::GetRegionCode, "GetRegionCode"},
131 {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, 199 {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"},
132 {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, 200 {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
133 {7, nullptr, "GetKeyCodeMap"}, 201 {7, &SET::GetKeyCodeMap, "GetKeyCodeMap"},
134 {8, &SET::GetQuestFlag, "GetQuestFlag"}, 202 {8, &SET::GetQuestFlag, "GetQuestFlag"},
135 {9, nullptr, "GetKeyCodeMap2"}, 203 {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"},
136 {10, nullptr, "GetFirmwareVersionForDebug"}, 204 {10, nullptr, "GetFirmwareVersionForDebug"},
137 }; 205 };
138 // clang-format on 206 // clang-format on
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index 6084b345d..8ac9c169d 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -44,6 +44,8 @@ private:
44 void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx); 44 void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx);
45 void GetQuestFlag(Kernel::HLERequestContext& ctx); 45 void GetQuestFlag(Kernel::HLERequestContext& ctx);
46 void GetRegionCode(Kernel::HLERequestContext& ctx); 46 void GetRegionCode(Kernel::HLERequestContext& ctx);
47 void GetKeyCodeMap(Kernel::HLERequestContext& ctx);
48 void GetKeyCodeMap2(Kernel::HLERequestContext& ctx);
47}; 49};
48 50
49} // namespace Service::Set 51} // namespace Service::Set
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 6ada13be4..d872de16c 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -142,7 +142,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
142 } 142 }
143 143
144 // Wake the threads waiting on the ServerPort 144 // Wake the threads waiting on the ServerPort
145 server_port->WakeupAllWaitingThreads(); 145 server_port->Signal();
146 146
147 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); 147 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId());
148 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; 148 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index e724d4ab8..865ed3b91 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -19,7 +19,7 @@ namespace Service::SPL {
19 19
20Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 20Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
21 : ServiceFramework(name), module(std::move(module)), 21 : ServiceFramework(name), module(std::move(module)),
22 rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {} 22 rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {}
23 23
24Module::Interface::~Interface() = default; 24Module::Interface::~Interface() = default;
25 25
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp
index 1575f0b49..59a272f4a 100644
--- a/src/core/hle/service/time/standard_steady_clock_core.cpp
+++ b/src/core/hle/service/time/standard_steady_clock_core.cpp
@@ -11,9 +11,8 @@
11namespace Service::Time::Clock { 11namespace Service::Time::Clock {
12 12
13TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { 13TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
14 const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( 14 const TimeSpanType ticks_time_span{
15 Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), 15 TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)};
16 Core::Hardware::CNTFREQ)};
17 TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; 16 TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds};
18 17
19 if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { 18 if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) {
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
index 44d5bc651..8baaa2a6a 100644
--- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
@@ -11,9 +11,8 @@
11namespace Service::Time::Clock { 11namespace Service::Time::Clock {
12 12
13SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { 13SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) {
14 const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( 14 const TimeSpanType ticks_time_span{
15 Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), 15 TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)};
16 Core::Hardware::CNTFREQ)};
17 16
18 return {ticks_time_span.ToSeconds(), GetClockSourceId()}; 17 return {ticks_time_span.ToSeconds(), GetClockSourceId()};
19} 18}
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index cc1dbd575..13e4b3818 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -241,9 +241,8 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERe
241 const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; 241 const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
242 242
243 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { 243 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
244 const auto ticks{Clock::TimeSpanType::FromTicks( 244 const auto ticks{Clock::TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(),
245 Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), 245 Core::Hardware::CNTFREQ)};
246 Core::Hardware::CNTFREQ)};
247 const s64 base_time_point{context.offset + current_time_point.time_point - 246 const s64 base_time_point{context.offset + current_time_point.time_point -
248 ticks.ToSeconds()}; 247 ticks.ToSeconds()};
249 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; 248 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
index 999ec1e51..e0ae9f874 100644
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -30,8 +30,7 @@ void SharedMemory::SetupStandardSteadyClock(Core::System& system,
30 const Common::UUID& clock_source_id, 30 const Common::UUID& clock_source_id,
31 Clock::TimeSpanType current_time_point) { 31 Clock::TimeSpanType current_time_point) {
32 const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( 32 const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks(
33 Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), 33 system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)};
34 Core::Hardware::CNTFREQ)};
35 const Clock::SteadyClockContext context{ 34 const Clock::SteadyClockContext context{
36 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), 35 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
37 clock_source_id}; 36 clock_source_id};
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 67b45e7c0..ea7b4ae13 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -511,6 +511,7 @@ private:
511 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 511 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
512 static_cast<u32>(transaction), flags); 512 static_cast<u32>(transaction), flags);
513 513
514 nv_flinger->Lock();
514 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 515 auto& buffer_queue = nv_flinger->FindBufferQueue(id);
515 516
516 switch (transaction) { 517 switch (transaction) {
@@ -518,9 +519,9 @@ private:
518 IGBPConnectRequestParcel request{ctx.ReadBuffer()}; 519 IGBPConnectRequestParcel request{ctx.ReadBuffer()};
519 IGBPConnectResponseParcel response{ 520 IGBPConnectResponseParcel response{
520 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) * 521 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) *
521 Settings::values.resolution_factor), 522 Settings::values.resolution_factor.GetValue()),
522 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) * 523 static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
523 Settings::values.resolution_factor)}; 524 Settings::values.resolution_factor.GetValue())};
524 ctx.WriteBuffer(response.Serialize()); 525 ctx.WriteBuffer(response.Serialize());
525 break; 526 break;
526 } 527 }
@@ -550,6 +551,7 @@ private:
550 [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, 551 [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
551 Kernel::ThreadWakeupReason reason) { 552 Kernel::ThreadWakeupReason reason) {
552 // Repeat TransactParcel DequeueBuffer when a buffer is available 553 // Repeat TransactParcel DequeueBuffer when a buffer is available
554 nv_flinger->Lock();
553 auto& buffer_queue = nv_flinger->FindBufferQueue(id); 555 auto& buffer_queue = nv_flinger->FindBufferQueue(id);
554 auto result = buffer_queue.DequeueBuffer(width, height); 556 auto result = buffer_queue.DequeueBuffer(width, height);
555 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); 557 ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer.");
@@ -747,14 +749,14 @@ private:
747 749
748 if (Settings::values.use_docked_mode) { 750 if (Settings::values.use_docked_mode) {
749 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) * 751 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
750 static_cast<u32>(Settings::values.resolution_factor)); 752 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
751 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) * 753 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
752 static_cast<u32>(Settings::values.resolution_factor)); 754 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
753 } else { 755 } else {
754 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) * 756 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
755 static_cast<u32>(Settings::values.resolution_factor)); 757 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
756 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) * 758 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
757 static_cast<u32>(Settings::values.resolution_factor)); 759 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
758 } 760 }
759 761
760 rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games. 762 rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
@@ -1029,9 +1031,9 @@ private:
1029 // between docked and undocked dimensions. We take the liberty of applying 1031 // between docked and undocked dimensions. We take the liberty of applying
1030 // the resolution scaling factor here. 1032 // the resolution scaling factor here.
1031 rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) * 1033 rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
1032 static_cast<u32>(Settings::values.resolution_factor)); 1034 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
1033 rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) * 1035 rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
1034 static_cast<u32>(Settings::values.resolution_factor)); 1036 static_cast<u32>(Settings::values.resolution_factor.GetValue()));
1035 } 1037 }
1036 1038
1037 void SetLayerScalingMode(Kernel::HLERequestContext& ctx) { 1039 void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
@@ -1064,8 +1066,8 @@ private:
1064 LOG_WARNING(Service_VI, "(STUBBED) called"); 1066 LOG_WARNING(Service_VI, "(STUBBED) called");
1065 1067
1066 DisplayInfo display_info; 1068 DisplayInfo display_info;
1067 display_info.width *= static_cast<u64>(Settings::values.resolution_factor); 1069 display_info.width *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
1068 display_info.height *= static_cast<u64>(Settings::values.resolution_factor); 1070 display_info.height *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
1069 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); 1071 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
1070 IPC::ResponseBuilder rb{ctx, 4}; 1072 IPC::ResponseBuilder rb{ctx, 4};
1071 rb.Push(RESULT_SUCCESS); 1073 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 9d87045a0..2c5588933 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -8,6 +8,7 @@
8#include <utility> 8#include <utility>
9 9
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/atomic_ops.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/page_table.h" 14#include "common/page_table.h"
@@ -29,15 +30,12 @@ namespace Core::Memory {
29struct Memory::Impl { 30struct Memory::Impl {
30 explicit Impl(Core::System& system_) : system{system_} {} 31 explicit Impl(Core::System& system_) : system{system_} {}
31 32
32 void SetCurrentPageTable(Kernel::Process& process) { 33 void SetCurrentPageTable(Kernel::Process& process, u32 core_id) {
33 current_page_table = &process.PageTable().PageTableImpl(); 34 current_page_table = &process.PageTable().PageTableImpl();
34 35
35 const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth(); 36 const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth();
36 37
37 system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width); 38 system.ArmInterface(core_id).PageTableChanged(*current_page_table, address_space_width);
38 system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width);
39 system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width);
40 system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
41 } 39 }
42 40
43 void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { 41 void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
@@ -179,6 +177,22 @@ struct Memory::Impl {
179 } 177 }
180 } 178 }
181 179
180 bool WriteExclusive8(const VAddr addr, const u8 data, const u8 expected) {
181 return WriteExclusive<u8>(addr, data, expected);
182 }
183
184 bool WriteExclusive16(const VAddr addr, const u16 data, const u16 expected) {
185 return WriteExclusive<u16_le>(addr, data, expected);
186 }
187
188 bool WriteExclusive32(const VAddr addr, const u32 data, const u32 expected) {
189 return WriteExclusive<u32_le>(addr, data, expected);
190 }
191
192 bool WriteExclusive64(const VAddr addr, const u64 data, const u64 expected) {
193 return WriteExclusive<u64_le>(addr, data, expected);
194 }
195
182 std::string ReadCString(VAddr vaddr, std::size_t max_length) { 196 std::string ReadCString(VAddr vaddr, std::size_t max_length) {
183 std::string string; 197 std::string string;
184 string.reserve(max_length); 198 string.reserve(max_length);
@@ -534,9 +548,9 @@ struct Memory::Impl {
534 // longer exist, and we should just leave the pagetable entry blank. 548 // longer exist, and we should just leave the pagetable entry blank.
535 page_type = Common::PageType::Unmapped; 549 page_type = Common::PageType::Unmapped;
536 } else { 550 } else {
537 page_type = Common::PageType::Memory;
538 current_page_table->pointers[vaddr >> PAGE_BITS] = 551 current_page_table->pointers[vaddr >> PAGE_BITS] =
539 pointer - (vaddr & ~PAGE_MASK); 552 pointer - (vaddr & ~PAGE_MASK);
553 page_type = Common::PageType::Memory;
540 } 554 }
541 break; 555 break;
542 } 556 }
@@ -577,9 +591,12 @@ struct Memory::Impl {
577 base + page_table.pointers.size()); 591 base + page_table.pointers.size());
578 592
579 if (!target) { 593 if (!target) {
594 ASSERT_MSG(type != Common::PageType::Memory,
595 "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
596
580 while (base != end) { 597 while (base != end) {
581 page_table.pointers[base] = nullptr;
582 page_table.attributes[base] = type; 598 page_table.attributes[base] = type;
599 page_table.pointers[base] = nullptr;
583 page_table.backing_addr[base] = 0; 600 page_table.backing_addr[base] = 0;
584 601
585 base += 1; 602 base += 1;
@@ -682,6 +699,67 @@ struct Memory::Impl {
682 } 699 }
683 } 700 }
684 701
702 template <typename T>
703 bool WriteExclusive(const VAddr vaddr, const T data, const T expected) {
704 u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
705 if (page_pointer != nullptr) {
706 // NOTE: Avoid adding any extra logic to this fast-path block
707 T volatile* pointer = reinterpret_cast<T volatile*>(&page_pointer[vaddr]);
708 return Common::AtomicCompareAndSwap(pointer, data, expected);
709 }
710
711 const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
712 switch (type) {
713 case Common::PageType::Unmapped:
714 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
715 static_cast<u32>(data), vaddr);
716 return true;
717 case Common::PageType::Memory:
718 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
719 break;
720 case Common::PageType::RasterizerCachedMemory: {
721 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
722 system.GPU().InvalidateRegion(vaddr, sizeof(T));
723 T volatile* pointer = reinterpret_cast<T volatile*>(&host_ptr);
724 return Common::AtomicCompareAndSwap(pointer, data, expected);
725 break;
726 }
727 default:
728 UNREACHABLE();
729 }
730 return true;
731 }
732
733 bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) {
734 u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
735 if (page_pointer != nullptr) {
736 // NOTE: Avoid adding any extra logic to this fast-path block
737 u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&page_pointer[vaddr]);
738 return Common::AtomicCompareAndSwap(pointer, data, expected);
739 }
740
741 const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
742 switch (type) {
743 case Common::PageType::Unmapped:
744 LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8,
745 static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr);
746 return true;
747 case Common::PageType::Memory:
748 ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
749 break;
750 case Common::PageType::RasterizerCachedMemory: {
751 u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
752 system.GPU().InvalidateRegion(vaddr, sizeof(u128));
753 u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&host_ptr);
754 return Common::AtomicCompareAndSwap(pointer, data, expected);
755 break;
756 }
757 default:
758 UNREACHABLE();
759 }
760 return true;
761 }
762
685 Common::PageTable* current_page_table = nullptr; 763 Common::PageTable* current_page_table = nullptr;
686 Core::System& system; 764 Core::System& system;
687}; 765};
@@ -689,8 +767,8 @@ struct Memory::Impl {
689Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {} 767Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
690Memory::~Memory() = default; 768Memory::~Memory() = default;
691 769
692void Memory::SetCurrentPageTable(Kernel::Process& process) { 770void Memory::SetCurrentPageTable(Kernel::Process& process, u32 core_id) {
693 impl->SetCurrentPageTable(process); 771 impl->SetCurrentPageTable(process, core_id);
694} 772}
695 773
696void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { 774void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
@@ -764,6 +842,26 @@ void Memory::Write64(VAddr addr, u64 data) {
764 impl->Write64(addr, data); 842 impl->Write64(addr, data);
765} 843}
766 844
845bool Memory::WriteExclusive8(VAddr addr, u8 data, u8 expected) {
846 return impl->WriteExclusive8(addr, data, expected);
847}
848
849bool Memory::WriteExclusive16(VAddr addr, u16 data, u16 expected) {
850 return impl->WriteExclusive16(addr, data, expected);
851}
852
853bool Memory::WriteExclusive32(VAddr addr, u32 data, u32 expected) {
854 return impl->WriteExclusive32(addr, data, expected);
855}
856
857bool Memory::WriteExclusive64(VAddr addr, u64 data, u64 expected) {
858 return impl->WriteExclusive64(addr, data, expected);
859}
860
861bool Memory::WriteExclusive128(VAddr addr, u128 data, u128 expected) {
862 return impl->WriteExclusive128(addr, data, expected);
863}
864
767std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { 865std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) {
768 return impl->ReadCString(vaddr, max_length); 866 return impl->ReadCString(vaddr, max_length);
769} 867}
diff --git a/src/core/memory.h b/src/core/memory.h
index 9292f3b0a..4a1cc63f4 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -64,7 +64,7 @@ public:
64 * 64 *
65 * @param process The process to use the page table of. 65 * @param process The process to use the page table of.
66 */ 66 */
67 void SetCurrentPageTable(Kernel::Process& process); 67 void SetCurrentPageTable(Kernel::Process& process, u32 core_id);
68 68
69 /** 69 /**
70 * Maps an allocated buffer onto a region of the emulated process address space. 70 * Maps an allocated buffer onto a region of the emulated process address space.
@@ -245,6 +245,71 @@ public:
245 void Write64(VAddr addr, u64 data); 245 void Write64(VAddr addr, u64 data);
246 246
247 /** 247 /**
248 * Writes a 8-bit unsigned integer to the given virtual address in
249 * the current process' address space if and only if the address contains
250 * the expected value. This operation is atomic.
251 *
252 * @param addr The virtual address to write the 8-bit unsigned integer to.
253 * @param data The 8-bit unsigned integer to write to the given virtual address.
254 * @param expected The 8-bit unsigned integer to check against the given virtual address.
255 *
256 * @post The memory range [addr, sizeof(data)) contains the given data value.
257 */
258 bool WriteExclusive8(VAddr addr, u8 data, u8 expected);
259
260 /**
261 * Writes a 16-bit unsigned integer to the given virtual address in
262 * the current process' address space if and only if the address contains
263 * the expected value. This operation is atomic.
264 *
265 * @param addr The virtual address to write the 16-bit unsigned integer to.
266 * @param data The 16-bit unsigned integer to write to the given virtual address.
267 * @param expected The 16-bit unsigned integer to check against the given virtual address.
268 *
269 * @post The memory range [addr, sizeof(data)) contains the given data value.
270 */
271 bool WriteExclusive16(VAddr addr, u16 data, u16 expected);
272
273 /**
274 * Writes a 32-bit unsigned integer to the given virtual address in
275 * the current process' address space if and only if the address contains
276 * the expected value. This operation is atomic.
277 *
278 * @param addr The virtual address to write the 32-bit unsigned integer to.
279 * @param data The 32-bit unsigned integer to write to the given virtual address.
280 * @param expected The 32-bit unsigned integer to check against the given virtual address.
281 *
282 * @post The memory range [addr, sizeof(data)) contains the given data value.
283 */
284 bool WriteExclusive32(VAddr addr, u32 data, u32 expected);
285
286 /**
287 * Writes a 64-bit unsigned integer to the given virtual address in
288 * the current process' address space if and only if the address contains
289 * the expected value. This operation is atomic.
290 *
291 * @param addr The virtual address to write the 64-bit unsigned integer to.
292 * @param data The 64-bit unsigned integer to write to the given virtual address.
293 * @param expected The 64-bit unsigned integer to check against the given virtual address.
294 *
295 * @post The memory range [addr, sizeof(data)) contains the given data value.
296 */
297 bool WriteExclusive64(VAddr addr, u64 data, u64 expected);
298
299 /**
300 * Writes a 128-bit unsigned integer to the given virtual address in
301 * the current process' address space if and only if the address contains
302 * the expected value. This operation is atomic.
303 *
304 * @param addr The virtual address to write the 128-bit unsigned integer to.
305 * @param data The 128-bit unsigned integer to write to the given virtual address.
306 * @param expected The 128-bit unsigned integer to check against the given virtual address.
307 *
308 * @post The memory range [addr, sizeof(data)) contains the given data value.
309 */
310 bool WriteExclusive128(VAddr addr, u128 data, u128 expected);
311
312 /**
248 * Reads a null-terminated string from the given virtual address. 313 * Reads a null-terminated string from the given virtual address.
249 * This function will continually read characters until either: 314 * This function will continually read characters until either:
250 * 315 *
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index b139e8465..53d27859b 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -20,7 +20,7 @@
20 20
21namespace Core::Memory { 21namespace Core::Memory {
22 22
23constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 12); 23constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(1000000000 / 12);
24constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; 24constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
25 25
26StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) 26StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata)
@@ -190,7 +190,7 @@ CheatEngine::~CheatEngine() {
190void CheatEngine::Initialize() { 190void CheatEngine::Initialize() {
191 event = Core::Timing::CreateEvent( 191 event = Core::Timing::CreateEvent(
192 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), 192 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
193 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); 193 [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); });
194 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); 194 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
195 195
196 metadata.process_id = system.CurrentProcess()->GetProcessID(); 196 metadata.process_id = system.CurrentProcess()->GetProcessID();
@@ -217,7 +217,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> cheats) {
217 217
218MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); 218MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
219 219
220void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { 220void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) {
221 if (is_pending_reload.exchange(false)) { 221 if (is_pending_reload.exchange(false)) {
222 vm.LoadProgram(cheats); 222 vm.LoadProgram(cheats);
223 } 223 }
@@ -230,7 +230,7 @@ void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
230 230
231 vm.Execute(metadata); 231 vm.Execute(metadata);
232 232
233 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event); 233 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - ns_late, event);
234} 234}
235 235
236} // namespace Core::Memory 236} // namespace Core::Memory
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index f1ae9d4df..29339ead7 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -119,13 +119,14 @@ double PerfStats::GetLastFrameTimeScale() {
119} 119}
120 120
121void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { 121void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
122 if (!Settings::values.use_frame_limit) { 122 if (!Settings::values.use_frame_limit.GetValue() ||
123 Settings::values.use_multi_core.GetValue()) {
123 return; 124 return;
124 } 125 }
125 126
126 auto now = Clock::now(); 127 auto now = Clock::now();
127 128
128 const double sleep_scale = Settings::values.frame_limit / 100.0; 129 const double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0;
129 130
130 // Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current 131 // Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current
131 // speed percent or it will clamp too much and prevent this from properly limiting to that 132 // speed percent or it will clamp too much and prevent this from properly limiting to that
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 4edff9cd8..d3886c4ec 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -62,6 +62,7 @@ const std::array<const char*, NumMouseButtons> mapping = {{
62} 62}
63 63
64Values values = {}; 64Values values = {};
65bool configuring_global = true;
65 66
66std::string GetTimeZoneString() { 67std::string GetTimeZoneString() {
67 static constexpr std::array<const char*, 46> timezones{{ 68 static constexpr std::array<const char*, 46> timezones{{
@@ -73,9 +74,9 @@ std::string GetTimeZoneString() {
73 "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu", 74 "UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
74 }}; 75 }};
75 76
76 ASSERT(Settings::values.time_zone_index < timezones.size()); 77 ASSERT(Settings::values.time_zone_index.GetValue() < timezones.size());
77 78
78 return timezones[Settings::values.time_zone_index]; 79 return timezones[Settings::values.time_zone_index.GetValue()];
79} 80}
80 81
81void Apply() { 82void Apply() {
@@ -97,25 +98,25 @@ void LogSetting(const std::string& name, const T& value) {
97 98
98void LogSettings() { 99void LogSettings() {
99 LOG_INFO(Config, "yuzu Configuration:"); 100 LOG_INFO(Config, "yuzu Configuration:");
100 LogSetting("System_UseDockedMode", Settings::values.use_docked_mode); 101 LogSetting("Controls_UseDockedMode", Settings::values.use_docked_mode);
101 LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0)); 102 LogSetting("System_RngSeed", Settings::values.rng_seed.GetValue().value_or(0));
102 LogSetting("System_CurrentUser", Settings::values.current_user); 103 LogSetting("System_CurrentUser", Settings::values.current_user);
103 LogSetting("System_LanguageIndex", Settings::values.language_index); 104 LogSetting("System_LanguageIndex", Settings::values.language_index.GetValue());
104 LogSetting("System_RegionIndex", Settings::values.region_index); 105 LogSetting("System_RegionIndex", Settings::values.region_index.GetValue());
105 LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index); 106 LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index.GetValue());
106 LogSetting("Core_UseMultiCore", Settings::values.use_multi_core); 107 LogSetting("Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
107 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); 108 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor.GetValue());
108 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); 109 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
109 LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); 110 LogSetting("Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
110 LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); 111 LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache.GetValue());
111 LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy); 112 LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy.GetValue());
112 LogSetting("Renderer_UseAsynchronousGpuEmulation", 113 LogSetting("Renderer_UseAsynchronousGpuEmulation",
113 Settings::values.use_asynchronous_gpu_emulation); 114 Settings::values.use_asynchronous_gpu_emulation.GetValue());
114 LogSetting("Renderer_UseVsync", Settings::values.use_vsync); 115 LogSetting("Renderer_UseVsync", Settings::values.use_vsync.GetValue());
115 LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders); 116 LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders.GetValue());
116 LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy); 117 LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy.GetValue());
117 LogSetting("Audio_OutputEngine", Settings::values.sink_id); 118 LogSetting("Audio_OutputEngine", Settings::values.sink_id);
118 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); 119 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching.GetValue());
119 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); 120 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
120 LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd); 121 LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
121 LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); 122 LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
@@ -127,12 +128,60 @@ void LogSettings() {
127 LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local); 128 LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local);
128} 129}
129 130
131float Volume() {
132 if (values.audio_muted) {
133 return 0.0f;
134 }
135 return values.volume.GetValue();
136}
137
130bool IsGPULevelExtreme() { 138bool IsGPULevelExtreme() {
131 return values.gpu_accuracy == GPUAccuracy::Extreme; 139 return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme;
132} 140}
133 141
134bool IsGPULevelHigh() { 142bool IsGPULevelHigh() {
135 return values.gpu_accuracy == GPUAccuracy::Extreme || values.gpu_accuracy == GPUAccuracy::High; 143 return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme ||
144 values.gpu_accuracy.GetValue() == GPUAccuracy::High;
145}
146
147void RestoreGlobalState() {
148 // If a game is running, DO NOT restore the global settings state
149 if (Core::System::GetInstance().IsPoweredOn()) {
150 return;
151 }
152
153 // Audio
154 values.enable_audio_stretching.SetGlobal(true);
155 values.volume.SetGlobal(true);
156
157 // Core
158 values.use_multi_core.SetGlobal(true);
159
160 // Renderer
161 values.renderer_backend.SetGlobal(true);
162 values.vulkan_device.SetGlobal(true);
163 values.aspect_ratio.SetGlobal(true);
164 values.max_anisotropy.SetGlobal(true);
165 values.use_frame_limit.SetGlobal(true);
166 values.frame_limit.SetGlobal(true);
167 values.use_disk_shader_cache.SetGlobal(true);
168 values.gpu_accuracy.SetGlobal(true);
169 values.use_asynchronous_gpu_emulation.SetGlobal(true);
170 values.use_vsync.SetGlobal(true);
171 values.use_assembly_shaders.SetGlobal(true);
172 values.use_fast_gpu_time.SetGlobal(true);
173 values.force_30fps_mode.SetGlobal(true);
174 values.bg_red.SetGlobal(true);
175 values.bg_green.SetGlobal(true);
176 values.bg_blue.SetGlobal(true);
177
178 // System
179 values.language_index.SetGlobal(true);
180 values.region_index.SetGlobal(true);
181 values.time_zone_index.SetGlobal(true);
182 values.rng_seed.SetGlobal(true);
183 values.custom_rtc.SetGlobal(true);
184 values.sound_index.SetGlobal(true);
136} 185}
137 186
138} // namespace Settings 187} // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index 78eb33737..850ca4072 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -382,20 +382,85 @@ enum class GPUAccuracy : u32 {
382 Extreme = 2, 382 Extreme = 2,
383}; 383};
384 384
385extern bool configuring_global;
386
387template <typename Type>
388class Setting final {
389public:
390 Setting() = default;
391 explicit Setting(Type val) : global{val} {}
392 ~Setting() = default;
393 void SetGlobal(bool to_global) {
394 use_global = to_global;
395 }
396 bool UsingGlobal() const {
397 return use_global;
398 }
399 Type GetValue(bool need_global = false) const {
400 if (use_global || need_global) {
401 return global;
402 }
403 return local;
404 }
405 void SetValue(const Type& value) {
406 if (use_global) {
407 global = value;
408 } else {
409 local = value;
410 }
411 }
412
413private:
414 bool use_global = true;
415 Type global{};
416 Type local{};
417};
418
385struct Values { 419struct Values {
420 // Audio
421 std::string audio_device_id;
422 std::string sink_id;
423 bool audio_muted;
424 Setting<bool> enable_audio_stretching;
425 Setting<float> volume;
426
427 // Core
428 Setting<bool> use_multi_core;
429
430 // Renderer
431 Setting<RendererBackend> renderer_backend;
432 bool renderer_debug;
433 Setting<int> vulkan_device;
434
435 Setting<u16> resolution_factor = Setting(static_cast<u16>(1));
436 Setting<int> aspect_ratio;
437 Setting<int> max_anisotropy;
438 Setting<bool> use_frame_limit;
439 Setting<u16> frame_limit;
440 Setting<bool> use_disk_shader_cache;
441 Setting<GPUAccuracy> gpu_accuracy;
442 Setting<bool> use_asynchronous_gpu_emulation;
443 Setting<bool> use_vsync;
444 Setting<bool> use_assembly_shaders;
445 Setting<bool> force_30fps_mode;
446 Setting<bool> use_fast_gpu_time;
447
448 Setting<float> bg_red;
449 Setting<float> bg_green;
450 Setting<float> bg_blue;
451
386 // System 452 // System
387 bool use_docked_mode; 453 Setting<std::optional<u32>> rng_seed;
388 std::optional<u32> rng_seed;
389 // Measured in seconds since epoch 454 // Measured in seconds since epoch
390 std::optional<std::chrono::seconds> custom_rtc; 455 Setting<std::optional<std::chrono::seconds>> custom_rtc;
391 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` 456 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
392 std::chrono::seconds custom_rtc_differential; 457 std::chrono::seconds custom_rtc_differential;
393 458
394 s32 current_user; 459 s32 current_user;
395 s32 language_index; 460 Setting<s32> language_index;
396 s32 region_index; 461 Setting<s32> region_index;
397 s32 time_zone_index; 462 Setting<s32> time_zone_index;
398 s32 sound_index; 463 Setting<s32> sound_index;
399 464
400 // Controls 465 // Controls
401 std::array<PlayerInput, 10> players; 466 std::array<PlayerInput, 10> players;
@@ -419,8 +484,7 @@ struct Values {
419 u16 udp_input_port; 484 u16 udp_input_port;
420 u8 udp_pad_index; 485 u8 udp_pad_index;
421 486
422 // Core 487 bool use_docked_mode;
423 bool use_multi_core;
424 488
425 // Data Storage 489 // Data Storage
426 bool use_virtual_sd; 490 bool use_virtual_sd;
@@ -432,38 +496,6 @@ struct Values {
432 NANDUserSize nand_user_size; 496 NANDUserSize nand_user_size;
433 SDMCSize sdmc_size; 497 SDMCSize sdmc_size;
434 498
435 // Renderer
436 RendererBackend renderer_backend;
437 bool renderer_debug;
438 int vulkan_device;
439
440 float resolution_factor;
441 int aspect_ratio;
442 int max_anisotropy;
443 bool use_frame_limit;
444 u16 frame_limit;
445 bool use_disk_shader_cache;
446 GPUAccuracy gpu_accuracy;
447 bool use_asynchronous_gpu_emulation;
448 bool use_vsync;
449 bool use_assembly_shaders;
450 bool force_30fps_mode;
451 bool use_fast_gpu_time;
452
453 float bg_red;
454 float bg_green;
455 float bg_blue;
456
457 std::string log_filter;
458
459 bool use_dev_keys;
460
461 // Audio
462 std::string sink_id;
463 bool enable_audio_stretching;
464 std::string audio_device_id;
465 float volume;
466
467 // Debugging 499 // Debugging
468 bool record_frame_times; 500 bool record_frame_times;
469 bool use_gdbstub; 501 bool use_gdbstub;
@@ -474,8 +506,13 @@ struct Values {
474 bool reporting_services; 506 bool reporting_services;
475 bool quest_flag; 507 bool quest_flag;
476 bool disable_cpu_opt; 508 bool disable_cpu_opt;
509 bool disable_macro_jit;
477 510
478 // BCAT 511 // Misceallaneous
512 std::string log_filter;
513 bool use_dev_keys;
514
515 // Services
479 std::string bcat_backend; 516 std::string bcat_backend;
480 bool bcat_boxcat_local; 517 bool bcat_boxcat_local;
481 518
@@ -489,6 +526,8 @@ struct Values {
489 std::map<u64, std::vector<std::string>> disabled_addons; 526 std::map<u64, std::vector<std::string>> disabled_addons;
490} extern values; 527} extern values;
491 528
529float Volume();
530
492bool IsGPULevelExtreme(); 531bool IsGPULevelExtreme();
493bool IsGPULevelHigh(); 532bool IsGPULevelHigh();
494 533
@@ -497,4 +536,7 @@ std::string GetTimeZoneString();
497void Apply(); 536void Apply();
498void LogSettings(); 537void LogSettings();
499 538
539// Restore the global state of all applicable settings in the Values struct
540void RestoreGlobalState();
541
500} // namespace Settings 542} // namespace Settings
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index c781b3cfc..78915e6db 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -189,19 +189,24 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
189 // Log user configuration information 189 // Log user configuration information
190 constexpr auto field_type = Telemetry::FieldType::UserConfig; 190 constexpr auto field_type = Telemetry::FieldType::UserConfig;
191 AddField(field_type, "Audio_SinkId", Settings::values.sink_id); 191 AddField(field_type, "Audio_SinkId", Settings::values.sink_id);
192 AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); 192 AddField(field_type, "Audio_EnableAudioStretching",
193 AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core); 193 Settings::values.enable_audio_stretching.GetValue());
194 AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend)); 194 AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
195 AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor); 195 AddField(field_type, "Renderer_Backend",
196 AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); 196 TranslateRenderer(Settings::values.renderer_backend.GetValue()));
197 AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit); 197 AddField(field_type, "Renderer_ResolutionFactor",
198 AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); 198 Settings::values.resolution_factor.GetValue());
199 AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
200 AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
201 AddField(field_type, "Renderer_UseDiskShaderCache",
202 Settings::values.use_disk_shader_cache.GetValue());
199 AddField(field_type, "Renderer_GPUAccuracyLevel", 203 AddField(field_type, "Renderer_GPUAccuracyLevel",
200 TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy)); 204 TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
201 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", 205 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
202 Settings::values.use_asynchronous_gpu_emulation); 206 Settings::values.use_asynchronous_gpu_emulation.GetValue());
203 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync); 207 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
204 AddField(field_type, "Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders); 208 AddField(field_type, "Renderer_UseAssemblyShaders",
209 Settings::values.use_assembly_shaders.GetValue());
205 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); 210 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
206} 211}
207 212
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index b2c6c537e..8b0c50d11 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -14,7 +14,7 @@
14namespace Tools { 14namespace Tools {
15namespace { 15namespace {
16 16
17constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); 17constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(1000000000 / 60);
18 18
19u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { 19u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
20 switch (width) { 20 switch (width) {
@@ -57,7 +57,7 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m
57 : core_timing{core_timing_}, memory{memory_} { 57 : core_timing{core_timing_}, memory{memory_} {
58 event = Core::Timing::CreateEvent( 58 event = Core::Timing::CreateEvent(
59 "MemoryFreezer::FrameCallback", 59 "MemoryFreezer::FrameCallback",
60 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); 60 [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); });
61 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); 61 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event);
62} 62}
63 63
@@ -158,7 +158,7 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const {
158 return entries; 158 return entries;
159} 159}
160 160
161void Freezer::FrameCallback(u64 userdata, s64 cycles_late) { 161void Freezer::FrameCallback(u64 userdata, s64 ns_late) {
162 if (!IsActive()) { 162 if (!IsActive()) {
163 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); 163 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
164 return; 164 return;
@@ -173,7 +173,7 @@ void Freezer::FrameCallback(u64 userdata, s64 cycles_late) {
173 MemoryWriteWidth(memory, entry.width, entry.address, entry.value); 173 MemoryWriteWidth(memory, entry.width, entry.address, entry.value);
174 } 174 }
175 175
176 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event); 176 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - ns_late, event);
177} 177}
178 178
179void Freezer::FillEntryReads() { 179void Freezer::FillEntryReads() {
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index a9c2392b1..3bd76dd23 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -7,6 +7,10 @@ add_library(input_common STATIC
7 main.h 7 main.h
8 motion_emu.cpp 8 motion_emu.cpp
9 motion_emu.h 9 motion_emu.h
10 gcadapter/gc_adapter.cpp
11 gcadapter/gc_adapter.h
12 gcadapter/gc_poller.cpp
13 gcadapter/gc_poller.h
10 sdl/sdl.cpp 14 sdl/sdl.cpp
11 sdl/sdl.h 15 sdl/sdl.h
12 udp/client.cpp 16 udp/client.cpp
@@ -26,5 +30,7 @@ if(SDL2_FOUND)
26 target_compile_definitions(input_common PRIVATE HAVE_SDL2) 30 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
27endif() 31endif()
28 32
33target_link_libraries(input_common PUBLIC ${LIBUSB_LIBRARIES})
34
29create_target_directory_groups(input_common) 35create_target_directory_groups(input_common)
30target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) 36target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
new file mode 100644
index 000000000..6d9f4d9eb
--- /dev/null
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -0,0 +1,398 @@
1// Copyright 2014 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <thread>
7#include "common/logging/log.h"
8#include "input_common/gcadapter/gc_adapter.h"
9
10namespace GCAdapter {
11
12/// Used to loop through and assign button in poller
13constexpr std::array<PadButton, 12> PadButtonArray{
14 PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN,
15 PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R,
16 PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B,
17 PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START,
18};
19
20Adapter::Adapter() {
21 if (usb_adapter_handle != nullptr) {
22 return;
23 }
24 LOG_INFO(Input, "GC Adapter Initialization started");
25
26 current_status = NO_ADAPTER_DETECTED;
27
28 const int init_res = libusb_init(&libusb_ctx);
29 if (init_res == LIBUSB_SUCCESS) {
30 StartScanThread();
31 } else {
32 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
33 }
34}
35
36GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) {
37 GCPadStatus pad = {};
38 bool get_origin = false;
39
40 ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4);
41 if (type != ControllerTypes::None) {
42 get_origin = true;
43 }
44
45 adapter_controllers_status[port] = type;
46
47 static constexpr std::array<PadButton, 8> b1_buttons{
48 PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X,
49 PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT,
50 PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP,
51 };
52
53 static constexpr std::array<PadButton, 4> b2_buttons{
54 PadButton::PAD_BUTTON_START,
55 PadButton::PAD_TRIGGER_Z,
56 PadButton::PAD_TRIGGER_R,
57 PadButton::PAD_TRIGGER_L,
58 };
59
60 if (adapter_controllers_status[port] != ControllerTypes::None) {
61 const u8 b1 = adapter_payload[1 + (9 * port) + 1];
62 const u8 b2 = adapter_payload[1 + (9 * port) + 2];
63
64 for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
65 if ((b1 & (1U << i)) != 0) {
66 pad.button |= static_cast<u16>(b1_buttons[i]);
67 }
68 }
69
70 for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
71 if ((b2 & (1U << j)) != 0) {
72 pad.button |= static_cast<u16>(b2_buttons[j]);
73 }
74 }
75
76 if (get_origin) {
77 pad.button |= PAD_GET_ORIGIN;
78 }
79
80 pad.stick_x = adapter_payload[1 + (9 * port) + 3];
81 pad.stick_y = adapter_payload[1 + (9 * port) + 4];
82 pad.substick_x = adapter_payload[1 + (9 * port) + 5];
83 pad.substick_y = adapter_payload[1 + (9 * port) + 6];
84 pad.trigger_left = adapter_payload[1 + (9 * port) + 7];
85 pad.trigger_right = adapter_payload[1 + (9 * port) + 8];
86 }
87 return pad;
88}
89
90void Adapter::PadToState(const GCPadStatus& pad, GCState& state) {
91 for (const auto& button : PadButtonArray) {
92 const u16 button_value = static_cast<u16>(button);
93 state.buttons.insert_or_assign(button_value, pad.button & button_value);
94 }
95
96 state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickX), pad.stick_x);
97 state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickY), pad.stick_y);
98 state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickX), pad.substick_x);
99 state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickY), pad.substick_y);
100 state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerLeft), pad.trigger_left);
101 state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerRight), pad.trigger_right);
102}
103
104void Adapter::Read() {
105 LOG_DEBUG(Input, "GC Adapter Read() thread started");
106
107 int payload_size_in, payload_size_copy;
108 std::array<u8, 37> adapter_payload;
109 std::array<u8, 37> adapter_payload_copy;
110 std::array<GCPadStatus, 4> pads;
111
112 while (adapter_thread_running) {
113 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
114 sizeof(adapter_payload), &payload_size_in, 16);
115 payload_size_copy = 0;
116 // this mutex might be redundant?
117 {
118 std::lock_guard<std::mutex> lk(s_mutex);
119 std::copy(std::begin(adapter_payload), std::end(adapter_payload),
120 std::begin(adapter_payload_copy));
121 payload_size_copy = payload_size_in;
122 }
123
124 if (payload_size_copy != sizeof(adapter_payload_copy) ||
125 adapter_payload_copy[0] != LIBUSB_DT_HID) {
126 LOG_ERROR(Input, "error reading payload (size: {}, type: {:02x})", payload_size_copy,
127 adapter_payload_copy[0]);
128 adapter_thread_running = false; // error reading from adapter, stop reading.
129 break;
130 }
131 for (std::size_t port = 0; port < pads.size(); ++port) {
132 pads[port] = GetPadStatus(port, adapter_payload_copy);
133 if (DeviceConnected(port) && configuring) {
134 if (pads[port].button != PAD_GET_ORIGIN) {
135 pad_queue[port].Push(pads[port]);
136 }
137
138 // Accounting for a threshold here because of some controller variance
139 if (pads[port].stick_x > pads[port].MAIN_STICK_CENTER_X + pads[port].THRESHOLD ||
140 pads[port].stick_x < pads[port].MAIN_STICK_CENTER_X - pads[port].THRESHOLD) {
141 pads[port].axis = GCAdapter::PadAxes::StickX;
142 pads[port].axis_value = pads[port].stick_x;
143 pad_queue[port].Push(pads[port]);
144 }
145 if (pads[port].stick_y > pads[port].MAIN_STICK_CENTER_Y + pads[port].THRESHOLD ||
146 pads[port].stick_y < pads[port].MAIN_STICK_CENTER_Y - pads[port].THRESHOLD) {
147 pads[port].axis = GCAdapter::PadAxes::StickY;
148 pads[port].axis_value = pads[port].stick_y;
149 pad_queue[port].Push(pads[port]);
150 }
151 if (pads[port].substick_x > pads[port].C_STICK_CENTER_X + pads[port].THRESHOLD ||
152 pads[port].substick_x < pads[port].C_STICK_CENTER_X - pads[port].THRESHOLD) {
153 pads[port].axis = GCAdapter::PadAxes::SubstickX;
154 pads[port].axis_value = pads[port].substick_x;
155 pad_queue[port].Push(pads[port]);
156 }
157 if (pads[port].substick_y > pads[port].C_STICK_CENTER_Y + pads[port].THRESHOLD ||
158 pads[port].substick_y < pads[port].C_STICK_CENTER_Y - pads[port].THRESHOLD) {
159 pads[port].axis = GCAdapter::PadAxes::SubstickY;
160 pads[port].axis_value = pads[port].substick_y;
161 pad_queue[port].Push(pads[port]);
162 }
163 if (pads[port].trigger_left > pads[port].TRIGGER_THRESHOLD) {
164 pads[port].axis = GCAdapter::PadAxes::TriggerLeft;
165 pads[port].axis_value = pads[port].trigger_left;
166 pad_queue[port].Push(pads[port]);
167 }
168 if (pads[port].trigger_right > pads[port].TRIGGER_THRESHOLD) {
169 pads[port].axis = GCAdapter::PadAxes::TriggerRight;
170 pads[port].axis_value = pads[port].trigger_right;
171 pad_queue[port].Push(pads[port]);
172 }
173 }
174 PadToState(pads[port], state[port]);
175 }
176 std::this_thread::yield();
177 }
178}
179
180void Adapter::ScanThreadFunc() {
181 LOG_INFO(Input, "GC Adapter scanning thread started");
182
183 while (detect_thread_running) {
184 if (usb_adapter_handle == nullptr) {
185 std::lock_guard<std::mutex> lk(initialization_mutex);
186 Setup();
187 }
188 std::this_thread::sleep_for(std::chrono::milliseconds(500));
189 }
190}
191
192void Adapter::StartScanThread() {
193 if (detect_thread_running) {
194 return;
195 }
196 if (!libusb_ctx) {
197 return;
198 }
199
200 detect_thread_running = true;
201 detect_thread = std::thread([=] { ScanThreadFunc(); });
202}
203
204void Adapter::StopScanThread() {
205 detect_thread_running = false;
206 detect_thread.join();
207}
208
209void Adapter::Setup() {
210 // Reset the error status in case the adapter gets unplugged
211 if (current_status < 0) {
212 current_status = NO_ADAPTER_DETECTED;
213 }
214
215 adapter_controllers_status.fill(ControllerTypes::None);
216
217 // pointer to list of connected usb devices
218 libusb_device** devices{};
219
220 // populate the list of devices, get the count
221 const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
222 if (device_count < 0) {
223 LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
224 detect_thread_running = false; // Stop the loop constantly checking for gc adapter
225 // TODO: For hotplug+gc adapter checkbox implementation, revert this.
226 return;
227 }
228
229 if (devices != nullptr) {
230 for (std::size_t index = 0; index < device_count; ++index) {
231 if (CheckDeviceAccess(devices[index])) {
232 // GC Adapter found and accessible, registering it
233 GetGCEndpoint(devices[index]);
234 break;
235 }
236 }
237 libusb_free_device_list(devices, 1);
238 }
239}
240
241bool Adapter::CheckDeviceAccess(libusb_device* device) {
242 libusb_device_descriptor desc;
243 const int get_descriptor_error = libusb_get_device_descriptor(device, &desc);
244 if (get_descriptor_error) {
245 // could not acquire the descriptor, no point in trying to use it.
246 LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}",
247 get_descriptor_error);
248 return false;
249 }
250
251 if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) {
252 // This isn't the device we are looking for.
253 return false;
254 }
255 const int open_error = libusb_open(device, &usb_adapter_handle);
256
257 if (open_error == LIBUSB_ERROR_ACCESS) {
258 LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.",
259 desc.idVendor, desc.idProduct);
260 return false;
261 }
262 if (open_error) {
263 LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error);
264 return false;
265 }
266
267 int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
268 if (kernel_driver_error == 1) {
269 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0);
270 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
271 LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}",
272 kernel_driver_error);
273 }
274 }
275
276 if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
277 libusb_close(usb_adapter_handle);
278 usb_adapter_handle = nullptr;
279 return false;
280 }
281
282 const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0);
283 if (interface_claim_error) {
284 LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error);
285 libusb_close(usb_adapter_handle);
286 usb_adapter_handle = nullptr;
287 return false;
288 }
289
290 return true;
291}
292
293void Adapter::GetGCEndpoint(libusb_device* device) {
294 libusb_config_descriptor* config = nullptr;
295 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
296 if (config_descriptor_return != LIBUSB_SUCCESS) {
297 LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
298 config_descriptor_return);
299 return;
300 }
301
302 for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
303 const libusb_interface* interfaceContainer = &config->interface[ic];
304 for (int i = 0; i < interfaceContainer->num_altsetting; i++) {
305 const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i];
306 for (u8 e = 0; e < interface->bNumEndpoints; e++) {
307 const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e];
308 if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
309 input_endpoint = endpoint->bEndpointAddress;
310 } else {
311 output_endpoint = endpoint->bEndpointAddress;
312 }
313 }
314 }
315 }
316 // This transfer seems to be responsible for clearing the state of the adapter
317 // Used to clear the "busy" state of when the device is unexpectedly unplugged
318 unsigned char clear_payload = 0x13;
319 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload,
320 sizeof(clear_payload), nullptr, 16);
321
322 adapter_thread_running = true;
323 current_status = ADAPTER_DETECTED;
324 adapter_input_thread = std::thread([=] { Read(); }); // Read input
325}
326
327Adapter::~Adapter() {
328 StopScanThread();
329 Reset();
330}
331
332void Adapter::Reset() {
333 std::unique_lock<std::mutex> lock(initialization_mutex, std::defer_lock);
334 if (!lock.try_lock()) {
335 return;
336 }
337 if (current_status != ADAPTER_DETECTED) {
338 return;
339 }
340
341 if (adapter_thread_running) {
342 adapter_thread_running = false;
343 }
344 adapter_input_thread.join();
345
346 adapter_controllers_status.fill(ControllerTypes::None);
347 current_status = NO_ADAPTER_DETECTED;
348
349 if (usb_adapter_handle) {
350 libusb_release_interface(usb_adapter_handle, 1);
351 libusb_close(usb_adapter_handle);
352 usb_adapter_handle = nullptr;
353 }
354
355 if (libusb_ctx) {
356 libusb_exit(libusb_ctx);
357 }
358}
359
360bool Adapter::DeviceConnected(int port) {
361 return adapter_controllers_status[port] != ControllerTypes::None;
362}
363
364void Adapter::ResetDeviceType(int port) {
365 adapter_controllers_status[port] = ControllerTypes::None;
366}
367
368void Adapter::BeginConfiguration() {
369 for (auto& pq : pad_queue) {
370 pq.Clear();
371 }
372 configuring = true;
373}
374
375void Adapter::EndConfiguration() {
376 for (auto& pq : pad_queue) {
377 pq.Clear();
378 }
379 configuring = false;
380}
381
382std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() {
383 return pad_queue;
384}
385
386const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const {
387 return pad_queue;
388}
389
390std::array<GCState, 4>& Adapter::GetPadState() {
391 return state;
392}
393
394const std::array<GCState, 4>& Adapter::GetPadState() const {
395 return state;
396}
397
398} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
new file mode 100644
index 000000000..b1c2a1958
--- /dev/null
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -0,0 +1,161 @@
1// Copyright 2014 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#pragma once
6#include <algorithm>
7#include <functional>
8#include <mutex>
9#include <thread>
10#include <unordered_map>
11#include <libusb.h>
12#include "common/common_types.h"
13#include "common/threadsafe_queue.h"
14
15namespace GCAdapter {
16
17enum {
18 PAD_USE_ORIGIN = 0x0080,
19 PAD_GET_ORIGIN = 0x2000,
20 PAD_ERR_STATUS = 0x8000,
21};
22
23enum class PadButton {
24 PAD_BUTTON_LEFT = 0x0001,
25 PAD_BUTTON_RIGHT = 0x0002,
26 PAD_BUTTON_DOWN = 0x0004,
27 PAD_BUTTON_UP = 0x0008,
28 PAD_TRIGGER_Z = 0x0010,
29 PAD_TRIGGER_R = 0x0020,
30 PAD_TRIGGER_L = 0x0040,
31 PAD_BUTTON_A = 0x0100,
32 PAD_BUTTON_B = 0x0200,
33 PAD_BUTTON_X = 0x0400,
34 PAD_BUTTON_Y = 0x0800,
35 PAD_BUTTON_START = 0x1000,
36 // Below is for compatibility with "AxisButton" type
37 PAD_STICK = 0x2000,
38};
39
40extern const std::array<PadButton, 12> PadButtonArray;
41
42enum class PadAxes : u8 {
43 StickX,
44 StickY,
45 SubstickX,
46 SubstickY,
47 TriggerLeft,
48 TriggerRight,
49 Undefined,
50};
51
52struct GCPadStatus {
53 u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
54 u8 stick_x{}; // 0 <= stick_x <= 255
55 u8 stick_y{}; // 0 <= stick_y <= 255
56 u8 substick_x{}; // 0 <= substick_x <= 255
57 u8 substick_y{}; // 0 <= substick_y <= 255
58 u8 trigger_left{}; // 0 <= trigger_left <= 255
59 u8 trigger_right{}; // 0 <= trigger_right <= 255
60
61 static constexpr u8 MAIN_STICK_CENTER_X = 0x80;
62 static constexpr u8 MAIN_STICK_CENTER_Y = 0x80;
63 static constexpr u8 MAIN_STICK_RADIUS = 0x7f;
64 static constexpr u8 C_STICK_CENTER_X = 0x80;
65 static constexpr u8 C_STICK_CENTER_Y = 0x80;
66 static constexpr u8 C_STICK_RADIUS = 0x7f;
67 static constexpr u8 THRESHOLD = 10;
68
69 // 256/4, at least a quarter press to count as a press. For polling mostly
70 static constexpr u8 TRIGGER_THRESHOLD = 64;
71
72 u8 port{};
73 PadAxes axis{PadAxes::Undefined};
74 u8 axis_value{255};
75};
76
77struct GCState {
78 std::unordered_map<int, bool> buttons;
79 std::unordered_map<int, u16> axes;
80};
81
82enum class ControllerTypes { None, Wired, Wireless };
83
84enum {
85 NO_ADAPTER_DETECTED = 0,
86 ADAPTER_DETECTED = 1,
87};
88
89class Adapter {
90public:
91 /// Initialize the GC Adapter capture and read sequence
92 Adapter();
93
94 /// Close the adapter read thread and release the adapter
95 ~Adapter();
96 /// Used for polling
97 void BeginConfiguration();
98 void EndConfiguration();
99
100 std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
101 const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
102
103 std::array<GCState, 4>& GetPadState();
104 const std::array<GCState, 4>& GetPadState() const;
105
106private:
107 GCPadStatus GetPadStatus(int port, const std::array<u8, 37>& adapter_payload);
108
109 void PadToState(const GCPadStatus& pad, GCState& state);
110
111 void Read();
112 void ScanThreadFunc();
113 /// Begin scanning for the GC Adapter.
114 void StartScanThread();
115
116 /// Stop scanning for the adapter
117 void StopScanThread();
118
119 /// Returns true if there is a device connected to port
120 bool DeviceConnected(int port);
121
122 /// Resets status of device connected to port
123 void ResetDeviceType(int port);
124
125 /// Returns true if we successfully gain access to GC Adapter
126 bool CheckDeviceAccess(libusb_device* device);
127
128 /// Captures GC Adapter endpoint address,
129 void GetGCEndpoint(libusb_device* device);
130
131 /// For shutting down, clear all data, join all threads, release usb
132 void Reset();
133
134 /// For use in initialization, querying devices to find the adapter
135 void Setup();
136
137 int current_status = NO_ADAPTER_DETECTED;
138 libusb_device_handle* usb_adapter_handle = nullptr;
139 std::array<ControllerTypes, 4> adapter_controllers_status{};
140
141 std::mutex s_mutex;
142
143 std::thread adapter_input_thread;
144 bool adapter_thread_running;
145
146 std::mutex initialization_mutex;
147 std::thread detect_thread;
148 bool detect_thread_running = false;
149
150 libusb_context* libusb_ctx;
151
152 u8 input_endpoint = 0;
153 u8 output_endpoint = 0;
154
155 bool configuring = false;
156
157 std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
158 std::array<GCState, 4> state;
159};
160
161} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
new file mode 100644
index 000000000..385ce8430
--- /dev/null
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -0,0 +1,272 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include <list>
7#include <mutex>
8#include <utility>
9#include "common/threadsafe_queue.h"
10#include "input_common/gcadapter/gc_adapter.h"
11#include "input_common/gcadapter/gc_poller.h"
12
13namespace InputCommon {
14
15class GCButton final : public Input::ButtonDevice {
16public:
17 explicit GCButton(int port_, int button_, GCAdapter::Adapter* adapter)
18 : port(port_), button(button_), gcadapter(adapter) {}
19
20 ~GCButton() override;
21
22 bool GetStatus() const override {
23 return gcadapter->GetPadState()[port].buttons.at(button);
24 }
25
26private:
27 const int port;
28 const int button;
29 GCAdapter::Adapter* gcadapter;
30};
31
32class GCAxisButton final : public Input::ButtonDevice {
33public:
34 explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_,
35 GCAdapter::Adapter* adapter)
36 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
37 gcadapter(adapter) {
38 // L/R triggers range is only in positive direction beginning near 0
39 // 0.0 threshold equates to near half trigger press, but threshold accounts for variability.
40 if (axis > 3) {
41 threshold *= -0.5;
42 }
43 }
44
45 bool GetStatus() const override {
46 const float axis_value = (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f;
47 if (trigger_if_greater) {
48 // TODO: Might be worthwile to set a slider for the trigger threshold. It is currently
49 // always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
50 return axis_value > threshold;
51 }
52 return axis_value < -threshold;
53 }
54
55private:
56 const int port;
57 const int axis;
58 float threshold;
59 bool trigger_if_greater;
60 GCAdapter::Adapter* gcadapter;
61};
62
63GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
64 : adapter(std::move(adapter_)) {}
65
66GCButton::~GCButton() = default;
67
68std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) {
69 const int button_id = params.Get("button", 0);
70 const int port = params.Get("port", 0);
71
72 constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK);
73
74 // button is not an axis/stick button
75 if (button_id != PAD_STICK_ID) {
76 auto button = std::make_unique<GCButton>(port, button_id, adapter.get());
77 return std::move(button);
78 }
79
80 // For Axis buttons, used by the binary sticks.
81 if (button_id == PAD_STICK_ID) {
82 const int axis = params.Get("axis", 0);
83 const float threshold = params.Get("threshold", 0.25f);
84 const std::string direction_name = params.Get("direction", "");
85 bool trigger_if_greater;
86 if (direction_name == "+") {
87 trigger_if_greater = true;
88 } else if (direction_name == "-") {
89 trigger_if_greater = false;
90 } else {
91 trigger_if_greater = true;
92 LOG_ERROR(Input, "Unknown direction {}", direction_name);
93 }
94 return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater,
95 adapter.get());
96 }
97}
98
99Common::ParamPackage GCButtonFactory::GetNextInput() {
100 Common::ParamPackage params;
101 GCAdapter::GCPadStatus pad;
102 auto& queue = adapter->GetPadQueue();
103 for (std::size_t port = 0; port < queue.size(); ++port) {
104 while (queue[port].Pop(pad)) {
105 // This while loop will break on the earliest detected button
106 params.Set("engine", "gcpad");
107 params.Set("port", static_cast<int>(port));
108 for (const auto& button : GCAdapter::PadButtonArray) {
109 const u16 button_value = static_cast<u16>(button);
110 if (pad.button & button_value) {
111 params.Set("button", button_value);
112 break;
113 }
114 }
115
116 // For Axis button implementation
117 if (pad.axis != GCAdapter::PadAxes::Undefined) {
118 params.Set("axis", static_cast<u8>(pad.axis));
119 params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK));
120 if (pad.axis_value > 128) {
121 params.Set("direction", "+");
122 params.Set("threshold", "0.25");
123 } else {
124 params.Set("direction", "-");
125 params.Set("threshold", "-0.25");
126 }
127 break;
128 }
129 }
130 }
131 return params;
132}
133
134void GCButtonFactory::BeginConfiguration() {
135 polling = true;
136 adapter->BeginConfiguration();
137}
138
139void GCButtonFactory::EndConfiguration() {
140 polling = false;
141 adapter->EndConfiguration();
142}
143
144class GCAnalog final : public Input::AnalogDevice {
145public:
146 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter)
147 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {}
148
149 float GetAxis(int axis) const {
150 std::lock_guard lock{mutex};
151 // division is not by a perfect 128 to account for some variance in center location
152 // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
153 // [20-230]
154 return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f;
155 }
156
157 std::pair<float, float> GetAnalog(int axis_x, int axis_y) const {
158 float x = GetAxis(axis_x);
159 float y = GetAxis(axis_y);
160
161 // Make sure the coordinates are in the unit circle,
162 // otherwise normalize it.
163 float r = x * x + y * y;
164 if (r > 1.0f) {
165 r = std::sqrt(r);
166 x /= r;
167 y /= r;
168 }
169
170 return {x, y};
171 }
172
173 std::tuple<float, float> GetStatus() const override {
174 const auto [x, y] = GetAnalog(axis_x, axis_y);
175 const float r = std::sqrt((x * x) + (y * y));
176 if (r > deadzone) {
177 return {x / r * (r - deadzone) / (1 - deadzone),
178 y / r * (r - deadzone) / (1 - deadzone)};
179 }
180 return {0.0f, 0.0f};
181 }
182
183 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
184 const auto [x, y] = GetStatus();
185 const float directional_deadzone = 0.4f;
186 switch (direction) {
187 case Input::AnalogDirection::RIGHT:
188 return x > directional_deadzone;
189 case Input::AnalogDirection::LEFT:
190 return x < -directional_deadzone;
191 case Input::AnalogDirection::UP:
192 return y > directional_deadzone;
193 case Input::AnalogDirection::DOWN:
194 return y < -directional_deadzone;
195 }
196 return false;
197 }
198
199private:
200 const int port;
201 const int axis_x;
202 const int axis_y;
203 const float deadzone;
204 mutable std::mutex mutex;
205 GCAdapter::Adapter* gcadapter;
206};
207
208/// An analog device factory that creates analog devices from GC Adapter
209GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
210 : adapter(std::move(adapter_)) {}
211
212/**
213 * Creates analog device from joystick axes
214 * @param params contains parameters for creating the device:
215 * - "port": the nth gcpad on the adapter
216 * - "axis_x": the index of the axis to be bind as x-axis
217 * - "axis_y": the index of the axis to be bind as y-axis
218 */
219std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) {
220 const int port = params.Get("port", 0);
221 const int axis_x = params.Get("axis_x", 0);
222 const int axis_y = params.Get("axis_y", 1);
223 const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
224
225 return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get());
226}
227
228void GCAnalogFactory::BeginConfiguration() {
229 polling = true;
230 adapter->BeginConfiguration();
231}
232
233void GCAnalogFactory::EndConfiguration() {
234 polling = false;
235 adapter->EndConfiguration();
236}
237
238Common::ParamPackage GCAnalogFactory::GetNextInput() {
239 GCAdapter::GCPadStatus pad;
240 auto& queue = adapter->GetPadQueue();
241 for (std::size_t port = 0; port < queue.size(); ++port) {
242 while (queue[port].Pop(pad)) {
243 if (pad.axis == GCAdapter::PadAxes::Undefined ||
244 std::abs((pad.axis_value - 128.0f) / 128.0f) < 0.1) {
245 continue;
246 }
247 // An analog device needs two axes, so we need to store the axis for later and wait for
248 // a second input event. The axes also must be from the same joystick.
249 const u8 axis = static_cast<u8>(pad.axis);
250 if (analog_x_axis == -1) {
251 analog_x_axis = axis;
252 controller_number = port;
253 } else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) {
254 analog_y_axis = axis;
255 }
256 }
257 }
258 Common::ParamPackage params;
259 if (analog_x_axis != -1 && analog_y_axis != -1) {
260 params.Set("engine", "gcpad");
261 params.Set("port", controller_number);
262 params.Set("axis_x", analog_x_axis);
263 params.Set("axis_y", analog_y_axis);
264 analog_x_axis = -1;
265 analog_y_axis = -1;
266 controller_number = -1;
267 return params;
268 }
269 return params;
270}
271
272} // namespace InputCommon
diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h
new file mode 100644
index 000000000..e96af7d51
--- /dev/null
+++ b/src/input_common/gcadapter/gc_poller.h
@@ -0,0 +1,67 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "core/frontend/input.h"
9#include "input_common/gcadapter/gc_adapter.h"
10
11namespace InputCommon {
12
13/**
14 * A button device factory representing a gcpad. It receives gcpad events and forward them
15 * to all button devices it created.
16 */
17class GCButtonFactory final : public Input::Factory<Input::ButtonDevice> {
18public:
19 explicit GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
20
21 /**
22 * Creates a button device from a button press
23 * @param params contains parameters for creating the device:
24 * - "code": the code of the key to bind with the button
25 */
26 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
27
28 Common::ParamPackage GetNextInput();
29
30 /// For device input configuration/polling
31 void BeginConfiguration();
32 void EndConfiguration();
33
34 bool IsPolling() const {
35 return polling;
36 }
37
38private:
39 std::shared_ptr<GCAdapter::Adapter> adapter;
40 bool polling = false;
41};
42
43/// An analog device factory that creates analog devices from GC Adapter
44class GCAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
45public:
46 explicit GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
47
48 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
49 Common::ParamPackage GetNextInput();
50
51 /// For device input configuration/polling
52 void BeginConfiguration();
53 void EndConfiguration();
54
55 bool IsPolling() const {
56 return polling;
57 }
58
59private:
60 std::shared_ptr<GCAdapter::Adapter> adapter;
61 int analog_x_axis = -1;
62 int analog_y_axis = -1;
63 int controller_number = -1;
64 bool polling = false;
65};
66
67} // namespace InputCommon
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp
index 078374be5..afb8e6612 100644
--- a/src/input_common/keyboard.cpp
+++ b/src/input_common/keyboard.cpp
@@ -76,7 +76,7 @@ std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage
76 int key_code = params.Get("code", 0); 76 int key_code = params.Get("code", 0);
77 std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list); 77 std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list);
78 key_button_list->AddKeyButton(key_code, button.get()); 78 key_button_list->AddKeyButton(key_code, button.get());
79 return std::move(button); 79 return button;
80} 80}
81 81
82void Keyboard::PressKey(int key_code) { 82void Keyboard::PressKey(int key_code) {
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 95e351e24..fd0af1019 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -4,8 +4,11 @@
4 4
5#include <memory> 5#include <memory>
6#include <thread> 6#include <thread>
7#include <libusb.h>
7#include "common/param_package.h" 8#include "common/param_package.h"
8#include "input_common/analog_from_button.h" 9#include "input_common/analog_from_button.h"
10#include "input_common/gcadapter/gc_adapter.h"
11#include "input_common/gcadapter/gc_poller.h"
9#include "input_common/keyboard.h" 12#include "input_common/keyboard.h"
10#include "input_common/main.h" 13#include "input_common/main.h"
11#include "input_common/motion_emu.h" 14#include "input_common/motion_emu.h"
@@ -22,8 +25,16 @@ static std::shared_ptr<MotionEmu> motion_emu;
22static std::unique_ptr<SDL::State> sdl; 25static std::unique_ptr<SDL::State> sdl;
23#endif 26#endif
24static std::unique_ptr<CemuhookUDP::State> udp; 27static std::unique_ptr<CemuhookUDP::State> udp;
28static std::shared_ptr<GCButtonFactory> gcbuttons;
29static std::shared_ptr<GCAnalogFactory> gcanalog;
25 30
26void Init() { 31void Init() {
32 auto gcadapter = std::make_shared<GCAdapter::Adapter>();
33 gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
34 Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
35 gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
36 Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
37
27 keyboard = std::make_shared<Keyboard>(); 38 keyboard = std::make_shared<Keyboard>();
28 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); 39 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
29 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", 40 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
@@ -48,6 +59,11 @@ void Shutdown() {
48 sdl.reset(); 59 sdl.reset();
49#endif 60#endif
50 udp.reset(); 61 udp.reset();
62 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
63 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
64
65 gcbuttons.reset();
66 gcanalog.reset();
51} 67}
52 68
53Keyboard* GetKeyboard() { 69Keyboard* GetKeyboard() {
@@ -58,6 +74,14 @@ MotionEmu* GetMotionEmu() {
58 return motion_emu.get(); 74 return motion_emu.get();
59} 75}
60 76
77GCButtonFactory* GetGCButtons() {
78 return gcbuttons.get();
79}
80
81GCAnalogFactory* GetGCAnalogs() {
82 return gcanalog.get();
83}
84
61std::string GenerateKeyboardParam(int key_code) { 85std::string GenerateKeyboardParam(int key_code) {
62 Common::ParamPackage param{ 86 Common::ParamPackage param{
63 {"engine", "keyboard"}, 87 {"engine", "keyboard"},
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 77a0ce90b..0e32856f6 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <vector> 9#include <vector>
10#include "input_common/gcadapter/gc_poller.h"
10 11
11namespace Common { 12namespace Common {
12class ParamPackage; 13class ParamPackage;
@@ -30,6 +31,10 @@ class MotionEmu;
30/// Gets the motion emulation factory. 31/// Gets the motion emulation factory.
31MotionEmu* GetMotionEmu(); 32MotionEmu* GetMotionEmu();
32 33
34GCButtonFactory* GetGCButtons();
35
36GCAnalogFactory* GetGCAnalogs();
37
33/// Generates a serialized param package for creating a keyboard button device 38/// Generates a serialized param package for creating a keyboard button device
34std::string GenerateKeyboardParam(int key_code); 39std::string GenerateKeyboardParam(int key_code);
35 40
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
index 868251628..d4cdf76a3 100644
--- a/src/input_common/motion_emu.cpp
+++ b/src/input_common/motion_emu.cpp
@@ -145,7 +145,7 @@ std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackag
145 // Previously created device is disconnected here. Having two motion devices for 3DS is not 145 // Previously created device is disconnected here. Having two motion devices for 3DS is not
146 // expected. 146 // expected.
147 current_device = device_wrapper->device; 147 current_device = device_wrapper->device;
148 return std::move(device_wrapper); 148 return device_wrapper;
149} 149}
150 150
151void MotionEmu::BeginTilt(int x, int y) { 151void MotionEmu::BeginTilt(int x, int y) {
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index c7038b217..47ef30aa9 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,6 +1,7 @@
1add_executable(tests 1add_executable(tests
2 common/bit_field.cpp 2 common/bit_field.cpp
3 common/bit_utils.cpp 3 common/bit_utils.cpp
4 common/fibers.cpp
4 common/multi_level_queue.cpp 5 common/multi_level_queue.cpp
5 common/param_package.cpp 6 common/param_package.cpp
6 common/ring_buffer.cpp 7 common/ring_buffer.cpp
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp
new file mode 100644
index 000000000..4fd92428f
--- /dev/null
+++ b/src/tests/common/fibers.cpp
@@ -0,0 +1,358 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include <cstdlib>
7#include <functional>
8#include <memory>
9#include <thread>
10#include <unordered_map>
11#include <vector>
12
13#include <catch2/catch.hpp>
14#include <math.h>
15#include "common/common_types.h"
16#include "common/fiber.h"
17#include "common/spin_lock.h"
18
19namespace Common {
20
21class TestControl1 {
22public:
23 TestControl1() = default;
24
25 void DoWork();
26
27 void ExecuteThread(u32 id);
28
29 std::unordered_map<std::thread::id, u32> ids;
30 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
31 std::vector<std::shared_ptr<Common::Fiber>> work_fibers;
32 std::vector<u32> items;
33 std::vector<u32> results;
34};
35
36static void WorkControl1(void* control) {
37 auto* test_control = static_cast<TestControl1*>(control);
38 test_control->DoWork();
39}
40
41void TestControl1::DoWork() {
42 std::thread::id this_id = std::this_thread::get_id();
43 u32 id = ids[this_id];
44 u32 value = items[id];
45 for (u32 i = 0; i < id; i++) {
46 value++;
47 }
48 results[id] = value;
49 Fiber::YieldTo(work_fibers[id], thread_fibers[id]);
50}
51
52void TestControl1::ExecuteThread(u32 id) {
53 std::thread::id this_id = std::this_thread::get_id();
54 ids[this_id] = id;
55 auto thread_fiber = Fiber::ThreadToFiber();
56 thread_fibers[id] = thread_fiber;
57 work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this);
58 items[id] = rand() % 256;
59 Fiber::YieldTo(thread_fibers[id], work_fibers[id]);
60 thread_fibers[id]->Exit();
61}
62
63static void ThreadStart1(u32 id, TestControl1& test_control) {
64 test_control.ExecuteThread(id);
65}
66
67/** This test checks for fiber setup configuration and validates that fibers are
68 * doing all the work required.
69 */
70TEST_CASE("Fibers::Setup", "[common]") {
71 constexpr std::size_t num_threads = 7;
72 TestControl1 test_control{};
73 test_control.thread_fibers.resize(num_threads);
74 test_control.work_fibers.resize(num_threads);
75 test_control.items.resize(num_threads, 0);
76 test_control.results.resize(num_threads, 0);
77 std::vector<std::thread> threads;
78 for (u32 i = 0; i < num_threads; i++) {
79 threads.emplace_back(ThreadStart1, i, std::ref(test_control));
80 }
81 for (u32 i = 0; i < num_threads; i++) {
82 threads[i].join();
83 }
84 for (u32 i = 0; i < num_threads; i++) {
85 REQUIRE(test_control.items[i] + i == test_control.results[i]);
86 }
87}
88
89class TestControl2 {
90public:
91 TestControl2() = default;
92
93 void DoWork1() {
94 trap2 = false;
95 while (trap.load())
96 ;
97 for (u32 i = 0; i < 12000; i++) {
98 value1 += i;
99 }
100 Fiber::YieldTo(fiber1, fiber3);
101 std::thread::id this_id = std::this_thread::get_id();
102 u32 id = ids[this_id];
103 assert1 = id == 1;
104 value2 += 5000;
105 Fiber::YieldTo(fiber1, thread_fibers[id]);
106 }
107
108 void DoWork2() {
109 while (trap2.load())
110 ;
111 value2 = 2000;
112 trap = false;
113 Fiber::YieldTo(fiber2, fiber1);
114 assert3 = false;
115 }
116
117 void DoWork3() {
118 std::thread::id this_id = std::this_thread::get_id();
119 u32 id = ids[this_id];
120 assert2 = id == 0;
121 value1 += 1000;
122 Fiber::YieldTo(fiber3, thread_fibers[id]);
123 }
124
125 void ExecuteThread(u32 id);
126
127 void CallFiber1() {
128 std::thread::id this_id = std::this_thread::get_id();
129 u32 id = ids[this_id];
130 Fiber::YieldTo(thread_fibers[id], fiber1);
131 }
132
133 void CallFiber2() {
134 std::thread::id this_id = std::this_thread::get_id();
135 u32 id = ids[this_id];
136 Fiber::YieldTo(thread_fibers[id], fiber2);
137 }
138
139 void Exit();
140
141 bool assert1{};
142 bool assert2{};
143 bool assert3{true};
144 u32 value1{};
145 u32 value2{};
146 std::atomic<bool> trap{true};
147 std::atomic<bool> trap2{true};
148 std::unordered_map<std::thread::id, u32> ids;
149 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
150 std::shared_ptr<Common::Fiber> fiber1;
151 std::shared_ptr<Common::Fiber> fiber2;
152 std::shared_ptr<Common::Fiber> fiber3;
153};
154
155static void WorkControl2_1(void* control) {
156 auto* test_control = static_cast<TestControl2*>(control);
157 test_control->DoWork1();
158}
159
160static void WorkControl2_2(void* control) {
161 auto* test_control = static_cast<TestControl2*>(control);
162 test_control->DoWork2();
163}
164
165static void WorkControl2_3(void* control) {
166 auto* test_control = static_cast<TestControl2*>(control);
167 test_control->DoWork3();
168}
169
170void TestControl2::ExecuteThread(u32 id) {
171 std::thread::id this_id = std::this_thread::get_id();
172 ids[this_id] = id;
173 auto thread_fiber = Fiber::ThreadToFiber();
174 thread_fibers[id] = thread_fiber;
175}
176
177void TestControl2::Exit() {
178 std::thread::id this_id = std::this_thread::get_id();
179 u32 id = ids[this_id];
180 thread_fibers[id]->Exit();
181}
182
183static void ThreadStart2_1(u32 id, TestControl2& test_control) {
184 test_control.ExecuteThread(id);
185 test_control.CallFiber1();
186 test_control.Exit();
187}
188
189static void ThreadStart2_2(u32 id, TestControl2& test_control) {
190 test_control.ExecuteThread(id);
191 test_control.CallFiber2();
192 test_control.Exit();
193}
194
195/** This test checks for fiber thread exchange configuration and validates that fibers are
196 * that a fiber has been succesfully transfered from one thread to another and that the TLS
197 * region of the thread is kept while changing fibers.
198 */
199TEST_CASE("Fibers::InterExchange", "[common]") {
200 TestControl2 test_control{};
201 test_control.thread_fibers.resize(2);
202 test_control.fiber1 =
203 std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_1}, &test_control);
204 test_control.fiber2 =
205 std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_2}, &test_control);
206 test_control.fiber3 =
207 std::make_shared<Fiber>(std::function<void(void*)>{WorkControl2_3}, &test_control);
208 std::thread thread1(ThreadStart2_1, 0, std::ref(test_control));
209 std::thread thread2(ThreadStart2_2, 1, std::ref(test_control));
210 thread1.join();
211 thread2.join();
212 REQUIRE(test_control.assert1);
213 REQUIRE(test_control.assert2);
214 REQUIRE(test_control.assert3);
215 REQUIRE(test_control.value2 == 7000);
216 u32 cal_value = 0;
217 for (u32 i = 0; i < 12000; i++) {
218 cal_value += i;
219 }
220 cal_value += 1000;
221 REQUIRE(test_control.value1 == cal_value);
222}
223
224class TestControl3 {
225public:
226 TestControl3() = default;
227
228 void DoWork1() {
229 value1 += 1;
230 Fiber::YieldTo(fiber1, fiber2);
231 std::thread::id this_id = std::this_thread::get_id();
232 u32 id = ids[this_id];
233 value3 += 1;
234 Fiber::YieldTo(fiber1, thread_fibers[id]);
235 }
236
237 void DoWork2() {
238 value2 += 1;
239 std::thread::id this_id = std::this_thread::get_id();
240 u32 id = ids[this_id];
241 Fiber::YieldTo(fiber2, thread_fibers[id]);
242 }
243
244 void ExecuteThread(u32 id);
245
246 void CallFiber1() {
247 std::thread::id this_id = std::this_thread::get_id();
248 u32 id = ids[this_id];
249 Fiber::YieldTo(thread_fibers[id], fiber1);
250 }
251
252 void Exit();
253
254 u32 value1{};
255 u32 value2{};
256 u32 value3{};
257 std::unordered_map<std::thread::id, u32> ids;
258 std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
259 std::shared_ptr<Common::Fiber> fiber1;
260 std::shared_ptr<Common::Fiber> fiber2;
261};
262
263static void WorkControl3_1(void* control) {
264 auto* test_control = static_cast<TestControl3*>(control);
265 test_control->DoWork1();
266}
267
268static void WorkControl3_2(void* control) {
269 auto* test_control = static_cast<TestControl3*>(control);
270 test_control->DoWork2();
271}
272
273void TestControl3::ExecuteThread(u32 id) {
274 std::thread::id this_id = std::this_thread::get_id();
275 ids[this_id] = id;
276 auto thread_fiber = Fiber::ThreadToFiber();
277 thread_fibers[id] = thread_fiber;
278}
279
280void TestControl3::Exit() {
281 std::thread::id this_id = std::this_thread::get_id();
282 u32 id = ids[this_id];
283 thread_fibers[id]->Exit();
284}
285
286static void ThreadStart3(u32 id, TestControl3& test_control) {
287 test_control.ExecuteThread(id);
288 test_control.CallFiber1();
289 test_control.Exit();
290}
291
292/** This test checks for one two threads racing for starting the same fiber.
293 * It checks execution occured in an ordered manner and by no time there were
294 * two contexts at the same time.
295 */
296TEST_CASE("Fibers::StartRace", "[common]") {
297 TestControl3 test_control{};
298 test_control.thread_fibers.resize(2);
299 test_control.fiber1 =
300 std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_1}, &test_control);
301 test_control.fiber2 =
302 std::make_shared<Fiber>(std::function<void(void*)>{WorkControl3_2}, &test_control);
303 std::thread thread1(ThreadStart3, 0, std::ref(test_control));
304 std::thread thread2(ThreadStart3, 1, std::ref(test_control));
305 thread1.join();
306 thread2.join();
307 REQUIRE(test_control.value1 == 1);
308 REQUIRE(test_control.value2 == 1);
309 REQUIRE(test_control.value3 == 1);
310}
311
312class TestControl4;
313
314static void WorkControl4(void* control);
315
316class TestControl4 {
317public:
318 TestControl4() {
319 fiber1 = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl4}, this);
320 goal_reached = false;
321 rewinded = false;
322 }
323
324 void Execute() {
325 thread_fiber = Fiber::ThreadToFiber();
326 Fiber::YieldTo(thread_fiber, fiber1);
327 thread_fiber->Exit();
328 }
329
330 void DoWork() {
331 fiber1->SetRewindPoint(std::function<void(void*)>{WorkControl4}, this);
332 if (rewinded) {
333 goal_reached = true;
334 Fiber::YieldTo(fiber1, thread_fiber);
335 }
336 rewinded = true;
337 fiber1->Rewind();
338 }
339
340 std::shared_ptr<Common::Fiber> fiber1;
341 std::shared_ptr<Common::Fiber> thread_fiber;
342 bool goal_reached;
343 bool rewinded;
344};
345
346static void WorkControl4(void* control) {
347 auto* test_control = static_cast<TestControl4*>(control);
348 test_control->DoWork();
349}
350
351TEST_CASE("Fibers::Rewind", "[common]") {
352 TestControl4 test_control{};
353 test_control.Execute();
354 REQUIRE(test_control.goal_reached);
355 REQUIRE(test_control.rewinded);
356}
357
358} // namespace Common
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index ff2d11cc8..e66db1940 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -18,29 +18,26 @@ namespace {
18// Numbers are chosen randomly to make sure the correct one is given. 18// Numbers are chosen randomly to make sure the correct one is given.
19constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; 19constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
20constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals 20constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals
21constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}};
22std::array<s64, 5> delays{};
21 23
22std::bitset<CB_IDS.size()> callbacks_ran_flags; 24std::bitset<CB_IDS.size()> callbacks_ran_flags;
23u64 expected_callback = 0; 25u64 expected_callback = 0;
24s64 lateness = 0;
25 26
26template <unsigned int IDX> 27template <unsigned int IDX>
27void CallbackTemplate(u64 userdata, s64 cycles_late) { 28void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) {
28 static_assert(IDX < CB_IDS.size(), "IDX out of range"); 29 static_assert(IDX < CB_IDS.size(), "IDX out of range");
29 callbacks_ran_flags.set(IDX); 30 callbacks_ran_flags.set(IDX);
30 REQUIRE(CB_IDS[IDX] == userdata); 31 REQUIRE(CB_IDS[IDX] == userdata);
31 REQUIRE(CB_IDS[IDX] == expected_callback); 32 REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]);
32 REQUIRE(lateness == cycles_late); 33 delays[IDX] = nanoseconds_late;
33} 34 ++expected_callback;
34
35u64 callbacks_done = 0;
36
37void EmptyCallback(u64 userdata, s64 cycles_late) {
38 ++callbacks_done;
39} 35}
40 36
41struct ScopeInit final { 37struct ScopeInit final {
42 ScopeInit() { 38 ScopeInit() {
43 core_timing.Initialize(); 39 core_timing.SetMulticore(true);
40 core_timing.Initialize([]() {});
44 } 41 }
45 ~ScopeInit() { 42 ~ScopeInit() {
46 core_timing.Shutdown(); 43 core_timing.Shutdown();
@@ -49,110 +46,101 @@ struct ScopeInit final {
49 Core::Timing::CoreTiming core_timing; 46 Core::Timing::CoreTiming core_timing;
50}; 47};
51 48
52void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 context = 0, 49#pragma optimize("", off)
53 int expected_lateness = 0, int cpu_downcount = 0) {
54 callbacks_ran_flags = 0;
55 expected_callback = CB_IDS[idx];
56 lateness = expected_lateness;
57
58 // Pretend we executed X cycles of instructions.
59 core_timing.SwitchContext(context);
60 core_timing.AddTicks(core_timing.GetDowncount() - cpu_downcount);
61 core_timing.Advance();
62 core_timing.SwitchContext((context + 1) % 4);
63 50
64 REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); 51u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) {
52 u64 start = core_timing.GetGlobalTimeNs().count();
53 u64 placebo = 0;
54 for (std::size_t i = 0; i < 1000; i++) {
55 placebo += core_timing.GetGlobalTimeNs().count();
56 }
57 u64 end = core_timing.GetGlobalTimeNs().count();
58 return (end - start);
65} 59}
60
61#pragma optimize("", on)
62
66} // Anonymous namespace 63} // Anonymous namespace
67 64
68TEST_CASE("CoreTiming[BasicOrder]", "[core]") { 65TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
69 ScopeInit guard; 66 ScopeInit guard;
70 auto& core_timing = guard.core_timing; 67 auto& core_timing = guard.core_timing;
68 std::vector<std::shared_ptr<Core::Timing::EventType>> events{
69 Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>),
70 Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>),
71 Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>),
72 Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>),
73 Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>),
74 };
75
76 expected_callback = 0;
77
78 core_timing.SyncPause(true);
79
80 u64 one_micro = 1000U;
81 for (std::size_t i = 0; i < events.size(); i++) {
82 u64 order = calls_order[i];
83 core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]);
84 }
85 /// test pause
86 REQUIRE(callbacks_ran_flags.none());
71 87
72 std::shared_ptr<Core::Timing::EventType> cb_a = 88 core_timing.Pause(false); // No need to sync
73 Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>);
74 std::shared_ptr<Core::Timing::EventType> cb_b =
75 Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>);
76 std::shared_ptr<Core::Timing::EventType> cb_c =
77 Core::Timing::CreateEvent("callbackC", CallbackTemplate<2>);
78 std::shared_ptr<Core::Timing::EventType> cb_d =
79 Core::Timing::CreateEvent("callbackD", CallbackTemplate<3>);
80 std::shared_ptr<Core::Timing::EventType> cb_e =
81 Core::Timing::CreateEvent("callbackE", CallbackTemplate<4>);
82
83 // Enter slice 0
84 core_timing.ResetRun();
85
86 // D -> B -> C -> A -> E
87 core_timing.SwitchContext(0);
88 core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
89 REQUIRE(1000 == core_timing.GetDowncount());
90 core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]);
91 REQUIRE(500 == core_timing.GetDowncount());
92 core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]);
93 REQUIRE(500 == core_timing.GetDowncount());
94 core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]);
95 REQUIRE(100 == core_timing.GetDowncount());
96 core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]);
97 REQUIRE(100 == core_timing.GetDowncount());
98
99 AdvanceAndCheck(core_timing, 3, 0);
100 AdvanceAndCheck(core_timing, 1, 1);
101 AdvanceAndCheck(core_timing, 2, 2);
102 AdvanceAndCheck(core_timing, 0, 3);
103 AdvanceAndCheck(core_timing, 4, 0);
104}
105
106TEST_CASE("CoreTiming[FairSharing]", "[core]") {
107 89
108 ScopeInit guard; 90 while (core_timing.HasPendingEvents())
109 auto& core_timing = guard.core_timing; 91 ;
110 92
111 std::shared_ptr<Core::Timing::EventType> empty_callback = 93 REQUIRE(callbacks_ran_flags.all());
112 Core::Timing::CreateEvent("empty_callback", EmptyCallback);
113 94
114 callbacks_done = 0; 95 for (std::size_t i = 0; i < delays.size(); i++) {
115 u64 MAX_CALLBACKS = 10; 96 const double delay = static_cast<double>(delays[i]);
116 for (std::size_t i = 0; i < 10; i++) { 97 const double micro = delay / 1000.0f;
117 core_timing.ScheduleEvent(i * 3333U, empty_callback, 0); 98 const double mili = micro / 1000.0f;
99 printf("HostTimer Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili);
118 } 100 }
119
120 const s64 advances = MAX_SLICE_LENGTH / 10;
121 core_timing.ResetRun();
122 u64 current_time = core_timing.GetTicks();
123 bool keep_running{};
124 do {
125 keep_running = false;
126 for (u32 active_core = 0; active_core < 4; ++active_core) {
127 core_timing.SwitchContext(active_core);
128 if (core_timing.CanCurrentContextRun()) {
129 core_timing.AddTicks(std::min<s64>(advances, core_timing.GetDowncount()));
130 core_timing.Advance();
131 }
132 keep_running |= core_timing.CanCurrentContextRun();
133 }
134 } while (keep_running);
135 u64 current_time_2 = core_timing.GetTicks();
136
137 REQUIRE(MAX_CALLBACKS == callbacks_done);
138 REQUIRE(current_time_2 == current_time + MAX_SLICE_LENGTH * 4);
139} 101}
140 102
141TEST_CASE("Core::Timing[PredictableLateness]", "[core]") { 103TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") {
142 ScopeInit guard; 104 ScopeInit guard;
143 auto& core_timing = guard.core_timing; 105 auto& core_timing = guard.core_timing;
106 std::vector<std::shared_ptr<Core::Timing::EventType>> events{
107 Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>),
108 Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>),
109 Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>),
110 Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>),
111 Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>),
112 };
113
114 core_timing.SyncPause(true);
115 core_timing.SyncPause(false);
116
117 expected_callback = 0;
118
119 u64 start = core_timing.GetGlobalTimeNs().count();
120 u64 one_micro = 1000U;
121 for (std::size_t i = 0; i < events.size(); i++) {
122 u64 order = calls_order[i];
123 core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]);
124 }
125 u64 end = core_timing.GetGlobalTimeNs().count();
126 const double scheduling_time = static_cast<double>(end - start);
127 const double timer_time = static_cast<double>(TestTimerSpeed(core_timing));
144 128
145 std::shared_ptr<Core::Timing::EventType> cb_a = 129 while (core_timing.HasPendingEvents())
146 Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); 130 ;
147 std::shared_ptr<Core::Timing::EventType> cb_b =
148 Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>);
149 131
150 // Enter slice 0 132 REQUIRE(callbacks_ran_flags.all());
151 core_timing.ResetRun();
152 133
153 core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]); 134 for (std::size_t i = 0; i < delays.size(); i++) {
154 core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]); 135 const double delay = static_cast<double>(delays[i]);
136 const double micro = delay / 1000.0f;
137 const double mili = micro / 1000.0f;
138 printf("HostTimer No Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili);
139 }
155 140
156 AdvanceAndCheck(core_timing, 0, 0, 10, -10); // (100 - 10) 141 const double micro = scheduling_time / 1000.0f;
157 AdvanceAndCheck(core_timing, 1, 1, 50, -50); 142 const double mili = micro / 1000.0f;
143 printf("HostTimer No Pausing Scheduling Time: %.3f %.6f\n", micro, mili);
144 printf("HostTimer No Pausing Timer Time: %.3f %.6f\n", timer_time / 1000.f,
145 timer_time / 1000000.f);
158} 146}
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index d6ee82836..21c46a567 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -3,6 +3,8 @@ add_library(video_core STATIC
3 buffer_cache/buffer_cache.h 3 buffer_cache/buffer_cache.h
4 buffer_cache/map_interval.cpp 4 buffer_cache/map_interval.cpp
5 buffer_cache/map_interval.h 5 buffer_cache/map_interval.h
6 compatible_formats.cpp
7 compatible_formats.h
6 dirty_flags.cpp 8 dirty_flags.cpp
7 dirty_flags.h 9 dirty_flags.h
8 dma_pusher.cpp 10 dma_pusher.cpp
@@ -25,6 +27,14 @@ add_library(video_core STATIC
25 engines/shader_bytecode.h 27 engines/shader_bytecode.h
26 engines/shader_header.h 28 engines/shader_header.h
27 engines/shader_type.h 29 engines/shader_type.h
30 macro/macro.cpp
31 macro/macro.h
32 macro/macro_hle.cpp
33 macro/macro_hle.h
34 macro/macro_interpreter.cpp
35 macro/macro_interpreter.h
36 macro/macro_jit_x64.cpp
37 macro/macro_jit_x64.h
28 fence_manager.h 38 fence_manager.h
29 gpu.cpp 39 gpu.cpp
30 gpu.h 40 gpu.h
@@ -36,8 +46,6 @@ add_library(video_core STATIC
36 gpu_thread.h 46 gpu_thread.h
37 guest_driver.cpp 47 guest_driver.cpp
38 guest_driver.h 48 guest_driver.h
39 macro_interpreter.cpp
40 macro_interpreter.h
41 memory_manager.cpp 49 memory_manager.cpp
42 memory_manager.h 50 memory_manager.h
43 morton.cpp 51 morton.cpp
@@ -45,11 +53,11 @@ add_library(video_core STATIC
45 query_cache.h 53 query_cache.h
46 rasterizer_accelerated.cpp 54 rasterizer_accelerated.cpp
47 rasterizer_accelerated.h 55 rasterizer_accelerated.h
48 rasterizer_cache.cpp
49 rasterizer_cache.h
50 rasterizer_interface.h 56 rasterizer_interface.h
51 renderer_base.cpp 57 renderer_base.cpp
52 renderer_base.h 58 renderer_base.h
59 renderer_opengl/gl_arb_decompiler.cpp
60 renderer_opengl/gl_arb_decompiler.h
53 renderer_opengl/gl_buffer_cache.cpp 61 renderer_opengl/gl_buffer_cache.cpp
54 renderer_opengl/gl_buffer_cache.h 62 renderer_opengl/gl_buffer_cache.h
55 renderer_opengl/gl_device.cpp 63 renderer_opengl/gl_device.cpp
@@ -89,6 +97,7 @@ add_library(video_core STATIC
89 renderer_opengl/utils.h 97 renderer_opengl/utils.h
90 sampler_cache.cpp 98 sampler_cache.cpp
91 sampler_cache.h 99 sampler_cache.h
100 shader_cache.h
92 shader/decode/arithmetic.cpp 101 shader/decode/arithmetic.cpp
93 shader/decode/arithmetic_immediate.cpp 102 shader/decode/arithmetic_immediate.cpp
94 shader/decode/bfe.cpp 103 shader/decode/bfe.cpp
diff --git a/src/video_core/buffer_cache/buffer_block.h b/src/video_core/buffer_cache/buffer_block.h
index e35ee0b67..e64170e66 100644
--- a/src/video_core/buffer_cache/buffer_block.h
+++ b/src/video_core/buffer_cache/buffer_block.h
@@ -15,48 +15,47 @@ namespace VideoCommon {
15 15
16class BufferBlock { 16class BufferBlock {
17public: 17public:
18 bool Overlaps(const VAddr start, const VAddr end) const { 18 bool Overlaps(VAddr start, VAddr end) const {
19 return (cpu_addr < end) && (cpu_addr_end > start); 19 return (cpu_addr < end) && (cpu_addr_end > start);
20 } 20 }
21 21
22 bool IsInside(const VAddr other_start, const VAddr other_end) const { 22 bool IsInside(VAddr other_start, VAddr other_end) const {
23 return cpu_addr <= other_start && other_end <= cpu_addr_end; 23 return cpu_addr <= other_start && other_end <= cpu_addr_end;
24 } 24 }
25 25
26 std::size_t GetOffset(const VAddr in_addr) { 26 std::size_t Offset(VAddr in_addr) const {
27 return static_cast<std::size_t>(in_addr - cpu_addr); 27 return static_cast<std::size_t>(in_addr - cpu_addr);
28 } 28 }
29 29
30 VAddr GetCpuAddr() const { 30 VAddr CpuAddr() const {
31 return cpu_addr; 31 return cpu_addr;
32 } 32 }
33 33
34 VAddr GetCpuAddrEnd() const { 34 VAddr CpuAddrEnd() const {
35 return cpu_addr_end; 35 return cpu_addr_end;
36 } 36 }
37 37
38 void SetCpuAddr(const VAddr new_addr) { 38 void SetCpuAddr(VAddr new_addr) {
39 cpu_addr = new_addr; 39 cpu_addr = new_addr;
40 cpu_addr_end = new_addr + size; 40 cpu_addr_end = new_addr + size;
41 } 41 }
42 42
43 std::size_t GetSize() const { 43 std::size_t Size() const {
44 return size; 44 return size;
45 } 45 }
46 46
47 void SetEpoch(u64 new_epoch) { 47 u64 Epoch() const {
48 epoch = new_epoch; 48 return epoch;
49 } 49 }
50 50
51 u64 GetEpoch() { 51 void SetEpoch(u64 new_epoch) {
52 return epoch; 52 epoch = new_epoch;
53 } 53 }
54 54
55protected: 55protected:
56 explicit BufferBlock(VAddr cpu_addr, const std::size_t size) : size{size} { 56 explicit BufferBlock(VAddr cpu_addr_, std::size_t size_) : size{size_} {
57 SetCpuAddr(cpu_addr); 57 SetCpuAddr(cpu_addr_);
58 } 58 }
59 ~BufferBlock() = default;
60 59
61private: 60private:
62 VAddr cpu_addr{}; 61 VAddr cpu_addr{};
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index d9a4a1b4d..dd7ce8c99 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -30,23 +30,31 @@
30 30
31namespace VideoCommon { 31namespace VideoCommon {
32 32
33template <typename OwnerBuffer, typename BufferType, typename StreamBuffer> 33template <typename Buffer, typename BufferType, typename StreamBuffer>
34class BufferCache { 34class BufferCache {
35 using IntervalSet = boost::icl::interval_set<VAddr>; 35 using IntervalSet = boost::icl::interval_set<VAddr>;
36 using IntervalType = typename IntervalSet::interval_type; 36 using IntervalType = typename IntervalSet::interval_type;
37 using VectorMapInterval = boost::container::small_vector<MapInterval*, 1>; 37 using VectorMapInterval = boost::container::small_vector<MapInterval*, 1>;
38 38
39 static constexpr u64 WRITE_PAGE_BIT = 11;
40 static constexpr u64 BLOCK_PAGE_BITS = 21;
41 static constexpr u64 BLOCK_PAGE_SIZE = 1ULL << BLOCK_PAGE_BITS;
42
39public: 43public:
40 using BufferInfo = std::pair<BufferType, u64>; 44 struct BufferInfo {
45 BufferType handle;
46 u64 offset;
47 u64 address;
48 };
41 49
42 BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, 50 BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4,
43 bool is_written = false, bool use_fast_cbuf = false) { 51 bool is_written = false, bool use_fast_cbuf = false) {
44 std::lock_guard lock{mutex}; 52 std::lock_guard lock{mutex};
45 53
46 const auto& memory_manager = system.GPU().MemoryManager(); 54 auto& memory_manager = system.GPU().MemoryManager();
47 const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr); 55 const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr);
48 if (!cpu_addr_opt) { 56 if (!cpu_addr_opt) {
49 return {GetEmptyBuffer(size), 0}; 57 return GetEmptyBuffer(size);
50 } 58 }
51 const VAddr cpu_addr = *cpu_addr_opt; 59 const VAddr cpu_addr = *cpu_addr_opt;
52 60
@@ -55,37 +63,41 @@ public:
55 constexpr std::size_t max_stream_size = 0x800; 63 constexpr std::size_t max_stream_size = 0x800;
56 if (use_fast_cbuf || size < max_stream_size) { 64 if (use_fast_cbuf || size < max_stream_size) {
57 if (!is_written && !IsRegionWritten(cpu_addr, cpu_addr + size - 1)) { 65 if (!is_written && !IsRegionWritten(cpu_addr, cpu_addr + size - 1)) {
58 auto& memory_manager = system.GPU().MemoryManager(); 66 const bool is_granular = memory_manager.IsGranularRange(gpu_addr, size);
59 if (use_fast_cbuf) { 67 if (use_fast_cbuf) {
60 if (memory_manager.IsGranularRange(gpu_addr, size)) { 68 u8* dest;
61 const auto host_ptr = memory_manager.GetPointer(gpu_addr); 69 if (is_granular) {
62 return ConstBufferUpload(host_ptr, size); 70 dest = memory_manager.GetPointer(gpu_addr);
63 } else { 71 } else {
64 staging_buffer.resize(size); 72 staging_buffer.resize(size);
65 memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); 73 dest = staging_buffer.data();
66 return ConstBufferUpload(staging_buffer.data(), size); 74 memory_manager.ReadBlockUnsafe(gpu_addr, dest, size);
67 } 75 }
76 return ConstBufferUpload(dest, size);
77 }
78 if (is_granular) {
79 u8* const host_ptr = memory_manager.GetPointer(gpu_addr);
80 return StreamBufferUpload(size, alignment, [host_ptr, size](u8* dest) {
81 std::memcpy(dest, host_ptr, size);
82 });
68 } else { 83 } else {
69 if (memory_manager.IsGranularRange(gpu_addr, size)) { 84 return StreamBufferUpload(
70 const auto host_ptr = memory_manager.GetPointer(gpu_addr); 85 size, alignment, [&memory_manager, gpu_addr, size](u8* dest) {
71 return StreamBufferUpload(host_ptr, size, alignment); 86 memory_manager.ReadBlockUnsafe(gpu_addr, dest, size);
72 } else { 87 });
73 staging_buffer.resize(size);
74 memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size);
75 return StreamBufferUpload(staging_buffer.data(), size, alignment);
76 }
77 } 88 }
78 } 89 }
79 } 90 }
80 91
81 OwnerBuffer block = GetBlock(cpu_addr, size); 92 Buffer* const block = GetBlock(cpu_addr, size);
82 MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size); 93 MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size);
83 if (!map) { 94 if (!map) {
84 return {GetEmptyBuffer(size), 0}; 95 return GetEmptyBuffer(size);
85 } 96 }
86 if (is_written) { 97 if (is_written) {
87 map->MarkAsModified(true, GetModifiedTicks()); 98 map->MarkAsModified(true, GetModifiedTicks());
88 if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) { 99 if (Settings::IsGPULevelHigh() &&
100 Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
89 MarkForAsyncFlush(map); 101 MarkForAsyncFlush(map);
90 } 102 }
91 if (!map->is_written) { 103 if (!map->is_written) {
@@ -94,41 +106,49 @@ public:
94 } 106 }
95 } 107 }
96 108
97 return {ToHandle(block), static_cast<u64>(block->GetOffset(cpu_addr))}; 109 return BufferInfo{block->Handle(), block->Offset(cpu_addr), block->Address()};
98 } 110 }
99 111
100 /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset. 112 /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset.
101 BufferInfo UploadHostMemory(const void* raw_pointer, std::size_t size, 113 BufferInfo UploadHostMemory(const void* raw_pointer, std::size_t size,
102 std::size_t alignment = 4) { 114 std::size_t alignment = 4) {
103 std::lock_guard lock{mutex}; 115 std::lock_guard lock{mutex};
104 return StreamBufferUpload(raw_pointer, size, alignment); 116 return StreamBufferUpload(size, alignment, [raw_pointer, size](u8* dest) {
117 std::memcpy(dest, raw_pointer, size);
118 });
105 } 119 }
106 120
107 void Map(std::size_t max_size) { 121 /// Prepares the buffer cache for data uploading
122 /// @param max_size Maximum number of bytes that will be uploaded
123 /// @return True when a stream buffer invalidation was required, false otherwise
124 bool Map(std::size_t max_size) {
108 std::lock_guard lock{mutex}; 125 std::lock_guard lock{mutex};
109 126
127 bool invalidated;
110 std::tie(buffer_ptr, buffer_offset_base, invalidated) = stream_buffer->Map(max_size, 4); 128 std::tie(buffer_ptr, buffer_offset_base, invalidated) = stream_buffer->Map(max_size, 4);
111 buffer_offset = buffer_offset_base; 129 buffer_offset = buffer_offset_base;
130
131 return invalidated;
112 } 132 }
113 133
114 /// Finishes the upload stream, returns true on bindings invalidation. 134 /// Finishes the upload stream
115 bool Unmap() { 135 void Unmap() {
116 std::lock_guard lock{mutex}; 136 std::lock_guard lock{mutex};
117
118 stream_buffer->Unmap(buffer_offset - buffer_offset_base); 137 stream_buffer->Unmap(buffer_offset - buffer_offset_base);
119 return std::exchange(invalidated, false);
120 } 138 }
121 139
140 /// Function called at the end of each frame, inteded for deferred operations
122 void TickFrame() { 141 void TickFrame() {
123 ++epoch; 142 ++epoch;
143
124 while (!pending_destruction.empty()) { 144 while (!pending_destruction.empty()) {
125 // Delay at least 4 frames before destruction. 145 // Delay at least 4 frames before destruction.
126 // This is due to triple buffering happening on some drivers. 146 // This is due to triple buffering happening on some drivers.
127 static constexpr u64 epochs_to_destroy = 5; 147 static constexpr u64 epochs_to_destroy = 5;
128 if (pending_destruction.front()->GetEpoch() + epochs_to_destroy > epoch) { 148 if (pending_destruction.front()->Epoch() + epochs_to_destroy > epoch) {
129 break; 149 break;
130 } 150 }
131 pending_destruction.pop_front(); 151 pending_destruction.pop();
132 } 152 }
133 } 153 }
134 154
@@ -239,28 +259,16 @@ public:
239 committed_flushes.pop_front(); 259 committed_flushes.pop_front();
240 } 260 }
241 261
242 virtual BufferType GetEmptyBuffer(std::size_t size) = 0; 262 virtual BufferInfo GetEmptyBuffer(std::size_t size) = 0;
243 263
244protected: 264protected:
245 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, 265 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system,
246 std::unique_ptr<StreamBuffer> stream_buffer) 266 std::unique_ptr<StreamBuffer> stream_buffer)
247 : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer)}, 267 : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer)} {}
248 stream_buffer_handle{this->stream_buffer->GetHandle()} {}
249 268
250 ~BufferCache() = default; 269 ~BufferCache() = default;
251 270
252 virtual BufferType ToHandle(const OwnerBuffer& storage) = 0; 271 virtual std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) = 0;
253
254 virtual OwnerBuffer CreateBlock(VAddr cpu_addr, std::size_t size) = 0;
255
256 virtual void UploadBlockData(const OwnerBuffer& buffer, std::size_t offset, std::size_t size,
257 const u8* data) = 0;
258
259 virtual void DownloadBlockData(const OwnerBuffer& buffer, std::size_t offset, std::size_t size,
260 u8* data) = 0;
261
262 virtual void CopyBlock(const OwnerBuffer& src, const OwnerBuffer& dst, std::size_t src_offset,
263 std::size_t dst_offset, std::size_t size) = 0;
264 272
265 virtual BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) { 273 virtual BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) {
266 return {}; 274 return {};
@@ -315,19 +323,18 @@ protected:
315 } 323 }
316 324
317private: 325private:
318 MapInterval* MapAddress(const OwnerBuffer& block, GPUVAddr gpu_addr, VAddr cpu_addr, 326 MapInterval* MapAddress(Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size) {
319 std::size_t size) {
320 const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size); 327 const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size);
321 if (overlaps.empty()) { 328 if (overlaps.empty()) {
322 auto& memory_manager = system.GPU().MemoryManager(); 329 auto& memory_manager = system.GPU().MemoryManager();
323 const VAddr cpu_addr_end = cpu_addr + size; 330 const VAddr cpu_addr_end = cpu_addr + size;
324 if (memory_manager.IsGranularRange(gpu_addr, size)) { 331 if (memory_manager.IsGranularRange(gpu_addr, size)) {
325 u8* host_ptr = memory_manager.GetPointer(gpu_addr); 332 u8* host_ptr = memory_manager.GetPointer(gpu_addr);
326 UploadBlockData(block, block->GetOffset(cpu_addr), size, host_ptr); 333 block->Upload(block->Offset(cpu_addr), size, host_ptr);
327 } else { 334 } else {
328 staging_buffer.resize(size); 335 staging_buffer.resize(size);
329 memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); 336 memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size);
330 UploadBlockData(block, block->GetOffset(cpu_addr), size, staging_buffer.data()); 337 block->Upload(block->Offset(cpu_addr), size, staging_buffer.data());
331 } 338 }
332 return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr)); 339 return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr));
333 } 340 }
@@ -363,15 +370,15 @@ private:
363 } 370 }
364 if (modified_inheritance) { 371 if (modified_inheritance) {
365 map->MarkAsModified(true, GetModifiedTicks()); 372 map->MarkAsModified(true, GetModifiedTicks());
366 if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) { 373 if (Settings::IsGPULevelHigh() &&
374 Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
367 MarkForAsyncFlush(map); 375 MarkForAsyncFlush(map);
368 } 376 }
369 } 377 }
370 return map; 378 return map;
371 } 379 }
372 380
373 void UpdateBlock(const OwnerBuffer& block, VAddr start, VAddr end, 381 void UpdateBlock(Buffer* block, VAddr start, VAddr end, const VectorMapInterval& overlaps) {
374 const VectorMapInterval& overlaps) {
375 const IntervalType base_interval{start, end}; 382 const IntervalType base_interval{start, end};
376 IntervalSet interval_set{}; 383 IntervalSet interval_set{};
377 interval_set.add(base_interval); 384 interval_set.add(base_interval);
@@ -380,13 +387,13 @@ private:
380 interval_set.subtract(subtract); 387 interval_set.subtract(subtract);
381 } 388 }
382 for (auto& interval : interval_set) { 389 for (auto& interval : interval_set) {
383 std::size_t size = interval.upper() - interval.lower(); 390 const std::size_t size = interval.upper() - interval.lower();
384 if (size > 0) { 391 if (size == 0) {
385 staging_buffer.resize(size); 392 continue;
386 system.Memory().ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size);
387 UploadBlockData(block, block->GetOffset(interval.lower()), size,
388 staging_buffer.data());
389 } 393 }
394 staging_buffer.resize(size);
395 system.Memory().ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size);
396 block->Upload(block->Offset(interval.lower()), size, staging_buffer.data());
390 } 397 }
391 } 398 }
392 399
@@ -416,23 +423,27 @@ private:
416 } 423 }
417 424
418 void FlushMap(MapInterval* map) { 425 void FlushMap(MapInterval* map) {
426 const auto it = blocks.find(map->start >> BLOCK_PAGE_BITS);
427 ASSERT_OR_EXECUTE(it != blocks.end(), return;);
428
429 std::shared_ptr<Buffer> block = it->second;
430
419 const std::size_t size = map->end - map->start; 431 const std::size_t size = map->end - map->start;
420 OwnerBuffer block = blocks[map->start >> block_page_bits];
421 staging_buffer.resize(size); 432 staging_buffer.resize(size);
422 DownloadBlockData(block, block->GetOffset(map->start), size, staging_buffer.data()); 433 block->Download(block->Offset(map->start), size, staging_buffer.data());
423 system.Memory().WriteBlockUnsafe(map->start, staging_buffer.data(), size); 434 system.Memory().WriteBlockUnsafe(map->start, staging_buffer.data(), size);
424 map->MarkAsModified(false, 0); 435 map->MarkAsModified(false, 0);
425 } 436 }
426 437
427 BufferInfo StreamBufferUpload(const void* raw_pointer, std::size_t size, 438 template <typename Callable>
428 std::size_t alignment) { 439 BufferInfo StreamBufferUpload(std::size_t size, std::size_t alignment, Callable&& callable) {
429 AlignBuffer(alignment); 440 AlignBuffer(alignment);
430 const std::size_t uploaded_offset = buffer_offset; 441 const std::size_t uploaded_offset = buffer_offset;
431 std::memcpy(buffer_ptr, raw_pointer, size); 442 callable(buffer_ptr);
432 443
433 buffer_ptr += size; 444 buffer_ptr += size;
434 buffer_offset += size; 445 buffer_offset += size;
435 return {stream_buffer_handle, uploaded_offset}; 446 return BufferInfo{stream_buffer->Handle(), uploaded_offset, stream_buffer->Address()};
436 } 447 }
437 448
438 void AlignBuffer(std::size_t alignment) { 449 void AlignBuffer(std::size_t alignment) {
@@ -442,97 +453,89 @@ private:
442 buffer_offset = offset_aligned; 453 buffer_offset = offset_aligned;
443 } 454 }
444 455
445 OwnerBuffer EnlargeBlock(OwnerBuffer buffer) { 456 std::shared_ptr<Buffer> EnlargeBlock(std::shared_ptr<Buffer> buffer) {
446 const std::size_t old_size = buffer->GetSize(); 457 const std::size_t old_size = buffer->Size();
447 const std::size_t new_size = old_size + block_page_size; 458 const std::size_t new_size = old_size + BLOCK_PAGE_SIZE;
448 const VAddr cpu_addr = buffer->GetCpuAddr(); 459 const VAddr cpu_addr = buffer->CpuAddr();
449 OwnerBuffer new_buffer = CreateBlock(cpu_addr, new_size); 460 std::shared_ptr<Buffer> new_buffer = CreateBlock(cpu_addr, new_size);
450 CopyBlock(buffer, new_buffer, 0, 0, old_size); 461 new_buffer->CopyFrom(*buffer, 0, 0, old_size);
451 buffer->SetEpoch(epoch); 462 QueueDestruction(std::move(buffer));
452 pending_destruction.push_back(buffer); 463
453 const VAddr cpu_addr_end = cpu_addr + new_size - 1; 464 const VAddr cpu_addr_end = cpu_addr + new_size - 1;
454 u64 page_start = cpu_addr >> block_page_bits; 465 const u64 page_end = cpu_addr_end >> BLOCK_PAGE_BITS;
455 const u64 page_end = cpu_addr_end >> block_page_bits; 466 for (u64 page_start = cpu_addr >> BLOCK_PAGE_BITS; page_start <= page_end; ++page_start) {
456 while (page_start <= page_end) { 467 blocks.insert_or_assign(page_start, new_buffer);
457 blocks[page_start] = new_buffer;
458 ++page_start;
459 } 468 }
469
460 return new_buffer; 470 return new_buffer;
461 } 471 }
462 472
463 OwnerBuffer MergeBlocks(OwnerBuffer first, OwnerBuffer second) { 473 std::shared_ptr<Buffer> MergeBlocks(std::shared_ptr<Buffer> first,
464 const std::size_t size_1 = first->GetSize(); 474 std::shared_ptr<Buffer> second) {
465 const std::size_t size_2 = second->GetSize(); 475 const std::size_t size_1 = first->Size();
466 const VAddr first_addr = first->GetCpuAddr(); 476 const std::size_t size_2 = second->Size();
467 const VAddr second_addr = second->GetCpuAddr(); 477 const VAddr first_addr = first->CpuAddr();
478 const VAddr second_addr = second->CpuAddr();
468 const VAddr new_addr = std::min(first_addr, second_addr); 479 const VAddr new_addr = std::min(first_addr, second_addr);
469 const std::size_t new_size = size_1 + size_2; 480 const std::size_t new_size = size_1 + size_2;
470 OwnerBuffer new_buffer = CreateBlock(new_addr, new_size); 481
471 CopyBlock(first, new_buffer, 0, new_buffer->GetOffset(first_addr), size_1); 482 std::shared_ptr<Buffer> new_buffer = CreateBlock(new_addr, new_size);
472 CopyBlock(second, new_buffer, 0, new_buffer->GetOffset(second_addr), size_2); 483 new_buffer->CopyFrom(*first, 0, new_buffer->Offset(first_addr), size_1);
473 first->SetEpoch(epoch); 484 new_buffer->CopyFrom(*second, 0, new_buffer->Offset(second_addr), size_2);
474 second->SetEpoch(epoch); 485 QueueDestruction(std::move(first));
475 pending_destruction.push_back(first); 486 QueueDestruction(std::move(second));
476 pending_destruction.push_back(second); 487
477 const VAddr cpu_addr_end = new_addr + new_size - 1; 488 const VAddr cpu_addr_end = new_addr + new_size - 1;
478 u64 page_start = new_addr >> block_page_bits; 489 const u64 page_end = cpu_addr_end >> BLOCK_PAGE_BITS;
479 const u64 page_end = cpu_addr_end >> block_page_bits; 490 for (u64 page_start = new_addr >> BLOCK_PAGE_BITS; page_start <= page_end; ++page_start) {
480 while (page_start <= page_end) { 491 blocks.insert_or_assign(page_start, new_buffer);
481 blocks[page_start] = new_buffer;
482 ++page_start;
483 } 492 }
484 return new_buffer; 493 return new_buffer;
485 } 494 }
486 495
487 OwnerBuffer GetBlock(const VAddr cpu_addr, const std::size_t size) { 496 Buffer* GetBlock(VAddr cpu_addr, std::size_t size) {
488 OwnerBuffer found; 497 std::shared_ptr<Buffer> found;
498
489 const VAddr cpu_addr_end = cpu_addr + size - 1; 499 const VAddr cpu_addr_end = cpu_addr + size - 1;
490 u64 page_start = cpu_addr >> block_page_bits; 500 const u64 page_end = cpu_addr_end >> BLOCK_PAGE_BITS;
491 const u64 page_end = cpu_addr_end >> block_page_bits; 501 for (u64 page_start = cpu_addr >> BLOCK_PAGE_BITS; page_start <= page_end; ++page_start) {
492 while (page_start <= page_end) {
493 auto it = blocks.find(page_start); 502 auto it = blocks.find(page_start);
494 if (it == blocks.end()) { 503 if (it == blocks.end()) {
495 if (found) { 504 if (found) {
496 found = EnlargeBlock(found); 505 found = EnlargeBlock(found);
497 } else { 506 continue;
498 const VAddr start_addr = (page_start << block_page_bits);
499 found = CreateBlock(start_addr, block_page_size);
500 blocks[page_start] = found;
501 }
502 } else {
503 if (found) {
504 if (found == it->second) {
505 ++page_start;
506 continue;
507 }
508 found = MergeBlocks(found, it->second);
509 } else {
510 found = it->second;
511 } 507 }
508 const VAddr start_addr = page_start << BLOCK_PAGE_BITS;
509 found = CreateBlock(start_addr, BLOCK_PAGE_SIZE);
510 blocks.insert_or_assign(page_start, found);
511 continue;
512 }
513 if (!found) {
514 found = it->second;
515 continue;
516 }
517 if (found != it->second) {
518 found = MergeBlocks(std::move(found), it->second);
512 } 519 }
513 ++page_start;
514 } 520 }
515 return found; 521 return found.get();
516 } 522 }
517 523
518 void MarkRegionAsWritten(const VAddr start, const VAddr end) { 524 void MarkRegionAsWritten(VAddr start, VAddr end) {
519 u64 page_start = start >> write_page_bit; 525 const u64 page_end = end >> WRITE_PAGE_BIT;
520 const u64 page_end = end >> write_page_bit; 526 for (u64 page_start = start >> WRITE_PAGE_BIT; page_start <= page_end; ++page_start) {
521 while (page_start <= page_end) {
522 auto it = written_pages.find(page_start); 527 auto it = written_pages.find(page_start);
523 if (it != written_pages.end()) { 528 if (it != written_pages.end()) {
524 it->second = it->second + 1; 529 it->second = it->second + 1;
525 } else { 530 } else {
526 written_pages[page_start] = 1; 531 written_pages.insert_or_assign(page_start, 1);
527 } 532 }
528 ++page_start;
529 } 533 }
530 } 534 }
531 535
532 void UnmarkRegionAsWritten(const VAddr start, const VAddr end) { 536 void UnmarkRegionAsWritten(VAddr start, VAddr end) {
533 u64 page_start = start >> write_page_bit; 537 const u64 page_end = end >> WRITE_PAGE_BIT;
534 const u64 page_end = end >> write_page_bit; 538 for (u64 page_start = start >> WRITE_PAGE_BIT; page_start <= page_end; ++page_start) {
535 while (page_start <= page_end) {
536 auto it = written_pages.find(page_start); 539 auto it = written_pages.find(page_start);
537 if (it != written_pages.end()) { 540 if (it != written_pages.end()) {
538 if (it->second > 1) { 541 if (it->second > 1) {
@@ -541,22 +544,24 @@ private:
541 written_pages.erase(it); 544 written_pages.erase(it);
542 } 545 }
543 } 546 }
544 ++page_start;
545 } 547 }
546 } 548 }
547 549
548 bool IsRegionWritten(const VAddr start, const VAddr end) const { 550 bool IsRegionWritten(VAddr start, VAddr end) const {
549 u64 page_start = start >> write_page_bit; 551 const u64 page_end = end >> WRITE_PAGE_BIT;
550 const u64 page_end = end >> write_page_bit; 552 for (u64 page_start = start >> WRITE_PAGE_BIT; page_start <= page_end; ++page_start) {
551 while (page_start <= page_end) {
552 if (written_pages.count(page_start) > 0) { 553 if (written_pages.count(page_start) > 0) {
553 return true; 554 return true;
554 } 555 }
555 ++page_start;
556 } 556 }
557 return false; 557 return false;
558 } 558 }
559 559
560 void QueueDestruction(std::shared_ptr<Buffer> buffer) {
561 buffer->SetEpoch(epoch);
562 pending_destruction.push(std::move(buffer));
563 }
564
560 void MarkForAsyncFlush(MapInterval* map) { 565 void MarkForAsyncFlush(MapInterval* map) {
561 if (!uncommitted_flushes) { 566 if (!uncommitted_flushes) {
562 uncommitted_flushes = std::make_shared<std::unordered_set<MapInterval*>>(); 567 uncommitted_flushes = std::make_shared<std::unordered_set<MapInterval*>>();
@@ -568,9 +573,7 @@ private:
568 Core::System& system; 573 Core::System& system;
569 574
570 std::unique_ptr<StreamBuffer> stream_buffer; 575 std::unique_ptr<StreamBuffer> stream_buffer;
571 BufferType stream_buffer_handle{}; 576 BufferType stream_buffer_handle;
572
573 bool invalidated = false;
574 577
575 u8* buffer_ptr = nullptr; 578 u8* buffer_ptr = nullptr;
576 u64 buffer_offset = 0; 579 u64 buffer_offset = 0;
@@ -580,18 +583,15 @@ private:
580 boost::intrusive::set<MapInterval, boost::intrusive::compare<MapIntervalCompare>> 583 boost::intrusive::set<MapInterval, boost::intrusive::compare<MapIntervalCompare>>
581 mapped_addresses; 584 mapped_addresses;
582 585
583 static constexpr u64 write_page_bit = 11;
584 std::unordered_map<u64, u32> written_pages; 586 std::unordered_map<u64, u32> written_pages;
587 std::unordered_map<u64, std::shared_ptr<Buffer>> blocks;
585 588
586 static constexpr u64 block_page_bits = 21; 589 std::queue<std::shared_ptr<Buffer>> pending_destruction;
587 static constexpr u64 block_page_size = 1ULL << block_page_bits;
588 std::unordered_map<u64, OwnerBuffer> blocks;
589
590 std::list<OwnerBuffer> pending_destruction;
591 u64 epoch = 0; 590 u64 epoch = 0;
592 u64 modified_ticks = 0; 591 u64 modified_ticks = 0;
593 592
594 std::vector<u8> staging_buffer; 593 std::vector<u8> staging_buffer;
594
595 std::list<MapInterval*> marked_for_unregister; 595 std::list<MapInterval*> marked_for_unregister;
596 596
597 std::shared_ptr<std::unordered_set<MapInterval*>> uncommitted_flushes; 597 std::shared_ptr<std::unordered_set<MapInterval*>> uncommitted_flushes;
diff --git a/src/video_core/compatible_formats.cpp b/src/video_core/compatible_formats.cpp
new file mode 100644
index 000000000..6c426b035
--- /dev/null
+++ b/src/video_core/compatible_formats.cpp
@@ -0,0 +1,162 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <bitset>
7#include <cstddef>
8
9#include "video_core/compatible_formats.h"
10#include "video_core/surface.h"
11
12namespace VideoCore::Surface {
13
14namespace {
15
16// Compatibility table taken from Table 3.X.2 in:
17// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_view.txt
18
19constexpr std::array VIEW_CLASS_128_BITS = {
20 PixelFormat::RGBA32F,
21 PixelFormat::RGBA32UI,
22};
23// Missing formats:
24// PixelFormat::RGBA32I
25
26constexpr std::array VIEW_CLASS_96_BITS = {
27 PixelFormat::RGB32F,
28};
29// Missing formats:
30// PixelFormat::RGB32UI,
31// PixelFormat::RGB32I,
32
33constexpr std::array VIEW_CLASS_64_BITS = {
34 PixelFormat::RGBA16F, PixelFormat::RG32F, PixelFormat::RGBA16UI, PixelFormat::RG32UI,
35 PixelFormat::RGBA16U, PixelFormat::RGBA16F, PixelFormat::RGBA16S,
36};
37// Missing formats:
38// PixelFormat::RGBA16I
39// PixelFormat::RG32I
40
41// TODO: How should we handle 48 bits?
42
43constexpr std::array VIEW_CLASS_32_BITS = {
44 PixelFormat::RG16F, PixelFormat::R11FG11FB10F, PixelFormat::R32F,
45 PixelFormat::A2B10G10R10U, PixelFormat::RG16UI, PixelFormat::R32UI,
46 PixelFormat::RG16I, PixelFormat::R32I, PixelFormat::ABGR8U,
47 PixelFormat::RG16, PixelFormat::ABGR8S, PixelFormat::RG16S,
48 PixelFormat::RGBA8_SRGB, PixelFormat::E5B9G9R9F, PixelFormat::BGRA8,
49 PixelFormat::BGRA8_SRGB,
50};
51// Missing formats:
52// PixelFormat::RGBA8UI
53// PixelFormat::RGBA8I
54// PixelFormat::RGB10_A2_UI
55
56// TODO: How should we handle 24 bits?
57
58constexpr std::array VIEW_CLASS_16_BITS = {
59 PixelFormat::R16F, PixelFormat::RG8UI, PixelFormat::R16UI, PixelFormat::R16I,
60 PixelFormat::RG8U, PixelFormat::R16U, PixelFormat::RG8S, PixelFormat::R16S,
61};
62// Missing formats:
63// PixelFormat::RG8I
64
65constexpr std::array VIEW_CLASS_8_BITS = {
66 PixelFormat::R8UI,
67 PixelFormat::R8U,
68};
69// Missing formats:
70// PixelFormat::R8I
71// PixelFormat::R8S
72
73constexpr std::array VIEW_CLASS_RGTC1_RED = {
74 PixelFormat::DXN1,
75};
76// Missing formats:
77// COMPRESSED_SIGNED_RED_RGTC1
78
79constexpr std::array VIEW_CLASS_RGTC2_RG = {
80 PixelFormat::DXN2UNORM,
81 PixelFormat::DXN2SNORM,
82};
83
84constexpr std::array VIEW_CLASS_BPTC_UNORM = {
85 PixelFormat::BC7U,
86 PixelFormat::BC7U_SRGB,
87};
88
89constexpr std::array VIEW_CLASS_BPTC_FLOAT = {
90 PixelFormat::BC6H_SF16,
91 PixelFormat::BC6H_UF16,
92};
93
94// Compatibility table taken from Table 4.X.1 in:
95// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_copy_image.txt
96
97constexpr std::array COPY_CLASS_128_BITS = {
98 PixelFormat::RGBA32UI, PixelFormat::RGBA32F, PixelFormat::DXT23,
99 PixelFormat::DXT23_SRGB, PixelFormat::DXT45, PixelFormat::DXT45_SRGB,
100 PixelFormat::DXN2SNORM, PixelFormat::BC7U, PixelFormat::BC7U_SRGB,
101 PixelFormat::BC6H_SF16, PixelFormat::BC6H_UF16,
102};
103// Missing formats:
104// PixelFormat::RGBA32I
105// COMPRESSED_RG_RGTC2
106
107constexpr std::array COPY_CLASS_64_BITS = {
108 PixelFormat::RGBA16F, PixelFormat::RG32F, PixelFormat::RGBA16UI, PixelFormat::RG32UI,
109 PixelFormat::RGBA16U, PixelFormat::RGBA16S, PixelFormat::DXT1_SRGB, PixelFormat::DXT1,
110
111};
112// Missing formats:
113// PixelFormat::RGBA16I
114// PixelFormat::RG32I,
115// COMPRESSED_RGB_S3TC_DXT1_EXT
116// COMPRESSED_SRGB_S3TC_DXT1_EXT
117// COMPRESSED_RGBA_S3TC_DXT1_EXT
118// COMPRESSED_SIGNED_RED_RGTC1
119
120void Enable(FormatCompatibility::Table& compatiblity, size_t format_a, size_t format_b) {
121 compatiblity[format_a][format_b] = true;
122 compatiblity[format_b][format_a] = true;
123}
124
125void Enable(FormatCompatibility::Table& compatibility, PixelFormat format_a, PixelFormat format_b) {
126 Enable(compatibility, static_cast<size_t>(format_a), static_cast<size_t>(format_b));
127}
128
129template <typename Range>
130void EnableRange(FormatCompatibility::Table& compatibility, const Range& range) {
131 for (auto it_a = range.begin(); it_a != range.end(); ++it_a) {
132 for (auto it_b = it_a; it_b != range.end(); ++it_b) {
133 Enable(compatibility, *it_a, *it_b);
134 }
135 }
136}
137
138} // Anonymous namespace
139
140FormatCompatibility::FormatCompatibility() {
141 for (size_t i = 0; i < MaxPixelFormat; ++i) {
142 // Identity is allowed
143 Enable(view, i, i);
144 }
145
146 EnableRange(view, VIEW_CLASS_128_BITS);
147 EnableRange(view, VIEW_CLASS_96_BITS);
148 EnableRange(view, VIEW_CLASS_64_BITS);
149 EnableRange(view, VIEW_CLASS_32_BITS);
150 EnableRange(view, VIEW_CLASS_16_BITS);
151 EnableRange(view, VIEW_CLASS_8_BITS);
152 EnableRange(view, VIEW_CLASS_RGTC1_RED);
153 EnableRange(view, VIEW_CLASS_RGTC2_RG);
154 EnableRange(view, VIEW_CLASS_BPTC_UNORM);
155 EnableRange(view, VIEW_CLASS_BPTC_FLOAT);
156
157 copy = view;
158 EnableRange(copy, COPY_CLASS_128_BITS);
159 EnableRange(copy, COPY_CLASS_64_BITS);
160}
161
162} // namespace VideoCore::Surface
diff --git a/src/video_core/compatible_formats.h b/src/video_core/compatible_formats.h
new file mode 100644
index 000000000..d1082566d
--- /dev/null
+++ b/src/video_core/compatible_formats.h
@@ -0,0 +1,32 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <bitset>
7#include <cstddef>
8
9#include "video_core/surface.h"
10
11namespace VideoCore::Surface {
12
13class FormatCompatibility {
14public:
15 using Table = std::array<std::bitset<MaxPixelFormat>, MaxPixelFormat>;
16
17 explicit FormatCompatibility();
18
19 bool TestView(PixelFormat format_a, PixelFormat format_b) const noexcept {
20 return view[static_cast<size_t>(format_a)][static_cast<size_t>(format_b)];
21 }
22
23 bool TestCopy(PixelFormat format_a, PixelFormat format_b) const noexcept {
24 return copy[static_cast<size_t>(format_a)][static_cast<size_t>(format_b)];
25 }
26
27private:
28 Table view;
29 Table copy;
30};
31
32} // namespace VideoCore::Surface
diff --git a/src/video_core/engines/const_buffer_engine_interface.h b/src/video_core/engines/const_buffer_engine_interface.h
index ebe139504..f46e81bb7 100644
--- a/src/video_core/engines/const_buffer_engine_interface.h
+++ b/src/video_core/engines/const_buffer_engine_interface.h
@@ -93,6 +93,7 @@ public:
93 virtual SamplerDescriptor AccessBoundSampler(ShaderType stage, u64 offset) const = 0; 93 virtual SamplerDescriptor AccessBoundSampler(ShaderType stage, u64 offset) const = 0;
94 virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, 94 virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer,
95 u64 offset) const = 0; 95 u64 offset) const = 0;
96 virtual SamplerDescriptor AccessSampler(u32 handle) const = 0;
96 virtual u32 GetBoundBuffer() const = 0; 97 virtual u32 GetBoundBuffer() const = 0;
97 98
98 virtual VideoCore::GuestDriverProfile& AccessGuestDriverProfile() = 0; 99 virtual VideoCore::GuestDriverProfile& AccessGuestDriverProfile() = 0;
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index f6237fc6a..a82b06a38 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -92,8 +92,11 @@ SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 con
92 ASSERT(stage == ShaderType::Compute); 92 ASSERT(stage == ShaderType::Compute);
93 const auto& tex_info_buffer = launch_description.const_buffer_config[const_buffer]; 93 const auto& tex_info_buffer = launch_description.const_buffer_config[const_buffer];
94 const GPUVAddr tex_info_address = tex_info_buffer.Address() + offset; 94 const GPUVAddr tex_info_address = tex_info_buffer.Address() + offset;
95 return AccessSampler(memory_manager.Read<u32>(tex_info_address));
96}
95 97
96 const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)}; 98SamplerDescriptor KeplerCompute::AccessSampler(u32 handle) const {
99 const Texture::TextureHandle tex_handle{handle};
97 const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle); 100 const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
98 SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic); 101 SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic);
99 result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value()); 102 result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index 18ceedfaf..b7f668d88 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -219,6 +219,8 @@ public:
219 SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, 219 SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer,
220 u64 offset) const override; 220 u64 offset) const override;
221 221
222 SamplerDescriptor AccessSampler(u32 handle) const override;
223
222 u32 GetBoundBuffer() const override { 224 u32 GetBoundBuffer() const override {
223 return regs.tex_cb_index; 225 return regs.tex_cb_index;
224 } 226 }
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 004f6b261..c01436295 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -25,9 +25,8 @@ constexpr u32 MacroRegistersStart = 0xE00;
25Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 25Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
26 MemoryManager& memory_manager) 26 MemoryManager& memory_manager)
27 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, 27 : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager},
28 macro_interpreter{*this}, upload_state{memory_manager, regs.upload} { 28 macro_engine{GetMacroEngine(*this)}, upload_state{memory_manager, regs.upload} {
29 dirty.flags.flip(); 29 dirty.flags.flip();
30
31 InitializeRegisterDefaults(); 30 InitializeRegisterDefaults();
32} 31}
33 32
@@ -106,7 +105,11 @@ void Maxwell3D::InitializeRegisterDefaults() {
106 regs.rasterize_enable = 1; 105 regs.rasterize_enable = 1;
107 regs.rt_separate_frag_data = 1; 106 regs.rt_separate_frag_data = 1;
108 regs.framebuffer_srgb = 1; 107 regs.framebuffer_srgb = 1;
108 regs.line_width_aliased = 1.0f;
109 regs.line_width_smooth = 1.0f;
109 regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise; 110 regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise;
111 regs.polygon_mode_back = Maxwell3D::Regs::PolygonMode::Fill;
112 regs.polygon_mode_front = Maxwell3D::Regs::PolygonMode::Fill;
110 113
111 shadow_state = regs; 114 shadow_state = regs;
112 115
@@ -116,7 +119,7 @@ void Maxwell3D::InitializeRegisterDefaults() {
116 mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true; 119 mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true;
117} 120}
118 121
119void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters) { 122void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) {
120 // Reset the current macro. 123 // Reset the current macro.
121 executing_macro = 0; 124 executing_macro = 0;
122 125
@@ -125,7 +128,7 @@ void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u3
125 ((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size()); 128 ((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size());
126 129
127 // Execute the current macro. 130 // Execute the current macro.
128 macro_interpreter.Execute(macro_positions[entry], num_parameters, parameters); 131 macro_engine->Execute(*this, macro_positions[entry], parameters);
129 if (mme_draw.current_mode != MMEDrawMode::Undefined) { 132 if (mme_draw.current_mode != MMEDrawMode::Undefined) {
130 FlushMMEInlineDraw(); 133 FlushMMEInlineDraw();
131 } 134 }
@@ -161,7 +164,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
161 164
162 // Call the macro when there are no more parameters in the command buffer 165 // Call the macro when there are no more parameters in the command buffer
163 if (is_last_call) { 166 if (is_last_call) {
164 CallMacroMethod(executing_macro, macro_params.size(), macro_params.data()); 167 CallMacroMethod(executing_macro, macro_params);
165 macro_params.clear(); 168 macro_params.clear();
166 } 169 }
167 return; 170 return;
@@ -197,7 +200,7 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
197 break; 200 break;
198 } 201 }
199 case MAXWELL3D_REG_INDEX(macros.data): { 202 case MAXWELL3D_REG_INDEX(macros.data): {
200 ProcessMacroUpload(arg); 203 macro_engine->AddCode(regs.macros.upload_address, arg);
201 break; 204 break;
202 } 205 }
203 case MAXWELL3D_REG_INDEX(macros.bind): { 206 case MAXWELL3D_REG_INDEX(macros.bind): {
@@ -306,7 +309,7 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
306 309
307 // Call the macro when there are no more parameters in the command buffer 310 // Call the macro when there are no more parameters in the command buffer
308 if (amount == methods_pending) { 311 if (amount == methods_pending) {
309 CallMacroMethod(executing_macro, macro_params.size(), macro_params.data()); 312 CallMacroMethod(executing_macro, macro_params);
310 macro_params.clear(); 313 macro_params.clear();
311 } 314 }
312 return; 315 return;
@@ -420,9 +423,7 @@ void Maxwell3D::FlushMMEInlineDraw() {
420} 423}
421 424
422void Maxwell3D::ProcessMacroUpload(u32 data) { 425void Maxwell3D::ProcessMacroUpload(u32 data) {
423 ASSERT_MSG(regs.macros.upload_address < macro_memory.size(), 426 macro_engine->AddCode(regs.macros.upload_address++, data);
424 "upload_address exceeded macro_memory size!");
425 macro_memory[regs.macros.upload_address++] = data;
426} 427}
427 428
428void Maxwell3D::ProcessMacroBind(u32 data) { 429void Maxwell3D::ProcessMacroBind(u32 data) {
@@ -739,8 +740,11 @@ SamplerDescriptor Maxwell3D::AccessBindlessSampler(ShaderType stage, u64 const_b
739 const auto& shader = state.shader_stages[static_cast<std::size_t>(stage)]; 740 const auto& shader = state.shader_stages[static_cast<std::size_t>(stage)];
740 const auto& tex_info_buffer = shader.const_buffers[const_buffer]; 741 const auto& tex_info_buffer = shader.const_buffers[const_buffer];
741 const GPUVAddr tex_info_address = tex_info_buffer.address + offset; 742 const GPUVAddr tex_info_address = tex_info_buffer.address + offset;
743 return AccessSampler(memory_manager.Read<u32>(tex_info_address));
744}
742 745
743 const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)}; 746SamplerDescriptor Maxwell3D::AccessSampler(u32 handle) const {
747 const Texture::TextureHandle tex_handle{handle};
744 const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle); 748 const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
745 SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic); 749 SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic);
746 result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value()); 750 result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 05dd6b39b..ef1618990 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -23,7 +23,7 @@
23#include "video_core/engines/engine_upload.h" 23#include "video_core/engines/engine_upload.h"
24#include "video_core/engines/shader_type.h" 24#include "video_core/engines/shader_type.h"
25#include "video_core/gpu.h" 25#include "video_core/gpu.h"
26#include "video_core/macro_interpreter.h" 26#include "video_core/macro/macro.h"
27#include "video_core/textures/texture.h" 27#include "video_core/textures/texture.h"
28 28
29namespace Core { 29namespace Core {
@@ -598,6 +598,7 @@ public:
598 BitField<4, 3, u32> block_height; 598 BitField<4, 3, u32> block_height;
599 BitField<8, 3, u32> block_depth; 599 BitField<8, 3, u32> block_depth;
600 BitField<12, 1, InvMemoryLayout> type; 600 BitField<12, 1, InvMemoryLayout> type;
601 BitField<16, 1, u32> is_3d;
601 } memory_layout; 602 } memory_layout;
602 union { 603 union {
603 BitField<0, 16, u32> layers; 604 BitField<0, 16, u32> layers;
@@ -1403,6 +1404,8 @@ public:
1403 SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, 1404 SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer,
1404 u64 offset) const override; 1405 u64 offset) const override;
1405 1406
1407 SamplerDescriptor AccessSampler(u32 handle) const override;
1408
1406 u32 GetBoundBuffer() const override { 1409 u32 GetBoundBuffer() const override {
1407 return regs.tex_cb_index; 1410 return regs.tex_cb_index;
1408 } 1411 }
@@ -1411,17 +1414,16 @@ public:
1411 1414
1412 const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; 1415 const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override;
1413 1416
1414 /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than 1417 bool ShouldExecute() const {
1415 /// we've seen used. 1418 return execute_on;
1416 using MacroMemory = std::array<u32, 0x40000>; 1419 }
1417 1420
1418 /// Gets a reference to macro memory. 1421 VideoCore::RasterizerInterface& GetRasterizer() {
1419 const MacroMemory& GetMacroMemory() const { 1422 return rasterizer;
1420 return macro_memory;
1421 } 1423 }
1422 1424
1423 bool ShouldExecute() const { 1425 const VideoCore::RasterizerInterface& GetRasterizer() const {
1424 return execute_on; 1426 return rasterizer;
1425 } 1427 }
1426 1428
1427 /// Notify a memory write has happened. 1429 /// Notify a memory write has happened.
@@ -1468,16 +1470,13 @@ private:
1468 1470
1469 std::array<bool, Regs::NUM_REGS> mme_inline{}; 1471 std::array<bool, Regs::NUM_REGS> mme_inline{};
1470 1472
1471 /// Memory for macro code
1472 MacroMemory macro_memory;
1473
1474 /// Macro method that is currently being executed / being fed parameters. 1473 /// Macro method that is currently being executed / being fed parameters.
1475 u32 executing_macro = 0; 1474 u32 executing_macro = 0;
1476 /// Parameters that have been submitted to the macro call so far. 1475 /// Parameters that have been submitted to the macro call so far.
1477 std::vector<u32> macro_params; 1476 std::vector<u32> macro_params;
1478 1477
1479 /// Interpreter for the macro codes uploaded to the GPU. 1478 /// Interpreter for the macro codes uploaded to the GPU.
1480 MacroInterpreter macro_interpreter; 1479 std::unique_ptr<MacroEngine> macro_engine;
1481 1480
1482 static constexpr u32 null_cb_data = 0xFFFFFFFF; 1481 static constexpr u32 null_cb_data = 0xFFFFFFFF;
1483 struct { 1482 struct {
@@ -1506,7 +1505,7 @@ private:
1506 * @param num_parameters Number of arguments 1505 * @param num_parameters Number of arguments
1507 * @param parameters Arguments to the method call 1506 * @param parameters Arguments to the method call
1508 */ 1507 */
1509 void CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters); 1508 void CallMacroMethod(u32 method, const std::vector<u32>& parameters);
1510 1509
1511 /// Handles writes to the macro uploading register. 1510 /// Handles writes to the macro uploading register.
1512 void ProcessMacroUpload(u32 data); 1511 void ProcessMacroUpload(u32 data);
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index e7cb87589..d374b73cf 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -661,6 +661,10 @@ union Instruction {
661 constexpr Instruction(u64 value) : value{value} {} 661 constexpr Instruction(u64 value) : value{value} {}
662 constexpr Instruction(const Instruction& instr) : value(instr.value) {} 662 constexpr Instruction(const Instruction& instr) : value(instr.value) {}
663 663
664 constexpr bool Bit(u64 offset) const {
665 return ((value >> offset) & 1) != 0;
666 }
667
664 BitField<0, 8, Register> gpr0; 668 BitField<0, 8, Register> gpr0;
665 BitField<8, 8, Register> gpr8; 669 BitField<8, 8, Register> gpr8;
666 union { 670 union {
@@ -1874,7 +1878,9 @@ public:
1874 HSETP2_C, 1878 HSETP2_C,
1875 HSETP2_R, 1879 HSETP2_R,
1876 HSETP2_IMM, 1880 HSETP2_IMM,
1881 HSET2_C,
1877 HSET2_R, 1882 HSET2_R,
1883 HSET2_IMM,
1878 POPC_C, 1884 POPC_C,
1879 POPC_R, 1885 POPC_R,
1880 POPC_IMM, 1886 POPC_IMM,
@@ -2194,7 +2200,9 @@ private:
2194 INST("0111111-1-------", Id::HSETP2_C, Type::HalfSetPredicate, "HSETP2_C"), 2200 INST("0111111-1-------", Id::HSETP2_C, Type::HalfSetPredicate, "HSETP2_C"),
2195 INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), 2201 INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"),
2196 INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), 2202 INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"),
2203 INST("0111110-1-------", Id::HSET2_C, Type::HalfSet, "HSET2_C"),
2197 INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), 2204 INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"),
2205 INST("0111110-0-------", Id::HSET2_IMM, Type::HalfSet, "HSET2_IMM"),
2198 INST("010110111010----", Id::FCMP_RR, Type::Arithmetic, "FCMP_RR"), 2206 INST("010110111010----", Id::FCMP_RR, Type::Arithmetic, "FCMP_RR"),
2199 INST("010010111010----", Id::FCMP_RC, Type::Arithmetic, "FCMP_RC"), 2207 INST("010010111010----", Id::FCMP_RC, Type::Arithmetic, "FCMP_RC"),
2200 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), 2208 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 8eb017f65..758bfe148 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono>
6
5#include "common/assert.h" 7#include "common/assert.h"
6#include "common/microprofile.h" 8#include "common/microprofile.h"
7#include "core/core.h" 9#include "core/core.h"
@@ -154,9 +156,8 @@ u64 GPU::GetTicks() const {
154 constexpr u64 gpu_ticks_num = 384; 156 constexpr u64 gpu_ticks_num = 384;
155 constexpr u64 gpu_ticks_den = 625; 157 constexpr u64 gpu_ticks_den = 625;
156 158
157 const u64 cpu_ticks = system.CoreTiming().GetTicks(); 159 u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count();
158 u64 nanoseconds = Core::Timing::CyclesToNs(cpu_ticks).count(); 160 if (Settings::values.use_fast_gpu_time.GetValue()) {
159 if (Settings::values.use_fast_gpu_time) {
160 nanoseconds /= 256; 161 nanoseconds /= 256;
161 } 162 }
162 const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; 163 const u64 nanoseconds_num = nanoseconds / gpu_ticks_den;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index a1b4c305c..2c42483bd 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -284,6 +284,12 @@ public:
284 /// core timing events. 284 /// core timing events.
285 virtual void Start() = 0; 285 virtual void Start() = 0;
286 286
287 /// Obtain the CPU Context
288 virtual void ObtainContext() = 0;
289
290 /// Release the CPU Context
291 virtual void ReleaseContext() = 0;
292
287 /// Push GPU command entries to be processed 293 /// Push GPU command entries to be processed
288 virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0; 294 virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
289 295
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index 53305ab43..7b855f63e 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -19,10 +19,17 @@ GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBa
19GPUAsynch::~GPUAsynch() = default; 19GPUAsynch::~GPUAsynch() = default;
20 20
21void GPUAsynch::Start() { 21void GPUAsynch::Start() {
22 cpu_context->MakeCurrent();
23 gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher); 22 gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher);
24} 23}
25 24
25void GPUAsynch::ObtainContext() {
26 cpu_context->MakeCurrent();
27}
28
29void GPUAsynch::ReleaseContext() {
30 cpu_context->DoneCurrent();
31}
32
26void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { 33void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
27 gpu_thread.SubmitList(std::move(entries)); 34 gpu_thread.SubmitList(std::move(entries));
28} 35}
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index 517658612..15e9f1d38 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -25,6 +25,8 @@ public:
25 ~GPUAsynch() override; 25 ~GPUAsynch() override;
26 26
27 void Start() override; 27 void Start() override;
28 void ObtainContext() override;
29 void ReleaseContext() override;
28 void PushGPUEntries(Tegra::CommandList&& entries) override; 30 void PushGPUEntries(Tegra::CommandList&& entries) override;
29 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 31 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
30 void FlushRegion(VAddr addr, u64 size) override; 32 void FlushRegion(VAddr addr, u64 size) override;
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index 6f38a672a..aaeb9811d 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -13,10 +13,16 @@ GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase
13 13
14GPUSynch::~GPUSynch() = default; 14GPUSynch::~GPUSynch() = default;
15 15
16void GPUSynch::Start() { 16void GPUSynch::Start() {}
17
18void GPUSynch::ObtainContext() {
17 context->MakeCurrent(); 19 context->MakeCurrent();
18} 20}
19 21
22void GPUSynch::ReleaseContext() {
23 context->DoneCurrent();
24}
25
20void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { 26void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
21 dma_pusher->Push(std::move(entries)); 27 dma_pusher->Push(std::move(entries));
22 dma_pusher->DispatchCalls(); 28 dma_pusher->DispatchCalls();
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 4a6e9a01d..762c20aa5 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -24,6 +24,8 @@ public:
24 ~GPUSynch() override; 24 ~GPUSynch() override;
25 25
26 void Start() override; 26 void Start() override;
27 void ObtainContext() override;
28 void ReleaseContext() override;
27 void PushGPUEntries(Tegra::CommandList&& entries) override; 29 void PushGPUEntries(Tegra::CommandList&& entries) override;
28 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; 30 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
29 void FlushRegion(VAddr addr, u64 size) override; 31 void FlushRegion(VAddr addr, u64 size) override;
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index c3bb4fe06..738c6f0c1 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "common/thread.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/frontend/emu_window.h" 9#include "core/frontend/emu_window.h"
9#include "core/settings.h" 10#include "core/settings.h"
@@ -18,7 +19,11 @@ namespace VideoCommon::GPUThread {
18static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, 19static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
19 Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher, 20 Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher,
20 SynchState& state) { 21 SynchState& state) {
21 MicroProfileOnThreadCreate("GpuThread"); 22 std::string name = "yuzu:GPU";
23 MicroProfileOnThreadCreate(name.c_str());
24 Common::SetCurrentThreadName(name.c_str());
25 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
26 system.RegisterHostThread();
22 27
23 // Wait for first GPU command before acquiring the window context 28 // Wait for first GPU command before acquiring the window context
24 while (state.queue.Empty()) 29 while (state.queue.Empty())
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
new file mode 100644
index 000000000..a50e7b4e0
--- /dev/null
+++ b/src/video_core/macro/macro.cpp
@@ -0,0 +1,91 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <optional>
6#include <boost/container_hash/hash.hpp>
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/settings.h"
10#include "video_core/engines/maxwell_3d.h"
11#include "video_core/macro/macro.h"
12#include "video_core/macro/macro_hle.h"
13#include "video_core/macro/macro_interpreter.h"
14#include "video_core/macro/macro_jit_x64.h"
15
16namespace Tegra {
17
18MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d)
19 : hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d)} {}
20
21MacroEngine::~MacroEngine() = default;
22
23void MacroEngine::AddCode(u32 method, u32 data) {
24 uploaded_macro_code[method].push_back(data);
25}
26
27void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method,
28 const std::vector<u32>& parameters) {
29 auto compiled_macro = macro_cache.find(method);
30 if (compiled_macro != macro_cache.end()) {
31 const auto& cache_info = compiled_macro->second;
32 if (cache_info.has_hle_program) {
33 cache_info.hle_program->Execute(parameters, method);
34 } else {
35 cache_info.lle_program->Execute(parameters, method);
36 }
37 } else {
38 // Macro not compiled, check if it's uploaded and if so, compile it
39 std::optional<u32> mid_method = std::nullopt;
40 const auto macro_code = uploaded_macro_code.find(method);
41 if (macro_code == uploaded_macro_code.end()) {
42 for (const auto& [method_base, code] : uploaded_macro_code) {
43 if (method >= method_base && (method - method_base) < code.size()) {
44 mid_method = method_base;
45 break;
46 }
47 }
48 if (!mid_method.has_value()) {
49 UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method);
50 return;
51 }
52 }
53 auto& cache_info = macro_cache[method];
54
55 if (!mid_method.has_value()) {
56 cache_info.lle_program = Compile(macro_code->second);
57 cache_info.hash = boost::hash_value(macro_code->second);
58 } else {
59 const auto& macro_cached = uploaded_macro_code[mid_method.value()];
60 const auto rebased_method = method - mid_method.value();
61 auto& code = uploaded_macro_code[method];
62 code.resize(macro_cached.size() - rebased_method);
63 std::memcpy(code.data(), macro_cached.data() + rebased_method,
64 code.size() * sizeof(u32));
65 cache_info.hash = boost::hash_value(code);
66 cache_info.lle_program = Compile(code);
67 }
68
69 auto hle_program = hle_macros->GetHLEProgram(cache_info.hash);
70 if (hle_program.has_value()) {
71 cache_info.has_hle_program = true;
72 cache_info.hle_program = std::move(hle_program.value());
73 cache_info.hle_program->Execute(parameters, method);
74 } else {
75 cache_info.lle_program->Execute(parameters, method);
76 }
77 }
78}
79
80std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d) {
81 if (Settings::values.disable_macro_jit) {
82 return std::make_unique<MacroInterpreter>(maxwell3d);
83 }
84#ifdef ARCHITECTURE_x86_64
85 return std::make_unique<MacroJITx64>(maxwell3d);
86#else
87 return std::make_unique<MacroInterpreter>(maxwell3d);
88#endif
89}
90
91} // namespace Tegra
diff --git a/src/video_core/macro/macro.h b/src/video_core/macro/macro.h
new file mode 100644
index 000000000..4d00b84b0
--- /dev/null
+++ b/src/video_core/macro/macro.h
@@ -0,0 +1,141 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <unordered_map>
9#include <vector>
10#include "common/bit_field.h"
11#include "common/common_types.h"
12
13namespace Tegra {
14
15namespace Engines {
16class Maxwell3D;
17}
18
19namespace Macro {
20constexpr std::size_t NUM_MACRO_REGISTERS = 8;
21enum class Operation : u32 {
22 ALU = 0,
23 AddImmediate = 1,
24 ExtractInsert = 2,
25 ExtractShiftLeftImmediate = 3,
26 ExtractShiftLeftRegister = 4,
27 Read = 5,
28 Unused = 6, // This operation doesn't seem to be a valid encoding.
29 Branch = 7,
30};
31
32enum class ALUOperation : u32 {
33 Add = 0,
34 AddWithCarry = 1,
35 Subtract = 2,
36 SubtractWithBorrow = 3,
37 // Operations 4-7 don't seem to be valid encodings.
38 Xor = 8,
39 Or = 9,
40 And = 10,
41 AndNot = 11,
42 Nand = 12
43};
44
45enum class ResultOperation : u32 {
46 IgnoreAndFetch = 0,
47 Move = 1,
48 MoveAndSetMethod = 2,
49 FetchAndSend = 3,
50 MoveAndSend = 4,
51 FetchAndSetMethod = 5,
52 MoveAndSetMethodFetchAndSend = 6,
53 MoveAndSetMethodSend = 7
54};
55
56enum class BranchCondition : u32 {
57 Zero = 0,
58 NotZero = 1,
59};
60
61union Opcode {
62 u32 raw;
63 BitField<0, 3, Operation> operation;
64 BitField<4, 3, ResultOperation> result_operation;
65 BitField<4, 1, BranchCondition> branch_condition;
66 // If set on a branch, then the branch doesn't have a delay slot.
67 BitField<5, 1, u32> branch_annul;
68 BitField<7, 1, u32> is_exit;
69 BitField<8, 3, u32> dst;
70 BitField<11, 3, u32> src_a;
71 BitField<14, 3, u32> src_b;
72 // The signed immediate overlaps the second source operand and the alu operation.
73 BitField<14, 18, s32> immediate;
74
75 BitField<17, 5, ALUOperation> alu_operation;
76
77 // Bitfield instructions data
78 BitField<17, 5, u32> bf_src_bit;
79 BitField<22, 5, u32> bf_size;
80 BitField<27, 5, u32> bf_dst_bit;
81
82 u32 GetBitfieldMask() const {
83 return (1 << bf_size) - 1;
84 }
85
86 s32 GetBranchTarget() const {
87 return static_cast<s32>(immediate * sizeof(u32));
88 }
89};
90
91union MethodAddress {
92 u32 raw;
93 BitField<0, 12, u32> address;
94 BitField<12, 6, u32> increment;
95};
96
97} // namespace Macro
98
99class HLEMacro;
100
101class CachedMacro {
102public:
103 virtual ~CachedMacro() = default;
104 /**
105 * Executes the macro code with the specified input parameters.
106 * @param code The macro byte code to execute
107 * @param parameters The parameters of the macro
108 */
109 virtual void Execute(const std::vector<u32>& parameters, u32 method) = 0;
110};
111
112class MacroEngine {
113public:
114 explicit MacroEngine(Engines::Maxwell3D& maxwell3d);
115 virtual ~MacroEngine();
116
117 // Store the uploaded macro code to compile them when they're called.
118 void AddCode(u32 method, u32 data);
119
120 // Compiles the macro if its not in the cache, and executes the compiled macro
121 void Execute(Engines::Maxwell3D& maxwell3d, u32 method, const std::vector<u32>& parameters);
122
123protected:
124 virtual std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) = 0;
125
126private:
127 struct CacheInfo {
128 std::unique_ptr<CachedMacro> lle_program{};
129 std::unique_ptr<CachedMacro> hle_program{};
130 u64 hash{};
131 bool has_hle_program{};
132 };
133
134 std::unordered_map<u32, CacheInfo> macro_cache;
135 std::unordered_map<u32, std::vector<u32>> uploaded_macro_code;
136 std::unique_ptr<HLEMacro> hle_macros;
137};
138
139std::unique_ptr<MacroEngine> GetMacroEngine(Engines::Maxwell3D& maxwell3d);
140
141} // namespace Tegra
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
new file mode 100644
index 000000000..410f99018
--- /dev/null
+++ b/src/video_core/macro/macro_hle.cpp
@@ -0,0 +1,113 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <vector>
7#include "video_core/engines/maxwell_3d.h"
8#include "video_core/macro/macro_hle.h"
9#include "video_core/rasterizer_interface.h"
10
11namespace Tegra {
12
13namespace {
14// HLE'd functions
15static void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d,
16 const std::vector<u32>& parameters) {
17 const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B);
18
19 maxwell3d.regs.draw.topology.Assign(
20 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] &
21 ~(0x3ffffff << 26)));
22 maxwell3d.regs.vb_base_instance = parameters[5];
23 maxwell3d.mme_draw.instance_count = instance_count;
24 maxwell3d.regs.vb_element_base = parameters[3];
25 maxwell3d.regs.index_array.count = parameters[1];
26 maxwell3d.regs.index_array.first = parameters[4];
27
28 if (maxwell3d.ShouldExecute()) {
29 maxwell3d.GetRasterizer().Draw(true, true);
30 }
31 maxwell3d.regs.index_array.count = 0;
32 maxwell3d.mme_draw.instance_count = 0;
33 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
34}
35
36static void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d,
37 const std::vector<u32>& parameters) {
38 const u32 count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
39
40 maxwell3d.regs.vertex_buffer.first = parameters[3];
41 maxwell3d.regs.vertex_buffer.count = parameters[1];
42 maxwell3d.regs.vb_base_instance = parameters[4];
43 maxwell3d.regs.draw.topology.Assign(
44 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
45 maxwell3d.mme_draw.instance_count = count;
46
47 if (maxwell3d.ShouldExecute()) {
48 maxwell3d.GetRasterizer().Draw(false, true);
49 }
50 maxwell3d.regs.vertex_buffer.count = 0;
51 maxwell3d.mme_draw.instance_count = 0;
52 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
53}
54
55static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d,
56 const std::vector<u32>& parameters) {
57 const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
58 const u32 element_base = parameters[4];
59 const u32 base_instance = parameters[5];
60 maxwell3d.regs.index_array.first = parameters[3];
61 maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base?
62 maxwell3d.regs.index_array.count = parameters[1];
63 maxwell3d.regs.vb_element_base = element_base;
64 maxwell3d.regs.vb_base_instance = base_instance;
65 maxwell3d.mme_draw.instance_count = instance_count;
66 maxwell3d.CallMethodFromMME(0x8e3, 0x640);
67 maxwell3d.CallMethodFromMME(0x8e4, element_base);
68 maxwell3d.CallMethodFromMME(0x8e5, base_instance);
69 maxwell3d.regs.draw.topology.Assign(
70 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0]));
71 if (maxwell3d.ShouldExecute()) {
72 maxwell3d.GetRasterizer().Draw(true, true);
73 }
74 maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base?
75 maxwell3d.regs.index_array.count = 0;
76 maxwell3d.regs.vb_element_base = 0x0;
77 maxwell3d.regs.vb_base_instance = 0x0;
78 maxwell3d.mme_draw.instance_count = 0;
79 maxwell3d.CallMethodFromMME(0x8e3, 0x640);
80 maxwell3d.CallMethodFromMME(0x8e4, 0x0);
81 maxwell3d.CallMethodFromMME(0x8e5, 0x0);
82 maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
83}
84} // namespace
85
86constexpr std::array<std::pair<u64, HLEFunction>, 3> hle_funcs{{
87 std::make_pair<u64, HLEFunction>(0x771BB18C62444DA0, &HLE_771BB18C62444DA0),
88 std::make_pair<u64, HLEFunction>(0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD),
89 std::make_pair<u64, HLEFunction>(0x0217920100488FF7, &HLE_0217920100488FF7),
90}};
91
92HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
93HLEMacro::~HLEMacro() = default;
94
95std::optional<std::unique_ptr<CachedMacro>> HLEMacro::GetHLEProgram(u64 hash) const {
96 const auto it = std::find_if(hle_funcs.cbegin(), hle_funcs.cend(),
97 [hash](const auto& pair) { return pair.first == hash; });
98 if (it == hle_funcs.end()) {
99 return std::nullopt;
100 }
101 return std::make_unique<HLEMacroImpl>(maxwell3d, it->second);
102}
103
104HLEMacroImpl::~HLEMacroImpl() = default;
105
106HLEMacroImpl::HLEMacroImpl(Engines::Maxwell3D& maxwell3d, HLEFunction func)
107 : maxwell3d(maxwell3d), func(func) {}
108
109void HLEMacroImpl::Execute(const std::vector<u32>& parameters, u32 method) {
110 func(maxwell3d, parameters);
111}
112
113} // namespace Tegra
diff --git a/src/video_core/macro/macro_hle.h b/src/video_core/macro/macro_hle.h
new file mode 100644
index 000000000..37af875a0
--- /dev/null
+++ b/src/video_core/macro/macro_hle.h
@@ -0,0 +1,44 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <optional>
9#include <vector>
10#include "common/common_types.h"
11#include "video_core/macro/macro.h"
12
13namespace Tegra {
14
15namespace Engines {
16class Maxwell3D;
17}
18
19using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters);
20
21class HLEMacro {
22public:
23 explicit HLEMacro(Engines::Maxwell3D& maxwell3d);
24 ~HLEMacro();
25
26 std::optional<std::unique_ptr<CachedMacro>> GetHLEProgram(u64 hash) const;
27
28private:
29 Engines::Maxwell3D& maxwell3d;
30};
31
32class HLEMacroImpl : public CachedMacro {
33public:
34 explicit HLEMacroImpl(Engines::Maxwell3D& maxwell3d, HLEFunction func);
35 ~HLEMacroImpl();
36
37 void Execute(const std::vector<u32>& parameters, u32 method) override;
38
39private:
40 Engines::Maxwell3D& maxwell3d;
41 HLEFunction func;
42};
43
44} // namespace Tegra
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp
index 947364928..aa5256419 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro/macro_interpreter.cpp
@@ -1,4 +1,4 @@
1// Copyright 2018 yuzu Emulator Project 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
@@ -6,109 +6,47 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/microprofile.h" 7#include "common/microprofile.h"
8#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
9#include "video_core/macro_interpreter.h" 9#include "video_core/macro/macro_interpreter.h"
10 10
11MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192)); 11MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192));
12 12
13namespace Tegra { 13namespace Tegra {
14namespace { 14MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d)
15enum class Operation : u32 { 15 : MacroEngine::MacroEngine(maxwell3d), maxwell3d(maxwell3d) {}
16 ALU = 0,
17 AddImmediate = 1,
18 ExtractInsert = 2,
19 ExtractShiftLeftImmediate = 3,
20 ExtractShiftLeftRegister = 4,
21 Read = 5,
22 Unused = 6, // This operation doesn't seem to be a valid encoding.
23 Branch = 7,
24};
25} // Anonymous namespace
26
27enum class MacroInterpreter::ALUOperation : u32 {
28 Add = 0,
29 AddWithCarry = 1,
30 Subtract = 2,
31 SubtractWithBorrow = 3,
32 // Operations 4-7 don't seem to be valid encodings.
33 Xor = 8,
34 Or = 9,
35 And = 10,
36 AndNot = 11,
37 Nand = 12
38};
39
40enum class MacroInterpreter::ResultOperation : u32 {
41 IgnoreAndFetch = 0,
42 Move = 1,
43 MoveAndSetMethod = 2,
44 FetchAndSend = 3,
45 MoveAndSend = 4,
46 FetchAndSetMethod = 5,
47 MoveAndSetMethodFetchAndSend = 6,
48 MoveAndSetMethodSend = 7
49};
50
51enum class MacroInterpreter::BranchCondition : u32 {
52 Zero = 0,
53 NotZero = 1,
54};
55
56union MacroInterpreter::Opcode {
57 u32 raw;
58 BitField<0, 3, Operation> operation;
59 BitField<4, 3, ResultOperation> result_operation;
60 BitField<4, 1, BranchCondition> branch_condition;
61 // If set on a branch, then the branch doesn't have a delay slot.
62 BitField<5, 1, u32> branch_annul;
63 BitField<7, 1, u32> is_exit;
64 BitField<8, 3, u32> dst;
65 BitField<11, 3, u32> src_a;
66 BitField<14, 3, u32> src_b;
67 // The signed immediate overlaps the second source operand and the alu operation.
68 BitField<14, 18, s32> immediate;
69
70 BitField<17, 5, ALUOperation> alu_operation;
71
72 // Bitfield instructions data
73 BitField<17, 5, u32> bf_src_bit;
74 BitField<22, 5, u32> bf_size;
75 BitField<27, 5, u32> bf_dst_bit;
76
77 u32 GetBitfieldMask() const {
78 return (1 << bf_size) - 1;
79 }
80 16
81 s32 GetBranchTarget() const { 17std::unique_ptr<CachedMacro> MacroInterpreter::Compile(const std::vector<u32>& code) {
82 return static_cast<s32>(immediate * sizeof(u32)); 18 return std::make_unique<MacroInterpreterImpl>(maxwell3d, code);
83 } 19}
84};
85 20
86MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} 21MacroInterpreterImpl::MacroInterpreterImpl(Engines::Maxwell3D& maxwell3d,
22 const std::vector<u32>& code)
23 : maxwell3d(maxwell3d), code(code) {}
87 24
88void MacroInterpreter::Execute(u32 offset, std::size_t num_parameters, const u32* parameters) { 25void MacroInterpreterImpl::Execute(const std::vector<u32>& parameters, u32 method) {
89 MICROPROFILE_SCOPE(MacroInterp); 26 MICROPROFILE_SCOPE(MacroInterp);
90 Reset(); 27 Reset();
91 28
92 registers[1] = parameters[0]; 29 registers[1] = parameters[0];
30 num_parameters = parameters.size();
93 31
94 if (num_parameters > parameters_capacity) { 32 if (num_parameters > parameters_capacity) {
95 parameters_capacity = num_parameters; 33 parameters_capacity = num_parameters;
96 this->parameters = std::make_unique<u32[]>(num_parameters); 34 this->parameters = std::make_unique<u32[]>(num_parameters);
97 } 35 }
98 std::memcpy(this->parameters.get(), parameters, num_parameters * sizeof(u32)); 36 std::memcpy(this->parameters.get(), parameters.data(), num_parameters * sizeof(u32));
99 this->num_parameters = num_parameters; 37 this->num_parameters = num_parameters;
100 38
101 // Execute the code until we hit an exit condition. 39 // Execute the code until we hit an exit condition.
102 bool keep_executing = true; 40 bool keep_executing = true;
103 while (keep_executing) { 41 while (keep_executing) {
104 keep_executing = Step(offset, false); 42 keep_executing = Step(false);
105 } 43 }
106 44
107 // Assert the the macro used all the input parameters 45 // Assert the the macro used all the input parameters
108 ASSERT(next_parameter_index == num_parameters); 46 ASSERT(next_parameter_index == num_parameters);
109} 47}
110 48
111void MacroInterpreter::Reset() { 49void MacroInterpreterImpl::Reset() {
112 registers = {}; 50 registers = {};
113 pc = 0; 51 pc = 0;
114 delayed_pc = {}; 52 delayed_pc = {};
@@ -120,10 +58,10 @@ void MacroInterpreter::Reset() {
120 carry_flag = false; 58 carry_flag = false;
121} 59}
122 60
123bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) { 61bool MacroInterpreterImpl::Step(bool is_delay_slot) {
124 u32 base_address = pc; 62 u32 base_address = pc;
125 63
126 Opcode opcode = GetOpcode(offset); 64 Macro::Opcode opcode = GetOpcode();
127 pc += 4; 65 pc += 4;
128 66
129 // Update the program counter if we were delayed 67 // Update the program counter if we were delayed
@@ -134,18 +72,18 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
134 } 72 }
135 73
136 switch (opcode.operation) { 74 switch (opcode.operation) {
137 case Operation::ALU: { 75 case Macro::Operation::ALU: {
138 u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a), 76 u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a),
139 GetRegister(opcode.src_b)); 77 GetRegister(opcode.src_b));
140 ProcessResult(opcode.result_operation, opcode.dst, result); 78 ProcessResult(opcode.result_operation, opcode.dst, result);
141 break; 79 break;
142 } 80 }
143 case Operation::AddImmediate: { 81 case Macro::Operation::AddImmediate: {
144 ProcessResult(opcode.result_operation, opcode.dst, 82 ProcessResult(opcode.result_operation, opcode.dst,
145 GetRegister(opcode.src_a) + opcode.immediate); 83 GetRegister(opcode.src_a) + opcode.immediate);
146 break; 84 break;
147 } 85 }
148 case Operation::ExtractInsert: { 86 case Macro::Operation::ExtractInsert: {
149 u32 dst = GetRegister(opcode.src_a); 87 u32 dst = GetRegister(opcode.src_a);
150 u32 src = GetRegister(opcode.src_b); 88 u32 src = GetRegister(opcode.src_b);
151 89
@@ -155,7 +93,7 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
155 ProcessResult(opcode.result_operation, opcode.dst, dst); 93 ProcessResult(opcode.result_operation, opcode.dst, dst);
156 break; 94 break;
157 } 95 }
158 case Operation::ExtractShiftLeftImmediate: { 96 case Macro::Operation::ExtractShiftLeftImmediate: {
159 u32 dst = GetRegister(opcode.src_a); 97 u32 dst = GetRegister(opcode.src_a);
160 u32 src = GetRegister(opcode.src_b); 98 u32 src = GetRegister(opcode.src_b);
161 99
@@ -164,7 +102,7 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
164 ProcessResult(opcode.result_operation, opcode.dst, result); 102 ProcessResult(opcode.result_operation, opcode.dst, result);
165 break; 103 break;
166 } 104 }
167 case Operation::ExtractShiftLeftRegister: { 105 case Macro::Operation::ExtractShiftLeftRegister: {
168 u32 dst = GetRegister(opcode.src_a); 106 u32 dst = GetRegister(opcode.src_a);
169 u32 src = GetRegister(opcode.src_b); 107 u32 src = GetRegister(opcode.src_b);
170 108
@@ -173,12 +111,12 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
173 ProcessResult(opcode.result_operation, opcode.dst, result); 111 ProcessResult(opcode.result_operation, opcode.dst, result);
174 break; 112 break;
175 } 113 }
176 case Operation::Read: { 114 case Macro::Operation::Read: {
177 u32 result = Read(GetRegister(opcode.src_a) + opcode.immediate); 115 u32 result = Read(GetRegister(opcode.src_a) + opcode.immediate);
178 ProcessResult(opcode.result_operation, opcode.dst, result); 116 ProcessResult(opcode.result_operation, opcode.dst, result);
179 break; 117 break;
180 } 118 }
181 case Operation::Branch: { 119 case Macro::Operation::Branch: {
182 ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid"); 120 ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid");
183 u32 value = GetRegister(opcode.src_a); 121 u32 value = GetRegister(opcode.src_a);
184 bool taken = EvaluateBranchCondition(opcode.branch_condition, value); 122 bool taken = EvaluateBranchCondition(opcode.branch_condition, value);
@@ -191,7 +129,7 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
191 129
192 delayed_pc = base_address + opcode.GetBranchTarget(); 130 delayed_pc = base_address + opcode.GetBranchTarget();
193 // Execute one more instruction due to the delay slot. 131 // Execute one more instruction due to the delay slot.
194 return Step(offset, true); 132 return Step(true);
195 } 133 }
196 break; 134 break;
197 } 135 }
@@ -204,51 +142,44 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
204 // cause an exit if it's executed inside a delay slot. 142 // cause an exit if it's executed inside a delay slot.
205 if (opcode.is_exit && !is_delay_slot) { 143 if (opcode.is_exit && !is_delay_slot) {
206 // Exit has a delay slot, execute the next instruction 144 // Exit has a delay slot, execute the next instruction
207 Step(offset, true); 145 Step(true);
208 return false; 146 return false;
209 } 147 }
210 148
211 return true; 149 return true;
212} 150}
213 151
214MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const { 152u32 MacroInterpreterImpl::GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b) {
215 const auto& macro_memory{maxwell3d.GetMacroMemory()};
216 ASSERT((pc % sizeof(u32)) == 0);
217 ASSERT((pc + offset) < macro_memory.size() * sizeof(u32));
218 return {macro_memory[offset + pc / sizeof(u32)]};
219}
220
221u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) {
222 switch (operation) { 153 switch (operation) {
223 case ALUOperation::Add: { 154 case Macro::ALUOperation::Add: {
224 const u64 result{static_cast<u64>(src_a) + src_b}; 155 const u64 result{static_cast<u64>(src_a) + src_b};
225 carry_flag = result > 0xffffffff; 156 carry_flag = result > 0xffffffff;
226 return static_cast<u32>(result); 157 return static_cast<u32>(result);
227 } 158 }
228 case ALUOperation::AddWithCarry: { 159 case Macro::ALUOperation::AddWithCarry: {
229 const u64 result{static_cast<u64>(src_a) + src_b + (carry_flag ? 1ULL : 0ULL)}; 160 const u64 result{static_cast<u64>(src_a) + src_b + (carry_flag ? 1ULL : 0ULL)};
230 carry_flag = result > 0xffffffff; 161 carry_flag = result > 0xffffffff;
231 return static_cast<u32>(result); 162 return static_cast<u32>(result);
232 } 163 }
233 case ALUOperation::Subtract: { 164 case Macro::ALUOperation::Subtract: {
234 const u64 result{static_cast<u64>(src_a) - src_b}; 165 const u64 result{static_cast<u64>(src_a) - src_b};
235 carry_flag = result < 0x100000000; 166 carry_flag = result < 0x100000000;
236 return static_cast<u32>(result); 167 return static_cast<u32>(result);
237 } 168 }
238 case ALUOperation::SubtractWithBorrow: { 169 case Macro::ALUOperation::SubtractWithBorrow: {
239 const u64 result{static_cast<u64>(src_a) - src_b - (carry_flag ? 0ULL : 1ULL)}; 170 const u64 result{static_cast<u64>(src_a) - src_b - (carry_flag ? 0ULL : 1ULL)};
240 carry_flag = result < 0x100000000; 171 carry_flag = result < 0x100000000;
241 return static_cast<u32>(result); 172 return static_cast<u32>(result);
242 } 173 }
243 case ALUOperation::Xor: 174 case Macro::ALUOperation::Xor:
244 return src_a ^ src_b; 175 return src_a ^ src_b;
245 case ALUOperation::Or: 176 case Macro::ALUOperation::Or:
246 return src_a | src_b; 177 return src_a | src_b;
247 case ALUOperation::And: 178 case Macro::ALUOperation::And:
248 return src_a & src_b; 179 return src_a & src_b;
249 case ALUOperation::AndNot: 180 case Macro::ALUOperation::AndNot:
250 return src_a & ~src_b; 181 return src_a & ~src_b;
251 case ALUOperation::Nand: 182 case Macro::ALUOperation::Nand:
252 return ~(src_a & src_b); 183 return ~(src_a & src_b);
253 184
254 default: 185 default:
@@ -257,43 +188,43 @@ u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b)
257 } 188 }
258} 189}
259 190
260void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 result) { 191void MacroInterpreterImpl::ProcessResult(Macro::ResultOperation operation, u32 reg, u32 result) {
261 switch (operation) { 192 switch (operation) {
262 case ResultOperation::IgnoreAndFetch: 193 case Macro::ResultOperation::IgnoreAndFetch:
263 // Fetch parameter and ignore result. 194 // Fetch parameter and ignore result.
264 SetRegister(reg, FetchParameter()); 195 SetRegister(reg, FetchParameter());
265 break; 196 break;
266 case ResultOperation::Move: 197 case Macro::ResultOperation::Move:
267 // Move result. 198 // Move result.
268 SetRegister(reg, result); 199 SetRegister(reg, result);
269 break; 200 break;
270 case ResultOperation::MoveAndSetMethod: 201 case Macro::ResultOperation::MoveAndSetMethod:
271 // Move result and use as Method Address. 202 // Move result and use as Method Address.
272 SetRegister(reg, result); 203 SetRegister(reg, result);
273 SetMethodAddress(result); 204 SetMethodAddress(result);
274 break; 205 break;
275 case ResultOperation::FetchAndSend: 206 case Macro::ResultOperation::FetchAndSend:
276 // Fetch parameter and send result. 207 // Fetch parameter and send result.
277 SetRegister(reg, FetchParameter()); 208 SetRegister(reg, FetchParameter());
278 Send(result); 209 Send(result);
279 break; 210 break;
280 case ResultOperation::MoveAndSend: 211 case Macro::ResultOperation::MoveAndSend:
281 // Move and send result. 212 // Move and send result.
282 SetRegister(reg, result); 213 SetRegister(reg, result);
283 Send(result); 214 Send(result);
284 break; 215 break;
285 case ResultOperation::FetchAndSetMethod: 216 case Macro::ResultOperation::FetchAndSetMethod:
286 // Fetch parameter and use result as Method Address. 217 // Fetch parameter and use result as Method Address.
287 SetRegister(reg, FetchParameter()); 218 SetRegister(reg, FetchParameter());
288 SetMethodAddress(result); 219 SetMethodAddress(result);
289 break; 220 break;
290 case ResultOperation::MoveAndSetMethodFetchAndSend: 221 case Macro::ResultOperation::MoveAndSetMethodFetchAndSend:
291 // Move result and use as Method Address, then fetch and send parameter. 222 // Move result and use as Method Address, then fetch and send parameter.
292 SetRegister(reg, result); 223 SetRegister(reg, result);
293 SetMethodAddress(result); 224 SetMethodAddress(result);
294 Send(FetchParameter()); 225 Send(FetchParameter());
295 break; 226 break;
296 case ResultOperation::MoveAndSetMethodSend: 227 case Macro::ResultOperation::MoveAndSetMethodSend:
297 // Move result and use as Method Address, then send bits 12:17 of result. 228 // Move result and use as Method Address, then send bits 12:17 of result.
298 SetRegister(reg, result); 229 SetRegister(reg, result);
299 SetMethodAddress(result); 230 SetMethodAddress(result);
@@ -304,16 +235,28 @@ void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 res
304 } 235 }
305} 236}
306 237
307u32 MacroInterpreter::FetchParameter() { 238bool MacroInterpreterImpl::EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const {
308 ASSERT(next_parameter_index < num_parameters); 239 switch (cond) {
309 return parameters[next_parameter_index++]; 240 case Macro::BranchCondition::Zero:
241 return value == 0;
242 case Macro::BranchCondition::NotZero:
243 return value != 0;
244 }
245 UNREACHABLE();
246 return true;
247}
248
249Macro::Opcode MacroInterpreterImpl::GetOpcode() const {
250 ASSERT((pc % sizeof(u32)) == 0);
251 ASSERT(pc < code.size() * sizeof(u32));
252 return {code[pc / sizeof(u32)]};
310} 253}
311 254
312u32 MacroInterpreter::GetRegister(u32 register_id) const { 255u32 MacroInterpreterImpl::GetRegister(u32 register_id) const {
313 return registers.at(register_id); 256 return registers.at(register_id);
314} 257}
315 258
316void MacroInterpreter::SetRegister(u32 register_id, u32 value) { 259void MacroInterpreterImpl::SetRegister(u32 register_id, u32 value) {
317 // Register 0 is hardwired as the zero register. 260 // Register 0 is hardwired as the zero register.
318 // Ensure no writes to it actually occur. 261 // Ensure no writes to it actually occur.
319 if (register_id == 0) { 262 if (register_id == 0) {
@@ -323,30 +266,24 @@ void MacroInterpreter::SetRegister(u32 register_id, u32 value) {
323 registers.at(register_id) = value; 266 registers.at(register_id) = value;
324} 267}
325 268
326void MacroInterpreter::SetMethodAddress(u32 address) { 269void MacroInterpreterImpl::SetMethodAddress(u32 address) {
327 method_address.raw = address; 270 method_address.raw = address;
328} 271}
329 272
330void MacroInterpreter::Send(u32 value) { 273void MacroInterpreterImpl::Send(u32 value) {
331 maxwell3d.CallMethodFromMME(method_address.address, value); 274 maxwell3d.CallMethodFromMME(method_address.address, value);
332 // Increment the method address by the method increment. 275 // Increment the method address by the method increment.
333 method_address.address.Assign(method_address.address.Value() + 276 method_address.address.Assign(method_address.address.Value() +
334 method_address.increment.Value()); 277 method_address.increment.Value());
335} 278}
336 279
337u32 MacroInterpreter::Read(u32 method) const { 280u32 MacroInterpreterImpl::Read(u32 method) const {
338 return maxwell3d.GetRegisterValue(method); 281 return maxwell3d.GetRegisterValue(method);
339} 282}
340 283
341bool MacroInterpreter::EvaluateBranchCondition(BranchCondition cond, u32 value) const { 284u32 MacroInterpreterImpl::FetchParameter() {
342 switch (cond) { 285 ASSERT(next_parameter_index < num_parameters);
343 case BranchCondition::Zero: 286 return parameters[next_parameter_index++];
344 return value == 0;
345 case BranchCondition::NotZero:
346 return value != 0;
347 }
348 UNREACHABLE();
349 return true;
350} 287}
351 288
352} // namespace Tegra 289} // namespace Tegra
diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro/macro_interpreter.h
index 631146d89..90217fc89 100644
--- a/src/video_core/macro_interpreter.h
+++ b/src/video_core/macro/macro_interpreter.h
@@ -1,44 +1,37 @@
1// Copyright 2018 yuzu Emulator Project 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6
7#include <array> 6#include <array>
8#include <optional> 7#include <optional>
9 8#include <vector>
10#include "common/bit_field.h" 9#include "common/bit_field.h"
11#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/macro/macro.h"
12 12
13namespace Tegra { 13namespace Tegra {
14namespace Engines { 14namespace Engines {
15class Maxwell3D; 15class Maxwell3D;
16} 16}
17 17
18class MacroInterpreter final { 18class MacroInterpreter final : public MacroEngine {
19public: 19public:
20 explicit MacroInterpreter(Engines::Maxwell3D& maxwell3d); 20 explicit MacroInterpreter(Engines::Maxwell3D& maxwell3d);
21 21
22 /** 22protected:
23 * Executes the macro code with the specified input parameters. 23 std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) override;
24 * @param offset Offset to start execution at.
25 * @param parameters The parameters of the macro.
26 */
27 void Execute(u32 offset, std::size_t num_parameters, const u32* parameters);
28 24
29private: 25private:
30 enum class ALUOperation : u32; 26 Engines::Maxwell3D& maxwell3d;
31 enum class BranchCondition : u32; 27};
32 enum class ResultOperation : u32;
33
34 union Opcode;
35 28
36 union MethodAddress { 29class MacroInterpreterImpl : public CachedMacro {
37 u32 raw; 30public:
38 BitField<0, 12, u32> address; 31 MacroInterpreterImpl(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& code);
39 BitField<12, 6, u32> increment; 32 void Execute(const std::vector<u32>& parameters, u32 method) override;
40 };
41 33
34private:
42 /// Resets the execution engine state, zeroing registers, etc. 35 /// Resets the execution engine state, zeroing registers, etc.
43 void Reset(); 36 void Reset();
44 37
@@ -49,20 +42,20 @@ private:
49 * @param is_delay_slot Whether the current step is being executed due to a delay slot in a 42 * @param is_delay_slot Whether the current step is being executed due to a delay slot in a
50 * previous instruction. 43 * previous instruction.
51 */ 44 */
52 bool Step(u32 offset, bool is_delay_slot); 45 bool Step(bool is_delay_slot);
53 46
54 /// Calculates the result of an ALU operation. src_a OP src_b; 47 /// Calculates the result of an ALU operation. src_a OP src_b;
55 u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b); 48 u32 GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b);
56 49
57 /// Performs the result operation on the input result and stores it in the specified register 50 /// Performs the result operation on the input result and stores it in the specified register
58 /// (if necessary). 51 /// (if necessary).
59 void ProcessResult(ResultOperation operation, u32 reg, u32 result); 52 void ProcessResult(Macro::ResultOperation operation, u32 reg, u32 result);
60 53
61 /// Evaluates the branch condition and returns whether the branch should be taken or not. 54 /// Evaluates the branch condition and returns whether the branch should be taken or not.
62 bool EvaluateBranchCondition(BranchCondition cond, u32 value) const; 55 bool EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const;
63 56
64 /// Reads an opcode at the current program counter location. 57 /// Reads an opcode at the current program counter location.
65 Opcode GetOpcode(u32 offset) const; 58 Macro::Opcode GetOpcode() const;
66 59
67 /// Returns the specified register's value. Register 0 is hardcoded to always return 0. 60 /// Returns the specified register's value. Register 0 is hardcoded to always return 0.
68 u32 GetRegister(u32 register_id) const; 61 u32 GetRegister(u32 register_id) const;
@@ -89,13 +82,11 @@ private:
89 /// Program counter to execute at after the delay slot is executed. 82 /// Program counter to execute at after the delay slot is executed.
90 std::optional<u32> delayed_pc; 83 std::optional<u32> delayed_pc;
91 84
92 static constexpr std::size_t NumMacroRegisters = 8;
93
94 /// General purpose macro registers. 85 /// General purpose macro registers.
95 std::array<u32, NumMacroRegisters> registers = {}; 86 std::array<u32, Macro::NUM_MACRO_REGISTERS> registers = {};
96 87
97 /// Method address to use for the next Send instruction. 88 /// Method address to use for the next Send instruction.
98 MethodAddress method_address = {}; 89 Macro::MethodAddress method_address = {};
99 90
100 /// Input parameters of the current macro. 91 /// Input parameters of the current macro.
101 std::unique_ptr<u32[]> parameters; 92 std::unique_ptr<u32[]> parameters;
@@ -105,5 +96,7 @@ private:
105 u32 next_parameter_index = 0; 96 u32 next_parameter_index = 0;
106 97
107 bool carry_flag = false; 98 bool carry_flag = false;
99 const std::vector<u32>& code;
108}; 100};
101
109} // namespace Tegra 102} // namespace Tegra
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp
new file mode 100644
index 000000000..07292702f
--- /dev/null
+++ b/src/video_core/macro/macro_jit_x64.cpp
@@ -0,0 +1,621 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "common/microprofile.h"
8#include "common/x64/xbyak_util.h"
9#include "video_core/engines/maxwell_3d.h"
10#include "video_core/macro/macro_interpreter.h"
11#include "video_core/macro/macro_jit_x64.h"
12
13MICROPROFILE_DEFINE(MacroJitCompile, "GPU", "Compile macro JIT", MP_RGB(173, 255, 47));
14MICROPROFILE_DEFINE(MacroJitExecute, "GPU", "Execute macro JIT", MP_RGB(255, 255, 0));
15
16namespace Tegra {
17static const Xbyak::Reg64 STATE = Xbyak::util::rbx;
18static const Xbyak::Reg32 RESULT = Xbyak::util::ebp;
19static const Xbyak::Reg64 PARAMETERS = Xbyak::util::r12;
20static const Xbyak::Reg32 METHOD_ADDRESS = Xbyak::util::r14d;
21static const Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15;
22
23static const std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({
24 STATE,
25 RESULT,
26 PARAMETERS,
27 METHOD_ADDRESS,
28 BRANCH_HOLDER,
29});
30
31MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d)
32 : MacroEngine::MacroEngine(maxwell3d), maxwell3d(maxwell3d) {}
33
34std::unique_ptr<CachedMacro> MacroJITx64::Compile(const std::vector<u32>& code) {
35 return std::make_unique<MacroJITx64Impl>(maxwell3d, code);
36}
37
38MacroJITx64Impl::MacroJITx64Impl(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& code)
39 : Xbyak::CodeGenerator(MAX_CODE_SIZE), code(code), maxwell3d(maxwell3d) {
40 Compile();
41}
42
43MacroJITx64Impl::~MacroJITx64Impl() = default;
44
45void MacroJITx64Impl::Execute(const std::vector<u32>& parameters, u32 method) {
46 MICROPROFILE_SCOPE(MacroJitExecute);
47 ASSERT_OR_EXECUTE(program != nullptr, { return; });
48 JITState state{};
49 state.maxwell3d = &maxwell3d;
50 state.registers = {};
51 program(&state, parameters.data());
52}
53
54void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) {
55 const bool is_a_zero = opcode.src_a == 0;
56 const bool is_b_zero = opcode.src_b == 0;
57 const bool valid_operation = !is_a_zero && !is_b_zero;
58 [[maybe_unused]] const bool is_move_operation = !is_a_zero && is_b_zero;
59 const bool has_zero_register = is_a_zero || is_b_zero;
60 const bool no_zero_reg_skip = opcode.alu_operation == Macro::ALUOperation::AddWithCarry ||
61 opcode.alu_operation == Macro::ALUOperation::SubtractWithBorrow;
62
63 Xbyak::Reg32 src_a;
64 Xbyak::Reg32 src_b;
65
66 if (!optimizer.zero_reg_skip || no_zero_reg_skip) {
67 src_a = Compile_GetRegister(opcode.src_a, RESULT);
68 src_b = Compile_GetRegister(opcode.src_b, eax);
69 } else {
70 if (!is_a_zero) {
71 src_a = Compile_GetRegister(opcode.src_a, RESULT);
72 }
73 if (!is_b_zero) {
74 src_b = Compile_GetRegister(opcode.src_b, eax);
75 }
76 }
77
78 bool has_emitted = false;
79
80 switch (opcode.alu_operation) {
81 case Macro::ALUOperation::Add:
82 if (optimizer.zero_reg_skip) {
83 if (valid_operation) {
84 add(src_a, src_b);
85 }
86 } else {
87 add(src_a, src_b);
88 }
89
90 if (!optimizer.can_skip_carry) {
91 setc(byte[STATE + offsetof(JITState, carry_flag)]);
92 }
93 break;
94 case Macro::ALUOperation::AddWithCarry:
95 bt(dword[STATE + offsetof(JITState, carry_flag)], 0);
96 adc(src_a, src_b);
97 setc(byte[STATE + offsetof(JITState, carry_flag)]);
98 break;
99 case Macro::ALUOperation::Subtract:
100 if (optimizer.zero_reg_skip) {
101 if (valid_operation) {
102 sub(src_a, src_b);
103 has_emitted = true;
104 }
105 } else {
106 sub(src_a, src_b);
107 has_emitted = true;
108 }
109 if (!optimizer.can_skip_carry && has_emitted) {
110 setc(byte[STATE + offsetof(JITState, carry_flag)]);
111 }
112 break;
113 case Macro::ALUOperation::SubtractWithBorrow:
114 bt(dword[STATE + offsetof(JITState, carry_flag)], 0);
115 sbb(src_a, src_b);
116 setc(byte[STATE + offsetof(JITState, carry_flag)]);
117 break;
118 case Macro::ALUOperation::Xor:
119 if (optimizer.zero_reg_skip) {
120 if (valid_operation) {
121 xor_(src_a, src_b);
122 }
123 } else {
124 xor_(src_a, src_b);
125 }
126 break;
127 case Macro::ALUOperation::Or:
128 if (optimizer.zero_reg_skip) {
129 if (valid_operation) {
130 or_(src_a, src_b);
131 }
132 } else {
133 or_(src_a, src_b);
134 }
135 break;
136 case Macro::ALUOperation::And:
137 if (optimizer.zero_reg_skip) {
138 if (!has_zero_register) {
139 and_(src_a, src_b);
140 }
141 } else {
142 and_(src_a, src_b);
143 }
144 break;
145 case Macro::ALUOperation::AndNot:
146 if (optimizer.zero_reg_skip) {
147 if (!is_a_zero) {
148 not_(src_b);
149 and_(src_a, src_b);
150 }
151 } else {
152 not_(src_b);
153 and_(src_a, src_b);
154 }
155 break;
156 case Macro::ALUOperation::Nand:
157 if (optimizer.zero_reg_skip) {
158 if (!is_a_zero) {
159 and_(src_a, src_b);
160 not_(src_a);
161 }
162 } else {
163 and_(src_a, src_b);
164 not_(src_a);
165 }
166 break;
167 default:
168 UNIMPLEMENTED_MSG("Unimplemented ALU operation {}",
169 static_cast<std::size_t>(opcode.alu_operation.Value()));
170 break;
171 }
172 Compile_ProcessResult(opcode.result_operation, opcode.dst);
173}
174
175void MacroJITx64Impl::Compile_AddImmediate(Macro::Opcode opcode) {
176 if (optimizer.skip_dummy_addimmediate) {
177 // Games tend to use this as an exit instruction placeholder. It's to encode an instruction
178 // without doing anything. In our case we can just not emit anything.
179 if (opcode.result_operation == Macro::ResultOperation::Move && opcode.dst == 0) {
180 return;
181 }
182 }
183 // Check for redundant moves
184 if (optimizer.optimize_for_method_move &&
185 opcode.result_operation == Macro::ResultOperation::MoveAndSetMethod) {
186 if (next_opcode.has_value()) {
187 const auto next = *next_opcode;
188 if (next.result_operation == Macro::ResultOperation::MoveAndSetMethod &&
189 opcode.dst == next.dst) {
190 return;
191 }
192 }
193 }
194 if (optimizer.zero_reg_skip && opcode.src_a == 0) {
195 if (opcode.immediate == 0) {
196 xor_(RESULT, RESULT);
197 } else {
198 mov(RESULT, opcode.immediate);
199 }
200 } else {
201 auto result = Compile_GetRegister(opcode.src_a, RESULT);
202 if (opcode.immediate > 2) {
203 add(result, opcode.immediate);
204 } else if (opcode.immediate == 1) {
205 inc(result);
206 } else if (opcode.immediate < 0) {
207 sub(result, opcode.immediate * -1);
208 }
209 }
210 Compile_ProcessResult(opcode.result_operation, opcode.dst);
211}
212
213void MacroJITx64Impl::Compile_ExtractInsert(Macro::Opcode opcode) {
214 auto dst = Compile_GetRegister(opcode.src_a, RESULT);
215 auto src = Compile_GetRegister(opcode.src_b, eax);
216
217 if (opcode.bf_src_bit != 0 && opcode.bf_src_bit != 31) {
218 shr(src, opcode.bf_src_bit);
219 } else if (opcode.bf_src_bit == 31) {
220 xor_(src, src);
221 }
222 // Don't bother masking the whole register since we're using a 32 bit register
223 if (opcode.bf_size != 31 && opcode.bf_size != 0) {
224 and_(src, opcode.GetBitfieldMask());
225 } else if (opcode.bf_size == 0) {
226 xor_(src, src);
227 }
228 if (opcode.bf_dst_bit != 31 && opcode.bf_dst_bit != 0) {
229 shl(src, opcode.bf_dst_bit);
230 } else if (opcode.bf_dst_bit == 31) {
231 xor_(src, src);
232 }
233
234 const u32 mask = ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit);
235 if (mask != 0xffffffff) {
236 and_(dst, mask);
237 }
238 or_(dst, src);
239 Compile_ProcessResult(opcode.result_operation, opcode.dst);
240}
241
242void MacroJITx64Impl::Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode) {
243 const auto dst = Compile_GetRegister(opcode.src_a, ecx);
244 const auto src = Compile_GetRegister(opcode.src_b, RESULT);
245
246 shr(src, dst.cvt8());
247 if (opcode.bf_size != 0 && opcode.bf_size != 31) {
248 and_(src, opcode.GetBitfieldMask());
249 } else if (opcode.bf_size == 0) {
250 xor_(src, src);
251 }
252
253 if (opcode.bf_dst_bit != 0 && opcode.bf_dst_bit != 31) {
254 shl(src, opcode.bf_dst_bit);
255 } else if (opcode.bf_dst_bit == 31) {
256 xor_(src, src);
257 }
258 Compile_ProcessResult(opcode.result_operation, opcode.dst);
259}
260
261void MacroJITx64Impl::Compile_ExtractShiftLeftRegister(Macro::Opcode opcode) {
262 const auto dst = Compile_GetRegister(opcode.src_a, ecx);
263 const auto src = Compile_GetRegister(opcode.src_b, RESULT);
264
265 if (opcode.bf_src_bit != 0) {
266 shr(src, opcode.bf_src_bit);
267 }
268
269 if (opcode.bf_size != 31) {
270 and_(src, opcode.GetBitfieldMask());
271 }
272 shl(src, dst.cvt8());
273
274 Compile_ProcessResult(opcode.result_operation, opcode.dst);
275}
276
277void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) {
278 if (optimizer.zero_reg_skip && opcode.src_a == 0) {
279 if (opcode.immediate == 0) {
280 xor_(RESULT, RESULT);
281 } else {
282 mov(RESULT, opcode.immediate);
283 }
284 } else {
285 auto result = Compile_GetRegister(opcode.src_a, RESULT);
286 if (opcode.immediate > 2) {
287 add(result, opcode.immediate);
288 } else if (opcode.immediate == 1) {
289 inc(result);
290 } else if (opcode.immediate < 0) {
291 sub(result, opcode.immediate * -1);
292 }
293 }
294
295 // Equivalent to Engines::Maxwell3D::GetRegisterValue:
296 if (optimizer.enable_asserts) {
297 Xbyak::Label pass_range_check;
298 cmp(RESULT, static_cast<u32>(Engines::Maxwell3D::Regs::NUM_REGS));
299 jb(pass_range_check);
300 int3();
301 L(pass_range_check);
302 }
303 mov(rax, qword[STATE]);
304 mov(RESULT,
305 dword[rax + offsetof(Engines::Maxwell3D, regs) +
306 offsetof(Engines::Maxwell3D::Regs, reg_array) + RESULT.cvt64() * sizeof(u32)]);
307
308 Compile_ProcessResult(opcode.result_operation, opcode.dst);
309}
310
311static void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) {
312 maxwell3d->CallMethodFromMME(method_address.address, value);
313}
314
315void Tegra::MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) {
316 Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
317 mov(Common::X64::ABI_PARAM1, qword[STATE]);
318 mov(Common::X64::ABI_PARAM2, METHOD_ADDRESS);
319 mov(Common::X64::ABI_PARAM3, value);
320 Common::X64::CallFarFunction(*this, &Send);
321 Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
322
323 Xbyak::Label dont_process{};
324 // Get increment
325 test(METHOD_ADDRESS, 0x3f000);
326 // If zero, method address doesn't update
327 je(dont_process);
328
329 mov(ecx, METHOD_ADDRESS);
330 and_(METHOD_ADDRESS, 0xfff);
331 shr(ecx, 12);
332 and_(ecx, 0x3f);
333 lea(eax, ptr[rcx + METHOD_ADDRESS.cvt64()]);
334 sal(ecx, 12);
335 or_(eax, ecx);
336
337 mov(METHOD_ADDRESS, eax);
338
339 L(dont_process);
340}
341
342void Tegra::MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) {
343 ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid");
344 const s32 jump_address =
345 static_cast<s32>(pc) + static_cast<s32>(opcode.GetBranchTarget() / sizeof(s32));
346
347 Xbyak::Label end;
348 auto value = Compile_GetRegister(opcode.src_a, eax);
349 test(value, value);
350 if (optimizer.has_delayed_pc) {
351 switch (opcode.branch_condition) {
352 case Macro::BranchCondition::Zero:
353 jne(end, T_NEAR);
354 break;
355 case Macro::BranchCondition::NotZero:
356 je(end, T_NEAR);
357 break;
358 }
359
360 if (opcode.branch_annul) {
361 xor_(BRANCH_HOLDER, BRANCH_HOLDER);
362 jmp(labels[jump_address], T_NEAR);
363 } else {
364 Xbyak::Label handle_post_exit{};
365 Xbyak::Label skip{};
366 jmp(skip, T_NEAR);
367 if (opcode.is_exit) {
368 L(handle_post_exit);
369 // Execute 1 instruction
370 mov(BRANCH_HOLDER, end_of_code);
371 // Jump to next instruction to skip delay slot check
372 jmp(labels[jump_address], T_NEAR);
373 } else {
374 L(handle_post_exit);
375 xor_(BRANCH_HOLDER, BRANCH_HOLDER);
376 jmp(labels[jump_address], T_NEAR);
377 }
378 L(skip);
379 mov(BRANCH_HOLDER, handle_post_exit);
380 jmp(delay_skip[pc], T_NEAR);
381 }
382 } else {
383 switch (opcode.branch_condition) {
384 case Macro::BranchCondition::Zero:
385 je(labels[jump_address], T_NEAR);
386 break;
387 case Macro::BranchCondition::NotZero:
388 jne(labels[jump_address], T_NEAR);
389 break;
390 }
391 }
392
393 L(end);
394}
395
396void Tegra::MacroJITx64Impl::Optimizer_ScanFlags() {
397 optimizer.can_skip_carry = true;
398 optimizer.has_delayed_pc = false;
399 for (auto raw_op : code) {
400 Macro::Opcode op{};
401 op.raw = raw_op;
402
403 if (op.operation == Macro::Operation::ALU) {
404 // Scan for any ALU operations which actually use the carry flag, if they don't exist in
405 // our current code we can skip emitting the carry flag handling operations
406 if (op.alu_operation == Macro::ALUOperation::AddWithCarry ||
407 op.alu_operation == Macro::ALUOperation::SubtractWithBorrow) {
408 optimizer.can_skip_carry = false;
409 }
410 }
411
412 if (op.operation == Macro::Operation::Branch) {
413 if (!op.branch_annul) {
414 optimizer.has_delayed_pc = true;
415 }
416 }
417 }
418}
419
420void MacroJITx64Impl::Compile() {
421 MICROPROFILE_SCOPE(MacroJitCompile);
422 bool keep_executing = true;
423 labels.fill(Xbyak::Label());
424
425 Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
426 // JIT state
427 mov(STATE, Common::X64::ABI_PARAM1);
428 mov(PARAMETERS, Common::X64::ABI_PARAM2);
429 xor_(RESULT, RESULT);
430 xor_(METHOD_ADDRESS, METHOD_ADDRESS);
431 xor_(BRANCH_HOLDER, BRANCH_HOLDER);
432
433 mov(dword[STATE + offsetof(JITState, registers) + 4], Compile_FetchParameter());
434
435 // Track get register for zero registers and mark it as no-op
436 optimizer.zero_reg_skip = true;
437
438 // AddImmediate tends to be used as a NOP instruction, if we detect this we can
439 // completely skip the entire code path and no emit anything
440 optimizer.skip_dummy_addimmediate = true;
441
442 // SMO tends to emit a lot of unnecessary method moves, we can mitigate this by only emitting
443 // one if our register isn't "dirty"
444 optimizer.optimize_for_method_move = true;
445
446 // Enable run-time assertions in JITted code
447 optimizer.enable_asserts = false;
448
449 // Check to see if we can skip emitting certain instructions
450 Optimizer_ScanFlags();
451
452 const u32 op_count = static_cast<u32>(code.size());
453 for (u32 i = 0; i < op_count; i++) {
454 if (i < op_count - 1) {
455 pc = i + 1;
456 next_opcode = GetOpCode();
457 } else {
458 next_opcode = {};
459 }
460 pc = i;
461 Compile_NextInstruction();
462 }
463
464 L(end_of_code);
465
466 Common::X64::ABI_PopRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
467 ret();
468 ready();
469 program = getCode<ProgramType>();
470}
471
472bool MacroJITx64Impl::Compile_NextInstruction() {
473 const auto opcode = GetOpCode();
474 if (labels[pc].getAddress()) {
475 return false;
476 }
477
478 L(labels[pc]);
479
480 switch (opcode.operation) {
481 case Macro::Operation::ALU:
482 Compile_ALU(opcode);
483 break;
484 case Macro::Operation::AddImmediate:
485 Compile_AddImmediate(opcode);
486 break;
487 case Macro::Operation::ExtractInsert:
488 Compile_ExtractInsert(opcode);
489 break;
490 case Macro::Operation::ExtractShiftLeftImmediate:
491 Compile_ExtractShiftLeftImmediate(opcode);
492 break;
493 case Macro::Operation::ExtractShiftLeftRegister:
494 Compile_ExtractShiftLeftRegister(opcode);
495 break;
496 case Macro::Operation::Read:
497 Compile_Read(opcode);
498 break;
499 case Macro::Operation::Branch:
500 Compile_Branch(opcode);
501 break;
502 default:
503 UNIMPLEMENTED_MSG("Unimplemented opcode {}", opcode.operation.Value());
504 break;
505 }
506
507 if (optimizer.has_delayed_pc) {
508 if (opcode.is_exit) {
509 mov(rax, end_of_code);
510 test(BRANCH_HOLDER, BRANCH_HOLDER);
511 cmove(BRANCH_HOLDER, rax);
512 // Jump to next instruction to skip delay slot check
513 je(labels[pc + 1], T_NEAR);
514 } else {
515 // TODO(ogniK): Optimize delay slot branching
516 Xbyak::Label no_delay_slot{};
517 test(BRANCH_HOLDER, BRANCH_HOLDER);
518 je(no_delay_slot, T_NEAR);
519 mov(rax, BRANCH_HOLDER);
520 xor_(BRANCH_HOLDER, BRANCH_HOLDER);
521 jmp(rax);
522 L(no_delay_slot);
523 }
524 L(delay_skip[pc]);
525 if (opcode.is_exit) {
526 return false;
527 }
528 } else {
529 test(BRANCH_HOLDER, BRANCH_HOLDER);
530 jne(end_of_code, T_NEAR);
531 if (opcode.is_exit) {
532 inc(BRANCH_HOLDER);
533 return false;
534 }
535 }
536 return true;
537}
538
539Xbyak::Reg32 Tegra::MacroJITx64Impl::Compile_FetchParameter() {
540 mov(eax, dword[PARAMETERS]);
541 add(PARAMETERS, sizeof(u32));
542 return eax;
543}
544
545Xbyak::Reg32 MacroJITx64Impl::Compile_GetRegister(u32 index, Xbyak::Reg32 dst) {
546 if (index == 0) {
547 // Register 0 is always zero
548 xor_(dst, dst);
549 } else {
550 mov(dst, dword[STATE + offsetof(JITState, registers) + index * sizeof(u32)]);
551 }
552
553 return dst;
554}
555
556void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u32 reg) {
557 const auto SetRegister = [this](u32 reg, const Xbyak::Reg32& result) {
558 // Register 0 is supposed to always return 0. NOP is implemented as a store to the zero
559 // register.
560 if (reg == 0) {
561 return;
562 }
563 mov(dword[STATE + offsetof(JITState, registers) + reg * sizeof(u32)], result);
564 };
565 const auto SetMethodAddress = [this](const Xbyak::Reg32& reg) { mov(METHOD_ADDRESS, reg); };
566
567 switch (operation) {
568 case Macro::ResultOperation::IgnoreAndFetch:
569 SetRegister(reg, Compile_FetchParameter());
570 break;
571 case Macro::ResultOperation::Move:
572 SetRegister(reg, RESULT);
573 break;
574 case Macro::ResultOperation::MoveAndSetMethod:
575 SetRegister(reg, RESULT);
576 SetMethodAddress(RESULT);
577 break;
578 case Macro::ResultOperation::FetchAndSend:
579 // Fetch parameter and send result.
580 SetRegister(reg, Compile_FetchParameter());
581 Compile_Send(RESULT);
582 break;
583 case Macro::ResultOperation::MoveAndSend:
584 // Move and send result.
585 SetRegister(reg, RESULT);
586 Compile_Send(RESULT);
587 break;
588 case Macro::ResultOperation::FetchAndSetMethod:
589 // Fetch parameter and use result as Method Address.
590 SetRegister(reg, Compile_FetchParameter());
591 SetMethodAddress(RESULT);
592 break;
593 case Macro::ResultOperation::MoveAndSetMethodFetchAndSend:
594 // Move result and use as Method Address, then fetch and send parameter.
595 SetRegister(reg, RESULT);
596 SetMethodAddress(RESULT);
597 Compile_Send(Compile_FetchParameter());
598 break;
599 case Macro::ResultOperation::MoveAndSetMethodSend:
600 // Move result and use as Method Address, then send bits 12:17 of result.
601 SetRegister(reg, RESULT);
602 SetMethodAddress(RESULT);
603 shr(RESULT, 12);
604 and_(RESULT, 0b111111);
605 Compile_Send(RESULT);
606 break;
607 default:
608 UNIMPLEMENTED_MSG("Unimplemented macro operation {}", static_cast<std::size_t>(operation));
609 }
610}
611
612Macro::Opcode MacroJITx64Impl::GetOpCode() const {
613 ASSERT(pc < code.size());
614 return {code[pc]};
615}
616
617std::bitset<32> MacroJITx64Impl::PersistentCallerSavedRegs() const {
618 return PERSISTENT_REGISTERS & Common::X64::ABI_ALL_CALLER_SAVED;
619}
620
621} // namespace Tegra
diff --git a/src/video_core/macro/macro_jit_x64.h b/src/video_core/macro/macro_jit_x64.h
new file mode 100644
index 000000000..a180e7428
--- /dev/null
+++ b/src/video_core/macro/macro_jit_x64.h
@@ -0,0 +1,98 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <bitset>
9#include <xbyak.h>
10#include "common/bit_field.h"
11#include "common/common_types.h"
12#include "common/x64/xbyak_abi.h"
13#include "video_core/macro/macro.h"
14
15namespace Tegra {
16
17namespace Engines {
18class Maxwell3D;
19}
20
21/// MAX_CODE_SIZE is arbitrarily chosen based on current booting games
22constexpr size_t MAX_CODE_SIZE = 0x10000;
23
24class MacroJITx64 final : public MacroEngine {
25public:
26 explicit MacroJITx64(Engines::Maxwell3D& maxwell3d);
27
28protected:
29 std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) override;
30
31private:
32 Engines::Maxwell3D& maxwell3d;
33};
34
35class MacroJITx64Impl : public Xbyak::CodeGenerator, public CachedMacro {
36public:
37 MacroJITx64Impl(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& code);
38 ~MacroJITx64Impl();
39
40 void Execute(const std::vector<u32>& parameters, u32 method) override;
41
42 void Compile_ALU(Macro::Opcode opcode);
43 void Compile_AddImmediate(Macro::Opcode opcode);
44 void Compile_ExtractInsert(Macro::Opcode opcode);
45 void Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode);
46 void Compile_ExtractShiftLeftRegister(Macro::Opcode opcode);
47 void Compile_Read(Macro::Opcode opcode);
48 void Compile_Branch(Macro::Opcode opcode);
49
50private:
51 void Optimizer_ScanFlags();
52
53 void Compile();
54 bool Compile_NextInstruction();
55
56 Xbyak::Reg32 Compile_FetchParameter();
57 Xbyak::Reg32 Compile_GetRegister(u32 index, Xbyak::Reg32 dst);
58
59 void Compile_ProcessResult(Macro::ResultOperation operation, u32 reg);
60 void Compile_Send(Xbyak::Reg32 value);
61
62 Macro::Opcode GetOpCode() const;
63 std::bitset<32> PersistentCallerSavedRegs() const;
64
65 struct JITState {
66 Engines::Maxwell3D* maxwell3d{};
67 std::array<u32, Macro::NUM_MACRO_REGISTERS> registers{};
68 u32 carry_flag{};
69 };
70 static_assert(offsetof(JITState, maxwell3d) == 0, "Maxwell3D is not at 0x0");
71 using ProgramType = void (*)(JITState*, const u32*);
72
73 struct OptimizerState {
74 bool can_skip_carry{};
75 bool has_delayed_pc{};
76 bool zero_reg_skip{};
77 bool skip_dummy_addimmediate{};
78 bool optimize_for_method_move{};
79 bool enable_asserts{};
80 };
81 OptimizerState optimizer{};
82
83 std::optional<Macro::Opcode> next_opcode{};
84 ProgramType program{nullptr};
85
86 std::array<Xbyak::Label, MAX_CODE_SIZE> labels;
87 std::array<Xbyak::Label, MAX_CODE_SIZE> delay_skip;
88 Xbyak::Label end_of_code{};
89
90 bool is_delay_slot{};
91 u32 pc{};
92 std::optional<u32> delayed_pc;
93
94 const std::vector<u32>& code;
95 Engines::Maxwell3D& maxwell3d;
96};
97
98} // namespace Tegra
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index dbee9f634..ff5505d12 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -210,10 +210,11 @@ bool MemoryManager::IsBlockContinuous(const GPUVAddr start, const std::size_t si
210 return range == inner_size; 210 return range == inner_size;
211} 211}
212 212
213void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const { 213void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer,
214 const std::size_t size) const {
214 std::size_t remaining_size{size}; 215 std::size_t remaining_size{size};
215 std::size_t page_index{src_addr >> page_bits}; 216 std::size_t page_index{gpu_src_addr >> page_bits};
216 std::size_t page_offset{src_addr & page_mask}; 217 std::size_t page_offset{gpu_src_addr & page_mask};
217 218
218 auto& memory = system.Memory(); 219 auto& memory = system.Memory();
219 220
@@ -234,11 +235,11 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s
234 } 235 }
235} 236}
236 237
237void MemoryManager::ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, 238void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
238 const std::size_t size) const { 239 const std::size_t size) const {
239 std::size_t remaining_size{size}; 240 std::size_t remaining_size{size};
240 std::size_t page_index{src_addr >> page_bits}; 241 std::size_t page_index{gpu_src_addr >> page_bits};
241 std::size_t page_offset{src_addr & page_mask}; 242 std::size_t page_offset{gpu_src_addr & page_mask};
242 243
243 auto& memory = system.Memory(); 244 auto& memory = system.Memory();
244 245
@@ -259,10 +260,11 @@ void MemoryManager::ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer,
259 } 260 }
260} 261}
261 262
262void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size) { 263void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer,
264 const std::size_t size) {
263 std::size_t remaining_size{size}; 265 std::size_t remaining_size{size};
264 std::size_t page_index{dest_addr >> page_bits}; 266 std::size_t page_index{gpu_dest_addr >> page_bits};
265 std::size_t page_offset{dest_addr & page_mask}; 267 std::size_t page_offset{gpu_dest_addr & page_mask};
266 268
267 auto& memory = system.Memory(); 269 auto& memory = system.Memory();
268 270
@@ -283,11 +285,11 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const
283 } 285 }
284} 286}
285 287
286void MemoryManager::WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, 288void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
287 const std::size_t size) { 289 const std::size_t size) {
288 std::size_t remaining_size{size}; 290 std::size_t remaining_size{size};
289 std::size_t page_index{dest_addr >> page_bits}; 291 std::size_t page_index{gpu_dest_addr >> page_bits};
290 std::size_t page_offset{dest_addr & page_mask}; 292 std::size_t page_offset{gpu_dest_addr & page_mask};
291 293
292 auto& memory = system.Memory(); 294 auto& memory = system.Memory();
293 295
@@ -306,16 +308,18 @@ void MemoryManager::WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer,
306 } 308 }
307} 309}
308 310
309void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { 311void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr,
312 const std::size_t size) {
310 std::vector<u8> tmp_buffer(size); 313 std::vector<u8> tmp_buffer(size);
311 ReadBlock(src_addr, tmp_buffer.data(), size); 314 ReadBlock(gpu_src_addr, tmp_buffer.data(), size);
312 WriteBlock(dest_addr, tmp_buffer.data(), size); 315 WriteBlock(gpu_dest_addr, tmp_buffer.data(), size);
313} 316}
314 317
315void MemoryManager::CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { 318void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr,
319 const std::size_t size) {
316 std::vector<u8> tmp_buffer(size); 320 std::vector<u8> tmp_buffer(size);
317 ReadBlockUnsafe(src_addr, tmp_buffer.data(), size); 321 ReadBlockUnsafe(gpu_src_addr, tmp_buffer.data(), size);
318 WriteBlockUnsafe(dest_addr, tmp_buffer.data(), size); 322 WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size);
319} 323}
320 324
321bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) { 325bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) {
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 0ddd52d5a..87658e87a 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -79,9 +79,9 @@ public:
79 * in the Host Memory counterpart. Note: This functions cause Host GPU Memory 79 * in the Host Memory counterpart. Note: This functions cause Host GPU Memory
80 * Flushes and Invalidations, respectively to each operation. 80 * Flushes and Invalidations, respectively to each operation.
81 */ 81 */
82 void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; 82 void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const;
83 void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); 83 void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size);
84 void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); 84 void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size);
85 85
86 /** 86 /**
87 * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and 87 * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and
@@ -93,9 +93,9 @@ public:
93 * WriteBlockUnsafe instead of WriteBlock since it shouldn't invalidate the texture 93 * WriteBlockUnsafe instead of WriteBlock since it shouldn't invalidate the texture
94 * being flushed. 94 * being flushed.
95 */ 95 */
96 void ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; 96 void ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const;
97 void WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); 97 void WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size);
98 void CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); 98 void CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size);
99 99
100 /** 100 /**
101 * IsGranularRange checks if a gpu region can be simply read with a pointer 101 * IsGranularRange checks if a gpu region can be simply read with a pointer
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index 2f75f8801..0d3a88765 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -132,7 +132,7 @@ public:
132 } 132 }
133 133
134 query->BindCounter(Stream(type).Current(), timestamp); 134 query->BindCounter(Stream(type).Current(), timestamp);
135 if (Settings::values.use_asynchronous_gpu_emulation) { 135 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
136 AsyncFlushQuery(cpu_addr); 136 AsyncFlushQuery(cpu_addr);
137 } 137 }
138 } 138 }
@@ -220,8 +220,8 @@ private:
220 return cache_begin < addr_end && addr_begin < cache_end; 220 return cache_begin < addr_end && addr_begin < cache_end;
221 }; 221 };
222 222
223 const u64 page_end = addr_end >> PAGE_SHIFT; 223 const u64 page_end = addr_end >> PAGE_BITS;
224 for (u64 page = addr_begin >> PAGE_SHIFT; page <= page_end; ++page) { 224 for (u64 page = addr_begin >> PAGE_BITS; page <= page_end; ++page) {
225 const auto& it = cached_queries.find(page); 225 const auto& it = cached_queries.find(page);
226 if (it == std::end(cached_queries)) { 226 if (it == std::end(cached_queries)) {
227 continue; 227 continue;
@@ -242,14 +242,14 @@ private:
242 /// Registers the passed parameters as cached and returns a pointer to the stored cached query. 242 /// Registers the passed parameters as cached and returns a pointer to the stored cached query.
243 CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) { 243 CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) {
244 rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1); 244 rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1);
245 const u64 page = static_cast<u64>(cpu_addr) >> PAGE_SHIFT; 245 const u64 page = static_cast<u64>(cpu_addr) >> PAGE_BITS;
246 return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr, 246 return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr,
247 host_ptr); 247 host_ptr);
248 } 248 }
249 249
250 /// Tries to a get a cached query. Returns nullptr on failure. 250 /// Tries to a get a cached query. Returns nullptr on failure.
251 CachedQuery* TryGet(VAddr addr) { 251 CachedQuery* TryGet(VAddr addr) {
252 const u64 page = static_cast<u64>(addr) >> PAGE_SHIFT; 252 const u64 page = static_cast<u64>(addr) >> PAGE_BITS;
253 const auto it = cached_queries.find(page); 253 const auto it = cached_queries.find(page);
254 if (it == std::end(cached_queries)) { 254 if (it == std::end(cached_queries)) {
255 return nullptr; 255 return nullptr;
@@ -268,7 +268,7 @@ private:
268 } 268 }
269 269
270 static constexpr std::uintptr_t PAGE_SIZE = 4096; 270 static constexpr std::uintptr_t PAGE_SIZE = 4096;
271 static constexpr unsigned PAGE_SHIFT = 12; 271 static constexpr unsigned PAGE_BITS = 12;
272 272
273 Core::System& system; 273 Core::System& system;
274 VideoCore::RasterizerInterface& rasterizer; 274 VideoCore::RasterizerInterface& rasterizer;
diff --git a/src/video_core/rasterizer_cache.cpp b/src/video_core/rasterizer_cache.cpp
deleted file mode 100644
index 093b2cdf4..000000000
--- a/src/video_core/rasterizer_cache.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "video_core/rasterizer_cache.h"
6
7RasterizerCacheObject::~RasterizerCacheObject() = default;
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
deleted file mode 100644
index 096ee337c..000000000
--- a/src/video_core/rasterizer_cache.h
+++ /dev/null
@@ -1,253 +0,0 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <mutex>
8#include <set>
9#include <unordered_map>
10
11#include <boost/icl/interval_map.hpp>
12#include <boost/range/iterator_range_core.hpp>
13
14#include "common/common_types.h"
15#include "core/settings.h"
16#include "video_core/gpu.h"
17#include "video_core/rasterizer_interface.h"
18
19class RasterizerCacheObject {
20public:
21 explicit RasterizerCacheObject(const VAddr cpu_addr) : cpu_addr{cpu_addr} {}
22
23 virtual ~RasterizerCacheObject();
24
25 VAddr GetCpuAddr() const {
26 return cpu_addr;
27 }
28
29 /// Gets the size of the shader in guest memory, required for cache management
30 virtual std::size_t GetSizeInBytes() const = 0;
31
32 /// Sets whether the cached object should be considered registered
33 void SetIsRegistered(bool registered) {
34 is_registered = registered;
35 }
36
37 /// Returns true if the cached object is registered
38 bool IsRegistered() const {
39 return is_registered;
40 }
41
42 /// Returns true if the cached object is dirty
43 bool IsDirty() const {
44 return is_dirty;
45 }
46
47 /// Returns ticks from when this cached object was last modified
48 u64 GetLastModifiedTicks() const {
49 return last_modified_ticks;
50 }
51
52 /// Marks an object as recently modified, used to specify whether it is clean or dirty
53 template <class T>
54 void MarkAsModified(bool dirty, T& cache) {
55 is_dirty = dirty;
56 last_modified_ticks = cache.GetModifiedTicks();
57 }
58
59 void SetMemoryMarked(bool is_memory_marked_) {
60 is_memory_marked = is_memory_marked_;
61 }
62
63 bool IsMemoryMarked() const {
64 return is_memory_marked;
65 }
66
67 void SetSyncPending(bool is_sync_pending_) {
68 is_sync_pending = is_sync_pending_;
69 }
70
71 bool IsSyncPending() const {
72 return is_sync_pending;
73 }
74
75private:
76 bool is_registered{}; ///< Whether the object is currently registered with the cache
77 bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory)
78 bool is_memory_marked{}; ///< Whether the object is marking rasterizer memory.
79 bool is_sync_pending{}; ///< Whether the object is pending deletion.
80 u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing
81 VAddr cpu_addr{}; ///< Cpu address memory, unique from emulated virtual address space
82};
83
84template <class T>
85class RasterizerCache : NonCopyable {
86 friend class RasterizerCacheObject;
87
88public:
89 explicit RasterizerCache(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {}
90
91 /// Write any cached resources overlapping the specified region back to memory
92 void FlushRegion(VAddr addr, std::size_t size) {
93 std::lock_guard lock{mutex};
94
95 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
96 for (auto& object : objects) {
97 FlushObject(object);
98 }
99 }
100
101 /// Mark the specified region as being invalidated
102 void InvalidateRegion(VAddr addr, u64 size) {
103 std::lock_guard lock{mutex};
104
105 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
106 for (auto& object : objects) {
107 if (!object->IsRegistered()) {
108 // Skip duplicates
109 continue;
110 }
111 Unregister(object);
112 }
113 }
114
115 void OnCPUWrite(VAddr addr, std::size_t size) {
116 std::lock_guard lock{mutex};
117
118 for (const auto& object : GetSortedObjectsFromRegion(addr, size)) {
119 if (object->IsRegistered()) {
120 UnmarkMemory(object);
121 object->SetSyncPending(true);
122 marked_for_unregister.emplace_back(object);
123 }
124 }
125 }
126
127 void SyncGuestHost() {
128 std::lock_guard lock{mutex};
129
130 for (const auto& object : marked_for_unregister) {
131 if (object->IsRegistered()) {
132 object->SetSyncPending(false);
133 Unregister(object);
134 }
135 }
136 marked_for_unregister.clear();
137 }
138
139 /// Invalidates everything in the cache
140 void InvalidateAll() {
141 std::lock_guard lock{mutex};
142
143 while (interval_cache.begin() != interval_cache.end()) {
144 Unregister(*interval_cache.begin()->second.begin());
145 }
146 }
147
148protected:
149 /// Tries to get an object from the cache with the specified cache address
150 T TryGet(VAddr addr) const {
151 const auto iter = map_cache.find(addr);
152 if (iter != map_cache.end())
153 return iter->second;
154 return nullptr;
155 }
156
157 /// Register an object into the cache
158 virtual void Register(const T& object) {
159 std::lock_guard lock{mutex};
160
161 object->SetIsRegistered(true);
162 interval_cache.add({GetInterval(object), ObjectSet{object}});
163 map_cache.insert({object->GetCpuAddr(), object});
164 rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), 1);
165 object->SetMemoryMarked(true);
166 }
167
168 /// Unregisters an object from the cache
169 virtual void Unregister(const T& object) {
170 std::lock_guard lock{mutex};
171
172 UnmarkMemory(object);
173 object->SetIsRegistered(false);
174 if (object->IsSyncPending()) {
175 marked_for_unregister.remove(object);
176 object->SetSyncPending(false);
177 }
178 const VAddr addr = object->GetCpuAddr();
179 interval_cache.subtract({GetInterval(object), ObjectSet{object}});
180 map_cache.erase(addr);
181 }
182
183 void UnmarkMemory(const T& object) {
184 if (!object->IsMemoryMarked()) {
185 return;
186 }
187 rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1);
188 object->SetMemoryMarked(false);
189 }
190
191 /// Returns a ticks counter used for tracking when cached objects were last modified
192 u64 GetModifiedTicks() {
193 std::lock_guard lock{mutex};
194
195 return ++modified_ticks;
196 }
197
198 virtual void FlushObjectInner(const T& object) = 0;
199
200 /// Flushes the specified object, updating appropriate cache state as needed
201 void FlushObject(const T& object) {
202 std::lock_guard lock{mutex};
203
204 if (!object->IsDirty()) {
205 return;
206 }
207 FlushObjectInner(object);
208 object->MarkAsModified(false, *this);
209 }
210
211 std::recursive_mutex mutex;
212
213private:
214 /// Returns a list of cached objects from the specified memory region, ordered by access time
215 std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) {
216 if (size == 0) {
217 return {};
218 }
219
220 std::vector<T> objects;
221 const ObjectInterval interval{addr, addr + size};
222 for (auto& pair : boost::make_iterator_range(interval_cache.equal_range(interval))) {
223 for (auto& cached_object : pair.second) {
224 if (!cached_object) {
225 continue;
226 }
227 objects.push_back(cached_object);
228 }
229 }
230
231 std::sort(objects.begin(), objects.end(), [](const T& a, const T& b) -> bool {
232 return a->GetLastModifiedTicks() < b->GetLastModifiedTicks();
233 });
234
235 return objects;
236 }
237
238 using ObjectSet = std::set<T>;
239 using ObjectCache = std::unordered_map<VAddr, T>;
240 using IntervalCache = boost::icl::interval_map<VAddr, ObjectSet>;
241 using ObjectInterval = typename IntervalCache::interval_type;
242
243 static auto GetInterval(const T& object) {
244 return ObjectInterval::right_open(object->GetCpuAddr(),
245 object->GetCpuAddr() + object->GetSizeInBytes());
246 }
247
248 ObjectCache map_cache;
249 IntervalCache interval_cache; ///< Cache of objects
250 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
251 VideoCore::RasterizerInterface& rasterizer;
252 std::list<T> marked_for_unregister;
253};
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 919d1f2d4..dfb06e87e 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -18,7 +18,7 @@ RendererBase::~RendererBase() = default;
18void RendererBase::RefreshBaseSettings() { 18void RendererBase::RefreshBaseSettings() {
19 UpdateCurrentFramebufferLayout(); 19 UpdateCurrentFramebufferLayout();
20 20
21 renderer_settings.use_framelimiter = Settings::values.use_frame_limit; 21 renderer_settings.use_framelimiter = Settings::values.use_frame_limit.GetValue();
22 renderer_settings.set_background_color = true; 22 renderer_settings.set_background_color = true;
23} 23}
24 24
diff --git a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
new file mode 100644
index 000000000..eb5158407
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
@@ -0,0 +1,2073 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <array>
7#include <cstddef>
8#include <string>
9#include <string_view>
10#include <utility>
11#include <variant>
12
13#include <fmt/format.h>
14
15#include "common/alignment.h"
16#include "common/assert.h"
17#include "common/common_types.h"
18#include "video_core/renderer_opengl/gl_arb_decompiler.h"
19#include "video_core/renderer_opengl/gl_device.h"
20#include "video_core/shader/registry.h"
21#include "video_core/shader/shader_ir.h"
22
23// Predicates in the decompiled code follow the convention that -1 means true and 0 means false.
24// GLASM lacks booleans, so they have to be implemented as integers.
25// Using -1 for true is useful because both CMP.S and NOT.U can negate it, and CMP.S can be used to
26// select between two values, because -1 will be evaluated as true and 0 as false.
27
28namespace OpenGL {
29
30namespace {
31
32using Tegra::Engines::ShaderType;
33using Tegra::Shader::Attribute;
34using Tegra::Shader::PixelImap;
35using Tegra::Shader::Register;
36using namespace VideoCommon::Shader;
37using Operation = const OperationNode&;
38
39constexpr std::array INTERNAL_FLAG_NAMES = {"ZERO", "SIGN", "CARRY", "OVERFLOW"};
40
41char Swizzle(std::size_t component) {
42 ASSERT(component < 4);
43 return component["xyzw"];
44}
45
46constexpr bool IsGenericAttribute(Attribute::Index index) {
47 return index >= Attribute::Index::Attribute_0 && index <= Attribute::Index::Attribute_31;
48}
49
50u32 GetGenericAttributeIndex(Attribute::Index index) {
51 ASSERT(IsGenericAttribute(index));
52 return static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
53}
54
55std::string_view Modifiers(Operation operation) {
56 const auto meta = std::get_if<MetaArithmetic>(&operation.GetMeta());
57 if (meta && meta->precise) {
58 return ".PREC";
59 }
60 return "";
61}
62
63std::string_view GetInputFlags(PixelImap attribute) {
64 switch (attribute) {
65 case PixelImap::Perspective:
66 return "";
67 case PixelImap::Constant:
68 return "FLAT ";
69 case PixelImap::ScreenLinear:
70 return "NOPERSPECTIVE ";
71 case PixelImap::Unused:
72 break;
73 }
74 UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<int>(attribute));
75 return {};
76}
77
78std::string_view ImageType(Tegra::Shader::ImageType image_type) {
79 switch (image_type) {
80 case Tegra::Shader::ImageType::Texture1D:
81 return "1D";
82 case Tegra::Shader::ImageType::TextureBuffer:
83 return "BUFFER";
84 case Tegra::Shader::ImageType::Texture1DArray:
85 return "ARRAY1D";
86 case Tegra::Shader::ImageType::Texture2D:
87 return "2D";
88 case Tegra::Shader::ImageType::Texture2DArray:
89 return "ARRAY2D";
90 case Tegra::Shader::ImageType::Texture3D:
91 return "3D";
92 }
93 UNREACHABLE();
94 return {};
95}
96
97std::string_view StackName(MetaStackClass stack) {
98 switch (stack) {
99 case MetaStackClass::Ssy:
100 return "SSY";
101 case MetaStackClass::Pbk:
102 return "PBK";
103 }
104 UNREACHABLE();
105 return "";
106};
107
108std::string_view PrimitiveDescription(Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology topology) {
109 switch (topology) {
110 case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::Points:
111 return "POINTS";
112 case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::Lines:
113 case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::LineStrip:
114 return "LINES";
115 case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::LinesAdjacency:
116 case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::LineStripAdjacency:
117 return "LINES_ADJACENCY";
118 case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::Triangles:
119 case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::TriangleStrip:
120 case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::TriangleFan:
121 return "TRIANGLES";
122 case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency:
123 case Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency:
124 return "TRIANGLES_ADJACENCY";
125 default:
126 UNIMPLEMENTED_MSG("topology={}", static_cast<int>(topology));
127 return "POINTS";
128 }
129}
130
131std::string_view TopologyName(Tegra::Shader::OutputTopology topology) {
132 switch (topology) {
133 case Tegra::Shader::OutputTopology::PointList:
134 return "POINTS";
135 case Tegra::Shader::OutputTopology::LineStrip:
136 return "LINE_STRIP";
137 case Tegra::Shader::OutputTopology::TriangleStrip:
138 return "TRIANGLE_STRIP";
139 default:
140 UNIMPLEMENTED_MSG("Unknown output topology: {}", static_cast<u32>(topology));
141 return "points";
142 }
143}
144
145std::string_view StageInputName(ShaderType stage) {
146 switch (stage) {
147 case ShaderType::Vertex:
148 case ShaderType::Geometry:
149 return "vertex";
150 case ShaderType::Fragment:
151 return "fragment";
152 case ShaderType::Compute:
153 return "invocation";
154 default:
155 UNREACHABLE();
156 return "";
157 }
158}
159
160std::string TextureType(const MetaTexture& meta) {
161 if (meta.sampler.is_buffer) {
162 return "BUFFER";
163 }
164 std::string type;
165 if (meta.sampler.is_shadow) {
166 type += "SHADOW";
167 }
168 if (meta.sampler.is_array) {
169 type += "ARRAY";
170 }
171 type += [&meta] {
172 switch (meta.sampler.type) {
173 case Tegra::Shader::TextureType::Texture1D:
174 return "1D";
175 case Tegra::Shader::TextureType::Texture2D:
176 return "2D";
177 case Tegra::Shader::TextureType::Texture3D:
178 return "3D";
179 case Tegra::Shader::TextureType::TextureCube:
180 return "CUBE";
181 }
182 UNREACHABLE();
183 return "2D";
184 }();
185 return type;
186}
187
188std::string GlobalMemoryName(const GlobalMemoryBase& base) {
189 return fmt::format("gmem{}_{}", base.cbuf_index, base.cbuf_offset);
190}
191
192class ARBDecompiler final {
193public:
194 explicit ARBDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry,
195 ShaderType stage, std::string_view identifier);
196
197 std::string Code() const {
198 return shader_source;
199 }
200
201private:
202 void DeclareHeader();
203 void DeclareVertex();
204 void DeclareGeometry();
205 void DeclareFragment();
206 void DeclareCompute();
207 void DeclareInputAttributes();
208 void DeclareOutputAttributes();
209 void DeclareLocalMemory();
210 void DeclareGlobalMemory();
211 void DeclareConstantBuffers();
212 void DeclareRegisters();
213 void DeclareTemporaries();
214 void DeclarePredicates();
215 void DeclareInternalFlags();
216
217 void InitializeVariables();
218
219 void DecompileAST();
220 void DecompileBranchMode();
221
222 void VisitAST(const ASTNode& node);
223 std::string VisitExpression(const Expr& node);
224
225 void VisitBlock(const NodeBlock& bb);
226
227 std::string Visit(const Node& node);
228
229 std::pair<std::string, std::size_t> BuildCoords(Operation);
230 std::string BuildAoffi(Operation);
231 void Exit();
232
233 std::string Assign(Operation);
234 std::string Select(Operation);
235 std::string FClamp(Operation);
236 std::string FCastHalf0(Operation);
237 std::string FCastHalf1(Operation);
238 std::string FSqrt(Operation);
239 std::string FSwizzleAdd(Operation);
240 std::string HAdd2(Operation);
241 std::string HMul2(Operation);
242 std::string HFma2(Operation);
243 std::string HAbsolute(Operation);
244 std::string HNegate(Operation);
245 std::string HClamp(Operation);
246 std::string HCastFloat(Operation);
247 std::string HUnpack(Operation);
248 std::string HMergeF32(Operation);
249 std::string HMergeH0(Operation);
250 std::string HMergeH1(Operation);
251 std::string HPack2(Operation);
252 std::string LogicalAssign(Operation);
253 std::string LogicalPick2(Operation);
254 std::string LogicalAnd2(Operation);
255 std::string FloatOrdered(Operation);
256 std::string FloatUnordered(Operation);
257 std::string LogicalAddCarry(Operation);
258 std::string Texture(Operation);
259 std::string TextureGather(Operation);
260 std::string TextureQueryDimensions(Operation);
261 std::string TextureQueryLod(Operation);
262 std::string TexelFetch(Operation);
263 std::string TextureGradient(Operation);
264 std::string ImageLoad(Operation);
265 std::string ImageStore(Operation);
266 std::string Branch(Operation);
267 std::string BranchIndirect(Operation);
268 std::string PushFlowStack(Operation);
269 std::string PopFlowStack(Operation);
270 std::string Exit(Operation);
271 std::string Discard(Operation);
272 std::string EmitVertex(Operation);
273 std::string EndPrimitive(Operation);
274 std::string InvocationId(Operation);
275 std::string YNegate(Operation);
276 std::string ThreadId(Operation);
277 std::string ShuffleIndexed(Operation);
278 std::string Barrier(Operation);
279 std::string MemoryBarrierGroup(Operation);
280 std::string MemoryBarrierGlobal(Operation);
281
282 template <const std::string_view& op>
283 std::string Unary(Operation operation) {
284 std::string temporary = AllocTemporary();
285 AddLine("{}{} {}, {};", op, Modifiers(operation), temporary, Visit(operation[0]));
286 return temporary;
287 }
288
289 template <const std::string_view& op>
290 std::string Binary(Operation operation) {
291 std::string temporary = AllocTemporary();
292 AddLine("{}{} {}, {}, {};", op, Modifiers(operation), temporary, Visit(operation[0]),
293 Visit(operation[1]));
294 return temporary;
295 }
296
297 template <const std::string_view& op>
298 std::string Trinary(Operation operation) {
299 std::string temporary = AllocTemporary();
300 AddLine("{}{} {}, {}, {}, {};", op, Modifiers(operation), temporary, Visit(operation[0]),
301 Visit(operation[1]), Visit(operation[2]));
302 return temporary;
303 }
304
305 template <const std::string_view& op, bool unordered>
306 std::string FloatComparison(Operation operation) {
307 std::string temporary = AllocTemporary();
308 AddLine("TRUNC.U.CC RC.x, {};", Binary<op>(operation));
309 AddLine("MOV.S {}, 0;", temporary);
310 AddLine("MOV.S {} (NE.x), -1;", temporary);
311
312 const std::string op_a = Visit(operation[0]);
313 const std::string op_b = Visit(operation[1]);
314 if constexpr (unordered) {
315 AddLine("SNE.F RC.x, {}, {};", op_a, op_a);
316 AddLine("TRUNC.U.CC RC.x, RC.x;");
317 AddLine("MOV.S {} (NE.x), -1;", temporary);
318 AddLine("SNE.F RC.x, {}, {};", op_b, op_b);
319 AddLine("TRUNC.U.CC RC.x, RC.x;");
320 AddLine("MOV.S {} (NE.x), -1;", temporary);
321 } else if (op == SNE_F) {
322 AddLine("SNE.F RC.x, {}, {};", op_a, op_a);
323 AddLine("TRUNC.U.CC RC.x, RC.x;");
324 AddLine("MOV.S {} (NE.x), 0;", temporary);
325 AddLine("SNE.F RC.x, {}, {};", op_b, op_b);
326 AddLine("TRUNC.U.CC RC.x, RC.x;");
327 AddLine("MOV.S {} (NE.x), 0;", temporary);
328 }
329 return temporary;
330 }
331
332 template <const std::string_view& op, bool is_nan>
333 std::string HalfComparison(Operation operation) {
334 std::string tmp1 = AllocVectorTemporary();
335 const std::string tmp2 = AllocVectorTemporary();
336 const std::string op_a = Visit(operation[0]);
337 const std::string op_b = Visit(operation[1]);
338 AddLine("UP2H.F {}, {};", tmp1, op_a);
339 AddLine("UP2H.F {}, {};", tmp2, op_b);
340 AddLine("{} {}, {}, {};", op, tmp1, tmp1, tmp2);
341 AddLine("TRUNC.U.CC RC.xy, {};", tmp1);
342 AddLine("MOV.S {}.xy, {{0, 0, 0, 0}};", tmp1);
343 AddLine("MOV.S {}.x (NE.x), -1;", tmp1);
344 AddLine("MOV.S {}.y (NE.y), -1;", tmp1);
345 if constexpr (is_nan) {
346 AddLine("MOVC.F RC.x, {};", op_a);
347 AddLine("MOV.S {}.x (NAN.x), -1;", tmp1);
348 AddLine("MOVC.F RC.x, {};", op_b);
349 AddLine("MOV.S {}.y (NAN.x), -1;", tmp1);
350 }
351 return tmp1;
352 }
353
354 template <const std::string_view& op, const std::string_view& type>
355 std::string AtomicImage(Operation operation) {
356 const auto& meta = std::get<MetaImage>(operation.GetMeta());
357 const u32 image_id = device.GetBaseBindings(stage).image + meta.image.index;
358 const std::size_t num_coords = operation.GetOperandsCount();
359 const std::size_t num_values = meta.values.size();
360
361 const std::string coord = AllocVectorTemporary();
362 const std::string value = AllocVectorTemporary();
363 for (std::size_t i = 0; i < num_coords; ++i) {
364 AddLine("MOV.S {}.{}, {};", coord, Swizzle(i), Visit(operation[i]));
365 }
366 for (std::size_t i = 0; i < num_values; ++i) {
367 AddLine("MOV.F {}.{}, {};", value, Swizzle(i), Visit(meta.values[i]));
368 }
369
370 AddLine("ATOMIM.{}.{} {}.x, {}, {}, image[{}], {};", op, type, coord, value, coord,
371 image_id, ImageType(meta.image.type));
372 return fmt::format("{}.x", coord);
373 }
374
375 template <const std::string_view& op, const std::string_view& type>
376 std::string Atomic(Operation operation) {
377 std::string temporary = AllocTemporary();
378 std::string address;
379 std::string_view opname;
380 if (const auto gmem = std::get_if<GmemNode>(&*operation[0])) {
381 AddLine("SUB.U {}, {}, {};", temporary, Visit(gmem->GetRealAddress()),
382 Visit(gmem->GetBaseAddress()));
383 address = fmt::format("{}[{}]", GlobalMemoryName(gmem->GetDescriptor()), temporary);
384 opname = "ATOMB";
385 } else if (const auto smem = std::get_if<SmemNode>(&*operation[0])) {
386 address = fmt::format("shared_mem[{}]", Visit(smem->GetAddress()));
387 opname = "ATOMS";
388 } else {
389 UNREACHABLE();
390 return "{0, 0, 0, 0}";
391 }
392 AddLine("{}.{}.{} {}, {}, {};", opname, op, type, temporary, Visit(operation[1]), address);
393 return temporary;
394 }
395
396 template <char type>
397 std::string Negate(Operation operation) {
398 std::string temporary = AllocTemporary();
399 if constexpr (type == 'F') {
400 AddLine("MOV.F32 {}, -{};", temporary, Visit(operation[0]));
401 } else {
402 AddLine("MOV.{} {}, -{};", type, temporary, Visit(operation[0]));
403 }
404 return temporary;
405 }
406
407 template <char type>
408 std::string Absolute(Operation operation) {
409 std::string temporary = AllocTemporary();
410 AddLine("MOV.{} {}, |{}|;", type, temporary, Visit(operation[0]));
411 return temporary;
412 }
413
414 template <char type>
415 std::string BitfieldInsert(Operation operation) {
416 const std::string temporary = AllocVectorTemporary();
417 AddLine("MOV.{} {}.x, {};", type, temporary, Visit(operation[3]));
418 AddLine("MOV.{} {}.y, {};", type, temporary, Visit(operation[2]));
419 AddLine("BFI.{} {}.x, {}, {}, {};", type, temporary, temporary, Visit(operation[1]),
420 Visit(operation[0]));
421 return fmt::format("{}.x", temporary);
422 }
423
424 template <char type>
425 std::string BitfieldExtract(Operation operation) {
426 const std::string temporary = AllocVectorTemporary();
427 AddLine("MOV.{} {}.x, {};", type, temporary, Visit(operation[2]));
428 AddLine("MOV.{} {}.y, {};", type, temporary, Visit(operation[1]));
429 AddLine("BFE.{} {}.x, {}, {};", type, temporary, temporary, Visit(operation[0]));
430 return fmt::format("{}.x", temporary);
431 }
432
433 template <char swizzle>
434 std::string LocalInvocationId(Operation) {
435 return fmt::format("invocation.localid.{}", swizzle);
436 }
437
438 template <char swizzle>
439 std::string WorkGroupId(Operation) {
440 return fmt::format("invocation.groupid.{}", swizzle);
441 }
442
443 template <char c1, char c2>
444 std::string ThreadMask(Operation) {
445 return fmt::format("{}.thread{}{}mask", StageInputName(stage), c1, c2);
446 }
447
448 template <typename... Args>
449 void AddExpression(std::string_view text, Args&&... args) {
450 shader_source += fmt::format(text, std::forward<Args>(args)...);
451 }
452
453 template <typename... Args>
454 void AddLine(std::string_view text, Args&&... args) {
455 AddExpression(text, std::forward<Args>(args)...);
456 shader_source += '\n';
457 }
458
459 std::string AllocTemporary() {
460 max_temporaries = std::max(max_temporaries, num_temporaries + 1);
461 return fmt::format("T{}.x", num_temporaries++);
462 }
463
464 std::string AllocVectorTemporary() {
465 max_temporaries = std::max(max_temporaries, num_temporaries + 1);
466 return fmt::format("T{}", num_temporaries++);
467 }
468
469 void ResetTemporaries() noexcept {
470 num_temporaries = 0;
471 }
472
473 const Device& device;
474 const ShaderIR& ir;
475 const Registry& registry;
476 const ShaderType stage;
477
478 std::size_t num_temporaries = 0;
479 std::size_t max_temporaries = 0;
480
481 std::string shader_source;
482
483 static constexpr std::string_view ADD_F32 = "ADD.F32";
484 static constexpr std::string_view ADD_S = "ADD.S";
485 static constexpr std::string_view ADD_U = "ADD.U";
486 static constexpr std::string_view MUL_F32 = "MUL.F32";
487 static constexpr std::string_view MUL_S = "MUL.S";
488 static constexpr std::string_view MUL_U = "MUL.U";
489 static constexpr std::string_view DIV_F32 = "DIV.F32";
490 static constexpr std::string_view DIV_S = "DIV.S";
491 static constexpr std::string_view DIV_U = "DIV.U";
492 static constexpr std::string_view MAD_F32 = "MAD.F32";
493 static constexpr std::string_view RSQ_F32 = "RSQ.F32";
494 static constexpr std::string_view COS_F32 = "COS.F32";
495 static constexpr std::string_view SIN_F32 = "SIN.F32";
496 static constexpr std::string_view EX2_F32 = "EX2.F32";
497 static constexpr std::string_view LG2_F32 = "LG2.F32";
498 static constexpr std::string_view SLT_F = "SLT.F32";
499 static constexpr std::string_view SLT_S = "SLT.S";
500 static constexpr std::string_view SLT_U = "SLT.U";
501 static constexpr std::string_view SEQ_F = "SEQ.F32";
502 static constexpr std::string_view SEQ_S = "SEQ.S";
503 static constexpr std::string_view SEQ_U = "SEQ.U";
504 static constexpr std::string_view SLE_F = "SLE.F32";
505 static constexpr std::string_view SLE_S = "SLE.S";
506 static constexpr std::string_view SLE_U = "SLE.U";
507 static constexpr std::string_view SGT_F = "SGT.F32";
508 static constexpr std::string_view SGT_S = "SGT.S";
509 static constexpr std::string_view SGT_U = "SGT.U";
510 static constexpr std::string_view SNE_F = "SNE.F32";
511 static constexpr std::string_view SNE_S = "SNE.S";
512 static constexpr std::string_view SNE_U = "SNE.U";
513 static constexpr std::string_view SGE_F = "SGE.F32";
514 static constexpr std::string_view SGE_S = "SGE.S";
515 static constexpr std::string_view SGE_U = "SGE.U";
516 static constexpr std::string_view AND_S = "AND.S";
517 static constexpr std::string_view AND_U = "AND.U";
518 static constexpr std::string_view TRUNC_F = "TRUNC.F";
519 static constexpr std::string_view TRUNC_S = "TRUNC.S";
520 static constexpr std::string_view TRUNC_U = "TRUNC.U";
521 static constexpr std::string_view SHL_S = "SHL.S";
522 static constexpr std::string_view SHL_U = "SHL.U";
523 static constexpr std::string_view SHR_S = "SHR.S";
524 static constexpr std::string_view SHR_U = "SHR.U";
525 static constexpr std::string_view OR_S = "OR.S";
526 static constexpr std::string_view OR_U = "OR.U";
527 static constexpr std::string_view XOR_S = "XOR.S";
528 static constexpr std::string_view XOR_U = "XOR.U";
529 static constexpr std::string_view NOT_S = "NOT.S";
530 static constexpr std::string_view NOT_U = "NOT.U";
531 static constexpr std::string_view BTC_S = "BTC.S";
532 static constexpr std::string_view BTC_U = "BTC.U";
533 static constexpr std::string_view BTFM_S = "BTFM.S";
534 static constexpr std::string_view BTFM_U = "BTFM.U";
535 static constexpr std::string_view ROUND_F = "ROUND.F";
536 static constexpr std::string_view CEIL_F = "CEIL.F";
537 static constexpr std::string_view FLR_F = "FLR.F";
538 static constexpr std::string_view I2F_S = "I2F.S";
539 static constexpr std::string_view I2F_U = "I2F.U";
540 static constexpr std::string_view MIN_F = "MIN.F";
541 static constexpr std::string_view MIN_S = "MIN.S";
542 static constexpr std::string_view MIN_U = "MIN.U";
543 static constexpr std::string_view MAX_F = "MAX.F";
544 static constexpr std::string_view MAX_S = "MAX.S";
545 static constexpr std::string_view MAX_U = "MAX.U";
546 static constexpr std::string_view MOV_U = "MOV.U";
547 static constexpr std::string_view TGBALLOT_U = "TGBALLOT.U";
548 static constexpr std::string_view TGALL_U = "TGALL.U";
549 static constexpr std::string_view TGANY_U = "TGANY.U";
550 static constexpr std::string_view TGEQ_U = "TGEQ.U";
551 static constexpr std::string_view EXCH = "EXCH";
552 static constexpr std::string_view ADD = "ADD";
553 static constexpr std::string_view MIN = "MIN";
554 static constexpr std::string_view MAX = "MAX";
555 static constexpr std::string_view AND = "AND";
556 static constexpr std::string_view OR = "OR";
557 static constexpr std::string_view XOR = "XOR";
558 static constexpr std::string_view U32 = "U32";
559 static constexpr std::string_view S32 = "S32";
560
561 static constexpr std::size_t NUM_ENTRIES = static_cast<std::size_t>(OperationCode::Amount);
562 using DecompilerType = std::string (ARBDecompiler::*)(Operation);
563 static constexpr std::array<DecompilerType, NUM_ENTRIES> OPERATION_DECOMPILERS = {
564 &ARBDecompiler::Assign,
565
566 &ARBDecompiler::Select,
567
568 &ARBDecompiler::Binary<ADD_F32>,
569 &ARBDecompiler::Binary<MUL_F32>,
570 &ARBDecompiler::Binary<DIV_F32>,
571 &ARBDecompiler::Trinary<MAD_F32>,
572 &ARBDecompiler::Negate<'F'>,
573 &ARBDecompiler::Absolute<'F'>,
574 &ARBDecompiler::FClamp,
575 &ARBDecompiler::FCastHalf0,
576 &ARBDecompiler::FCastHalf1,
577 &ARBDecompiler::Binary<MIN_F>,
578 &ARBDecompiler::Binary<MAX_F>,
579 &ARBDecompiler::Unary<COS_F32>,
580 &ARBDecompiler::Unary<SIN_F32>,
581 &ARBDecompiler::Unary<EX2_F32>,
582 &ARBDecompiler::Unary<LG2_F32>,
583 &ARBDecompiler::Unary<RSQ_F32>,
584 &ARBDecompiler::FSqrt,
585 &ARBDecompiler::Unary<ROUND_F>,
586 &ARBDecompiler::Unary<FLR_F>,
587 &ARBDecompiler::Unary<CEIL_F>,
588 &ARBDecompiler::Unary<TRUNC_F>,
589 &ARBDecompiler::Unary<I2F_S>,
590 &ARBDecompiler::Unary<I2F_U>,
591 &ARBDecompiler::FSwizzleAdd,
592
593 &ARBDecompiler::Binary<ADD_S>,
594 &ARBDecompiler::Binary<MUL_S>,
595 &ARBDecompiler::Binary<DIV_S>,
596 &ARBDecompiler::Negate<'S'>,
597 &ARBDecompiler::Absolute<'S'>,
598 &ARBDecompiler::Binary<MIN_S>,
599 &ARBDecompiler::Binary<MAX_S>,
600
601 &ARBDecompiler::Unary<TRUNC_S>,
602 &ARBDecompiler::Unary<MOV_U>,
603 &ARBDecompiler::Binary<SHL_S>,
604 &ARBDecompiler::Binary<SHR_U>,
605 &ARBDecompiler::Binary<SHR_S>,
606 &ARBDecompiler::Binary<AND_S>,
607 &ARBDecompiler::Binary<OR_S>,
608 &ARBDecompiler::Binary<XOR_S>,
609 &ARBDecompiler::Unary<NOT_S>,
610 &ARBDecompiler::BitfieldInsert<'S'>,
611 &ARBDecompiler::BitfieldExtract<'S'>,
612 &ARBDecompiler::Unary<BTC_S>,
613 &ARBDecompiler::Unary<BTFM_S>,
614
615 &ARBDecompiler::Binary<ADD_U>,
616 &ARBDecompiler::Binary<MUL_U>,
617 &ARBDecompiler::Binary<DIV_U>,
618 &ARBDecompiler::Binary<MIN_U>,
619 &ARBDecompiler::Binary<MAX_U>,
620 &ARBDecompiler::Unary<TRUNC_U>,
621 &ARBDecompiler::Unary<MOV_U>,
622 &ARBDecompiler::Binary<SHL_U>,
623 &ARBDecompiler::Binary<SHR_U>,
624 &ARBDecompiler::Binary<SHR_U>,
625 &ARBDecompiler::Binary<AND_U>,
626 &ARBDecompiler::Binary<OR_U>,
627 &ARBDecompiler::Binary<XOR_U>,
628 &ARBDecompiler::Unary<NOT_U>,
629 &ARBDecompiler::BitfieldInsert<'U'>,
630 &ARBDecompiler::BitfieldExtract<'U'>,
631 &ARBDecompiler::Unary<BTC_U>,
632 &ARBDecompiler::Unary<BTFM_U>,
633
634 &ARBDecompiler::HAdd2,
635 &ARBDecompiler::HMul2,
636 &ARBDecompiler::HFma2,
637 &ARBDecompiler::HAbsolute,
638 &ARBDecompiler::HNegate,
639 &ARBDecompiler::HClamp,
640 &ARBDecompiler::HCastFloat,
641 &ARBDecompiler::HUnpack,
642 &ARBDecompiler::HMergeF32,
643 &ARBDecompiler::HMergeH0,
644 &ARBDecompiler::HMergeH1,
645 &ARBDecompiler::HPack2,
646
647 &ARBDecompiler::LogicalAssign,
648 &ARBDecompiler::Binary<AND_U>,
649 &ARBDecompiler::Binary<OR_U>,
650 &ARBDecompiler::Binary<XOR_U>,
651 &ARBDecompiler::Unary<NOT_U>,
652 &ARBDecompiler::LogicalPick2,
653 &ARBDecompiler::LogicalAnd2,
654
655 &ARBDecompiler::FloatComparison<SLT_F, false>,
656 &ARBDecompiler::FloatComparison<SEQ_F, false>,
657 &ARBDecompiler::FloatComparison<SLE_F, false>,
658 &ARBDecompiler::FloatComparison<SGT_F, false>,
659 &ARBDecompiler::FloatComparison<SNE_F, false>,
660 &ARBDecompiler::FloatComparison<SGE_F, false>,
661 &ARBDecompiler::FloatOrdered,
662 &ARBDecompiler::FloatUnordered,
663 &ARBDecompiler::FloatComparison<SLT_F, true>,
664 &ARBDecompiler::FloatComparison<SEQ_F, true>,
665 &ARBDecompiler::FloatComparison<SLE_F, true>,
666 &ARBDecompiler::FloatComparison<SGT_F, true>,
667 &ARBDecompiler::FloatComparison<SNE_F, true>,
668 &ARBDecompiler::FloatComparison<SGE_F, true>,
669
670 &ARBDecompiler::Binary<SLT_S>,
671 &ARBDecompiler::Binary<SEQ_S>,
672 &ARBDecompiler::Binary<SLE_S>,
673 &ARBDecompiler::Binary<SGT_S>,
674 &ARBDecompiler::Binary<SNE_S>,
675 &ARBDecompiler::Binary<SGE_S>,
676
677 &ARBDecompiler::Binary<SLT_U>,
678 &ARBDecompiler::Binary<SEQ_U>,
679 &ARBDecompiler::Binary<SLE_U>,
680 &ARBDecompiler::Binary<SGT_U>,
681 &ARBDecompiler::Binary<SNE_U>,
682 &ARBDecompiler::Binary<SGE_U>,
683
684 &ARBDecompiler::LogicalAddCarry,
685
686 &ARBDecompiler::HalfComparison<SLT_F, false>,
687 &ARBDecompiler::HalfComparison<SEQ_F, false>,
688 &ARBDecompiler::HalfComparison<SLE_F, false>,
689 &ARBDecompiler::HalfComparison<SGT_F, false>,
690 &ARBDecompiler::HalfComparison<SNE_F, false>,
691 &ARBDecompiler::HalfComparison<SGE_F, false>,
692 &ARBDecompiler::HalfComparison<SLT_F, true>,
693 &ARBDecompiler::HalfComparison<SEQ_F, true>,
694 &ARBDecompiler::HalfComparison<SLE_F, true>,
695 &ARBDecompiler::HalfComparison<SGT_F, true>,
696 &ARBDecompiler::HalfComparison<SNE_F, true>,
697 &ARBDecompiler::HalfComparison<SGE_F, true>,
698
699 &ARBDecompiler::Texture,
700 &ARBDecompiler::Texture,
701 &ARBDecompiler::TextureGather,
702 &ARBDecompiler::TextureQueryDimensions,
703 &ARBDecompiler::TextureQueryLod,
704 &ARBDecompiler::TexelFetch,
705 &ARBDecompiler::TextureGradient,
706
707 &ARBDecompiler::ImageLoad,
708 &ARBDecompiler::ImageStore,
709
710 &ARBDecompiler::AtomicImage<ADD, U32>,
711 &ARBDecompiler::AtomicImage<AND, U32>,
712 &ARBDecompiler::AtomicImage<OR, U32>,
713 &ARBDecompiler::AtomicImage<XOR, U32>,
714 &ARBDecompiler::AtomicImage<EXCH, U32>,
715
716 &ARBDecompiler::Atomic<EXCH, U32>,
717 &ARBDecompiler::Atomic<ADD, U32>,
718 &ARBDecompiler::Atomic<MIN, U32>,
719 &ARBDecompiler::Atomic<MAX, U32>,
720 &ARBDecompiler::Atomic<AND, U32>,
721 &ARBDecompiler::Atomic<OR, U32>,
722 &ARBDecompiler::Atomic<XOR, U32>,
723
724 &ARBDecompiler::Atomic<EXCH, S32>,
725 &ARBDecompiler::Atomic<ADD, S32>,
726 &ARBDecompiler::Atomic<MIN, S32>,
727 &ARBDecompiler::Atomic<MAX, S32>,
728 &ARBDecompiler::Atomic<AND, S32>,
729 &ARBDecompiler::Atomic<OR, S32>,
730 &ARBDecompiler::Atomic<XOR, S32>,
731
732 &ARBDecompiler::Atomic<ADD, U32>,
733 &ARBDecompiler::Atomic<MIN, U32>,
734 &ARBDecompiler::Atomic<MAX, U32>,
735 &ARBDecompiler::Atomic<AND, U32>,
736 &ARBDecompiler::Atomic<OR, U32>,
737 &ARBDecompiler::Atomic<XOR, U32>,
738
739 &ARBDecompiler::Atomic<ADD, S32>,
740 &ARBDecompiler::Atomic<MIN, S32>,
741 &ARBDecompiler::Atomic<MAX, S32>,
742 &ARBDecompiler::Atomic<AND, S32>,
743 &ARBDecompiler::Atomic<OR, S32>,
744 &ARBDecompiler::Atomic<XOR, S32>,
745
746 &ARBDecompiler::Branch,
747 &ARBDecompiler::BranchIndirect,
748 &ARBDecompiler::PushFlowStack,
749 &ARBDecompiler::PopFlowStack,
750 &ARBDecompiler::Exit,
751 &ARBDecompiler::Discard,
752
753 &ARBDecompiler::EmitVertex,
754 &ARBDecompiler::EndPrimitive,
755
756 &ARBDecompiler::InvocationId,
757 &ARBDecompiler::YNegate,
758 &ARBDecompiler::LocalInvocationId<'x'>,
759 &ARBDecompiler::LocalInvocationId<'y'>,
760 &ARBDecompiler::LocalInvocationId<'z'>,
761 &ARBDecompiler::WorkGroupId<'x'>,
762 &ARBDecompiler::WorkGroupId<'y'>,
763 &ARBDecompiler::WorkGroupId<'z'>,
764
765 &ARBDecompiler::Unary<TGBALLOT_U>,
766 &ARBDecompiler::Unary<TGALL_U>,
767 &ARBDecompiler::Unary<TGANY_U>,
768 &ARBDecompiler::Unary<TGEQ_U>,
769
770 &ARBDecompiler::ThreadId,
771 &ARBDecompiler::ThreadMask<'e', 'q'>,
772 &ARBDecompiler::ThreadMask<'g', 'e'>,
773 &ARBDecompiler::ThreadMask<'g', 't'>,
774 &ARBDecompiler::ThreadMask<'l', 'e'>,
775 &ARBDecompiler::ThreadMask<'l', 't'>,
776 &ARBDecompiler::ShuffleIndexed,
777
778 &ARBDecompiler::Barrier,
779 &ARBDecompiler::MemoryBarrierGroup,
780 &ARBDecompiler::MemoryBarrierGlobal,
781 };
782};
783
784ARBDecompiler::ARBDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry,
785 ShaderType stage, std::string_view identifier)
786 : device{device}, ir{ir}, registry{registry}, stage{stage} {
787 AddLine("TEMP RC;");
788 AddLine("TEMP FSWZA[4];");
789 AddLine("TEMP FSWZB[4];");
790 if (ir.IsDecompiled()) {
791 DecompileAST();
792 } else {
793 DecompileBranchMode();
794 }
795 AddLine("END");
796
797 const std::string code = std::move(shader_source);
798 DeclareHeader();
799 DeclareVertex();
800 DeclareGeometry();
801 DeclareFragment();
802 DeclareCompute();
803 DeclareInputAttributes();
804 DeclareOutputAttributes();
805 DeclareLocalMemory();
806 DeclareGlobalMemory();
807 DeclareConstantBuffers();
808 DeclareRegisters();
809 DeclareTemporaries();
810 DeclarePredicates();
811 DeclareInternalFlags();
812
813 shader_source += code;
814}
815
816std::string_view HeaderStageName(ShaderType stage) {
817 switch (stage) {
818 case ShaderType::Vertex:
819 return "vp";
820 case ShaderType::Geometry:
821 return "gp";
822 case ShaderType::Fragment:
823 return "fp";
824 case ShaderType::Compute:
825 return "cp";
826 default:
827 UNREACHABLE();
828 return "";
829 }
830}
831
832void ARBDecompiler::DeclareHeader() {
833 AddLine("!!NV{}5.0", HeaderStageName(stage));
834 // Enabling this allows us to cheat on some instructions like TXL with SHADOWARRAY2D
835 AddLine("OPTION NV_internal;");
836 AddLine("OPTION NV_gpu_program_fp64;");
837 AddLine("OPTION NV_shader_storage_buffer;");
838 AddLine("OPTION NV_shader_thread_group;");
839 if (ir.UsesWarps() && device.HasWarpIntrinsics()) {
840 AddLine("OPTION NV_shader_thread_shuffle;");
841 }
842 if (stage == ShaderType::Vertex) {
843 if (device.HasNvViewportArray2()) {
844 AddLine("OPTION NV_viewport_array2;");
845 }
846 }
847 if (stage == ShaderType::Fragment) {
848 AddLine("OPTION ARB_draw_buffers;");
849 }
850 if (device.HasImageLoadFormatted()) {
851 AddLine("OPTION EXT_shader_image_load_formatted;");
852 }
853}
854
855void ARBDecompiler::DeclareVertex() {
856 if (stage != ShaderType::Vertex) {
857 return;
858 }
859 AddLine("OUTPUT result_clip[] = {{ result.clip[0..7] }};");
860}
861
862void ARBDecompiler::DeclareGeometry() {
863 if (stage != ShaderType::Geometry) {
864 return;
865 }
866 const auto& info = registry.GetGraphicsInfo();
867 const auto& header = ir.GetHeader();
868 AddLine("PRIMITIVE_IN {};", PrimitiveDescription(info.primitive_topology));
869 AddLine("PRIMITIVE_OUT {};", TopologyName(header.common3.output_topology));
870 AddLine("VERTICES_OUT {};", header.common4.max_output_vertices.Value());
871 AddLine("ATTRIB vertex_position = vertex.position;");
872}
873
874void ARBDecompiler::DeclareFragment() {
875 if (stage != ShaderType::Fragment) {
876 return;
877 }
878 AddLine("OUTPUT result_color7 = result.color[7];");
879 AddLine("OUTPUT result_color6 = result.color[6];");
880 AddLine("OUTPUT result_color5 = result.color[5];");
881 AddLine("OUTPUT result_color4 = result.color[4];");
882 AddLine("OUTPUT result_color3 = result.color[3];");
883 AddLine("OUTPUT result_color2 = result.color[2];");
884 AddLine("OUTPUT result_color1 = result.color[1];");
885 AddLine("OUTPUT result_color0 = result.color;");
886}
887
888void ARBDecompiler::DeclareCompute() {
889 if (stage != ShaderType::Compute) {
890 return;
891 }
892 const ComputeInfo& info = registry.GetComputeInfo();
893 AddLine("GROUP_SIZE {} {} {};", info.workgroup_size[0], info.workgroup_size[1],
894 info.workgroup_size[2]);
895 if (info.shared_memory_size_in_words > 0) {
896 const u32 size_in_bytes = info.shared_memory_size_in_words * 4;
897 AddLine("SHARED_MEMORY {};", size_in_bytes);
898 AddLine("SHARED shared_mem[] = {{program.sharedmem}};");
899 }
900}
901
902void ARBDecompiler::DeclareInputAttributes() {
903 if (stage == ShaderType::Compute) {
904 return;
905 }
906 const std::string_view stage_name = StageInputName(stage);
907 for (const auto attribute : ir.GetInputAttributes()) {
908 if (!IsGenericAttribute(attribute)) {
909 continue;
910 }
911 const u32 index = GetGenericAttributeIndex(attribute);
912
913 std::string_view suffix;
914 if (stage == ShaderType::Fragment) {
915 const auto input_mode{ir.GetHeader().ps.GetPixelImap(index)};
916 if (input_mode == PixelImap::Unused) {
917 return;
918 }
919 suffix = GetInputFlags(input_mode);
920 }
921 AddLine("{}ATTRIB in_attr{}[] = {{ {}.attrib[{}..{}] }};", suffix, index, stage_name, index,
922 index);
923 }
924}
925
926void ARBDecompiler::DeclareOutputAttributes() {
927 if (stage == ShaderType::Compute) {
928 return;
929 }
930 for (const auto attribute : ir.GetOutputAttributes()) {
931 if (!IsGenericAttribute(attribute)) {
932 continue;
933 }
934 const u32 index = GetGenericAttributeIndex(attribute);
935 AddLine("OUTPUT out_attr{}[] = {{ result.attrib[{}..{}] }};", index, index, index);
936 }
937}
938
939void ARBDecompiler::DeclareLocalMemory() {
940 u64 size = 0;
941 if (stage == ShaderType::Compute) {
942 size = registry.GetComputeInfo().local_memory_size_in_words * 4ULL;
943 } else {
944 size = ir.GetHeader().GetLocalMemorySize();
945 }
946 if (size == 0) {
947 return;
948 }
949 const u64 element_count = Common::AlignUp(size, 4) / 4;
950 AddLine("TEMP lmem[{}];", element_count);
951}
952
953void ARBDecompiler::DeclareGlobalMemory() {
954 u32 binding = 0; // device.GetBaseBindings(stage).shader_storage_buffer;
955 for (const auto& pair : ir.GetGlobalMemory()) {
956 const auto& base = pair.first;
957 AddLine("STORAGE {}[] = {{ program.storage[{}] }};", GlobalMemoryName(base), binding);
958 ++binding;
959 }
960}
961
962void ARBDecompiler::DeclareConstantBuffers() {
963 u32 binding = 0;
964 for (const auto& cbuf : ir.GetConstantBuffers()) {
965 AddLine("CBUFFER cbuf{}[] = {{ program.buffer[{}] }};", cbuf.first, binding);
966 ++binding;
967 }
968}
969
970void ARBDecompiler::DeclareRegisters() {
971 for (const u32 gpr : ir.GetRegisters()) {
972 AddLine("TEMP R{};", gpr);
973 }
974}
975
976void ARBDecompiler::DeclareTemporaries() {
977 for (std::size_t i = 0; i < max_temporaries; ++i) {
978 AddLine("TEMP T{};", i);
979 }
980}
981
982void ARBDecompiler::DeclarePredicates() {
983 for (const Tegra::Shader::Pred pred : ir.GetPredicates()) {
984 AddLine("TEMP P{};", static_cast<u64>(pred));
985 }
986}
987
988void ARBDecompiler::DeclareInternalFlags() {
989 for (const char* name : INTERNAL_FLAG_NAMES) {
990 AddLine("TEMP {};", name);
991 }
992}
993
994void ARBDecompiler::InitializeVariables() {
995 AddLine("MOV.F32 FSWZA[0], -1;");
996 AddLine("MOV.F32 FSWZA[1], 1;");
997 AddLine("MOV.F32 FSWZA[2], -1;");
998 AddLine("MOV.F32 FSWZA[3], 0;");
999 AddLine("MOV.F32 FSWZB[0], -1;");
1000 AddLine("MOV.F32 FSWZB[1], -1;");
1001 AddLine("MOV.F32 FSWZB[2], 1;");
1002 AddLine("MOV.F32 FSWZB[3], -1;");
1003
1004 if (stage == ShaderType::Vertex || stage == ShaderType::Geometry) {
1005 AddLine("MOV.F result.position, {{0, 0, 0, 1}};");
1006 }
1007 for (const auto attribute : ir.GetOutputAttributes()) {
1008 if (!IsGenericAttribute(attribute)) {
1009 continue;
1010 }
1011 const u32 index = GetGenericAttributeIndex(attribute);
1012 AddLine("MOV.F result.attrib[{}], {{0, 0, 0, 1}};", index);
1013 }
1014 for (const u32 gpr : ir.GetRegisters()) {
1015 AddLine("MOV.F R{}, {{0, 0, 0, 0}};", gpr);
1016 }
1017 for (const Tegra::Shader::Pred pred : ir.GetPredicates()) {
1018 AddLine("MOV.U P{}, {{0, 0, 0, 0}};", static_cast<u64>(pred));
1019 }
1020}
1021
1022void ARBDecompiler::DecompileAST() {
1023 const u32 num_flow_variables = ir.GetASTNumVariables();
1024 for (u32 i = 0; i < num_flow_variables; ++i) {
1025 AddLine("TEMP F{};", i);
1026 }
1027 for (u32 i = 0; i < num_flow_variables; ++i) {
1028 AddLine("MOV.U F{}, {{0, 0, 0, 0}};", i);
1029 }
1030
1031 InitializeVariables();
1032
1033 VisitAST(ir.GetASTProgram());
1034}
1035
1036void ARBDecompiler::DecompileBranchMode() {
1037 static constexpr u32 FLOW_STACK_SIZE = 20;
1038 if (!ir.IsFlowStackDisabled()) {
1039 AddLine("TEMP SSY[{}];", FLOW_STACK_SIZE);
1040 AddLine("TEMP PBK[{}];", FLOW_STACK_SIZE);
1041 AddLine("TEMP SSY_TOP;");
1042 AddLine("TEMP PBK_TOP;");
1043 }
1044
1045 AddLine("TEMP PC;");
1046
1047 if (!ir.IsFlowStackDisabled()) {
1048 AddLine("MOV.U SSY_TOP.x, 0;");
1049 AddLine("MOV.U PBK_TOP.x, 0;");
1050 }
1051
1052 InitializeVariables();
1053
1054 const auto basic_block_end = ir.GetBasicBlocks().end();
1055 auto basic_block_it = ir.GetBasicBlocks().begin();
1056 const u32 first_address = basic_block_it->first;
1057 AddLine("MOV.U PC.x, {};", first_address);
1058
1059 AddLine("REP;");
1060
1061 std::size_t num_blocks = 0;
1062 while (basic_block_it != basic_block_end) {
1063 const auto& [address, bb] = *basic_block_it;
1064 ++num_blocks;
1065
1066 AddLine("SEQ.S.CC RC.x, PC.x, {};", address);
1067 AddLine("IF NE.x;");
1068
1069 VisitBlock(bb);
1070
1071 ++basic_block_it;
1072
1073 if (basic_block_it != basic_block_end) {
1074 const auto op = std::get_if<OperationNode>(&*bb[bb.size() - 1]);
1075 if (!op || op->GetCode() != OperationCode::Branch) {
1076 const u32 next_address = basic_block_it->first;
1077 AddLine("MOV.U PC.x, {};", next_address);
1078 AddLine("CONT;");
1079 }
1080 }
1081
1082 AddLine("ELSE;");
1083 }
1084 AddLine("RET;");
1085 while (num_blocks--) {
1086 AddLine("ENDIF;");
1087 }
1088
1089 AddLine("ENDREP;");
1090}
1091
1092void ARBDecompiler::VisitAST(const ASTNode& node) {
1093 if (const auto ast = std::get_if<ASTProgram>(&*node->GetInnerData())) {
1094 for (ASTNode current = ast->nodes.GetFirst(); current; current = current->GetNext()) {
1095 VisitAST(current);
1096 }
1097 } else if (const auto ast = std::get_if<ASTIfThen>(&*node->GetInnerData())) {
1098 const std::string condition = VisitExpression(ast->condition);
1099 ResetTemporaries();
1100
1101 AddLine("MOVC.U RC.x, {};", condition);
1102 AddLine("IF NE.x;");
1103 for (ASTNode current = ast->nodes.GetFirst(); current; current = current->GetNext()) {
1104 VisitAST(current);
1105 }
1106 AddLine("ENDIF;");
1107 } else if (const auto ast = std::get_if<ASTIfElse>(&*node->GetInnerData())) {
1108 AddLine("ELSE;");
1109 for (ASTNode current = ast->nodes.GetFirst(); current; current = current->GetNext()) {
1110 VisitAST(current);
1111 }
1112 } else if (const auto ast = std::get_if<ASTBlockDecoded>(&*node->GetInnerData())) {
1113 VisitBlock(ast->nodes);
1114 } else if (const auto ast = std::get_if<ASTVarSet>(&*node->GetInnerData())) {
1115 AddLine("MOV.U F{}, {};", ast->index, VisitExpression(ast->condition));
1116 ResetTemporaries();
1117 } else if (const auto ast = std::get_if<ASTDoWhile>(&*node->GetInnerData())) {
1118 const std::string condition = VisitExpression(ast->condition);
1119 ResetTemporaries();
1120 AddLine("REP;");
1121 for (ASTNode current = ast->nodes.GetFirst(); current; current = current->GetNext()) {
1122 VisitAST(current);
1123 }
1124 AddLine("MOVC.U RC.x, {};", condition);
1125 AddLine("BRK (NE.x);");
1126 AddLine("ENDREP;");
1127 } else if (const auto ast = std::get_if<ASTReturn>(&*node->GetInnerData())) {
1128 const bool is_true = ExprIsTrue(ast->condition);
1129 if (!is_true) {
1130 AddLine("MOVC.U RC.x, {};", VisitExpression(ast->condition));
1131 AddLine("IF NE.x;");
1132 ResetTemporaries();
1133 }
1134 if (ast->kills) {
1135 AddLine("KIL TR;");
1136 } else {
1137 Exit();
1138 }
1139 if (!is_true) {
1140 AddLine("ENDIF;");
1141 }
1142 } else if (const auto ast = std::get_if<ASTBreak>(&*node->GetInnerData())) {
1143 if (ExprIsTrue(ast->condition)) {
1144 AddLine("BRK;");
1145 } else {
1146 AddLine("MOVC.U RC.x, {};", VisitExpression(ast->condition));
1147 AddLine("BRK (NE.x);");
1148 ResetTemporaries();
1149 }
1150 } else if (std::holds_alternative<ASTLabel>(*node->GetInnerData())) {
1151 // Nothing to do
1152 } else {
1153 UNREACHABLE();
1154 }
1155}
1156
1157std::string ARBDecompiler::VisitExpression(const Expr& node) {
1158 if (const auto expr = std::get_if<ExprAnd>(&*node)) {
1159 std::string result = AllocTemporary();
1160 AddLine("AND.U {}, {}, {};", result, VisitExpression(expr->operand1),
1161 VisitExpression(expr->operand2));
1162 return result;
1163 }
1164 if (const auto expr = std::get_if<ExprOr>(&*node)) {
1165 std::string result = AllocTemporary();
1166 AddLine("OR.U {}, {}, {};", result, VisitExpression(expr->operand1),
1167 VisitExpression(expr->operand2));
1168 return result;
1169 }
1170 if (const auto expr = std::get_if<ExprNot>(&*node)) {
1171 std::string result = AllocTemporary();
1172 AddLine("CMP.S {}, {}, 0, -1;", result, VisitExpression(expr->operand1));
1173 return result;
1174 }
1175 if (const auto expr = std::get_if<ExprPredicate>(&*node)) {
1176 return fmt::format("P{}.x", static_cast<u64>(expr->predicate));
1177 }
1178 if (const auto expr = std::get_if<ExprCondCode>(&*node)) {
1179 return Visit(ir.GetConditionCode(expr->cc));
1180 }
1181 if (const auto expr = std::get_if<ExprVar>(&*node)) {
1182 return fmt::format("F{}.x", expr->var_index);
1183 }
1184 if (const auto expr = std::get_if<ExprBoolean>(&*node)) {
1185 return expr->value ? "0xffffffff" : "0";
1186 }
1187 if (const auto expr = std::get_if<ExprGprEqual>(&*node)) {
1188 std::string result = AllocTemporary();
1189 AddLine("SEQ.U {}, R{}.x, {};", result, expr->gpr, expr->value);
1190 return result;
1191 }
1192 UNREACHABLE();
1193 return "0";
1194}
1195
1196void ARBDecompiler::VisitBlock(const NodeBlock& bb) {
1197 for (const auto& node : bb) {
1198 Visit(node);
1199 }
1200}
1201
1202std::string ARBDecompiler::Visit(const Node& node) {
1203 if (const auto operation = std::get_if<OperationNode>(&*node)) {
1204 if (const auto amend_index = operation->GetAmendIndex()) {
1205 Visit(ir.GetAmendNode(*amend_index));
1206 }
1207 const std::size_t index = static_cast<std::size_t>(operation->GetCode());
1208 if (index >= OPERATION_DECOMPILERS.size()) {
1209 UNREACHABLE_MSG("Out of bounds operation: {}", index);
1210 return {};
1211 }
1212 const auto decompiler = OPERATION_DECOMPILERS[index];
1213 if (decompiler == nullptr) {
1214 UNREACHABLE_MSG("Undefined operation: {}", index);
1215 return {};
1216 }
1217 return (this->*decompiler)(*operation);
1218 }
1219
1220 if (const auto gpr = std::get_if<GprNode>(&*node)) {
1221 const u32 index = gpr->GetIndex();
1222 if (index == Register::ZeroIndex) {
1223 return "{0, 0, 0, 0}.x";
1224 }
1225 return fmt::format("R{}.x", index);
1226 }
1227
1228 if (const auto cv = std::get_if<CustomVarNode>(&*node)) {
1229 return fmt::format("CV{}.x", cv->GetIndex());
1230 }
1231
1232 if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
1233 std::string temporary = AllocTemporary();
1234 AddLine("MOV.U {}, {};", temporary, immediate->GetValue());
1235 return temporary;
1236 }
1237
1238 if (const auto predicate = std::get_if<PredicateNode>(&*node)) {
1239 std::string temporary = AllocTemporary();
1240 switch (const auto index = predicate->GetIndex(); index) {
1241 case Tegra::Shader::Pred::UnusedIndex:
1242 AddLine("MOV.S {}, -1;", temporary);
1243 break;
1244 case Tegra::Shader::Pred::NeverExecute:
1245 AddLine("MOV.S {}, 0;", temporary);
1246 break;
1247 default:
1248 AddLine("MOV.S {}, P{}.x;", temporary, static_cast<u64>(index));
1249 break;
1250 }
1251 if (predicate->IsNegated()) {
1252 AddLine("CMP.S {}, {}, 0, -1;", temporary, temporary);
1253 }
1254 return temporary;
1255 }
1256
1257 if (const auto abuf = std::get_if<AbufNode>(&*node)) {
1258 if (abuf->IsPhysicalBuffer()) {
1259 UNIMPLEMENTED_MSG("Physical buffers are not implemented");
1260 return "{0, 0, 0, 0}.x";
1261 }
1262
1263 const auto buffer_index = [this, &abuf]() -> std::string {
1264 if (stage != ShaderType::Geometry) {
1265 return "";
1266 }
1267 return fmt::format("[{}]", Visit(abuf->GetBuffer()));
1268 };
1269
1270 const Attribute::Index index = abuf->GetIndex();
1271 const u32 element = abuf->GetElement();
1272 const char swizzle = Swizzle(element);
1273 switch (index) {
1274 case Attribute::Index::Position: {
1275 if (stage == ShaderType::Geometry) {
1276 return fmt::format("{}_position[{}].{}", StageInputName(stage),
1277 Visit(abuf->GetBuffer()), swizzle);
1278 } else {
1279 return fmt::format("{}.position.{}", StageInputName(stage), swizzle);
1280 }
1281 }
1282 case Attribute::Index::TessCoordInstanceIDVertexID:
1283 ASSERT(stage == ShaderType::Vertex);
1284 switch (element) {
1285 case 2:
1286 return "vertex.instance";
1287 case 3:
1288 return "vertex.id";
1289 }
1290 UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
1291 break;
1292 case Attribute::Index::PointCoord:
1293 switch (element) {
1294 case 0:
1295 return "fragment.pointcoord.x";
1296 case 1:
1297 return "fragment.pointcoord.y";
1298 }
1299 UNIMPLEMENTED();
1300 break;
1301 case Attribute::Index::FrontFacing: {
1302 ASSERT(stage == ShaderType::Fragment);
1303 ASSERT(element == 3);
1304 const std::string temporary = AllocVectorTemporary();
1305 AddLine("SGT.S RC.x, fragment.facing, {{0, 0, 0, 0}};");
1306 AddLine("MOV.U.CC RC.x, -RC;");
1307 AddLine("MOV.S {}.x, 0;", temporary);
1308 AddLine("MOV.S {}.x (NE.x), -1;", temporary);
1309 return fmt::format("{}.x", temporary);
1310 }
1311 default:
1312 if (IsGenericAttribute(index)) {
1313 if (stage == ShaderType::Geometry) {
1314 return fmt::format("in_attr{}[{}][0].{}", GetGenericAttributeIndex(index),
1315 Visit(abuf->GetBuffer()), swizzle);
1316 } else {
1317 return fmt::format("{}.attrib[{}].{}", StageInputName(stage),
1318 GetGenericAttributeIndex(index), swizzle);
1319 }
1320 }
1321 UNIMPLEMENTED_MSG("Unimplemented input attribute={}", static_cast<int>(index));
1322 break;
1323 }
1324 return "{0, 0, 0, 0}.x";
1325 }
1326
1327 if (const auto cbuf = std::get_if<CbufNode>(&*node)) {
1328 std::string offset_string;
1329 const auto& offset = cbuf->GetOffset();
1330 if (const auto imm = std::get_if<ImmediateNode>(&*offset)) {
1331 offset_string = std::to_string(imm->GetValue());
1332 } else {
1333 offset_string = Visit(offset);
1334 }
1335 std::string temporary = AllocTemporary();
1336 AddLine("LDC.F32 {}, cbuf{}[{}];", temporary, cbuf->GetIndex(), offset_string);
1337 return temporary;
1338 }
1339
1340 if (const auto gmem = std::get_if<GmemNode>(&*node)) {
1341 std::string temporary = AllocTemporary();
1342 AddLine("SUB.U {}, {}, {};", temporary, Visit(gmem->GetRealAddress()),
1343 Visit(gmem->GetBaseAddress()));
1344 AddLine("LDB.U32 {}, {}[{}];", temporary, GlobalMemoryName(gmem->GetDescriptor()),
1345 temporary);
1346 return temporary;
1347 }
1348
1349 if (const auto lmem = std::get_if<LmemNode>(&*node)) {
1350 std::string temporary = Visit(lmem->GetAddress());
1351 AddLine("SHR.U {}, {}, 2;", temporary, temporary);
1352 AddLine("MOV.U {}, lmem[{}].x;", temporary, temporary);
1353 return temporary;
1354 }
1355
1356 if (const auto smem = std::get_if<SmemNode>(&*node)) {
1357 std::string temporary = Visit(smem->GetAddress());
1358 AddLine("LDS.U32 {}, shared_mem[{}];", temporary, temporary);
1359 return temporary;
1360 }
1361
1362 if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
1363 const std::size_t index = static_cast<std::size_t>(internal_flag->GetFlag());
1364 return fmt::format("{}.x", INTERNAL_FLAG_NAMES[index]);
1365 }
1366
1367 if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
1368 if (const auto amend_index = conditional->GetAmendIndex()) {
1369 Visit(ir.GetAmendNode(*amend_index));
1370 }
1371 AddLine("MOVC.U RC.x, {};", Visit(conditional->GetCondition()));
1372 AddLine("IF NE.x;");
1373 VisitBlock(conditional->GetCode());
1374 AddLine("ENDIF;");
1375 return {};
1376 }
1377
1378 if (const auto cmt = std::get_if<CommentNode>(&*node)) {
1379 // Uncommenting this will generate invalid code. GLASM lacks comments.
1380 // AddLine("// {}", cmt->GetText());
1381 return {};
1382 }
1383
1384 UNIMPLEMENTED();
1385 return {};
1386}
1387
1388std::pair<std::string, std::size_t> ARBDecompiler::BuildCoords(Operation operation) {
1389 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1390 UNIMPLEMENTED_IF(meta.sampler.is_indexed);
1391 UNIMPLEMENTED_IF(meta.sampler.is_shadow && meta.sampler.is_array &&
1392 meta.sampler.type == Tegra::Shader::TextureType::TextureCube);
1393
1394 const std::size_t count = operation.GetOperandsCount();
1395 std::string temporary = AllocVectorTemporary();
1396 std::size_t i = 0;
1397 for (; i < count; ++i) {
1398 AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i), Visit(operation[i]));
1399 }
1400 if (meta.sampler.is_array) {
1401 AddLine("I2F.S {}.{}, {};", temporary, Swizzle(i++), Visit(meta.array));
1402 }
1403 if (meta.sampler.is_shadow) {
1404 AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i++), Visit(meta.depth_compare));
1405 }
1406 return {std::move(temporary), i};
1407}
1408
1409std::string ARBDecompiler::BuildAoffi(Operation operation) {
1410 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1411 if (meta.aoffi.empty()) {
1412 return {};
1413 }
1414 const std::string temporary = AllocVectorTemporary();
1415 std::size_t i = 0;
1416 for (auto& node : meta.aoffi) {
1417 AddLine("MOV.S {}.{}, {};", temporary, Swizzle(i++), Visit(node));
1418 }
1419 return fmt::format(", offset({})", temporary);
1420}
1421
1422void ARBDecompiler::Exit() {
1423 if (stage != ShaderType::Fragment) {
1424 AddLine("RET;");
1425 return;
1426 }
1427
1428 const auto safe_get_register = [this](u32 reg) -> std::string {
1429 // TODO(Rodrigo): Replace with contains once C++20 releases
1430 const auto& used_registers = ir.GetRegisters();
1431 if (used_registers.find(reg) != used_registers.end()) {
1432 return fmt::format("R{}.x", reg);
1433 }
1434 return "{0, 0, 0, 0}.x";
1435 };
1436
1437 const auto& header = ir.GetHeader();
1438 u32 current_reg = 0;
1439 for (u32 rt = 0; rt < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; ++rt) {
1440 for (u32 component = 0; component < 4; ++component) {
1441 if (!header.ps.IsColorComponentOutputEnabled(rt, component)) {
1442 continue;
1443 }
1444 AddLine("MOV.F result_color{}.{}, {};", rt, Swizzle(component),
1445 safe_get_register(current_reg));
1446 ++current_reg;
1447 }
1448 }
1449 if (header.ps.omap.depth) {
1450 AddLine("MOV.F result.depth.z, {};", safe_get_register(current_reg + 1));
1451 }
1452
1453 AddLine("RET;");
1454}
1455
1456std::string ARBDecompiler::Assign(Operation operation) {
1457 const Node& dest = operation[0];
1458 const Node& src = operation[1];
1459
1460 std::string dest_name;
1461 if (const auto gpr = std::get_if<GprNode>(&*dest)) {
1462 if (gpr->GetIndex() == Register::ZeroIndex) {
1463 // Writing to Register::ZeroIndex is a no op
1464 return {};
1465 }
1466 dest_name = fmt::format("R{}.x", gpr->GetIndex());
1467 } else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
1468 const u32 element = abuf->GetElement();
1469 const char swizzle = Swizzle(element);
1470 switch (const Attribute::Index index = abuf->GetIndex()) {
1471 case Attribute::Index::Position:
1472 dest_name = fmt::format("result.position.{}", swizzle);
1473 break;
1474 case Attribute::Index::LayerViewportPointSize:
1475 switch (element) {
1476 case 0:
1477 UNIMPLEMENTED();
1478 return {};
1479 case 1:
1480 case 2:
1481 if (!device.HasNvViewportArray2()) {
1482 LOG_ERROR(
1483 Render_OpenGL,
1484 "NV_viewport_array2 is missing. Maxwell gen 2 or better is required.");
1485 return {};
1486 }
1487 dest_name = element == 1 ? "result.layer.x" : "result.viewport.x";
1488 break;
1489 case 3:
1490 dest_name = "result.pointsize.x";
1491 break;
1492 }
1493 break;
1494 case Attribute::Index::ClipDistances0123:
1495 dest_name = fmt::format("result.clip[{}].x", element);
1496 break;
1497 case Attribute::Index::ClipDistances4567:
1498 dest_name = fmt::format("result.clip[{}].x", element + 4);
1499 break;
1500 default:
1501 if (!IsGenericAttribute(index)) {
1502 UNREACHABLE();
1503 return {};
1504 }
1505 dest_name =
1506 fmt::format("result.attrib[{}].{}", GetGenericAttributeIndex(index), swizzle);
1507 break;
1508 }
1509 } else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
1510 const std::string address = Visit(lmem->GetAddress());
1511 AddLine("SHR.U {}, {}, 2;", address, address);
1512 dest_name = fmt::format("lmem[{}].x", address);
1513 } else if (const auto smem = std::get_if<SmemNode>(&*dest)) {
1514 AddLine("STS.U32 {}, shared_mem[{}];", Visit(src), Visit(smem->GetAddress()));
1515 ResetTemporaries();
1516 return {};
1517 } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
1518 const std::string temporary = AllocTemporary();
1519 AddLine("SUB.U {}, {}, {};", temporary, Visit(gmem->GetRealAddress()),
1520 Visit(gmem->GetBaseAddress()));
1521 AddLine("STB.U32 {}, {}[{}];", Visit(src), GlobalMemoryName(gmem->GetDescriptor()),
1522 temporary);
1523 ResetTemporaries();
1524 return {};
1525 } else {
1526 UNREACHABLE();
1527 ResetTemporaries();
1528 return {};
1529 }
1530
1531 AddLine("MOV.U {}, {};", dest_name, Visit(src));
1532 ResetTemporaries();
1533 return {};
1534}
1535
1536std::string ARBDecompiler::Select(Operation operation) {
1537 std::string temporary = AllocTemporary();
1538 AddLine("CMP.S {}, {}, {}, {};", temporary, Visit(operation[0]), Visit(operation[1]),
1539 Visit(operation[2]));
1540 return temporary;
1541}
1542
1543std::string ARBDecompiler::FClamp(Operation operation) {
1544 // 1.0f in hex, replace with std::bit_cast on C++20
1545 static constexpr u32 POSITIVE_ONE = 0x3f800000;
1546
1547 std::string temporary = AllocTemporary();
1548 const Node& value = operation[0];
1549 const Node& low = operation[1];
1550 const Node& high = operation[2];
1551 const auto* const imm_low = std::get_if<ImmediateNode>(&*low);
1552 const auto* const imm_high = std::get_if<ImmediateNode>(&*high);
1553 if (imm_low && imm_high && imm_low->GetValue() == 0 && imm_high->GetValue() == POSITIVE_ONE) {
1554 AddLine("MOV.F32.SAT {}, {};", temporary, Visit(value));
1555 } else {
1556 AddLine("MIN.F {}, {}, {};", temporary, Visit(value), Visit(high));
1557 AddLine("MAX.F {}, {}, {};", temporary, temporary, Visit(low));
1558 }
1559 return temporary;
1560}
1561
1562std::string ARBDecompiler::FCastHalf0(Operation operation) {
1563 const std::string temporary = AllocVectorTemporary();
1564 AddLine("UP2H.F {}.x, {};", temporary, Visit(operation[0]));
1565 return fmt::format("{}.x", temporary);
1566}
1567
1568std::string ARBDecompiler::FCastHalf1(Operation operation) {
1569 const std::string temporary = AllocVectorTemporary();
1570 AddLine("UP2H.F {}.y, {};", temporary, Visit(operation[0]));
1571 AddLine("MOV {}.x, {}.y;", temporary, temporary);
1572 return fmt::format("{}.x", temporary);
1573}
1574
1575std::string ARBDecompiler::FSqrt(Operation operation) {
1576 std::string temporary = AllocTemporary();
1577 AddLine("RSQ.F32 {}, {};", temporary, Visit(operation[0]));
1578 AddLine("RCP.F32 {}, {};", temporary, temporary);
1579 return temporary;
1580}
1581
1582std::string ARBDecompiler::FSwizzleAdd(Operation operation) {
1583 const std::string temporary = AllocVectorTemporary();
1584 if (!device.HasWarpIntrinsics()) {
1585 LOG_ERROR(Render_OpenGL,
1586 "NV_shader_thread_shuffle is missing. Kepler or better is required.");
1587 AddLine("ADD.F {}.x, {}, {};", temporary, Visit(operation[0]), Visit(operation[1]));
1588 return fmt::format("{}.x", temporary);
1589 }
1590
1591 AddLine("AND.U {}.z, {}.threadid, 3;", temporary, StageInputName(stage));
1592 AddLine("SHL.U {}.z, {}.z, 1;", temporary, temporary);
1593 AddLine("SHR.U {}.z, {}, {}.z;", temporary, Visit(operation[2]), temporary);
1594 AddLine("AND.U {}.z, {}.z, 3;", temporary, temporary);
1595 AddLine("MUL.F32 {}.x, {}, FSWZA[{}.z];", temporary, Visit(operation[0]), temporary);
1596 AddLine("MUL.F32 {}.y, {}, FSWZB[{}.z];", temporary, Visit(operation[1]), temporary);
1597 AddLine("ADD.F32 {}.x, {}.x, {}.y;", temporary, temporary, temporary);
1598 return fmt::format("{}.x", temporary);
1599}
1600
1601std::string ARBDecompiler::HAdd2(Operation operation) {
1602 const std::string tmp1 = AllocVectorTemporary();
1603 const std::string tmp2 = AllocVectorTemporary();
1604 AddLine("UP2H.F {}.xy, {};", tmp1, Visit(operation[0]));
1605 AddLine("UP2H.F {}.xy, {};", tmp2, Visit(operation[1]));
1606 AddLine("ADD.F16 {}, {}, {};", tmp1, tmp1, tmp2);
1607 AddLine("PK2H.F {}.x, {};", tmp1, tmp1);
1608 return fmt::format("{}.x", tmp1);
1609}
1610
1611std::string ARBDecompiler::HMul2(Operation operation) {
1612 const std::string tmp1 = AllocVectorTemporary();
1613 const std::string tmp2 = AllocVectorTemporary();
1614 AddLine("UP2H.F {}.xy, {};", tmp1, Visit(operation[0]));
1615 AddLine("UP2H.F {}.xy, {};", tmp2, Visit(operation[1]));
1616 AddLine("MUL.F16 {}, {}, {};", tmp1, tmp1, tmp2);
1617 AddLine("PK2H.F {}.x, {};", tmp1, tmp1);
1618 return fmt::format("{}.x", tmp1);
1619}
1620
1621std::string ARBDecompiler::HFma2(Operation operation) {
1622 const std::string tmp1 = AllocVectorTemporary();
1623 const std::string tmp2 = AllocVectorTemporary();
1624 const std::string tmp3 = AllocVectorTemporary();
1625 AddLine("UP2H.F {}.xy, {};", tmp1, Visit(operation[0]));
1626 AddLine("UP2H.F {}.xy, {};", tmp2, Visit(operation[1]));
1627 AddLine("UP2H.F {}.xy, {};", tmp3, Visit(operation[2]));
1628 AddLine("MAD.F16 {}, {}, {}, {};", tmp1, tmp1, tmp2, tmp3);
1629 AddLine("PK2H.F {}.x, {};", tmp1, tmp1);
1630 return fmt::format("{}.x", tmp1);
1631}
1632
1633std::string ARBDecompiler::HAbsolute(Operation operation) {
1634 const std::string temporary = AllocVectorTemporary();
1635 AddLine("UP2H.F {}.xy, {};", temporary, Visit(operation[0]));
1636 AddLine("PK2H.F {}.x, |{}|;", temporary, temporary);
1637 return fmt::format("{}.x", temporary);
1638}
1639
1640std::string ARBDecompiler::HNegate(Operation operation) {
1641 const std::string temporary = AllocVectorTemporary();
1642 AddLine("UP2H.F {}.xy, {};", temporary, Visit(operation[0]));
1643 AddLine("MOVC.S RC.x, {};", Visit(operation[1]));
1644 AddLine("MOV.F {}.x (NE.x), -{}.x;", temporary, temporary);
1645 AddLine("MOVC.S RC.x, {};", Visit(operation[2]));
1646 AddLine("MOV.F {}.y (NE.x), -{}.y;", temporary, temporary);
1647 AddLine("PK2H.F {}.x, {};", temporary, temporary);
1648 return fmt::format("{}.x", temporary);
1649}
1650
1651std::string ARBDecompiler::HClamp(Operation operation) {
1652 const std::string tmp1 = AllocVectorTemporary();
1653 const std::string tmp2 = AllocVectorTemporary();
1654 AddLine("UP2H.F {}.xy, {};", tmp1, Visit(operation[0]));
1655 AddLine("MOV.U {}.x, {};", tmp2, Visit(operation[1]));
1656 AddLine("MOV.U {}.y, {}.x;", tmp2, tmp2);
1657 AddLine("MAX.F {}, {}, {};", tmp1, tmp1, tmp2);
1658 AddLine("MOV.U {}.x, {};", tmp2, Visit(operation[2]));
1659 AddLine("MOV.U {}.y, {}.x;", tmp2, tmp2);
1660 AddLine("MIN.F {}, {}, {};", tmp1, tmp1, tmp2);
1661 AddLine("PK2H.F {}.x, {};", tmp1, tmp1);
1662 return fmt::format("{}.x", tmp1);
1663}
1664
1665std::string ARBDecompiler::HCastFloat(Operation operation) {
1666 const std::string temporary = AllocVectorTemporary();
1667 AddLine("MOV.F {}.y, {{0, 0, 0, 0}};", temporary);
1668 AddLine("MOV.F {}.x, {};", temporary, Visit(operation[0]));
1669 AddLine("PK2H.F {}.x, {};", temporary, temporary);
1670 return fmt::format("{}.x", temporary);
1671}
1672
1673std::string ARBDecompiler::HUnpack(Operation operation) {
1674 const std::string operand = Visit(operation[0]);
1675 switch (std::get<Tegra::Shader::HalfType>(operation.GetMeta())) {
1676 case Tegra::Shader::HalfType::H0_H1:
1677 return operand;
1678 case Tegra::Shader::HalfType::F32: {
1679 const std::string temporary = AllocVectorTemporary();
1680 AddLine("MOV.U {}.x, {};", temporary, operand);
1681 AddLine("MOV.U {}.y, {}.x;", temporary, temporary);
1682 AddLine("PK2H.F {}.x, {};", temporary, temporary);
1683 return fmt::format("{}.x", temporary);
1684 }
1685 case Tegra::Shader::HalfType::H0_H0: {
1686 const std::string temporary = AllocVectorTemporary();
1687 AddLine("UP2H.F {}.xy, {};", temporary, operand);
1688 AddLine("MOV.U {}.y, {}.x;", temporary, temporary);
1689 AddLine("PK2H.F {}.x, {};", temporary, temporary);
1690 return fmt::format("{}.x", temporary);
1691 }
1692 case Tegra::Shader::HalfType::H1_H1: {
1693 const std::string temporary = AllocVectorTemporary();
1694 AddLine("UP2H.F {}.xy, {};", temporary, operand);
1695 AddLine("MOV.U {}.x, {}.y;", temporary, temporary);
1696 AddLine("PK2H.F {}.x, {};", temporary, temporary);
1697 return fmt::format("{}.x", temporary);
1698 }
1699 }
1700 UNREACHABLE();
1701 return "{0, 0, 0, 0}.x";
1702}
1703
1704std::string ARBDecompiler::HMergeF32(Operation operation) {
1705 const std::string temporary = AllocVectorTemporary();
1706 AddLine("UP2H.F {}.xy, {};", temporary, Visit(operation[0]));
1707 return fmt::format("{}.x", temporary);
1708}
1709
1710std::string ARBDecompiler::HMergeH0(Operation operation) {
1711 const std::string temporary = AllocVectorTemporary();
1712 AddLine("UP2H.F {}.xy, {};", temporary, Visit(operation[0]));
1713 AddLine("UP2H.F {}.zw, {};", temporary, Visit(operation[1]));
1714 AddLine("MOV.U {}.x, {}.z;", temporary, temporary);
1715 AddLine("PK2H.F {}.x, {};", temporary, temporary);
1716 return fmt::format("{}.x", temporary);
1717}
1718
1719std::string ARBDecompiler::HMergeH1(Operation operation) {
1720 const std::string temporary = AllocVectorTemporary();
1721 AddLine("UP2H.F {}.xy, {};", temporary, Visit(operation[0]));
1722 AddLine("UP2H.F {}.zw, {};", temporary, Visit(operation[1]));
1723 AddLine("MOV.U {}.y, {}.w;", temporary, temporary);
1724 AddLine("PK2H.F {}.x, {};", temporary, temporary);
1725 return fmt::format("{}.x", temporary);
1726}
1727
1728std::string ARBDecompiler::HPack2(Operation operation) {
1729 const std::string temporary = AllocVectorTemporary();
1730 AddLine("MOV.U {}.x, {};", temporary, Visit(operation[0]));
1731 AddLine("MOV.U {}.y, {};", temporary, Visit(operation[1]));
1732 AddLine("PK2H.F {}.x, {};", temporary, temporary);
1733 return fmt::format("{}.x", temporary);
1734}
1735
1736std::string ARBDecompiler::LogicalAssign(Operation operation) {
1737 const Node& dest = operation[0];
1738 const Node& src = operation[1];
1739
1740 std::string target;
1741
1742 if (const auto pred = std::get_if<PredicateNode>(&*dest)) {
1743 ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment");
1744
1745 const Tegra::Shader::Pred index = pred->GetIndex();
1746 switch (index) {
1747 case Tegra::Shader::Pred::NeverExecute:
1748 case Tegra::Shader::Pred::UnusedIndex:
1749 // Writing to these predicates is a no-op
1750 return {};
1751 }
1752 target = fmt::format("P{}.x", static_cast<u64>(index));
1753 } else if (const auto internal_flag = std::get_if<InternalFlagNode>(&*dest)) {
1754 const std::size_t index = static_cast<std::size_t>(internal_flag->GetFlag());
1755 target = fmt::format("{}.x", INTERNAL_FLAG_NAMES[index]);
1756 } else {
1757 UNREACHABLE();
1758 ResetTemporaries();
1759 return {};
1760 }
1761
1762 AddLine("MOV.U {}, {};", target, Visit(src));
1763 ResetTemporaries();
1764 return {};
1765}
1766
1767std::string ARBDecompiler::LogicalPick2(Operation operation) {
1768 std::string temporary = AllocTemporary();
1769 const u32 index = std::get<ImmediateNode>(*operation[1]).GetValue();
1770 AddLine("MOV.U {}, {}.{};", temporary, Visit(operation[0]), Swizzle(index));
1771 return temporary;
1772}
1773
1774std::string ARBDecompiler::LogicalAnd2(Operation operation) {
1775 std::string temporary = AllocTemporary();
1776 const std::string op = Visit(operation[0]);
1777 AddLine("AND.U {}, {}.x, {}.y;", temporary, op, op);
1778 return temporary;
1779}
1780
1781std::string ARBDecompiler::FloatOrdered(Operation operation) {
1782 std::string temporary = AllocTemporary();
1783 AddLine("MOVC.F32 RC.x, {};", Visit(operation[0]));
1784 AddLine("MOVC.F32 RC.y, {};", Visit(operation[1]));
1785 AddLine("MOV.S {}, -1;", temporary);
1786 AddLine("MOV.S {} (NAN.x), 0;", temporary);
1787 AddLine("MOV.S {} (NAN.y), 0;", temporary);
1788 return temporary;
1789}
1790
1791std::string ARBDecompiler::FloatUnordered(Operation operation) {
1792 std::string temporary = AllocTemporary();
1793 AddLine("MOVC.F32 RC.x, {};", Visit(operation[0]));
1794 AddLine("MOVC.F32 RC.y, {};", Visit(operation[1]));
1795 AddLine("MOV.S {}, 0;", temporary);
1796 AddLine("MOV.S {} (NAN.x), -1;", temporary);
1797 AddLine("MOV.S {} (NAN.y), -1;", temporary);
1798 return temporary;
1799}
1800
1801std::string ARBDecompiler::LogicalAddCarry(Operation operation) {
1802 std::string temporary = AllocTemporary();
1803 AddLine("ADDC.U RC, {}, {};", Visit(operation[0]), Visit(operation[1]));
1804 AddLine("MOV.S {}, 0;", temporary);
1805 AddLine("IF CF.x;");
1806 AddLine("MOV.S {}, -1;", temporary);
1807 AddLine("ENDIF;");
1808 return temporary;
1809}
1810
1811std::string ARBDecompiler::Texture(Operation operation) {
1812 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1813 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
1814 const auto [temporary, swizzle] = BuildCoords(operation);
1815
1816 std::string_view opcode = "TEX";
1817 std::string extra;
1818 if (meta.bias) {
1819 ASSERT(!meta.lod);
1820 opcode = "TXB";
1821
1822 if (swizzle < 4) {
1823 AddLine("MOV.F {}.w, {};", temporary, Visit(meta.bias));
1824 } else {
1825 const std::string bias = AllocTemporary();
1826 AddLine("MOV.F {}, {};", bias, Visit(meta.bias));
1827 extra = fmt::format(" {},", bias);
1828 }
1829 }
1830 if (meta.lod) {
1831 ASSERT(!meta.bias);
1832 opcode = "TXL";
1833
1834 if (swizzle < 4) {
1835 AddLine("MOV.F {}.w, {};", temporary, Visit(meta.lod));
1836 } else {
1837 const std::string lod = AllocTemporary();
1838 AddLine("MOV.F {}, {};", lod, Visit(meta.lod));
1839 extra = fmt::format(" {},", lod);
1840 }
1841 }
1842
1843 AddLine("{}.F {}, {},{} texture[{}], {}{};", opcode, temporary, temporary, extra, sampler_id,
1844 TextureType(meta), BuildAoffi(operation));
1845 AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element));
1846 return fmt::format("{}.x", temporary);
1847}
1848
1849std::string ARBDecompiler::TextureGather(Operation operation) {
1850 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1851 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
1852 const auto [temporary, swizzle] = BuildCoords(operation);
1853
1854 std::string comp;
1855 if (!meta.sampler.is_shadow) {
1856 const auto& immediate = std::get<ImmediateNode>(*meta.component);
1857 comp = fmt::format(".{}", Swizzle(immediate.GetValue()));
1858 }
1859
1860 AddLine("TXG.F {}, {}, texture[{}]{}, {}{};", temporary, temporary, sampler_id, comp,
1861 TextureType(meta), BuildAoffi(operation));
1862 AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element));
1863 return fmt::format("{}.x", temporary);
1864}
1865
1866std::string ARBDecompiler::TextureQueryDimensions(Operation operation) {
1867 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1868 const std::string temporary = AllocVectorTemporary();
1869 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
1870
1871 ASSERT(!meta.sampler.is_array);
1872
1873 const std::string lod = operation.GetOperandsCount() > 0 ? Visit(operation[0]) : "0";
1874 AddLine("TXQ {}, {}, texture[{}], {};", temporary, lod, sampler_id, TextureType(meta));
1875 AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element));
1876 return fmt::format("{}.x", temporary);
1877}
1878
1879std::string ARBDecompiler::TextureQueryLod(Operation operation) {
1880 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1881 const std::string temporary = AllocVectorTemporary();
1882 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
1883
1884 ASSERT(!meta.sampler.is_array);
1885
1886 const std::size_t count = operation.GetOperandsCount();
1887 for (std::size_t i = 0; i < count; ++i) {
1888 AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i), Visit(operation[i]));
1889 }
1890 AddLine("LOD.F {}, {}, texture[{}], {};", temporary, temporary, sampler_id, TextureType(meta));
1891 AddLine("MUL.F32 {}, {}, {{256, 256, 0, 0}};", temporary, temporary);
1892 AddLine("TRUNC.S {}, {};", temporary, temporary);
1893 AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element));
1894 return fmt::format("{}.x", temporary);
1895}
1896
1897std::string ARBDecompiler::TexelFetch(Operation operation) {
1898 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1899 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
1900 const auto [temporary, swizzle] = BuildCoords(operation);
1901
1902 if (!meta.sampler.is_buffer) {
1903 ASSERT(swizzle < 4);
1904 AddLine("MOV.F {}.w, {};", temporary, Visit(meta.lod));
1905 }
1906 AddLine("TXF.F {}, {}, texture[{}], {}{};", temporary, temporary, sampler_id, TextureType(meta),
1907 BuildAoffi(operation));
1908 AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element));
1909 return fmt::format("{}.x", temporary);
1910}
1911
1912std::string ARBDecompiler::TextureGradient(Operation operation) {
1913 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1914 const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
1915 const std::string ddx = AllocVectorTemporary();
1916 const std::string ddy = AllocVectorTemporary();
1917 const std::string coord = BuildCoords(operation).first;
1918
1919 const std::size_t num_components = meta.derivates.size() / 2;
1920 for (std::size_t index = 0; index < num_components; ++index) {
1921 const char swizzle = Swizzle(index);
1922 AddLine("MOV.F {}.{}, {};", ddx, swizzle, Visit(meta.derivates[index * 2]));
1923 AddLine("MOV.F {}.{}, {};", ddy, swizzle, Visit(meta.derivates[index * 2 + 1]));
1924 }
1925
1926 const std::string_view result = coord;
1927 AddLine("TXD.F {}, {}, {}, {}, texture[{}], {}{};", result, coord, ddx, ddy, sampler_id,
1928 TextureType(meta), BuildAoffi(operation));
1929 AddLine("MOV.F {}.x, {}.{};", result, result, Swizzle(meta.element));
1930 return fmt::format("{}.x", result);
1931}
1932
1933std::string ARBDecompiler::ImageLoad(Operation operation) {
1934 const auto& meta = std::get<MetaImage>(operation.GetMeta());
1935 const u32 image_id = device.GetBaseBindings(stage).image + meta.image.index;
1936 const std::size_t count = operation.GetOperandsCount();
1937 const std::string_view type = ImageType(meta.image.type);
1938
1939 const std::string temporary = AllocVectorTemporary();
1940 for (std::size_t i = 0; i < count; ++i) {
1941 AddLine("MOV.S {}.{}, {};", temporary, Swizzle(i), Visit(operation[i]));
1942 }
1943 AddLine("LOADIM.F {}, {}, image[{}], {};", temporary, temporary, image_id, type);
1944 AddLine("MOV.F {}.x, {}.{};", temporary, temporary, Swizzle(meta.element));
1945 return fmt::format("{}.x", temporary);
1946}
1947
1948std::string ARBDecompiler::ImageStore(Operation operation) {
1949 const auto& meta = std::get<MetaImage>(operation.GetMeta());
1950 const u32 image_id = device.GetBaseBindings(stage).image + meta.image.index;
1951 const std::size_t num_coords = operation.GetOperandsCount();
1952 const std::size_t num_values = meta.values.size();
1953 const std::string_view type = ImageType(meta.image.type);
1954
1955 const std::string coord = AllocVectorTemporary();
1956 const std::string value = AllocVectorTemporary();
1957 for (std::size_t i = 0; i < num_coords; ++i) {
1958 AddLine("MOV.S {}.{}, {};", coord, Swizzle(i), Visit(operation[i]));
1959 }
1960 for (std::size_t i = 0; i < num_values; ++i) {
1961 AddLine("MOV.F {}.{}, {};", value, Swizzle(i), Visit(meta.values[i]));
1962 }
1963 AddLine("STOREIM.F image[{}], {}, {}, {};", image_id, value, coord, type);
1964 return {};
1965}
1966
1967std::string ARBDecompiler::Branch(Operation operation) {
1968 const auto target = std::get<ImmediateNode>(*operation[0]);
1969 AddLine("MOV.U PC.x, {};", target.GetValue());
1970 AddLine("CONT;");
1971 return {};
1972}
1973
1974std::string ARBDecompiler::BranchIndirect(Operation operation) {
1975 AddLine("MOV.U PC.x, {};", Visit(operation[0]));
1976 AddLine("CONT;");
1977 return {};
1978}
1979
1980std::string ARBDecompiler::PushFlowStack(Operation operation) {
1981 const auto stack = std::get<MetaStackClass>(operation.GetMeta());
1982 const u32 target = std::get<ImmediateNode>(*operation[0]).GetValue();
1983 const std::string_view stack_name = StackName(stack);
1984 AddLine("MOV.U {}[{}_TOP.x].x, {};", stack_name, stack_name, target);
1985 AddLine("ADD.S {}_TOP.x, {}_TOP.x, 1;", stack_name, stack_name);
1986 return {};
1987}
1988
1989std::string ARBDecompiler::PopFlowStack(Operation operation) {
1990 const auto stack = std::get<MetaStackClass>(operation.GetMeta());
1991 const std::string_view stack_name = StackName(stack);
1992 AddLine("SUB.S {}_TOP.x, {}_TOP.x, 1;", stack_name, stack_name);
1993 AddLine("MOV.U PC.x, {}[{}_TOP.x].x;", stack_name, stack_name);
1994 AddLine("CONT;");
1995 return {};
1996}
1997
1998std::string ARBDecompiler::Exit(Operation) {
1999 Exit();
2000 return {};
2001}
2002
2003std::string ARBDecompiler::Discard(Operation) {
2004 AddLine("KIL TR;");
2005 return {};
2006}
2007
2008std::string ARBDecompiler::EmitVertex(Operation) {
2009 AddLine("EMIT;");
2010 return {};
2011}
2012
2013std::string ARBDecompiler::EndPrimitive(Operation) {
2014 AddLine("ENDPRIM;");
2015 return {};
2016}
2017
2018std::string ARBDecompiler::InvocationId(Operation) {
2019 return "primitive.invocation";
2020}
2021
2022std::string ARBDecompiler::YNegate(Operation) {
2023 LOG_WARNING(Render_OpenGL, "(STUBBED)");
2024 const std::string temporary = AllocTemporary();
2025 AddLine("MOV.F {}, 1;", temporary);
2026 return temporary;
2027}
2028
2029std::string ARBDecompiler::ThreadId(Operation) {
2030 return fmt::format("{}.threadid", StageInputName(stage));
2031}
2032
2033std::string ARBDecompiler::ShuffleIndexed(Operation operation) {
2034 if (!device.HasWarpIntrinsics()) {
2035 LOG_ERROR(Render_OpenGL,
2036 "NV_shader_thread_shuffle is missing. Kepler or better is required.");
2037 return Visit(operation[0]);
2038 }
2039 const std::string temporary = AllocVectorTemporary();
2040 AddLine("SHFIDX.U {}, {}, {}, {{31, 0, 0, 0}};", temporary, Visit(operation[0]),
2041 Visit(operation[1]));
2042 AddLine("MOV.U {}.x, {}.y;", temporary, temporary);
2043 return fmt::format("{}.x", temporary);
2044}
2045
2046std::string ARBDecompiler::Barrier(Operation) {
2047 if (!ir.IsDecompiled()) {
2048 LOG_ERROR(Render_OpenGL, "BAR used but shader is not decompiled");
2049 return {};
2050 }
2051 AddLine("BAR;");
2052 return {};
2053}
2054
2055std::string ARBDecompiler::MemoryBarrierGroup(Operation) {
2056 AddLine("MEMBAR.CTA;");
2057 return {};
2058}
2059
2060std::string ARBDecompiler::MemoryBarrierGlobal(Operation) {
2061 AddLine("MEMBAR;");
2062 return {};
2063}
2064
2065} // Anonymous namespace
2066
2067std::string DecompileAssemblyShader(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
2068 const VideoCommon::Shader::Registry& registry,
2069 Tegra::Engines::ShaderType stage, std::string_view identifier) {
2070 return ARBDecompiler(device, ir, registry, stage, identifier).Code();
2071}
2072
2073} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_arb_decompiler.h b/src/video_core/renderer_opengl/gl_arb_decompiler.h
new file mode 100644
index 000000000..6afc87220
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_arb_decompiler.h
@@ -0,0 +1,29 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <string_view>
9
10#include "common/common_types.h"
11
12namespace Tegra::Engines {
13enum class ShaderType : u32;
14}
15
16namespace VideoCommon::Shader {
17class ShaderIR;
18class Registry;
19} // namespace VideoCommon::Shader
20
21namespace OpenGL {
22
23class Device;
24
25std::string DecompileAssemblyShader(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
26 const VideoCommon::Shader::Registry& registry,
27 Tegra::Engines::ShaderType stage, std::string_view identifier);
28
29} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 9964ea894..e461e4c70 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -22,22 +22,53 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
22 22
23MICROPROFILE_DEFINE(OpenGL_Buffer_Download, "OpenGL", "Buffer Download", MP_RGB(192, 192, 128)); 23MICROPROFILE_DEFINE(OpenGL_Buffer_Download, "OpenGL", "Buffer Download", MP_RGB(192, 192, 128));
24 24
25CachedBufferBlock::CachedBufferBlock(VAddr cpu_addr, const std::size_t size) 25Buffer::Buffer(const Device& device, VAddr cpu_addr, std::size_t size)
26 : VideoCommon::BufferBlock{cpu_addr, size} { 26 : VideoCommon::BufferBlock{cpu_addr, size} {
27 gl_buffer.Create(); 27 gl_buffer.Create();
28 glNamedBufferData(gl_buffer.handle, static_cast<GLsizeiptr>(size), nullptr, GL_DYNAMIC_DRAW); 28 glNamedBufferData(gl_buffer.handle, static_cast<GLsizeiptr>(size), nullptr, GL_DYNAMIC_DRAW);
29 if (device.HasVertexBufferUnifiedMemory()) {
30 glMakeNamedBufferResidentNV(gl_buffer.handle, GL_READ_WRITE);
31 glGetNamedBufferParameterui64vNV(gl_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, &gpu_address);
32 }
33}
34
35Buffer::~Buffer() = default;
36
37void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) {
38 glNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size),
39 data);
40}
41
42void Buffer::Download(std::size_t offset, std::size_t size, u8* data) {
43 MICROPROFILE_SCOPE(OpenGL_Buffer_Download);
44 const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size);
45 const GLintptr gl_offset = static_cast<GLintptr>(offset);
46 if (read_buffer.handle == 0) {
47 read_buffer.Create();
48 glNamedBufferData(read_buffer.handle, static_cast<GLsizeiptr>(Size()), nullptr,
49 GL_STREAM_READ);
50 }
51 glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
52 glCopyNamedBufferSubData(gl_buffer.handle, read_buffer.handle, gl_offset, gl_offset, gl_size);
53 glGetNamedBufferSubData(read_buffer.handle, gl_offset, gl_size, data);
29} 54}
30 55
31CachedBufferBlock::~CachedBufferBlock() = default; 56void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
57 std::size_t size) {
58 glCopyNamedBufferSubData(src.Handle(), Handle(), static_cast<GLintptr>(src_offset),
59 static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size));
60}
32 61
33OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, 62OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system,
34 const Device& device, std::size_t stream_size) 63 const Device& device_, std::size_t stream_size)
35 : GenericBufferCache{rasterizer, system, std::make_unique<OGLStreamBuffer>(stream_size, true)} { 64 : GenericBufferCache{rasterizer, system,
65 std::make_unique<OGLStreamBuffer>(device_, stream_size, true)},
66 device{device_} {
36 if (!device.HasFastBufferSubData()) { 67 if (!device.HasFastBufferSubData()) {
37 return; 68 return;
38 } 69 }
39 70
40 static constexpr auto size = static_cast<GLsizeiptr>(Maxwell::MaxConstBufferSize); 71 static constexpr GLsizeiptr size = static_cast<GLsizeiptr>(Maxwell::MaxConstBufferSize);
41 glCreateBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs)); 72 glCreateBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs));
42 for (const GLuint cbuf : cbufs) { 73 for (const GLuint cbuf : cbufs) {
43 glNamedBufferData(cbuf, size, nullptr, GL_STREAM_DRAW); 74 glNamedBufferData(cbuf, size, nullptr, GL_STREAM_DRAW);
@@ -48,44 +79,21 @@ OGLBufferCache::~OGLBufferCache() {
48 glDeleteBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs)); 79 glDeleteBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs));
49} 80}
50 81
51Buffer OGLBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { 82std::shared_ptr<Buffer> OGLBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) {
52 return std::make_shared<CachedBufferBlock>(cpu_addr, size); 83 return std::make_shared<Buffer>(device, cpu_addr, size);
53} 84}
54 85
55GLuint OGLBufferCache::ToHandle(const Buffer& buffer) { 86OGLBufferCache::BufferInfo OGLBufferCache::GetEmptyBuffer(std::size_t) {
56 return buffer->GetHandle(); 87 return {0, 0, 0};
57}
58
59GLuint OGLBufferCache::GetEmptyBuffer(std::size_t) {
60 return 0;
61}
62
63void OGLBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
64 const u8* data) {
65 glNamedBufferSubData(buffer->GetHandle(), static_cast<GLintptr>(offset),
66 static_cast<GLsizeiptr>(size), data);
67}
68
69void OGLBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
70 u8* data) {
71 MICROPROFILE_SCOPE(OpenGL_Buffer_Download);
72 glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
73 glGetNamedBufferSubData(buffer->GetHandle(), static_cast<GLintptr>(offset),
74 static_cast<GLsizeiptr>(size), data);
75}
76
77void OGLBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset,
78 std::size_t dst_offset, std::size_t size) {
79 glCopyNamedBufferSubData(src->GetHandle(), dst->GetHandle(), static_cast<GLintptr>(src_offset),
80 static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size));
81} 88}
82 89
83OGLBufferCache::BufferInfo OGLBufferCache::ConstBufferUpload(const void* raw_pointer, 90OGLBufferCache::BufferInfo OGLBufferCache::ConstBufferUpload(const void* raw_pointer,
84 std::size_t size) { 91 std::size_t size) {
85 DEBUG_ASSERT(cbuf_cursor < std::size(cbufs)); 92 DEBUG_ASSERT(cbuf_cursor < std::size(cbufs));
86 const GLuint& cbuf = cbufs[cbuf_cursor++]; 93 const GLuint cbuf = cbufs[cbuf_cursor++];
94
87 glNamedBufferSubData(cbuf, 0, static_cast<GLsizeiptr>(size), raw_pointer); 95 glNamedBufferSubData(cbuf, 0, static_cast<GLsizeiptr>(size), raw_pointer);
88 return {cbuf, 0}; 96 return {cbuf, 0, 0};
89} 97}
90 98
91} // namespace OpenGL 99} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index a9e86cfc7..88fdc0536 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -10,7 +10,6 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/buffer_cache/buffer_cache.h" 11#include "video_core/buffer_cache/buffer_cache.h"
12#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/rasterizer_cache.h"
14#include "video_core/renderer_opengl/gl_resource_manager.h" 13#include "video_core/renderer_opengl/gl_resource_manager.h"
15#include "video_core/renderer_opengl/gl_stream_buffer.h" 14#include "video_core/renderer_opengl/gl_stream_buffer.h"
16 15
@@ -24,57 +23,58 @@ class Device;
24class OGLStreamBuffer; 23class OGLStreamBuffer;
25class RasterizerOpenGL; 24class RasterizerOpenGL;
26 25
27class CachedBufferBlock; 26class Buffer : public VideoCommon::BufferBlock {
27public:
28 explicit Buffer(const Device& device, VAddr cpu_addr, std::size_t size);
29 ~Buffer();
28 30
29using Buffer = std::shared_ptr<CachedBufferBlock>; 31 void Upload(std::size_t offset, std::size_t size, const u8* data);
30using GenericBufferCache = VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>;
31 32
32class CachedBufferBlock : public VideoCommon::BufferBlock { 33 void Download(std::size_t offset, std::size_t size, u8* data);
33public: 34
34 explicit CachedBufferBlock(VAddr cpu_addr, const std::size_t size); 35 void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
35 ~CachedBufferBlock(); 36 std::size_t size);
36 37
37 GLuint GetHandle() const { 38 GLuint Handle() const noexcept {
38 return gl_buffer.handle; 39 return gl_buffer.handle;
39 } 40 }
40 41
42 u64 Address() const noexcept {
43 return gpu_address;
44 }
45
41private: 46private:
42 OGLBuffer gl_buffer; 47 OGLBuffer gl_buffer;
48 OGLBuffer read_buffer;
49 u64 gpu_address = 0;
43}; 50};
44 51
52using GenericBufferCache = VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>;
45class OGLBufferCache final : public GenericBufferCache { 53class OGLBufferCache final : public GenericBufferCache {
46public: 54public:
47 explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, 55 explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system,
48 const Device& device, std::size_t stream_size); 56 const Device& device, std::size_t stream_size);
49 ~OGLBufferCache(); 57 ~OGLBufferCache();
50 58
51 GLuint GetEmptyBuffer(std::size_t) override; 59 BufferInfo GetEmptyBuffer(std::size_t) override;
52 60
53 void Acquire() noexcept { 61 void Acquire() noexcept {
54 cbuf_cursor = 0; 62 cbuf_cursor = 0;
55 } 63 }
56 64
57protected: 65protected:
58 Buffer CreateBlock(VAddr cpu_addr, std::size_t size) override; 66 std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) override;
59
60 GLuint ToHandle(const Buffer& buffer) override;
61
62 void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
63 const u8* data) override;
64
65 void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
66 u8* data) override;
67
68 void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset,
69 std::size_t dst_offset, std::size_t size) override;
70 67
71 BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) override; 68 BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) override;
72 69
73private: 70private:
71 static constexpr std::size_t NUM_CBUFS = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers *
72 Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram;
73
74 const Device& device;
75
74 std::size_t cbuf_cursor = 0; 76 std::size_t cbuf_cursor = 0;
75 std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers * 77 std::array<GLuint, NUM_CBUFS> cbufs{};
76 Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram>
77 cbufs;
78}; 78};
79 79
80} // namespace OpenGL 80} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 466a911db..c1f20f0ab 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -6,6 +6,7 @@
6#include <array> 6#include <array>
7#include <cstddef> 7#include <cstddef>
8#include <cstring> 8#include <cstring>
9#include <limits>
9#include <optional> 10#include <optional>
10#include <vector> 11#include <vector>
11 12
@@ -26,24 +27,27 @@ constexpr u32 ReservedUniformBlocks = 1;
26 27
27constexpr u32 NumStages = 5; 28constexpr u32 NumStages = 5;
28 29
29constexpr std::array LimitUBOs = {GL_MAX_VERTEX_UNIFORM_BLOCKS, GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, 30constexpr std::array LimitUBOs = {
30 GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, 31 GL_MAX_VERTEX_UNIFORM_BLOCKS, GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS,
31 GL_MAX_GEOMETRY_UNIFORM_BLOCKS, GL_MAX_FRAGMENT_UNIFORM_BLOCKS}; 32 GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, GL_MAX_GEOMETRY_UNIFORM_BLOCKS,
33 GL_MAX_FRAGMENT_UNIFORM_BLOCKS, GL_MAX_COMPUTE_UNIFORM_BLOCKS};
32 34
33constexpr std::array LimitSSBOs = { 35constexpr std::array LimitSSBOs = {
34 GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS, 36 GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS,
35 GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS, GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS, 37 GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS, GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS,
36 GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS}; 38 GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS};
37 39
38constexpr std::array LimitSamplers = { 40constexpr std::array LimitSamplers = {GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,
39 GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, 41 GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS,
40 GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, 42 GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS,
41 GL_MAX_TEXTURE_IMAGE_UNITS}; 43 GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS,
44 GL_MAX_TEXTURE_IMAGE_UNITS,
45 GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS};
42 46
43constexpr std::array LimitImages = {GL_MAX_VERTEX_IMAGE_UNIFORMS, 47constexpr std::array LimitImages = {
44 GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, 48 GL_MAX_VERTEX_IMAGE_UNIFORMS, GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS,
45 GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, 49 GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, GL_MAX_GEOMETRY_IMAGE_UNIFORMS,
46 GL_MAX_GEOMETRY_IMAGE_UNIFORMS, GL_MAX_FRAGMENT_IMAGE_UNIFORMS}; 50 GL_MAX_FRAGMENT_IMAGE_UNIFORMS, GL_MAX_COMPUTE_IMAGE_UNIFORMS};
47 51
48template <typename T> 52template <typename T>
49T GetInteger(GLenum pname) { 53T GetInteger(GLenum pname) {
@@ -85,6 +89,13 @@ u32 Extract(u32& base, u32& num, u32 amount, std::optional<GLenum> limit = {}) {
85 return std::exchange(base, base + amount); 89 return std::exchange(base, base + amount);
86} 90}
87 91
92std::array<u32, Tegra::Engines::MaxShaderTypes> BuildMaxUniformBuffers() noexcept {
93 std::array<u32, Tegra::Engines::MaxShaderTypes> max;
94 std::transform(LimitUBOs.begin(), LimitUBOs.end(), max.begin(),
95 [](GLenum pname) { return GetInteger<u32>(pname); });
96 return max;
97}
98
88std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> BuildBaseBindings() noexcept { 99std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> BuildBaseBindings() noexcept {
89 std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> bindings; 100 std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> bindings;
90 101
@@ -112,16 +123,24 @@ std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> BuildBaseBindin
112 u32 num_images = GetInteger<u32>(GL_MAX_IMAGE_UNITS); 123 u32 num_images = GetInteger<u32>(GL_MAX_IMAGE_UNITS);
113 u32 base_images = 0; 124 u32 base_images = 0;
114 125
115 // Reserve more image bindings on fragment and vertex stages. 126 // GL_MAX_IMAGE_UNITS is guaranteed by the spec to have a minimum value of 8.
127 // Due to the limitation of GL_MAX_IMAGE_UNITS, reserve at least 4 image bindings on the
128 // fragment stage, and at least 1 for the rest of the stages.
129 // So far games are observed to use 1 image binding on vertex and 4 on fragment stages.
130
131 // Reserve at least 4 image bindings on the fragment stage.
116 bindings[4].image = 132 bindings[4].image =
117 Extract(base_images, num_images, num_images / NumStages + 2, LimitImages[4]); 133 Extract(base_images, num_images, std::max(4U, num_images / NumStages), LimitImages[4]);
118 bindings[0].image = 134
119 Extract(base_images, num_images, num_images / NumStages + 1, LimitImages[0]); 135 // This is guaranteed to be at least 1.
136 const u32 total_extracted_images = num_images / (NumStages - 1);
120 137
121 // Reserve the other image bindings. 138 // Reserve the other image bindings.
122 const u32 total_extracted_images = num_images / (NumStages - 2); 139 for (std::size_t i = 0; i < NumStages; ++i) {
123 for (std::size_t i = 2; i < NumStages; ++i) {
124 const std::size_t stage = stage_swizzle[i]; 140 const std::size_t stage = stage_swizzle[i];
141 if (stage == 4) {
142 continue;
143 }
125 bindings[stage].image = 144 bindings[stage].image =
126 Extract(base_images, num_images, total_extracted_images, LimitImages[stage]); 145 Extract(base_images, num_images, total_extracted_images, LimitImages[stage]);
127 } 146 }
@@ -133,6 +152,7 @@ std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> BuildBaseBindin
133} 152}
134 153
135bool IsASTCSupported() { 154bool IsASTCSupported() {
155 static constexpr std::array targets = {GL_TEXTURE_2D, GL_TEXTURE_2D_ARRAY};
136 static constexpr std::array formats = { 156 static constexpr std::array formats = {
137 GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_RGBA_ASTC_5x4_KHR, 157 GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_RGBA_ASTC_5x4_KHR,
138 GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_COMPRESSED_RGBA_ASTC_6x5_KHR, 158 GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_COMPRESSED_RGBA_ASTC_6x5_KHR,
@@ -149,25 +169,44 @@ bool IsASTCSupported() {
149 GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, 169 GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR,
150 GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, 170 GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR,
151 }; 171 };
152 return std::find_if_not(formats.begin(), formats.end(), [](GLenum format) { 172 static constexpr std::array required_support = {
153 GLint supported; 173 GL_VERTEX_TEXTURE, GL_TESS_CONTROL_TEXTURE, GL_TESS_EVALUATION_TEXTURE,
154 glGetInternalformativ(GL_TEXTURE_2D, format, GL_INTERNALFORMAT_SUPPORTED, 1, 174 GL_GEOMETRY_TEXTURE, GL_FRAGMENT_TEXTURE, GL_COMPUTE_TEXTURE,
155 &supported); 175 };
156 return supported == GL_TRUE; 176
157 }) == formats.end(); 177 for (const GLenum target : targets) {
178 for (const GLenum format : formats) {
179 for (const GLenum support : required_support) {
180 GLint value;
181 glGetInternalformativ(target, format, support, 1, &value);
182 if (value != GL_FULL_SUPPORT) {
183 return false;
184 }
185 }
186 }
187 }
188 return true;
158} 189}
159 190
160} // Anonymous namespace 191} // Anonymous namespace
161 192
162Device::Device() : base_bindings{BuildBaseBindings()} { 193Device::Device()
194 : max_uniform_buffers{BuildMaxUniformBuffers()}, base_bindings{BuildBaseBindings()} {
163 const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); 195 const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
164 const auto renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER)); 196 const std::string_view renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
197 const std::string_view version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
165 const std::vector extensions = GetExtensions(); 198 const std::vector extensions = GetExtensions();
166 199
167 const bool is_nvidia = vendor == "NVIDIA Corporation"; 200 const bool is_nvidia = vendor == "NVIDIA Corporation";
168 const bool is_amd = vendor == "ATI Technologies Inc."; 201 const bool is_amd = vendor == "ATI Technologies Inc.";
169 const bool is_intel = vendor == "Intel"; 202
170 const bool is_intel_proprietary = is_intel && std::strstr(renderer, "Mesa") == nullptr; 203 bool disable_fast_buffer_sub_data = false;
204 if (is_nvidia && version == "4.6.0 NVIDIA 443.24") {
205 LOG_WARNING(
206 Render_OpenGL,
207 "Beta driver 443.24 is known to have issues. There might be performance issues.");
208 disable_fast_buffer_sub_data = true;
209 }
171 210
172 uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); 211 uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
173 shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); 212 shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
@@ -178,36 +217,43 @@ Device::Device() : base_bindings{BuildBaseBindings()} {
178 has_shader_ballot = GLAD_GL_ARB_shader_ballot; 217 has_shader_ballot = GLAD_GL_ARB_shader_ballot;
179 has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array; 218 has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array;
180 has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted"); 219 has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted");
220 has_texture_shadow_lod = HasExtension(extensions, "GL_EXT_texture_shadow_lod");
181 has_astc = IsASTCSupported(); 221 has_astc = IsASTCSupported();
182 has_variable_aoffi = TestVariableAoffi(); 222 has_variable_aoffi = TestVariableAoffi();
183 has_component_indexing_bug = is_amd; 223 has_component_indexing_bug = is_amd;
184 has_precise_bug = TestPreciseBug(); 224 has_precise_bug = TestPreciseBug();
185 has_broken_compute = is_intel_proprietary; 225 has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2;
186 has_fast_buffer_sub_data = is_nvidia; 226 has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory;
187 use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 && 227
188 GLAD_GL_NV_compute_program5; 228 // At the moment of writing this, only Nvidia's driver optimizes BufferSubData on exclusive
229 // uniform buffers as "push constants"
230 has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data;
231
232 use_assembly_shaders = Settings::values.use_assembly_shaders.GetValue() &&
233 GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 &&
234 GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
189 235
190 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); 236 LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
191 LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug); 237 LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
192 LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug); 238 LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug);
193 239
194 if (Settings::values.use_assembly_shaders && !use_assembly_shaders) { 240 if (Settings::values.use_assembly_shaders.GetValue() && !use_assembly_shaders) {
195 LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported"); 241 LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported");
196 } 242 }
197} 243}
198 244
199Device::Device(std::nullptr_t) { 245Device::Device(std::nullptr_t) {
200 uniform_buffer_alignment = 0; 246 max_uniform_buffers.fill(std::numeric_limits<u32>::max());
247 uniform_buffer_alignment = 4;
248 shader_storage_alignment = 4;
201 max_vertex_attributes = 16; 249 max_vertex_attributes = 16;
202 max_varyings = 15; 250 max_varyings = 15;
203 has_warp_intrinsics = true; 251 has_warp_intrinsics = true;
204 has_shader_ballot = true; 252 has_shader_ballot = true;
205 has_vertex_viewport_layer = true; 253 has_vertex_viewport_layer = true;
206 has_image_load_formatted = true; 254 has_image_load_formatted = true;
255 has_texture_shadow_lod = true;
207 has_variable_aoffi = true; 256 has_variable_aoffi = true;
208 has_component_indexing_bug = false;
209 has_broken_compute = false;
210 has_precise_bug = false;
211} 257}
212 258
213bool Device::TestVariableAoffi() { 259bool Device::TestVariableAoffi() {
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index e915dbd86..e1d811966 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -24,6 +24,10 @@ public:
24 explicit Device(); 24 explicit Device();
25 explicit Device(std::nullptr_t); 25 explicit Device(std::nullptr_t);
26 26
27 u32 GetMaxUniformBuffers(Tegra::Engines::ShaderType shader_type) const noexcept {
28 return max_uniform_buffers[static_cast<std::size_t>(shader_type)];
29 }
30
27 const BaseBindings& GetBaseBindings(std::size_t stage_index) const noexcept { 31 const BaseBindings& GetBaseBindings(std::size_t stage_index) const noexcept {
28 return base_bindings[stage_index]; 32 return base_bindings[stage_index];
29 } 33 }
@@ -64,6 +68,14 @@ public:
64 return has_image_load_formatted; 68 return has_image_load_formatted;
65 } 69 }
66 70
71 bool HasTextureShadowLod() const {
72 return has_texture_shadow_lod;
73 }
74
75 bool HasVertexBufferUnifiedMemory() const {
76 return has_vertex_buffer_unified_memory;
77 }
78
67 bool HasASTC() const { 79 bool HasASTC() const {
68 return has_astc; 80 return has_astc;
69 } 81 }
@@ -80,14 +92,14 @@ public:
80 return has_precise_bug; 92 return has_precise_bug;
81 } 93 }
82 94
83 bool HasBrokenCompute() const {
84 return has_broken_compute;
85 }
86
87 bool HasFastBufferSubData() const { 95 bool HasFastBufferSubData() const {
88 return has_fast_buffer_sub_data; 96 return has_fast_buffer_sub_data;
89 } 97 }
90 98
99 bool HasNvViewportArray2() const {
100 return has_nv_viewport_array2;
101 }
102
91 bool UseAssemblyShaders() const { 103 bool UseAssemblyShaders() const {
92 return use_assembly_shaders; 104 return use_assembly_shaders;
93 } 105 }
@@ -96,7 +108,8 @@ private:
96 static bool TestVariableAoffi(); 108 static bool TestVariableAoffi();
97 static bool TestPreciseBug(); 109 static bool TestPreciseBug();
98 110
99 std::array<BaseBindings, Tegra::Engines::MaxShaderTypes> base_bindings; 111 std::array<u32, Tegra::Engines::MaxShaderTypes> max_uniform_buffers{};
112 std::array<BaseBindings, Tegra::Engines::MaxShaderTypes> base_bindings{};
100 std::size_t uniform_buffer_alignment{}; 113 std::size_t uniform_buffer_alignment{};
101 std::size_t shader_storage_alignment{}; 114 std::size_t shader_storage_alignment{};
102 u32 max_vertex_attributes{}; 115 u32 max_vertex_attributes{};
@@ -105,12 +118,14 @@ private:
105 bool has_shader_ballot{}; 118 bool has_shader_ballot{};
106 bool has_vertex_viewport_layer{}; 119 bool has_vertex_viewport_layer{};
107 bool has_image_load_formatted{}; 120 bool has_image_load_formatted{};
121 bool has_texture_shadow_lod{};
122 bool has_vertex_buffer_unified_memory{};
108 bool has_astc{}; 123 bool has_astc{};
109 bool has_variable_aoffi{}; 124 bool has_variable_aoffi{};
110 bool has_component_indexing_bug{}; 125 bool has_component_indexing_bug{};
111 bool has_precise_bug{}; 126 bool has_precise_bug{};
112 bool has_broken_compute{};
113 bool has_fast_buffer_sub_data{}; 127 bool has_fast_buffer_sub_data{};
128 bool has_nv_viewport_array2{};
114 bool use_assembly_shaders{}; 129 bool use_assembly_shaders{};
115}; 130};
116 131
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 716d43e65..e960a0ef1 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -30,6 +30,7 @@
30#include "video_core/renderer_opengl/gl_shader_cache.h" 30#include "video_core/renderer_opengl/gl_shader_cache.h"
31#include "video_core/renderer_opengl/maxwell_to_gl.h" 31#include "video_core/renderer_opengl/maxwell_to_gl.h"
32#include "video_core/renderer_opengl/renderer_opengl.h" 32#include "video_core/renderer_opengl/renderer_opengl.h"
33#include "video_core/shader_cache.h"
33 34
34namespace OpenGL { 35namespace OpenGL {
35 36
@@ -54,15 +55,34 @@ MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255
54 55
55namespace { 56namespace {
56 57
57constexpr std::size_t NumSupportedVertexAttributes = 16; 58constexpr std::size_t NUM_CONST_BUFFERS_PER_STAGE = 18;
59constexpr std::size_t NUM_CONST_BUFFERS_BYTES_PER_STAGE =
60 NUM_CONST_BUFFERS_PER_STAGE * Maxwell::MaxConstBufferSize;
61constexpr std::size_t TOTAL_CONST_BUFFER_BYTES =
62 NUM_CONST_BUFFERS_BYTES_PER_STAGE * Maxwell::MaxShaderStage;
63
64constexpr std::size_t NUM_SUPPORTED_VERTEX_ATTRIBUTES = 16;
65constexpr std::size_t NUM_SUPPORTED_VERTEX_BINDINGS = 16;
58 66
59template <typename Engine, typename Entry> 67template <typename Engine, typename Entry>
60Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, 68Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry,
61 ShaderType shader_type, std::size_t index = 0) { 69 ShaderType shader_type, std::size_t index = 0) {
70 if constexpr (std::is_same_v<Entry, SamplerEntry>) {
71 if (entry.is_separated) {
72 const u32 buffer_1 = entry.buffer;
73 const u32 buffer_2 = entry.secondary_buffer;
74 const u32 offset_1 = entry.offset;
75 const u32 offset_2 = entry.secondary_offset;
76 const u32 handle_1 = engine.AccessConstBuffer32(shader_type, buffer_1, offset_1);
77 const u32 handle_2 = engine.AccessConstBuffer32(shader_type, buffer_2, offset_2);
78 return engine.GetTextureInfo(handle_1 | handle_2);
79 }
80 }
62 if (entry.is_bindless) { 81 if (entry.is_bindless) {
63 const auto tex_handle = engine.AccessConstBuffer32(shader_type, entry.buffer, entry.offset); 82 const u32 handle = engine.AccessConstBuffer32(shader_type, entry.buffer, entry.offset);
64 return engine.GetTextureInfo(tex_handle); 83 return engine.GetTextureInfo(handle);
65 } 84 }
85
66 const auto& gpu_profile = engine.AccessGuestDriverProfile(); 86 const auto& gpu_profile = engine.AccessGuestDriverProfile();
67 const u32 offset = entry.offset + static_cast<u32>(index * gpu_profile.GetTextureHandlerSize()); 87 const u32 offset = entry.offset + static_cast<u32>(index * gpu_profile.GetTextureHandlerSize());
68 if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) { 88 if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) {
@@ -87,6 +107,34 @@ std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
87 return buffer.size; 107 return buffer.size;
88} 108}
89 109
110/// Translates hardware transform feedback indices
111/// @param location Hardware location
112/// @return Pair of ARB_transform_feedback3 token stream first and third arguments
113/// @note Read https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_transform_feedback3.txt
114std::pair<GLint, GLint> TransformFeedbackEnum(u8 location) {
115 const u8 index = location / 4;
116 if (index >= 8 && index <= 39) {
117 return {GL_GENERIC_ATTRIB_NV, index - 8};
118 }
119 if (index >= 48 && index <= 55) {
120 return {GL_TEXTURE_COORD_NV, index - 48};
121 }
122 switch (index) {
123 case 7:
124 return {GL_POSITION, 0};
125 case 40:
126 return {GL_PRIMARY_COLOR_NV, 0};
127 case 41:
128 return {GL_SECONDARY_COLOR_NV, 0};
129 case 42:
130 return {GL_BACK_PRIMARY_COLOR_NV, 0};
131 case 43:
132 return {GL_BACK_SECONDARY_COLOR_NV, 0};
133 }
134 UNIMPLEMENTED_MSG("index={}", static_cast<int>(index));
135 return {GL_POSITION, 0};
136}
137
90void oglEnable(GLenum cap, bool state) { 138void oglEnable(GLenum cap, bool state) {
91 (state ? glEnable : glDisable)(cap); 139 (state ? glEnable : glDisable)(cap);
92} 140}
@@ -104,6 +152,9 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
104 screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker} { 152 screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker} {
105 CheckExtensions(); 153 CheckExtensions();
106 154
155 unified_uniform_buffer.Create();
156 glNamedBufferStorage(unified_uniform_buffer.handle, TOTAL_CONST_BUFFER_BYTES, nullptr, 0);
157
107 if (device.UseAssemblyShaders()) { 158 if (device.UseAssemblyShaders()) {
108 glCreateBuffers(static_cast<GLsizei>(staging_cbufs.size()), staging_cbufs.data()); 159 glCreateBuffers(static_cast<GLsizei>(staging_cbufs.size()), staging_cbufs.data());
109 for (const GLuint cbuf : staging_cbufs) { 160 for (const GLuint cbuf : staging_cbufs) {
@@ -143,7 +194,7 @@ void RasterizerOpenGL::SetupVertexFormat() {
143 // avoid OpenGL errors. 194 // avoid OpenGL errors.
144 // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't 195 // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
145 // assume every shader uses them all. 196 // assume every shader uses them all.
146 for (std::size_t index = 0; index < NumSupportedVertexAttributes; ++index) { 197 for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) {
147 if (!flags[Dirty::VertexFormat0 + index]) { 198 if (!flags[Dirty::VertexFormat0 + index]) {
148 continue; 199 continue;
149 } 200 }
@@ -162,9 +213,10 @@ void RasterizerOpenGL::SetupVertexFormat() {
162 if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt || 213 if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt ||
163 attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) { 214 attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) {
164 glVertexAttribIFormat(gl_index, attrib.ComponentCount(), 215 glVertexAttribIFormat(gl_index, attrib.ComponentCount(),
165 MaxwellToGL::VertexType(attrib), attrib.offset); 216 MaxwellToGL::VertexFormat(attrib), attrib.offset);
166 } else { 217 } else {
167 glVertexAttribFormat(gl_index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), 218 glVertexAttribFormat(gl_index, attrib.ComponentCount(),
219 MaxwellToGL::VertexFormat(attrib),
168 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); 220 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
169 } 221 }
170 glVertexAttribBinding(gl_index, attrib.buffer); 222 glVertexAttribBinding(gl_index, attrib.buffer);
@@ -181,9 +233,11 @@ void RasterizerOpenGL::SetupVertexBuffer() {
181 233
182 MICROPROFILE_SCOPE(OpenGL_VB); 234 MICROPROFILE_SCOPE(OpenGL_VB);
183 235
236 const bool use_unified_memory = device.HasVertexBufferUnifiedMemory();
237
184 // Upload all guest vertex arrays sequentially to our buffer 238 // Upload all guest vertex arrays sequentially to our buffer
185 const auto& regs = gpu.regs; 239 const auto& regs = gpu.regs;
186 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 240 for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_BINDINGS; ++index) {
187 if (!flags[Dirty::VertexBuffer0 + index]) { 241 if (!flags[Dirty::VertexBuffer0 + index]) {
188 continue; 242 continue;
189 } 243 }
@@ -196,16 +250,25 @@ void RasterizerOpenGL::SetupVertexBuffer() {
196 250
197 const GPUVAddr start = vertex_array.StartAddress(); 251 const GPUVAddr start = vertex_array.StartAddress();
198 const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); 252 const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
199
200 ASSERT(end >= start); 253 ASSERT(end >= start);
254
255 const GLuint gl_index = static_cast<GLuint>(index);
201 const u64 size = end - start; 256 const u64 size = end - start;
202 if (size == 0) { 257 if (size == 0) {
203 glBindVertexBuffer(static_cast<GLuint>(index), 0, 0, vertex_array.stride); 258 glBindVertexBuffer(gl_index, 0, 0, vertex_array.stride);
259 if (use_unified_memory) {
260 glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, gl_index, 0, 0);
261 }
204 continue; 262 continue;
205 } 263 }
206 const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size); 264 const auto info = buffer_cache.UploadMemory(start, size);
207 glBindVertexBuffer(static_cast<GLuint>(index), vertex_buffer, vertex_buffer_offset, 265 if (use_unified_memory) {
208 vertex_array.stride); 266 glBindVertexBuffer(gl_index, 0, 0, vertex_array.stride);
267 glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, gl_index,
268 info.address + info.offset, size);
269 } else {
270 glBindVertexBuffer(gl_index, info.handle, info.offset, vertex_array.stride);
271 }
209 } 272 }
210} 273}
211 274
@@ -218,7 +281,7 @@ void RasterizerOpenGL::SetupVertexInstances() {
218 flags[Dirty::VertexInstances] = false; 281 flags[Dirty::VertexInstances] = false;
219 282
220 const auto& regs = gpu.regs; 283 const auto& regs = gpu.regs;
221 for (std::size_t index = 0; index < NumSupportedVertexAttributes; ++index) { 284 for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) {
222 if (!flags[Dirty::VertexInstance0 + index]) { 285 if (!flags[Dirty::VertexInstance0 + index]) {
223 continue; 286 continue;
224 } 287 }
@@ -235,9 +298,9 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() {
235 MICROPROFILE_SCOPE(OpenGL_Index); 298 MICROPROFILE_SCOPE(OpenGL_Index);
236 const auto& regs = system.GPU().Maxwell3D().regs; 299 const auto& regs = system.GPU().Maxwell3D().regs;
237 const std::size_t size = CalculateIndexBufferSize(); 300 const std::size_t size = CalculateIndexBufferSize();
238 const auto [buffer, offset] = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size); 301 const auto info = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size);
239 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); 302 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, info.handle);
240 return offset; 303 return info.offset;
241} 304}
242 305
243void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { 306void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
@@ -273,7 +336,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
273 continue; 336 continue;
274 } 337 }
275 338
276 Shader shader{shader_cache.GetStageProgram(program)}; 339 Shader* const shader = shader_cache.GetStageProgram(program);
277 340
278 if (device.UseAssemblyShaders()) { 341 if (device.UseAssemblyShaders()) {
279 // Check for ARB limitation. We only have 16 SSBOs per context state. To workaround this 342 // Check for ARB limitation. We only have 16 SSBOs per context state. To workaround this
@@ -567,7 +630,16 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
567 (Maxwell::MaxConstBufferSize + device.GetUniformBufferAlignment()); 630 (Maxwell::MaxConstBufferSize + device.GetUniformBufferAlignment());
568 631
569 // Prepare the vertex array. 632 // Prepare the vertex array.
570 buffer_cache.Map(buffer_size); 633 const bool invalidated = buffer_cache.Map(buffer_size);
634
635 if (invalidated) {
636 // When the stream buffer has been invalidated, we have to consider vertex buffers as dirty
637 auto& dirty = gpu.dirty.flags;
638 dirty[Dirty::VertexBuffers] = true;
639 for (int index = Dirty::VertexBuffer0; index <= Dirty::VertexBuffer31; ++index) {
640 dirty[index] = true;
641 }
642 }
571 643
572 // Prepare vertex array format. 644 // Prepare vertex array format.
573 SetupVertexFormat(); 645 SetupVertexFormat();
@@ -584,9 +656,9 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
584 if (!device.UseAssemblyShaders()) { 656 if (!device.UseAssemblyShaders()) {
585 MaxwellUniformData ubo; 657 MaxwellUniformData ubo;
586 ubo.SetFromRegs(gpu); 658 ubo.SetFromRegs(gpu);
587 const auto [buffer, offset] = 659 const auto info =
588 buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment()); 660 buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment());
589 glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, buffer, offset, 661 glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, info.handle, info.offset,
590 static_cast<GLsizeiptr>(sizeof(ubo))); 662 static_cast<GLsizeiptr>(sizeof(ubo)));
591 } 663 }
592 664
@@ -655,10 +727,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
655} 727}
656 728
657void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { 729void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
658 if (device.HasBrokenCompute()) {
659 return;
660 }
661
662 buffer_cache.Acquire(); 730 buffer_cache.Acquire();
663 current_cbuf = 0; 731 current_cbuf = 0;
664 732
@@ -837,7 +905,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
837 return true; 905 return true;
838} 906}
839 907
840void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, const Shader& shader) { 908void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, Shader* shader) {
841 static constexpr std::array PARAMETER_LUT = { 909 static constexpr std::array PARAMETER_LUT = {
842 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, 910 GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV,
843 GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV, GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV, 911 GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV, GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV,
@@ -846,41 +914,62 @@ void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, const Shad
846 MICROPROFILE_SCOPE(OpenGL_UBO); 914 MICROPROFILE_SCOPE(OpenGL_UBO);
847 const auto& stages = system.GPU().Maxwell3D().state.shader_stages; 915 const auto& stages = system.GPU().Maxwell3D().state.shader_stages;
848 const auto& shader_stage = stages[stage_index]; 916 const auto& shader_stage = stages[stage_index];
917 const auto& entries = shader->GetEntries();
918 const bool use_unified = entries.use_unified_uniforms;
919 const std::size_t base_unified_offset = stage_index * NUM_CONST_BUFFERS_BYTES_PER_STAGE;
849 920
850 u32 binding = 921 const auto base_bindings = device.GetBaseBindings(stage_index);
851 device.UseAssemblyShaders() ? 0 : device.GetBaseBindings(stage_index).uniform_buffer; 922 u32 binding = device.UseAssemblyShaders() ? 0 : base_bindings.uniform_buffer;
852 for (const auto& entry : shader->GetEntries().const_buffers) { 923 for (const auto& entry : entries.const_buffers) {
853 const auto& buffer = shader_stage.const_buffers[entry.GetIndex()]; 924 const u32 index = entry.GetIndex();
854 SetupConstBuffer(PARAMETER_LUT[stage_index], binding++, buffer, entry); 925 const auto& buffer = shader_stage.const_buffers[index];
926 SetupConstBuffer(PARAMETER_LUT[stage_index], binding, buffer, entry, use_unified,
927 base_unified_offset + index * Maxwell::MaxConstBufferSize);
928 ++binding;
929 }
930 if (use_unified) {
931 const u32 index = static_cast<u32>(base_bindings.shader_storage_buffer +
932 entries.global_memory_entries.size());
933 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, index, unified_uniform_buffer.handle,
934 base_unified_offset, NUM_CONST_BUFFERS_BYTES_PER_STAGE);
855 } 935 }
856} 936}
857 937
858void RasterizerOpenGL::SetupComputeConstBuffers(const Shader& kernel) { 938void RasterizerOpenGL::SetupComputeConstBuffers(Shader* kernel) {
859 MICROPROFILE_SCOPE(OpenGL_UBO); 939 MICROPROFILE_SCOPE(OpenGL_UBO);
860 const auto& launch_desc = system.GPU().KeplerCompute().launch_description; 940 const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
941 const auto& entries = kernel->GetEntries();
942 const bool use_unified = entries.use_unified_uniforms;
861 943
862 u32 binding = 0; 944 u32 binding = 0;
863 for (const auto& entry : kernel->GetEntries().const_buffers) { 945 for (const auto& entry : entries.const_buffers) {
864 const auto& config = launch_desc.const_buffer_config[entry.GetIndex()]; 946 const auto& config = launch_desc.const_buffer_config[entry.GetIndex()];
865 const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value(); 947 const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value();
866 Tegra::Engines::ConstBufferInfo buffer; 948 Tegra::Engines::ConstBufferInfo buffer;
867 buffer.address = config.Address(); 949 buffer.address = config.Address();
868 buffer.size = config.size; 950 buffer.size = config.size;
869 buffer.enabled = mask[entry.GetIndex()]; 951 buffer.enabled = mask[entry.GetIndex()];
870 SetupConstBuffer(GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV, binding++, buffer, entry); 952 SetupConstBuffer(GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV, binding, buffer, entry,
953 use_unified, entry.GetIndex() * Maxwell::MaxConstBufferSize);
954 ++binding;
955 }
956 if (use_unified) {
957 const GLuint index = static_cast<GLuint>(entries.global_memory_entries.size());
958 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, index, unified_uniform_buffer.handle, 0,
959 NUM_CONST_BUFFERS_BYTES_PER_STAGE);
871 } 960 }
872} 961}
873 962
874void RasterizerOpenGL::SetupConstBuffer(GLenum stage, u32 binding, 963void RasterizerOpenGL::SetupConstBuffer(GLenum stage, u32 binding,
875 const Tegra::Engines::ConstBufferInfo& buffer, 964 const Tegra::Engines::ConstBufferInfo& buffer,
876 const ConstBufferEntry& entry) { 965 const ConstBufferEntry& entry, bool use_unified,
966 std::size_t unified_offset) {
877 if (!buffer.enabled) { 967 if (!buffer.enabled) {
878 // Set values to zero to unbind buffers 968 // Set values to zero to unbind buffers
879 if (device.UseAssemblyShaders()) { 969 if (device.UseAssemblyShaders()) {
880 glBindBufferRangeNV(stage, entry.GetIndex(), 0, 0, 0); 970 glBindBufferRangeNV(stage, entry.GetIndex(), 0, 0, 0);
881 } else { 971 } else {
882 glBindBufferRange(GL_UNIFORM_BUFFER, binding, 972 glBindBufferRange(GL_UNIFORM_BUFFER, binding, 0, 0, sizeof(float));
883 buffer_cache.GetEmptyBuffer(sizeof(float)), 0, sizeof(float));
884 } 973 }
885 return; 974 return;
886 } 975 }
@@ -889,23 +978,33 @@ void RasterizerOpenGL::SetupConstBuffer(GLenum stage, u32 binding,
889 // UBO alignment requirements. 978 // UBO alignment requirements.
890 const std::size_t size = Common::AlignUp(GetConstBufferSize(buffer, entry), sizeof(GLvec4)); 979 const std::size_t size = Common::AlignUp(GetConstBufferSize(buffer, entry), sizeof(GLvec4));
891 980
892 const auto alignment = device.GetUniformBufferAlignment(); 981 const bool fast_upload = !use_unified && device.HasFastBufferSubData();
893 auto [cbuf, offset] = buffer_cache.UploadMemory(buffer.address, size, alignment, false, 982
894 device.HasFastBufferSubData()); 983 const std::size_t alignment = use_unified ? 4 : device.GetUniformBufferAlignment();
895 if (!device.UseAssemblyShaders()) { 984 const GPUVAddr gpu_addr = buffer.address;
896 glBindBufferRange(GL_UNIFORM_BUFFER, binding, cbuf, offset, size); 985 auto info = buffer_cache.UploadMemory(gpu_addr, size, alignment, false, fast_upload);
986
987 if (device.UseAssemblyShaders()) {
988 UNIMPLEMENTED_IF(use_unified);
989 if (info.offset != 0) {
990 const GLuint staging_cbuf = staging_cbufs[current_cbuf++];
991 glCopyNamedBufferSubData(info.handle, staging_cbuf, info.offset, 0, size);
992 info.handle = staging_cbuf;
993 info.offset = 0;
994 }
995 glBindBufferRangeNV(stage, binding, info.handle, info.offset, size);
897 return; 996 return;
898 } 997 }
899 if (offset != 0) { 998
900 const GLuint staging_cbuf = staging_cbufs[current_cbuf++]; 999 if (use_unified) {
901 glCopyNamedBufferSubData(cbuf, staging_cbuf, offset, 0, size); 1000 glCopyNamedBufferSubData(info.handle, unified_uniform_buffer.handle, info.offset,
902 cbuf = staging_cbuf; 1001 unified_offset, size);
903 offset = 0; 1002 } else {
1003 glBindBufferRange(GL_UNIFORM_BUFFER, binding, info.handle, info.offset, size);
904 } 1004 }
905 glBindBufferRangeNV(stage, binding, cbuf, offset, size);
906} 1005}
907 1006
908void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, const Shader& shader) { 1007void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, Shader* shader) {
909 auto& gpu{system.GPU()}; 1008 auto& gpu{system.GPU()};
910 auto& memory_manager{gpu.MemoryManager()}; 1009 auto& memory_manager{gpu.MemoryManager()};
911 const auto cbufs{gpu.Maxwell3D().state.shader_stages[stage_index]}; 1010 const auto cbufs{gpu.Maxwell3D().state.shader_stages[stage_index]};
@@ -920,7 +1019,7 @@ void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, const Shad
920 } 1019 }
921} 1020}
922 1021
923void RasterizerOpenGL::SetupComputeGlobalMemory(const Shader& kernel) { 1022void RasterizerOpenGL::SetupComputeGlobalMemory(Shader* kernel) {
924 auto& gpu{system.GPU()}; 1023 auto& gpu{system.GPU()};
925 auto& memory_manager{gpu.MemoryManager()}; 1024 auto& memory_manager{gpu.MemoryManager()};
926 const auto cbufs{gpu.KeplerCompute().launch_description.const_buffer_config}; 1025 const auto cbufs{gpu.KeplerCompute().launch_description.const_buffer_config};
@@ -937,13 +1036,12 @@ void RasterizerOpenGL::SetupComputeGlobalMemory(const Shader& kernel) {
937void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, 1036void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry,
938 GPUVAddr gpu_addr, std::size_t size) { 1037 GPUVAddr gpu_addr, std::size_t size) {
939 const auto alignment{device.GetShaderStorageBufferAlignment()}; 1038 const auto alignment{device.GetShaderStorageBufferAlignment()};
940 const auto [ssbo, buffer_offset] = 1039 const auto info = buffer_cache.UploadMemory(gpu_addr, size, alignment, entry.is_written);
941 buffer_cache.UploadMemory(gpu_addr, size, alignment, entry.is_written); 1040 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, binding, info.handle, info.offset,
942 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, binding, ssbo, buffer_offset,
943 static_cast<GLsizeiptr>(size)); 1041 static_cast<GLsizeiptr>(size));
944} 1042}
945 1043
946void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader& shader) { 1044void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, Shader* shader) {
947 MICROPROFILE_SCOPE(OpenGL_Texture); 1045 MICROPROFILE_SCOPE(OpenGL_Texture);
948 const auto& maxwell3d = system.GPU().Maxwell3D(); 1046 const auto& maxwell3d = system.GPU().Maxwell3D();
949 u32 binding = device.GetBaseBindings(stage_index).sampler; 1047 u32 binding = device.GetBaseBindings(stage_index).sampler;
@@ -956,7 +1054,7 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader&
956 } 1054 }
957} 1055}
958 1056
959void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) { 1057void RasterizerOpenGL::SetupComputeTextures(Shader* kernel) {
960 MICROPROFILE_SCOPE(OpenGL_Texture); 1058 MICROPROFILE_SCOPE(OpenGL_Texture);
961 const auto& compute = system.GPU().KeplerCompute(); 1059 const auto& compute = system.GPU().KeplerCompute();
962 u32 binding = 0; 1060 u32 binding = 0;
@@ -985,7 +1083,7 @@ void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextu
985 } 1083 }
986} 1084}
987 1085
988void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& shader) { 1086void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, Shader* shader) {
989 const auto& maxwell3d = system.GPU().Maxwell3D(); 1087 const auto& maxwell3d = system.GPU().Maxwell3D();
990 u32 binding = device.GetBaseBindings(stage_index).image; 1088 u32 binding = device.GetBaseBindings(stage_index).image;
991 for (const auto& entry : shader->GetEntries().images) { 1089 for (const auto& entry : shader->GetEntries().images) {
@@ -995,7 +1093,7 @@ void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& sh
995 } 1093 }
996} 1094}
997 1095
998void RasterizerOpenGL::SetupComputeImages(const Shader& shader) { 1096void RasterizerOpenGL::SetupComputeImages(Shader* shader) {
999 const auto& compute = system.GPU().KeplerCompute(); 1097 const auto& compute = system.GPU().KeplerCompute();
1000 u32 binding = 0; 1098 u32 binding = 0;
1001 for (const auto& entry : shader->GetEntries().images) { 1099 for (const auto& entry : shader->GetEntries().images) {
@@ -1024,6 +1122,26 @@ void RasterizerOpenGL::SyncViewport() {
1024 const auto& regs = gpu.regs; 1122 const auto& regs = gpu.regs;
1025 1123
1026 const bool dirty_viewport = flags[Dirty::Viewports]; 1124 const bool dirty_viewport = flags[Dirty::Viewports];
1125 const bool dirty_clip_control = flags[Dirty::ClipControl];
1126
1127 if (dirty_clip_control || flags[Dirty::FrontFace]) {
1128 flags[Dirty::FrontFace] = false;
1129
1130 GLenum mode = MaxwellToGL::FrontFace(regs.front_face);
1131 if (regs.screen_y_control.triangle_rast_flip != 0 &&
1132 regs.viewport_transform[0].scale_y < 0.0f) {
1133 switch (mode) {
1134 case GL_CW:
1135 mode = GL_CCW;
1136 break;
1137 case GL_CCW:
1138 mode = GL_CW;
1139 break;
1140 }
1141 }
1142 glFrontFace(mode);
1143 }
1144
1027 if (dirty_viewport || flags[Dirty::ClipControl]) { 1145 if (dirty_viewport || flags[Dirty::ClipControl]) {
1028 flags[Dirty::ClipControl] = false; 1146 flags[Dirty::ClipControl] = false;
1029 1147
@@ -1121,11 +1239,6 @@ void RasterizerOpenGL::SyncCullMode() {
1121 glDisable(GL_CULL_FACE); 1239 glDisable(GL_CULL_FACE);
1122 } 1240 }
1123 } 1241 }
1124
1125 if (flags[Dirty::FrontFace]) {
1126 flags[Dirty::FrontFace] = false;
1127 glFrontFace(MaxwellToGL::FrontFace(regs.front_face));
1128 }
1129} 1242}
1130 1243
1131void RasterizerOpenGL::SyncPrimitiveRestart() { 1244void RasterizerOpenGL::SyncPrimitiveRestart() {
@@ -1496,12 +1609,70 @@ void RasterizerOpenGL::SyncFramebufferSRGB() {
1496 oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb); 1609 oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb);
1497} 1610}
1498 1611
1612void RasterizerOpenGL::SyncTransformFeedback() {
1613 // TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
1614 // when this is required.
1615 const auto& regs = system.GPU().Maxwell3D().regs;
1616
1617 static constexpr std::size_t STRIDE = 3;
1618 std::array<GLint, 128 * STRIDE * Maxwell::NumTransformFeedbackBuffers> attribs;
1619 std::array<GLint, Maxwell::NumTransformFeedbackBuffers> streams;
1620
1621 GLint* cursor = attribs.data();
1622 GLint* current_stream = streams.data();
1623
1624 for (std::size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
1625 const auto& layout = regs.tfb_layouts[feedback];
1626 UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding");
1627 if (layout.varying_count == 0) {
1628 continue;
1629 }
1630
1631 *current_stream = static_cast<GLint>(feedback);
1632 if (current_stream != streams.data()) {
1633 // When stepping one stream, push the expected token
1634 cursor[0] = GL_NEXT_BUFFER_NV;
1635 cursor[1] = 0;
1636 cursor[2] = 0;
1637 cursor += STRIDE;
1638 }
1639 ++current_stream;
1640
1641 const auto& locations = regs.tfb_varying_locs[feedback];
1642 std::optional<u8> current_index;
1643 for (u32 offset = 0; offset < layout.varying_count; ++offset) {
1644 const u8 location = locations[offset];
1645 const u8 index = location / 4;
1646
1647 if (current_index == index) {
1648 // Increase number of components of the previous attachment
1649 ++cursor[-2];
1650 continue;
1651 }
1652 current_index = index;
1653
1654 std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(location);
1655 cursor[1] = 1;
1656 cursor += STRIDE;
1657 }
1658 }
1659
1660 const GLsizei num_attribs = static_cast<GLsizei>((cursor - attribs.data()) / STRIDE);
1661 const GLsizei num_strides = static_cast<GLsizei>(current_stream - streams.data());
1662 glTransformFeedbackStreamAttribsNV(num_attribs, attribs.data(), num_strides, streams.data(),
1663 GL_INTERLEAVED_ATTRIBS);
1664}
1665
1499void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) { 1666void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
1500 const auto& regs = system.GPU().Maxwell3D().regs; 1667 const auto& regs = system.GPU().Maxwell3D().regs;
1501 if (regs.tfb_enabled == 0) { 1668 if (regs.tfb_enabled == 0) {
1502 return; 1669 return;
1503 } 1670 }
1504 1671
1672 if (device.UseAssemblyShaders()) {
1673 SyncTransformFeedback();
1674 }
1675
1505 UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || 1676 UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) ||
1506 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || 1677 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) ||
1507 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry)); 1678 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry));
@@ -1528,6 +1699,10 @@ void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
1528 static_cast<GLsizeiptr>(size)); 1699 static_cast<GLsizeiptr>(size));
1529 } 1700 }
1530 1701
1702 // We may have to call BeginTransformFeedbackNV here since they seem to call different
1703 // implementations on Nvidia's driver (the pointer is different) but we are using
1704 // ARB_transform_feedback3 features with NV_transform_feedback interactions and the ARB
1705 // extension doesn't define BeginTransformFeedback (without NV) interactions. It just works.
1531 glBeginTransformFeedback(GL_POINTS); 1706 glBeginTransformFeedback(GL_POINTS);
1532} 1707}
1533 1708
@@ -1549,8 +1724,9 @@ void RasterizerOpenGL::EndTransformFeedback() {
1549 const GLuint handle = transform_feedback_buffers[index].handle; 1724 const GLuint handle = transform_feedback_buffers[index].handle;
1550 const GPUVAddr gpu_addr = binding.Address(); 1725 const GPUVAddr gpu_addr = binding.Address();
1551 const std::size_t size = binding.buffer_size; 1726 const std::size_t size = binding.buffer_size;
1552 const auto [dest_buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true); 1727 const auto info = buffer_cache.UploadMemory(gpu_addr, size, 4, true);
1553 glCopyNamedBufferSubData(handle, dest_buffer, 0, offset, static_cast<GLsizeiptr>(size)); 1728 glCopyNamedBufferSubData(handle, info.handle, 0, info.offset,
1729 static_cast<GLsizeiptr>(size));
1554 } 1730 }
1555} 1731}
1556 1732
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 87f7fe159..4f082592f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -19,7 +19,6 @@
19#include "video_core/engines/const_buffer_info.h" 19#include "video_core/engines/const_buffer_info.h"
20#include "video_core/engines/maxwell_3d.h" 20#include "video_core/engines/maxwell_3d.h"
21#include "video_core/rasterizer_accelerated.h" 21#include "video_core/rasterizer_accelerated.h"
22#include "video_core/rasterizer_cache.h"
23#include "video_core/rasterizer_interface.h" 22#include "video_core/rasterizer_interface.h"
24#include "video_core/renderer_opengl/gl_buffer_cache.h" 23#include "video_core/renderer_opengl/gl_buffer_cache.h"
25#include "video_core/renderer_opengl/gl_device.h" 24#include "video_core/renderer_opengl/gl_device.h"
@@ -100,40 +99,41 @@ private:
100 void ConfigureClearFramebuffer(bool using_color, bool using_depth_stencil); 99 void ConfigureClearFramebuffer(bool using_color, bool using_depth_stencil);
101 100
102 /// Configures the current constbuffers to use for the draw command. 101 /// Configures the current constbuffers to use for the draw command.
103 void SetupDrawConstBuffers(std::size_t stage_index, const Shader& shader); 102 void SetupDrawConstBuffers(std::size_t stage_index, Shader* shader);
104 103
105 /// Configures the current constbuffers to use for the kernel invocation. 104 /// Configures the current constbuffers to use for the kernel invocation.
106 void SetupComputeConstBuffers(const Shader& kernel); 105 void SetupComputeConstBuffers(Shader* kernel);
107 106
108 /// Configures a constant buffer. 107 /// Configures a constant buffer.
109 void SetupConstBuffer(GLenum stage, u32 binding, const Tegra::Engines::ConstBufferInfo& buffer, 108 void SetupConstBuffer(GLenum stage, u32 binding, const Tegra::Engines::ConstBufferInfo& buffer,
110 const ConstBufferEntry& entry); 109 const ConstBufferEntry& entry, bool use_unified,
110 std::size_t unified_offset);
111 111
112 /// Configures the current global memory entries to use for the draw command. 112 /// Configures the current global memory entries to use for the draw command.
113 void SetupDrawGlobalMemory(std::size_t stage_index, const Shader& shader); 113 void SetupDrawGlobalMemory(std::size_t stage_index, Shader* shader);
114 114
115 /// Configures the current global memory entries to use for the kernel invocation. 115 /// Configures the current global memory entries to use for the kernel invocation.
116 void SetupComputeGlobalMemory(const Shader& kernel); 116 void SetupComputeGlobalMemory(Shader* kernel);
117 117
118 /// Configures a constant buffer. 118 /// Configures a constant buffer.
119 void SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, GPUVAddr gpu_addr, 119 void SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
120 std::size_t size); 120 std::size_t size);
121 121
122 /// Configures the current textures to use for the draw command. 122 /// Configures the current textures to use for the draw command.
123 void SetupDrawTextures(std::size_t stage_index, const Shader& shader); 123 void SetupDrawTextures(std::size_t stage_index, Shader* shader);
124 124
125 /// Configures the textures used in a compute shader. 125 /// Configures the textures used in a compute shader.
126 void SetupComputeTextures(const Shader& kernel); 126 void SetupComputeTextures(Shader* kernel);
127 127
128 /// Configures a texture. 128 /// Configures a texture.
129 void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture, 129 void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
130 const SamplerEntry& entry); 130 const SamplerEntry& entry);
131 131
132 /// Configures images in a graphics shader. 132 /// Configures images in a graphics shader.
133 void SetupDrawImages(std::size_t stage_index, const Shader& shader); 133 void SetupDrawImages(std::size_t stage_index, Shader* shader);
134 134
135 /// Configures images in a compute shader. 135 /// Configures images in a compute shader.
136 void SetupComputeImages(const Shader& shader); 136 void SetupComputeImages(Shader* shader);
137 137
138 /// Configures an image. 138 /// Configures an image.
139 void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic, const ImageEntry& entry); 139 void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic, const ImageEntry& entry);
@@ -201,6 +201,10 @@ private:
201 /// Syncs the framebuffer sRGB state to match the guest state 201 /// Syncs the framebuffer sRGB state to match the guest state
202 void SyncFramebufferSRGB(); 202 void SyncFramebufferSRGB();
203 203
204 /// Syncs transform feedback state to match guest state
205 /// @note Only valid on assembly shaders
206 void SyncTransformFeedback();
207
204 /// Begin a transform feedback 208 /// Begin a transform feedback
205 void BeginTransformFeedback(GLenum primitive_mode); 209 void BeginTransformFeedback(GLenum primitive_mode);
206 210
@@ -253,6 +257,7 @@ private:
253 Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; 257 Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram;
254 std::array<GLuint, NUM_CONSTANT_BUFFERS> staging_cbufs{}; 258 std::array<GLuint, NUM_CONSTANT_BUFFERS> staging_cbufs{};
255 std::size_t current_cbuf = 0; 259 std::size_t current_cbuf = 0;
260 OGLBuffer unified_uniform_buffer;
256 261
257 /// Number of commands queued to the OpenGL driver. Reseted on flush. 262 /// Number of commands queued to the OpenGL driver. Reseted on flush.
258 std::size_t num_queued_commands = 0; 263 std::size_t num_queued_commands = 0;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 4cd0f36cf..c6a3bf3a1 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -20,6 +20,7 @@
20#include "video_core/engines/maxwell_3d.h" 20#include "video_core/engines/maxwell_3d.h"
21#include "video_core/engines/shader_type.h" 21#include "video_core/engines/shader_type.h"
22#include "video_core/memory_manager.h" 22#include "video_core/memory_manager.h"
23#include "video_core/renderer_opengl/gl_arb_decompiler.h"
23#include "video_core/renderer_opengl/gl_rasterizer.h" 24#include "video_core/renderer_opengl/gl_rasterizer.h"
24#include "video_core/renderer_opengl/gl_shader_cache.h" 25#include "video_core/renderer_opengl/gl_shader_cache.h"
25#include "video_core/renderer_opengl/gl_shader_decompiler.h" 26#include "video_core/renderer_opengl/gl_shader_decompiler.h"
@@ -29,6 +30,7 @@
29#include "video_core/shader/memory_util.h" 30#include "video_core/shader/memory_util.h"
30#include "video_core/shader/registry.h" 31#include "video_core/shader/registry.h"
31#include "video_core/shader/shader_ir.h" 32#include "video_core/shader/shader_ir.h"
33#include "video_core/shader_cache.h"
32 34
33namespace OpenGL { 35namespace OpenGL {
34 36
@@ -147,7 +149,8 @@ ProgramSharedPtr BuildShader(const Device& device, ShaderType shader_type, u64 u
147 auto program = std::make_shared<ProgramHandle>(); 149 auto program = std::make_shared<ProgramHandle>();
148 150
149 if (device.UseAssemblyShaders()) { 151 if (device.UseAssemblyShaders()) {
150 const std::string arb = "Not implemented"; 152 const std::string arb =
153 DecompileAssemblyShader(device, ir, registry, shader_type, shader_id);
151 154
152 GLuint& arb_prog = program->assembly_program.handle; 155 GLuint& arb_prog = program->assembly_program.handle;
153 156
@@ -194,12 +197,9 @@ std::unordered_set<GLenum> GetSupportedFormats() {
194 197
195} // Anonymous namespace 198} // Anonymous namespace
196 199
197CachedShader::CachedShader(VAddr cpu_addr, std::size_t size_in_bytes, 200Shader::Shader(std::shared_ptr<VideoCommon::Shader::Registry> registry_, ShaderEntries entries_,
198 std::shared_ptr<VideoCommon::Shader::Registry> registry, 201 ProgramSharedPtr program_)
199 ShaderEntries entries, ProgramSharedPtr program_) 202 : registry{std::move(registry_)}, entries{std::move(entries_)}, program{std::move(program_)} {
200 : RasterizerCacheObject{cpu_addr}, registry{std::move(registry)}, entries{std::move(entries)},
201 size_in_bytes{size_in_bytes}, program{std::move(program_)} {
202 // Assign either the assembly program or source program. We can't have both.
203 handle = program->assembly_program.handle; 203 handle = program->assembly_program.handle;
204 if (handle == 0) { 204 if (handle == 0) {
205 handle = program->source_program.handle; 205 handle = program->source_program.handle;
@@ -207,16 +207,16 @@ CachedShader::CachedShader(VAddr cpu_addr, std::size_t size_in_bytes,
207 ASSERT(handle != 0); 207 ASSERT(handle != 0);
208} 208}
209 209
210CachedShader::~CachedShader() = default; 210Shader::~Shader() = default;
211 211
212GLuint CachedShader::GetHandle() const { 212GLuint Shader::GetHandle() const {
213 DEBUG_ASSERT(registry->IsConsistent()); 213 DEBUG_ASSERT(registry->IsConsistent());
214 return handle; 214 return handle;
215} 215}
216 216
217Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params, 217std::unique_ptr<Shader> Shader::CreateStageFromMemory(const ShaderParameters& params,
218 Maxwell::ShaderProgram program_type, ProgramCode code, 218 Maxwell::ShaderProgram program_type,
219 ProgramCode code_b) { 219 ProgramCode code, ProgramCode code_b) {
220 const auto shader_type = GetShaderType(program_type); 220 const auto shader_type = GetShaderType(program_type);
221 const std::size_t size_in_bytes = code.size() * sizeof(u64); 221 const std::size_t size_in_bytes = code.size() * sizeof(u64);
222 222
@@ -241,11 +241,12 @@ Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params,
241 entry.bindless_samplers = registry->GetBindlessSamplers(); 241 entry.bindless_samplers = registry->GetBindlessSamplers();
242 params.disk_cache.SaveEntry(std::move(entry)); 242 params.disk_cache.SaveEntry(std::move(entry));
243 243
244 return std::shared_ptr<CachedShader>(new CachedShader( 244 return std::unique_ptr<Shader>(new Shader(
245 params.cpu_addr, size_in_bytes, std::move(registry), MakeEntries(ir), std::move(program))); 245 std::move(registry), MakeEntries(params.device, ir, shader_type), std::move(program)));
246} 246}
247 247
248Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code) { 248std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& params,
249 ProgramCode code) {
249 const std::size_t size_in_bytes = code.size() * sizeof(u64); 250 const std::size_t size_in_bytes = code.size() * sizeof(u64);
250 251
251 auto& engine = params.system.GPU().KeplerCompute(); 252 auto& engine = params.system.GPU().KeplerCompute();
@@ -265,22 +266,23 @@ Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, Prog
265 entry.bindless_samplers = registry->GetBindlessSamplers(); 266 entry.bindless_samplers = registry->GetBindlessSamplers();
266 params.disk_cache.SaveEntry(std::move(entry)); 267 params.disk_cache.SaveEntry(std::move(entry));
267 268
268 return std::shared_ptr<CachedShader>(new CachedShader( 269 return std::unique_ptr<Shader>(new Shader(std::move(registry),
269 params.cpu_addr, size_in_bytes, std::move(registry), MakeEntries(ir), std::move(program))); 270 MakeEntries(params.device, ir, ShaderType::Compute),
271 std::move(program)));
270} 272}
271 273
272Shader CachedShader::CreateFromCache(const ShaderParameters& params, 274std::unique_ptr<Shader> Shader::CreateFromCache(const ShaderParameters& params,
273 const PrecompiledShader& precompiled_shader, 275 const PrecompiledShader& precompiled_shader) {
274 std::size_t size_in_bytes) { 276 return std::unique_ptr<Shader>(new Shader(
275 return std::shared_ptr<CachedShader>( 277 precompiled_shader.registry, precompiled_shader.entries, precompiled_shader.program));
276 new CachedShader(params.cpu_addr, size_in_bytes, precompiled_shader.registry,
277 precompiled_shader.entries, precompiled_shader.program));
278} 278}
279 279
280ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, 280ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
281 Core::Frontend::EmuWindow& emu_window, const Device& device) 281 Core::Frontend::EmuWindow& emu_window, const Device& device)
282 : RasterizerCache{rasterizer}, system{system}, emu_window{emu_window}, device{device}, 282 : VideoCommon::ShaderCache<Shader>{rasterizer}, system{system},
283 disk_cache{system} {} 283 emu_window{emu_window}, device{device}, disk_cache{system} {}
284
285ShaderCacheOpenGL::~ShaderCacheOpenGL() = default;
284 286
285void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, 287void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
286 const VideoCore::DiskResourceLoadCallback& callback) { 288 const VideoCore::DiskResourceLoadCallback& callback) {
@@ -348,7 +350,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
348 PrecompiledShader shader; 350 PrecompiledShader shader;
349 shader.program = std::move(program); 351 shader.program = std::move(program);
350 shader.registry = std::move(registry); 352 shader.registry = std::move(registry);
351 shader.entries = MakeEntries(ir); 353 shader.entries = MakeEntries(device, ir, entry.type);
352 354
353 std::scoped_lock lock{mutex}; 355 std::scoped_lock lock{mutex};
354 if (callback) { 356 if (callback) {
@@ -434,7 +436,7 @@ ProgramSharedPtr ShaderCacheOpenGL::GeneratePrecompiledProgram(
434 return program; 436 return program;
435} 437}
436 438
437Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { 439Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
438 if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) { 440 if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) {
439 return last_shaders[static_cast<std::size_t>(program)]; 441 return last_shaders[static_cast<std::size_t>(program)];
440 } 442 }
@@ -444,8 +446,7 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
444 446
445 // Look up shader in the cache based on address 447 // Look up shader in the cache based on address
446 const auto cpu_addr{memory_manager.GpuToCpuAddress(address)}; 448 const auto cpu_addr{memory_manager.GpuToCpuAddress(address)};
447 Shader shader{cpu_addr ? TryGet(*cpu_addr) : null_shader}; 449 if (Shader* const shader{cpu_addr ? TryGet(*cpu_addr) : null_shader.get()}) {
448 if (shader) {
449 return last_shaders[static_cast<std::size_t>(program)] = shader; 450 return last_shaders[static_cast<std::size_t>(program)] = shader;
450 } 451 }
451 452
@@ -459,62 +460,64 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
459 const u8* host_ptr_b = memory_manager.GetPointer(address_b); 460 const u8* host_ptr_b = memory_manager.GetPointer(address_b);
460 code_b = GetShaderCode(memory_manager, address_b, host_ptr_b, false); 461 code_b = GetShaderCode(memory_manager, address_b, host_ptr_b, false);
461 } 462 }
463 const std::size_t code_size = code.size() * sizeof(u64);
462 464
463 const auto unique_identifier = GetUniqueIdentifier( 465 const u64 unique_identifier = GetUniqueIdentifier(
464 GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b); 466 GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b);
465 467
466 const ShaderParameters params{system, disk_cache, device, 468 const ShaderParameters params{system, disk_cache, device,
467 *cpu_addr, host_ptr, unique_identifier}; 469 *cpu_addr, host_ptr, unique_identifier};
468 470
471 std::unique_ptr<Shader> shader;
469 const auto found = runtime_cache.find(unique_identifier); 472 const auto found = runtime_cache.find(unique_identifier);
470 if (found == runtime_cache.end()) { 473 if (found == runtime_cache.end()) {
471 shader = CachedShader::CreateStageFromMemory(params, program, std::move(code), 474 shader = Shader::CreateStageFromMemory(params, program, std::move(code), std::move(code_b));
472 std::move(code_b));
473 } else { 475 } else {
474 const std::size_t size_in_bytes = code.size() * sizeof(u64); 476 shader = Shader::CreateFromCache(params, found->second);
475 shader = CachedShader::CreateFromCache(params, found->second, size_in_bytes);
476 } 477 }
477 478
479 Shader* const result = shader.get();
478 if (cpu_addr) { 480 if (cpu_addr) {
479 Register(shader); 481 Register(std::move(shader), *cpu_addr, code_size);
480 } else { 482 } else {
481 null_shader = shader; 483 null_shader = std::move(shader);
482 } 484 }
483 485
484 return last_shaders[static_cast<std::size_t>(program)] = shader; 486 return last_shaders[static_cast<std::size_t>(program)] = result;
485} 487}
486 488
487Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) { 489Shader* ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) {
488 auto& memory_manager{system.GPU().MemoryManager()}; 490 auto& memory_manager{system.GPU().MemoryManager()};
489 const auto cpu_addr{memory_manager.GpuToCpuAddress(code_addr)}; 491 const auto cpu_addr{memory_manager.GpuToCpuAddress(code_addr)};
490 492
491 auto kernel = cpu_addr ? TryGet(*cpu_addr) : null_kernel; 493 if (Shader* const kernel = cpu_addr ? TryGet(*cpu_addr) : null_kernel.get()) {
492 if (kernel) {
493 return kernel; 494 return kernel;
494 } 495 }
495 496
496 const auto host_ptr{memory_manager.GetPointer(code_addr)}; 497 const auto host_ptr{memory_manager.GetPointer(code_addr)};
497 // No kernel found, create a new one 498 // No kernel found, create a new one
498 auto code{GetShaderCode(memory_manager, code_addr, host_ptr, true)}; 499 ProgramCode code{GetShaderCode(memory_manager, code_addr, host_ptr, true)};
499 const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)}; 500 const std::size_t code_size{code.size() * sizeof(u64)};
501 const u64 unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)};
500 502
501 const ShaderParameters params{system, disk_cache, device, 503 const ShaderParameters params{system, disk_cache, device,
502 *cpu_addr, host_ptr, unique_identifier}; 504 *cpu_addr, host_ptr, unique_identifier};
503 505
506 std::unique_ptr<Shader> kernel;
504 const auto found = runtime_cache.find(unique_identifier); 507 const auto found = runtime_cache.find(unique_identifier);
505 if (found == runtime_cache.end()) { 508 if (found == runtime_cache.end()) {
506 kernel = CachedShader::CreateKernelFromMemory(params, std::move(code)); 509 kernel = Shader::CreateKernelFromMemory(params, std::move(code));
507 } else { 510 } else {
508 const std::size_t size_in_bytes = code.size() * sizeof(u64); 511 kernel = Shader::CreateFromCache(params, found->second);
509 kernel = CachedShader::CreateFromCache(params, found->second, size_in_bytes);
510 } 512 }
511 513
514 Shader* const result = kernel.get();
512 if (cpu_addr) { 515 if (cpu_addr) {
513 Register(kernel); 516 Register(std::move(kernel), *cpu_addr, code_size);
514 } else { 517 } else {
515 null_kernel = kernel; 518 null_kernel = std::move(kernel);
516 } 519 }
517 return kernel; 520 return result;
518} 521}
519 522
520} // namespace OpenGL 523} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index b2ae8d7f9..994aaeaf2 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -18,12 +18,12 @@
18 18
19#include "common/common_types.h" 19#include "common/common_types.h"
20#include "video_core/engines/shader_type.h" 20#include "video_core/engines/shader_type.h"
21#include "video_core/rasterizer_cache.h"
22#include "video_core/renderer_opengl/gl_resource_manager.h" 21#include "video_core/renderer_opengl/gl_resource_manager.h"
23#include "video_core/renderer_opengl/gl_shader_decompiler.h" 22#include "video_core/renderer_opengl/gl_shader_decompiler.h"
24#include "video_core/renderer_opengl/gl_shader_disk_cache.h" 23#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
25#include "video_core/shader/registry.h" 24#include "video_core/shader/registry.h"
26#include "video_core/shader/shader_ir.h" 25#include "video_core/shader/shader_ir.h"
26#include "video_core/shader_cache.h"
27 27
28namespace Core { 28namespace Core {
29class System; 29class System;
@@ -35,12 +35,9 @@ class EmuWindow;
35 35
36namespace OpenGL { 36namespace OpenGL {
37 37
38class CachedShader;
39class Device; 38class Device;
40class RasterizerOpenGL; 39class RasterizerOpenGL;
41struct UnspecializedShader;
42 40
43using Shader = std::shared_ptr<CachedShader>;
44using Maxwell = Tegra::Engines::Maxwell3D::Regs; 41using Maxwell = Tegra::Engines::Maxwell3D::Regs;
45 42
46struct ProgramHandle { 43struct ProgramHandle {
@@ -64,62 +61,53 @@ struct ShaderParameters {
64 u64 unique_identifier; 61 u64 unique_identifier;
65}; 62};
66 63
67class CachedShader final : public RasterizerCacheObject { 64class Shader final {
68public: 65public:
69 ~CachedShader(); 66 ~Shader();
70 67
71 /// Gets the GL program handle for the shader 68 /// Gets the GL program handle for the shader
72 GLuint GetHandle() const; 69 GLuint GetHandle() const;
73 70
74 /// Returns the size in bytes of the shader
75 std::size_t GetSizeInBytes() const override {
76 return size_in_bytes;
77 }
78
79 /// Gets the shader entries for the shader 71 /// Gets the shader entries for the shader
80 const ShaderEntries& GetEntries() const { 72 const ShaderEntries& GetEntries() const {
81 return entries; 73 return entries;
82 } 74 }
83 75
84 static Shader CreateStageFromMemory(const ShaderParameters& params, 76 static std::unique_ptr<Shader> CreateStageFromMemory(const ShaderParameters& params,
85 Maxwell::ShaderProgram program_type, 77 Maxwell::ShaderProgram program_type,
86 ProgramCode program_code, ProgramCode program_code_b); 78 ProgramCode program_code,
87 static Shader CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code); 79 ProgramCode program_code_b);
80 static std::unique_ptr<Shader> CreateKernelFromMemory(const ShaderParameters& params,
81 ProgramCode code);
88 82
89 static Shader CreateFromCache(const ShaderParameters& params, 83 static std::unique_ptr<Shader> CreateFromCache(const ShaderParameters& params,
90 const PrecompiledShader& precompiled_shader, 84 const PrecompiledShader& precompiled_shader);
91 std::size_t size_in_bytes);
92 85
93private: 86private:
94 explicit CachedShader(VAddr cpu_addr, std::size_t size_in_bytes, 87 explicit Shader(std::shared_ptr<VideoCommon::Shader::Registry> registry, ShaderEntries entries,
95 std::shared_ptr<VideoCommon::Shader::Registry> registry, 88 ProgramSharedPtr program);
96 ShaderEntries entries, ProgramSharedPtr program);
97 89
98 std::shared_ptr<VideoCommon::Shader::Registry> registry; 90 std::shared_ptr<VideoCommon::Shader::Registry> registry;
99 ShaderEntries entries; 91 ShaderEntries entries;
100 std::size_t size_in_bytes = 0;
101 ProgramSharedPtr program; 92 ProgramSharedPtr program;
102 GLuint handle = 0; 93 GLuint handle = 0;
103}; 94};
104 95
105class ShaderCacheOpenGL final : public RasterizerCache<Shader> { 96class ShaderCacheOpenGL final : public VideoCommon::ShaderCache<Shader> {
106public: 97public:
107 explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, 98 explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
108 Core::Frontend::EmuWindow& emu_window, const Device& device); 99 Core::Frontend::EmuWindow& emu_window, const Device& device);
100 ~ShaderCacheOpenGL() override;
109 101
110 /// Loads disk cache for the current game 102 /// Loads disk cache for the current game
111 void LoadDiskCache(const std::atomic_bool& stop_loading, 103 void LoadDiskCache(const std::atomic_bool& stop_loading,
112 const VideoCore::DiskResourceLoadCallback& callback); 104 const VideoCore::DiskResourceLoadCallback& callback);
113 105
114 /// Gets the current specified shader stage program 106 /// Gets the current specified shader stage program
115 Shader GetStageProgram(Maxwell::ShaderProgram program); 107 Shader* GetStageProgram(Maxwell::ShaderProgram program);
116 108
117 /// Gets a compute kernel in the passed address 109 /// Gets a compute kernel in the passed address
118 Shader GetComputeKernel(GPUVAddr code_addr); 110 Shader* GetComputeKernel(GPUVAddr code_addr);
119
120protected:
121 // We do not have to flush this cache as things in it are never modified by us.
122 void FlushObjectInner(const Shader& object) override {}
123 111
124private: 112private:
125 ProgramSharedPtr GeneratePrecompiledProgram( 113 ProgramSharedPtr GeneratePrecompiledProgram(
@@ -132,10 +120,10 @@ private:
132 ShaderDiskCacheOpenGL disk_cache; 120 ShaderDiskCacheOpenGL disk_cache;
133 std::unordered_map<u64, PrecompiledShader> runtime_cache; 121 std::unordered_map<u64, PrecompiledShader> runtime_cache;
134 122
135 Shader null_shader{}; 123 std::unique_ptr<Shader> null_shader;
136 Shader null_kernel{}; 124 std::unique_ptr<Shader> null_kernel;
137 125
138 std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; 126 std::array<Shader*, Maxwell::MaxShaderProgram> last_shaders{};
139}; 127};
140 128
141} // namespace OpenGL 129} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 9cb115959..2c49aeaac 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -37,6 +37,7 @@ using Tegra::Shader::IpaMode;
37using Tegra::Shader::IpaSampleMode; 37using Tegra::Shader::IpaSampleMode;
38using Tegra::Shader::PixelImap; 38using Tegra::Shader::PixelImap;
39using Tegra::Shader::Register; 39using Tegra::Shader::Register;
40using Tegra::Shader::TextureType;
40using VideoCommon::Shader::BuildTransformFeedback; 41using VideoCommon::Shader::BuildTransformFeedback;
41using VideoCommon::Shader::Registry; 42using VideoCommon::Shader::Registry;
42 43
@@ -61,8 +62,8 @@ struct TextureDerivates {};
61using TextureArgument = std::pair<Type, Node>; 62using TextureArgument = std::pair<Type, Node>;
62using TextureIR = std::variant<TextureOffset, TextureDerivates, TextureArgument>; 63using TextureIR = std::variant<TextureOffset, TextureDerivates, TextureArgument>;
63 64
64constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 65constexpr u32 MAX_CONSTBUFFER_SCALARS = static_cast<u32>(Maxwell::MaxConstBufferSize) / sizeof(u32);
65 static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float)); 66constexpr u32 MAX_CONSTBUFFER_ELEMENTS = MAX_CONSTBUFFER_SCALARS / sizeof(u32);
66 67
67constexpr std::string_view CommonDeclarations = R"(#define ftoi floatBitsToInt 68constexpr std::string_view CommonDeclarations = R"(#define ftoi floatBitsToInt
68#define ftou floatBitsToUint 69#define ftou floatBitsToUint
@@ -402,6 +403,13 @@ std::string FlowStackTopName(MetaStackClass stack) {
402 return fmt::format("{}_flow_stack_top", GetFlowStackPrefix(stack)); 403 return fmt::format("{}_flow_stack_top", GetFlowStackPrefix(stack));
403} 404}
404 405
406bool UseUnifiedUniforms(const Device& device, const ShaderIR& ir, ShaderType stage) {
407 const u32 num_ubos = static_cast<u32>(ir.GetConstantBuffers().size());
408 // We waste one UBO for emulation
409 const u32 num_available_ubos = device.GetMaxUniformBuffers(stage) - 1;
410 return num_ubos > num_available_ubos;
411}
412
405struct GenericVaryingDescription { 413struct GenericVaryingDescription {
406 std::string name; 414 std::string name;
407 u8 first_element = 0; 415 u8 first_element = 0;
@@ -412,8 +420,9 @@ class GLSLDecompiler final {
412public: 420public:
413 explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry, 421 explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry,
414 ShaderType stage, std::string_view identifier, std::string_view suffix) 422 ShaderType stage, std::string_view identifier, std::string_view suffix)
415 : device{device}, ir{ir}, registry{registry}, stage{stage}, 423 : device{device}, ir{ir}, registry{registry}, stage{stage}, identifier{identifier},
416 identifier{identifier}, suffix{suffix}, header{ir.GetHeader()} { 424 suffix{suffix}, header{ir.GetHeader()}, use_unified_uniforms{
425 UseUnifiedUniforms(device, ir, stage)} {
417 if (stage != ShaderType::Compute) { 426 if (stage != ShaderType::Compute) {
418 transform_feedback = BuildTransformFeedback(registry.GetGraphicsInfo()); 427 transform_feedback = BuildTransformFeedback(registry.GetGraphicsInfo());
419 } 428 }
@@ -518,6 +527,9 @@ private:
518 if (device.HasImageLoadFormatted()) { 527 if (device.HasImageLoadFormatted()) {
519 code.AddLine("#extension GL_EXT_shader_image_load_formatted : require"); 528 code.AddLine("#extension GL_EXT_shader_image_load_formatted : require");
520 } 529 }
530 if (device.HasTextureShadowLod()) {
531 code.AddLine("#extension GL_EXT_texture_shadow_lod : require");
532 }
521 if (device.HasWarpIntrinsics()) { 533 if (device.HasWarpIntrinsics()) {
522 code.AddLine("#extension GL_NV_gpu_shader5 : require"); 534 code.AddLine("#extension GL_NV_gpu_shader5 : require");
523 code.AddLine("#extension GL_NV_shader_thread_group : require"); 535 code.AddLine("#extension GL_NV_shader_thread_group : require");
@@ -618,7 +630,9 @@ private:
618 break; 630 break;
619 } 631 }
620 } 632 }
621 if (stage != ShaderType::Vertex || device.HasVertexViewportLayer()) { 633
634 if (stage != ShaderType::Geometry &&
635 (stage != ShaderType::Vertex || device.HasVertexViewportLayer())) {
622 if (ir.UsesLayer()) { 636 if (ir.UsesLayer()) {
623 code.AddLine("int gl_Layer;"); 637 code.AddLine("int gl_Layer;");
624 } 638 }
@@ -647,6 +661,16 @@ private:
647 --code.scope; 661 --code.scope;
648 code.AddLine("}};"); 662 code.AddLine("}};");
649 code.AddNewLine(); 663 code.AddNewLine();
664
665 if (stage == ShaderType::Geometry) {
666 if (ir.UsesLayer()) {
667 code.AddLine("out int gl_Layer;");
668 }
669 if (ir.UsesViewportIndex()) {
670 code.AddLine("out int gl_ViewportIndex;");
671 }
672 }
673 code.AddNewLine();
650 } 674 }
651 675
652 void DeclareRegisters() { 676 void DeclareRegisters() {
@@ -834,12 +858,24 @@ private:
834 } 858 }
835 859
836 void DeclareConstantBuffers() { 860 void DeclareConstantBuffers() {
861 if (use_unified_uniforms) {
862 const u32 binding = device.GetBaseBindings(stage).shader_storage_buffer +
863 static_cast<u32>(ir.GetGlobalMemory().size());
864 code.AddLine("layout (std430, binding = {}) readonly buffer UnifiedUniforms {{",
865 binding);
866 code.AddLine(" uint cbufs[];");
867 code.AddLine("}};");
868 code.AddNewLine();
869 return;
870 }
871
837 u32 binding = device.GetBaseBindings(stage).uniform_buffer; 872 u32 binding = device.GetBaseBindings(stage).uniform_buffer;
838 for (const auto& buffers : ir.GetConstantBuffers()) { 873 for (const auto [index, info] : ir.GetConstantBuffers()) {
839 const auto index = buffers.first; 874 const u32 num_elements = Common::AlignUp(info.GetSize(), 4) / 4;
875 const u32 size = info.IsIndirect() ? MAX_CONSTBUFFER_ELEMENTS : num_elements;
840 code.AddLine("layout (std140, binding = {}) uniform {} {{", binding++, 876 code.AddLine("layout (std140, binding = {}) uniform {} {{", binding++,
841 GetConstBufferBlock(index)); 877 GetConstBufferBlock(index));
842 code.AddLine(" uvec4 {}[{}];", GetConstBuffer(index), MAX_CONSTBUFFER_ELEMENTS); 878 code.AddLine(" uvec4 {}[{}];", GetConstBuffer(index), size);
843 code.AddLine("}};"); 879 code.AddLine("}};");
844 code.AddNewLine(); 880 code.AddNewLine();
845 } 881 }
@@ -877,13 +913,13 @@ private:
877 return "samplerBuffer"; 913 return "samplerBuffer";
878 } 914 }
879 switch (sampler.type) { 915 switch (sampler.type) {
880 case Tegra::Shader::TextureType::Texture1D: 916 case TextureType::Texture1D:
881 return "sampler1D"; 917 return "sampler1D";
882 case Tegra::Shader::TextureType::Texture2D: 918 case TextureType::Texture2D:
883 return "sampler2D"; 919 return "sampler2D";
884 case Tegra::Shader::TextureType::Texture3D: 920 case TextureType::Texture3D:
885 return "sampler3D"; 921 return "sampler3D";
886 case Tegra::Shader::TextureType::TextureCube: 922 case TextureType::TextureCube:
887 return "samplerCube"; 923 return "samplerCube";
888 default: 924 default:
889 UNREACHABLE(); 925 UNREACHABLE();
@@ -1038,42 +1074,51 @@ private:
1038 1074
1039 if (const auto cbuf = std::get_if<CbufNode>(&*node)) { 1075 if (const auto cbuf = std::get_if<CbufNode>(&*node)) {
1040 const Node offset = cbuf->GetOffset(); 1076 const Node offset = cbuf->GetOffset();
1077 const u32 base_unified_offset = cbuf->GetIndex() * MAX_CONSTBUFFER_SCALARS;
1078
1041 if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) { 1079 if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
1042 // Direct access 1080 // Direct access
1043 const u32 offset_imm = immediate->GetValue(); 1081 const u32 offset_imm = immediate->GetValue();
1044 ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access"); 1082 ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access");
1045 return {fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), 1083 if (use_unified_uniforms) {
1046 offset_imm / (4 * 4), (offset_imm / 4) % 4), 1084 return {fmt::format("cbufs[{}]", base_unified_offset + offset_imm / 4),
1047 Type::Uint}; 1085 Type::Uint};
1086 } else {
1087 return {fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()),
1088 offset_imm / (4 * 4), (offset_imm / 4) % 4),
1089 Type::Uint};
1090 }
1048 } 1091 }
1049 1092
1050 if (std::holds_alternative<OperationNode>(*offset)) { 1093 // Indirect access
1051 // Indirect access 1094 if (use_unified_uniforms) {
1052 const std::string final_offset = code.GenerateTemporary(); 1095 return {fmt::format("cbufs[{} + ({} >> 2)]", base_unified_offset,
1053 code.AddLine("uint {} = {} >> 2;", final_offset, Visit(offset).AsUint()); 1096 Visit(offset).AsUint()),
1097 Type::Uint};
1098 }
1054 1099
1055 if (!device.HasComponentIndexingBug()) { 1100 const std::string final_offset = code.GenerateTemporary();
1056 return {fmt::format("{}[{} >> 2][{} & 3]", GetConstBuffer(cbuf->GetIndex()), 1101 code.AddLine("uint {} = {} >> 2;", final_offset, Visit(offset).AsUint());
1057 final_offset, final_offset),
1058 Type::Uint};
1059 }
1060 1102
1061 // AMD's proprietary GLSL compiler emits ill code for variable component access. 1103 if (!device.HasComponentIndexingBug()) {
1062 // To bypass this driver bug generate 4 ifs, one per each component. 1104 return {fmt::format("{}[{} >> 2][{} & 3]", GetConstBuffer(cbuf->GetIndex()),
1063 const std::string pack = code.GenerateTemporary(); 1105 final_offset, final_offset),
1064 code.AddLine("uvec4 {} = {}[{} >> 2];", pack, GetConstBuffer(cbuf->GetIndex()), 1106 Type::Uint};
1065 final_offset);
1066
1067 const std::string result = code.GenerateTemporary();
1068 code.AddLine("uint {};", result);
1069 for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
1070 code.AddLine("if (({} & 3) == {}) {} = {}{};", final_offset, swizzle, result,
1071 pack, GetSwizzle(swizzle));
1072 }
1073 return {result, Type::Uint};
1074 } 1107 }
1075 1108
1076 UNREACHABLE_MSG("Unmanaged offset node type"); 1109 // AMD's proprietary GLSL compiler emits ill code for variable component access.
1110 // To bypass this driver bug generate 4 ifs, one per each component.
1111 const std::string pack = code.GenerateTemporary();
1112 code.AddLine("uvec4 {} = {}[{} >> 2];", pack, GetConstBuffer(cbuf->GetIndex()),
1113 final_offset);
1114
1115 const std::string result = code.GenerateTemporary();
1116 code.AddLine("uint {};", result);
1117 for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
1118 code.AddLine("if (({} & 3) == {}) {} = {}{};", final_offset, swizzle, result, pack,
1119 GetSwizzle(swizzle));
1120 }
1121 return {result, Type::Uint};
1077 } 1122 }
1078 1123
1079 if (const auto gmem = std::get_if<GmemNode>(&*node)) { 1124 if (const auto gmem = std::get_if<GmemNode>(&*node)) {
@@ -1339,8 +1384,19 @@ private:
1339 const std::size_t count = operation.GetOperandsCount(); 1384 const std::size_t count = operation.GetOperandsCount();
1340 const bool has_array = meta->sampler.is_array; 1385 const bool has_array = meta->sampler.is_array;
1341 const bool has_shadow = meta->sampler.is_shadow; 1386 const bool has_shadow = meta->sampler.is_shadow;
1387 const bool workaround_lod_array_shadow_as_grad =
1388 !device.HasTextureShadowLod() && function_suffix == "Lod" && meta->sampler.is_shadow &&
1389 ((meta->sampler.type == TextureType::Texture2D && meta->sampler.is_array) ||
1390 meta->sampler.type == TextureType::TextureCube);
1391
1392 std::string expr = "texture";
1393
1394 if (workaround_lod_array_shadow_as_grad) {
1395 expr += "Grad";
1396 } else {
1397 expr += function_suffix;
1398 }
1342 1399
1343 std::string expr = "texture" + function_suffix;
1344 if (!meta->aoffi.empty()) { 1400 if (!meta->aoffi.empty()) {
1345 expr += "Offset"; 1401 expr += "Offset";
1346 } else if (!meta->ptp.empty()) { 1402 } else if (!meta->ptp.empty()) {
@@ -1374,6 +1430,16 @@ private:
1374 expr += ')'; 1430 expr += ')';
1375 } 1431 }
1376 1432
1433 if (workaround_lod_array_shadow_as_grad) {
1434 switch (meta->sampler.type) {
1435 case TextureType::Texture2D:
1436 return expr + ", vec2(0.0), vec2(0.0))";
1437 case TextureType::TextureCube:
1438 return expr + ", vec3(0.0), vec3(0.0))";
1439 }
1440 UNREACHABLE();
1441 }
1442
1377 for (const auto& variant : extras) { 1443 for (const auto& variant : extras) {
1378 if (const auto argument = std::get_if<TextureArgument>(&variant)) { 1444 if (const auto argument = std::get_if<TextureArgument>(&variant)) {
1379 expr += GenerateTextureArgument(*argument); 1445 expr += GenerateTextureArgument(*argument);
@@ -2000,8 +2066,19 @@ private:
2000 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 2066 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
2001 ASSERT(meta); 2067 ASSERT(meta);
2002 2068
2003 std::string expr = GenerateTexture( 2069 std::string expr{};
2004 operation, "Lod", {TextureArgument{Type::Float, meta->lod}, TextureOffset{}}); 2070
2071 if (!device.HasTextureShadowLod() && meta->sampler.is_shadow &&
2072 ((meta->sampler.type == TextureType::Texture2D && meta->sampler.is_array) ||
2073 meta->sampler.type == TextureType::TextureCube)) {
2074 LOG_ERROR(Render_OpenGL,
2075 "Device lacks GL_EXT_texture_shadow_lod, using textureGrad as a workaround");
2076 expr = GenerateTexture(operation, "Lod", {});
2077 } else {
2078 expr = GenerateTexture(operation, "Lod",
2079 {TextureArgument{Type::Float, meta->lod}, TextureOffset{}});
2080 }
2081
2005 if (meta->sampler.is_shadow) { 2082 if (meta->sampler.is_shadow) {
2006 expr = "vec4(" + expr + ')'; 2083 expr = "vec4(" + expr + ')';
2007 } 2084 }
@@ -2710,6 +2787,7 @@ private:
2710 const std::string_view identifier; 2787 const std::string_view identifier;
2711 const std::string_view suffix; 2788 const std::string_view suffix;
2712 const Header header; 2789 const Header header;
2790 const bool use_unified_uniforms;
2713 std::unordered_map<u8, VaryingTFB> transform_feedback; 2791 std::unordered_map<u8, VaryingTFB> transform_feedback;
2714 2792
2715 ShaderWriter code; 2793 ShaderWriter code;
@@ -2905,7 +2983,7 @@ void GLSLDecompiler::DecompileAST() {
2905 2983
2906} // Anonymous namespace 2984} // Anonymous namespace
2907 2985
2908ShaderEntries MakeEntries(const VideoCommon::Shader::ShaderIR& ir) { 2986ShaderEntries MakeEntries(const Device& device, const ShaderIR& ir, ShaderType stage) {
2909 ShaderEntries entries; 2987 ShaderEntries entries;
2910 for (const auto& cbuf : ir.GetConstantBuffers()) { 2988 for (const auto& cbuf : ir.GetConstantBuffers()) {
2911 entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(), 2989 entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(),
@@ -2926,6 +3004,7 @@ ShaderEntries MakeEntries(const VideoCommon::Shader::ShaderIR& ir) {
2926 entries.clip_distances = (clip_distances[i] ? 1U : 0U) << i; 3004 entries.clip_distances = (clip_distances[i] ? 1U : 0U) << i;
2927 } 3005 }
2928 entries.shader_length = ir.GetLength(); 3006 entries.shader_length = ir.GetLength();
3007 entries.use_unified_uniforms = UseUnifiedUniforms(device, ir, stage);
2929 return entries; 3008 return entries;
2930} 3009}
2931 3010
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index e8a178764..451c9689a 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -53,11 +53,13 @@ struct ShaderEntries {
53 std::vector<GlobalMemoryEntry> global_memory_entries; 53 std::vector<GlobalMemoryEntry> global_memory_entries;
54 std::vector<SamplerEntry> samplers; 54 std::vector<SamplerEntry> samplers;
55 std::vector<ImageEntry> images; 55 std::vector<ImageEntry> images;
56 u32 clip_distances{};
57 std::size_t shader_length{}; 56 std::size_t shader_length{};
57 u32 clip_distances{};
58 bool use_unified_uniforms{};
58}; 59};
59 60
60ShaderEntries MakeEntries(const VideoCommon::Shader::ShaderIR& ir); 61ShaderEntries MakeEntries(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
62 Tegra::Engines::ShaderType stage);
61 63
62std::string DecompileShader(const Device& device, const VideoCommon::Shader::ShaderIR& ir, 64std::string DecompileShader(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
63 const VideoCommon::Shader::Registry& registry, 65 const VideoCommon::Shader::Registry& registry,
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 9e95a122b..2dcc2b0eb 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -29,6 +29,8 @@ using VideoCommon::Shader::KeyMap;
29 29
30namespace { 30namespace {
31 31
32using VideoCommon::Shader::SeparateSamplerKey;
33
32using ShaderCacheVersionHash = std::array<u8, 64>; 34using ShaderCacheVersionHash = std::array<u8, 64>;
33 35
34struct ConstBufferKey { 36struct ConstBufferKey {
@@ -37,18 +39,26 @@ struct ConstBufferKey {
37 u32 value = 0; 39 u32 value = 0;
38}; 40};
39 41
40struct BoundSamplerKey { 42struct BoundSamplerEntry {
41 u32 offset = 0; 43 u32 offset = 0;
42 Tegra::Engines::SamplerDescriptor sampler; 44 Tegra::Engines::SamplerDescriptor sampler;
43}; 45};
44 46
45struct BindlessSamplerKey { 47struct SeparateSamplerEntry {
48 u32 cbuf1 = 0;
49 u32 cbuf2 = 0;
50 u32 offset1 = 0;
51 u32 offset2 = 0;
52 Tegra::Engines::SamplerDescriptor sampler;
53};
54
55struct BindlessSamplerEntry {
46 u32 cbuf = 0; 56 u32 cbuf = 0;
47 u32 offset = 0; 57 u32 offset = 0;
48 Tegra::Engines::SamplerDescriptor sampler; 58 Tegra::Engines::SamplerDescriptor sampler;
49}; 59};
50 60
51constexpr u32 NativeVersion = 20; 61constexpr u32 NativeVersion = 21;
52 62
53ShaderCacheVersionHash GetShaderCacheVersionHash() { 63ShaderCacheVersionHash GetShaderCacheVersionHash() {
54 ShaderCacheVersionHash hash{}; 64 ShaderCacheVersionHash hash{};
@@ -87,12 +97,14 @@ bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) {
87 u32 texture_handler_size_value; 97 u32 texture_handler_size_value;
88 u32 num_keys; 98 u32 num_keys;
89 u32 num_bound_samplers; 99 u32 num_bound_samplers;
100 u32 num_separate_samplers;
90 u32 num_bindless_samplers; 101 u32 num_bindless_samplers;
91 if (file.ReadArray(&unique_identifier, 1) != 1 || file.ReadArray(&bound_buffer, 1) != 1 || 102 if (file.ReadArray(&unique_identifier, 1) != 1 || file.ReadArray(&bound_buffer, 1) != 1 ||
92 file.ReadArray(&is_texture_handler_size_known, 1) != 1 || 103 file.ReadArray(&is_texture_handler_size_known, 1) != 1 ||
93 file.ReadArray(&texture_handler_size_value, 1) != 1 || 104 file.ReadArray(&texture_handler_size_value, 1) != 1 ||
94 file.ReadArray(&graphics_info, 1) != 1 || file.ReadArray(&compute_info, 1) != 1 || 105 file.ReadArray(&graphics_info, 1) != 1 || file.ReadArray(&compute_info, 1) != 1 ||
95 file.ReadArray(&num_keys, 1) != 1 || file.ReadArray(&num_bound_samplers, 1) != 1 || 106 file.ReadArray(&num_keys, 1) != 1 || file.ReadArray(&num_bound_samplers, 1) != 1 ||
107 file.ReadArray(&num_separate_samplers, 1) != 1 ||
96 file.ReadArray(&num_bindless_samplers, 1) != 1) { 108 file.ReadArray(&num_bindless_samplers, 1) != 1) {
97 return false; 109 return false;
98 } 110 }
@@ -101,23 +113,32 @@ bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) {
101 } 113 }
102 114
103 std::vector<ConstBufferKey> flat_keys(num_keys); 115 std::vector<ConstBufferKey> flat_keys(num_keys);
104 std::vector<BoundSamplerKey> flat_bound_samplers(num_bound_samplers); 116 std::vector<BoundSamplerEntry> flat_bound_samplers(num_bound_samplers);
105 std::vector<BindlessSamplerKey> flat_bindless_samplers(num_bindless_samplers); 117 std::vector<SeparateSamplerEntry> flat_separate_samplers(num_separate_samplers);
118 std::vector<BindlessSamplerEntry> flat_bindless_samplers(num_bindless_samplers);
106 if (file.ReadArray(flat_keys.data(), flat_keys.size()) != flat_keys.size() || 119 if (file.ReadArray(flat_keys.data(), flat_keys.size()) != flat_keys.size() ||
107 file.ReadArray(flat_bound_samplers.data(), flat_bound_samplers.size()) != 120 file.ReadArray(flat_bound_samplers.data(), flat_bound_samplers.size()) !=
108 flat_bound_samplers.size() || 121 flat_bound_samplers.size() ||
122 file.ReadArray(flat_separate_samplers.data(), flat_separate_samplers.size()) !=
123 flat_separate_samplers.size() ||
109 file.ReadArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) != 124 file.ReadArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) !=
110 flat_bindless_samplers.size()) { 125 flat_bindless_samplers.size()) {
111 return false; 126 return false;
112 } 127 }
113 for (const auto& key : flat_keys) { 128 for (const auto& entry : flat_keys) {
114 keys.insert({{key.cbuf, key.offset}, key.value}); 129 keys.insert({{entry.cbuf, entry.offset}, entry.value});
115 } 130 }
116 for (const auto& key : flat_bound_samplers) { 131 for (const auto& entry : flat_bound_samplers) {
117 bound_samplers.emplace(key.offset, key.sampler); 132 bound_samplers.emplace(entry.offset, entry.sampler);
118 } 133 }
119 for (const auto& key : flat_bindless_samplers) { 134 for (const auto& entry : flat_separate_samplers) {
120 bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler}); 135 SeparateSamplerKey key;
136 key.buffers = {entry.cbuf1, entry.cbuf2};
137 key.offsets = {entry.offset1, entry.offset2};
138 separate_samplers.emplace(key, entry.sampler);
139 }
140 for (const auto& entry : flat_bindless_samplers) {
141 bindless_samplers.insert({{entry.cbuf, entry.offset}, entry.sampler});
121 } 142 }
122 143
123 return true; 144 return true;
@@ -142,6 +163,7 @@ bool ShaderDiskCacheEntry::Save(FileUtil::IOFile& file) const {
142 file.WriteObject(graphics_info) != 1 || file.WriteObject(compute_info) != 1 || 163 file.WriteObject(graphics_info) != 1 || file.WriteObject(compute_info) != 1 ||
143 file.WriteObject(static_cast<u32>(keys.size())) != 1 || 164 file.WriteObject(static_cast<u32>(keys.size())) != 1 ||
144 file.WriteObject(static_cast<u32>(bound_samplers.size())) != 1 || 165 file.WriteObject(static_cast<u32>(bound_samplers.size())) != 1 ||
166 file.WriteObject(static_cast<u32>(separate_samplers.size())) != 1 ||
145 file.WriteObject(static_cast<u32>(bindless_samplers.size())) != 1) { 167 file.WriteObject(static_cast<u32>(bindless_samplers.size())) != 1) {
146 return false; 168 return false;
147 } 169 }
@@ -152,22 +174,34 @@ bool ShaderDiskCacheEntry::Save(FileUtil::IOFile& file) const {
152 flat_keys.push_back(ConstBufferKey{address.first, address.second, value}); 174 flat_keys.push_back(ConstBufferKey{address.first, address.second, value});
153 } 175 }
154 176
155 std::vector<BoundSamplerKey> flat_bound_samplers; 177 std::vector<BoundSamplerEntry> flat_bound_samplers;
156 flat_bound_samplers.reserve(bound_samplers.size()); 178 flat_bound_samplers.reserve(bound_samplers.size());
157 for (const auto& [address, sampler] : bound_samplers) { 179 for (const auto& [address, sampler] : bound_samplers) {
158 flat_bound_samplers.push_back(BoundSamplerKey{address, sampler}); 180 flat_bound_samplers.push_back(BoundSamplerEntry{address, sampler});
181 }
182
183 std::vector<SeparateSamplerEntry> flat_separate_samplers;
184 flat_separate_samplers.reserve(separate_samplers.size());
185 for (const auto& [key, sampler] : separate_samplers) {
186 SeparateSamplerEntry entry;
187 std::tie(entry.cbuf1, entry.cbuf2) = key.buffers;
188 std::tie(entry.offset1, entry.offset2) = key.offsets;
189 entry.sampler = sampler;
190 flat_separate_samplers.push_back(entry);
159 } 191 }
160 192
161 std::vector<BindlessSamplerKey> flat_bindless_samplers; 193 std::vector<BindlessSamplerEntry> flat_bindless_samplers;
162 flat_bindless_samplers.reserve(bindless_samplers.size()); 194 flat_bindless_samplers.reserve(bindless_samplers.size());
163 for (const auto& [address, sampler] : bindless_samplers) { 195 for (const auto& [address, sampler] : bindless_samplers) {
164 flat_bindless_samplers.push_back( 196 flat_bindless_samplers.push_back(
165 BindlessSamplerKey{address.first, address.second, sampler}); 197 BindlessSamplerEntry{address.first, address.second, sampler});
166 } 198 }
167 199
168 return file.WriteArray(flat_keys.data(), flat_keys.size()) == flat_keys.size() && 200 return file.WriteArray(flat_keys.data(), flat_keys.size()) == flat_keys.size() &&
169 file.WriteArray(flat_bound_samplers.data(), flat_bound_samplers.size()) == 201 file.WriteArray(flat_bound_samplers.data(), flat_bound_samplers.size()) ==
170 flat_bound_samplers.size() && 202 flat_bound_samplers.size() &&
203 file.WriteArray(flat_separate_samplers.data(), flat_separate_samplers.size()) ==
204 flat_separate_samplers.size() &&
171 file.WriteArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) == 205 file.WriteArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) ==
172 flat_bindless_samplers.size(); 206 flat_bindless_samplers.size();
173} 207}
@@ -179,7 +213,7 @@ ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
179std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() { 213std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() {
180 // Skip games without title id 214 // Skip games without title id
181 const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0; 215 const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
182 if (!Settings::values.use_disk_shader_cache || !has_title_id) { 216 if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
183 return {}; 217 return {};
184 } 218 }
185 219
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
index d5be52e40..a79cef0e9 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
@@ -57,6 +57,7 @@ struct ShaderDiskCacheEntry {
57 VideoCommon::Shader::ComputeInfo compute_info; 57 VideoCommon::Shader::ComputeInfo compute_info;
58 VideoCommon::Shader::KeyMap keys; 58 VideoCommon::Shader::KeyMap keys;
59 VideoCommon::Shader::BoundSamplerMap bound_samplers; 59 VideoCommon::Shader::BoundSamplerMap bound_samplers;
60 VideoCommon::Shader::SeparateSamplerMap separate_samplers;
60 VideoCommon::Shader::BindlessSamplerMap bindless_samplers; 61 VideoCommon::Shader::BindlessSamplerMap bindless_samplers;
61}; 62};
62 63
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
index 6ec328c53..3655ff629 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
@@ -2,11 +2,13 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <deque> 5#include <tuple>
6#include <vector> 6#include <vector>
7
7#include "common/alignment.h" 8#include "common/alignment.h"
8#include "common/assert.h" 9#include "common/assert.h"
9#include "common/microprofile.h" 10#include "common/microprofile.h"
11#include "video_core/renderer_opengl/gl_device.h"
10#include "video_core/renderer_opengl/gl_stream_buffer.h" 12#include "video_core/renderer_opengl/gl_stream_buffer.h"
11 13
12MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning", 14MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning",
@@ -14,8 +16,7 @@ MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning",
14 16
15namespace OpenGL { 17namespace OpenGL {
16 18
17OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent, 19OGLStreamBuffer::OGLStreamBuffer(const Device& device, GLsizeiptr size, bool vertex_data_usage)
18 bool use_persistent)
19 : buffer_size(size) { 20 : buffer_size(size) {
20 gl_buffer.Create(); 21 gl_buffer.Create();
21 22
@@ -29,34 +30,22 @@ OGLStreamBuffer::OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool p
29 allocate_size *= 2; 30 allocate_size *= 2;
30 } 31 }
31 32
32 if (use_persistent) { 33 static constexpr GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
33 persistent = true; 34 glNamedBufferStorage(gl_buffer.handle, allocate_size, nullptr, flags);
34 coherent = prefer_coherent; 35 mapped_ptr = static_cast<u8*>(
35 const GLbitfield flags = 36 glMapNamedBufferRange(gl_buffer.handle, 0, buffer_size, flags | GL_MAP_FLUSH_EXPLICIT_BIT));
36 GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); 37
37 glNamedBufferStorage(gl_buffer.handle, allocate_size, nullptr, flags); 38 if (device.HasVertexBufferUnifiedMemory()) {
38 mapped_ptr = static_cast<u8*>(glMapNamedBufferRange( 39 glMakeNamedBufferResidentNV(gl_buffer.handle, GL_READ_ONLY);
39 gl_buffer.handle, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT))); 40 glGetNamedBufferParameterui64vNV(gl_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, &gpu_address);
40 } else {
41 glNamedBufferData(gl_buffer.handle, allocate_size, nullptr, GL_STREAM_DRAW);
42 } 41 }
43} 42}
44 43
45OGLStreamBuffer::~OGLStreamBuffer() { 44OGLStreamBuffer::~OGLStreamBuffer() {
46 if (persistent) { 45 glUnmapNamedBuffer(gl_buffer.handle);
47 glUnmapNamedBuffer(gl_buffer.handle);
48 }
49 gl_buffer.Release(); 46 gl_buffer.Release();
50} 47}
51 48
52GLuint OGLStreamBuffer::GetHandle() const {
53 return gl_buffer.handle;
54}
55
56GLsizeiptr OGLStreamBuffer::GetSize() const {
57 return buffer_size;
58}
59
60std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr alignment) { 49std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr alignment) {
61 ASSERT(size <= buffer_size); 50 ASSERT(size <= buffer_size);
62 ASSERT(alignment <= buffer_size); 51 ASSERT(alignment <= buffer_size);
@@ -68,36 +57,21 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
68 57
69 bool invalidate = false; 58 bool invalidate = false;
70 if (buffer_pos + size > buffer_size) { 59 if (buffer_pos + size > buffer_size) {
60 MICROPROFILE_SCOPE(OpenGL_StreamBuffer);
61 glInvalidateBufferData(gl_buffer.handle);
62
71 buffer_pos = 0; 63 buffer_pos = 0;
72 invalidate = true; 64 invalidate = true;
73
74 if (persistent) {
75 glUnmapNamedBuffer(gl_buffer.handle);
76 }
77 } 65 }
78 66
79 if (invalidate || !persistent) { 67 return std::make_tuple(mapped_ptr + buffer_pos, buffer_pos, invalidate);
80 MICROPROFILE_SCOPE(OpenGL_StreamBuffer);
81 GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) |
82 (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) |
83 (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT);
84 mapped_ptr = static_cast<u8*>(
85 glMapNamedBufferRange(gl_buffer.handle, buffer_pos, buffer_size - buffer_pos, flags));
86 mapped_offset = buffer_pos;
87 }
88
89 return std::make_tuple(mapped_ptr + buffer_pos - mapped_offset, buffer_pos, invalidate);
90} 68}
91 69
92void OGLStreamBuffer::Unmap(GLsizeiptr size) { 70void OGLStreamBuffer::Unmap(GLsizeiptr size) {
93 ASSERT(size <= mapped_size); 71 ASSERT(size <= mapped_size);
94 72
95 if (!coherent && size > 0) { 73 if (size > 0) {
96 glFlushMappedNamedBufferRange(gl_buffer.handle, buffer_pos - mapped_offset, size); 74 glFlushMappedNamedBufferRange(gl_buffer.handle, buffer_pos, size);
97 }
98
99 if (!persistent) {
100 glUnmapNamedBuffer(gl_buffer.handle);
101 } 75 }
102 76
103 buffer_pos += size; 77 buffer_pos += size;
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h
index f8383cbd4..307a67113 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.h
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.h
@@ -11,15 +11,13 @@
11 11
12namespace OpenGL { 12namespace OpenGL {
13 13
14class Device;
15
14class OGLStreamBuffer : private NonCopyable { 16class OGLStreamBuffer : private NonCopyable {
15public: 17public:
16 explicit OGLStreamBuffer(GLsizeiptr size, bool vertex_data_usage, bool prefer_coherent = false, 18 explicit OGLStreamBuffer(const Device& device, GLsizeiptr size, bool vertex_data_usage);
17 bool use_persistent = true);
18 ~OGLStreamBuffer(); 19 ~OGLStreamBuffer();
19 20
20 GLuint GetHandle() const;
21 GLsizeiptr GetSize() const;
22
23 /* 21 /*
24 * Allocates a linear chunk of memory in the GPU buffer with at least "size" bytes 22 * Allocates a linear chunk of memory in the GPU buffer with at least "size" bytes
25 * and the optional alignment requirement. 23 * and the optional alignment requirement.
@@ -32,15 +30,24 @@ public:
32 30
33 void Unmap(GLsizeiptr size); 31 void Unmap(GLsizeiptr size);
34 32
33 GLuint Handle() const {
34 return gl_buffer.handle;
35 }
36
37 u64 Address() const {
38 return gpu_address;
39 }
40
41 GLsizeiptr Size() const noexcept {
42 return buffer_size;
43 }
44
35private: 45private:
36 OGLBuffer gl_buffer; 46 OGLBuffer gl_buffer;
37 47
38 bool coherent = false; 48 GLuint64EXT gpu_address = 0;
39 bool persistent = false;
40
41 GLintptr buffer_pos = 0; 49 GLintptr buffer_pos = 0;
42 GLsizeiptr buffer_size = 0; 50 GLsizeiptr buffer_size = 0;
43 GLintptr mapped_offset = 0;
44 GLsizeiptr mapped_size = 0; 51 GLsizeiptr mapped_size = 0;
45 u8* mapped_ptr = nullptr; 52 u8* mapped_ptr = nullptr;
46}; 53};
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 4faa8b90c..61505879b 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -263,9 +263,14 @@ CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& param
263 target = GetTextureTarget(params.target); 263 target = GetTextureTarget(params.target);
264 texture = CreateTexture(params, target, internal_format, texture_buffer); 264 texture = CreateTexture(params, target, internal_format, texture_buffer);
265 DecorateSurfaceName(); 265 DecorateSurfaceName();
266 main_view = CreateViewInner( 266
267 ViewParams(params.target, 0, params.is_layered ? params.depth : 1, 0, params.num_levels), 267 u32 num_layers = 1;
268 true); 268 if (params.is_layered || params.target == SurfaceTarget::Texture3D) {
269 num_layers = params.depth;
270 }
271
272 main_view =
273 CreateViewInner(ViewParams(params.target, 0, num_layers, 0, params.num_levels), true);
269} 274}
270 275
271CachedSurface::~CachedSurface() = default; 276CachedSurface::~CachedSurface() = default;
@@ -404,8 +409,7 @@ View CachedSurface::CreateViewInner(const ViewParams& view_key, const bool is_pr
404 409
405CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& params, 410CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& params,
406 bool is_proxy) 411 bool is_proxy)
407 : VideoCommon::ViewBase(params), surface{surface}, 412 : VideoCommon::ViewBase(params), surface{surface}, format{surface.internal_format},
408 format{GetFormatTuple(surface.GetSurfaceParams().pixel_format).internal_format},
409 target{GetTextureTarget(params.target)}, is_proxy{is_proxy} { 413 target{GetTextureTarget(params.target)}, is_proxy{is_proxy} {
410 if (!is_proxy) { 414 if (!is_proxy) {
411 main_view = CreateTextureView(); 415 main_view = CreateTextureView();
@@ -414,20 +418,23 @@ CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& p
414 418
415CachedSurfaceView::~CachedSurfaceView() = default; 419CachedSurfaceView::~CachedSurfaceView() = default;
416 420
417void CachedSurfaceView::Attach(GLenum attachment, GLenum target) const { 421void CachedSurfaceView::Attach(GLenum attachment, GLenum fb_target) const {
418 ASSERT(params.num_levels == 1); 422 ASSERT(params.num_levels == 1);
419 423
424 if (params.target == SurfaceTarget::Texture3D) {
425 if (params.num_layers > 1) {
426 ASSERT(params.base_layer == 0);
427 glFramebufferTexture(fb_target, attachment, surface.texture.handle, params.base_level);
428 } else {
429 glFramebufferTexture3D(fb_target, attachment, target, surface.texture.handle,
430 params.base_level, params.base_layer);
431 }
432 return;
433 }
434
420 if (params.num_layers > 1) { 435 if (params.num_layers > 1) {
421 // Layered framebuffer attachments
422 UNIMPLEMENTED_IF(params.base_layer != 0); 436 UNIMPLEMENTED_IF(params.base_layer != 0);
423 437 glFramebufferTexture(fb_target, attachment, GetTexture(), 0);
424 switch (params.target) {
425 case SurfaceTarget::Texture2DArray:
426 glFramebufferTexture(target, attachment, GetTexture(), 0);
427 break;
428 default:
429 UNIMPLEMENTED();
430 }
431 return; 438 return;
432 } 439 }
433 440
@@ -435,16 +442,16 @@ void CachedSurfaceView::Attach(GLenum attachment, GLenum target) const {
435 const GLuint texture = surface.GetTexture(); 442 const GLuint texture = surface.GetTexture();
436 switch (surface.GetSurfaceParams().target) { 443 switch (surface.GetSurfaceParams().target) {
437 case SurfaceTarget::Texture1D: 444 case SurfaceTarget::Texture1D:
438 glFramebufferTexture1D(target, attachment, view_target, texture, params.base_level); 445 glFramebufferTexture1D(fb_target, attachment, view_target, texture, params.base_level);
439 break; 446 break;
440 case SurfaceTarget::Texture2D: 447 case SurfaceTarget::Texture2D:
441 glFramebufferTexture2D(target, attachment, view_target, texture, params.base_level); 448 glFramebufferTexture2D(fb_target, attachment, view_target, texture, params.base_level);
442 break; 449 break;
443 case SurfaceTarget::Texture1DArray: 450 case SurfaceTarget::Texture1DArray:
444 case SurfaceTarget::Texture2DArray: 451 case SurfaceTarget::Texture2DArray:
445 case SurfaceTarget::TextureCubemap: 452 case SurfaceTarget::TextureCubemap:
446 case SurfaceTarget::TextureCubeArray: 453 case SurfaceTarget::TextureCubeArray:
447 glFramebufferTextureLayer(target, attachment, texture, params.base_level, 454 glFramebufferTextureLayer(fb_target, attachment, texture, params.base_level,
448 params.base_layer); 455 params.base_layer);
449 break; 456 break;
450 default: 457 default:
@@ -501,8 +508,13 @@ OGLTextureView CachedSurfaceView::CreateTextureView() const {
501 OGLTextureView texture_view; 508 OGLTextureView texture_view;
502 texture_view.Create(); 509 texture_view.Create();
503 510
504 glTextureView(texture_view.handle, target, surface.texture.handle, format, params.base_level, 511 if (target == GL_TEXTURE_3D) {
505 params.num_levels, params.base_layer, params.num_layers); 512 glTextureView(texture_view.handle, target, surface.texture.handle, format,
513 params.base_level, params.num_levels, 0, 1);
514 } else {
515 glTextureView(texture_view.handle, target, surface.texture.handle, format,
516 params.base_level, params.num_levels, params.base_layer, params.num_layers);
517 }
506 ApplyTextureDefaults(surface.GetSurfaceParams(), texture_view.handle); 518 ApplyTextureDefaults(surface.GetSurfaceParams(), texture_view.handle);
507 519
508 return texture_view; 520 return texture_view;
@@ -545,8 +557,8 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
545 const Tegra::Engines::Fermi2D::Config& copy_config) { 557 const Tegra::Engines::Fermi2D::Config& copy_config) {
546 const auto& src_params{src_view->GetSurfaceParams()}; 558 const auto& src_params{src_view->GetSurfaceParams()};
547 const auto& dst_params{dst_view->GetSurfaceParams()}; 559 const auto& dst_params{dst_view->GetSurfaceParams()};
548 UNIMPLEMENTED_IF(src_params.target == SurfaceTarget::Texture3D); 560 UNIMPLEMENTED_IF(src_params.depth != 1);
549 UNIMPLEMENTED_IF(dst_params.target == SurfaceTarget::Texture3D); 561 UNIMPLEMENTED_IF(dst_params.depth != 1);
550 562
551 state_tracker.NotifyScissor0(); 563 state_tracker.NotifyScissor0();
552 state_tracker.NotifyFramebuffer(); 564 state_tracker.NotifyFramebuffer();
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 8a2ac8603..bfc4ddf5d 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -80,8 +80,10 @@ public:
80 explicit CachedSurfaceView(CachedSurface& surface, const ViewParams& params, bool is_proxy); 80 explicit CachedSurfaceView(CachedSurface& surface, const ViewParams& params, bool is_proxy);
81 ~CachedSurfaceView(); 81 ~CachedSurfaceView();
82 82
83 /// Attaches this texture view to the current bound GL_DRAW_FRAMEBUFFER 83 /// @brief Attaches this texture view to the currently bound fb_target framebuffer
84 void Attach(GLenum attachment, GLenum target) const; 84 /// @param attachment Attachment to bind textures to
85 /// @param fb_target Framebuffer target to attach to (e.g. DRAW_FRAMEBUFFER)
86 void Attach(GLenum attachment, GLenum fb_target) const;
85 87
86 GLuint GetTexture(Tegra::Texture::SwizzleSource x_source, 88 GLuint GetTexture(Tegra::Texture::SwizzleSource x_source,
87 Tegra::Texture::SwizzleSource y_source, 89 Tegra::Texture::SwizzleSource y_source,
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 994ae98eb..fe9bd4b5a 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -24,10 +24,11 @@ namespace MaxwellToGL {
24 24
25using Maxwell = Tegra::Engines::Maxwell3D::Regs; 25using Maxwell = Tegra::Engines::Maxwell3D::Regs;
26 26
27inline GLenum VertexType(Maxwell::VertexAttribute attrib) { 27inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
28 switch (attrib.type) { 28 switch (attrib.type) {
29 case Maxwell::VertexAttribute::Type::UnsignedInt:
30 case Maxwell::VertexAttribute::Type::UnsignedNorm: 29 case Maxwell::VertexAttribute::Type::UnsignedNorm:
30 case Maxwell::VertexAttribute::Type::UnsignedScaled:
31 case Maxwell::VertexAttribute::Type::UnsignedInt:
31 switch (attrib.size) { 32 switch (attrib.size) {
32 case Maxwell::VertexAttribute::Size::Size_8: 33 case Maxwell::VertexAttribute::Size::Size_8:
33 case Maxwell::VertexAttribute::Size::Size_8_8: 34 case Maxwell::VertexAttribute::Size::Size_8_8:
@@ -46,12 +47,11 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
46 return GL_UNSIGNED_INT; 47 return GL_UNSIGNED_INT;
47 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 48 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
48 return GL_UNSIGNED_INT_2_10_10_10_REV; 49 return GL_UNSIGNED_INT_2_10_10_10_REV;
49 default:
50 LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
51 return {};
52 } 50 }
53 case Maxwell::VertexAttribute::Type::SignedInt: 51 break;
54 case Maxwell::VertexAttribute::Type::SignedNorm: 52 case Maxwell::VertexAttribute::Type::SignedNorm:
53 case Maxwell::VertexAttribute::Type::SignedScaled:
54 case Maxwell::VertexAttribute::Type::SignedInt:
55 switch (attrib.size) { 55 switch (attrib.size) {
56 case Maxwell::VertexAttribute::Size::Size_8: 56 case Maxwell::VertexAttribute::Size::Size_8:
57 case Maxwell::VertexAttribute::Size::Size_8_8: 57 case Maxwell::VertexAttribute::Size::Size_8_8:
@@ -70,10 +70,8 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
70 return GL_INT; 70 return GL_INT;
71 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 71 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
72 return GL_INT_2_10_10_10_REV; 72 return GL_INT_2_10_10_10_REV;
73 default:
74 LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
75 return {};
76 } 73 }
74 break;
77 case Maxwell::VertexAttribute::Type::Float: 75 case Maxwell::VertexAttribute::Type::Float:
78 switch (attrib.size) { 76 switch (attrib.size) {
79 case Maxwell::VertexAttribute::Size::Size_16: 77 case Maxwell::VertexAttribute::Size::Size_16:
@@ -86,46 +84,12 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
86 case Maxwell::VertexAttribute::Size::Size_32_32_32: 84 case Maxwell::VertexAttribute::Size::Size_32_32_32:
87 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 85 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
88 return GL_FLOAT; 86 return GL_FLOAT;
89 default:
90 LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
91 return {};
92 }
93 case Maxwell::VertexAttribute::Type::UnsignedScaled:
94 switch (attrib.size) {
95 case Maxwell::VertexAttribute::Size::Size_8:
96 case Maxwell::VertexAttribute::Size::Size_8_8:
97 case Maxwell::VertexAttribute::Size::Size_8_8_8:
98 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
99 return GL_UNSIGNED_BYTE;
100 case Maxwell::VertexAttribute::Size::Size_16:
101 case Maxwell::VertexAttribute::Size::Size_16_16:
102 case Maxwell::VertexAttribute::Size::Size_16_16_16:
103 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
104 return GL_UNSIGNED_SHORT;
105 default:
106 LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
107 return {};
108 } 87 }
109 case Maxwell::VertexAttribute::Type::SignedScaled: 88 break;
110 switch (attrib.size) {
111 case Maxwell::VertexAttribute::Size::Size_8:
112 case Maxwell::VertexAttribute::Size::Size_8_8:
113 case Maxwell::VertexAttribute::Size::Size_8_8_8:
114 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
115 return GL_BYTE;
116 case Maxwell::VertexAttribute::Size::Size_16:
117 case Maxwell::VertexAttribute::Size::Size_16_16:
118 case Maxwell::VertexAttribute::Size::Size_16_16_16:
119 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
120 return GL_SHORT;
121 default:
122 LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
123 return {};
124 }
125 default:
126 LOG_ERROR(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
127 return {};
128 } 89 }
90 UNIMPLEMENTED_MSG("Unimplemented vertex format of type={} and size={}", attrib.TypeString(),
91 attrib.SizeString());
92 return {};
129} 93}
130 94
131inline GLenum IndexFormat(Maxwell::IndexFormat index_format) { 95inline GLenum IndexFormat(Maxwell::IndexFormat index_format) {
@@ -137,8 +101,7 @@ inline GLenum IndexFormat(Maxwell::IndexFormat index_format) {
137 case Maxwell::IndexFormat::UnsignedInt: 101 case Maxwell::IndexFormat::UnsignedInt:
138 return GL_UNSIGNED_INT; 102 return GL_UNSIGNED_INT;
139 } 103 }
140 LOG_CRITICAL(Render_OpenGL, "Unimplemented index_format={}", static_cast<u32>(index_format)); 104 UNREACHABLE_MSG("Invalid index_format={}", static_cast<u32>(index_format));
141 UNREACHABLE();
142 return {}; 105 return {};
143} 106}
144 107
@@ -180,33 +143,32 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
180} 143}
181 144
182inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, 145inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
183 Tegra::Texture::TextureMipmapFilter mip_filter_mode) { 146 Tegra::Texture::TextureMipmapFilter mipmap_filter_mode) {
184 switch (filter_mode) { 147 switch (filter_mode) {
185 case Tegra::Texture::TextureFilter::Linear: { 148 case Tegra::Texture::TextureFilter::Nearest:
186 switch (mip_filter_mode) { 149 switch (mipmap_filter_mode) {
187 case Tegra::Texture::TextureMipmapFilter::None: 150 case Tegra::Texture::TextureMipmapFilter::None:
188 return GL_LINEAR; 151 return GL_NEAREST;
189 case Tegra::Texture::TextureMipmapFilter::Nearest: 152 case Tegra::Texture::TextureMipmapFilter::Nearest:
190 return GL_LINEAR_MIPMAP_NEAREST; 153 return GL_NEAREST_MIPMAP_NEAREST;
191 case Tegra::Texture::TextureMipmapFilter::Linear: 154 case Tegra::Texture::TextureMipmapFilter::Linear:
192 return GL_LINEAR_MIPMAP_LINEAR; 155 return GL_NEAREST_MIPMAP_LINEAR;
193 } 156 }
194 break; 157 break;
195 } 158 case Tegra::Texture::TextureFilter::Linear:
196 case Tegra::Texture::TextureFilter::Nearest: { 159 switch (mipmap_filter_mode) {
197 switch (mip_filter_mode) {
198 case Tegra::Texture::TextureMipmapFilter::None: 160 case Tegra::Texture::TextureMipmapFilter::None:
199 return GL_NEAREST; 161 return GL_LINEAR;
200 case Tegra::Texture::TextureMipmapFilter::Nearest: 162 case Tegra::Texture::TextureMipmapFilter::Nearest:
201 return GL_NEAREST_MIPMAP_NEAREST; 163 return GL_LINEAR_MIPMAP_NEAREST;
202 case Tegra::Texture::TextureMipmapFilter::Linear: 164 case Tegra::Texture::TextureMipmapFilter::Linear:
203 return GL_NEAREST_MIPMAP_LINEAR; 165 return GL_LINEAR_MIPMAP_LINEAR;
204 } 166 }
205 break; 167 break;
206 } 168 }
207 } 169 UNREACHABLE_MSG("Invalid texture filter mode={} and mipmap filter mode={}",
208 LOG_ERROR(Render_OpenGL, "Unimplemented texture filter mode={}", static_cast<u32>(filter_mode)); 170 static_cast<u32>(filter_mode), static_cast<u32>(mipmap_filter_mode));
209 return GL_LINEAR; 171 return GL_NEAREST;
210} 172}
211 173
212inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { 174inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
@@ -229,10 +191,15 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
229 } else { 191 } else {
230 return GL_MIRROR_CLAMP_TO_EDGE; 192 return GL_MIRROR_CLAMP_TO_EDGE;
231 } 193 }
232 default: 194 case Tegra::Texture::WrapMode::MirrorOnceClampOGL:
233 LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); 195 if (GL_EXT_texture_mirror_clamp) {
234 return GL_REPEAT; 196 return GL_MIRROR_CLAMP_EXT;
197 } else {
198 return GL_MIRROR_CLAMP_TO_EDGE;
199 }
235 } 200 }
201 UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
202 return GL_REPEAT;
236} 203}
237 204
238inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { 205inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
@@ -254,8 +221,7 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
254 case Tegra::Texture::DepthCompareFunc::Always: 221 case Tegra::Texture::DepthCompareFunc::Always:
255 return GL_ALWAYS; 222 return GL_ALWAYS;
256 } 223 }
257 LOG_ERROR(Render_OpenGL, "Unimplemented texture depth compare function ={}", 224 UNIMPLEMENTED_MSG("Unimplemented texture depth compare function={}", static_cast<u32>(func));
258 static_cast<u32>(func));
259 return GL_GREATER; 225 return GL_GREATER;
260} 226}
261 227
@@ -277,7 +243,7 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
277 case Maxwell::Blend::Equation::MaxGL: 243 case Maxwell::Blend::Equation::MaxGL:
278 return GL_MAX; 244 return GL_MAX;
279 } 245 }
280 LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); 246 UNIMPLEMENTED_MSG("Unimplemented blend equation={}", static_cast<u32>(equation));
281 return GL_FUNC_ADD; 247 return GL_FUNC_ADD;
282} 248}
283 249
@@ -341,7 +307,7 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
341 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: 307 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL:
342 return GL_ONE_MINUS_CONSTANT_ALPHA; 308 return GL_ONE_MINUS_CONSTANT_ALPHA;
343 } 309 }
344 LOG_ERROR(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor)); 310 UNIMPLEMENTED_MSG("Unimplemented blend factor={}", static_cast<u32>(factor));
345 return GL_ZERO; 311 return GL_ZERO;
346} 312}
347 313
@@ -361,7 +327,7 @@ inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
361 case Tegra::Texture::SwizzleSource::OneFloat: 327 case Tegra::Texture::SwizzleSource::OneFloat:
362 return GL_ONE; 328 return GL_ONE;
363 } 329 }
364 LOG_ERROR(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source)); 330 UNIMPLEMENTED_MSG("Unimplemented swizzle source={}", static_cast<u32>(source));
365 return GL_ZERO; 331 return GL_ZERO;
366} 332}
367 333
@@ -392,7 +358,7 @@ inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
392 case Maxwell::ComparisonOp::AlwaysOld: 358 case Maxwell::ComparisonOp::AlwaysOld:
393 return GL_ALWAYS; 359 return GL_ALWAYS;
394 } 360 }
395 LOG_ERROR(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison)); 361 UNIMPLEMENTED_MSG("Unimplemented comparison op={}", static_cast<u32>(comparison));
396 return GL_ALWAYS; 362 return GL_ALWAYS;
397} 363}
398 364
@@ -423,7 +389,7 @@ inline GLenum StencilOp(Maxwell::StencilOp stencil) {
423 case Maxwell::StencilOp::DecrWrapOGL: 389 case Maxwell::StencilOp::DecrWrapOGL:
424 return GL_DECR_WRAP; 390 return GL_DECR_WRAP;
425 } 391 }
426 LOG_ERROR(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil)); 392 UNIMPLEMENTED_MSG("Unimplemented stencil op={}", static_cast<u32>(stencil));
427 return GL_KEEP; 393 return GL_KEEP;
428} 394}
429 395
@@ -434,7 +400,7 @@ inline GLenum FrontFace(Maxwell::FrontFace front_face) {
434 case Maxwell::FrontFace::CounterClockWise: 400 case Maxwell::FrontFace::CounterClockWise:
435 return GL_CCW; 401 return GL_CCW;
436 } 402 }
437 LOG_ERROR(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face)); 403 UNIMPLEMENTED_MSG("Unimplemented front face cull={}", static_cast<u32>(front_face));
438 return GL_CCW; 404 return GL_CCW;
439} 405}
440 406
@@ -447,7 +413,7 @@ inline GLenum CullFace(Maxwell::CullFace cull_face) {
447 case Maxwell::CullFace::FrontAndBack: 413 case Maxwell::CullFace::FrontAndBack:
448 return GL_FRONT_AND_BACK; 414 return GL_FRONT_AND_BACK;
449 } 415 }
450 LOG_ERROR(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face)); 416 UNIMPLEMENTED_MSG("Unimplemented cull face={}", static_cast<u32>(cull_face));
451 return GL_BACK; 417 return GL_BACK;
452} 418}
453 419
@@ -486,7 +452,7 @@ inline GLenum LogicOp(Maxwell::LogicOperation operation) {
486 case Maxwell::LogicOperation::Set: 452 case Maxwell::LogicOperation::Set:
487 return GL_SET; 453 return GL_SET;
488 } 454 }
489 LOG_ERROR(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation)); 455 UNIMPLEMENTED_MSG("Unimplemented logic operation={}", static_cast<u32>(operation));
490 return GL_COPY; 456 return GL_COPY;
491} 457}
492 458
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 6b489e6db..e66cdc083 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -455,8 +455,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
455void RendererOpenGL::InitOpenGLObjects() { 455void RendererOpenGL::InitOpenGLObjects() {
456 frame_mailbox = std::make_unique<FrameMailbox>(); 456 frame_mailbox = std::make_unique<FrameMailbox>();
457 457
458 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 458 glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
459 0.0f); 459 Settings::values.bg_blue.GetValue(), 0.0f);
460 460
461 // Create shader programs 461 // Create shader programs
462 OGLShader vertex_shader; 462 OGLShader vertex_shader;
@@ -488,6 +488,15 @@ void RendererOpenGL::InitOpenGLObjects() {
488 488
489 // Clear screen to black 489 // Clear screen to black
490 LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); 490 LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture);
491
492 // Enable unified vertex attributes and query vertex buffer address when the driver supports it
493 if (device.HasVertexBufferUnifiedMemory()) {
494 glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV);
495
496 glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY);
497 glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV,
498 &vertex_buffer_address);
499 }
491} 500}
492 501
493void RendererOpenGL::AddTelemetryFields() { 502void RendererOpenGL::AddTelemetryFields() {
@@ -552,8 +561,8 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
552void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { 561void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
553 if (renderer_settings.set_background_color) { 562 if (renderer_settings.set_background_color) {
554 // Update background color before drawing 563 // Update background color before drawing
555 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 564 glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
556 0.0f); 565 Settings::values.bg_blue.GetValue(), 0.0f);
557 } 566 }
558 567
559 // Set projection matrix 568 // Set projection matrix
@@ -656,7 +665,13 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
656 offsetof(ScreenRectVertex, tex_coord)); 665 offsetof(ScreenRectVertex, tex_coord));
657 glVertexAttribBinding(PositionLocation, 0); 666 glVertexAttribBinding(PositionLocation, 0);
658 glVertexAttribBinding(TexCoordLocation, 0); 667 glVertexAttribBinding(TexCoordLocation, 0);
659 glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); 668 if (device.HasVertexBufferUnifiedMemory()) {
669 glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex));
670 glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address,
671 sizeof(vertices));
672 } else {
673 glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
674 }
660 675
661 glBindTextureUnit(0, screen_info.display_texture); 676 glBindTextureUnit(0, screen_info.display_texture);
662 glBindSampler(0, 0); 677 glBindSampler(0, 0);
@@ -751,8 +766,9 @@ void RendererOpenGL::RenderScreenshot() {
751} 766}
752 767
753bool RendererOpenGL::Init() { 768bool RendererOpenGL::Init() {
754 if (GLAD_GL_KHR_debug) { 769 if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) {
755 glEnable(GL_DEBUG_OUTPUT); 770 glEnable(GL_DEBUG_OUTPUT);
771 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
756 glDebugMessageCallback(DebugHandler, nullptr); 772 glDebugMessageCallback(DebugHandler, nullptr);
757 } 773 }
758 774
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 61bf507f4..8b18d32e6 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -107,6 +107,9 @@ private:
107 OGLPipeline pipeline; 107 OGLPipeline pipeline;
108 OGLFramebuffer screenshot_framebuffer; 108 OGLFramebuffer screenshot_framebuffer;
109 109
110 // GPU address of the vertex buffer
111 GLuint64EXT vertex_buffer_address = 0;
112
110 /// Display information for Switch screen 113 /// Display information for Switch screen
111 ScreenInfo screen_info; 114 ScreenInfo screen_info;
112 115
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 568744e3c..d1f0ea932 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -39,53 +39,18 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
39 39
40} // Anonymous namespace 40} // Anonymous namespace
41 41
42void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept { 42void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) {
43 raw = 0;
44 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
45 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
46 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
47 front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
48 if (regs.stencil_two_side_enable) {
49 back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
50 back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
51 back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
52 back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
53 } else {
54 back.action_stencil_fail.Assign(front.action_stencil_fail);
55 back.action_depth_fail.Assign(front.action_depth_fail);
56 back.action_depth_pass.Assign(front.action_depth_pass);
57 back.test_func.Assign(front.test_func);
58 }
59 depth_test_enable.Assign(regs.depth_test_enable);
60 depth_write_enable.Assign(regs.depth_write_enabled);
61 depth_bounds_enable.Assign(regs.depth_bounds_enable);
62 stencil_enable.Assign(regs.stencil_enable);
63 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
64}
65
66void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
67 const auto& clip = regs.view_volume_clip_control; 43 const auto& clip = regs.view_volume_clip_control;
68 const std::array enabled_lut = {regs.polygon_offset_point_enable, 44 const std::array enabled_lut = {regs.polygon_offset_point_enable,
69 regs.polygon_offset_line_enable, 45 regs.polygon_offset_line_enable,
70 regs.polygon_offset_fill_enable}; 46 regs.polygon_offset_fill_enable};
71 const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); 47 const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
72 48
73 u32 packed_front_face = PackFrontFace(regs.front_face);
74 if (regs.screen_y_control.triangle_rast_flip != 0 &&
75 regs.viewport_transform[0].scale_y > 0.0f) {
76 // Flip front face
77 packed_front_face = 1 - packed_front_face;
78 }
79
80 raw = 0; 49 raw = 0;
81 topology.Assign(topology_index);
82 primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); 50 primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
83 cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
84 depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); 51 depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
85 depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); 52 depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value());
86 ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); 53 ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
87 cull_face.Assign(PackCullFace(regs.cull_face));
88 front_face.Assign(packed_front_face);
89 polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); 54 polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
90 patch_control_points_minus_one.Assign(regs.patch_vertices - 1); 55 patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
91 tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value())); 56 tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value()));
@@ -94,19 +59,37 @@ void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
94 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); 59 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
95 logic_op.Assign(PackLogicOp(regs.logic_op.operation)); 60 logic_op.Assign(PackLogicOp(regs.logic_op.operation));
96 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); 61 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
62
97 std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast 63 std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
98}
99 64
100void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept { 65 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
66 binding_divisors[index] =
67 regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0;
68 }
69
70 for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
71 const auto& input = regs.vertex_attrib_format[index];
72 auto& attribute = attributes[index];
73 attribute.raw = 0;
74 attribute.enabled.Assign(input.IsConstant() ? 0 : 1);
75 attribute.buffer.Assign(input.buffer);
76 attribute.offset.Assign(input.offset);
77 attribute.type.Assign(static_cast<u32>(input.type.Value()));
78 attribute.size.Assign(static_cast<u32>(input.size.Value()));
79 }
80
101 for (std::size_t index = 0; index < std::size(attachments); ++index) { 81 for (std::size_t index = 0; index < std::size(attachments); ++index) {
102 attachments[index].Fill(regs, index); 82 attachments[index].Fill(regs, index);
103 } 83 }
104}
105 84
106void FixedPipelineState::ViewportSwizzles::Fill(const Maxwell& regs) noexcept {
107 const auto& transform = regs.viewport_transform; 85 const auto& transform = regs.viewport_transform;
108 std::transform(transform.begin(), transform.end(), swizzles.begin(), 86 std::transform(transform.begin(), transform.end(), viewport_swizzles.begin(),
109 [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); }); 87 [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); });
88
89 if (!has_extended_dynamic_state) {
90 no_extended_dynamic_state.Assign(1);
91 dynamic_state.Fill(regs);
92 }
110} 93}
111 94
112void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) { 95void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) {
@@ -148,20 +131,57 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size
148 enable.Assign(1); 131 enable.Assign(1);
149} 132}
150 133
151void FixedPipelineState::Fill(const Maxwell& regs) { 134void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {
152 rasterizer.Fill(regs); 135 const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
153 depth_stencil.Fill(regs); 136 u32 packed_front_face = PackFrontFace(regs.front_face);
154 color_blending.Fill(regs); 137 if (regs.screen_y_control.triangle_rast_flip != 0) {
155 viewport_swizzles.Fill(regs); 138 // Flip front face
139 packed_front_face = 1 - packed_front_face;
140 }
141
142 raw1 = 0;
143 raw2 = 0;
144 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
145 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
146 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
147 front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
148 if (regs.stencil_two_side_enable) {
149 back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
150 back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
151 back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
152 back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
153 } else {
154 back.action_stencil_fail.Assign(front.action_stencil_fail);
155 back.action_depth_fail.Assign(front.action_depth_fail);
156 back.action_depth_pass.Assign(front.action_depth_pass);
157 back.test_func.Assign(front.test_func);
158 }
159 stencil_enable.Assign(regs.stencil_enable);
160 depth_write_enable.Assign(regs.depth_write_enabled);
161 depth_bounds_enable.Assign(regs.depth_bounds_enable);
162 depth_test_enable.Assign(regs.depth_test_enable);
163 front_face.Assign(packed_front_face);
164 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
165 topology.Assign(topology_index);
166 cull_face.Assign(PackCullFace(regs.cull_face));
167 cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
168
169 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
170 const auto& input = regs.vertex_array[index];
171 VertexBinding& binding = vertex_bindings[index];
172 binding.raw = 0;
173 binding.enabled.Assign(input.IsEnabled() ? 1 : 0);
174 binding.stride.Assign(static_cast<u16>(input.stride.Value()));
175 }
156} 176}
157 177
158std::size_t FixedPipelineState::Hash() const noexcept { 178std::size_t FixedPipelineState::Hash() const noexcept {
159 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); 179 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
160 return static_cast<std::size_t>(hash); 180 return static_cast<std::size_t>(hash);
161} 181}
162 182
163bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { 183bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept {
164 return std::memcmp(this, &rhs, sizeof *this) == 0; 184 return std::memcmp(this, &rhs, Size()) == 0;
165} 185}
166 186
167u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept { 187u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept {
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 31a6398f2..cdcbb65f5 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -60,14 +60,6 @@ struct FixedPipelineState {
60 60
61 void Fill(const Maxwell& regs, std::size_t index); 61 void Fill(const Maxwell& regs, std::size_t index);
62 62
63 std::size_t Hash() const noexcept;
64
65 bool operator==(const BlendingAttachment& rhs) const noexcept;
66
67 bool operator!=(const BlendingAttachment& rhs) const noexcept {
68 return !operator==(rhs);
69 }
70
71 constexpr std::array<bool, 4> Mask() const noexcept { 63 constexpr std::array<bool, 4> Mask() const noexcept {
72 return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0}; 64 return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0};
73 } 65 }
@@ -97,156 +89,116 @@ struct FixedPipelineState {
97 } 89 }
98 }; 90 };
99 91
100 struct VertexInput { 92 union VertexAttribute {
101 union Binding { 93 u32 raw;
102 u16 raw; 94 BitField<0, 1, u32> enabled;
103 BitField<0, 1, u16> enabled; 95 BitField<1, 5, u32> buffer;
104 BitField<1, 12, u16> stride; 96 BitField<6, 14, u32> offset;
105 }; 97 BitField<20, 3, u32> type;
98 BitField<23, 6, u32> size;
106 99
107 union Attribute { 100 constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
108 u32 raw; 101 return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
109 BitField<0, 1, u32> enabled;
110 BitField<1, 5, u32> buffer;
111 BitField<6, 14, u32> offset;
112 BitField<20, 3, u32> type;
113 BitField<23, 6, u32> size;
114
115 constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
116 return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
117 }
118
119 constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
120 return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
121 }
122 };
123
124 std::array<Binding, Maxwell::NumVertexArrays> bindings;
125 std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
126 std::array<Attribute, Maxwell::NumVertexAttributes> attributes;
127
128 void SetBinding(std::size_t index, bool enabled, u32 stride, u32 divisor) noexcept {
129 auto& binding = bindings[index];
130 binding.raw = 0;
131 binding.enabled.Assign(enabled ? 1 : 0);
132 binding.stride.Assign(static_cast<u16>(stride));
133 binding_divisors[index] = divisor;
134 } 102 }
135 103
136 void SetAttribute(std::size_t index, bool enabled, u32 buffer, u32 offset, 104 constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
137 Maxwell::VertexAttribute::Type type, 105 return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
138 Maxwell::VertexAttribute::Size size) noexcept {
139 auto& attribute = attributes[index];
140 attribute.raw = 0;
141 attribute.enabled.Assign(enabled ? 1 : 0);
142 attribute.buffer.Assign(buffer);
143 attribute.offset.Assign(offset);
144 attribute.type.Assign(static_cast<u32>(type));
145 attribute.size.Assign(static_cast<u32>(size));
146 } 106 }
147 }; 107 };
148 108
149 struct Rasterizer { 109 template <std::size_t Position>
150 union { 110 union StencilFace {
151 u32 raw; 111 BitField<Position + 0, 3, u32> action_stencil_fail;
152 BitField<0, 4, u32> topology; 112 BitField<Position + 3, 3, u32> action_depth_fail;
153 BitField<4, 1, u32> primitive_restart_enable; 113 BitField<Position + 6, 3, u32> action_depth_pass;
154 BitField<5, 1, u32> cull_enable; 114 BitField<Position + 9, 3, u32> test_func;
155 BitField<6, 1, u32> depth_bias_enable;
156 BitField<7, 1, u32> depth_clamp_disabled;
157 BitField<8, 1, u32> ndc_minus_one_to_one;
158 BitField<9, 2, u32> cull_face;
159 BitField<11, 1, u32> front_face;
160 BitField<12, 2, u32> polygon_mode;
161 BitField<14, 5, u32> patch_control_points_minus_one;
162 BitField<19, 2, u32> tessellation_primitive;
163 BitField<21, 2, u32> tessellation_spacing;
164 BitField<23, 1, u32> tessellation_clockwise;
165 BitField<24, 1, u32> logic_op_enable;
166 BitField<25, 4, u32> logic_op;
167 BitField<29, 1, u32> rasterize_enable;
168 };
169
170 // TODO(Rodrigo): Move this to push constants
171 u32 point_size;
172 115
173 void Fill(const Maxwell& regs) noexcept; 116 Maxwell::StencilOp ActionStencilFail() const noexcept {
117 return UnpackStencilOp(action_stencil_fail);
118 }
174 119
175 constexpr Maxwell::PrimitiveTopology Topology() const noexcept { 120 Maxwell::StencilOp ActionDepthFail() const noexcept {
176 return static_cast<Maxwell::PrimitiveTopology>(topology.Value()); 121 return UnpackStencilOp(action_depth_fail);
177 } 122 }
178 123
179 Maxwell::CullFace CullFace() const noexcept { 124 Maxwell::StencilOp ActionDepthPass() const noexcept {
180 return UnpackCullFace(cull_face.Value()); 125 return UnpackStencilOp(action_depth_pass);
181 } 126 }
182 127
183 Maxwell::FrontFace FrontFace() const noexcept { 128 Maxwell::ComparisonOp TestFunc() const noexcept {
184 return UnpackFrontFace(front_face.Value()); 129 return UnpackComparisonOp(test_func);
185 } 130 }
186 }; 131 };
187 132
188 struct DepthStencil { 133 union VertexBinding {
189 template <std::size_t Position> 134 u16 raw;
190 union StencilFace { 135 BitField<0, 12, u16> stride;
191 BitField<Position + 0, 3, u32> action_stencil_fail; 136 BitField<12, 1, u16> enabled;
192 BitField<Position + 3, 3, u32> action_depth_fail; 137 };
193 BitField<Position + 6, 3, u32> action_depth_pass;
194 BitField<Position + 9, 3, u32> test_func;
195
196 Maxwell::StencilOp ActionStencilFail() const noexcept {
197 return UnpackStencilOp(action_stencil_fail);
198 }
199
200 Maxwell::StencilOp ActionDepthFail() const noexcept {
201 return UnpackStencilOp(action_depth_fail);
202 }
203
204 Maxwell::StencilOp ActionDepthPass() const noexcept {
205 return UnpackStencilOp(action_depth_pass);
206 }
207
208 Maxwell::ComparisonOp TestFunc() const noexcept {
209 return UnpackComparisonOp(test_func);
210 }
211 };
212 138
139 struct DynamicState {
213 union { 140 union {
214 u32 raw; 141 u32 raw1;
215 StencilFace<0> front; 142 StencilFace<0> front;
216 StencilFace<12> back; 143 StencilFace<12> back;
217 BitField<24, 1, u32> depth_test_enable; 144 BitField<24, 1, u32> stencil_enable;
218 BitField<25, 1, u32> depth_write_enable; 145 BitField<25, 1, u32> depth_write_enable;
219 BitField<26, 1, u32> depth_bounds_enable; 146 BitField<26, 1, u32> depth_bounds_enable;
220 BitField<27, 1, u32> stencil_enable; 147 BitField<27, 1, u32> depth_test_enable;
221 BitField<28, 3, u32> depth_test_func; 148 BitField<28, 1, u32> front_face;
149 BitField<29, 3, u32> depth_test_func;
150 };
151 union {
152 u32 raw2;
153 BitField<0, 4, u32> topology;
154 BitField<4, 2, u32> cull_face;
155 BitField<6, 1, u32> cull_enable;
222 }; 156 };
157 std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings;
223 158
224 void Fill(const Maxwell& regs) noexcept; 159 void Fill(const Maxwell& regs);
225 160
226 Maxwell::ComparisonOp DepthTestFunc() const noexcept { 161 Maxwell::ComparisonOp DepthTestFunc() const noexcept {
227 return UnpackComparisonOp(depth_test_func); 162 return UnpackComparisonOp(depth_test_func);
228 } 163 }
229 };
230
231 struct ColorBlending {
232 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
233 164
234 void Fill(const Maxwell& regs) noexcept; 165 Maxwell::CullFace CullFace() const noexcept {
235 }; 166 return UnpackCullFace(cull_face.Value());
167 }
236 168
237 struct ViewportSwizzles { 169 Maxwell::FrontFace FrontFace() const noexcept {
238 std::array<u16, Maxwell::NumViewports> swizzles; 170 return UnpackFrontFace(front_face.Value());
171 }
239 172
240 void Fill(const Maxwell& regs) noexcept; 173 constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
174 return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
175 }
241 }; 176 };
242 177
243 VertexInput vertex_input; 178 union {
244 Rasterizer rasterizer; 179 u32 raw;
245 DepthStencil depth_stencil; 180 BitField<0, 1, u32> no_extended_dynamic_state;
246 ColorBlending color_blending; 181 BitField<2, 1, u32> primitive_restart_enable;
247 ViewportSwizzles viewport_swizzles; 182 BitField<3, 1, u32> depth_bias_enable;
183 BitField<4, 1, u32> depth_clamp_disabled;
184 BitField<5, 1, u32> ndc_minus_one_to_one;
185 BitField<6, 2, u32> polygon_mode;
186 BitField<8, 5, u32> patch_control_points_minus_one;
187 BitField<13, 2, u32> tessellation_primitive;
188 BitField<15, 2, u32> tessellation_spacing;
189 BitField<17, 1, u32> tessellation_clockwise;
190 BitField<18, 1, u32> logic_op_enable;
191 BitField<19, 4, u32> logic_op;
192 BitField<23, 1, u32> rasterize_enable;
193 };
194 u32 point_size;
195 std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
196 std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
197 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
198 std::array<u16, Maxwell::NumViewports> viewport_swizzles;
199 DynamicState dynamic_state;
248 200
249 void Fill(const Maxwell& regs); 201 void Fill(const Maxwell& regs, bool has_extended_dynamic_state);
250 202
251 std::size_t Hash() const noexcept; 203 std::size_t Hash() const noexcept;
252 204
@@ -255,6 +207,11 @@ struct FixedPipelineState {
255 bool operator!=(const FixedPipelineState& rhs) const noexcept { 207 bool operator!=(const FixedPipelineState& rhs) const noexcept {
256 return !operator==(rhs); 208 return !operator==(rhs);
257 } 209 }
210
211 std::size_t Size() const noexcept {
212 const std::size_t total_size = sizeof *this;
213 return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState));
214 }
258}; 215};
259static_assert(std::has_unique_object_representations_v<FixedPipelineState>); 216static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
260static_assert(std::is_trivially_copyable_v<FixedPipelineState>); 217static_assert(std::is_trivially_copyable_v<FixedPipelineState>);
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 2871035f5..d7f1ae89f 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -21,29 +21,29 @@ namespace Sampler {
21 21
22VkFilter Filter(Tegra::Texture::TextureFilter filter) { 22VkFilter Filter(Tegra::Texture::TextureFilter filter) {
23 switch (filter) { 23 switch (filter) {
24 case Tegra::Texture::TextureFilter::Linear:
25 return VK_FILTER_LINEAR;
26 case Tegra::Texture::TextureFilter::Nearest: 24 case Tegra::Texture::TextureFilter::Nearest:
27 return VK_FILTER_NEAREST; 25 return VK_FILTER_NEAREST;
26 case Tegra::Texture::TextureFilter::Linear:
27 return VK_FILTER_LINEAR;
28 } 28 }
29 UNIMPLEMENTED_MSG("Unimplemented sampler filter={}", static_cast<u32>(filter)); 29 UNREACHABLE_MSG("Invalid sampler filter={}", static_cast<u32>(filter));
30 return {}; 30 return {};
31} 31}
32 32
33VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter) { 33VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter) {
34 switch (mipmap_filter) { 34 switch (mipmap_filter) {
35 case Tegra::Texture::TextureMipmapFilter::None: 35 case Tegra::Texture::TextureMipmapFilter::None:
36 // TODO(Rodrigo): None seems to be mapped to OpenGL's mag and min filters without mipmapping 36 // There are no Vulkan filter modes that directly correspond to OpenGL minification filters
37 // (e.g. GL_NEAREST and GL_LINEAR). Vulkan doesn't have such a thing, find out if we have to 37 // of GL_LINEAR or GL_NEAREST, but they can be emulated using
38 // use an image view with a single mipmap level to emulate this. 38 // VK_SAMPLER_MIPMAP_MODE_NEAREST, minLod = 0, and maxLod = 0.25, and using minFilter =
39 return VK_SAMPLER_MIPMAP_MODE_LINEAR; 39 // VK_FILTER_LINEAR or minFilter = VK_FILTER_NEAREST, respectively.
40 ; 40 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
41 case Tegra::Texture::TextureMipmapFilter::Linear:
42 return VK_SAMPLER_MIPMAP_MODE_LINEAR;
43 case Tegra::Texture::TextureMipmapFilter::Nearest: 41 case Tegra::Texture::TextureMipmapFilter::Nearest:
44 return VK_SAMPLER_MIPMAP_MODE_NEAREST; 42 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
43 case Tegra::Texture::TextureMipmapFilter::Linear:
44 return VK_SAMPLER_MIPMAP_MODE_LINEAR;
45 } 45 }
46 UNIMPLEMENTED_MSG("Unimplemented sampler mipmap mode={}", static_cast<u32>(mipmap_filter)); 46 UNREACHABLE_MSG("Invalid sampler mipmap mode={}", static_cast<u32>(mipmap_filter));
47 return {}; 47 return {};
48} 48}
49 49
@@ -78,10 +78,9 @@ VkSamplerAddressMode WrapMode(const VKDevice& device, Tegra::Texture::WrapMode w
78 case Tegra::Texture::WrapMode::MirrorOnceBorder: 78 case Tegra::Texture::WrapMode::MirrorOnceBorder:
79 UNIMPLEMENTED(); 79 UNIMPLEMENTED();
80 return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; 80 return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
81 default:
82 UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode));
83 return {};
84 } 81 }
82 UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode));
83 return {};
85} 84}
86 85
87VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) { 86VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) {
@@ -149,7 +148,7 @@ struct FormatTuple {
149 {VK_FORMAT_R16_SFLOAT, Attachable | Storage}, // R16F 148 {VK_FORMAT_R16_SFLOAT, Attachable | Storage}, // R16F
150 {VK_FORMAT_R16_UNORM, Attachable | Storage}, // R16U 149 {VK_FORMAT_R16_UNORM, Attachable | Storage}, // R16U
151 {VK_FORMAT_UNDEFINED}, // R16S 150 {VK_FORMAT_UNDEFINED}, // R16S
152 {VK_FORMAT_UNDEFINED}, // R16UI 151 {VK_FORMAT_R16_UINT, Attachable | Storage}, // R16UI
153 {VK_FORMAT_UNDEFINED}, // R16I 152 {VK_FORMAT_UNDEFINED}, // R16I
154 {VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // RG16 153 {VK_FORMAT_R16G16_UNORM, Attachable | Storage}, // RG16
155 {VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // RG16F 154 {VK_FORMAT_R16G16_SFLOAT, Attachable | Storage}, // RG16F
@@ -288,14 +287,35 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const VKDevice& device,
288 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; 287 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
289 case Maxwell::PrimitiveTopology::Patches: 288 case Maxwell::PrimitiveTopology::Patches:
290 return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; 289 return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
291 default:
292 UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology));
293 return {};
294 } 290 }
291 UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology));
292 return {};
295} 293}
296 294
297VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { 295VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) {
298 switch (type) { 296 switch (type) {
297 case Maxwell::VertexAttribute::Type::UnsignedNorm:
298 switch (size) {
299 case Maxwell::VertexAttribute::Size::Size_8:
300 return VK_FORMAT_R8_UNORM;
301 case Maxwell::VertexAttribute::Size::Size_8_8:
302 return VK_FORMAT_R8G8_UNORM;
303 case Maxwell::VertexAttribute::Size::Size_8_8_8:
304 return VK_FORMAT_R8G8B8_UNORM;
305 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
306 return VK_FORMAT_R8G8B8A8_UNORM;
307 case Maxwell::VertexAttribute::Size::Size_16:
308 return VK_FORMAT_R16_UNORM;
309 case Maxwell::VertexAttribute::Size::Size_16_16:
310 return VK_FORMAT_R16G16_UNORM;
311 case Maxwell::VertexAttribute::Size::Size_16_16_16:
312 return VK_FORMAT_R16G16B16_UNORM;
313 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
314 return VK_FORMAT_R16G16B16A16_UNORM;
315 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
316 return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
317 }
318 break;
299 case Maxwell::VertexAttribute::Type::SignedNorm: 319 case Maxwell::VertexAttribute::Type::SignedNorm:
300 switch (size) { 320 switch (size) {
301 case Maxwell::VertexAttribute::Size::Size_8: 321 case Maxwell::VertexAttribute::Size::Size_8:
@@ -316,62 +336,50 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
316 return VK_FORMAT_R16G16B16A16_SNORM; 336 return VK_FORMAT_R16G16B16A16_SNORM;
317 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 337 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
318 return VK_FORMAT_A2B10G10R10_SNORM_PACK32; 338 return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
319 default:
320 break;
321 } 339 }
322 break; 340 break;
323 case Maxwell::VertexAttribute::Type::UnsignedNorm: 341 case Maxwell::VertexAttribute::Type::UnsignedScaled:
324 switch (size) { 342 switch (size) {
325 case Maxwell::VertexAttribute::Size::Size_8: 343 case Maxwell::VertexAttribute::Size::Size_8:
326 return VK_FORMAT_R8_UNORM; 344 return VK_FORMAT_R8_USCALED;
327 case Maxwell::VertexAttribute::Size::Size_8_8: 345 case Maxwell::VertexAttribute::Size::Size_8_8:
328 return VK_FORMAT_R8G8_UNORM; 346 return VK_FORMAT_R8G8_USCALED;
329 case Maxwell::VertexAttribute::Size::Size_8_8_8: 347 case Maxwell::VertexAttribute::Size::Size_8_8_8:
330 return VK_FORMAT_R8G8B8_UNORM; 348 return VK_FORMAT_R8G8B8_USCALED;
331 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 349 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
332 return VK_FORMAT_R8G8B8A8_UNORM; 350 return VK_FORMAT_R8G8B8A8_USCALED;
333 case Maxwell::VertexAttribute::Size::Size_16: 351 case Maxwell::VertexAttribute::Size::Size_16:
334 return VK_FORMAT_R16_UNORM; 352 return VK_FORMAT_R16_USCALED;
335 case Maxwell::VertexAttribute::Size::Size_16_16: 353 case Maxwell::VertexAttribute::Size::Size_16_16:
336 return VK_FORMAT_R16G16_UNORM; 354 return VK_FORMAT_R16G16_USCALED;
337 case Maxwell::VertexAttribute::Size::Size_16_16_16: 355 case Maxwell::VertexAttribute::Size::Size_16_16_16:
338 return VK_FORMAT_R16G16B16_UNORM; 356 return VK_FORMAT_R16G16B16_USCALED;
339 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 357 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
340 return VK_FORMAT_R16G16B16A16_UNORM; 358 return VK_FORMAT_R16G16B16A16_USCALED;
341 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 359 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
342 return VK_FORMAT_A2B10G10R10_UNORM_PACK32; 360 return VK_FORMAT_A2B10G10R10_USCALED_PACK32;
343 default:
344 break;
345 } 361 }
346 break; 362 break;
347 case Maxwell::VertexAttribute::Type::SignedInt: 363 case Maxwell::VertexAttribute::Type::SignedScaled:
348 switch (size) { 364 switch (size) {
349 case Maxwell::VertexAttribute::Size::Size_8: 365 case Maxwell::VertexAttribute::Size::Size_8:
350 return VK_FORMAT_R8_SINT; 366 return VK_FORMAT_R8_SSCALED;
351 case Maxwell::VertexAttribute::Size::Size_8_8: 367 case Maxwell::VertexAttribute::Size::Size_8_8:
352 return VK_FORMAT_R8G8_SINT; 368 return VK_FORMAT_R8G8_SSCALED;
353 case Maxwell::VertexAttribute::Size::Size_8_8_8: 369 case Maxwell::VertexAttribute::Size::Size_8_8_8:
354 return VK_FORMAT_R8G8B8_SINT; 370 return VK_FORMAT_R8G8B8_SSCALED;
355 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 371 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
356 return VK_FORMAT_R8G8B8A8_SINT; 372 return VK_FORMAT_R8G8B8A8_SSCALED;
357 case Maxwell::VertexAttribute::Size::Size_16: 373 case Maxwell::VertexAttribute::Size::Size_16:
358 return VK_FORMAT_R16_SINT; 374 return VK_FORMAT_R16_SSCALED;
359 case Maxwell::VertexAttribute::Size::Size_16_16: 375 case Maxwell::VertexAttribute::Size::Size_16_16:
360 return VK_FORMAT_R16G16_SINT; 376 return VK_FORMAT_R16G16_SSCALED;
361 case Maxwell::VertexAttribute::Size::Size_16_16_16: 377 case Maxwell::VertexAttribute::Size::Size_16_16_16:
362 return VK_FORMAT_R16G16B16_SINT; 378 return VK_FORMAT_R16G16B16_SSCALED;
363 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 379 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
364 return VK_FORMAT_R16G16B16A16_SINT; 380 return VK_FORMAT_R16G16B16A16_SSCALED;
365 case Maxwell::VertexAttribute::Size::Size_32: 381 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
366 return VK_FORMAT_R32_SINT; 382 return VK_FORMAT_A2B10G10R10_SSCALED_PACK32;
367 case Maxwell::VertexAttribute::Size::Size_32_32:
368 return VK_FORMAT_R32G32_SINT;
369 case Maxwell::VertexAttribute::Size::Size_32_32_32:
370 return VK_FORMAT_R32G32B32_SINT;
371 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
372 return VK_FORMAT_R32G32B32A32_SINT;
373 default:
374 break;
375 } 383 }
376 break; 384 break;
377 case Maxwell::VertexAttribute::Type::UnsignedInt: 385 case Maxwell::VertexAttribute::Type::UnsignedInt:
@@ -400,56 +408,50 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
400 return VK_FORMAT_R32G32B32_UINT; 408 return VK_FORMAT_R32G32B32_UINT;
401 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 409 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
402 return VK_FORMAT_R32G32B32A32_UINT; 410 return VK_FORMAT_R32G32B32A32_UINT;
403 default: 411 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
404 break; 412 return VK_FORMAT_A2B10G10R10_UINT_PACK32;
405 } 413 }
406 break; 414 break;
407 case Maxwell::VertexAttribute::Type::UnsignedScaled: 415 case Maxwell::VertexAttribute::Type::SignedInt:
408 switch (size) { 416 switch (size) {
409 case Maxwell::VertexAttribute::Size::Size_8: 417 case Maxwell::VertexAttribute::Size::Size_8:
410 return VK_FORMAT_R8_USCALED; 418 return VK_FORMAT_R8_SINT;
411 case Maxwell::VertexAttribute::Size::Size_8_8: 419 case Maxwell::VertexAttribute::Size::Size_8_8:
412 return VK_FORMAT_R8G8_USCALED; 420 return VK_FORMAT_R8G8_SINT;
413 case Maxwell::VertexAttribute::Size::Size_8_8_8: 421 case Maxwell::VertexAttribute::Size::Size_8_8_8:
414 return VK_FORMAT_R8G8B8_USCALED; 422 return VK_FORMAT_R8G8B8_SINT;
415 case Maxwell::VertexAttribute::Size::Size_8_8_8_8: 423 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
416 return VK_FORMAT_R8G8B8A8_USCALED; 424 return VK_FORMAT_R8G8B8A8_SINT;
417 case Maxwell::VertexAttribute::Size::Size_16: 425 case Maxwell::VertexAttribute::Size::Size_16:
418 return VK_FORMAT_R16_USCALED; 426 return VK_FORMAT_R16_SINT;
419 case Maxwell::VertexAttribute::Size::Size_16_16: 427 case Maxwell::VertexAttribute::Size::Size_16_16:
420 return VK_FORMAT_R16G16_USCALED; 428 return VK_FORMAT_R16G16_SINT;
421 case Maxwell::VertexAttribute::Size::Size_16_16_16: 429 case Maxwell::VertexAttribute::Size::Size_16_16_16:
422 return VK_FORMAT_R16G16B16_USCALED; 430 return VK_FORMAT_R16G16B16_SINT;
423 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 431 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
424 return VK_FORMAT_R16G16B16A16_USCALED; 432 return VK_FORMAT_R16G16B16A16_SINT;
425 default: 433 case Maxwell::VertexAttribute::Size::Size_32:
426 break; 434 return VK_FORMAT_R32_SINT;
435 case Maxwell::VertexAttribute::Size::Size_32_32:
436 return VK_FORMAT_R32G32_SINT;
437 case Maxwell::VertexAttribute::Size::Size_32_32_32:
438 return VK_FORMAT_R32G32B32_SINT;
439 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
440 return VK_FORMAT_R32G32B32A32_SINT;
441 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
442 return VK_FORMAT_A2B10G10R10_SINT_PACK32;
427 } 443 }
428 break; 444 break;
429 case Maxwell::VertexAttribute::Type::SignedScaled: 445 case Maxwell::VertexAttribute::Type::Float:
430 switch (size) { 446 switch (size) {
431 case Maxwell::VertexAttribute::Size::Size_8:
432 return VK_FORMAT_R8_SSCALED;
433 case Maxwell::VertexAttribute::Size::Size_8_8:
434 return VK_FORMAT_R8G8_SSCALED;
435 case Maxwell::VertexAttribute::Size::Size_8_8_8:
436 return VK_FORMAT_R8G8B8_SSCALED;
437 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
438 return VK_FORMAT_R8G8B8A8_SSCALED;
439 case Maxwell::VertexAttribute::Size::Size_16: 447 case Maxwell::VertexAttribute::Size::Size_16:
440 return VK_FORMAT_R16_SSCALED; 448 return VK_FORMAT_R16_SFLOAT;
441 case Maxwell::VertexAttribute::Size::Size_16_16: 449 case Maxwell::VertexAttribute::Size::Size_16_16:
442 return VK_FORMAT_R16G16_SSCALED; 450 return VK_FORMAT_R16G16_SFLOAT;
443 case Maxwell::VertexAttribute::Size::Size_16_16_16: 451 case Maxwell::VertexAttribute::Size::Size_16_16_16:
444 return VK_FORMAT_R16G16B16_SSCALED; 452 return VK_FORMAT_R16G16B16_SFLOAT;
445 case Maxwell::VertexAttribute::Size::Size_16_16_16_16: 453 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
446 return VK_FORMAT_R16G16B16A16_SSCALED; 454 return VK_FORMAT_R16G16B16A16_SFLOAT;
447 default:
448 break;
449 }
450 break;
451 case Maxwell::VertexAttribute::Type::Float:
452 switch (size) {
453 case Maxwell::VertexAttribute::Size::Size_32: 455 case Maxwell::VertexAttribute::Size::Size_32:
454 return VK_FORMAT_R32_SFLOAT; 456 return VK_FORMAT_R32_SFLOAT;
455 case Maxwell::VertexAttribute::Size::Size_32_32: 457 case Maxwell::VertexAttribute::Size::Size_32_32:
@@ -458,16 +460,6 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
458 return VK_FORMAT_R32G32B32_SFLOAT; 460 return VK_FORMAT_R32G32B32_SFLOAT;
459 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 461 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
460 return VK_FORMAT_R32G32B32A32_SFLOAT; 462 return VK_FORMAT_R32G32B32A32_SFLOAT;
461 case Maxwell::VertexAttribute::Size::Size_16:
462 return VK_FORMAT_R16_SFLOAT;
463 case Maxwell::VertexAttribute::Size::Size_16_16:
464 return VK_FORMAT_R16G16_SFLOAT;
465 case Maxwell::VertexAttribute::Size::Size_16_16_16:
466 return VK_FORMAT_R16G16B16_SFLOAT;
467 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
468 return VK_FORMAT_R16G16B16A16_SFLOAT;
469 default:
470 break;
471 } 463 }
472 break; 464 break;
473 } 465 }
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 59b441943..2258479f5 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -13,6 +13,7 @@
13#include <fmt/format.h> 13#include <fmt/format.h>
14 14
15#include "common/dynamic_library.h" 15#include "common/dynamic_library.h"
16#include "common/file_util.h"
16#include "common/logging/log.h" 17#include "common/logging/log.h"
17#include "common/telemetry.h" 18#include "common/telemetry.h"
18#include "core/core.h" 19#include "core/core.h"
@@ -76,7 +77,8 @@ Common::DynamicLibrary OpenVulkanLibrary() {
76 char* libvulkan_env = getenv("LIBVULKAN_PATH"); 77 char* libvulkan_env = getenv("LIBVULKAN_PATH");
77 if (!libvulkan_env || !library.Open(libvulkan_env)) { 78 if (!libvulkan_env || !library.Open(libvulkan_env)) {
78 // Use the libvulkan.dylib from the application bundle. 79 // Use the libvulkan.dylib from the application bundle.
79 std::string filename = File::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib"; 80 const std::string filename =
81 FileUtil::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib";
80 library.Open(filename.c_str()); 82 library.Open(filename.c_str());
81 } 83 }
82#else 84#else
@@ -153,11 +155,31 @@ vk::Instance CreateInstance(Common::DynamicLibrary& library, vk::InstanceDispatc
153 } 155 }
154 } 156 }
155 157
156 static constexpr std::array layers_data{"VK_LAYER_LUNARG_standard_validation"}; 158 std::vector<const char*> layers;
157 vk::Span<const char*> layers = layers_data; 159 layers.reserve(1);
158 if (!enable_layers) { 160 if (enable_layers) {
159 layers = {}; 161 layers.push_back("VK_LAYER_KHRONOS_validation");
162 }
163
164 const std::optional layer_properties = vk::EnumerateInstanceLayerProperties(dld);
165 if (!layer_properties) {
166 LOG_ERROR(Render_Vulkan, "Failed to query layer properties, disabling layers");
167 layers.clear();
168 }
169
170 for (auto layer_it = layers.begin(); layer_it != layers.end();) {
171 const char* const layer = *layer_it;
172 const auto it = std::find_if(
173 layer_properties->begin(), layer_properties->end(),
174 [layer](const VkLayerProperties& prop) { return !std::strcmp(layer, prop.layerName); });
175 if (it == layer_properties->end()) {
176 LOG_ERROR(Render_Vulkan, "Layer {} not available, removing it", layer);
177 layer_it = layers.erase(layer_it);
178 } else {
179 ++layer_it;
180 }
160 } 181 }
182
161 vk::Instance instance = vk::Instance::Create(layers, extensions, dld); 183 vk::Instance instance = vk::Instance::Create(layers, extensions, dld);
162 if (!instance) { 184 if (!instance) {
163 LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance"); 185 LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance");
@@ -387,7 +409,7 @@ bool RendererVulkan::PickDevices() {
387 return false; 409 return false;
388 } 410 }
389 411
390 const s32 device_index = Settings::values.vulkan_device; 412 const s32 device_index = Settings::values.vulkan_device.GetValue();
391 if (device_index < 0 || device_index >= static_cast<s32>(devices->size())) { 413 if (device_index < 0 || device_index >= static_cast<s32>(devices->size())) {
392 LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); 414 LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
393 return false; 415 return false;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 5f33d9e40..2be38d419 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -37,9 +37,9 @@ std::unique_ptr<VKStreamBuffer> CreateStreamBuffer(const VKDevice& device, VKSch
37 37
38} // Anonymous namespace 38} // Anonymous namespace
39 39
40CachedBufferBlock::CachedBufferBlock(const VKDevice& device, VKMemoryManager& memory_manager, 40Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler_,
41 VAddr cpu_addr, std::size_t size) 41 VKStagingBufferPool& staging_pool_, VAddr cpu_addr, std::size_t size)
42 : VideoCommon::BufferBlock{cpu_addr, size} { 42 : VideoCommon::BufferBlock{cpu_addr, size}, scheduler{scheduler_}, staging_pool{staging_pool_} {
43 VkBufferCreateInfo ci; 43 VkBufferCreateInfo ci;
44 ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; 44 ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
45 ci.pNext = nullptr; 45 ci.pNext = nullptr;
@@ -54,46 +54,17 @@ CachedBufferBlock::CachedBufferBlock(const VKDevice& device, VKMemoryManager& me
54 buffer.commit = memory_manager.Commit(buffer.handle, false); 54 buffer.commit = memory_manager.Commit(buffer.handle, false);
55} 55}
56 56
57CachedBufferBlock::~CachedBufferBlock() = default; 57Buffer::~Buffer() = default;
58 58
59VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, 59void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) {
60 const VKDevice& device, VKMemoryManager& memory_manager,
61 VKScheduler& scheduler, VKStagingBufferPool& staging_pool)
62 : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer, system,
63 CreateStreamBuffer(device,
64 scheduler)},
65 device{device}, memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{
66 staging_pool} {}
67
68VKBufferCache::~VKBufferCache() = default;
69
70Buffer VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) {
71 return std::make_shared<CachedBufferBlock>(device, memory_manager, cpu_addr, size);
72}
73
74VkBuffer VKBufferCache::ToHandle(const Buffer& buffer) {
75 return buffer->GetHandle();
76}
77
78VkBuffer VKBufferCache::GetEmptyBuffer(std::size_t size) {
79 size = std::max(size, std::size_t(4));
80 const auto& empty = staging_pool.GetUnusedBuffer(size, false);
81 scheduler.RequestOutsideRenderPassOperationContext();
82 scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) {
83 cmdbuf.FillBuffer(buffer, 0, size, 0);
84 });
85 return *empty.handle;
86}
87
88void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
89 const u8* data) {
90 const auto& staging = staging_pool.GetUnusedBuffer(size, true); 60 const auto& staging = staging_pool.GetUnusedBuffer(size, true);
91 std::memcpy(staging.commit->Map(size), data, size); 61 std::memcpy(staging.commit->Map(size), data, size);
92 62
93 scheduler.RequestOutsideRenderPassOperationContext(); 63 scheduler.RequestOutsideRenderPassOperationContext();
94 scheduler.Record([staging = *staging.handle, buffer = buffer->GetHandle(), offset, 64
95 size](vk::CommandBuffer cmdbuf) { 65 const VkBuffer handle = Handle();
96 cmdbuf.CopyBuffer(staging, buffer, VkBufferCopy{0, offset, size}); 66 scheduler.Record([staging = *staging.handle, handle, offset, size](vk::CommandBuffer cmdbuf) {
67 cmdbuf.CopyBuffer(staging, handle, VkBufferCopy{0, offset, size});
97 68
98 VkBufferMemoryBarrier barrier; 69 VkBufferMemoryBarrier barrier;
99 barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; 70 barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
@@ -102,7 +73,7 @@ void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, st
102 barrier.dstAccessMask = UPLOAD_ACCESS_BARRIERS; 73 barrier.dstAccessMask = UPLOAD_ACCESS_BARRIERS;
103 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 74 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
104 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 75 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
105 barrier.buffer = buffer; 76 barrier.buffer = handle;
106 barrier.offset = offset; 77 barrier.offset = offset;
107 barrier.size = size; 78 barrier.size = size;
108 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, UPLOAD_PIPELINE_STAGE, 0, {}, 79 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, UPLOAD_PIPELINE_STAGE, 0, {},
@@ -110,12 +81,12 @@ void VKBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, st
110 }); 81 });
111} 82}
112 83
113void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, 84void Buffer::Download(std::size_t offset, std::size_t size, u8* data) {
114 u8* data) {
115 const auto& staging = staging_pool.GetUnusedBuffer(size, true); 85 const auto& staging = staging_pool.GetUnusedBuffer(size, true);
116 scheduler.RequestOutsideRenderPassOperationContext(); 86 scheduler.RequestOutsideRenderPassOperationContext();
117 scheduler.Record([staging = *staging.handle, buffer = buffer->GetHandle(), offset, 87
118 size](vk::CommandBuffer cmdbuf) { 88 const VkBuffer handle = Handle();
89 scheduler.Record([staging = *staging.handle, handle, offset, size](vk::CommandBuffer cmdbuf) {
119 VkBufferMemoryBarrier barrier; 90 VkBufferMemoryBarrier barrier;
120 barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; 91 barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
121 barrier.pNext = nullptr; 92 barrier.pNext = nullptr;
@@ -123,7 +94,7 @@ void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset,
123 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; 94 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
124 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 95 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
125 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 96 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
126 barrier.buffer = buffer; 97 barrier.buffer = handle;
127 barrier.offset = offset; 98 barrier.offset = offset;
128 barrier.size = size; 99 barrier.size = size;
129 100
@@ -131,18 +102,20 @@ void VKBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset,
131 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | 102 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
132 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 103 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
133 VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, barrier, {}); 104 VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, barrier, {});
134 cmdbuf.CopyBuffer(buffer, staging, VkBufferCopy{offset, 0, size}); 105 cmdbuf.CopyBuffer(handle, staging, VkBufferCopy{offset, 0, size});
135 }); 106 });
136 scheduler.Finish(); 107 scheduler.Finish();
137 108
138 std::memcpy(data, staging.commit->Map(size), size); 109 std::memcpy(data, staging.commit->Map(size), size);
139} 110}
140 111
141void VKBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset, 112void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
142 std::size_t dst_offset, std::size_t size) { 113 std::size_t size) {
143 scheduler.RequestOutsideRenderPassOperationContext(); 114 scheduler.RequestOutsideRenderPassOperationContext();
144 scheduler.Record([src_buffer = src->GetHandle(), dst_buffer = dst->GetHandle(), src_offset, 115
145 dst_offset, size](vk::CommandBuffer cmdbuf) { 116 const VkBuffer dst_buffer = Handle();
117 scheduler.Record([src_buffer = src.Handle(), dst_buffer, src_offset, dst_offset,
118 size](vk::CommandBuffer cmdbuf) {
146 cmdbuf.CopyBuffer(src_buffer, dst_buffer, VkBufferCopy{src_offset, dst_offset, size}); 119 cmdbuf.CopyBuffer(src_buffer, dst_buffer, VkBufferCopy{src_offset, dst_offset, size});
147 120
148 std::array<VkBufferMemoryBarrier, 2> barriers; 121 std::array<VkBufferMemoryBarrier, 2> barriers;
@@ -169,4 +142,30 @@ void VKBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t
169 }); 142 });
170} 143}
171 144
145VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system,
146 const VKDevice& device, VKMemoryManager& memory_manager,
147 VKScheduler& scheduler, VKStagingBufferPool& staging_pool)
148 : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer, system,
149 CreateStreamBuffer(device,
150 scheduler)},
151 device{device}, memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{
152 staging_pool} {}
153
154VKBufferCache::~VKBufferCache() = default;
155
156std::shared_ptr<Buffer> VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) {
157 return std::make_shared<Buffer>(device, memory_manager, scheduler, staging_pool, cpu_addr,
158 size);
159}
160
161VKBufferCache::BufferInfo VKBufferCache::GetEmptyBuffer(std::size_t size) {
162 size = std::max(size, std::size_t(4));
163 const auto& empty = staging_pool.GetUnusedBuffer(size, false);
164 scheduler.RequestOutsideRenderPassOperationContext();
165 scheduler.Record([size, buffer = *empty.handle](vk::CommandBuffer cmdbuf) {
166 cmdbuf.FillBuffer(buffer, 0, size, 0);
167 });
168 return {*empty.handle, 0, 0};
169}
170
172} // namespace Vulkan 171} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index a54583e7d..991ee451c 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -8,7 +8,6 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/buffer_cache/buffer_cache.h" 10#include "video_core/buffer_cache/buffer_cache.h"
11#include "video_core/rasterizer_cache.h"
12#include "video_core/renderer_vulkan/vk_memory_manager.h" 11#include "video_core/renderer_vulkan/vk_memory_manager.h"
13#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" 12#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
14#include "video_core/renderer_vulkan/vk_stream_buffer.h" 13#include "video_core/renderer_vulkan/vk_stream_buffer.h"
@@ -24,22 +23,34 @@ class VKDevice;
24class VKMemoryManager; 23class VKMemoryManager;
25class VKScheduler; 24class VKScheduler;
26 25
27class CachedBufferBlock final : public VideoCommon::BufferBlock { 26class Buffer final : public VideoCommon::BufferBlock {
28public: 27public:
29 explicit CachedBufferBlock(const VKDevice& device, VKMemoryManager& memory_manager, 28 explicit Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler,
30 VAddr cpu_addr, std::size_t size); 29 VKStagingBufferPool& staging_pool, VAddr cpu_addr, std::size_t size);
31 ~CachedBufferBlock(); 30 ~Buffer();
32 31
33 VkBuffer GetHandle() const { 32 void Upload(std::size_t offset, std::size_t size, const u8* data);
33
34 void Download(std::size_t offset, std::size_t size, u8* data);
35
36 void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset,
37 std::size_t size);
38
39 VkBuffer Handle() const {
34 return *buffer.handle; 40 return *buffer.handle;
35 } 41 }
36 42
43 u64 Address() const {
44 return 0;
45 }
46
37private: 47private:
48 VKScheduler& scheduler;
49 VKStagingBufferPool& staging_pool;
50
38 VKBuffer buffer; 51 VKBuffer buffer;
39}; 52};
40 53
41using Buffer = std::shared_ptr<CachedBufferBlock>;
42
43class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> { 54class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> {
44public: 55public:
45 explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, 56 explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system,
@@ -47,21 +58,10 @@ public:
47 VKScheduler& scheduler, VKStagingBufferPool& staging_pool); 58 VKScheduler& scheduler, VKStagingBufferPool& staging_pool);
48 ~VKBufferCache(); 59 ~VKBufferCache();
49 60
50 VkBuffer GetEmptyBuffer(std::size_t size) override; 61 BufferInfo GetEmptyBuffer(std::size_t size) override;
51 62
52protected: 63protected:
53 VkBuffer ToHandle(const Buffer& buffer) override; 64 std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) override;
54
55 Buffer CreateBlock(VAddr cpu_addr, std::size_t size) override;
56
57 void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
58 const u8* data) override;
59
60 void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
61 u8* data) override;
62
63 void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset,
64 std::size_t dst_offset, std::size_t size) override;
65 65
66private: 66private:
67 const VKDevice& device; 67 const VKDevice& device;
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 8e1b46277..281bf9ac3 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -53,8 +53,9 @@ vk::DescriptorSetLayout VKComputePipeline::CreateDescriptorSetLayout() const {
53 }; 53 };
54 add_bindings(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, entries.const_buffers.size()); 54 add_bindings(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, entries.const_buffers.size());
55 add_bindings(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, entries.global_buffers.size()); 55 add_bindings(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, entries.global_buffers.size());
56 add_bindings(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, entries.texel_buffers.size()); 56 add_bindings(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, entries.uniform_texels.size());
57 add_bindings(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, entries.samplers.size()); 57 add_bindings(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, entries.samplers.size());
58 add_bindings(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, entries.storage_texels.size());
58 add_bindings(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, entries.images.size()); 59 add_bindings(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, entries.images.size());
59 60
60 VkDescriptorSetLayoutCreateInfo ci; 61 VkDescriptorSetLayoutCreateInfo ci;
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
index 890fd52cf..9259b618d 100644
--- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
@@ -42,6 +42,7 @@ vk::DescriptorPool* VKDescriptorPool::AllocateNewPool() {
42 {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, num_sets * 60}, 42 {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, num_sets * 60},
43 {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, num_sets * 64}, 43 {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, num_sets * 64},
44 {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, num_sets * 64}, 44 {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, num_sets * 64},
45 {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, num_sets * 64},
45 {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, num_sets * 40}}; 46 {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, num_sets * 40}};
46 47
47 VkDescriptorPoolCreateInfo ci; 48 VkDescriptorPoolCreateInfo ci;
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index 750e5a0ca..fdaea4210 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -73,76 +73,79 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType
73 73
74std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties( 74std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(
75 vk::PhysicalDevice physical, const vk::InstanceDispatch& dld) { 75 vk::PhysicalDevice physical, const vk::InstanceDispatch& dld) {
76 static constexpr std::array formats{VK_FORMAT_A8B8G8R8_UNORM_PACK32, 76 static constexpr std::array formats{
77 VK_FORMAT_A8B8G8R8_UINT_PACK32, 77 VK_FORMAT_A8B8G8R8_UNORM_PACK32,
78 VK_FORMAT_A8B8G8R8_SNORM_PACK32, 78 VK_FORMAT_A8B8G8R8_UINT_PACK32,
79 VK_FORMAT_A8B8G8R8_SRGB_PACK32, 79 VK_FORMAT_A8B8G8R8_SNORM_PACK32,
80 VK_FORMAT_B5G6R5_UNORM_PACK16, 80 VK_FORMAT_A8B8G8R8_SRGB_PACK32,
81 VK_FORMAT_A2B10G10R10_UNORM_PACK32, 81 VK_FORMAT_B5G6R5_UNORM_PACK16,
82 VK_FORMAT_A1R5G5B5_UNORM_PACK16, 82 VK_FORMAT_A2B10G10R10_UNORM_PACK32,
83 VK_FORMAT_R32G32B32A32_SFLOAT, 83 VK_FORMAT_A1R5G5B5_UNORM_PACK16,
84 VK_FORMAT_R32G32B32A32_UINT, 84 VK_FORMAT_R32G32B32A32_SFLOAT,
85 VK_FORMAT_R32G32_SFLOAT, 85 VK_FORMAT_R32G32B32A32_UINT,
86 VK_FORMAT_R32G32_UINT, 86 VK_FORMAT_R32G32_SFLOAT,
87 VK_FORMAT_R16G16B16A16_UINT, 87 VK_FORMAT_R32G32_UINT,
88 VK_FORMAT_R16G16B16A16_SNORM, 88 VK_FORMAT_R16G16B16A16_UINT,
89 VK_FORMAT_R16G16B16A16_UNORM, 89 VK_FORMAT_R16G16B16A16_SNORM,
90 VK_FORMAT_R16G16_UNORM, 90 VK_FORMAT_R16G16B16A16_UNORM,
91 VK_FORMAT_R16G16_SNORM, 91 VK_FORMAT_R16G16_UNORM,
92 VK_FORMAT_R16G16_SFLOAT, 92 VK_FORMAT_R16G16_SNORM,
93 VK_FORMAT_R16_UNORM, 93 VK_FORMAT_R16G16_SFLOAT,
94 VK_FORMAT_R8G8B8A8_SRGB, 94 VK_FORMAT_R16_UNORM,
95 VK_FORMAT_R8G8_UNORM, 95 VK_FORMAT_R16_UINT,
96 VK_FORMAT_R8G8_SNORM, 96 VK_FORMAT_R8G8B8A8_SRGB,
97 VK_FORMAT_R8G8_UINT, 97 VK_FORMAT_R8G8_UNORM,
98 VK_FORMAT_R8_UNORM, 98 VK_FORMAT_R8G8_SNORM,
99 VK_FORMAT_R8_UINT, 99 VK_FORMAT_R8G8_UINT,
100 VK_FORMAT_B10G11R11_UFLOAT_PACK32, 100 VK_FORMAT_R8_UNORM,
101 VK_FORMAT_R32_SFLOAT, 101 VK_FORMAT_R8_UINT,
102 VK_FORMAT_R32_UINT, 102 VK_FORMAT_B10G11R11_UFLOAT_PACK32,
103 VK_FORMAT_R32_SINT, 103 VK_FORMAT_R32_SFLOAT,
104 VK_FORMAT_R16_SFLOAT, 104 VK_FORMAT_R32_UINT,
105 VK_FORMAT_R16G16B16A16_SFLOAT, 105 VK_FORMAT_R32_SINT,
106 VK_FORMAT_B8G8R8A8_UNORM, 106 VK_FORMAT_R16_SFLOAT,
107 VK_FORMAT_B8G8R8A8_SRGB, 107 VK_FORMAT_R16G16B16A16_SFLOAT,
108 VK_FORMAT_R4G4B4A4_UNORM_PACK16, 108 VK_FORMAT_B8G8R8A8_UNORM,
109 VK_FORMAT_D32_SFLOAT, 109 VK_FORMAT_B8G8R8A8_SRGB,
110 VK_FORMAT_D16_UNORM, 110 VK_FORMAT_R4G4B4A4_UNORM_PACK16,
111 VK_FORMAT_D16_UNORM_S8_UINT, 111 VK_FORMAT_D32_SFLOAT,
112 VK_FORMAT_D24_UNORM_S8_UINT, 112 VK_FORMAT_D16_UNORM,
113 VK_FORMAT_D32_SFLOAT_S8_UINT, 113 VK_FORMAT_D16_UNORM_S8_UINT,
114 VK_FORMAT_BC1_RGBA_UNORM_BLOCK, 114 VK_FORMAT_D24_UNORM_S8_UINT,
115 VK_FORMAT_BC2_UNORM_BLOCK, 115 VK_FORMAT_D32_SFLOAT_S8_UINT,
116 VK_FORMAT_BC3_UNORM_BLOCK, 116 VK_FORMAT_BC1_RGBA_UNORM_BLOCK,
117 VK_FORMAT_BC4_UNORM_BLOCK, 117 VK_FORMAT_BC2_UNORM_BLOCK,
118 VK_FORMAT_BC5_UNORM_BLOCK, 118 VK_FORMAT_BC3_UNORM_BLOCK,
119 VK_FORMAT_BC5_SNORM_BLOCK, 119 VK_FORMAT_BC4_UNORM_BLOCK,
120 VK_FORMAT_BC7_UNORM_BLOCK, 120 VK_FORMAT_BC5_UNORM_BLOCK,
121 VK_FORMAT_BC6H_UFLOAT_BLOCK, 121 VK_FORMAT_BC5_SNORM_BLOCK,
122 VK_FORMAT_BC6H_SFLOAT_BLOCK, 122 VK_FORMAT_BC7_UNORM_BLOCK,
123 VK_FORMAT_BC1_RGBA_SRGB_BLOCK, 123 VK_FORMAT_BC6H_UFLOAT_BLOCK,
124 VK_FORMAT_BC2_SRGB_BLOCK, 124 VK_FORMAT_BC6H_SFLOAT_BLOCK,
125 VK_FORMAT_BC3_SRGB_BLOCK, 125 VK_FORMAT_BC1_RGBA_SRGB_BLOCK,
126 VK_FORMAT_BC7_SRGB_BLOCK, 126 VK_FORMAT_BC2_SRGB_BLOCK,
127 VK_FORMAT_ASTC_4x4_SRGB_BLOCK, 127 VK_FORMAT_BC3_SRGB_BLOCK,
128 VK_FORMAT_ASTC_8x8_SRGB_BLOCK, 128 VK_FORMAT_BC7_SRGB_BLOCK,
129 VK_FORMAT_ASTC_8x5_SRGB_BLOCK, 129 VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
130 VK_FORMAT_ASTC_5x4_SRGB_BLOCK, 130 VK_FORMAT_ASTC_8x8_SRGB_BLOCK,
131 VK_FORMAT_ASTC_5x5_UNORM_BLOCK, 131 VK_FORMAT_ASTC_8x5_SRGB_BLOCK,
132 VK_FORMAT_ASTC_5x5_SRGB_BLOCK, 132 VK_FORMAT_ASTC_5x4_SRGB_BLOCK,
133 VK_FORMAT_ASTC_10x8_UNORM_BLOCK, 133 VK_FORMAT_ASTC_5x5_UNORM_BLOCK,
134 VK_FORMAT_ASTC_10x8_SRGB_BLOCK, 134 VK_FORMAT_ASTC_5x5_SRGB_BLOCK,
135 VK_FORMAT_ASTC_6x6_UNORM_BLOCK, 135 VK_FORMAT_ASTC_10x8_UNORM_BLOCK,
136 VK_FORMAT_ASTC_6x6_SRGB_BLOCK, 136 VK_FORMAT_ASTC_10x8_SRGB_BLOCK,
137 VK_FORMAT_ASTC_10x10_UNORM_BLOCK, 137 VK_FORMAT_ASTC_6x6_UNORM_BLOCK,
138 VK_FORMAT_ASTC_10x10_SRGB_BLOCK, 138 VK_FORMAT_ASTC_6x6_SRGB_BLOCK,
139 VK_FORMAT_ASTC_12x12_UNORM_BLOCK, 139 VK_FORMAT_ASTC_10x10_UNORM_BLOCK,
140 VK_FORMAT_ASTC_12x12_SRGB_BLOCK, 140 VK_FORMAT_ASTC_10x10_SRGB_BLOCK,
141 VK_FORMAT_ASTC_8x6_UNORM_BLOCK, 141 VK_FORMAT_ASTC_12x12_UNORM_BLOCK,
142 VK_FORMAT_ASTC_8x6_SRGB_BLOCK, 142 VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
143 VK_FORMAT_ASTC_6x5_UNORM_BLOCK, 143 VK_FORMAT_ASTC_8x6_UNORM_BLOCK,
144 VK_FORMAT_ASTC_6x5_SRGB_BLOCK, 144 VK_FORMAT_ASTC_8x6_SRGB_BLOCK,
145 VK_FORMAT_E5B9G9R9_UFLOAT_PACK32}; 145 VK_FORMAT_ASTC_6x5_UNORM_BLOCK,
146 VK_FORMAT_ASTC_6x5_SRGB_BLOCK,
147 VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,
148 };
146 std::unordered_map<VkFormat, VkFormatProperties> format_properties; 149 std::unordered_map<VkFormat, VkFormatProperties> format_properties;
147 for (const auto format : formats) { 150 for (const auto format : formats) {
148 format_properties.emplace(format, physical.GetFormatProperties(format)); 151 format_properties.emplace(format, physical.GetFormatProperties(format));
@@ -310,6 +313,16 @@ bool VKDevice::Create() {
310 LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors"); 313 LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors");
311 } 314 }
312 315
316 VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
317 if (ext_extended_dynamic_state) {
318 dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
319 dynamic_state.pNext = nullptr;
320 dynamic_state.extendedDynamicState = VK_TRUE;
321 SetNext(next, dynamic_state);
322 } else {
323 LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
324 }
325
313 if (!ext_depth_range_unrestricted) { 326 if (!ext_depth_range_unrestricted) {
314 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); 327 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
315 } 328 }
@@ -538,6 +551,7 @@ std::vector<const char*> VKDevice::LoadExtensions() {
538 bool has_ext_subgroup_size_control{}; 551 bool has_ext_subgroup_size_control{};
539 bool has_ext_transform_feedback{}; 552 bool has_ext_transform_feedback{};
540 bool has_ext_custom_border_color{}; 553 bool has_ext_custom_border_color{};
554 bool has_ext_extended_dynamic_state{};
541 for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) { 555 for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) {
542 Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true); 556 Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true);
543 Test(extension, khr_uniform_buffer_standard_layout, 557 Test(extension, khr_uniform_buffer_standard_layout,
@@ -555,6 +569,8 @@ std::vector<const char*> VKDevice::LoadExtensions() {
555 false); 569 false);
556 Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, 570 Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME,
557 false); 571 false);
572 Test(extension, has_ext_extended_dynamic_state,
573 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
558 if (Settings::values.renderer_debug) { 574 if (Settings::values.renderer_debug) {
559 Test(extension, nv_device_diagnostics_config, 575 Test(extension, nv_device_diagnostics_config,
560 VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true); 576 VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true);
@@ -640,6 +656,19 @@ std::vector<const char*> VKDevice::LoadExtensions() {
640 } 656 }
641 } 657 }
642 658
659 if (has_ext_extended_dynamic_state) {
660 VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
661 dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
662 dynamic_state.pNext = nullptr;
663 features.pNext = &dynamic_state;
664 physical.GetFeatures2KHR(features);
665
666 if (dynamic_state.extendedDynamicState) {
667 extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
668 ext_extended_dynamic_state = true;
669 }
670 }
671
643 return extensions; 672 return extensions;
644} 673}
645 674
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h
index 6b9227b09..ae5c21baa 100644
--- a/src/video_core/renderer_vulkan/vk_device.h
+++ b/src/video_core/renderer_vulkan/vk_device.h
@@ -182,6 +182,11 @@ public:
182 return ext_custom_border_color; 182 return ext_custom_border_color;
183 } 183 }
184 184
185 /// Returns true if the device supports VK_EXT_extended_dynamic_state.
186 bool IsExtExtendedDynamicStateSupported() const {
187 return ext_extended_dynamic_state;
188 }
189
185 /// Returns the vendor name reported from Vulkan. 190 /// Returns the vendor name reported from Vulkan.
186 std::string_view GetVendorName() const { 191 std::string_view GetVendorName() const {
187 return vendor_name; 192 return vendor_name;
@@ -239,6 +244,7 @@ private:
239 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. 244 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
240 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. 245 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
241 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. 246 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
247 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
242 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. 248 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
243 249
244 // Telemetry parameters 250 // Telemetry parameters
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 69b6bba00..844445105 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -176,20 +176,32 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
176 176
177vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, 177vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
178 const SPIRVProgram& program) const { 178 const SPIRVProgram& program) const {
179 const auto& vi = fixed_state.vertex_input; 179 const auto& state = fixed_state;
180 const auto& ds = fixed_state.depth_stencil; 180 const auto& viewport_swizzles = state.viewport_swizzles;
181 const auto& cd = fixed_state.color_blending; 181
182 const auto& rs = fixed_state.rasterizer; 182 FixedPipelineState::DynamicState dynamic;
183 const auto& viewport_swizzles = fixed_state.viewport_swizzles.swizzles; 183 if (device.IsExtExtendedDynamicStateSupported()) {
184 // Insert dummy values, as long as they are valid they don't matter as extended dynamic
185 // state is ignored
186 dynamic.raw1 = 0;
187 dynamic.raw2 = 0;
188 for (FixedPipelineState::VertexBinding& binding : dynamic.vertex_bindings) {
189 // Enable all vertex bindings
190 binding.raw = 0;
191 binding.enabled.Assign(1);
192 }
193 } else {
194 dynamic = state.dynamic_state;
195 }
184 196
185 std::vector<VkVertexInputBindingDescription> vertex_bindings; 197 std::vector<VkVertexInputBindingDescription> vertex_bindings;
186 std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; 198 std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors;
187 for (std::size_t index = 0; index < std::size(vi.bindings); ++index) { 199 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
188 const auto& binding = vi.bindings[index]; 200 const auto& binding = dynamic.vertex_bindings[index];
189 if (!binding.enabled) { 201 if (!binding.enabled) {
190 continue; 202 continue;
191 } 203 }
192 const bool instanced = vi.binding_divisors[index] != 0; 204 const bool instanced = state.binding_divisors[index] != 0;
193 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; 205 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
194 206
195 auto& vertex_binding = vertex_bindings.emplace_back(); 207 auto& vertex_binding = vertex_bindings.emplace_back();
@@ -200,14 +212,14 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
200 if (instanced) { 212 if (instanced) {
201 auto& binding_divisor = vertex_binding_divisors.emplace_back(); 213 auto& binding_divisor = vertex_binding_divisors.emplace_back();
202 binding_divisor.binding = static_cast<u32>(index); 214 binding_divisor.binding = static_cast<u32>(index);
203 binding_divisor.divisor = vi.binding_divisors[index]; 215 binding_divisor.divisor = state.binding_divisors[index];
204 } 216 }
205 } 217 }
206 218
207 std::vector<VkVertexInputAttributeDescription> vertex_attributes; 219 std::vector<VkVertexInputAttributeDescription> vertex_attributes;
208 const auto& input_attributes = program[0]->entries.attributes; 220 const auto& input_attributes = program[0]->entries.attributes;
209 for (std::size_t index = 0; index < std::size(vi.attributes); ++index) { 221 for (std::size_t index = 0; index < state.attributes.size(); ++index) {
210 const auto& attribute = vi.attributes[index]; 222 const auto& attribute = state.attributes[index];
211 if (!attribute.enabled) { 223 if (!attribute.enabled) {
212 continue; 224 continue;
213 } 225 }
@@ -244,15 +256,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
244 input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; 256 input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
245 input_assembly_ci.pNext = nullptr; 257 input_assembly_ci.pNext = nullptr;
246 input_assembly_ci.flags = 0; 258 input_assembly_ci.flags = 0;
247 input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, rs.Topology()); 259 input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology());
248 input_assembly_ci.primitiveRestartEnable = 260 input_assembly_ci.primitiveRestartEnable =
249 rs.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology); 261 state.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology);
250 262
251 VkPipelineTessellationStateCreateInfo tessellation_ci; 263 VkPipelineTessellationStateCreateInfo tessellation_ci;
252 tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; 264 tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
253 tessellation_ci.pNext = nullptr; 265 tessellation_ci.pNext = nullptr;
254 tessellation_ci.flags = 0; 266 tessellation_ci.flags = 0;
255 tessellation_ci.patchControlPoints = rs.patch_control_points_minus_one.Value() + 1; 267 tessellation_ci.patchControlPoints = state.patch_control_points_minus_one.Value() + 1;
256 268
257 VkPipelineViewportStateCreateInfo viewport_ci; 269 VkPipelineViewportStateCreateInfo viewport_ci;
258 viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; 270 viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
@@ -280,13 +292,13 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
280 rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; 292 rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
281 rasterization_ci.pNext = nullptr; 293 rasterization_ci.pNext = nullptr;
282 rasterization_ci.flags = 0; 294 rasterization_ci.flags = 0;
283 rasterization_ci.depthClampEnable = rs.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE; 295 rasterization_ci.depthClampEnable = state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE;
284 rasterization_ci.rasterizerDiscardEnable = rs.rasterize_enable == 0 ? VK_TRUE : VK_FALSE; 296 rasterization_ci.rasterizerDiscardEnable = state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE;
285 rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL; 297 rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL;
286 rasterization_ci.cullMode = 298 rasterization_ci.cullMode =
287 rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE; 299 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE;
288 rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.FrontFace()); 300 rasterization_ci.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace());
289 rasterization_ci.depthBiasEnable = rs.depth_bias_enable; 301 rasterization_ci.depthBiasEnable = state.depth_bias_enable;
290 rasterization_ci.depthBiasConstantFactor = 0.0f; 302 rasterization_ci.depthBiasConstantFactor = 0.0f;
291 rasterization_ci.depthBiasClamp = 0.0f; 303 rasterization_ci.depthBiasClamp = 0.0f;
292 rasterization_ci.depthBiasSlopeFactor = 0.0f; 304 rasterization_ci.depthBiasSlopeFactor = 0.0f;
@@ -307,14 +319,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
307 depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; 319 depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
308 depth_stencil_ci.pNext = nullptr; 320 depth_stencil_ci.pNext = nullptr;
309 depth_stencil_ci.flags = 0; 321 depth_stencil_ci.flags = 0;
310 depth_stencil_ci.depthTestEnable = ds.depth_test_enable; 322 depth_stencil_ci.depthTestEnable = dynamic.depth_test_enable;
311 depth_stencil_ci.depthWriteEnable = ds.depth_write_enable; 323 depth_stencil_ci.depthWriteEnable = dynamic.depth_write_enable;
312 depth_stencil_ci.depthCompareOp = 324 depth_stencil_ci.depthCompareOp = dynamic.depth_test_enable
313 ds.depth_test_enable ? MaxwellToVK::ComparisonOp(ds.DepthTestFunc()) : VK_COMPARE_OP_ALWAYS; 325 ? MaxwellToVK::ComparisonOp(dynamic.DepthTestFunc())
314 depth_stencil_ci.depthBoundsTestEnable = ds.depth_bounds_enable; 326 : VK_COMPARE_OP_ALWAYS;
315 depth_stencil_ci.stencilTestEnable = ds.stencil_enable; 327 depth_stencil_ci.depthBoundsTestEnable = dynamic.depth_bounds_enable;
316 depth_stencil_ci.front = GetStencilFaceState(ds.front); 328 depth_stencil_ci.stencilTestEnable = dynamic.stencil_enable;
317 depth_stencil_ci.back = GetStencilFaceState(ds.back); 329 depth_stencil_ci.front = GetStencilFaceState(dynamic.front);
330 depth_stencil_ci.back = GetStencilFaceState(dynamic.back);
318 depth_stencil_ci.minDepthBounds = 0.0f; 331 depth_stencil_ci.minDepthBounds = 0.0f;
319 depth_stencil_ci.maxDepthBounds = 0.0f; 332 depth_stencil_ci.maxDepthBounds = 0.0f;
320 333
@@ -324,7 +337,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
324 static constexpr std::array COMPONENT_TABLE = { 337 static constexpr std::array COMPONENT_TABLE = {
325 VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, 338 VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT,
326 VK_COLOR_COMPONENT_A_BIT}; 339 VK_COLOR_COMPONENT_A_BIT};
327 const auto& blend = cd.attachments[index]; 340 const auto& blend = state.attachments[index];
328 341
329 VkColorComponentFlags color_components = 0; 342 VkColorComponentFlags color_components = 0;
330 for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) { 343 for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) {
@@ -354,11 +367,27 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
354 color_blend_ci.pAttachments = cb_attachments.data(); 367 color_blend_ci.pAttachments = cb_attachments.data();
355 std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants)); 368 std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants));
356 369
357 static constexpr std::array dynamic_states = { 370 std::vector dynamic_states = {
358 VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, 371 VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
359 VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, 372 VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
360 VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, 373 VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
361 VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE}; 374 VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
375 };
376 if (device.IsExtExtendedDynamicStateSupported()) {
377 static constexpr std::array extended = {
378 VK_DYNAMIC_STATE_CULL_MODE_EXT,
379 VK_DYNAMIC_STATE_FRONT_FACE_EXT,
380 VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT,
381 VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT,
382 VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT,
383 VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT,
384 VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT,
385 VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT,
386 VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
387 VK_DYNAMIC_STATE_STENCIL_OP_EXT,
388 };
389 dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
390 }
362 391
363 VkPipelineDynamicStateCreateInfo dynamic_state_ci; 392 VkPipelineDynamicStateCreateInfo dynamic_state_ci;
364 dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; 393 dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index a5c7b7945..3da835324 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -27,6 +27,7 @@
27#include "video_core/renderer_vulkan/wrapper.h" 27#include "video_core/renderer_vulkan/wrapper.h"
28#include "video_core/shader/compiler_settings.h" 28#include "video_core/shader/compiler_settings.h"
29#include "video_core/shader/memory_util.h" 29#include "video_core/shader/memory_util.h"
30#include "video_core/shader_cache.h"
30 31
31namespace Vulkan { 32namespace Vulkan {
32 33
@@ -45,6 +46,7 @@ constexpr VkDescriptorType UNIFORM_BUFFER = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
45constexpr VkDescriptorType STORAGE_BUFFER = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; 46constexpr VkDescriptorType STORAGE_BUFFER = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
46constexpr VkDescriptorType UNIFORM_TEXEL_BUFFER = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; 47constexpr VkDescriptorType UNIFORM_TEXEL_BUFFER = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
47constexpr VkDescriptorType COMBINED_IMAGE_SAMPLER = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; 48constexpr VkDescriptorType COMBINED_IMAGE_SAMPLER = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
49constexpr VkDescriptorType STORAGE_TEXEL_BUFFER = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
48constexpr VkDescriptorType STORAGE_IMAGE = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; 50constexpr VkDescriptorType STORAGE_IMAGE = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
49 51
50constexpr VideoCommon::Shader::CompilerSettings compiler_settings{ 52constexpr VideoCommon::Shader::CompilerSettings compiler_settings{
@@ -104,8 +106,9 @@ u32 FillDescriptorLayout(const ShaderEntries& entries,
104 u32 binding = base_binding; 106 u32 binding = base_binding;
105 AddBindings<UNIFORM_BUFFER>(bindings, binding, flags, entries.const_buffers); 107 AddBindings<UNIFORM_BUFFER>(bindings, binding, flags, entries.const_buffers);
106 AddBindings<STORAGE_BUFFER>(bindings, binding, flags, entries.global_buffers); 108 AddBindings<STORAGE_BUFFER>(bindings, binding, flags, entries.global_buffers);
107 AddBindings<UNIFORM_TEXEL_BUFFER>(bindings, binding, flags, entries.texel_buffers); 109 AddBindings<UNIFORM_TEXEL_BUFFER>(bindings, binding, flags, entries.uniform_texels);
108 AddBindings<COMBINED_IMAGE_SAMPLER>(bindings, binding, flags, entries.samplers); 110 AddBindings<COMBINED_IMAGE_SAMPLER>(bindings, binding, flags, entries.samplers);
111 AddBindings<STORAGE_TEXEL_BUFFER>(bindings, binding, flags, entries.storage_texels);
109 AddBindings<STORAGE_IMAGE>(bindings, binding, flags, entries.images); 112 AddBindings<STORAGE_IMAGE>(bindings, binding, flags, entries.images);
110 return binding; 113 return binding;
111} 114}
@@ -113,12 +116,12 @@ u32 FillDescriptorLayout(const ShaderEntries& entries,
113} // Anonymous namespace 116} // Anonymous namespace
114 117
115std::size_t GraphicsPipelineCacheKey::Hash() const noexcept { 118std::size_t GraphicsPipelineCacheKey::Hash() const noexcept {
116 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); 119 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
117 return static_cast<std::size_t>(hash); 120 return static_cast<std::size_t>(hash);
118} 121}
119 122
120bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept { 123bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept {
121 return std::memcmp(&rhs, this, sizeof *this) == 0; 124 return std::memcmp(&rhs, this, Size()) == 0;
122} 125}
123 126
124std::size_t ComputePipelineCacheKey::Hash() const noexcept { 127std::size_t ComputePipelineCacheKey::Hash() const noexcept {
@@ -130,19 +133,18 @@ bool ComputePipelineCacheKey::operator==(const ComputePipelineCacheKey& rhs) con
130 return std::memcmp(&rhs, this, sizeof *this) == 0; 133 return std::memcmp(&rhs, this, sizeof *this) == 0;
131} 134}
132 135
133CachedShader::CachedShader(Core::System& system, Tegra::Engines::ShaderType stage, 136Shader::Shader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr,
134 GPUVAddr gpu_addr, VAddr cpu_addr, ProgramCode program_code, 137 VideoCommon::Shader::ProgramCode program_code, u32 main_offset)
135 u32 main_offset) 138 : gpu_addr{gpu_addr}, program_code{std::move(program_code)},
136 : RasterizerCacheObject{cpu_addr}, gpu_addr{gpu_addr}, program_code{std::move(program_code)},
137 registry{stage, GetEngine(system, stage)}, shader_ir{this->program_code, main_offset, 139 registry{stage, GetEngine(system, stage)}, shader_ir{this->program_code, main_offset,
138 compiler_settings, registry}, 140 compiler_settings, registry},
139 entries{GenerateShaderEntries(shader_ir)} {} 141 entries{GenerateShaderEntries(shader_ir)} {}
140 142
141CachedShader::~CachedShader() = default; 143Shader::~Shader() = default;
142 144
143Tegra::Engines::ConstBufferEngineInterface& CachedShader::GetEngine( 145Tegra::Engines::ConstBufferEngineInterface& Shader::GetEngine(Core::System& system,
144 Core::System& system, Tegra::Engines::ShaderType stage) { 146 Tegra::Engines::ShaderType stage) {
145 if (stage == Tegra::Engines::ShaderType::Compute) { 147 if (stage == ShaderType::Compute) {
146 return system.GPU().KeplerCompute(); 148 return system.GPU().KeplerCompute();
147 } else { 149 } else {
148 return system.GPU().Maxwell3D(); 150 return system.GPU().Maxwell3D();
@@ -154,16 +156,16 @@ VKPipelineCache::VKPipelineCache(Core::System& system, RasterizerVulkan& rasteri
154 VKDescriptorPool& descriptor_pool, 156 VKDescriptorPool& descriptor_pool,
155 VKUpdateDescriptorQueue& update_descriptor_queue, 157 VKUpdateDescriptorQueue& update_descriptor_queue,
156 VKRenderPassCache& renderpass_cache) 158 VKRenderPassCache& renderpass_cache)
157 : RasterizerCache{rasterizer}, system{system}, device{device}, scheduler{scheduler}, 159 : VideoCommon::ShaderCache<Shader>{rasterizer}, system{system}, device{device},
158 descriptor_pool{descriptor_pool}, update_descriptor_queue{update_descriptor_queue}, 160 scheduler{scheduler}, descriptor_pool{descriptor_pool},
159 renderpass_cache{renderpass_cache} {} 161 update_descriptor_queue{update_descriptor_queue}, renderpass_cache{renderpass_cache} {}
160 162
161VKPipelineCache::~VKPipelineCache() = default; 163VKPipelineCache::~VKPipelineCache() = default;
162 164
163std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() { 165std::array<Shader*, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
164 const auto& gpu = system.GPU().Maxwell3D(); 166 const auto& gpu = system.GPU().Maxwell3D();
165 167
166 std::array<Shader, Maxwell::MaxShaderProgram> shaders; 168 std::array<Shader*, Maxwell::MaxShaderProgram> shaders{};
167 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 169 for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
168 const auto program{static_cast<Maxwell::ShaderProgram>(index)}; 170 const auto program{static_cast<Maxwell::ShaderProgram>(index)};
169 171
@@ -176,24 +178,28 @@ std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
176 const GPUVAddr program_addr{GetShaderAddress(system, program)}; 178 const GPUVAddr program_addr{GetShaderAddress(system, program)};
177 const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr); 179 const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr);
178 ASSERT(cpu_addr); 180 ASSERT(cpu_addr);
179 auto shader = cpu_addr ? TryGet(*cpu_addr) : null_shader; 181
180 if (!shader) { 182 Shader* result = cpu_addr ? TryGet(*cpu_addr) : null_shader.get();
183 if (!result) {
181 const auto host_ptr{memory_manager.GetPointer(program_addr)}; 184 const auto host_ptr{memory_manager.GetPointer(program_addr)};
182 185
183 // No shader found - create a new one 186 // No shader found - create a new one
184 constexpr u32 stage_offset = STAGE_MAIN_OFFSET; 187 constexpr u32 stage_offset = STAGE_MAIN_OFFSET;
185 const auto stage = static_cast<Tegra::Engines::ShaderType>(index == 0 ? 0 : index - 1); 188 const auto stage = static_cast<ShaderType>(index == 0 ? 0 : index - 1);
186 ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, false); 189 ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, false);
190 const std::size_t size_in_bytes = code.size() * sizeof(u64);
191
192 auto shader = std::make_unique<Shader>(system, stage, program_addr, std::move(code),
193 stage_offset);
194 result = shader.get();
187 195
188 shader = std::make_shared<CachedShader>(system, stage, program_addr, *cpu_addr,
189 std::move(code), stage_offset);
190 if (cpu_addr) { 196 if (cpu_addr) {
191 Register(shader); 197 Register(std::move(shader), *cpu_addr, size_in_bytes);
192 } else { 198 } else {
193 null_shader = shader; 199 null_shader = std::move(shader);
194 } 200 }
195 } 201 }
196 shaders[index] = std::move(shader); 202 shaders[index] = result;
197 } 203 }
198 return last_shaders = shaders; 204 return last_shaders = shaders;
199} 205}
@@ -234,19 +240,22 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
234 const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr); 240 const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr);
235 ASSERT(cpu_addr); 241 ASSERT(cpu_addr);
236 242
237 auto shader = cpu_addr ? TryGet(*cpu_addr) : null_kernel; 243 Shader* shader = cpu_addr ? TryGet(*cpu_addr) : null_kernel.get();
238 if (!shader) { 244 if (!shader) {
239 // No shader found - create a new one 245 // No shader found - create a new one
240 const auto host_ptr = memory_manager.GetPointer(program_addr); 246 const auto host_ptr = memory_manager.GetPointer(program_addr);
241 247
242 ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, true); 248 ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, true);
243 shader = std::make_shared<CachedShader>(system, Tegra::Engines::ShaderType::Compute, 249 const std::size_t size_in_bytes = code.size() * sizeof(u64);
244 program_addr, *cpu_addr, std::move(code), 250
245 KERNEL_MAIN_OFFSET); 251 auto shader_info = std::make_unique<Shader>(system, ShaderType::Compute, program_addr,
252 std::move(code), KERNEL_MAIN_OFFSET);
253 shader = shader_info.get();
254
246 if (cpu_addr) { 255 if (cpu_addr) {
247 Register(shader); 256 Register(std::move(shader_info), *cpu_addr, size_in_bytes);
248 } else { 257 } else {
249 null_kernel = shader; 258 null_kernel = std::move(shader_info);
250 } 259 }
251 } 260 }
252 261
@@ -262,7 +271,7 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
262 return *entry; 271 return *entry;
263} 272}
264 273
265void VKPipelineCache::Unregister(const Shader& shader) { 274void VKPipelineCache::OnShaderRemoval(Shader* shader) {
266 bool finished = false; 275 bool finished = false;
267 const auto Finish = [&] { 276 const auto Finish = [&] {
268 // TODO(Rodrigo): Instead of finishing here, wait for the fences that use this pipeline and 277 // TODO(Rodrigo): Instead of finishing here, wait for the fences that use this pipeline and
@@ -294,8 +303,6 @@ void VKPipelineCache::Unregister(const Shader& shader) {
294 Finish(); 303 Finish();
295 it = compute_cache.erase(it); 304 it = compute_cache.erase(it);
296 } 305 }
297
298 RasterizerCache::Unregister(shader);
299} 306}
300 307
301std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> 308std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>>
@@ -305,16 +312,19 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
305 const auto& gpu = system.GPU().Maxwell3D(); 312 const auto& gpu = system.GPU().Maxwell3D();
306 313
307 Specialization specialization; 314 Specialization specialization;
308 if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) { 315 if (fixed_state.dynamic_state.Topology() == Maxwell::PrimitiveTopology::Points ||
316 device.IsExtExtendedDynamicStateSupported()) {
309 float point_size; 317 float point_size;
310 std::memcpy(&point_size, &fixed_state.rasterizer.point_size, sizeof(float)); 318 std::memcpy(&point_size, &fixed_state.point_size, sizeof(float));
311 specialization.point_size = point_size; 319 specialization.point_size = point_size;
312 ASSERT(point_size != 0.0f); 320 ASSERT(point_size != 0.0f);
313 } 321 }
314 for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { 322 for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
315 specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].Type(); 323 const auto& attribute = fixed_state.attributes[i];
324 specialization.enabled_attributes[i] = attribute.enabled.Value() != 0;
325 specialization.attribute_types[i] = attribute.Type();
316 } 326 }
317 specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one; 327 specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one;
318 328
319 SPIRVProgram program; 329 SPIRVProgram program;
320 std::vector<VkDescriptorSetLayoutBinding> bindings; 330 std::vector<VkDescriptorSetLayoutBinding> bindings;
@@ -328,12 +338,11 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
328 } 338 }
329 339
330 const GPUVAddr gpu_addr = GetShaderAddress(system, program_enum); 340 const GPUVAddr gpu_addr = GetShaderAddress(system, program_enum);
331 const auto cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr); 341 const std::optional<VAddr> cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr);
332 const auto shader = cpu_addr ? TryGet(*cpu_addr) : null_shader; 342 Shader* const shader = cpu_addr ? TryGet(*cpu_addr) : null_shader.get();
333 ASSERT(shader);
334 343
335 const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5 344 const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5
336 const auto program_type = GetShaderType(program_enum); 345 const ShaderType program_type = GetShaderType(program_enum);
337 const auto& entries = shader->GetEntries(); 346 const auto& entries = shader->GetEntries();
338 program[stage] = { 347 program[stage] = {
339 Decompile(device, shader->GetIR(), program_type, shader->GetRegistry(), specialization), 348 Decompile(device, shader->GetIR(), program_type, shader->GetRegistry(), specialization),
@@ -375,16 +384,17 @@ void AddEntry(std::vector<VkDescriptorUpdateTemplateEntry>& template_entries, u3
375 return; 384 return;
376 } 385 }
377 386
378 if constexpr (descriptor_type == UNIFORM_TEXEL_BUFFER) { 387 if constexpr (descriptor_type == UNIFORM_TEXEL_BUFFER ||
379 // Nvidia has a bug where updating multiple uniform texels at once causes the driver to 388 descriptor_type == STORAGE_TEXEL_BUFFER) {
380 // crash. 389 // Nvidia has a bug where updating multiple texels at once causes the driver to crash.
390 // Note: Fixed in driver Windows 443.24, Linux 440.66.15
381 for (u32 i = 0; i < count; ++i) { 391 for (u32 i = 0; i < count; ++i) {
382 VkDescriptorUpdateTemplateEntry& entry = template_entries.emplace_back(); 392 VkDescriptorUpdateTemplateEntry& entry = template_entries.emplace_back();
383 entry.dstBinding = binding + i; 393 entry.dstBinding = binding + i;
384 entry.dstArrayElement = 0; 394 entry.dstArrayElement = 0;
385 entry.descriptorCount = 1; 395 entry.descriptorCount = 1;
386 entry.descriptorType = descriptor_type; 396 entry.descriptorType = descriptor_type;
387 entry.offset = offset + i * entry_size; 397 entry.offset = static_cast<std::size_t>(offset + i * entry_size);
388 entry.stride = entry_size; 398 entry.stride = entry_size;
389 } 399 }
390 } else if (count > 0) { 400 } else if (count > 0) {
@@ -405,8 +415,9 @@ void FillDescriptorUpdateTemplateEntries(
405 std::vector<VkDescriptorUpdateTemplateEntryKHR>& template_entries) { 415 std::vector<VkDescriptorUpdateTemplateEntryKHR>& template_entries) {
406 AddEntry<UNIFORM_BUFFER>(template_entries, offset, binding, entries.const_buffers); 416 AddEntry<UNIFORM_BUFFER>(template_entries, offset, binding, entries.const_buffers);
407 AddEntry<STORAGE_BUFFER>(template_entries, offset, binding, entries.global_buffers); 417 AddEntry<STORAGE_BUFFER>(template_entries, offset, binding, entries.global_buffers);
408 AddEntry<UNIFORM_TEXEL_BUFFER>(template_entries, offset, binding, entries.texel_buffers); 418 AddEntry<UNIFORM_TEXEL_BUFFER>(template_entries, offset, binding, entries.uniform_texels);
409 AddEntry<COMBINED_IMAGE_SAMPLER>(template_entries, offset, binding, entries.samplers); 419 AddEntry<COMBINED_IMAGE_SAMPLER>(template_entries, offset, binding, entries.samplers);
420 AddEntry<STORAGE_TEXEL_BUFFER>(template_entries, offset, binding, entries.storage_texels);
410 AddEntry<STORAGE_IMAGE>(template_entries, offset, binding, entries.images); 421 AddEntry<STORAGE_IMAGE>(template_entries, offset, binding, entries.images);
411} 422}
412 423
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 0b5796fef..0a3fe65fb 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -17,7 +17,6 @@
17#include "common/common_types.h" 17#include "common/common_types.h"
18#include "video_core/engines/const_buffer_engine_interface.h" 18#include "video_core/engines/const_buffer_engine_interface.h"
19#include "video_core/engines/maxwell_3d.h" 19#include "video_core/engines/maxwell_3d.h"
20#include "video_core/rasterizer_cache.h"
21#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 20#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
22#include "video_core/renderer_vulkan/vk_graphics_pipeline.h" 21#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
23#include "video_core/renderer_vulkan/vk_renderpass_cache.h" 22#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
@@ -26,6 +25,7 @@
26#include "video_core/shader/memory_util.h" 25#include "video_core/shader/memory_util.h"
27#include "video_core/shader/registry.h" 26#include "video_core/shader/registry.h"
28#include "video_core/shader/shader_ir.h" 27#include "video_core/shader/shader_ir.h"
28#include "video_core/shader_cache.h"
29 29
30namespace Core { 30namespace Core {
31class System; 31class System;
@@ -41,15 +41,13 @@ class VKFence;
41class VKScheduler; 41class VKScheduler;
42class VKUpdateDescriptorQueue; 42class VKUpdateDescriptorQueue;
43 43
44class CachedShader;
45using Shader = std::shared_ptr<CachedShader>;
46using Maxwell = Tegra::Engines::Maxwell3D::Regs; 44using Maxwell = Tegra::Engines::Maxwell3D::Regs;
47 45
48struct GraphicsPipelineCacheKey { 46struct GraphicsPipelineCacheKey {
49 FixedPipelineState fixed_state;
50 RenderPassParams renderpass_params; 47 RenderPassParams renderpass_params;
48 u32 padding;
51 std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders; 49 std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
52 u64 padding; // This is necessary for unique object representations 50 FixedPipelineState fixed_state;
53 51
54 std::size_t Hash() const noexcept; 52 std::size_t Hash() const noexcept;
55 53
@@ -58,6 +56,10 @@ struct GraphicsPipelineCacheKey {
58 bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept { 56 bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
59 return !operator==(rhs); 57 return !operator==(rhs);
60 } 58 }
59
60 std::size_t Size() const noexcept {
61 return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
62 }
61}; 63};
62static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>); 64static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
63static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>); 65static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
@@ -102,21 +104,16 @@ struct hash<Vulkan::ComputePipelineCacheKey> {
102 104
103namespace Vulkan { 105namespace Vulkan {
104 106
105class CachedShader final : public RasterizerCacheObject { 107class Shader {
106public: 108public:
107 explicit CachedShader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr, 109 explicit Shader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr,
108 VAddr cpu_addr, VideoCommon::Shader::ProgramCode program_code, 110 VideoCommon::Shader::ProgramCode program_code, u32 main_offset);
109 u32 main_offset); 111 ~Shader();
110 ~CachedShader();
111 112
112 GPUVAddr GetGpuAddr() const { 113 GPUVAddr GetGpuAddr() const {
113 return gpu_addr; 114 return gpu_addr;
114 } 115 }
115 116
116 std::size_t GetSizeInBytes() const override {
117 return program_code.size() * sizeof(u64);
118 }
119
120 VideoCommon::Shader::ShaderIR& GetIR() { 117 VideoCommon::Shader::ShaderIR& GetIR() {
121 return shader_ir; 118 return shader_ir;
122 } 119 }
@@ -144,25 +141,23 @@ private:
144 ShaderEntries entries; 141 ShaderEntries entries;
145}; 142};
146 143
147class VKPipelineCache final : public RasterizerCache<Shader> { 144class VKPipelineCache final : public VideoCommon::ShaderCache<Shader> {
148public: 145public:
149 explicit VKPipelineCache(Core::System& system, RasterizerVulkan& rasterizer, 146 explicit VKPipelineCache(Core::System& system, RasterizerVulkan& rasterizer,
150 const VKDevice& device, VKScheduler& scheduler, 147 const VKDevice& device, VKScheduler& scheduler,
151 VKDescriptorPool& descriptor_pool, 148 VKDescriptorPool& descriptor_pool,
152 VKUpdateDescriptorQueue& update_descriptor_queue, 149 VKUpdateDescriptorQueue& update_descriptor_queue,
153 VKRenderPassCache& renderpass_cache); 150 VKRenderPassCache& renderpass_cache);
154 ~VKPipelineCache(); 151 ~VKPipelineCache() override;
155 152
156 std::array<Shader, Maxwell::MaxShaderProgram> GetShaders(); 153 std::array<Shader*, Maxwell::MaxShaderProgram> GetShaders();
157 154
158 VKGraphicsPipeline& GetGraphicsPipeline(const GraphicsPipelineCacheKey& key); 155 VKGraphicsPipeline& GetGraphicsPipeline(const GraphicsPipelineCacheKey& key);
159 156
160 VKComputePipeline& GetComputePipeline(const ComputePipelineCacheKey& key); 157 VKComputePipeline& GetComputePipeline(const ComputePipelineCacheKey& key);
161 158
162protected: 159protected:
163 void Unregister(const Shader& shader) override; 160 void OnShaderRemoval(Shader* shader) final;
164
165 void FlushObjectInner(const Shader& object) override {}
166 161
167private: 162private:
168 std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders( 163 std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders(
@@ -175,10 +170,10 @@ private:
175 VKUpdateDescriptorQueue& update_descriptor_queue; 170 VKUpdateDescriptorQueue& update_descriptor_queue;
176 VKRenderPassCache& renderpass_cache; 171 VKRenderPassCache& renderpass_cache;
177 172
178 Shader null_shader{}; 173 std::unique_ptr<Shader> null_shader;
179 Shader null_kernel{}; 174 std::unique_ptr<Shader> null_kernel;
180 175
181 std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; 176 std::array<Shader*, Maxwell::MaxShaderProgram> last_shaders{};
182 177
183 GraphicsPipelineCacheKey last_graphics_key; 178 GraphicsPipelineCacheKey last_graphics_key;
184 VKGraphicsPipeline* last_graphics_pipeline = nullptr; 179 VKGraphicsPipeline* last_graphics_pipeline = nullptr;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index be5b77fae..380ed532b 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -38,6 +38,7 @@
38#include "video_core/renderer_vulkan/vk_texture_cache.h" 38#include "video_core/renderer_vulkan/vk_texture_cache.h"
39#include "video_core/renderer_vulkan/vk_update_descriptor.h" 39#include "video_core/renderer_vulkan/vk_update_descriptor.h"
40#include "video_core/renderer_vulkan/wrapper.h" 40#include "video_core/renderer_vulkan/wrapper.h"
41#include "video_core/shader_cache.h"
41 42
42namespace Vulkan { 43namespace Vulkan {
43 44
@@ -98,7 +99,7 @@ VkRect2D GetScissorState(const Maxwell& regs, std::size_t index) {
98} 99}
99 100
100std::array<GPUVAddr, Maxwell::MaxShaderProgram> GetShaderAddresses( 101std::array<GPUVAddr, Maxwell::MaxShaderProgram> GetShaderAddresses(
101 const std::array<Shader, Maxwell::MaxShaderProgram>& shaders) { 102 const std::array<Shader*, Maxwell::MaxShaderProgram>& shaders) {
102 std::array<GPUVAddr, Maxwell::MaxShaderProgram> addresses; 103 std::array<GPUVAddr, Maxwell::MaxShaderProgram> addresses;
103 for (std::size_t i = 0; i < std::size(addresses); ++i) { 104 for (std::size_t i = 0; i < std::size(addresses); ++i) {
104 addresses[i] = shaders[i] ? shaders[i]->GetGpuAddr() : 0; 105 addresses[i] = shaders[i] ? shaders[i]->GetGpuAddr() : 0;
@@ -117,6 +118,17 @@ template <typename Engine, typename Entry>
117Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, 118Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry,
118 std::size_t stage, std::size_t index = 0) { 119 std::size_t stage, std::size_t index = 0) {
119 const auto stage_type = static_cast<Tegra::Engines::ShaderType>(stage); 120 const auto stage_type = static_cast<Tegra::Engines::ShaderType>(stage);
121 if constexpr (std::is_same_v<Entry, SamplerEntry>) {
122 if (entry.is_separated) {
123 const u32 buffer_1 = entry.buffer;
124 const u32 buffer_2 = entry.secondary_buffer;
125 const u32 offset_1 = entry.offset;
126 const u32 offset_2 = entry.secondary_offset;
127 const u32 handle_1 = engine.AccessConstBuffer32(stage_type, buffer_1, offset_1);
128 const u32 handle_2 = engine.AccessConstBuffer32(stage_type, buffer_2, offset_2);
129 return engine.GetTextureInfo(handle_1 | handle_2);
130 }
131 }
120 if (entry.is_bindless) { 132 if (entry.is_bindless) {
121 const auto tex_handle = engine.AccessConstBuffer32(stage_type, entry.buffer, entry.offset); 133 const auto tex_handle = engine.AccessConstBuffer32(stage_type, entry.buffer, entry.offset);
122 return engine.GetTextureInfo(tex_handle); 134 return engine.GetTextureInfo(tex_handle);
@@ -131,13 +143,65 @@ Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry
131 } 143 }
132} 144}
133 145
146/// @brief Determine if an attachment to be updated has to preserve contents
147/// @param is_clear True when a clear is being executed
148/// @param regs 3D registers
149/// @return True when the contents have to be preserved
150bool HasToPreserveColorContents(bool is_clear, const Maxwell& regs) {
151 if (!is_clear) {
152 return true;
153 }
154 // First we have to make sure all clear masks are enabled.
155 if (!regs.clear_buffers.R || !regs.clear_buffers.G || !regs.clear_buffers.B ||
156 !regs.clear_buffers.A) {
157 return true;
158 }
159 // If scissors are disabled, the whole screen is cleared
160 if (!regs.clear_flags.scissor) {
161 return false;
162 }
163 // Then we have to confirm scissor testing clears the whole image
164 const std::size_t index = regs.clear_buffers.RT;
165 const auto& scissor = regs.scissor_test[0];
166 return scissor.min_x > 0 || scissor.min_y > 0 || scissor.max_x < regs.rt[index].width ||
167 scissor.max_y < regs.rt[index].height;
168}
169
170/// @brief Determine if an attachment to be updated has to preserve contents
171/// @param is_clear True when a clear is being executed
172/// @param regs 3D registers
173/// @return True when the contents have to be preserved
174bool HasToPreserveDepthContents(bool is_clear, const Maxwell& regs) {
175 // If we are not clearing, the contents have to be preserved
176 if (!is_clear) {
177 return true;
178 }
179 // For depth stencil clears we only have to confirm scissor test covers the whole image
180 if (!regs.clear_flags.scissor) {
181 return false;
182 }
183 // Make sure the clear cover the whole image
184 const auto& scissor = regs.scissor_test[0];
185 return scissor.min_x > 0 || scissor.min_y > 0 || scissor.max_x < regs.zeta_width ||
186 scissor.max_y < regs.zeta_height;
187}
188
189template <std::size_t N>
190std::array<VkDeviceSize, N> ExpandStrides(const std::array<u16, N>& strides) {
191 std::array<VkDeviceSize, N> expanded;
192 std::copy(strides.begin(), strides.end(), expanded.begin());
193 return expanded;
194}
195
134} // Anonymous namespace 196} // Anonymous namespace
135 197
136class BufferBindings final { 198class BufferBindings final {
137public: 199public:
138 void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset) { 200 void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, u32 stride) {
139 vertex.buffers[vertex.num_buffers] = buffer; 201 vertex.buffers[vertex.num_buffers] = buffer;
140 vertex.offsets[vertex.num_buffers] = offset; 202 vertex.offsets[vertex.num_buffers] = offset;
203 vertex.sizes[vertex.num_buffers] = size;
204 vertex.strides[vertex.num_buffers] = static_cast<u16>(stride);
141 ++vertex.num_buffers; 205 ++vertex.num_buffers;
142 } 206 }
143 207
@@ -147,76 +211,76 @@ public:
147 index.type = type; 211 index.type = type;
148 } 212 }
149 213
150 void Bind(VKScheduler& scheduler) const { 214 void Bind(const VKDevice& device, VKScheduler& scheduler) const {
151 // Use this large switch case to avoid dispatching more memory in the record lambda than 215 // Use this large switch case to avoid dispatching more memory in the record lambda than
152 // what we need. It looks horrible, but it's the best we can do on standard C++. 216 // what we need. It looks horrible, but it's the best we can do on standard C++.
153 switch (vertex.num_buffers) { 217 switch (vertex.num_buffers) {
154 case 0: 218 case 0:
155 return BindStatic<0>(scheduler); 219 return BindStatic<0>(device, scheduler);
156 case 1: 220 case 1:
157 return BindStatic<1>(scheduler); 221 return BindStatic<1>(device, scheduler);
158 case 2: 222 case 2:
159 return BindStatic<2>(scheduler); 223 return BindStatic<2>(device, scheduler);
160 case 3: 224 case 3:
161 return BindStatic<3>(scheduler); 225 return BindStatic<3>(device, scheduler);
162 case 4: 226 case 4:
163 return BindStatic<4>(scheduler); 227 return BindStatic<4>(device, scheduler);
164 case 5: 228 case 5:
165 return BindStatic<5>(scheduler); 229 return BindStatic<5>(device, scheduler);
166 case 6: 230 case 6:
167 return BindStatic<6>(scheduler); 231 return BindStatic<6>(device, scheduler);
168 case 7: 232 case 7:
169 return BindStatic<7>(scheduler); 233 return BindStatic<7>(device, scheduler);
170 case 8: 234 case 8:
171 return BindStatic<8>(scheduler); 235 return BindStatic<8>(device, scheduler);
172 case 9: 236 case 9:
173 return BindStatic<9>(scheduler); 237 return BindStatic<9>(device, scheduler);
174 case 10: 238 case 10:
175 return BindStatic<10>(scheduler); 239 return BindStatic<10>(device, scheduler);
176 case 11: 240 case 11:
177 return BindStatic<11>(scheduler); 241 return BindStatic<11>(device, scheduler);
178 case 12: 242 case 12:
179 return BindStatic<12>(scheduler); 243 return BindStatic<12>(device, scheduler);
180 case 13: 244 case 13:
181 return BindStatic<13>(scheduler); 245 return BindStatic<13>(device, scheduler);
182 case 14: 246 case 14:
183 return BindStatic<14>(scheduler); 247 return BindStatic<14>(device, scheduler);
184 case 15: 248 case 15:
185 return BindStatic<15>(scheduler); 249 return BindStatic<15>(device, scheduler);
186 case 16: 250 case 16:
187 return BindStatic<16>(scheduler); 251 return BindStatic<16>(device, scheduler);
188 case 17: 252 case 17:
189 return BindStatic<17>(scheduler); 253 return BindStatic<17>(device, scheduler);
190 case 18: 254 case 18:
191 return BindStatic<18>(scheduler); 255 return BindStatic<18>(device, scheduler);
192 case 19: 256 case 19:
193 return BindStatic<19>(scheduler); 257 return BindStatic<19>(device, scheduler);
194 case 20: 258 case 20:
195 return BindStatic<20>(scheduler); 259 return BindStatic<20>(device, scheduler);
196 case 21: 260 case 21:
197 return BindStatic<21>(scheduler); 261 return BindStatic<21>(device, scheduler);
198 case 22: 262 case 22:
199 return BindStatic<22>(scheduler); 263 return BindStatic<22>(device, scheduler);
200 case 23: 264 case 23:
201 return BindStatic<23>(scheduler); 265 return BindStatic<23>(device, scheduler);
202 case 24: 266 case 24:
203 return BindStatic<24>(scheduler); 267 return BindStatic<24>(device, scheduler);
204 case 25: 268 case 25:
205 return BindStatic<25>(scheduler); 269 return BindStatic<25>(device, scheduler);
206 case 26: 270 case 26:
207 return BindStatic<26>(scheduler); 271 return BindStatic<26>(device, scheduler);
208 case 27: 272 case 27:
209 return BindStatic<27>(scheduler); 273 return BindStatic<27>(device, scheduler);
210 case 28: 274 case 28:
211 return BindStatic<28>(scheduler); 275 return BindStatic<28>(device, scheduler);
212 case 29: 276 case 29:
213 return BindStatic<29>(scheduler); 277 return BindStatic<29>(device, scheduler);
214 case 30: 278 case 30:
215 return BindStatic<30>(scheduler); 279 return BindStatic<30>(device, scheduler);
216 case 31: 280 case 31:
217 return BindStatic<31>(scheduler); 281 return BindStatic<31>(device, scheduler);
218 case 32: 282 case 32:
219 return BindStatic<32>(scheduler); 283 return BindStatic<32>(device, scheduler);
220 } 284 }
221 UNREACHABLE(); 285 UNREACHABLE();
222 } 286 }
@@ -227,6 +291,8 @@ private:
227 std::size_t num_buffers = 0; 291 std::size_t num_buffers = 0;
228 std::array<VkBuffer, Maxwell::NumVertexArrays> buffers; 292 std::array<VkBuffer, Maxwell::NumVertexArrays> buffers;
229 std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets; 293 std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets;
294 std::array<VkDeviceSize, Maxwell::NumVertexArrays> sizes;
295 std::array<u16, Maxwell::NumVertexArrays> strides;
230 } vertex; 296 } vertex;
231 297
232 struct { 298 struct {
@@ -236,15 +302,23 @@ private:
236 } index; 302 } index;
237 303
238 template <std::size_t N> 304 template <std::size_t N>
239 void BindStatic(VKScheduler& scheduler) const { 305 void BindStatic(const VKDevice& device, VKScheduler& scheduler) const {
240 if (index.buffer) { 306 if (device.IsExtExtendedDynamicStateSupported()) {
241 BindStatic<N, true>(scheduler); 307 if (index.buffer) {
308 BindStatic<N, true, true>(scheduler);
309 } else {
310 BindStatic<N, false, true>(scheduler);
311 }
242 } else { 312 } else {
243 BindStatic<N, false>(scheduler); 313 if (index.buffer) {
314 BindStatic<N, true, false>(scheduler);
315 } else {
316 BindStatic<N, false, false>(scheduler);
317 }
244 } 318 }
245 } 319 }
246 320
247 template <std::size_t N, bool is_indexed> 321 template <std::size_t N, bool is_indexed, bool has_extended_dynamic_state>
248 void BindStatic(VKScheduler& scheduler) const { 322 void BindStatic(VKScheduler& scheduler) const {
249 static_assert(N <= Maxwell::NumVertexArrays); 323 static_assert(N <= Maxwell::NumVertexArrays);
250 if constexpr (N == 0) { 324 if constexpr (N == 0) {
@@ -256,6 +330,31 @@ private:
256 std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin()); 330 std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin());
257 std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin()); 331 std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin());
258 332
333 if constexpr (has_extended_dynamic_state) {
334 // With extended dynamic states we can specify the length and stride of a vertex buffer
335 // std::array<VkDeviceSize, N> sizes;
336 std::array<u16, N> strides;
337 // std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin());
338 std::copy(vertex.strides.begin(), vertex.strides.begin() + N, strides.begin());
339
340 if constexpr (is_indexed) {
341 scheduler.Record(
342 [buffers, offsets, strides, index = index](vk::CommandBuffer cmdbuf) {
343 cmdbuf.BindIndexBuffer(index.buffer, index.offset, index.type);
344 cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
345 offsets.data(), nullptr,
346 ExpandStrides(strides).data());
347 });
348 } else {
349 scheduler.Record([buffers, offsets, strides](vk::CommandBuffer cmdbuf) {
350 cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
351 offsets.data(), nullptr,
352 ExpandStrides(strides).data());
353 });
354 }
355 return;
356 }
357
259 if constexpr (is_indexed) { 358 if constexpr (is_indexed) {
260 // Indexed draw 359 // Indexed draw
261 scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) { 360 scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) {
@@ -314,7 +413,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
314 413
315 const auto& gpu = system.GPU().Maxwell3D(); 414 const auto& gpu = system.GPU().Maxwell3D();
316 GraphicsPipelineCacheKey key; 415 GraphicsPipelineCacheKey key;
317 key.fixed_state.Fill(gpu.regs); 416 key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported());
318 417
319 buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed)); 418 buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed));
320 419
@@ -332,7 +431,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
332 431
333 buffer_cache.Unmap(); 432 buffer_cache.Unmap();
334 433
335 const Texceptions texceptions = UpdateAttachments(); 434 const Texceptions texceptions = UpdateAttachments(false);
336 SetupImageTransitions(texceptions, color_attachments, zeta_attachment); 435 SetupImageTransitions(texceptions, color_attachments, zeta_attachment);
337 436
338 key.renderpass_params = GetRenderPassParams(texceptions); 437 key.renderpass_params = GetRenderPassParams(texceptions);
@@ -347,7 +446,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
347 446
348 UpdateDynamicStates(); 447 UpdateDynamicStates();
349 448
350 buffer_bindings.Bind(scheduler); 449 buffer_bindings.Bind(device, scheduler);
351 450
352 BeginTransformFeedback(); 451 BeginTransformFeedback();
353 452
@@ -388,7 +487,7 @@ void RasterizerVulkan::Clear() {
388 return; 487 return;
389 } 488 }
390 489
391 [[maybe_unused]] const auto texceptions = UpdateAttachments(); 490 [[maybe_unused]] const auto texceptions = UpdateAttachments(true);
392 DEBUG_ASSERT(texceptions.none()); 491 DEBUG_ASSERT(texceptions.none());
393 SetupImageTransitions(0, color_attachments, zeta_attachment); 492 SetupImageTransitions(0, color_attachments, zeta_attachment);
394 493
@@ -468,8 +567,9 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) {
468 const auto& entries = pipeline.GetEntries(); 567 const auto& entries = pipeline.GetEntries();
469 SetupComputeConstBuffers(entries); 568 SetupComputeConstBuffers(entries);
470 SetupComputeGlobalBuffers(entries); 569 SetupComputeGlobalBuffers(entries);
471 SetupComputeTexelBuffers(entries); 570 SetupComputeUniformTexels(entries);
472 SetupComputeTextures(entries); 571 SetupComputeTextures(entries);
572 SetupComputeStorageTexels(entries);
473 SetupComputeImages(entries); 573 SetupComputeImages(entries);
474 574
475 buffer_cache.Unmap(); 575 buffer_cache.Unmap();
@@ -664,9 +764,12 @@ void RasterizerVulkan::FlushWork() {
664 draw_counter = 0; 764 draw_counter = 0;
665} 765}
666 766
667RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { 767RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments(bool is_clear) {
668 MICROPROFILE_SCOPE(Vulkan_RenderTargets); 768 MICROPROFILE_SCOPE(Vulkan_RenderTargets);
669 auto& dirty = system.GPU().Maxwell3D().dirty.flags; 769 auto& maxwell3d = system.GPU().Maxwell3D();
770 auto& dirty = maxwell3d.dirty.flags;
771 auto& regs = maxwell3d.regs;
772
670 const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets]; 773 const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets];
671 dirty[VideoCommon::Dirty::RenderTargets] = false; 774 dirty[VideoCommon::Dirty::RenderTargets] = false;
672 775
@@ -675,7 +778,8 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() {
675 Texceptions texceptions; 778 Texceptions texceptions;
676 for (std::size_t rt = 0; rt < Maxwell::NumRenderTargets; ++rt) { 779 for (std::size_t rt = 0; rt < Maxwell::NumRenderTargets; ++rt) {
677 if (update_rendertargets) { 780 if (update_rendertargets) {
678 color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true); 781 const bool preserve_contents = HasToPreserveColorContents(is_clear, regs);
782 color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, preserve_contents);
679 } 783 }
680 if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) { 784 if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) {
681 texceptions[rt] = true; 785 texceptions[rt] = true;
@@ -683,7 +787,8 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() {
683 } 787 }
684 788
685 if (update_rendertargets) { 789 if (update_rendertargets) {
686 zeta_attachment = texture_cache.GetDepthBufferSurface(true); 790 const bool preserve_contents = HasToPreserveDepthContents(is_clear, regs);
791 zeta_attachment = texture_cache.GetDepthBufferSurface(preserve_contents);
687 } 792 }
688 if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) { 793 if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) {
689 texceptions[ZETA_TEXCEPTION_INDEX] = true; 794 texceptions[ZETA_TEXCEPTION_INDEX] = true;
@@ -715,7 +820,7 @@ std::tuple<VkFramebuffer, VkExtent2D> RasterizerVulkan::ConfigureFramebuffers(
715 if (!view) { 820 if (!view) {
716 return false; 821 return false;
717 } 822 }
718 key.views.push_back(view->GetHandle()); 823 key.views.push_back(view->GetAttachment());
719 key.width = std::min(key.width, view->GetWidth()); 824 key.width = std::min(key.width, view->GetWidth());
720 key.height = std::min(key.height, view->GetHeight()); 825 key.height = std::min(key.height, view->GetHeight());
721 key.layers = std::min(key.layers, view->GetNumLayers()); 826 key.layers = std::min(key.layers, view->GetNumLayers());
@@ -761,7 +866,7 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt
761 const auto& gpu = system.GPU().Maxwell3D(); 866 const auto& gpu = system.GPU().Maxwell3D();
762 const auto& regs = gpu.regs; 867 const auto& regs = gpu.regs;
763 868
764 SetupVertexArrays(fixed_state.vertex_input, buffer_bindings); 869 SetupVertexArrays(buffer_bindings);
765 870
766 const u32 base_instance = regs.vb_base_instance; 871 const u32 base_instance = regs.vb_base_instance;
767 const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1; 872 const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1;
@@ -775,20 +880,21 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt
775} 880}
776 881
777void RasterizerVulkan::SetupShaderDescriptors( 882void RasterizerVulkan::SetupShaderDescriptors(
778 const std::array<Shader, Maxwell::MaxShaderProgram>& shaders) { 883 const std::array<Shader*, Maxwell::MaxShaderProgram>& shaders) {
779 texture_cache.GuardSamplers(true); 884 texture_cache.GuardSamplers(true);
780 885
781 for (std::size_t stage = 0; stage < Maxwell::MaxShaderStage; ++stage) { 886 for (std::size_t stage = 0; stage < Maxwell::MaxShaderStage; ++stage) {
782 // Skip VertexA stage 887 // Skip VertexA stage
783 const auto& shader = shaders[stage + 1]; 888 Shader* const shader = shaders[stage + 1];
784 if (!shader) { 889 if (!shader) {
785 continue; 890 continue;
786 } 891 }
787 const auto& entries = shader->GetEntries(); 892 const auto& entries = shader->GetEntries();
788 SetupGraphicsConstBuffers(entries, stage); 893 SetupGraphicsConstBuffers(entries, stage);
789 SetupGraphicsGlobalBuffers(entries, stage); 894 SetupGraphicsGlobalBuffers(entries, stage);
790 SetupGraphicsTexelBuffers(entries, stage); 895 SetupGraphicsUniformTexels(entries, stage);
791 SetupGraphicsTextures(entries, stage); 896 SetupGraphicsTextures(entries, stage);
897 SetupGraphicsStorageTexels(entries, stage);
792 SetupGraphicsImages(entries, stage); 898 SetupGraphicsImages(entries, stage);
793 } 899 }
794 texture_cache.GuardSamplers(false); 900 texture_cache.GuardSamplers(false);
@@ -831,6 +937,17 @@ void RasterizerVulkan::UpdateDynamicStates() {
831 UpdateBlendConstants(regs); 937 UpdateBlendConstants(regs);
832 UpdateDepthBounds(regs); 938 UpdateDepthBounds(regs);
833 UpdateStencilFaces(regs); 939 UpdateStencilFaces(regs);
940 if (device.IsExtExtendedDynamicStateSupported()) {
941 UpdateCullMode(regs);
942 UpdateDepthBoundsTestEnable(regs);
943 UpdateDepthTestEnable(regs);
944 UpdateDepthWriteEnable(regs);
945 UpdateDepthCompareOp(regs);
946 UpdateFrontFace(regs);
947 UpdatePrimitiveTopology(regs);
948 UpdateStencilOp(regs);
949 UpdateStencilTestEnable(regs);
950 }
834} 951}
835 952
836void RasterizerVulkan::BeginTransformFeedback() { 953void RasterizerVulkan::BeginTransformFeedback() {
@@ -838,6 +955,10 @@ void RasterizerVulkan::BeginTransformFeedback() {
838 if (regs.tfb_enabled == 0) { 955 if (regs.tfb_enabled == 0) {
839 return; 956 return;
840 } 957 }
958 if (!device.IsExtTransformFeedbackSupported()) {
959 LOG_ERROR(Render_Vulkan, "Transform feedbacks used but not supported");
960 return;
961 }
841 962
842 UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || 963 UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) ||
843 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || 964 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) ||
@@ -852,10 +973,10 @@ void RasterizerVulkan::BeginTransformFeedback() {
852 UNIMPLEMENTED_IF(binding.buffer_offset != 0); 973 UNIMPLEMENTED_IF(binding.buffer_offset != 0);
853 974
854 const GPUVAddr gpu_addr = binding.Address(); 975 const GPUVAddr gpu_addr = binding.Address();
855 const std::size_t size = binding.buffer_size; 976 const VkDeviceSize size = static_cast<VkDeviceSize>(binding.buffer_size);
856 const auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true); 977 const auto info = buffer_cache.UploadMemory(gpu_addr, size, 4, true);
857 978
858 scheduler.Record([buffer = buffer, offset = offset, size](vk::CommandBuffer cmdbuf) { 979 scheduler.Record([buffer = info.handle, offset = info.offset, size](vk::CommandBuffer cmdbuf) {
859 cmdbuf.BindTransformFeedbackBuffersEXT(0, 1, &buffer, &offset, &size); 980 cmdbuf.BindTransformFeedbackBuffersEXT(0, 1, &buffer, &offset, &size);
860 cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); 981 cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr);
861 }); 982 });
@@ -866,50 +987,33 @@ void RasterizerVulkan::EndTransformFeedback() {
866 if (regs.tfb_enabled == 0) { 987 if (regs.tfb_enabled == 0) {
867 return; 988 return;
868 } 989 }
990 if (!device.IsExtTransformFeedbackSupported()) {
991 return;
992 }
869 993
870 scheduler.Record( 994 scheduler.Record(
871 [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); }); 995 [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); });
872} 996}
873 997
874void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, 998void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) {
875 BufferBindings& buffer_bindings) {
876 const auto& regs = system.GPU().Maxwell3D().regs; 999 const auto& regs = system.GPU().Maxwell3D().regs;
877 1000
878 for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
879 const auto& attrib = regs.vertex_attrib_format[index];
880 if (!attrib.IsValid()) {
881 vertex_input.SetAttribute(index, false, 0, 0, {}, {});
882 continue;
883 }
884
885 [[maybe_unused]] const auto& buffer = regs.vertex_array[attrib.buffer];
886 ASSERT(buffer.IsEnabled());
887
888 vertex_input.SetAttribute(index, true, attrib.buffer, attrib.offset, attrib.type.Value(),
889 attrib.size.Value());
890 }
891
892 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 1001 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
893 const auto& vertex_array = regs.vertex_array[index]; 1002 const auto& vertex_array = regs.vertex_array[index];
894 if (!vertex_array.IsEnabled()) { 1003 if (!vertex_array.IsEnabled()) {
895 vertex_input.SetBinding(index, false, 0, 0);
896 continue; 1004 continue;
897 } 1005 }
898 vertex_input.SetBinding(
899 index, true, vertex_array.stride,
900 regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0);
901
902 const GPUVAddr start{vertex_array.StartAddress()}; 1006 const GPUVAddr start{vertex_array.StartAddress()};
903 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; 1007 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
904 1008
905 ASSERT(end >= start); 1009 ASSERT(end >= start);
906 const std::size_t size{end - start}; 1010 const std::size_t size = end - start;
907 if (size == 0) { 1011 if (size == 0) {
908 buffer_bindings.AddVertexBinding(DefaultBuffer(), 0); 1012 buffer_bindings.AddVertexBinding(DefaultBuffer(), 0, DEFAULT_BUFFER_SIZE, 0);
909 continue; 1013 continue;
910 } 1014 }
911 const auto [buffer, offset] = buffer_cache.UploadMemory(start, size); 1015 const auto info = buffer_cache.UploadMemory(start, size);
912 buffer_bindings.AddVertexBinding(buffer, offset); 1016 buffer_bindings.AddVertexBinding(info.handle, info.offset, size, vertex_array.stride);
913 } 1017 }
914} 1018}
915 1019
@@ -931,7 +1035,9 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar
931 break; 1035 break;
932 } 1036 }
933 const GPUVAddr gpu_addr = regs.index_array.IndexStart(); 1037 const GPUVAddr gpu_addr = regs.index_array.IndexStart();
934 auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); 1038 const auto info = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize());
1039 VkBuffer buffer = info.handle;
1040 u64 offset = info.offset;
935 std::tie(buffer, offset) = quad_indexed_pass.Assemble( 1041 std::tie(buffer, offset) = quad_indexed_pass.Assemble(
936 regs.index_array.format, params.num_vertices, params.base_vertex, buffer, offset); 1042 regs.index_array.format, params.num_vertices, params.base_vertex, buffer, offset);
937 1043
@@ -945,7 +1051,9 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar
945 break; 1051 break;
946 } 1052 }
947 const GPUVAddr gpu_addr = regs.index_array.IndexStart(); 1053 const GPUVAddr gpu_addr = regs.index_array.IndexStart();
948 auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize()); 1054 const auto info = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize());
1055 VkBuffer buffer = info.handle;
1056 u64 offset = info.offset;
949 1057
950 auto format = regs.index_array.format; 1058 auto format = regs.index_array.format;
951 const bool is_uint8 = format == Maxwell::IndexFormat::UnsignedByte; 1059 const bool is_uint8 = format == Maxwell::IndexFormat::UnsignedByte;
@@ -980,12 +1088,12 @@ void RasterizerVulkan::SetupGraphicsGlobalBuffers(const ShaderEntries& entries,
980 } 1088 }
981} 1089}
982 1090
983void RasterizerVulkan::SetupGraphicsTexelBuffers(const ShaderEntries& entries, std::size_t stage) { 1091void RasterizerVulkan::SetupGraphicsUniformTexels(const ShaderEntries& entries, std::size_t stage) {
984 MICROPROFILE_SCOPE(Vulkan_Textures); 1092 MICROPROFILE_SCOPE(Vulkan_Textures);
985 const auto& gpu = system.GPU().Maxwell3D(); 1093 const auto& gpu = system.GPU().Maxwell3D();
986 for (const auto& entry : entries.texel_buffers) { 1094 for (const auto& entry : entries.uniform_texels) {
987 const auto image = GetTextureInfo(gpu, entry, stage).tic; 1095 const auto image = GetTextureInfo(gpu, entry, stage).tic;
988 SetupTexelBuffer(image, entry); 1096 SetupUniformTexels(image, entry);
989 } 1097 }
990} 1098}
991 1099
@@ -1000,6 +1108,15 @@ void RasterizerVulkan::SetupGraphicsTextures(const ShaderEntries& entries, std::
1000 } 1108 }
1001} 1109}
1002 1110
1111void RasterizerVulkan::SetupGraphicsStorageTexels(const ShaderEntries& entries, std::size_t stage) {
1112 MICROPROFILE_SCOPE(Vulkan_Textures);
1113 const auto& gpu = system.GPU().Maxwell3D();
1114 for (const auto& entry : entries.storage_texels) {
1115 const auto image = GetTextureInfo(gpu, entry, stage).tic;
1116 SetupStorageTexel(image, entry);
1117 }
1118}
1119
1003void RasterizerVulkan::SetupGraphicsImages(const ShaderEntries& entries, std::size_t stage) { 1120void RasterizerVulkan::SetupGraphicsImages(const ShaderEntries& entries, std::size_t stage) {
1004 MICROPROFILE_SCOPE(Vulkan_Images); 1121 MICROPROFILE_SCOPE(Vulkan_Images);
1005 const auto& gpu = system.GPU().Maxwell3D(); 1122 const auto& gpu = system.GPU().Maxwell3D();
@@ -1032,12 +1149,12 @@ void RasterizerVulkan::SetupComputeGlobalBuffers(const ShaderEntries& entries) {
1032 } 1149 }
1033} 1150}
1034 1151
1035void RasterizerVulkan::SetupComputeTexelBuffers(const ShaderEntries& entries) { 1152void RasterizerVulkan::SetupComputeUniformTexels(const ShaderEntries& entries) {
1036 MICROPROFILE_SCOPE(Vulkan_Textures); 1153 MICROPROFILE_SCOPE(Vulkan_Textures);
1037 const auto& gpu = system.GPU().KeplerCompute(); 1154 const auto& gpu = system.GPU().KeplerCompute();
1038 for (const auto& entry : entries.texel_buffers) { 1155 for (const auto& entry : entries.uniform_texels) {
1039 const auto image = GetTextureInfo(gpu, entry, ComputeShaderIndex).tic; 1156 const auto image = GetTextureInfo(gpu, entry, ComputeShaderIndex).tic;
1040 SetupTexelBuffer(image, entry); 1157 SetupUniformTexels(image, entry);
1041 } 1158 }
1042} 1159}
1043 1160
@@ -1052,6 +1169,15 @@ void RasterizerVulkan::SetupComputeTextures(const ShaderEntries& entries) {
1052 } 1169 }
1053} 1170}
1054 1171
1172void RasterizerVulkan::SetupComputeStorageTexels(const ShaderEntries& entries) {
1173 MICROPROFILE_SCOPE(Vulkan_Textures);
1174 const auto& gpu = system.GPU().KeplerCompute();
1175 for (const auto& entry : entries.storage_texels) {
1176 const auto image = GetTextureInfo(gpu, entry, ComputeShaderIndex).tic;
1177 SetupStorageTexel(image, entry);
1178 }
1179}
1180
1055void RasterizerVulkan::SetupComputeImages(const ShaderEntries& entries) { 1181void RasterizerVulkan::SetupComputeImages(const ShaderEntries& entries) {
1056 MICROPROFILE_SCOPE(Vulkan_Images); 1182 MICROPROFILE_SCOPE(Vulkan_Images);
1057 const auto& gpu = system.GPU().KeplerCompute(); 1183 const auto& gpu = system.GPU().KeplerCompute();
@@ -1074,10 +1200,9 @@ void RasterizerVulkan::SetupConstBuffer(const ConstBufferEntry& entry,
1074 Common::AlignUp(CalculateConstBufferSize(entry, buffer), 4 * sizeof(float)); 1200 Common::AlignUp(CalculateConstBufferSize(entry, buffer), 4 * sizeof(float));
1075 ASSERT(size <= MaxConstbufferSize); 1201 ASSERT(size <= MaxConstbufferSize);
1076 1202
1077 const auto [buffer_handle, offset] = 1203 const auto info =
1078 buffer_cache.UploadMemory(buffer.address, size, device.GetUniformBufferAlignment()); 1204 buffer_cache.UploadMemory(buffer.address, size, device.GetUniformBufferAlignment());
1079 1205 update_descriptor_queue.AddBuffer(info.handle, info.offset, size);
1080 update_descriptor_queue.AddBuffer(buffer_handle, offset, size);
1081} 1206}
1082 1207
1083void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address) { 1208void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address) {
@@ -1091,18 +1216,18 @@ void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAdd
1091 // Note: Do *not* use DefaultBuffer() here, storage buffers can be written breaking the 1216 // Note: Do *not* use DefaultBuffer() here, storage buffers can be written breaking the
1092 // default buffer. 1217 // default buffer.
1093 static constexpr std::size_t dummy_size = 4; 1218 static constexpr std::size_t dummy_size = 4;
1094 const auto buffer = buffer_cache.GetEmptyBuffer(dummy_size); 1219 const auto info = buffer_cache.GetEmptyBuffer(dummy_size);
1095 update_descriptor_queue.AddBuffer(buffer, 0, dummy_size); 1220 update_descriptor_queue.AddBuffer(info.handle, info.offset, dummy_size);
1096 return; 1221 return;
1097 } 1222 }
1098 1223
1099 const auto [buffer, offset] = buffer_cache.UploadMemory( 1224 const auto info = buffer_cache.UploadMemory(
1100 actual_addr, size, device.GetStorageBufferAlignment(), entry.IsWritten()); 1225 actual_addr, size, device.GetStorageBufferAlignment(), entry.IsWritten());
1101 update_descriptor_queue.AddBuffer(buffer, offset, size); 1226 update_descriptor_queue.AddBuffer(info.handle, info.offset, size);
1102} 1227}
1103 1228
1104void RasterizerVulkan::SetupTexelBuffer(const Tegra::Texture::TICEntry& tic, 1229void RasterizerVulkan::SetupUniformTexels(const Tegra::Texture::TICEntry& tic,
1105 const TexelBufferEntry& entry) { 1230 const UniformTexelEntry& entry) {
1106 const auto view = texture_cache.GetTextureSurface(tic, entry); 1231 const auto view = texture_cache.GetTextureSurface(tic, entry);
1107 ASSERT(view->IsBufferView()); 1232 ASSERT(view->IsBufferView());
1108 1233
@@ -1114,16 +1239,24 @@ void RasterizerVulkan::SetupTexture(const Tegra::Texture::FullTextureInfo& textu
1114 auto view = texture_cache.GetTextureSurface(texture.tic, entry); 1239 auto view = texture_cache.GetTextureSurface(texture.tic, entry);
1115 ASSERT(!view->IsBufferView()); 1240 ASSERT(!view->IsBufferView());
1116 1241
1117 const auto image_view = view->GetHandle(texture.tic.x_source, texture.tic.y_source, 1242 const VkImageView image_view = view->GetImageView(texture.tic.x_source, texture.tic.y_source,
1118 texture.tic.z_source, texture.tic.w_source); 1243 texture.tic.z_source, texture.tic.w_source);
1119 const auto sampler = sampler_cache.GetSampler(texture.tsc); 1244 const auto sampler = sampler_cache.GetSampler(texture.tsc);
1120 update_descriptor_queue.AddSampledImage(sampler, image_view); 1245 update_descriptor_queue.AddSampledImage(sampler, image_view);
1121 1246
1122 const auto image_layout = update_descriptor_queue.GetLastImageLayout(); 1247 VkImageLayout* const image_layout = update_descriptor_queue.LastImageLayout();
1123 *image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; 1248 *image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1124 sampled_views.push_back(ImageView{std::move(view), image_layout}); 1249 sampled_views.push_back(ImageView{std::move(view), image_layout});
1125} 1250}
1126 1251
1252void RasterizerVulkan::SetupStorageTexel(const Tegra::Texture::TICEntry& tic,
1253 const StorageTexelEntry& entry) {
1254 const auto view = texture_cache.GetImageSurface(tic, entry);
1255 ASSERT(view->IsBufferView());
1256
1257 update_descriptor_queue.AddTexelBuffer(view->GetBufferView());
1258}
1259
1127void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const ImageEntry& entry) { 1260void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const ImageEntry& entry) {
1128 auto view = texture_cache.GetImageSurface(tic, entry); 1261 auto view = texture_cache.GetImageSurface(tic, entry);
1129 1262
@@ -1133,10 +1266,11 @@ void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const Ima
1133 1266
1134 UNIMPLEMENTED_IF(tic.IsBuffer()); 1267 UNIMPLEMENTED_IF(tic.IsBuffer());
1135 1268
1136 const auto image_view = view->GetHandle(tic.x_source, tic.y_source, tic.z_source, tic.w_source); 1269 const VkImageView image_view =
1270 view->GetImageView(tic.x_source, tic.y_source, tic.z_source, tic.w_source);
1137 update_descriptor_queue.AddImage(image_view); 1271 update_descriptor_queue.AddImage(image_view);
1138 1272
1139 const auto image_layout = update_descriptor_queue.GetLastImageLayout(); 1273 VkImageLayout* const image_layout = update_descriptor_queue.LastImageLayout();
1140 *image_layout = VK_IMAGE_LAYOUT_GENERAL; 1274 *image_layout = VK_IMAGE_LAYOUT_GENERAL;
1141 image_views.push_back(ImageView{std::move(view), image_layout}); 1275 image_views.push_back(ImageView{std::move(view), image_layout});
1142} 1276}
@@ -1231,6 +1365,117 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
1231 } 1365 }
1232} 1366}
1233 1367
1368void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) {
1369 if (!state_tracker.TouchCullMode()) {
1370 return;
1371 }
1372 scheduler.Record(
1373 [enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) {
1374 cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE);
1375 });
1376}
1377
1378void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1379 if (!state_tracker.TouchDepthBoundsTestEnable()) {
1380 return;
1381 }
1382 scheduler.Record([enable = regs.depth_bounds_enable](vk::CommandBuffer cmdbuf) {
1383 cmdbuf.SetDepthBoundsTestEnableEXT(enable);
1384 });
1385}
1386
1387void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1388 if (!state_tracker.TouchDepthTestEnable()) {
1389 return;
1390 }
1391 scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) {
1392 cmdbuf.SetDepthTestEnableEXT(enable);
1393 });
1394}
1395
1396void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1397 if (!state_tracker.TouchDepthWriteEnable()) {
1398 return;
1399 }
1400 scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) {
1401 cmdbuf.SetDepthWriteEnableEXT(enable);
1402 });
1403}
1404
1405void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) {
1406 if (!state_tracker.TouchDepthCompareOp()) {
1407 return;
1408 }
1409 scheduler.Record([func = regs.depth_test_func](vk::CommandBuffer cmdbuf) {
1410 cmdbuf.SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(func));
1411 });
1412}
1413
1414void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
1415 if (!state_tracker.TouchFrontFace()) {
1416 return;
1417 }
1418
1419 VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face);
1420 if (regs.screen_y_control.triangle_rast_flip != 0) {
1421 front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE
1422 : VK_FRONT_FACE_CLOCKWISE;
1423 }
1424 scheduler.Record(
1425 [front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); });
1426}
1427
1428void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
1429 if (!state_tracker.TouchPrimitiveTopology()) {
1430 return;
1431 }
1432 const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
1433 scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
1434 cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
1435 });
1436}
1437
1438void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
1439 if (!state_tracker.TouchStencilOp()) {
1440 return;
1441 }
1442 const Maxwell::StencilOp fail = regs.stencil_front_op_fail;
1443 const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail;
1444 const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass;
1445 const Maxwell::ComparisonOp compare = regs.stencil_front_func_func;
1446 if (regs.stencil_two_side_enable) {
1447 scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) {
1448 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail),
1449 MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
1450 MaxwellToVK::ComparisonOp(compare));
1451 });
1452 } else {
1453 const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail;
1454 const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail;
1455 const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass;
1456 const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func;
1457 scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass,
1458 back_compare](vk::CommandBuffer cmdbuf) {
1459 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail),
1460 MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
1461 MaxwellToVK::ComparisonOp(compare));
1462 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_BACK_BIT, MaxwellToVK::StencilOp(back_fail),
1463 MaxwellToVK::StencilOp(back_zpass),
1464 MaxwellToVK::StencilOp(back_zfail),
1465 MaxwellToVK::ComparisonOp(back_compare));
1466 });
1467 }
1468}
1469
1470void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1471 if (!state_tracker.TouchStencilTestEnable()) {
1472 return;
1473 }
1474 scheduler.Record([enable = regs.stencil_enable](vk::CommandBuffer cmdbuf) {
1475 cmdbuf.SetStencilTestEnableEXT(enable);
1476 });
1477}
1478
1234std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const { 1479std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const {
1235 std::size_t size = CalculateVertexArraysSize(); 1480 std::size_t size = CalculateVertexArraysSize();
1236 if (is_indexed) { 1481 if (is_indexed) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 0ed0e48c6..923178b0b 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -159,7 +159,10 @@ private:
159 159
160 void FlushWork(); 160 void FlushWork();
161 161
162 Texceptions UpdateAttachments(); 162 /// @brief Updates the currently bound attachments
163 /// @param is_clear True when the framebuffer is updated as a clear
164 /// @return Bitfield of attachments being used as sampled textures
165 Texceptions UpdateAttachments(bool is_clear);
163 166
164 std::tuple<VkFramebuffer, VkExtent2D> ConfigureFramebuffers(VkRenderPass renderpass); 167 std::tuple<VkFramebuffer, VkExtent2D> ConfigureFramebuffers(VkRenderPass renderpass);
165 168
@@ -168,7 +171,7 @@ private:
168 bool is_indexed, bool is_instanced); 171 bool is_indexed, bool is_instanced);
169 172
170 /// Setup descriptors in the graphics pipeline. 173 /// Setup descriptors in the graphics pipeline.
171 void SetupShaderDescriptors(const std::array<Shader, Maxwell::MaxShaderProgram>& shaders); 174 void SetupShaderDescriptors(const std::array<Shader*, Maxwell::MaxShaderProgram>& shaders);
172 175
173 void SetupImageTransitions(Texceptions texceptions, 176 void SetupImageTransitions(Texceptions texceptions,
174 const std::array<View, Maxwell::NumRenderTargets>& color_attachments, 177 const std::array<View, Maxwell::NumRenderTargets>& color_attachments,
@@ -182,8 +185,7 @@ private:
182 185
183 bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment); 186 bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment);
184 187
185 void SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, 188 void SetupVertexArrays(BufferBindings& buffer_bindings);
186 BufferBindings& buffer_bindings);
187 189
188 void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed); 190 void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed);
189 191
@@ -193,12 +195,15 @@ private:
193 /// Setup global buffers in the graphics pipeline. 195 /// Setup global buffers in the graphics pipeline.
194 void SetupGraphicsGlobalBuffers(const ShaderEntries& entries, std::size_t stage); 196 void SetupGraphicsGlobalBuffers(const ShaderEntries& entries, std::size_t stage);
195 197
196 /// Setup texel buffers in the graphics pipeline. 198 /// Setup uniform texels in the graphics pipeline.
197 void SetupGraphicsTexelBuffers(const ShaderEntries& entries, std::size_t stage); 199 void SetupGraphicsUniformTexels(const ShaderEntries& entries, std::size_t stage);
198 200
199 /// Setup textures in the graphics pipeline. 201 /// Setup textures in the graphics pipeline.
200 void SetupGraphicsTextures(const ShaderEntries& entries, std::size_t stage); 202 void SetupGraphicsTextures(const ShaderEntries& entries, std::size_t stage);
201 203
204 /// Setup storage texels in the graphics pipeline.
205 void SetupGraphicsStorageTexels(const ShaderEntries& entries, std::size_t stage);
206
202 /// Setup images in the graphics pipeline. 207 /// Setup images in the graphics pipeline.
203 void SetupGraphicsImages(const ShaderEntries& entries, std::size_t stage); 208 void SetupGraphicsImages(const ShaderEntries& entries, std::size_t stage);
204 209
@@ -209,11 +214,14 @@ private:
209 void SetupComputeGlobalBuffers(const ShaderEntries& entries); 214 void SetupComputeGlobalBuffers(const ShaderEntries& entries);
210 215
211 /// Setup texel buffers in the compute pipeline. 216 /// Setup texel buffers in the compute pipeline.
212 void SetupComputeTexelBuffers(const ShaderEntries& entries); 217 void SetupComputeUniformTexels(const ShaderEntries& entries);
213 218
214 /// Setup textures in the compute pipeline. 219 /// Setup textures in the compute pipeline.
215 void SetupComputeTextures(const ShaderEntries& entries); 220 void SetupComputeTextures(const ShaderEntries& entries);
216 221
222 /// Setup storage texels in the compute pipeline.
223 void SetupComputeStorageTexels(const ShaderEntries& entries);
224
217 /// Setup images in the compute pipeline. 225 /// Setup images in the compute pipeline.
218 void SetupComputeImages(const ShaderEntries& entries); 226 void SetupComputeImages(const ShaderEntries& entries);
219 227
@@ -222,10 +230,12 @@ private:
222 230
223 void SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address); 231 void SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address);
224 232
225 void SetupTexelBuffer(const Tegra::Texture::TICEntry& image, const TexelBufferEntry& entry); 233 void SetupUniformTexels(const Tegra::Texture::TICEntry& image, const UniformTexelEntry& entry);
226 234
227 void SetupTexture(const Tegra::Texture::FullTextureInfo& texture, const SamplerEntry& entry); 235 void SetupTexture(const Tegra::Texture::FullTextureInfo& texture, const SamplerEntry& entry);
228 236
237 void SetupStorageTexel(const Tegra::Texture::TICEntry& tic, const StorageTexelEntry& entry);
238
229 void SetupImage(const Tegra::Texture::TICEntry& tic, const ImageEntry& entry); 239 void SetupImage(const Tegra::Texture::TICEntry& tic, const ImageEntry& entry);
230 240
231 void UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs); 241 void UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs);
@@ -235,6 +245,16 @@ private:
235 void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); 245 void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
236 void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); 246 void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
237 247
248 void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs);
249 void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
250 void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
251 void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
252 void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
253 void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
254 void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs);
255 void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
256 void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
257
238 std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const; 258 std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const;
239 259
240 std::size_t CalculateComputeStreamBufferSize() const; 260 std::size_t CalculateComputeStreamBufferSize() const;
diff --git a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp
index e6f2fa553..616eacc36 100644
--- a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp
@@ -9,6 +9,8 @@
9#include "video_core/renderer_vulkan/wrapper.h" 9#include "video_core/renderer_vulkan/wrapper.h"
10#include "video_core/textures/texture.h" 10#include "video_core/textures/texture.h"
11 11
12using Tegra::Texture::TextureMipmapFilter;
13
12namespace Vulkan { 14namespace Vulkan {
13 15
14namespace { 16namespace {
@@ -63,8 +65,8 @@ vk::Sampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc) c
63 ci.maxAnisotropy = tsc.GetMaxAnisotropy(); 65 ci.maxAnisotropy = tsc.GetMaxAnisotropy();
64 ci.compareEnable = tsc.depth_compare_enabled; 66 ci.compareEnable = tsc.depth_compare_enabled;
65 ci.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func); 67 ci.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func);
66 ci.minLod = tsc.GetMinLod(); 68 ci.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.GetMinLod();
67 ci.maxLod = tsc.GetMaxLod(); 69 ci.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.GetMaxLod();
68 ci.borderColor = arbitrary_borders ? VK_BORDER_COLOR_INT_CUSTOM_EXT : ConvertBorderColor(color); 70 ci.borderColor = arbitrary_borders ? VK_BORDER_COLOR_INT_CUSTOM_EXT : ConvertBorderColor(color);
69 ci.unnormalizedCoordinates = VK_FALSE; 71 ci.unnormalizedCoordinates = VK_FALSE;
70 return device.GetLogical().CreateSampler(ci); 72 return device.GetLogical().CreateSampler(ci);
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 82ec9180e..56524e6f3 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -9,6 +9,7 @@
9#include <utility> 9#include <utility>
10 10
11#include "common/microprofile.h" 11#include "common/microprofile.h"
12#include "common/thread.h"
12#include "video_core/renderer_vulkan/vk_device.h" 13#include "video_core/renderer_vulkan/vk_device.h"
13#include "video_core/renderer_vulkan/vk_query_cache.h" 14#include "video_core/renderer_vulkan/vk_query_cache.h"
14#include "video_core/renderer_vulkan/vk_resource_manager.h" 15#include "video_core/renderer_vulkan/vk_resource_manager.h"
@@ -133,6 +134,7 @@ void VKScheduler::BindGraphicsPipeline(VkPipeline pipeline) {
133} 134}
134 135
135void VKScheduler::WorkerThread() { 136void VKScheduler::WorkerThread() {
137 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
136 std::unique_lock lock{mutex}; 138 std::unique_lock lock{mutex};
137 do { 139 do {
138 cv.wait(lock, [this] { return !chunk_queue.Empty() || quit; }); 140 cv.wait(lock, [this] { return !chunk_queue.Empty() || quit; });
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index 6f6dedd82..97429cc59 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -400,8 +400,9 @@ private:
400 u32 binding = specialization.base_binding; 400 u32 binding = specialization.base_binding;
401 binding = DeclareConstantBuffers(binding); 401 binding = DeclareConstantBuffers(binding);
402 binding = DeclareGlobalBuffers(binding); 402 binding = DeclareGlobalBuffers(binding);
403 binding = DeclareTexelBuffers(binding); 403 binding = DeclareUniformTexels(binding);
404 binding = DeclareSamplers(binding); 404 binding = DeclareSamplers(binding);
405 binding = DeclareStorageTexels(binding);
405 binding = DeclareImages(binding); 406 binding = DeclareImages(binding);
406 407
407 const Id main = OpFunction(t_void, {}, TypeFunction(t_void)); 408 const Id main = OpFunction(t_void, {}, TypeFunction(t_void));
@@ -741,8 +742,10 @@ private:
741 if (!IsGenericAttribute(index)) { 742 if (!IsGenericAttribute(index)) {
742 continue; 743 continue;
743 } 744 }
744
745 const u32 location = GetGenericAttributeLocation(index); 745 const u32 location = GetGenericAttributeLocation(index);
746 if (!IsAttributeEnabled(location)) {
747 continue;
748 }
746 const auto type_descriptor = GetAttributeType(location); 749 const auto type_descriptor = GetAttributeType(location);
747 Id type; 750 Id type;
748 if (IsInputAttributeArray()) { 751 if (IsInputAttributeArray()) {
@@ -887,7 +890,7 @@ private:
887 return binding; 890 return binding;
888 } 891 }
889 892
890 u32 DeclareTexelBuffers(u32 binding) { 893 u32 DeclareUniformTexels(u32 binding) {
891 for (const auto& sampler : ir.GetSamplers()) { 894 for (const auto& sampler : ir.GetSamplers()) {
892 if (!sampler.is_buffer) { 895 if (!sampler.is_buffer) {
893 continue; 896 continue;
@@ -908,7 +911,7 @@ private:
908 Decorate(id, spv::Decoration::Binding, binding++); 911 Decorate(id, spv::Decoration::Binding, binding++);
909 Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); 912 Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
910 913
911 texel_buffers.emplace(sampler.index, TexelBuffer{image_type, id}); 914 uniform_texels.emplace(sampler.index, TexelBuffer{image_type, id});
912 } 915 }
913 return binding; 916 return binding;
914 } 917 }
@@ -943,31 +946,48 @@ private:
943 return binding; 946 return binding;
944 } 947 }
945 948
946 u32 DeclareImages(u32 binding) { 949 u32 DeclareStorageTexels(u32 binding) {
947 for (const auto& image : ir.GetImages()) { 950 for (const auto& image : ir.GetImages()) {
948 const auto [dim, arrayed] = GetImageDim(image); 951 if (image.type != Tegra::Shader::ImageType::TextureBuffer) {
949 constexpr int depth = 0; 952 continue;
950 constexpr bool ms = false;
951 constexpr int sampled = 2; // This won't be accessed with a sampler
952 constexpr auto format = spv::ImageFormat::Unknown;
953 const Id image_type = TypeImage(t_uint, dim, depth, arrayed, ms, sampled, format, {});
954 const Id pointer_type = TypePointer(spv::StorageClass::UniformConstant, image_type);
955 const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant);
956 AddGlobalVariable(Name(id, fmt::format("image_{}", image.index)));
957
958 Decorate(id, spv::Decoration::Binding, binding++);
959 Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
960 if (image.is_read && !image.is_written) {
961 Decorate(id, spv::Decoration::NonWritable);
962 } else if (image.is_written && !image.is_read) {
963 Decorate(id, spv::Decoration::NonReadable);
964 } 953 }
954 DeclareImage(image, binding);
955 }
956 return binding;
957 }
965 958
966 images.emplace(image.index, StorageImage{image_type, id}); 959 u32 DeclareImages(u32 binding) {
960 for (const auto& image : ir.GetImages()) {
961 if (image.type == Tegra::Shader::ImageType::TextureBuffer) {
962 continue;
963 }
964 DeclareImage(image, binding);
967 } 965 }
968 return binding; 966 return binding;
969 } 967 }
970 968
969 void DeclareImage(const Image& image, u32& binding) {
970 const auto [dim, arrayed] = GetImageDim(image);
971 constexpr int depth = 0;
972 constexpr bool ms = false;
973 constexpr int sampled = 2; // This won't be accessed with a sampler
974 const auto format = image.is_atomic ? spv::ImageFormat::R32ui : spv::ImageFormat::Unknown;
975 const Id image_type = TypeImage(t_uint, dim, depth, arrayed, ms, sampled, format, {});
976 const Id pointer_type = TypePointer(spv::StorageClass::UniformConstant, image_type);
977 const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant);
978 AddGlobalVariable(Name(id, fmt::format("image_{}", image.index)));
979
980 Decorate(id, spv::Decoration::Binding, binding++);
981 Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
982 if (image.is_read && !image.is_written) {
983 Decorate(id, spv::Decoration::NonWritable);
984 } else if (image.is_written && !image.is_read) {
985 Decorate(id, spv::Decoration::NonReadable);
986 }
987
988 images.emplace(image.index, StorageImage{image_type, id});
989 }
990
971 bool IsRenderTargetEnabled(u32 rt) const { 991 bool IsRenderTargetEnabled(u32 rt) const {
972 for (u32 component = 0; component < 4; ++component) { 992 for (u32 component = 0; component < 4; ++component) {
973 if (header.ps.IsColorComponentOutputEnabled(rt, component)) { 993 if (header.ps.IsColorComponentOutputEnabled(rt, component)) {
@@ -986,6 +1006,10 @@ private:
986 return stage == ShaderType::TesselationControl; 1006 return stage == ShaderType::TesselationControl;
987 } 1007 }
988 1008
1009 bool IsAttributeEnabled(u32 location) const {
1010 return stage != ShaderType::Vertex || specialization.enabled_attributes[location];
1011 }
1012
989 u32 GetNumInputVertices() const { 1013 u32 GetNumInputVertices() const {
990 switch (stage) { 1014 switch (stage) {
991 case ShaderType::Geometry: 1015 case ShaderType::Geometry:
@@ -1201,16 +1225,20 @@ private:
1201 UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element); 1225 UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
1202 return {v_float_zero, Type::Float}; 1226 return {v_float_zero, Type::Float};
1203 default: 1227 default:
1204 if (IsGenericAttribute(attribute)) { 1228 if (!IsGenericAttribute(attribute)) {
1205 const u32 location = GetGenericAttributeLocation(attribute); 1229 break;
1206 const auto type_descriptor = GetAttributeType(location);
1207 const Type type = type_descriptor.type;
1208 const Id attribute_id = input_attributes.at(attribute);
1209 const std::vector elements = {element};
1210 const Id pointer = ArrayPass(type_descriptor.scalar, attribute_id, elements);
1211 return {OpLoad(GetTypeDefinition(type), pointer), type};
1212 } 1230 }
1213 break; 1231 const u32 location = GetGenericAttributeLocation(attribute);
1232 if (!IsAttributeEnabled(location)) {
1233 // Disabled attributes (also known as constant attributes) always return zero.
1234 return {v_float_zero, Type::Float};
1235 }
1236 const auto type_descriptor = GetAttributeType(location);
1237 const Type type = type_descriptor.type;
1238 const Id attribute_id = input_attributes.at(attribute);
1239 const std::vector elements = {element};
1240 const Id pointer = ArrayPass(type_descriptor.scalar, attribute_id, elements);
1241 return {OpLoad(GetTypeDefinition(type), pointer), type};
1214 } 1242 }
1215 UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); 1243 UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
1216 return {v_float_zero, Type::Float}; 1244 return {v_float_zero, Type::Float};
@@ -1246,7 +1274,7 @@ private:
1246 } else { 1274 } else {
1247 UNREACHABLE_MSG("Unmanaged offset node type"); 1275 UNREACHABLE_MSG("Unmanaged offset node type");
1248 } 1276 }
1249 pointer = OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0), buffer_index, 1277 pointer = OpAccessChain(t_cbuf_float, buffer_id, v_uint_zero, buffer_index,
1250 buffer_element); 1278 buffer_element);
1251 } 1279 }
1252 return {OpLoad(t_float, pointer), Type::Float}; 1280 return {OpLoad(t_float, pointer), Type::Float};
@@ -1601,7 +1629,7 @@ private:
1601 1629
1602 const Id result = OpIAddCarry(TypeStruct({t_uint, t_uint}), op_a, op_b); 1630 const Id result = OpIAddCarry(TypeStruct({t_uint, t_uint}), op_a, op_b);
1603 const Id carry = OpCompositeExtract(t_uint, result, 1); 1631 const Id carry = OpCompositeExtract(t_uint, result, 1);
1604 return {OpINotEqual(t_bool, carry, Constant(t_uint, 0)), Type::Bool}; 1632 return {OpINotEqual(t_bool, carry, v_uint_zero), Type::Bool};
1605 } 1633 }
1606 1634
1607 Expression LogicalAssign(Operation operation) { 1635 Expression LogicalAssign(Operation operation) {
@@ -1664,7 +1692,7 @@ private:
1664 const auto& meta = std::get<MetaTexture>(operation.GetMeta()); 1692 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1665 const u32 index = meta.sampler.index; 1693 const u32 index = meta.sampler.index;
1666 if (meta.sampler.is_buffer) { 1694 if (meta.sampler.is_buffer) {
1667 const auto& entry = texel_buffers.at(index); 1695 const auto& entry = uniform_texels.at(index);
1668 return OpLoad(entry.image_type, entry.image); 1696 return OpLoad(entry.image_type, entry.image);
1669 } else { 1697 } else {
1670 const auto& entry = sampled_images.at(index); 1698 const auto& entry = sampled_images.at(index);
@@ -1941,39 +1969,20 @@ private:
1941 return {}; 1969 return {};
1942 } 1970 }
1943 1971
1944 Expression AtomicImageAdd(Operation operation) { 1972 template <Id (Module::*func)(Id, Id, Id, Id, Id)>
1945 UNIMPLEMENTED(); 1973 Expression AtomicImage(Operation operation) {
1946 return {}; 1974 const auto& meta{std::get<MetaImage>(operation.GetMeta())};
1947 } 1975 ASSERT(meta.values.size() == 1);
1948
1949 Expression AtomicImageMin(Operation operation) {
1950 UNIMPLEMENTED();
1951 return {};
1952 }
1953
1954 Expression AtomicImageMax(Operation operation) {
1955 UNIMPLEMENTED();
1956 return {};
1957 }
1958
1959 Expression AtomicImageAnd(Operation operation) {
1960 UNIMPLEMENTED();
1961 return {};
1962 }
1963
1964 Expression AtomicImageOr(Operation operation) {
1965 UNIMPLEMENTED();
1966 return {};
1967 }
1968 1976
1969 Expression AtomicImageXor(Operation operation) { 1977 const Id coordinate = GetCoordinates(operation, Type::Int);
1970 UNIMPLEMENTED(); 1978 const Id image = images.at(meta.image.index).image;
1971 return {}; 1979 const Id sample = v_uint_zero;
1972 } 1980 const Id pointer = OpImageTexelPointer(t_image_uint, image, coordinate, sample);
1973 1981
1974 Expression AtomicImageExchange(Operation operation) { 1982 const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device));
1975 UNIMPLEMENTED(); 1983 const Id semantics = v_uint_zero;
1976 return {}; 1984 const Id value = AsUint(Visit(meta.values[0]));
1985 return {(this->*func)(t_uint, pointer, scope, semantics, value), Type::Uint};
1977 } 1986 }
1978 1987
1979 template <Id (Module::*func)(Id, Id, Id, Id, Id)> 1988 template <Id (Module::*func)(Id, Id, Id, Id, Id)>
@@ -1988,7 +1997,7 @@ private:
1988 return {v_float_zero, Type::Float}; 1997 return {v_float_zero, Type::Float};
1989 } 1998 }
1990 const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); 1999 const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device));
1991 const Id semantics = Constant(t_uint, 0); 2000 const Id semantics = v_uint_zero;
1992 const Id value = AsUint(Visit(operation[1])); 2001 const Id value = AsUint(Visit(operation[1]));
1993 2002
1994 return {(this->*func)(t_uint, pointer, scope, semantics, value), Type::Uint}; 2003 return {(this->*func)(t_uint, pointer, scope, semantics, value), Type::Uint};
@@ -2612,11 +2621,11 @@ private:
2612 2621
2613 &SPIRVDecompiler::ImageLoad, 2622 &SPIRVDecompiler::ImageLoad,
2614 &SPIRVDecompiler::ImageStore, 2623 &SPIRVDecompiler::ImageStore,
2615 &SPIRVDecompiler::AtomicImageAdd, 2624 &SPIRVDecompiler::AtomicImage<&Module::OpAtomicIAdd>,
2616 &SPIRVDecompiler::AtomicImageAnd, 2625 &SPIRVDecompiler::AtomicImage<&Module::OpAtomicAnd>,
2617 &SPIRVDecompiler::AtomicImageOr, 2626 &SPIRVDecompiler::AtomicImage<&Module::OpAtomicOr>,
2618 &SPIRVDecompiler::AtomicImageXor, 2627 &SPIRVDecompiler::AtomicImage<&Module::OpAtomicXor>,
2619 &SPIRVDecompiler::AtomicImageExchange, 2628 &SPIRVDecompiler::AtomicImage<&Module::OpAtomicExchange>,
2620 2629
2621 &SPIRVDecompiler::Atomic<&Module::OpAtomicExchange>, 2630 &SPIRVDecompiler::Atomic<&Module::OpAtomicExchange>,
2622 &SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd>, 2631 &SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd>,
@@ -2758,8 +2767,11 @@ private:
2758 Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); 2767 Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
2759 const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct); 2768 const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct);
2760 2769
2770 const Id t_image_uint = TypePointer(spv::StorageClass::Image, t_uint);
2771
2761 const Id v_float_zero = Constant(t_float, 0.0f); 2772 const Id v_float_zero = Constant(t_float, 0.0f);
2762 const Id v_float_one = Constant(t_float, 1.0f); 2773 const Id v_float_one = Constant(t_float, 1.0f);
2774 const Id v_uint_zero = Constant(t_uint, 0);
2763 2775
2764 // Nvidia uses these defaults for varyings (e.g. position and generic attributes) 2776 // Nvidia uses these defaults for varyings (e.g. position and generic attributes)
2765 const Id v_varying_default = 2777 const Id v_varying_default =
@@ -2784,15 +2796,16 @@ private:
2784 std::unordered_map<u8, GenericVaryingDescription> output_attributes; 2796 std::unordered_map<u8, GenericVaryingDescription> output_attributes;
2785 std::map<u32, Id> constant_buffers; 2797 std::map<u32, Id> constant_buffers;
2786 std::map<GlobalMemoryBase, Id> global_buffers; 2798 std::map<GlobalMemoryBase, Id> global_buffers;
2787 std::map<u32, TexelBuffer> texel_buffers; 2799 std::map<u32, TexelBuffer> uniform_texels;
2788 std::map<u32, SampledImage> sampled_images; 2800 std::map<u32, SampledImage> sampled_images;
2801 std::map<u32, TexelBuffer> storage_texels;
2789 std::map<u32, StorageImage> images; 2802 std::map<u32, StorageImage> images;
2790 2803
2804 std::array<Id, Maxwell::NumRenderTargets> frag_colors{};
2791 Id instance_index{}; 2805 Id instance_index{};
2792 Id vertex_index{}; 2806 Id vertex_index{};
2793 Id base_instance{}; 2807 Id base_instance{};
2794 Id base_vertex{}; 2808 Id base_vertex{};
2795 std::array<Id, Maxwell::NumRenderTargets> frag_colors{};
2796 Id frag_depth{}; 2809 Id frag_depth{};
2797 Id frag_coord{}; 2810 Id frag_coord{};
2798 Id front_facing{}; 2811 Id front_facing{};
@@ -3048,13 +3061,17 @@ ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir) {
3048 } 3061 }
3049 for (const auto& sampler : ir.GetSamplers()) { 3062 for (const auto& sampler : ir.GetSamplers()) {
3050 if (sampler.is_buffer) { 3063 if (sampler.is_buffer) {
3051 entries.texel_buffers.emplace_back(sampler); 3064 entries.uniform_texels.emplace_back(sampler);
3052 } else { 3065 } else {
3053 entries.samplers.emplace_back(sampler); 3066 entries.samplers.emplace_back(sampler);
3054 } 3067 }
3055 } 3068 }
3056 for (const auto& image : ir.GetImages()) { 3069 for (const auto& image : ir.GetImages()) {
3057 entries.images.emplace_back(image); 3070 if (image.type == Tegra::Shader::ImageType::TextureBuffer) {
3071 entries.storage_texels.emplace_back(image);
3072 } else {
3073 entries.images.emplace_back(image);
3074 }
3058 } 3075 }
3059 for (const auto& attribute : ir.GetInputAttributes()) { 3076 for (const auto& attribute : ir.GetInputAttributes()) {
3060 if (IsGenericAttribute(attribute)) { 3077 if (IsGenericAttribute(attribute)) {
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h
index f4c05ac3c..2b0e90396 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.h
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h
@@ -21,8 +21,9 @@ class VKDevice;
21namespace Vulkan { 21namespace Vulkan {
22 22
23using Maxwell = Tegra::Engines::Maxwell3D::Regs; 23using Maxwell = Tegra::Engines::Maxwell3D::Regs;
24using TexelBufferEntry = VideoCommon::Shader::Sampler; 24using UniformTexelEntry = VideoCommon::Shader::Sampler;
25using SamplerEntry = VideoCommon::Shader::Sampler; 25using SamplerEntry = VideoCommon::Shader::Sampler;
26using StorageTexelEntry = VideoCommon::Shader::Image;
26using ImageEntry = VideoCommon::Shader::Image; 27using ImageEntry = VideoCommon::Shader::Image;
27 28
28constexpr u32 DESCRIPTOR_SET = 0; 29constexpr u32 DESCRIPTOR_SET = 0;
@@ -66,13 +67,15 @@ private:
66struct ShaderEntries { 67struct ShaderEntries {
67 u32 NumBindings() const { 68 u32 NumBindings() const {
68 return static_cast<u32>(const_buffers.size() + global_buffers.size() + 69 return static_cast<u32>(const_buffers.size() + global_buffers.size() +
69 texel_buffers.size() + samplers.size() + images.size()); 70 uniform_texels.size() + samplers.size() + storage_texels.size() +
71 images.size());
70 } 72 }
71 73
72 std::vector<ConstBufferEntry> const_buffers; 74 std::vector<ConstBufferEntry> const_buffers;
73 std::vector<GlobalBufferEntry> global_buffers; 75 std::vector<GlobalBufferEntry> global_buffers;
74 std::vector<TexelBufferEntry> texel_buffers; 76 std::vector<UniformTexelEntry> uniform_texels;
75 std::vector<SamplerEntry> samplers; 77 std::vector<SamplerEntry> samplers;
78 std::vector<StorageTexelEntry> storage_texels;
76 std::vector<ImageEntry> images; 79 std::vector<ImageEntry> images;
77 std::set<u32> attributes; 80 std::set<u32> attributes;
78 std::array<bool, Maxwell::NumClipDistances> clip_distances{}; 81 std::array<bool, Maxwell::NumClipDistances> clip_distances{};
@@ -88,7 +91,8 @@ struct Specialization final {
88 u32 shared_memory_size{}; 91 u32 shared_memory_size{};
89 92
90 // Graphics specific 93 // Graphics specific
91 std::optional<float> point_size{}; 94 std::optional<float> point_size;
95 std::bitset<Maxwell::NumVertexAttributes> enabled_attributes;
92 std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{}; 96 std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{};
93 bool ndc_minus_one_to_one{}; 97 bool ndc_minus_one_to_one{};
94}; 98};
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 94a89e388..e5a583dd5 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -36,6 +36,15 @@ Flags MakeInvalidationFlags() {
36 flags[BlendConstants] = true; 36 flags[BlendConstants] = true;
37 flags[DepthBounds] = true; 37 flags[DepthBounds] = true;
38 flags[StencilProperties] = true; 38 flags[StencilProperties] = true;
39 flags[CullMode] = true;
40 flags[DepthBoundsEnable] = true;
41 flags[DepthTestEnable] = true;
42 flags[DepthWriteEnable] = true;
43 flags[DepthCompareOp] = true;
44 flags[FrontFace] = true;
45 flags[PrimitiveTopology] = true;
46 flags[StencilOp] = true;
47 flags[StencilTestEnable] = true;
39 return flags; 48 return flags;
40} 49}
41 50
@@ -75,6 +84,57 @@ void SetupDirtyStencilProperties(Tables& tables) {
75 table[OFF(stencil_back_func_mask)] = StencilProperties; 84 table[OFF(stencil_back_func_mask)] = StencilProperties;
76} 85}
77 86
87void SetupDirtyCullMode(Tables& tables) {
88 auto& table = tables[0];
89 table[OFF(cull_face)] = CullMode;
90 table[OFF(cull_test_enabled)] = CullMode;
91}
92
93void SetupDirtyDepthBoundsEnable(Tables& tables) {
94 tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable;
95}
96
97void SetupDirtyDepthTestEnable(Tables& tables) {
98 tables[0][OFF(depth_test_enable)] = DepthTestEnable;
99}
100
101void SetupDirtyDepthWriteEnable(Tables& tables) {
102 tables[0][OFF(depth_write_enabled)] = DepthWriteEnable;
103}
104
105void SetupDirtyDepthCompareOp(Tables& tables) {
106 tables[0][OFF(depth_test_func)] = DepthCompareOp;
107}
108
109void SetupDirtyFrontFace(Tables& tables) {
110 auto& table = tables[0];
111 table[OFF(front_face)] = FrontFace;
112 table[OFF(screen_y_control)] = FrontFace;
113}
114
115void SetupDirtyPrimitiveTopology(Tables& tables) {
116 tables[0][OFF(draw.topology)] = PrimitiveTopology;
117}
118
119void SetupDirtyStencilOp(Tables& tables) {
120 auto& table = tables[0];
121 table[OFF(stencil_front_op_fail)] = StencilOp;
122 table[OFF(stencil_front_op_zfail)] = StencilOp;
123 table[OFF(stencil_front_op_zpass)] = StencilOp;
124 table[OFF(stencil_front_func_func)] = StencilOp;
125 table[OFF(stencil_back_op_fail)] = StencilOp;
126 table[OFF(stencil_back_op_zfail)] = StencilOp;
127 table[OFF(stencil_back_op_zpass)] = StencilOp;
128 table[OFF(stencil_back_func_func)] = StencilOp;
129
130 // Table 0 is used by StencilProperties
131 tables[1][OFF(stencil_two_side_enable)] = StencilOp;
132}
133
134void SetupDirtyStencilTestEnable(Tables& tables) {
135 tables[0][OFF(stencil_enable)] = StencilTestEnable;
136}
137
78} // Anonymous namespace 138} // Anonymous namespace
79 139
80StateTracker::StateTracker(Core::System& system) 140StateTracker::StateTracker(Core::System& system)
@@ -90,6 +150,14 @@ void StateTracker::Initialize() {
90 SetupDirtyBlendConstants(tables); 150 SetupDirtyBlendConstants(tables);
91 SetupDirtyDepthBounds(tables); 151 SetupDirtyDepthBounds(tables);
92 SetupDirtyStencilProperties(tables); 152 SetupDirtyStencilProperties(tables);
153 SetupDirtyCullMode(tables);
154 SetupDirtyDepthBoundsEnable(tables);
155 SetupDirtyDepthTestEnable(tables);
156 SetupDirtyDepthWriteEnable(tables);
157 SetupDirtyDepthCompareOp(tables);
158 SetupDirtyFrontFace(tables);
159 SetupDirtyPrimitiveTopology(tables);
160 SetupDirtyStencilOp(tables);
93} 161}
94 162
95void StateTracker::InvalidateCommandBufferState() { 163void StateTracker::InvalidateCommandBufferState() {
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 03bc415b2..54ca0d6c6 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -26,6 +26,16 @@ enum : u8 {
26 DepthBounds, 26 DepthBounds,
27 StencilProperties, 27 StencilProperties,
28 28
29 CullMode,
30 DepthBoundsEnable,
31 DepthTestEnable,
32 DepthWriteEnable,
33 DepthCompareOp,
34 FrontFace,
35 PrimitiveTopology,
36 StencilOp,
37 StencilTestEnable,
38
29 Last 39 Last
30}; 40};
31static_assert(Last <= std::numeric_limits<u8>::max()); 41static_assert(Last <= std::numeric_limits<u8>::max());
@@ -64,6 +74,46 @@ public:
64 return Exchange(Dirty::StencilProperties, false); 74 return Exchange(Dirty::StencilProperties, false);
65 } 75 }
66 76
77 bool TouchCullMode() {
78 return Exchange(Dirty::CullMode, false);
79 }
80
81 bool TouchDepthBoundsTestEnable() {
82 return Exchange(Dirty::DepthBoundsEnable, false);
83 }
84
85 bool TouchDepthTestEnable() {
86 return Exchange(Dirty::DepthTestEnable, false);
87 }
88
89 bool TouchDepthBoundsEnable() {
90 return Exchange(Dirty::DepthBoundsEnable, false);
91 }
92
93 bool TouchDepthWriteEnable() {
94 return Exchange(Dirty::DepthWriteEnable, false);
95 }
96
97 bool TouchDepthCompareOp() {
98 return Exchange(Dirty::DepthCompareOp, false);
99 }
100
101 bool TouchFrontFace() {
102 return Exchange(Dirty::FrontFace, false);
103 }
104
105 bool TouchPrimitiveTopology() {
106 return Exchange(Dirty::PrimitiveTopology, false);
107 }
108
109 bool TouchStencilOp() {
110 return Exchange(Dirty::StencilOp, false);
111 }
112
113 bool TouchStencilTestEnable() {
114 return Exchange(Dirty::StencilTestEnable, false);
115 }
116
67private: 117private:
68 bool Exchange(std::size_t id, bool new_value) const noexcept { 118 bool Exchange(std::size_t id, bool new_value) const noexcept {
69 auto& flags = system.GPU().Maxwell3D().dirty.flags; 119 auto& flags = system.GPU().Maxwell3D().dirty.flags;
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
index 868447af2..2d28a6c47 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
@@ -121,7 +121,7 @@ void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) {
121 121
122 // Substract from the preferred heap size some bytes to avoid getting out of memory. 122 // Substract from the preferred heap size some bytes to avoid getting out of memory.
123 const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size; 123 const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size;
124 const VkDeviceSize allocable_size = heap_size - 4 * 1024 * 1024; 124 const VkDeviceSize allocable_size = heap_size - 9 * 1024 * 1024;
125 125
126 VkBufferCreateInfo buffer_ci; 126 VkBufferCreateInfo buffer_ci;
127 buffer_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; 127 buffer_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h
index dfddf7ad6..689f0d276 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.h
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.h
@@ -35,10 +35,14 @@ public:
35 /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy. 35 /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy.
36 void Unmap(u64 size); 36 void Unmap(u64 size);
37 37
38 VkBuffer GetHandle() const { 38 VkBuffer Handle() const noexcept {
39 return *buffer; 39 return *buffer;
40 } 40 }
41 41
42 u64 Address() const noexcept {
43 return 0;
44 }
45
42private: 46private:
43 struct Watch final { 47 struct Watch final {
44 VKFenceWatch fence; 48 VKFenceWatch fence;
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 2f1d5021d..430031665 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -100,8 +100,8 @@ vk::Buffer CreateBuffer(const VKDevice& device, const SurfaceParams& params,
100 ci.pNext = nullptr; 100 ci.pNext = nullptr;
101 ci.flags = 0; 101 ci.flags = 0;
102 ci.size = static_cast<VkDeviceSize>(host_memory_size); 102 ci.size = static_cast<VkDeviceSize>(host_memory_size);
103 ci.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | 103 ci.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
104 VK_BUFFER_USAGE_TRANSFER_DST_BIT; 104 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
105 ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 105 ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
106 ci.queueFamilyIndexCount = 0; 106 ci.queueFamilyIndexCount = 0;
107 ci.pQueueFamilyIndices = nullptr; 107 ci.pQueueFamilyIndices = nullptr;
@@ -167,6 +167,7 @@ VkImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceP
167 ci.extent = {params.width, params.height, 1}; 167 ci.extent = {params.width, params.height, 1};
168 break; 168 break;
169 case SurfaceTarget::Texture3D: 169 case SurfaceTarget::Texture3D:
170 ci.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
170 ci.extent = {params.width, params.height, params.depth}; 171 ci.extent = {params.width, params.height, params.depth};
171 break; 172 break;
172 case SurfaceTarget::TextureBuffer: 173 case SurfaceTarget::TextureBuffer:
@@ -176,6 +177,12 @@ VkImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceP
176 return ci; 177 return ci;
177} 178}
178 179
180u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source, Tegra::Texture::SwizzleSource y_source,
181 Tegra::Texture::SwizzleSource z_source, Tegra::Texture::SwizzleSource w_source) {
182 return (static_cast<u32>(x_source) << 24) | (static_cast<u32>(y_source) << 16) |
183 (static_cast<u32>(z_source) << 8) | static_cast<u32>(w_source);
184}
185
179} // Anonymous namespace 186} // Anonymous namespace
180 187
181CachedSurface::CachedSurface(Core::System& system, const VKDevice& device, 188CachedSurface::CachedSurface(Core::System& system, const VKDevice& device,
@@ -203,9 +210,11 @@ CachedSurface::CachedSurface(Core::System& system, const VKDevice& device,
203 } 210 }
204 211
205 // TODO(Rodrigo): Move this to a virtual function. 212 // TODO(Rodrigo): Move this to a virtual function.
206 main_view = CreateViewInner( 213 u32 num_layers = 1;
207 ViewParams(params.target, 0, static_cast<u32>(params.GetNumLayers()), 0, params.num_levels), 214 if (params.is_layered || params.target == SurfaceTarget::Texture3D) {
208 true); 215 num_layers = params.depth;
216 }
217 main_view = CreateView(ViewParams(params.target, 0, num_layers, 0, params.num_levels));
209} 218}
210 219
211CachedSurface::~CachedSurface() = default; 220CachedSurface::~CachedSurface() = default;
@@ -253,12 +262,8 @@ void CachedSurface::DecorateSurfaceName() {
253} 262}
254 263
255View CachedSurface::CreateView(const ViewParams& params) { 264View CachedSurface::CreateView(const ViewParams& params) {
256 return CreateViewInner(params, false);
257}
258
259View CachedSurface::CreateViewInner(const ViewParams& params, bool is_proxy) {
260 // TODO(Rodrigo): Add name decorations 265 // TODO(Rodrigo): Add name decorations
261 return views[params] = std::make_shared<CachedSurfaceView>(device, *this, params, is_proxy); 266 return views[params] = std::make_shared<CachedSurfaceView>(device, *this, params);
262} 267}
263 268
264void CachedSurface::UploadBuffer(const std::vector<u8>& staging_buffer) { 269void CachedSurface::UploadBuffer(const std::vector<u8>& staging_buffer) {
@@ -342,18 +347,27 @@ VkImageSubresourceRange CachedSurface::GetImageSubresourceRange() const {
342} 347}
343 348
344CachedSurfaceView::CachedSurfaceView(const VKDevice& device, CachedSurface& surface, 349CachedSurfaceView::CachedSurfaceView(const VKDevice& device, CachedSurface& surface,
345 const ViewParams& params, bool is_proxy) 350 const ViewParams& params)
346 : VideoCommon::ViewBase{params}, params{surface.GetSurfaceParams()}, 351 : VideoCommon::ViewBase{params}, params{surface.GetSurfaceParams()},
347 image{surface.GetImageHandle()}, buffer_view{surface.GetBufferViewHandle()}, 352 image{surface.GetImageHandle()}, buffer_view{surface.GetBufferViewHandle()},
348 aspect_mask{surface.GetAspectMask()}, device{device}, surface{surface}, 353 aspect_mask{surface.GetAspectMask()}, device{device}, surface{surface},
349 base_layer{params.base_layer}, num_layers{params.num_layers}, base_level{params.base_level}, 354 base_level{params.base_level}, num_levels{params.num_levels},
350 num_levels{params.num_levels}, image_view_type{image ? GetImageViewType(params.target) 355 image_view_type{image ? GetImageViewType(params.target) : VK_IMAGE_VIEW_TYPE_1D} {
351 : VK_IMAGE_VIEW_TYPE_1D} {} 356 if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) {
357 base_layer = 0;
358 num_layers = 1;
359 base_slice = params.base_layer;
360 num_slices = params.num_layers;
361 } else {
362 base_layer = params.base_layer;
363 num_layers = params.num_layers;
364 }
365}
352 366
353CachedSurfaceView::~CachedSurfaceView() = default; 367CachedSurfaceView::~CachedSurfaceView() = default;
354 368
355VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y_source, 369VkImageView CachedSurfaceView::GetImageView(SwizzleSource x_source, SwizzleSource y_source,
356 SwizzleSource z_source, SwizzleSource w_source) { 370 SwizzleSource z_source, SwizzleSource w_source) {
357 const u32 new_swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source); 371 const u32 new_swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source);
358 if (last_image_view && last_swizzle == new_swizzle) { 372 if (last_image_view && last_swizzle == new_swizzle) {
359 return last_image_view; 373 return last_image_view;
@@ -399,6 +413,11 @@ VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y
399 }); 413 });
400 } 414 }
401 415
416 if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) {
417 ASSERT(base_slice == 0);
418 ASSERT(num_slices == params.depth);
419 }
420
402 VkImageViewCreateInfo ci; 421 VkImageViewCreateInfo ci;
403 ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; 422 ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
404 ci.pNext = nullptr; 423 ci.pNext = nullptr;
@@ -417,6 +436,35 @@ VkImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y
417 return last_image_view = *image_view; 436 return last_image_view = *image_view;
418} 437}
419 438
439VkImageView CachedSurfaceView::GetAttachment() {
440 if (render_target) {
441 return *render_target;
442 }
443
444 VkImageViewCreateInfo ci;
445 ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
446 ci.pNext = nullptr;
447 ci.flags = 0;
448 ci.image = surface.GetImageHandle();
449 ci.format = surface.GetImage().GetFormat();
450 ci.components = {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
451 VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
452 ci.subresourceRange.aspectMask = aspect_mask;
453 ci.subresourceRange.baseMipLevel = base_level;
454 ci.subresourceRange.levelCount = num_levels;
455 if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) {
456 ci.viewType = num_slices > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
457 ci.subresourceRange.baseArrayLayer = base_slice;
458 ci.subresourceRange.layerCount = num_slices;
459 } else {
460 ci.viewType = image_view_type;
461 ci.subresourceRange.baseArrayLayer = base_layer;
462 ci.subresourceRange.layerCount = num_layers;
463 }
464 render_target = device.GetLogical().CreateImageView(ci);
465 return *render_target;
466}
467
420VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, 468VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
421 const VKDevice& device, VKResourceManager& resource_manager, 469 const VKDevice& device, VKResourceManager& resource_manager,
422 VKMemoryManager& memory_manager, VKScheduler& scheduler, 470 VKMemoryManager& memory_manager, VKScheduler& scheduler,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index f211ccb1e..807e26c8a 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -91,7 +91,6 @@ protected:
91 void DecorateSurfaceName(); 91 void DecorateSurfaceName();
92 92
93 View CreateView(const ViewParams& params) override; 93 View CreateView(const ViewParams& params) override;
94 View CreateViewInner(const ViewParams& params, bool is_proxy);
95 94
96private: 95private:
97 void UploadBuffer(const std::vector<u8>& staging_buffer); 96 void UploadBuffer(const std::vector<u8>& staging_buffer);
@@ -120,23 +119,20 @@ private:
120class CachedSurfaceView final : public VideoCommon::ViewBase { 119class CachedSurfaceView final : public VideoCommon::ViewBase {
121public: 120public:
122 explicit CachedSurfaceView(const VKDevice& device, CachedSurface& surface, 121 explicit CachedSurfaceView(const VKDevice& device, CachedSurface& surface,
123 const ViewParams& params, bool is_proxy); 122 const ViewParams& params);
124 ~CachedSurfaceView(); 123 ~CachedSurfaceView();
125 124
126 VkImageView GetHandle(Tegra::Texture::SwizzleSource x_source, 125 VkImageView GetImageView(Tegra::Texture::SwizzleSource x_source,
127 Tegra::Texture::SwizzleSource y_source, 126 Tegra::Texture::SwizzleSource y_source,
128 Tegra::Texture::SwizzleSource z_source, 127 Tegra::Texture::SwizzleSource z_source,
129 Tegra::Texture::SwizzleSource w_source); 128 Tegra::Texture::SwizzleSource w_source);
129
130 VkImageView GetAttachment();
130 131
131 bool IsSameSurface(const CachedSurfaceView& rhs) const { 132 bool IsSameSurface(const CachedSurfaceView& rhs) const {
132 return &surface == &rhs.surface; 133 return &surface == &rhs.surface;
133 } 134 }
134 135
135 VkImageView GetHandle() {
136 return GetHandle(Tegra::Texture::SwizzleSource::R, Tegra::Texture::SwizzleSource::G,
137 Tegra::Texture::SwizzleSource::B, Tegra::Texture::SwizzleSource::A);
138 }
139
140 u32 GetWidth() const { 136 u32 GetWidth() const {
141 return params.GetMipWidth(base_level); 137 return params.GetMipWidth(base_level);
142 } 138 }
@@ -180,14 +176,6 @@ public:
180 } 176 }
181 177
182private: 178private:
183 static u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source,
184 Tegra::Texture::SwizzleSource y_source,
185 Tegra::Texture::SwizzleSource z_source,
186 Tegra::Texture::SwizzleSource w_source) {
187 return (static_cast<u32>(x_source) << 24) | (static_cast<u32>(y_source) << 16) |
188 (static_cast<u32>(z_source) << 8) | static_cast<u32>(w_source);
189 }
190
191 // Store a copy of these values to avoid double dereference when reading them 179 // Store a copy of these values to avoid double dereference when reading them
192 const SurfaceParams params; 180 const SurfaceParams params;
193 const VkImage image; 181 const VkImage image;
@@ -196,15 +184,18 @@ private:
196 184
197 const VKDevice& device; 185 const VKDevice& device;
198 CachedSurface& surface; 186 CachedSurface& surface;
199 const u32 base_layer;
200 const u32 num_layers;
201 const u32 base_level; 187 const u32 base_level;
202 const u32 num_levels; 188 const u32 num_levels;
203 const VkImageViewType image_view_type; 189 const VkImageViewType image_view_type;
190 u32 base_layer = 0;
191 u32 num_layers = 0;
192 u32 base_slice = 0;
193 u32 num_slices = 0;
204 194
205 VkImageView last_image_view = nullptr; 195 VkImageView last_image_view = nullptr;
206 u32 last_swizzle = 0; 196 u32 last_swizzle = 0;
207 197
198 vk::ImageView render_target;
208 std::unordered_map<u32, vk::ImageView> view_cache; 199 std::unordered_map<u32, vk::ImageView> view_cache;
209}; 200};
210 201
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
index 681ecde98..351c048d2 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp
@@ -24,35 +24,25 @@ void VKUpdateDescriptorQueue::TickFrame() {
24} 24}
25 25
26void VKUpdateDescriptorQueue::Acquire() { 26void VKUpdateDescriptorQueue::Acquire() {
27 entries.clear(); 27 // Minimum number of entries required.
28} 28 // This is the maximum number of entries a single draw call migth use.
29 static constexpr std::size_t MIN_ENTRIES = 0x400;
29 30
30void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template, 31 if (payload.size() + MIN_ENTRIES >= payload.max_size()) {
31 VkDescriptorSet set) {
32 if (payload.size() + entries.size() >= payload.max_size()) {
33 LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread"); 32 LOG_WARNING(Render_Vulkan, "Payload overflow, waiting for worker thread");
34 scheduler.WaitWorker(); 33 scheduler.WaitWorker();
35 payload.clear(); 34 payload.clear();
36 } 35 }
36 upload_start = &*payload.end();
37}
37 38
38 // TODO(Rodrigo): Rework to write the payload directly 39void VKUpdateDescriptorQueue::Send(VkDescriptorUpdateTemplateKHR update_template,
39 const auto payload_start = payload.data() + payload.size(); 40 VkDescriptorSet set) {
40 for (const auto& entry : entries) { 41 const void* const data = upload_start;
41 if (const auto image = std::get_if<VkDescriptorImageInfo>(&entry)) { 42 const vk::Device* const logical = &device.GetLogical();
42 payload.push_back(*image); 43 scheduler.Record([data, logical, set, update_template](vk::CommandBuffer) {
43 } else if (const auto buffer = std::get_if<VkDescriptorBufferInfo>(&entry)) { 44 logical->UpdateDescriptorSet(set, update_template, data);
44 payload.push_back(*buffer); 45 });
45 } else if (const auto texel = std::get_if<VkBufferView>(&entry)) {
46 payload.push_back(*texel);
47 } else {
48 UNREACHABLE();
49 }
50 }
51
52 scheduler.Record(
53 [payload_start, set, update_template, logical = &device.GetLogical()](vk::CommandBuffer) {
54 logical->UpdateDescriptorSet(set, update_template, payload_start);
55 });
56} 46}
57 47
58} // namespace Vulkan 48} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h
index cc7e3dff4..945320c72 100644
--- a/src/video_core/renderer_vulkan/vk_update_descriptor.h
+++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h
@@ -15,17 +15,13 @@ namespace Vulkan {
15class VKDevice; 15class VKDevice;
16class VKScheduler; 16class VKScheduler;
17 17
18class DescriptorUpdateEntry { 18struct DescriptorUpdateEntry {
19public: 19 DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {}
20 explicit DescriptorUpdateEntry() {}
21
22 DescriptorUpdateEntry(VkDescriptorImageInfo image) : image{image} {}
23 20
24 DescriptorUpdateEntry(VkDescriptorBufferInfo buffer) : buffer{buffer} {} 21 DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {}
25 22
26 DescriptorUpdateEntry(VkBufferView texel_buffer) : texel_buffer{texel_buffer} {} 23 DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {}
27 24
28private:
29 union { 25 union {
30 VkDescriptorImageInfo image; 26 VkDescriptorImageInfo image;
31 VkDescriptorBufferInfo buffer; 27 VkDescriptorBufferInfo buffer;
@@ -45,32 +41,34 @@ public:
45 void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set); 41 void Send(VkDescriptorUpdateTemplateKHR update_template, VkDescriptorSet set);
46 42
47 void AddSampledImage(VkSampler sampler, VkImageView image_view) { 43 void AddSampledImage(VkSampler sampler, VkImageView image_view) {
48 entries.emplace_back(VkDescriptorImageInfo{sampler, image_view, {}}); 44 payload.emplace_back(VkDescriptorImageInfo{sampler, image_view, {}});
49 } 45 }
50 46
51 void AddImage(VkImageView image_view) { 47 void AddImage(VkImageView image_view) {
52 entries.emplace_back(VkDescriptorImageInfo{{}, image_view, {}}); 48 payload.emplace_back(VkDescriptorImageInfo{{}, image_view, {}});
53 } 49 }
54 50
55 void AddBuffer(VkBuffer buffer, u64 offset, std::size_t size) { 51 void AddBuffer(VkBuffer buffer, u64 offset, std::size_t size) {
56 entries.emplace_back(VkDescriptorBufferInfo{buffer, offset, size}); 52 payload.emplace_back(VkDescriptorBufferInfo{buffer, offset, size});
57 } 53 }
58 54
59 void AddTexelBuffer(VkBufferView texel_buffer) { 55 void AddTexelBuffer(VkBufferView texel_buffer) {
60 entries.emplace_back(texel_buffer); 56 payload.emplace_back(texel_buffer);
61 } 57 }
62 58
63 VkImageLayout* GetLastImageLayout() { 59 VkImageLayout* LastImageLayout() {
64 return &std::get<VkDescriptorImageInfo>(entries.back()).imageLayout; 60 return &payload.back().image.imageLayout;
65 } 61 }
66 62
67private: 63 const VkImageLayout* LastImageLayout() const {
68 using Variant = std::variant<VkDescriptorImageInfo, VkDescriptorBufferInfo, VkBufferView>; 64 return &payload.back().image.imageLayout;
65 }
69 66
67private:
70 const VKDevice& device; 68 const VKDevice& device;
71 VKScheduler& scheduler; 69 VKScheduler& scheduler;
72 70
73 boost::container::static_vector<Variant, 0x400> entries; 71 const DescriptorUpdateEntry* upload_start = nullptr;
74 boost::container::static_vector<DescriptorUpdateEntry, 0x10000> payload; 72 boost::container::static_vector<DescriptorUpdateEntry, 0x10000> payload;
75}; 73};
76 74
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index 2ce9b0626..051298cc8 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -88,6 +88,16 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
88 X(vkCmdSetStencilWriteMask); 88 X(vkCmdSetStencilWriteMask);
89 X(vkCmdSetViewport); 89 X(vkCmdSetViewport);
90 X(vkCmdWaitEvents); 90 X(vkCmdWaitEvents);
91 X(vkCmdBindVertexBuffers2EXT);
92 X(vkCmdSetCullModeEXT);
93 X(vkCmdSetDepthBoundsTestEnableEXT);
94 X(vkCmdSetDepthCompareOpEXT);
95 X(vkCmdSetDepthTestEnableEXT);
96 X(vkCmdSetDepthWriteEnableEXT);
97 X(vkCmdSetFrontFaceEXT);
98 X(vkCmdSetPrimitiveTopologyEXT);
99 X(vkCmdSetStencilOpEXT);
100 X(vkCmdSetStencilTestEnableEXT);
91 X(vkCreateBuffer); 101 X(vkCreateBuffer);
92 X(vkCreateBufferView); 102 X(vkCreateBufferView);
93 X(vkCreateCommandPool); 103 X(vkCreateCommandPool);
@@ -153,7 +163,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
153 163
154bool Load(InstanceDispatch& dld) noexcept { 164bool Load(InstanceDispatch& dld) noexcept {
155#define X(name) Proc(dld.name, dld, #name) 165#define X(name) Proc(dld.name, dld, #name)
156 return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties); 166 return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties) &&
167 X(vkEnumerateInstanceLayerProperties);
157#undef X 168#undef X
158} 169}
159 170
@@ -725,8 +736,7 @@ bool PhysicalDevice::GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR s
725 return supported == VK_TRUE; 736 return supported == VK_TRUE;
726} 737}
727 738
728VkSurfaceCapabilitiesKHR PhysicalDevice::GetSurfaceCapabilitiesKHR(VkSurfaceKHR surface) const 739VkSurfaceCapabilitiesKHR PhysicalDevice::GetSurfaceCapabilitiesKHR(VkSurfaceKHR surface) const {
729 noexcept {
730 VkSurfaceCapabilitiesKHR capabilities; 740 VkSurfaceCapabilitiesKHR capabilities;
731 Check(dld->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &capabilities)); 741 Check(dld->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &capabilities));
732 return capabilities; 742 return capabilities;
@@ -771,4 +781,17 @@ std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProp
771 return properties; 781 return properties;
772} 782}
773 783
784std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
785 const InstanceDispatch& dld) {
786 u32 num;
787 if (dld.vkEnumerateInstanceLayerProperties(&num, nullptr) != VK_SUCCESS) {
788 return std::nullopt;
789 }
790 std::vector<VkLayerProperties> properties(num);
791 if (dld.vkEnumerateInstanceLayerProperties(&num, properties.data()) != VK_SUCCESS) {
792 return std::nullopt;
793 }
794 return properties;
795}
796
774} // namespace Vulkan::vk 797} // namespace Vulkan::vk
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h
index 98937a77a..71daac9d7 100644
--- a/src/video_core/renderer_vulkan/wrapper.h
+++ b/src/video_core/renderer_vulkan/wrapper.h
@@ -141,6 +141,7 @@ struct InstanceDispatch {
141 PFN_vkCreateInstance vkCreateInstance; 141 PFN_vkCreateInstance vkCreateInstance;
142 PFN_vkDestroyInstance vkDestroyInstance; 142 PFN_vkDestroyInstance vkDestroyInstance;
143 PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; 143 PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
144 PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
144 145
145 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; 146 PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT;
146 PFN_vkCreateDevice vkCreateDevice; 147 PFN_vkCreateDevice vkCreateDevice;
@@ -206,6 +207,16 @@ struct DeviceDispatch : public InstanceDispatch {
206 PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; 207 PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
207 PFN_vkCmdSetViewport vkCmdSetViewport; 208 PFN_vkCmdSetViewport vkCmdSetViewport;
208 PFN_vkCmdWaitEvents vkCmdWaitEvents; 209 PFN_vkCmdWaitEvents vkCmdWaitEvents;
210 PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT;
211 PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT;
212 PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT;
213 PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT;
214 PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT;
215 PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT;
216 PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT;
217 PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT;
218 PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT;
219 PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT;
209 PFN_vkCreateBuffer vkCreateBuffer; 220 PFN_vkCreateBuffer vkCreateBuffer;
210 PFN_vkCreateBufferView vkCreateBufferView; 221 PFN_vkCreateBufferView vkCreateBufferView;
211 PFN_vkCreateCommandPool vkCreateCommandPool; 222 PFN_vkCreateCommandPool vkCreateCommandPool;
@@ -779,7 +790,7 @@ public:
779 790
780 bool GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR) const; 791 bool GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR) const;
781 792
782 VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const noexcept; 793 VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const;
783 794
784 std::vector<VkSurfaceFormatKHR> GetSurfaceFormatsKHR(VkSurfaceKHR) const; 795 std::vector<VkSurfaceFormatKHR> GetSurfaceFormatsKHR(VkSurfaceKHR) const;
785 796
@@ -968,6 +979,50 @@ public:
968 buffer_barriers.data(), image_barriers.size(), image_barriers.data()); 979 buffer_barriers.data(), image_barriers.size(), image_barriers.data());
969 } 980 }
970 981
982 void BindVertexBuffers2EXT(u32 first_binding, u32 binding_count, const VkBuffer* buffers,
983 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
984 const VkDeviceSize* strides) const noexcept {
985 dld->vkCmdBindVertexBuffers2EXT(handle, first_binding, binding_count, buffers, offsets,
986 sizes, strides);
987 }
988
989 void SetCullModeEXT(VkCullModeFlags cull_mode) const noexcept {
990 dld->vkCmdSetCullModeEXT(handle, cull_mode);
991 }
992
993 void SetDepthBoundsTestEnableEXT(bool enable) const noexcept {
994 dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
995 }
996
997 void SetDepthCompareOpEXT(VkCompareOp compare_op) const noexcept {
998 dld->vkCmdSetDepthCompareOpEXT(handle, compare_op);
999 }
1000
1001 void SetDepthTestEnableEXT(bool enable) const noexcept {
1002 dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1003 }
1004
1005 void SetDepthWriteEnableEXT(bool enable) const noexcept {
1006 dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1007 }
1008
1009 void SetFrontFaceEXT(VkFrontFace front_face) const noexcept {
1010 dld->vkCmdSetFrontFaceEXT(handle, front_face);
1011 }
1012
1013 void SetPrimitiveTopologyEXT(VkPrimitiveTopology primitive_topology) const noexcept {
1014 dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology);
1015 }
1016
1017 void SetStencilOpEXT(VkStencilFaceFlags face_mask, VkStencilOp fail_op, VkStencilOp pass_op,
1018 VkStencilOp depth_fail_op, VkCompareOp compare_op) const noexcept {
1019 dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op);
1020 }
1021
1022 void SetStencilTestEnableEXT(bool enable) const noexcept {
1023 dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1024 }
1025
971 void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, 1026 void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
972 const VkDeviceSize* offsets, 1027 const VkDeviceSize* offsets,
973 const VkDeviceSize* sizes) const noexcept { 1028 const VkDeviceSize* sizes) const noexcept {
@@ -996,4 +1051,7 @@ private:
996std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties( 1051std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties(
997 const InstanceDispatch& dld); 1052 const InstanceDispatch& dld);
998 1053
1054std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
1055 const InstanceDispatch& dld);
1056
999} // namespace Vulkan::vk 1057} // namespace Vulkan::vk
diff --git a/src/video_core/shader/decode/half_set.cpp b/src/video_core/shader/decode/half_set.cpp
index 848e46874..b2e88fa20 100644
--- a/src/video_core/shader/decode/half_set.cpp
+++ b/src/video_core/shader/decode/half_set.cpp
@@ -13,55 +13,101 @@
13 13
14namespace VideoCommon::Shader { 14namespace VideoCommon::Shader {
15 15
16using std::move;
16using Tegra::Shader::Instruction; 17using Tegra::Shader::Instruction;
17using Tegra::Shader::OpCode; 18using Tegra::Shader::OpCode;
19using Tegra::Shader::PredCondition;
18 20
19u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) { 21u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) {
20 const Instruction instr = {program_code[pc]}; 22 const Instruction instr = {program_code[pc]};
21 const auto opcode = OpCode::Decode(instr); 23 const auto opcode = OpCode::Decode(instr);
22 24
23 if (instr.hset2.ftz == 0) { 25 PredCondition cond;
24 LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); 26 bool bf;
27 bool ftz;
28 bool neg_a;
29 bool abs_a;
30 bool neg_b;
31 bool abs_b;
32 switch (opcode->get().GetId()) {
33 case OpCode::Id::HSET2_C:
34 case OpCode::Id::HSET2_IMM:
35 cond = instr.hsetp2.cbuf_and_imm.cond;
36 bf = instr.Bit(53);
37 ftz = instr.Bit(54);
38 neg_a = instr.Bit(43);
39 abs_a = instr.Bit(44);
40 neg_b = instr.Bit(56);
41 abs_b = instr.Bit(54);
42 break;
43 case OpCode::Id::HSET2_R:
44 cond = instr.hsetp2.reg.cond;
45 bf = instr.Bit(49);
46 ftz = instr.Bit(50);
47 neg_a = instr.Bit(43);
48 abs_a = instr.Bit(44);
49 neg_b = instr.Bit(31);
50 abs_b = instr.Bit(30);
51 break;
52 default:
53 UNREACHABLE();
25 } 54 }
26 55
27 Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hset2.type_a); 56 Node op_b = [this, instr, opcode] {
28 op_a = GetOperandAbsNegHalf(op_a, instr.hset2.abs_a, instr.hset2.negate_a);
29
30 Node op_b = [&]() {
31 switch (opcode->get().GetId()) { 57 switch (opcode->get().GetId()) {
58 case OpCode::Id::HSET2_C:
59 // Inform as unimplemented as this is not tested.
60 UNIMPLEMENTED_MSG("HSET2_C is not implemented");
61 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
32 case OpCode::Id::HSET2_R: 62 case OpCode::Id::HSET2_R:
33 return GetRegister(instr.gpr20); 63 return GetRegister(instr.gpr20);
64 case OpCode::Id::HSET2_IMM:
65 return UnpackHalfImmediate(instr, true);
34 default: 66 default:
35 UNREACHABLE(); 67 UNREACHABLE();
36 return Immediate(0); 68 return Node{};
37 } 69 }
38 }(); 70 }();
39 op_b = UnpackHalfFloat(op_b, instr.hset2.type_b);
40 op_b = GetOperandAbsNegHalf(op_b, instr.hset2.abs_b, instr.hset2.negate_b);
41 71
42 const Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred); 72 if (!ftz) {
73 LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName());
74 }
75
76 Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hset2.type_a);
77 op_a = GetOperandAbsNegHalf(op_a, abs_a, neg_a);
78
79 switch (opcode->get().GetId()) {
80 case OpCode::Id::HSET2_R:
81 op_b = GetOperandAbsNegHalf(move(op_b), abs_b, neg_b);
82 [[fallthrough]];
83 case OpCode::Id::HSET2_C:
84 op_b = UnpackHalfFloat(move(op_b), instr.hset2.type_b);
85 break;
86 default:
87 break;
88 }
43 89
44 const Node comparison_pair = GetPredicateComparisonHalf(instr.hset2.cond, op_a, op_b); 90 Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred);
91
92 Node comparison_pair = GetPredicateComparisonHalf(cond, op_a, op_b);
45 93
46 const OperationCode combiner = GetPredicateCombiner(instr.hset2.op); 94 const OperationCode combiner = GetPredicateCombiner(instr.hset2.op);
47 95
48 // HSET2 operates on each half float in the pack. 96 // HSET2 operates on each half float in the pack.
49 std::array<Node, 2> values; 97 std::array<Node, 2> values;
50 for (u32 i = 0; i < 2; ++i) { 98 for (u32 i = 0; i < 2; ++i) {
51 const u32 raw_value = instr.hset2.bf ? 0x3c00 : 0xffff; 99 const u32 raw_value = bf ? 0x3c00 : 0xffff;
52 const Node true_value = Immediate(raw_value << (i * 16)); 100 Node true_value = Immediate(raw_value << (i * 16));
53 const Node false_value = Immediate(0); 101 Node false_value = Immediate(0);
54
55 const Node comparison =
56 Operation(OperationCode::LogicalPick2, comparison_pair, Immediate(i));
57 const Node predicate = Operation(combiner, comparison, second_pred);
58 102
103 Node comparison = Operation(OperationCode::LogicalPick2, comparison_pair, Immediate(i));
104 Node predicate = Operation(combiner, comparison, second_pred);
59 values[i] = 105 values[i] =
60 Operation(OperationCode::Select, NO_PRECISE, predicate, true_value, false_value); 106 Operation(OperationCode::Select, predicate, move(true_value), move(false_value));
61 } 107 }
62 108
63 const Node value = Operation(OperationCode::UBitwiseOr, NO_PRECISE, values[0], values[1]); 109 Node value = Operation(OperationCode::UBitwiseOr, values[0], values[1]);
64 SetRegister(bb, instr.gpr0, value); 110 SetRegister(bb, instr.gpr0, move(value));
65 111
66 return pc; 112 return pc;
67} 113}
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp
index 60b6ad72a..07778dc3e 100644
--- a/src/video_core/shader/decode/image.cpp
+++ b/src/video_core/shader/decode/image.cpp
@@ -97,6 +97,7 @@ ComponentType GetComponentType(Tegra::Engines::SamplerDescriptor descriptor,
97 break; 97 break;
98 case TextureFormat::B5G6R5: 98 case TextureFormat::B5G6R5:
99 case TextureFormat::B6G5R5: 99 case TextureFormat::B6G5R5:
100 case TextureFormat::BF10GF11RF11:
100 if (component == 0) { 101 if (component == 0) {
101 return descriptor.b_type; 102 return descriptor.b_type;
102 } 103 }
@@ -119,7 +120,7 @@ ComponentType GetComponentType(Tegra::Engines::SamplerDescriptor descriptor,
119 } 120 }
120 break; 121 break;
121 } 122 }
122 UNIMPLEMENTED_MSG("texture format not implement={}", format); 123 UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
123 return ComponentType::FLOAT; 124 return ComponentType::FLOAT;
124} 125}
125 126
@@ -191,6 +192,14 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) {
191 return 6; 192 return 6;
192 } 193 }
193 return 0; 194 return 0;
195 case TextureFormat::BF10GF11RF11:
196 if (component == 1 || component == 2) {
197 return 11;
198 }
199 if (component == 0) {
200 return 10;
201 }
202 return 0;
194 case TextureFormat::G8R24: 203 case TextureFormat::G8R24:
195 if (component == 0) { 204 if (component == 0) {
196 return 8; 205 return 8;
@@ -211,10 +220,9 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) {
211 return (component == 0 || component == 1) ? 8 : 0; 220 return (component == 0 || component == 1) ? 8 : 0;
212 case TextureFormat::G4R4: 221 case TextureFormat::G4R4:
213 return (component == 0 || component == 1) ? 4 : 0; 222 return (component == 0 || component == 1) ? 4 : 0;
214 default:
215 UNIMPLEMENTED_MSG("texture format not implement={}", format);
216 return 0;
217 } 223 }
224 UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
225 return 0;
218} 226}
219 227
220std::size_t GetImageComponentMask(TextureFormat format) { 228std::size_t GetImageComponentMask(TextureFormat format) {
@@ -235,6 +243,7 @@ std::size_t GetImageComponentMask(TextureFormat format) {
235 case TextureFormat::R32_B24G8: 243 case TextureFormat::R32_B24G8:
236 case TextureFormat::B5G6R5: 244 case TextureFormat::B5G6R5:
237 case TextureFormat::B6G5R5: 245 case TextureFormat::B6G5R5:
246 case TextureFormat::BF10GF11RF11:
238 return std::size_t{R | G | B}; 247 return std::size_t{R | G | B};
239 case TextureFormat::R32_G32: 248 case TextureFormat::R32_G32:
240 case TextureFormat::R16_G16: 249 case TextureFormat::R16_G16:
@@ -248,10 +257,9 @@ std::size_t GetImageComponentMask(TextureFormat format) {
248 case TextureFormat::R8: 257 case TextureFormat::R8:
249 case TextureFormat::R1: 258 case TextureFormat::R1:
250 return std::size_t{R}; 259 return std::size_t{R};
251 default:
252 UNIMPLEMENTED_MSG("texture format not implement={}", format);
253 return std::size_t{R | G | B | A};
254 } 260 }
261 UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
262 return std::size_t{R | G | B | A};
255} 263}
256 264
257std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) { 265std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) {
@@ -299,7 +307,7 @@ std::pair<Node, bool> ShaderIR::GetComponentValue(ComponentType component_type,
299 return {std::move(original_value), true}; 307 return {std::move(original_value), true};
300 } 308 }
301 default: 309 default:
302 UNIMPLEMENTED_MSG("Unimplement component type={}", component_type); 310 UNIMPLEMENTED_MSG("Unimplemented component type={}", component_type);
303 return {std::move(original_value), true}; 311 return {std::move(original_value), true};
304 } 312 }
305} 313}
@@ -459,7 +467,7 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
459 default: 467 default:
460 break; 468 break;
461 } 469 }
462 UNIMPLEMENTED_MSG("Unimplemented operation={} type={}", 470 UNIMPLEMENTED_MSG("Unimplemented operation={}, type={}",
463 static_cast<u64>(instr.suatom_d.operation.Value()), 471 static_cast<u64>(instr.suatom_d.operation.Value()),
464 static_cast<u64>(instr.suatom_d.operation_type.Value())); 472 static_cast<u64>(instr.suatom_d.operation_type.Value()));
465 return OperationCode::AtomicImageAdd; 473 return OperationCode::AtomicImageAdd;
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp
index d00e10913..c0a8f233f 100644
--- a/src/video_core/shader/decode/other.cpp
+++ b/src/video_core/shader/decode/other.cpp
@@ -83,7 +83,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
83 return Operation(OperationCode::YNegate); 83 return Operation(OperationCode::YNegate);
84 case SystemVariable::InvocationInfo: 84 case SystemVariable::InvocationInfo:
85 LOG_WARNING(HW_GPU, "S2R instruction with InvocationInfo is incomplete"); 85 LOG_WARNING(HW_GPU, "S2R instruction with InvocationInfo is incomplete");
86 return Immediate(0U); 86 return Immediate(0x00ff'0000U);
87 case SystemVariable::WscaleFactorXY: 87 case SystemVariable::WscaleFactorXY:
88 UNIMPLEMENTED_MSG("S2R WscaleFactorXY is not implemented"); 88 UNIMPLEMENTED_MSG("S2R WscaleFactorXY is not implemented");
89 return Immediate(0U); 89 return Immediate(0U);
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index 8f0bb996e..29ebf65ba 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -357,13 +357,11 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
357 return pc; 357 return pc;
358} 358}
359 359
360ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(SamplerInfo info, u32 offset, 360ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(
361 std::optional<u32> buffer) { 361 SamplerInfo info, std::optional<Tegra::Engines::SamplerDescriptor> sampler) {
362 if (info.IsComplete()) { 362 if (info.IsComplete()) {
363 return info; 363 return info;
364 } 364 }
365 const auto sampler = buffer ? registry.ObtainBindlessSampler(*buffer, offset)
366 : registry.ObtainBoundSampler(offset);
367 if (!sampler) { 365 if (!sampler) {
368 LOG_WARNING(HW_GPU, "Unknown sampler info"); 366 LOG_WARNING(HW_GPU, "Unknown sampler info");
369 info.type = info.type.value_or(Tegra::Shader::TextureType::Texture2D); 367 info.type = info.type.value_or(Tegra::Shader::TextureType::Texture2D);
@@ -381,8 +379,8 @@ ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(SamplerInfo info, u32 offset,
381 379
382std::optional<Sampler> ShaderIR::GetSampler(Tegra::Shader::Sampler sampler, 380std::optional<Sampler> ShaderIR::GetSampler(Tegra::Shader::Sampler sampler,
383 SamplerInfo sampler_info) { 381 SamplerInfo sampler_info) {
384 const auto offset = static_cast<u32>(sampler.index.Value()); 382 const u32 offset = static_cast<u32>(sampler.index.Value());
385 const auto info = GetSamplerInfo(sampler_info, offset); 383 const auto info = GetSamplerInfo(sampler_info, registry.ObtainBoundSampler(offset));
386 384
387 // If this sampler has already been used, return the existing mapping. 385 // If this sampler has already been used, return the existing mapping.
388 const auto it = std::find_if(used_samplers.begin(), used_samplers.end(), 386 const auto it = std::find_if(used_samplers.begin(), used_samplers.end(),
@@ -404,20 +402,19 @@ std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
404 const Node sampler_register = GetRegister(reg); 402 const Node sampler_register = GetRegister(reg);
405 const auto [base_node, tracked_sampler_info] = 403 const auto [base_node, tracked_sampler_info] =
406 TrackBindlessSampler(sampler_register, global_code, static_cast<s64>(global_code.size())); 404 TrackBindlessSampler(sampler_register, global_code, static_cast<s64>(global_code.size()));
407 ASSERT(base_node != nullptr); 405 if (!base_node) {
408 if (base_node == nullptr) { 406 UNREACHABLE();
409 return std::nullopt; 407 return std::nullopt;
410 } 408 }
411 409
412 if (const auto bindless_sampler_info = 410 if (const auto sampler_info = std::get_if<BindlessSamplerNode>(&*tracked_sampler_info)) {
413 std::get_if<BindlessSamplerNode>(&*tracked_sampler_info)) { 411 const u32 buffer = sampler_info->index;
414 const u32 buffer = bindless_sampler_info->GetIndex(); 412 const u32 offset = sampler_info->offset;
415 const u32 offset = bindless_sampler_info->GetOffset(); 413 info = GetSamplerInfo(info, registry.ObtainBindlessSampler(buffer, offset));
416 info = GetSamplerInfo(info, offset, buffer);
417 414
418 // If this sampler has already been used, return the existing mapping. 415 // If this sampler has already been used, return the existing mapping.
419 const auto it = std::find_if(used_samplers.begin(), used_samplers.end(), 416 const auto it = std::find_if(used_samplers.begin(), used_samplers.end(),
420 [buffer = buffer, offset = offset](const Sampler& entry) { 417 [buffer, offset](const Sampler& entry) {
421 return entry.buffer == buffer && entry.offset == offset; 418 return entry.buffer == buffer && entry.offset == offset;
422 }); 419 });
423 if (it != used_samplers.end()) { 420 if (it != used_samplers.end()) {
@@ -431,10 +428,32 @@ std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
431 return used_samplers.emplace_back(next_index, offset, buffer, *info.type, *info.is_array, 428 return used_samplers.emplace_back(next_index, offset, buffer, *info.type, *info.is_array,
432 *info.is_shadow, *info.is_buffer, false); 429 *info.is_shadow, *info.is_buffer, false);
433 } 430 }
434 if (const auto array_sampler_info = std::get_if<ArraySamplerNode>(&*tracked_sampler_info)) { 431 if (const auto sampler_info = std::get_if<SeparateSamplerNode>(&*tracked_sampler_info)) {
435 const u32 base_offset = array_sampler_info->GetBaseOffset() / 4; 432 const std::pair indices = sampler_info->indices;
436 index_var = GetCustomVariable(array_sampler_info->GetIndexVar()); 433 const std::pair offsets = sampler_info->offsets;
437 info = GetSamplerInfo(info, base_offset); 434 info = GetSamplerInfo(info, registry.ObtainSeparateSampler(indices, offsets));
435
436 // Try to use an already created sampler if it exists
437 const auto it = std::find_if(
438 used_samplers.begin(), used_samplers.end(), [indices, offsets](const Sampler& entry) {
439 return offsets == std::pair{entry.offset, entry.secondary_offset} &&
440 indices == std::pair{entry.buffer, entry.secondary_buffer};
441 });
442 if (it != used_samplers.end()) {
443 ASSERT(it->is_separated && it->type == info.type && it->is_array == info.is_array &&
444 it->is_shadow == info.is_shadow && it->is_buffer == info.is_buffer);
445 return *it;
446 }
447
448 // Otherwise create a new mapping for this sampler
449 const u32 next_index = static_cast<u32>(used_samplers.size());
450 return used_samplers.emplace_back(next_index, offsets, indices, *info.type, *info.is_array,
451 *info.is_shadow, *info.is_buffer);
452 }
453 if (const auto sampler_info = std::get_if<ArraySamplerNode>(&*tracked_sampler_info)) {
454 const u32 base_offset = sampler_info->base_offset / 4;
455 index_var = GetCustomVariable(sampler_info->bindless_var);
456 info = GetSamplerInfo(info, registry.ObtainBoundSampler(base_offset));
438 457
439 // If this sampler has already been used, return the existing mapping. 458 // If this sampler has already been used, return the existing mapping.
440 const auto it = std::find_if( 459 const auto it = std::find_if(
diff --git a/src/video_core/shader/memory_util.cpp b/src/video_core/shader/memory_util.cpp
index 074f21691..5071c83ca 100644
--- a/src/video_core/shader/memory_util.cpp
+++ b/src/video_core/shader/memory_util.cpp
@@ -66,12 +66,12 @@ ProgramCode GetShaderCode(Tegra::MemoryManager& memory_manager, GPUVAddr gpu_add
66 66
67u64 GetUniqueIdentifier(Tegra::Engines::ShaderType shader_type, bool is_a, const ProgramCode& code, 67u64 GetUniqueIdentifier(Tegra::Engines::ShaderType shader_type, bool is_a, const ProgramCode& code,
68 const ProgramCode& code_b) { 68 const ProgramCode& code_b) {
69 u64 unique_identifier = boost::hash_value(code); 69 size_t unique_identifier = boost::hash_value(code);
70 if (is_a) { 70 if (is_a) {
71 // VertexA programs include two programs 71 // VertexA programs include two programs
72 boost::hash_combine(unique_identifier, boost::hash_value(code_b)); 72 boost::hash_combine(unique_identifier, boost::hash_value(code_b));
73 } 73 }
74 return unique_identifier; 74 return static_cast<u64>(unique_identifier);
75} 75}
76 76
77} // namespace VideoCommon::Shader 77} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h
index c5e5165ff..8f230d57a 100644
--- a/src/video_core/shader/node.h
+++ b/src/video_core/shader/node.h
@@ -275,10 +275,11 @@ using Node = std::shared_ptr<NodeData>;
275using Node4 = std::array<Node, 4>; 275using Node4 = std::array<Node, 4>;
276using NodeBlock = std::vector<Node>; 276using NodeBlock = std::vector<Node>;
277 277
278class BindlessSamplerNode; 278struct ArraySamplerNode;
279class ArraySamplerNode; 279struct BindlessSamplerNode;
280struct SeparateSamplerNode;
280 281
281using TrackSamplerData = std::variant<BindlessSamplerNode, ArraySamplerNode>; 282using TrackSamplerData = std::variant<BindlessSamplerNode, SeparateSamplerNode, ArraySamplerNode>;
282using TrackSampler = std::shared_ptr<TrackSamplerData>; 283using TrackSampler = std::shared_ptr<TrackSamplerData>;
283 284
284struct Sampler { 285struct Sampler {
@@ -288,63 +289,51 @@ struct Sampler {
288 : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow}, 289 : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow},
289 is_buffer{is_buffer}, is_indexed{is_indexed} {} 290 is_buffer{is_buffer}, is_indexed{is_indexed} {}
290 291
292 /// Separate sampler constructor
293 constexpr explicit Sampler(u32 index, std::pair<u32, u32> offsets, std::pair<u32, u32> buffers,
294 Tegra::Shader::TextureType type, bool is_array, bool is_shadow,
295 bool is_buffer)
296 : index{index}, offset{offsets.first}, secondary_offset{offsets.second},
297 buffer{buffers.first}, secondary_buffer{buffers.second}, type{type}, is_array{is_array},
298 is_shadow{is_shadow}, is_buffer{is_buffer}, is_separated{true} {}
299
291 /// Bindless samplers constructor 300 /// Bindless samplers constructor
292 constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type, 301 constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type,
293 bool is_array, bool is_shadow, bool is_buffer, bool is_indexed) 302 bool is_array, bool is_shadow, bool is_buffer, bool is_indexed)
294 : index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array}, 303 : index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array},
295 is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true}, is_indexed{is_indexed} {} 304 is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true}, is_indexed{is_indexed} {}
296 305
297 u32 index = 0; ///< Emulated index given for the this sampler. 306 u32 index = 0; ///< Emulated index given for the this sampler.
298 u32 offset = 0; ///< Offset in the const buffer from where the sampler is being read. 307 u32 offset = 0; ///< Offset in the const buffer from where the sampler is being read.
299 u32 buffer = 0; ///< Buffer where the bindless sampler is being read (unused on bound samplers). 308 u32 secondary_offset = 0; ///< Secondary offset in the const buffer.
300 u32 size = 1; ///< Size of the sampler. 309 u32 buffer = 0; ///< Buffer where the bindless sampler is read.
310 u32 secondary_buffer = 0; ///< Secondary buffer where the bindless sampler is read.
311 u32 size = 1; ///< Size of the sampler.
301 312
302 Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) 313 Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc)
303 bool is_array = false; ///< Whether the texture is being sampled as an array texture or not. 314 bool is_array = false; ///< Whether the texture is being sampled as an array texture or not.
304 bool is_shadow = false; ///< Whether the texture is being sampled as a depth texture or not. 315 bool is_shadow = false; ///< Whether the texture is being sampled as a depth texture or not.
305 bool is_buffer = false; ///< Whether the texture is a texture buffer without sampler. 316 bool is_buffer = false; ///< Whether the texture is a texture buffer without sampler.
306 bool is_bindless = false; ///< Whether this sampler belongs to a bindless texture or not. 317 bool is_bindless = false; ///< Whether this sampler belongs to a bindless texture or not.
307 bool is_indexed = false; ///< Whether this sampler is an indexed array of textures. 318 bool is_indexed = false; ///< Whether this sampler is an indexed array of textures.
319 bool is_separated = false; ///< Whether the image and sampler is separated or not.
308}; 320};
309 321
310/// Represents a tracked bindless sampler into a direct const buffer 322/// Represents a tracked bindless sampler into a direct const buffer
311class ArraySamplerNode final { 323struct ArraySamplerNode {
312public:
313 explicit ArraySamplerNode(u32 index, u32 base_offset, u32 bindless_var)
314 : index{index}, base_offset{base_offset}, bindless_var{bindless_var} {}
315
316 constexpr u32 GetIndex() const {
317 return index;
318 }
319
320 constexpr u32 GetBaseOffset() const {
321 return base_offset;
322 }
323
324 constexpr u32 GetIndexVar() const {
325 return bindless_var;
326 }
327
328private:
329 u32 index; 324 u32 index;
330 u32 base_offset; 325 u32 base_offset;
331 u32 bindless_var; 326 u32 bindless_var;
332}; 327};
333 328
334/// Represents a tracked bindless sampler into a direct const buffer 329/// Represents a tracked separate sampler image pair that was folded statically
335class BindlessSamplerNode final { 330struct SeparateSamplerNode {
336public: 331 std::pair<u32, u32> indices;
337 explicit BindlessSamplerNode(u32 index, u32 offset) : index{index}, offset{offset} {} 332 std::pair<u32, u32> offsets;
338 333};
339 constexpr u32 GetIndex() const {
340 return index;
341 }
342
343 constexpr u32 GetOffset() const {
344 return offset;
345 }
346 334
347private: 335/// Represents a tracked bindless sampler into a direct const buffer
336struct BindlessSamplerNode {
348 u32 index; 337 u32 index;
349 u32 offset; 338 u32 offset;
350}; 339};
diff --git a/src/video_core/shader/node_helper.h b/src/video_core/shader/node_helper.h
index 11231bbea..1e0886185 100644
--- a/src/video_core/shader/node_helper.h
+++ b/src/video_core/shader/node_helper.h
@@ -48,7 +48,7 @@ Node MakeNode(Args&&... args) {
48template <typename T, typename... Args> 48template <typename T, typename... Args>
49TrackSampler MakeTrackSampler(Args&&... args) { 49TrackSampler MakeTrackSampler(Args&&... args) {
50 static_assert(std::is_convertible_v<T, TrackSamplerData>); 50 static_assert(std::is_convertible_v<T, TrackSamplerData>);
51 return std::make_shared<TrackSamplerData>(T(std::forward<Args>(args)...)); 51 return std::make_shared<TrackSamplerData>(T{std::forward<Args>(args)...});
52} 52}
53 53
54template <typename... Args> 54template <typename... Args>
diff --git a/src/video_core/shader/registry.cpp b/src/video_core/shader/registry.cpp
index af70b3f35..cdf274e54 100644
--- a/src/video_core/shader/registry.cpp
+++ b/src/video_core/shader/registry.cpp
@@ -93,6 +93,26 @@ std::optional<SamplerDescriptor> Registry::ObtainBoundSampler(u32 offset) {
93 return value; 93 return value;
94} 94}
95 95
96std::optional<Tegra::Engines::SamplerDescriptor> Registry::ObtainSeparateSampler(
97 std::pair<u32, u32> buffers, std::pair<u32, u32> offsets) {
98 SeparateSamplerKey key;
99 key.buffers = buffers;
100 key.offsets = offsets;
101 const auto iter = separate_samplers.find(key);
102 if (iter != separate_samplers.end()) {
103 return iter->second;
104 }
105 if (!engine) {
106 return std::nullopt;
107 }
108
109 const u32 handle_1 = engine->AccessConstBuffer32(stage, key.buffers.first, key.offsets.first);
110 const u32 handle_2 = engine->AccessConstBuffer32(stage, key.buffers.second, key.offsets.second);
111 const SamplerDescriptor value = engine->AccessSampler(handle_1 | handle_2);
112 separate_samplers.emplace(key, value);
113 return value;
114}
115
96std::optional<Tegra::Engines::SamplerDescriptor> Registry::ObtainBindlessSampler(u32 buffer, 116std::optional<Tegra::Engines::SamplerDescriptor> Registry::ObtainBindlessSampler(u32 buffer,
97 u32 offset) { 117 u32 offset) {
98 const std::pair key = {buffer, offset}; 118 const std::pair key = {buffer, offset};
diff --git a/src/video_core/shader/registry.h b/src/video_core/shader/registry.h
index 0c80d35fd..231206765 100644
--- a/src/video_core/shader/registry.h
+++ b/src/video_core/shader/registry.h
@@ -19,8 +19,39 @@
19 19
20namespace VideoCommon::Shader { 20namespace VideoCommon::Shader {
21 21
22struct SeparateSamplerKey {
23 std::pair<u32, u32> buffers;
24 std::pair<u32, u32> offsets;
25};
26
27} // namespace VideoCommon::Shader
28
29namespace std {
30
31template <>
32struct hash<VideoCommon::Shader::SeparateSamplerKey> {
33 std::size_t operator()(const VideoCommon::Shader::SeparateSamplerKey& key) const noexcept {
34 return std::hash<u32>{}(key.buffers.first ^ key.buffers.second ^ key.offsets.first ^
35 key.offsets.second);
36 }
37};
38
39template <>
40struct equal_to<VideoCommon::Shader::SeparateSamplerKey> {
41 bool operator()(const VideoCommon::Shader::SeparateSamplerKey& lhs,
42 const VideoCommon::Shader::SeparateSamplerKey& rhs) const noexcept {
43 return lhs.buffers == rhs.buffers && lhs.offsets == rhs.offsets;
44 }
45};
46
47} // namespace std
48
49namespace VideoCommon::Shader {
50
22using KeyMap = std::unordered_map<std::pair<u32, u32>, u32, Common::PairHash>; 51using KeyMap = std::unordered_map<std::pair<u32, u32>, u32, Common::PairHash>;
23using BoundSamplerMap = std::unordered_map<u32, Tegra::Engines::SamplerDescriptor>; 52using BoundSamplerMap = std::unordered_map<u32, Tegra::Engines::SamplerDescriptor>;
53using SeparateSamplerMap =
54 std::unordered_map<SeparateSamplerKey, Tegra::Engines::SamplerDescriptor>;
24using BindlessSamplerMap = 55using BindlessSamplerMap =
25 std::unordered_map<std::pair<u32, u32>, Tegra::Engines::SamplerDescriptor, Common::PairHash>; 56 std::unordered_map<std::pair<u32, u32>, Tegra::Engines::SamplerDescriptor, Common::PairHash>;
26 57
@@ -73,6 +104,9 @@ public:
73 104
74 std::optional<Tegra::Engines::SamplerDescriptor> ObtainBoundSampler(u32 offset); 105 std::optional<Tegra::Engines::SamplerDescriptor> ObtainBoundSampler(u32 offset);
75 106
107 std::optional<Tegra::Engines::SamplerDescriptor> ObtainSeparateSampler(
108 std::pair<u32, u32> buffers, std::pair<u32, u32> offsets);
109
76 std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset); 110 std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset);
77 111
78 /// Inserts a key. 112 /// Inserts a key.
@@ -128,6 +162,7 @@ private:
128 Tegra::Engines::ConstBufferEngineInterface* engine = nullptr; 162 Tegra::Engines::ConstBufferEngineInterface* engine = nullptr;
129 KeyMap keys; 163 KeyMap keys;
130 BoundSamplerMap bound_samplers; 164 BoundSamplerMap bound_samplers;
165 SeparateSamplerMap separate_samplers;
131 BindlessSamplerMap bindless_samplers; 166 BindlessSamplerMap bindless_samplers;
132 u32 bound_buffer; 167 u32 bound_buffer;
133 GraphicsInfo graphics_info; 168 GraphicsInfo graphics_info;
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 15ae152f2..3a98b2104 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -330,8 +330,8 @@ private:
330 OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation); 330 OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation);
331 331
332 /// Queries the missing sampler info from the execution context. 332 /// Queries the missing sampler info from the execution context.
333 SamplerInfo GetSamplerInfo(SamplerInfo info, u32 offset, 333 SamplerInfo GetSamplerInfo(SamplerInfo info,
334 std::optional<u32> buffer = std::nullopt); 334 std::optional<Tegra::Engines::SamplerDescriptor> sampler);
335 335
336 /// Accesses a texture sampler. 336 /// Accesses a texture sampler.
337 std::optional<Sampler> GetSampler(Tegra::Shader::Sampler sampler, SamplerInfo info); 337 std::optional<Sampler> GetSampler(Tegra::Shader::Sampler sampler, SamplerInfo info);
@@ -409,8 +409,14 @@ private:
409 409
410 std::tuple<Node, u32, u32> TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const; 410 std::tuple<Node, u32, u32> TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const;
411 411
412 std::tuple<Node, TrackSampler> TrackBindlessSampler(Node tracked, const NodeBlock& code, 412 std::pair<Node, TrackSampler> TrackBindlessSampler(Node tracked, const NodeBlock& code,
413 s64 cursor); 413 s64 cursor);
414
415 std::pair<Node, TrackSampler> HandleBindlessIndirectRead(const CbufNode& cbuf,
416 const OperationNode& operation,
417 Node gpr, Node base_offset,
418 Node tracked, const NodeBlock& code,
419 s64 cursor);
414 420
415 std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const; 421 std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const;
416 422
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp
index eb97bfd41..d5ed81442 100644
--- a/src/video_core/shader/track.cpp
+++ b/src/video_core/shader/track.cpp
@@ -14,6 +14,7 @@
14namespace VideoCommon::Shader { 14namespace VideoCommon::Shader {
15 15
16namespace { 16namespace {
17
17std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor, 18std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor,
18 OperationCode operation_code) { 19 OperationCode operation_code) {
19 for (; cursor >= 0; --cursor) { 20 for (; cursor >= 0; --cursor) {
@@ -63,7 +64,8 @@ bool AmendNodeCv(std::size_t amend_index, Node node) {
63 if (const auto operation = std::get_if<OperationNode>(&*node)) { 64 if (const auto operation = std::get_if<OperationNode>(&*node)) {
64 operation->SetAmendIndex(amend_index); 65 operation->SetAmendIndex(amend_index);
65 return true; 66 return true;
66 } else if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { 67 }
68 if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
67 conditional->SetAmendIndex(amend_index); 69 conditional->SetAmendIndex(amend_index);
68 return true; 70 return true;
69 } 71 }
@@ -72,40 +74,27 @@ bool AmendNodeCv(std::size_t amend_index, Node node) {
72 74
73} // Anonymous namespace 75} // Anonymous namespace
74 76
75std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, const NodeBlock& code, 77std::pair<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, const NodeBlock& code,
76 s64 cursor) { 78 s64 cursor) {
77 if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { 79 if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) {
80 const u32 cbuf_index = cbuf->GetIndex();
81
78 // Constant buffer found, test if it's an immediate 82 // Constant buffer found, test if it's an immediate
79 const auto& offset = cbuf->GetOffset(); 83 const auto& offset = cbuf->GetOffset();
80 if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) { 84 if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
81 auto track = 85 auto track = MakeTrackSampler<BindlessSamplerNode>(cbuf_index, immediate->GetValue());
82 MakeTrackSampler<BindlessSamplerNode>(cbuf->GetIndex(), immediate->GetValue());
83 return {tracked, track}; 86 return {tracked, track};
84 } 87 }
85 if (const auto operation = std::get_if<OperationNode>(&*offset)) { 88 if (const auto operation = std::get_if<OperationNode>(&*offset)) {
86 const u32 bound_buffer = registry.GetBoundBuffer(); 89 const u32 bound_buffer = registry.GetBoundBuffer();
87 if (bound_buffer != cbuf->GetIndex()) { 90 if (bound_buffer != cbuf_index) {
88 return {}; 91 return {};
89 } 92 }
90 const auto pair = DecoupleIndirectRead(*operation); 93 if (const std::optional pair = DecoupleIndirectRead(*operation)) {
91 if (!pair) { 94 auto [gpr, base_offset] = *pair;
92 return {}; 95 return HandleBindlessIndirectRead(*cbuf, *operation, gpr, base_offset, tracked,
96 code, cursor);
93 } 97 }
94 auto [gpr, base_offset] = *pair;
95 const auto offset_inm = std::get_if<ImmediateNode>(&*base_offset);
96 const auto& gpu_driver = registry.AccessGuestDriverProfile();
97 const u32 bindless_cv = NewCustomVariable();
98 Node op =
99 Operation(OperationCode::UDiv, gpr, Immediate(gpu_driver.GetTextureHandlerSize()));
100
101 const Node cv_node = GetCustomVariable(bindless_cv);
102 Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op));
103 const std::size_t amend_index = DeclareAmend(std::move(amend_op));
104 AmendNodeCv(amend_index, code[cursor]);
105 // TODO Implement Bindless Index custom variable
106 auto track = MakeTrackSampler<ArraySamplerNode>(cbuf->GetIndex(),
107 offset_inm->GetValue(), bindless_cv);
108 return {tracked, track};
109 } 98 }
110 return {}; 99 return {};
111 } 100 }
@@ -122,10 +111,23 @@ std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, cons
122 return TrackBindlessSampler(source, code, new_cursor); 111 return TrackBindlessSampler(source, code, new_cursor);
123 } 112 }
124 if (const auto operation = std::get_if<OperationNode>(&*tracked)) { 113 if (const auto operation = std::get_if<OperationNode>(&*tracked)) {
125 for (std::size_t i = operation->GetOperandsCount(); i > 0; --i) { 114 const OperationNode& op = *operation;
126 if (auto found = TrackBindlessSampler((*operation)[i - 1], code, cursor); 115
127 std::get<0>(found)) { 116 const OperationCode opcode = operation->GetCode();
128 // Cbuf found in operand. 117 if (opcode == OperationCode::IBitwiseOr || opcode == OperationCode::UBitwiseOr) {
118 ASSERT(op.GetOperandsCount() == 2);
119 auto [node_a, index_a, offset_a] = TrackCbuf(op[0], code, cursor);
120 auto [node_b, index_b, offset_b] = TrackCbuf(op[1], code, cursor);
121 if (node_a && node_b) {
122 auto track = MakeTrackSampler<SeparateSamplerNode>(std::pair{index_a, index_b},
123 std::pair{offset_a, offset_b});
124 return {tracked, std::move(track)};
125 }
126 }
127 std::size_t i = op.GetOperandsCount();
128 while (i--) {
129 if (auto found = TrackBindlessSampler(op[i - 1], code, cursor); std::get<0>(found)) {
130 // Constant buffer found in operand.
129 return found; 131 return found;
130 } 132 }
131 } 133 }
@@ -139,6 +141,26 @@ std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, cons
139 return {}; 141 return {};
140} 142}
141 143
144std::pair<Node, TrackSampler> ShaderIR::HandleBindlessIndirectRead(
145 const CbufNode& cbuf, const OperationNode& operation, Node gpr, Node base_offset, Node tracked,
146 const NodeBlock& code, s64 cursor) {
147 const auto offset_imm = std::get<ImmediateNode>(*base_offset);
148 const auto& gpu_driver = registry.AccessGuestDriverProfile();
149 const u32 bindless_cv = NewCustomVariable();
150 const u32 texture_handler_size = gpu_driver.GetTextureHandlerSize();
151 Node op = Operation(OperationCode::UDiv, gpr, Immediate(texture_handler_size));
152
153 Node cv_node = GetCustomVariable(bindless_cv);
154 Node amend_op = Operation(OperationCode::Assign, std::move(cv_node), std::move(op));
155 const std::size_t amend_index = DeclareAmend(std::move(amend_op));
156 AmendNodeCv(amend_index, code[cursor]);
157
158 // TODO: Implement bindless index custom variable
159 auto track =
160 MakeTrackSampler<ArraySamplerNode>(cbuf.GetIndex(), offset_imm.GetValue(), bindless_cv);
161 return {tracked, track};
162}
163
142std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, 164std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code,
143 s64 cursor) const { 165 s64 cursor) const {
144 if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { 166 if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) {
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h
new file mode 100644
index 000000000..b7608fc7b
--- /dev/null
+++ b/src/video_core/shader_cache.h
@@ -0,0 +1,240 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <memory>
9#include <mutex>
10#include <unordered_map>
11#include <utility>
12#include <vector>
13
14#include "common/assert.h"
15#include "common/common_types.h"
16#include "video_core/rasterizer_interface.h"
17
18namespace VideoCommon {
19
20template <class T>
21class ShaderCache {
22 static constexpr u64 PAGE_BITS = 14;
23 static constexpr u64 PAGE_SIZE = u64(1) << PAGE_BITS;
24
25 struct Entry {
26 VAddr addr_start;
27 VAddr addr_end;
28 T* data;
29
30 bool is_memory_marked = true;
31
32 constexpr bool Overlaps(VAddr start, VAddr end) const noexcept {
33 return start < addr_end && addr_start < end;
34 }
35 };
36
37public:
38 virtual ~ShaderCache() = default;
39
40 /// @brief Removes shaders inside a given region
41 /// @note Checks for ranges
42 /// @param addr Start address of the invalidation
43 /// @param size Number of bytes of the invalidation
44 void InvalidateRegion(VAddr addr, std::size_t size) {
45 std::scoped_lock lock{invalidation_mutex};
46 InvalidatePagesInRegion(addr, size);
47 RemovePendingShaders();
48 }
49
50 /// @brief Unmarks a memory region as cached and marks it for removal
51 /// @param addr Start address of the CPU write operation
52 /// @param size Number of bytes of the CPU write operation
53 void OnCPUWrite(VAddr addr, std::size_t size) {
54 std::lock_guard lock{invalidation_mutex};
55 InvalidatePagesInRegion(addr, size);
56 }
57
58 /// @brief Flushes delayed removal operations
59 void SyncGuestHost() {
60 std::scoped_lock lock{invalidation_mutex};
61 RemovePendingShaders();
62 }
63
64 /// @brief Tries to obtain a cached shader starting in a given address
65 /// @note Doesn't check for ranges, the given address has to be the start of the shader
66 /// @param addr Start address of the shader, this doesn't cache for region
67 /// @return Pointer to a valid shader, nullptr when nothing is found
68 T* TryGet(VAddr addr) const {
69 std::scoped_lock lock{lookup_mutex};
70
71 const auto it = lookup_cache.find(addr);
72 if (it == lookup_cache.end()) {
73 return nullptr;
74 }
75 return it->second->data;
76 }
77
78protected:
79 explicit ShaderCache(VideoCore::RasterizerInterface& rasterizer_) : rasterizer{rasterizer_} {}
80
81 /// @brief Register in the cache a given entry
82 /// @param data Shader to store in the cache
83 /// @param addr Start address of the shader that will be registered
84 /// @param size Size in bytes of the shader
85 void Register(std::unique_ptr<T> data, VAddr addr, std::size_t size) {
86 std::scoped_lock lock{invalidation_mutex, lookup_mutex};
87
88 const VAddr addr_end = addr + size;
89 Entry* const entry = NewEntry(addr, addr_end, data.get());
90
91 const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
92 for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) {
93 invalidation_cache[page].push_back(entry);
94 }
95
96 storage.push_back(std::move(data));
97
98 rasterizer.UpdatePagesCachedCount(addr, size, 1);
99 }
100
101 /// @brief Called when a shader is going to be removed
102 /// @param shader Shader that will be removed
103 /// @pre invalidation_cache is locked
104 /// @pre lookup_mutex is locked
105 virtual void OnShaderRemoval([[maybe_unused]] T* shader) {}
106
107private:
108 /// @brief Invalidate pages in a given region
109 /// @pre invalidation_mutex is locked
110 void InvalidatePagesInRegion(VAddr addr, std::size_t size) {
111 const VAddr addr_end = addr + size;
112 const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
113 for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) {
114 auto it = invalidation_cache.find(page);
115 if (it == invalidation_cache.end()) {
116 continue;
117 }
118 InvalidatePageEntries(it->second, addr, addr_end);
119 }
120 }
121
122 /// @brief Remove shaders marked for deletion
123 /// @pre invalidation_mutex is locked
124 void RemovePendingShaders() {
125 if (marked_for_removal.empty()) {
126 return;
127 }
128 // Remove duplicates
129 std::sort(marked_for_removal.begin(), marked_for_removal.end());
130 marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()),
131 marked_for_removal.end());
132
133 std::vector<T*> removed_shaders;
134 removed_shaders.reserve(marked_for_removal.size());
135
136 std::scoped_lock lock{lookup_mutex};
137
138 for (Entry* const entry : marked_for_removal) {
139 removed_shaders.push_back(entry->data);
140
141 const auto it = lookup_cache.find(entry->addr_start);
142 ASSERT(it != lookup_cache.end());
143 lookup_cache.erase(it);
144 }
145 marked_for_removal.clear();
146
147 if (!removed_shaders.empty()) {
148 RemoveShadersFromStorage(std::move(removed_shaders));
149 }
150 }
151
152 /// @brief Invalidates entries in a given range for the passed page
153 /// @param entries Vector of entries in the page, it will be modified on overlaps
154 /// @param addr Start address of the invalidation
155 /// @param addr_end Non-inclusive end address of the invalidation
156 /// @pre invalidation_mutex is locked
157 void InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end) {
158 std::size_t index = 0;
159 while (index < entries.size()) {
160 Entry* const entry = entries[index];
161 if (!entry->Overlaps(addr, addr_end)) {
162 ++index;
163 continue;
164 }
165
166 UnmarkMemory(entry);
167 RemoveEntryFromInvalidationCache(entry);
168 marked_for_removal.push_back(entry);
169 }
170 }
171
172 /// @brief Removes all references to an entry in the invalidation cache
173 /// @param entry Entry to remove from the invalidation cache
174 /// @pre invalidation_mutex is locked
175 void RemoveEntryFromInvalidationCache(const Entry* entry) {
176 const u64 page_end = (entry->addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
177 for (u64 page = entry->addr_start >> PAGE_BITS; page < page_end; ++page) {
178 const auto entries_it = invalidation_cache.find(page);
179 ASSERT(entries_it != invalidation_cache.end());
180 std::vector<Entry*>& entries = entries_it->second;
181
182 const auto entry_it = std::find(entries.begin(), entries.end(), entry);
183 ASSERT(entry_it != entries.end());
184 entries.erase(entry_it);
185 }
186 }
187
188 /// @brief Unmarks an entry from the rasterizer cache
189 /// @param entry Entry to unmark from memory
190 void UnmarkMemory(Entry* entry) {
191 if (!entry->is_memory_marked) {
192 return;
193 }
194 entry->is_memory_marked = false;
195
196 const VAddr addr = entry->addr_start;
197 const std::size_t size = entry->addr_end - addr;
198 rasterizer.UpdatePagesCachedCount(addr, size, -1);
199 }
200
201 /// @brief Removes a vector of shaders from a list
202 /// @param removed_shaders Shaders to be removed from the storage
203 /// @pre invalidation_mutex is locked
204 /// @pre lookup_mutex is locked
205 void RemoveShadersFromStorage(std::vector<T*> removed_shaders) {
206 // Notify removals
207 for (T* const shader : removed_shaders) {
208 OnShaderRemoval(shader);
209 }
210
211 // Remove them from the cache
212 const auto is_removed = [&removed_shaders](std::unique_ptr<T>& shader) {
213 return std::find(removed_shaders.begin(), removed_shaders.end(), shader.get()) !=
214 removed_shaders.end();
215 };
216 storage.erase(std::remove_if(storage.begin(), storage.end(), is_removed), storage.end());
217 }
218
219 /// @brief Creates a new entry in the lookup cache and returns its pointer
220 /// @pre lookup_mutex is locked
221 Entry* NewEntry(VAddr addr, VAddr addr_end, T* data) {
222 auto entry = std::make_unique<Entry>(Entry{addr, addr_end, data});
223 Entry* const entry_pointer = entry.get();
224
225 lookup_cache.emplace(addr, std::move(entry));
226 return entry_pointer;
227 }
228
229 VideoCore::RasterizerInterface& rasterizer;
230
231 mutable std::mutex lookup_mutex;
232 std::mutex invalidation_mutex;
233
234 std::unordered_map<u64, std::unique_ptr<Entry>> lookup_cache;
235 std::unordered_map<u64, std::vector<Entry*>> invalidation_cache;
236 std::vector<std::unique_ptr<T>> storage;
237 std::vector<Entry*> marked_for_removal;
238};
239
240} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index 7032e0059..f476f03b0 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -41,7 +41,7 @@ struct Table {
41 ComponentType alpha_component; 41 ComponentType alpha_component;
42 bool is_srgb; 42 bool is_srgb;
43}; 43};
44constexpr std::array<Table, 77> DefinitionTable = {{ 44constexpr std::array<Table, 78> DefinitionTable = {{
45 {TextureFormat::A8R8G8B8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ABGR8U}, 45 {TextureFormat::A8R8G8B8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ABGR8U},
46 {TextureFormat::A8R8G8B8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::ABGR8S}, 46 {TextureFormat::A8R8G8B8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::ABGR8S},
47 {TextureFormat::A8R8G8B8, C, UINT, UINT, UINT, UINT, PixelFormat::ABGR8UI}, 47 {TextureFormat::A8R8G8B8, C, UINT, UINT, UINT, UINT, PixelFormat::ABGR8UI},
@@ -98,6 +98,7 @@ constexpr std::array<Table, 77> DefinitionTable = {{
98 {TextureFormat::ZF32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::Z32F}, 98 {TextureFormat::ZF32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::Z32F},
99 {TextureFormat::Z16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::Z16}, 99 {TextureFormat::Z16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::Z16},
100 {TextureFormat::S8Z24, C, UINT, UNORM, UNORM, UNORM, PixelFormat::S8Z24}, 100 {TextureFormat::S8Z24, C, UINT, UNORM, UNORM, UNORM, PixelFormat::S8Z24},
101 {TextureFormat::G24R8, C, UINT, UNORM, UNORM, UNORM, PixelFormat::S8Z24},
101 {TextureFormat::ZF32_X24S8, C, FLOAT, UINT, UNORM, UNORM, PixelFormat::Z32FS8}, 102 {TextureFormat::ZF32_X24S8, C, FLOAT, UINT, UNORM, UNORM, PixelFormat::Z32FS8},
102 103
103 {TextureFormat::DXT1, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXT1}, 104 {TextureFormat::DXT1, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXT1},
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp
index 715f39d0d..0caf3b4f0 100644
--- a/src/video_core/texture_cache/surface_base.cpp
+++ b/src/video_core/texture_cache/surface_base.cpp
@@ -120,6 +120,9 @@ std::optional<std::pair<u32, u32>> SurfaceBaseImpl::GetLayerMipmap(
120 } 120 }
121 const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)}; 121 const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)};
122 const auto layer{static_cast<u32>(relative_address / layer_size)}; 122 const auto layer{static_cast<u32>(relative_address / layer_size)};
123 if (layer >= params.depth) {
124 return {};
125 }
123 const GPUVAddr mipmap_address = relative_address - layer_size * layer; 126 const GPUVAddr mipmap_address = relative_address - layer_size * layer;
124 const auto mipmap_it = 127 const auto mipmap_it =
125 Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address); 128 Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address);
@@ -248,12 +251,11 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager,
248 251
249 // Use an extra temporal buffer 252 // Use an extra temporal buffer
250 auto& tmp_buffer = staging_cache.GetBuffer(1); 253 auto& tmp_buffer = staging_cache.GetBuffer(1);
251 // Special case for 3D Texture Segments
252 const bool must_read_current_data =
253 params.block_depth > 0 && params.target == VideoCore::Surface::SurfaceTarget::Texture2D;
254 tmp_buffer.resize(guest_memory_size); 254 tmp_buffer.resize(guest_memory_size);
255 host_ptr = tmp_buffer.data(); 255 host_ptr = tmp_buffer.data();
256 if (must_read_current_data) { 256
257 if (params.target == SurfaceTarget::Texture3D) {
258 // Special case for 3D texture segments
257 memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size); 259 memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size);
258 } 260 }
259 261
diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h
index 79e10ffbb..173f2edba 100644
--- a/src/video_core/texture_cache/surface_base.h
+++ b/src/video_core/texture_cache/surface_base.h
@@ -217,8 +217,8 @@ public:
217 } 217 }
218 218
219 bool IsProtected() const { 219 bool IsProtected() const {
220 // Only 3D Slices are to be protected 220 // Only 3D slices are to be protected
221 return is_target && params.block_depth > 0; 221 return is_target && params.target == SurfaceTarget::Texture3D;
222 } 222 }
223 223
224 bool IsRenderTarget() const { 224 bool IsRenderTarget() const {
@@ -250,6 +250,11 @@ public:
250 return GetView(ViewParams(overview_params.target, 0, num_layers, 0, params.num_levels)); 250 return GetView(ViewParams(overview_params.target, 0, num_layers, 0, params.num_levels));
251 } 251 }
252 252
253 TView Emplace3DView(u32 slice, u32 depth, u32 base_level, u32 num_levels) {
254 return GetView(ViewParams(VideoCore::Surface::SurfaceTarget::Texture3D, slice, depth,
255 base_level, num_levels));
256 }
257
253 std::optional<TView> EmplaceIrregularView(const SurfaceParams& view_params, 258 std::optional<TView> EmplaceIrregularView(const SurfaceParams& view_params,
254 const GPUVAddr view_addr, 259 const GPUVAddr view_addr,
255 const std::size_t candidate_size, const u32 mipmap, 260 const std::size_t candidate_size, const u32 mipmap,
@@ -272,8 +277,8 @@ public:
272 std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr, 277 std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr,
273 const std::size_t candidate_size) { 278 const std::size_t candidate_size) {
274 if (params.target == SurfaceTarget::Texture3D || 279 if (params.target == SurfaceTarget::Texture3D ||
275 (params.num_levels == 1 && !params.is_layered) || 280 view_params.target == SurfaceTarget::Texture3D ||
276 view_params.target == SurfaceTarget::Texture3D) { 281 (params.num_levels == 1 && !params.is_layered)) {
277 return {}; 282 return {};
278 } 283 }
279 const auto layer_mipmap{GetLayerMipmap(view_addr)}; 284 const auto layer_mipmap{GetLayerMipmap(view_addr)};
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index 884fabffe..0b2b2b8c4 100644
--- a/src/video_core/texture_cache/surface_params.cpp
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -215,10 +215,19 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz
215 params.num_levels = 1; 215 params.num_levels = 1;
216 params.emulated_levels = 1; 216 params.emulated_levels = 1;
217 217
218 const bool is_layered = config.layers > 1 && params.block_depth == 0; 218 if (config.memory_layout.is_3d != 0) {
219 params.is_layered = is_layered; 219 params.depth = config.layers.Value();
220 params.depth = is_layered ? config.layers.Value() : 1; 220 params.is_layered = false;
221 params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D; 221 params.target = SurfaceTarget::Texture3D;
222 } else if (config.layers > 1) {
223 params.depth = config.layers.Value();
224 params.is_layered = true;
225 params.target = SurfaceTarget::Texture2DArray;
226 } else {
227 params.depth = 1;
228 params.is_layered = false;
229 params.target = SurfaceTarget::Texture2D;
230 }
222 return params; 231 return params;
223} 232}
224 233
@@ -237,7 +246,7 @@ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
237 params.width = config.width; 246 params.width = config.width;
238 params.height = config.height; 247 params.height = config.height;
239 params.pitch = config.pitch; 248 params.pitch = config.pitch;
240 // TODO(Rodrigo): Try to guess the surface target from depth and layer parameters 249 // TODO(Rodrigo): Try to guess texture arrays from parameters
241 params.target = SurfaceTarget::Texture2D; 250 params.target = SurfaceTarget::Texture2D;
242 params.depth = 1; 251 params.depth = 1;
243 params.num_levels = 1; 252 params.num_levels = 1;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 8bfc541d4..cdcddb225 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -14,6 +14,7 @@
14#include <unordered_map> 14#include <unordered_map>
15#include <vector> 15#include <vector>
16 16
17#include <boost/container/small_vector.hpp>
17#include <boost/icl/interval_map.hpp> 18#include <boost/icl/interval_map.hpp>
18#include <boost/range/iterator_range.hpp> 19#include <boost/range/iterator_range.hpp>
19 20
@@ -23,6 +24,7 @@
23#include "core/core.h" 24#include "core/core.h"
24#include "core/memory.h" 25#include "core/memory.h"
25#include "core/settings.h" 26#include "core/settings.h"
27#include "video_core/compatible_formats.h"
26#include "video_core/dirty_flags.h" 28#include "video_core/dirty_flags.h"
27#include "video_core/engines/fermi_2d.h" 29#include "video_core/engines/fermi_2d.h"
28#include "video_core/engines/maxwell_3d.h" 30#include "video_core/engines/maxwell_3d.h"
@@ -46,13 +48,14 @@ class RasterizerInterface;
46 48
47namespace VideoCommon { 49namespace VideoCommon {
48 50
51using VideoCore::Surface::FormatCompatibility;
49using VideoCore::Surface::PixelFormat; 52using VideoCore::Surface::PixelFormat;
50
51using VideoCore::Surface::SurfaceTarget; 53using VideoCore::Surface::SurfaceTarget;
52using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig; 54using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig;
53 55
54template <typename TSurface, typename TView> 56template <typename TSurface, typename TView>
55class TextureCache { 57class TextureCache {
58 using VectorSurface = boost::container::small_vector<TSurface, 1>;
56 59
57public: 60public:
58 void InvalidateRegion(VAddr addr, std::size_t size) { 61 void InvalidateRegion(VAddr addr, std::size_t size) {
@@ -246,7 +249,7 @@ public:
246 auto& surface = render_targets[index].target; 249 auto& surface = render_targets[index].target;
247 surface->MarkAsRenderTarget(false, NO_RT); 250 surface->MarkAsRenderTarget(false, NO_RT);
248 const auto& cr_params = surface->GetSurfaceParams(); 251 const auto& cr_params = surface->GetSurfaceParams();
249 if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation) { 252 if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
250 AsyncFlushSurface(surface); 253 AsyncFlushSurface(surface);
251 } 254 }
252 } 255 }
@@ -296,30 +299,30 @@ public:
296 const GPUVAddr src_gpu_addr = src_config.Address(); 299 const GPUVAddr src_gpu_addr = src_config.Address();
297 const GPUVAddr dst_gpu_addr = dst_config.Address(); 300 const GPUVAddr dst_gpu_addr = dst_config.Address();
298 DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr); 301 DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr);
299 const std::optional<VAddr> dst_cpu_addr = 302
300 system.GPU().MemoryManager().GpuToCpuAddress(dst_gpu_addr); 303 const auto& memory_manager = system.GPU().MemoryManager();
301 const std::optional<VAddr> src_cpu_addr = 304 const std::optional<VAddr> dst_cpu_addr = memory_manager.GpuToCpuAddress(dst_gpu_addr);
302 system.GPU().MemoryManager().GpuToCpuAddress(src_gpu_addr); 305 const std::optional<VAddr> src_cpu_addr = memory_manager.GpuToCpuAddress(src_gpu_addr);
303 std::pair<TSurface, TView> dst_surface = 306 std::pair dst_surface = GetSurface(dst_gpu_addr, *dst_cpu_addr, dst_params, true, false);
304 GetSurface(dst_gpu_addr, *dst_cpu_addr, dst_params, true, false); 307 TView src_surface = GetSurface(src_gpu_addr, *src_cpu_addr, src_params, true, false).second;
305 std::pair<TSurface, TView> src_surface = 308 ImageBlit(src_surface, dst_surface.second, copy_config);
306 GetSurface(src_gpu_addr, *src_cpu_addr, src_params, true, false);
307 ImageBlit(src_surface.second, dst_surface.second, copy_config);
308 dst_surface.first->MarkAsModified(true, Tick()); 309 dst_surface.first->MarkAsModified(true, Tick());
309 } 310 }
310 311
311 TSurface TryFindFramebufferSurface(VAddr addr) { 312 TSurface TryFindFramebufferSurface(VAddr addr) const {
312 if (!addr) { 313 if (!addr) {
313 return nullptr; 314 return nullptr;
314 } 315 }
315 const VAddr page = addr >> registry_page_bits; 316 const VAddr page = addr >> registry_page_bits;
316 std::vector<TSurface>& list = registry[page]; 317 const auto it = registry.find(page);
317 for (auto& surface : list) { 318 if (it == registry.end()) {
318 if (surface->GetCpuAddr() == addr) { 319 return nullptr;
319 return surface;
320 }
321 } 320 }
322 return nullptr; 321 const auto& list = it->second;
322 const auto found = std::find_if(list.begin(), list.end(), [addr](const auto& surface) {
323 return surface->GetCpuAddr() == addr;
324 });
325 return found != list.end() ? *found : nullptr;
323 } 326 }
324 327
325 u64 Tick() { 328 u64 Tick() {
@@ -498,18 +501,18 @@ private:
498 * @param untopological Indicates to the recycler that the texture has no way 501 * @param untopological Indicates to the recycler that the texture has no way
499 * to match the overlaps due to topological reasons. 502 * to match the overlaps due to topological reasons.
500 **/ 503 **/
501 RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params, 504 RecycleStrategy PickStrategy(VectorSurface& overlaps, const SurfaceParams& params,
502 const GPUVAddr gpu_addr, const MatchTopologyResult untopological) { 505 const GPUVAddr gpu_addr, const MatchTopologyResult untopological) {
503 if (Settings::IsGPULevelExtreme()) { 506 if (Settings::IsGPULevelExtreme()) {
504 return RecycleStrategy::Flush; 507 return RecycleStrategy::Flush;
505 } 508 }
506 // 3D Textures decision 509 // 3D Textures decision
507 if (params.block_depth > 1 || params.target == SurfaceTarget::Texture3D) { 510 if (params.target == SurfaceTarget::Texture3D) {
508 return RecycleStrategy::Flush; 511 return RecycleStrategy::Flush;
509 } 512 }
510 for (const auto& s : overlaps) { 513 for (const auto& s : overlaps) {
511 const auto& s_params = s->GetSurfaceParams(); 514 const auto& s_params = s->GetSurfaceParams();
512 if (s_params.block_depth > 1 || s_params.target == SurfaceTarget::Texture3D) { 515 if (s_params.target == SurfaceTarget::Texture3D) {
513 return RecycleStrategy::Flush; 516 return RecycleStrategy::Flush;
514 } 517 }
515 } 518 }
@@ -538,9 +541,8 @@ private:
538 * @param untopological Indicates to the recycler that the texture has no way to match the 541 * @param untopological Indicates to the recycler that the texture has no way to match the
539 * overlaps due to topological reasons. 542 * overlaps due to topological reasons.
540 **/ 543 **/
541 std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps, 544 std::pair<TSurface, TView> RecycleSurface(VectorSurface& overlaps, const SurfaceParams& params,
542 const SurfaceParams& params, const GPUVAddr gpu_addr, 545 const GPUVAddr gpu_addr, const bool preserve_contents,
543 const bool preserve_contents,
544 const MatchTopologyResult untopological) { 546 const MatchTopologyResult untopological) {
545 const bool do_load = preserve_contents && Settings::IsGPULevelExtreme(); 547 const bool do_load = preserve_contents && Settings::IsGPULevelExtreme();
546 for (auto& surface : overlaps) { 548 for (auto& surface : overlaps) {
@@ -594,7 +596,7 @@ private:
594 } else { 596 } else {
595 new_surface = GetUncachedSurface(gpu_addr, params); 597 new_surface = GetUncachedSurface(gpu_addr, params);
596 } 598 }
597 const auto& final_params = new_surface->GetSurfaceParams(); 599 const SurfaceParams& final_params = new_surface->GetSurfaceParams();
598 if (cr_params.type != final_params.type) { 600 if (cr_params.type != final_params.type) {
599 if (Settings::IsGPULevelExtreme()) { 601 if (Settings::IsGPULevelExtreme()) {
600 BufferCopy(current_surface, new_surface); 602 BufferCopy(current_surface, new_surface);
@@ -602,7 +604,7 @@ private:
602 } else { 604 } else {
603 std::vector<CopyParams> bricks = current_surface->BreakDown(final_params); 605 std::vector<CopyParams> bricks = current_surface->BreakDown(final_params);
604 for (auto& brick : bricks) { 606 for (auto& brick : bricks) {
605 ImageCopy(current_surface, new_surface, brick); 607 TryCopyImage(current_surface, new_surface, brick);
606 } 608 }
607 } 609 }
608 Unregister(current_surface); 610 Unregister(current_surface);
@@ -650,47 +652,65 @@ private:
650 * @param params The parameters on the new surface. 652 * @param params The parameters on the new surface.
651 * @param gpu_addr The starting address of the new surface. 653 * @param gpu_addr The starting address of the new surface.
652 **/ 654 **/
653 std::optional<std::pair<TSurface, TView>> TryReconstructSurface(std::vector<TSurface>& overlaps, 655 std::optional<std::pair<TSurface, TView>> TryReconstructSurface(VectorSurface& overlaps,
654 const SurfaceParams& params, 656 const SurfaceParams& params,
655 const GPUVAddr gpu_addr) { 657 GPUVAddr gpu_addr) {
656 if (params.target == SurfaceTarget::Texture3D) { 658 if (params.target == SurfaceTarget::Texture3D) {
657 return {}; 659 return std::nullopt;
658 } 660 }
659 bool modified = false; 661 const auto test_modified = [](TSurface& surface) { return surface->IsModified(); };
660 TSurface new_surface = GetUncachedSurface(gpu_addr, params); 662 TSurface new_surface = GetUncachedSurface(gpu_addr, params);
661 u32 passed_tests = 0; 663
664 if (std::none_of(overlaps.begin(), overlaps.end(), test_modified)) {
665 LoadSurface(new_surface);
666 for (const auto& surface : overlaps) {
667 Unregister(surface);
668 }
669 Register(new_surface);
670 return {{new_surface, new_surface->GetMainView()}};
671 }
672
673 std::size_t passed_tests = 0;
662 for (auto& surface : overlaps) { 674 for (auto& surface : overlaps) {
663 const SurfaceParams& src_params = surface->GetSurfaceParams(); 675 const SurfaceParams& src_params = surface->GetSurfaceParams();
664 if (src_params.is_layered || src_params.num_levels > 1) { 676 const auto mipmap_layer{new_surface->GetLayerMipmap(surface->GetGpuAddr())};
665 // We send this cases to recycle as they are more complex to handle
666 return {};
667 }
668 const std::size_t candidate_size = surface->GetSizeInBytes();
669 auto mipmap_layer{new_surface->GetLayerMipmap(surface->GetGpuAddr())};
670 if (!mipmap_layer) { 677 if (!mipmap_layer) {
671 continue; 678 continue;
672 } 679 }
673 const auto [layer, mipmap] = *mipmap_layer; 680 const auto [base_layer, base_mipmap] = *mipmap_layer;
674 if (new_surface->GetMipmapSize(mipmap) != candidate_size) { 681 if (new_surface->GetMipmapSize(base_mipmap) != surface->GetMipmapSize(0)) {
675 continue; 682 continue;
676 } 683 }
677 modified |= surface->IsModified(); 684 ++passed_tests;
678 // Now we got all the data set up 685
679 const u32 width = SurfaceParams::IntersectWidth(src_params, params, 0, mipmap); 686 // Copy all mipmaps and layers
680 const u32 height = SurfaceParams::IntersectHeight(src_params, params, 0, mipmap); 687 const u32 block_width = params.GetDefaultBlockWidth();
681 const CopyParams copy_params(0, 0, 0, 0, 0, layer, 0, mipmap, width, height, 1); 688 const u32 block_height = params.GetDefaultBlockHeight();
682 passed_tests++; 689 for (u32 mipmap = base_mipmap; mipmap < base_mipmap + src_params.num_levels; ++mipmap) {
683 ImageCopy(surface, new_surface, copy_params); 690 const u32 width = SurfaceParams::IntersectWidth(src_params, params, 0, mipmap);
691 const u32 height = SurfaceParams::IntersectHeight(src_params, params, 0, mipmap);
692 if (width < block_width || height < block_height) {
693 // Current APIs forbid copying small compressed textures, avoid errors
694 break;
695 }
696 const CopyParams copy_params(0, 0, 0, 0, 0, base_layer, 0, mipmap, width, height,
697 src_params.depth);
698 TryCopyImage(surface, new_surface, copy_params);
699 }
684 } 700 }
685 if (passed_tests == 0) { 701 if (passed_tests == 0) {
686 return {}; 702 return std::nullopt;
703 }
704 if (Settings::IsGPULevelExtreme() && passed_tests != overlaps.size()) {
687 // In Accurate GPU all tests should pass, else we recycle 705 // In Accurate GPU all tests should pass, else we recycle
688 } else if (Settings::IsGPULevelExtreme() && passed_tests != overlaps.size()) { 706 return std::nullopt;
689 return {};
690 } 707 }
708
709 const bool modified = std::any_of(overlaps.begin(), overlaps.end(), test_modified);
691 for (const auto& surface : overlaps) { 710 for (const auto& surface : overlaps) {
692 Unregister(surface); 711 Unregister(surface);
693 } 712 }
713
694 new_surface->MarkAsModified(modified, Tick()); 714 new_surface->MarkAsModified(modified, Tick());
695 Register(new_surface); 715 Register(new_surface);
696 return {{new_surface, new_surface->GetMainView()}}; 716 return {{new_surface, new_surface->GetMainView()}};
@@ -708,53 +728,11 @@ private:
708 * @param preserve_contents Indicates that the new surface should be loaded from memory or 728 * @param preserve_contents Indicates that the new surface should be loaded from memory or
709 * left blank. 729 * left blank.
710 */ 730 */
711 std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(std::vector<TSurface>& overlaps, 731 std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(VectorSurface& overlaps,
712 const SurfaceParams& params, 732 const SurfaceParams& params,
713 const GPUVAddr gpu_addr, 733 GPUVAddr gpu_addr, VAddr cpu_addr,
714 const VAddr cpu_addr,
715 bool preserve_contents) { 734 bool preserve_contents) {
716 if (params.target == SurfaceTarget::Texture3D) { 735 if (params.target != SurfaceTarget::Texture3D) {
717 bool failed = false;
718 if (params.num_levels > 1) {
719 // We can't handle mipmaps in 3D textures yet, better fallback to LLE approach
720 return std::nullopt;
721 }
722 TSurface new_surface = GetUncachedSurface(gpu_addr, params);
723 bool modified = false;
724 for (auto& surface : overlaps) {
725 const SurfaceParams& src_params = surface->GetSurfaceParams();
726 if (src_params.target != SurfaceTarget::Texture2D) {
727 failed = true;
728 break;
729 }
730 if (src_params.height != params.height) {
731 failed = true;
732 break;
733 }
734 if (src_params.block_depth != params.block_depth ||
735 src_params.block_height != params.block_height) {
736 failed = true;
737 break;
738 }
739 const u32 offset = static_cast<u32>(surface->GetCpuAddr() - cpu_addr);
740 const auto offsets = params.GetBlockOffsetXYZ(offset);
741 const auto z = std::get<2>(offsets);
742 modified |= surface->IsModified();
743 const CopyParams copy_params(0, 0, 0, 0, 0, z, 0, 0, params.width, params.height,
744 1);
745 ImageCopy(surface, new_surface, copy_params);
746 }
747 if (failed) {
748 return std::nullopt;
749 }
750 for (const auto& surface : overlaps) {
751 Unregister(surface);
752 }
753 new_surface->MarkAsModified(modified, Tick());
754 Register(new_surface);
755 auto view = new_surface->GetMainView();
756 return {{std::move(new_surface), view}};
757 } else {
758 for (const auto& surface : overlaps) { 736 for (const auto& surface : overlaps) {
759 if (!surface->MatchTarget(params.target)) { 737 if (!surface->MatchTarget(params.target)) {
760 if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) { 738 if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) {
@@ -770,11 +748,60 @@ private:
770 continue; 748 continue;
771 } 749 }
772 if (surface->MatchesStructure(params) == MatchStructureResult::FullMatch) { 750 if (surface->MatchesStructure(params) == MatchStructureResult::FullMatch) {
773 return {{surface, surface->GetMainView()}}; 751 return std::make_pair(surface, surface->GetMainView());
774 } 752 }
775 } 753 }
776 return InitializeSurface(gpu_addr, params, preserve_contents); 754 return InitializeSurface(gpu_addr, params, preserve_contents);
777 } 755 }
756
757 if (params.num_levels > 1) {
758 // We can't handle mipmaps in 3D textures yet, better fallback to LLE approach
759 return std::nullopt;
760 }
761
762 if (overlaps.size() == 1) {
763 const auto& surface = overlaps[0];
764 const SurfaceParams& overlap_params = surface->GetSurfaceParams();
765 // Don't attempt to render to textures with more than one level for now
766 // The texture has to be to the right or the sample address if we want to render to it
767 if (overlap_params.num_levels == 1 && cpu_addr >= surface->GetCpuAddr()) {
768 const u32 offset = static_cast<u32>(cpu_addr - surface->GetCpuAddr());
769 const u32 slice = std::get<2>(params.GetBlockOffsetXYZ(offset));
770 if (slice < overlap_params.depth) {
771 auto view = surface->Emplace3DView(slice, params.depth, 0, 1);
772 return std::make_pair(std::move(surface), std::move(view));
773 }
774 }
775 }
776
777 TSurface new_surface = GetUncachedSurface(gpu_addr, params);
778 bool modified = false;
779
780 for (auto& surface : overlaps) {
781 const SurfaceParams& src_params = surface->GetSurfaceParams();
782 if (src_params.target != SurfaceTarget::Texture2D ||
783 src_params.height != params.height ||
784 src_params.block_depth != params.block_depth ||
785 src_params.block_height != params.block_height) {
786 return std::nullopt;
787 }
788 modified |= surface->IsModified();
789
790 const u32 offset = static_cast<u32>(surface->GetCpuAddr() - cpu_addr);
791 const u32 slice = std::get<2>(params.GetBlockOffsetXYZ(offset));
792 const u32 width = params.width;
793 const u32 height = params.height;
794 const CopyParams copy_params(0, 0, 0, 0, 0, slice, 0, 0, width, height, 1);
795 TryCopyImage(surface, new_surface, copy_params);
796 }
797 for (const auto& surface : overlaps) {
798 Unregister(surface);
799 }
800 new_surface->MarkAsModified(modified, Tick());
801 Register(new_surface);
802
803 TView view = new_surface->GetMainView();
804 return std::make_pair(std::move(new_surface), std::move(view));
778 } 805 }
779 806
780 /** 807 /**
@@ -810,7 +837,7 @@ private:
810 TSurface& current_surface = iter->second; 837 TSurface& current_surface = iter->second;
811 const auto topological_result = current_surface->MatchesTopology(params); 838 const auto topological_result = current_surface->MatchesTopology(params);
812 if (topological_result != MatchTopologyResult::FullMatch) { 839 if (topological_result != MatchTopologyResult::FullMatch) {
813 std::vector<TSurface> overlaps{current_surface}; 840 VectorSurface overlaps{current_surface};
814 return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, 841 return RecycleSurface(overlaps, params, gpu_addr, preserve_contents,
815 topological_result); 842 topological_result);
816 } 843 }
@@ -852,7 +879,7 @@ private:
852 } 879 }
853 } 880 }
854 881
855 // Check if it's a 3D texture 882 // Manage 3D textures
856 if (params.block_depth > 0) { 883 if (params.block_depth > 0) {
857 auto surface = 884 auto surface =
858 Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr, preserve_contents); 885 Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr, preserve_contents);
@@ -868,12 +895,9 @@ private:
868 // two things either the candidate surface is a supertexture of the overlap 895 // two things either the candidate surface is a supertexture of the overlap
869 // or they don't match in any known way. 896 // or they don't match in any known way.
870 if (!current_surface->IsInside(gpu_addr, gpu_addr + candidate_size)) { 897 if (!current_surface->IsInside(gpu_addr, gpu_addr + candidate_size)) {
871 if (current_surface->GetGpuAddr() == gpu_addr) { 898 const std::optional view = TryReconstructSurface(overlaps, params, gpu_addr);
872 std::optional<std::pair<TSurface, TView>> view = 899 if (view) {
873 TryReconstructSurface(overlaps, params, gpu_addr); 900 return *view;
874 if (view) {
875 return *view;
876 }
877 } 901 }
878 return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, 902 return RecycleSurface(overlaps, params, gpu_addr, preserve_contents,
879 MatchTopologyResult::FullMatch); 903 MatchTopologyResult::FullMatch);
@@ -1030,7 +1054,7 @@ private:
1030 void DeduceBestBlit(SurfaceParams& src_params, SurfaceParams& dst_params, 1054 void DeduceBestBlit(SurfaceParams& src_params, SurfaceParams& dst_params,
1031 const GPUVAddr src_gpu_addr, const GPUVAddr dst_gpu_addr) { 1055 const GPUVAddr src_gpu_addr, const GPUVAddr dst_gpu_addr) {
1032 auto deduced_src = DeduceSurface(src_gpu_addr, src_params); 1056 auto deduced_src = DeduceSurface(src_gpu_addr, src_params);
1033 auto deduced_dst = DeduceSurface(src_gpu_addr, src_params); 1057 auto deduced_dst = DeduceSurface(dst_gpu_addr, dst_params);
1034 if (deduced_src.Failed() || deduced_dst.Failed()) { 1058 if (deduced_src.Failed() || deduced_dst.Failed()) {
1035 return; 1059 return;
1036 } 1060 }
@@ -1126,23 +1150,25 @@ private:
1126 } 1150 }
1127 } 1151 }
1128 1152
1129 std::vector<TSurface> GetSurfacesInRegion(const VAddr cpu_addr, const std::size_t size) { 1153 VectorSurface GetSurfacesInRegion(const VAddr cpu_addr, const std::size_t size) {
1130 if (size == 0) { 1154 if (size == 0) {
1131 return {}; 1155 return {};
1132 } 1156 }
1133 const VAddr cpu_addr_end = cpu_addr + size; 1157 const VAddr cpu_addr_end = cpu_addr + size;
1134 VAddr start = cpu_addr >> registry_page_bits;
1135 const VAddr end = (cpu_addr_end - 1) >> registry_page_bits; 1158 const VAddr end = (cpu_addr_end - 1) >> registry_page_bits;
1136 std::vector<TSurface> surfaces; 1159 VectorSurface surfaces;
1137 while (start <= end) { 1160 for (VAddr start = cpu_addr >> registry_page_bits; start <= end; ++start) {
1138 std::vector<TSurface>& list = registry[start]; 1161 const auto it = registry.find(start);
1139 for (auto& surface : list) { 1162 if (it == registry.end()) {
1140 if (!surface->IsPicked() && surface->Overlaps(cpu_addr, cpu_addr_end)) { 1163 continue;
1141 surface->MarkAsPicked(true); 1164 }
1142 surfaces.push_back(surface); 1165 for (auto& surface : it->second) {
1166 if (surface->IsPicked() || !surface->Overlaps(cpu_addr, cpu_addr_end)) {
1167 continue;
1143 } 1168 }
1169 surface->MarkAsPicked(true);
1170 surfaces.push_back(surface);
1144 } 1171 }
1145 start++;
1146 } 1172 }
1147 for (auto& surface : surfaces) { 1173 for (auto& surface : surfaces) {
1148 surface->MarkAsPicked(false); 1174 surface->MarkAsPicked(false);
@@ -1167,6 +1193,19 @@ private:
1167 return {}; 1193 return {};
1168 } 1194 }
1169 1195
1196 /// Try to do an image copy logging when formats are incompatible.
1197 void TryCopyImage(TSurface& src, TSurface& dst, const CopyParams& copy) {
1198 const SurfaceParams& src_params = src->GetSurfaceParams();
1199 const SurfaceParams& dst_params = dst->GetSurfaceParams();
1200 if (!format_compatibility.TestCopy(src_params.pixel_format, dst_params.pixel_format)) {
1201 LOG_ERROR(HW_GPU, "Illegal copy between formats={{{}, {}}}",
1202 static_cast<int>(dst_params.pixel_format),
1203 static_cast<int>(src_params.pixel_format));
1204 return;
1205 }
1206 ImageCopy(src, dst, copy);
1207 }
1208
1170 constexpr PixelFormat GetSiblingFormat(PixelFormat format) const { 1209 constexpr PixelFormat GetSiblingFormat(PixelFormat format) const {
1171 return siblings_table[static_cast<std::size_t>(format)]; 1210 return siblings_table[static_cast<std::size_t>(format)];
1172 } 1211 }
@@ -1216,6 +1255,7 @@ private:
1216 VideoCore::RasterizerInterface& rasterizer; 1255 VideoCore::RasterizerInterface& rasterizer;
1217 1256
1218 FormatLookupTable format_lookup_table; 1257 FormatLookupTable format_lookup_table;
1258 FormatCompatibility format_compatibility;
1219 1259
1220 u64 ticks{}; 1260 u64 ticks{};
1221 1261
diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp
index d1939d744..4171e3ef2 100644
--- a/src/video_core/textures/texture.cpp
+++ b/src/video_core/textures/texture.cpp
@@ -48,7 +48,7 @@ constexpr std::array<float, 256> SRGB_CONVERSION_LUT = {
48}; 48};
49 49
50unsigned SettingsMinimumAnisotropy() noexcept { 50unsigned SettingsMinimumAnisotropy() noexcept {
51 switch (static_cast<Anisotropy>(Settings::values.max_anisotropy)) { 51 switch (static_cast<Anisotropy>(Settings::values.max_anisotropy.GetValue())) {
52 default: 52 default:
53 case Anisotropy::Default: 53 case Anisotropy::Default:
54 return 1U; 54 return 1U;
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index f60bdc60a..45f360bdd 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -19,7 +19,7 @@ namespace {
19std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, 19std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
20 Core::System& system, 20 Core::System& system,
21 Core::Frontend::GraphicsContext& context) { 21 Core::Frontend::GraphicsContext& context) {
22 switch (Settings::values.renderer_backend) { 22 switch (Settings::values.renderer_backend.GetValue()) {
23 case Settings::RendererBackend::OpenGL: 23 case Settings::RendererBackend::OpenGL:
24 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context); 24 return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);
25#ifdef HAS_VULKAN 25#ifdef HAS_VULKAN
@@ -42,7 +42,7 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
42 return nullptr; 42 return nullptr;
43 } 43 }
44 44
45 if (Settings::values.use_asynchronous_gpu_emulation) { 45 if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
46 return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer), 46 return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer),
47 std::move(context)); 47 std::move(context));
48 } 48 }
@@ -51,8 +51,8 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
51 51
52u16 GetResolutionScaleFactor(const RendererBase& renderer) { 52u16 GetResolutionScaleFactor(const RendererBase& renderer) {
53 return static_cast<u16>( 53 return static_cast<u16>(
54 Settings::values.resolution_factor != 0 54 Settings::values.resolution_factor.GetValue() != 0
55 ? Settings::values.resolution_factor 55 ? Settings::values.resolution_factor.GetValue()
56 : renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio()); 56 : renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio());
57} 57}
58 58
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 8b9404718..6b25a7fa0 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -24,6 +24,8 @@ add_executable(yuzu
24 compatibility_list.h 24 compatibility_list.h
25 configuration/config.cpp 25 configuration/config.cpp
26 configuration/config.h 26 configuration/config.h
27 configuration/configuration_shared.cpp
28 configuration/configuration_shared.h
27 configuration/configure.ui 29 configuration/configure.ui
28 configuration/configure_audio.cpp 30 configuration/configure_audio.cpp
29 configuration/configure_audio.h 31 configuration/configure_audio.h
@@ -60,9 +62,12 @@ add_executable(yuzu
60 configuration/configure_mouse_advanced.cpp 62 configuration/configure_mouse_advanced.cpp
61 configuration/configure_mouse_advanced.h 63 configuration/configure_mouse_advanced.h
62 configuration/configure_mouse_advanced.ui 64 configuration/configure_mouse_advanced.ui
63 configuration/configure_per_general.cpp 65 configuration/configure_per_game.cpp
64 configuration/configure_per_general.h 66 configuration/configure_per_game.h
65 configuration/configure_per_general.ui 67 configuration/configure_per_game.ui
68 configuration/configure_per_game_addons.cpp
69 configuration/configure_per_game_addons.h
70 configuration/configure_per_game_addons.ui
66 configuration/configure_profile_manager.cpp 71 configuration/configure_profile_manager.cpp
67 configuration/configure_profile_manager.h 72 configuration/configure_profile_manager.h
68 configuration/configure_profile_manager.ui 73 configuration/configure_profile_manager.ui
@@ -147,7 +152,7 @@ endif()
147create_target_directory_groups(yuzu) 152create_target_directory_groups(yuzu)
148 153
149target_link_libraries(yuzu PRIVATE common core input_common video_core) 154target_link_libraries(yuzu PRIVATE common core input_common video_core)
150target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets) 155target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets)
151target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 156target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
152 157
153if (ENABLE_VULKAN AND NOT WIN32) 158if (ENABLE_VULKAN AND NOT WIN32)
@@ -208,6 +213,10 @@ if (MSVC)
208 copy_yuzu_unicorn_deps(yuzu) 213 copy_yuzu_unicorn_deps(yuzu)
209endif() 214endif()
210 215
216if (NOT APPLE)
217 target_compile_definitions(yuzu PRIVATE HAS_OPENGL)
218endif()
219
211if (ENABLE_VULKAN) 220if (ENABLE_VULKAN)
212 target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) 221 target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
213 target_compile_definitions(yuzu PRIVATE HAS_VULKAN) 222 target_compile_definitions(yuzu PRIVATE HAS_VULKAN)
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 1adf8932b..5738787ac 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -8,13 +8,16 @@
8#include <QHBoxLayout> 8#include <QHBoxLayout>
9#include <QKeyEvent> 9#include <QKeyEvent>
10#include <QMessageBox> 10#include <QMessageBox>
11#include <QOffscreenSurface>
12#include <QOpenGLContext>
13#include <QPainter> 11#include <QPainter>
14#include <QScreen> 12#include <QScreen>
15#include <QStringList> 13#include <QStringList>
16#include <QWindow> 14#include <QWindow>
17 15
16#ifdef HAS_OPENGL
17#include <QOffscreenSurface>
18#include <QOpenGLContext>
19#endif
20
18#if !defined(WIN32) && HAS_VULKAN 21#if !defined(WIN32) && HAS_VULKAN
19#include <qpa/qplatformnativeinterface.h> 22#include <qpa/qplatformnativeinterface.h>
20#endif 23#endif
@@ -41,49 +44,65 @@ EmuThread::EmuThread() = default;
41EmuThread::~EmuThread() = default; 44EmuThread::~EmuThread() = default;
42 45
43void EmuThread::run() { 46void EmuThread::run() {
44 MicroProfileOnThreadCreate("EmuThread"); 47 std::string name = "yuzu:EmuControlThread";
48 MicroProfileOnThreadCreate(name.c_str());
49 Common::SetCurrentThreadName(name.c_str());
50
51 auto& system = Core::System::GetInstance();
52
53 system.RegisterHostThread();
54
55 auto& gpu = system.GPU();
45 56
46 // Main process has been loaded. Make the context current to this thread and begin GPU and CPU 57 // Main process has been loaded. Make the context current to this thread and begin GPU and CPU
47 // execution. 58 // execution.
48 Core::System::GetInstance().GPU().Start(); 59 gpu.Start();
60
61 gpu.ObtainContext();
49 62
50 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); 63 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
51 64
52 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( 65 system.Renderer().Rasterizer().LoadDiskResources(
53 stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { 66 stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
54 emit LoadProgress(stage, value, total); 67 emit LoadProgress(stage, value, total);
55 }); 68 });
56 69
57 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); 70 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
58 71
72 gpu.ReleaseContext();
73
59 // Holds whether the cpu was running during the last iteration, 74 // Holds whether the cpu was running during the last iteration,
60 // so that the DebugModeLeft signal can be emitted before the 75 // so that the DebugModeLeft signal can be emitted before the
61 // next execution step 76 // next execution step
62 bool was_active = false; 77 bool was_active = false;
63 while (!stop_run) { 78 while (!stop_run) {
64 if (running) { 79 if (running) {
65 if (!was_active) 80 if (was_active) {
66 emit DebugModeLeft(); 81 emit DebugModeLeft();
82 }
67 83
68 Core::System::ResultStatus result = Core::System::GetInstance().RunLoop(); 84 running_guard = true;
85 Core::System::ResultStatus result = system.Run();
69 if (result != Core::System::ResultStatus::Success) { 86 if (result != Core::System::ResultStatus::Success) {
87 running_guard = false;
70 this->SetRunning(false); 88 this->SetRunning(false);
71 emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); 89 emit ErrorThrown(result, system.GetStatusDetails());
72 } 90 }
91 running_wait.Wait();
92 result = system.Pause();
93 if (result != Core::System::ResultStatus::Success) {
94 running_guard = false;
95 this->SetRunning(false);
96 emit ErrorThrown(result, system.GetStatusDetails());
97 }
98 running_guard = false;
73 99
74 was_active = running || exec_step; 100 if (!stop_run) {
75 if (!was_active && !stop_run) 101 was_active = true;
76 emit DebugModeEntered(); 102 emit DebugModeEntered();
103 }
77 } else if (exec_step) { 104 } else if (exec_step) {
78 if (!was_active) 105 UNIMPLEMENTED();
79 emit DebugModeLeft();
80
81 exec_step = false;
82 Core::System::GetInstance().SingleStep();
83 emit DebugModeEntered();
84 yieldCurrentThread();
85
86 was_active = false;
87 } else { 106 } else {
88 std::unique_lock lock{running_mutex}; 107 std::unique_lock lock{running_mutex};
89 running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); 108 running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; });
@@ -91,13 +110,14 @@ void EmuThread::run() {
91 } 110 }
92 111
93 // Shutdown the core emulation 112 // Shutdown the core emulation
94 Core::System::GetInstance().Shutdown(); 113 system.Shutdown();
95 114
96#if MICROPROFILE_ENABLED 115#if MICROPROFILE_ENABLED
97 MicroProfileOnThreadExit(); 116 MicroProfileOnThreadExit();
98#endif 117#endif
99} 118}
100 119
120#ifdef HAS_OPENGL
101class OpenGLSharedContext : public Core::Frontend::GraphicsContext { 121class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
102public: 122public:
103 /// Create the original context that should be shared from 123 /// Create the original context that should be shared from
@@ -106,6 +126,9 @@ public:
106 format.setVersion(4, 3); 126 format.setVersion(4, 3);
107 format.setProfile(QSurfaceFormat::CompatibilityProfile); 127 format.setProfile(QSurfaceFormat::CompatibilityProfile);
108 format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); 128 format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
129 if (Settings::values.renderer_debug) {
130 format.setOption(QSurfaceFormat::FormatOption::DebugContext);
131 }
109 // TODO: expose a setting for buffer value (ie default/single/double/triple) 132 // TODO: expose a setting for buffer value (ie default/single/double/triple)
110 format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); 133 format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
111 format.setSwapInterval(0); 134 format.setSwapInterval(0);
@@ -122,7 +145,7 @@ public:
122 145
123 // disable vsync for any shared contexts 146 // disable vsync for any shared contexts
124 auto format = share_context->format(); 147 auto format = share_context->format();
125 format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0); 148 format.setSwapInterval(main_surface ? Settings::values.use_vsync.GetValue() : 0);
126 149
127 context = std::make_unique<QOpenGLContext>(); 150 context = std::make_unique<QOpenGLContext>();
128 context->setShareContext(share_context); 151 context->setShareContext(share_context);
@@ -180,6 +203,7 @@ private:
180 std::unique_ptr<QOffscreenSurface> offscreen_surface{}; 203 std::unique_ptr<QOffscreenSurface> offscreen_surface{};
181 QSurface* surface; 204 QSurface* surface;
182}; 205};
206#endif
183 207
184class DummyContext : public Core::Frontend::GraphicsContext {}; 208class DummyContext : public Core::Frontend::GraphicsContext {};
185 209
@@ -352,7 +376,7 @@ QByteArray GRenderWindow::saveGeometry() {
352} 376}
353 377
354qreal GRenderWindow::windowPixelRatio() const { 378qreal GRenderWindow::windowPixelRatio() const {
355 return devicePixelRatio(); 379 return devicePixelRatioF();
356} 380}
357 381
358std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const { 382std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const {
@@ -470,13 +494,15 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
470} 494}
471 495
472std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { 496std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
473 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { 497#ifdef HAS_OPENGL
498 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
474 auto c = static_cast<OpenGLSharedContext*>(main_context.get()); 499 auto c = static_cast<OpenGLSharedContext*>(main_context.get());
475 // Bind the shared contexts to the main surface in case the backend wants to take over 500 // Bind the shared contexts to the main surface in case the backend wants to take over
476 // presentation 501 // presentation
477 return std::make_unique<OpenGLSharedContext>(c->GetShareContext(), 502 return std::make_unique<OpenGLSharedContext>(c->GetShareContext(),
478 child_widget->windowHandle()); 503 child_widget->windowHandle());
479 } 504 }
505#endif
480 return std::make_unique<DummyContext>(); 506 return std::make_unique<DummyContext>();
481} 507}
482 508
@@ -485,7 +511,7 @@ bool GRenderWindow::InitRenderTarget() {
485 511
486 first_frame = false; 512 first_frame = false;
487 513
488 switch (Settings::values.renderer_backend) { 514 switch (Settings::values.renderer_backend.GetValue()) {
489 case Settings::RendererBackend::OpenGL: 515 case Settings::RendererBackend::OpenGL:
490 if (!InitializeOpenGL()) { 516 if (!InitializeOpenGL()) {
491 return false; 517 return false;
@@ -512,7 +538,7 @@ bool GRenderWindow::InitRenderTarget() {
512 OnFramebufferSizeChanged(); 538 OnFramebufferSizeChanged();
513 BackupGeometry(); 539 BackupGeometry();
514 540
515 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { 541 if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
516 if (!LoadOpenGL()) { 542 if (!LoadOpenGL()) {
517 return false; 543 return false;
518 } 544 }
@@ -557,6 +583,7 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
557} 583}
558 584
559bool GRenderWindow::InitializeOpenGL() { 585bool GRenderWindow::InitializeOpenGL() {
586#ifdef HAS_OPENGL
560 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, 587 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
561 // WA_DontShowOnScreen, WA_DeleteOnClose 588 // WA_DontShowOnScreen, WA_DeleteOnClose
562 auto child = new OpenGLRenderWidget(this); 589 auto child = new OpenGLRenderWidget(this);
@@ -568,6 +595,11 @@ bool GRenderWindow::InitializeOpenGL() {
568 std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle())); 595 std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle()));
569 596
570 return true; 597 return true;
598#else
599 QMessageBox::warning(this, tr("OpenGL not available!"),
600 tr("yuzu has not been compiled with OpenGL support."));
601 return false;
602#endif
571} 603}
572 604
573bool GRenderWindow::InitializeVulkan() { 605bool GRenderWindow::InitializeVulkan() {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 3626604ca..6c59b4d5c 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -59,6 +59,12 @@ public:
59 this->running = running; 59 this->running = running;
60 lock.unlock(); 60 lock.unlock();
61 running_cv.notify_all(); 61 running_cv.notify_all();
62 if (!running) {
63 running_wait.Set();
64 /// Wait until effectively paused
65 while (running_guard)
66 ;
67 }
62 } 68 }
63 69
64 /** 70 /**
@@ -84,6 +90,8 @@ private:
84 std::atomic_bool stop_run{false}; 90 std::atomic_bool stop_run{false};
85 std::mutex running_mutex; 91 std::mutex running_mutex;
86 std::condition_variable running_cv; 92 std::condition_variable running_cv;
93 Common::Event running_wait{};
94 std::atomic_bool running_guard{false};
87 95
88signals: 96signals:
89 /** 97 /**
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index b08b87426..1b2b1b2bb 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -13,17 +13,20 @@
13#include "input_common/udp/client.h" 13#include "input_common/udp/client.h"
14#include "yuzu/configuration/config.h" 14#include "yuzu/configuration/config.h"
15 15
16Config::Config() { 16Config::Config(const std::string& config_file, bool is_global) {
17 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 17 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
18 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; 18 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + config_file;
19 FileUtil::CreateFullPath(qt_config_loc); 19 FileUtil::CreateFullPath(qt_config_loc);
20 qt_config = 20 qt_config =
21 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); 21 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
22 global = is_global;
22 Reload(); 23 Reload();
23} 24}
24 25
25Config::~Config() { 26Config::~Config() {
26 Save(); 27 if (global) {
28 Save();
29 }
27} 30}
28 31
29const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { 32const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
@@ -211,8 +214,8 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
211// This must be in alphabetical order according to action name as it must have the same order as 214// This must be in alphabetical order according to action name as it must have the same order as
212// UISetting::values.shortcuts, which is alphabetically ordered. 215// UISetting::values.shortcuts, which is alphabetically ordered.
213// clang-format off 216// clang-format off
214const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{{ 217const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
215 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, 218 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
216 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, 219 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
217 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, 220 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
218 {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, 221 {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
@@ -220,8 +223,9 @@ const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{{
220 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, 223 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
221 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, 224 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
222 {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, 225 {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
223 {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, 226 {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
224 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}}, 227 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
228 {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
225 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, 229 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
226 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, 230 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
227 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, 231 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
@@ -401,16 +405,19 @@ void Config::ApplyDefaultProfileIfInputInvalid() {
401void Config::ReadAudioValues() { 405void Config::ReadAudioValues() {
402 qt_config->beginGroup(QStringLiteral("Audio")); 406 qt_config->beginGroup(QStringLiteral("Audio"));
403 407
404 Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto")) 408 if (global) {
405 .toString() 409 Settings::values.sink_id =
406 .toStdString(); 410 ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
407 Settings::values.enable_audio_stretching = 411 .toString()
408 ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool(); 412 .toStdString();
409 Settings::values.audio_device_id = 413 Settings::values.audio_device_id =
410 ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto")) 414 ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
411 .toString() 415 .toString()
412 .toStdString(); 416 .toStdString();
413 Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat(); 417 }
418 ReadSettingGlobal(Settings::values.enable_audio_stretching,
419 QStringLiteral("enable_audio_stretching"), true);
420 ReadSettingGlobal(Settings::values.volume, QStringLiteral("volume"), 1);
414 421
415 qt_config->endGroup(); 422 qt_config->endGroup();
416} 423}
@@ -439,6 +446,8 @@ void Config::ReadControlValues() {
439 .toInt()); 446 .toInt());
440 Settings::values.udp_pad_index = 447 Settings::values.udp_pad_index =
441 static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt()); 448 static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt());
449 Settings::values.use_docked_mode =
450 ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
442 451
443 qt_config->endGroup(); 452 qt_config->endGroup();
444} 453}
@@ -446,7 +455,7 @@ void Config::ReadControlValues() {
446void Config::ReadCoreValues() { 455void Config::ReadCoreValues() {
447 qt_config->beginGroup(QStringLiteral("Core")); 456 qt_config->beginGroup(QStringLiteral("Core"));
448 457
449 Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool(); 458 ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), false);
450 459
451 qt_config->endGroup(); 460 qt_config->endGroup();
452} 461}
@@ -533,6 +542,8 @@ void Config::ReadDebuggingValues() {
533 Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool(); 542 Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool();
534 Settings::values.disable_cpu_opt = 543 Settings::values.disable_cpu_opt =
535 ReadSetting(QStringLiteral("disable_cpu_opt"), false).toBool(); 544 ReadSetting(QStringLiteral("disable_cpu_opt"), false).toBool();
545 Settings::values.disable_macro_jit =
546 ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool();
536 547
537 qt_config->endGroup(); 548 qt_config->endGroup();
538} 549}
@@ -625,34 +636,28 @@ void Config::ReadPathValues() {
625void Config::ReadRendererValues() { 636void Config::ReadRendererValues() {
626 qt_config->beginGroup(QStringLiteral("Renderer")); 637 qt_config->beginGroup(QStringLiteral("Renderer"));
627 638
628 Settings::values.renderer_backend = 639 ReadSettingGlobal(Settings::values.renderer_backend, QStringLiteral("backend"), 0);
629 static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt()); 640 ReadSettingGlobal(Settings::values.renderer_debug, QStringLiteral("debug"), false);
630 Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool(); 641 ReadSettingGlobal(Settings::values.vulkan_device, QStringLiteral("vulkan_device"), 0);
631 Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt(); 642 ReadSettingGlobal(Settings::values.aspect_ratio, QStringLiteral("aspect_ratio"), 0);
632 Settings::values.resolution_factor = 643 ReadSettingGlobal(Settings::values.max_anisotropy, QStringLiteral("max_anisotropy"), 0);
633 ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); 644 ReadSettingGlobal(Settings::values.use_frame_limit, QStringLiteral("use_frame_limit"), true);
634 Settings::values.aspect_ratio = ReadSetting(QStringLiteral("aspect_ratio"), 0).toInt(); 645 ReadSettingGlobal(Settings::values.frame_limit, QStringLiteral("frame_limit"), 100);
635 Settings::values.max_anisotropy = ReadSetting(QStringLiteral("max_anisotropy"), 0).toInt(); 646 ReadSettingGlobal(Settings::values.use_disk_shader_cache,
636 Settings::values.use_frame_limit = 647 QStringLiteral("use_disk_shader_cache"), true);
637 ReadSetting(QStringLiteral("use_frame_limit"), true).toBool(); 648 ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0);
638 Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt(); 649 ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation,
639 Settings::values.use_disk_shader_cache = 650 QStringLiteral("use_asynchronous_gpu_emulation"), false);
640 ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool(); 651 ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
641 const int gpu_accuracy_level = ReadSetting(QStringLiteral("gpu_accuracy"), 0).toInt(); 652 ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
642 Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); 653 false);
643 Settings::values.use_asynchronous_gpu_emulation = 654 ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
644 ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); 655 true);
645 Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool(); 656 ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false);
646 Settings::values.use_assembly_shaders = 657
647 ReadSetting(QStringLiteral("use_assembly_shaders"), false).toBool(); 658 ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0);
648 Settings::values.use_fast_gpu_time = 659 ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0);
649 ReadSetting(QStringLiteral("use_fast_gpu_time"), true).toBool(); 660 ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0);
650 Settings::values.force_30fps_mode =
651 ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool();
652
653 Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat();
654 Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat();
655 Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat();
656 661
657 qt_config->endGroup(); 662 qt_config->endGroup();
658} 663}
@@ -664,11 +669,13 @@ void Config::ReadShortcutValues() {
664 const auto& [keyseq, context] = shortcut; 669 const auto& [keyseq, context] = shortcut;
665 qt_config->beginGroup(group); 670 qt_config->beginGroup(group);
666 qt_config->beginGroup(name); 671 qt_config->beginGroup(name);
672 // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1
673 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
674 // a file dialog in windowed mode
667 UISettings::values.shortcuts.push_back( 675 UISettings::values.shortcuts.push_back(
668 {name, 676 {name,
669 group, 677 group,
670 {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), 678 {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}});
671 ReadSetting(QStringLiteral("Context"), context).toInt()}});
672 qt_config->endGroup(); 679 qt_config->endGroup();
673 qt_config->endGroup(); 680 qt_config->endGroup();
674 } 681 }
@@ -679,35 +686,45 @@ void Config::ReadShortcutValues() {
679void Config::ReadSystemValues() { 686void Config::ReadSystemValues() {
680 qt_config->beginGroup(QStringLiteral("System")); 687 qt_config->beginGroup(QStringLiteral("System"));
681 688
682 Settings::values.use_docked_mode = 689 ReadSettingGlobal(Settings::values.current_user, QStringLiteral("current_user"), 0);
683 ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); 690 Settings::values.current_user =
684 691 std::clamp<int>(Settings::values.current_user, 0, Service::Account::MAX_USERS - 1);
685 Settings::values.current_user = std::clamp<int>(
686 ReadSetting(QStringLiteral("current_user"), 0).toInt(), 0, Service::Account::MAX_USERS - 1);
687 692
688 Settings::values.language_index = ReadSetting(QStringLiteral("language_index"), 1).toInt(); 693 ReadSettingGlobal(Settings::values.language_index, QStringLiteral("language_index"), 1);
689 694
690 Settings::values.region_index = ReadSetting(QStringLiteral("region_index"), 1).toInt(); 695 ReadSettingGlobal(Settings::values.region_index, QStringLiteral("region_index"), 1);
691 696
692 Settings::values.time_zone_index = ReadSetting(QStringLiteral("time_zone_index"), 0).toInt(); 697 ReadSettingGlobal(Settings::values.time_zone_index, QStringLiteral("time_zone_index"), 0);
693 698
694 const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool(); 699 bool rng_seed_enabled;
695 if (rng_seed_enabled) { 700 ReadSettingGlobal(rng_seed_enabled, QStringLiteral("rng_seed_enabled"), false);
696 Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong(); 701 bool rng_seed_global =
697 } else { 702 global || qt_config->value(QStringLiteral("rng_seed/use_global"), true).toBool();
698 Settings::values.rng_seed = std::nullopt; 703 Settings::values.rng_seed.SetGlobal(rng_seed_global);
704 if (global || !rng_seed_global) {
705 if (rng_seed_enabled) {
706 Settings::values.rng_seed.SetValue(
707 ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong());
708 } else {
709 Settings::values.rng_seed.SetValue(std::nullopt);
710 }
699 } 711 }
700 712
701 const auto custom_rtc_enabled = 713 bool custom_rtc_enabled;
702 ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool(); 714 ReadSettingGlobal(custom_rtc_enabled, QStringLiteral("custom_rtc_enabled"), false);
703 if (custom_rtc_enabled) { 715 bool custom_rtc_global =
704 Settings::values.custom_rtc = 716 global || qt_config->value(QStringLiteral("custom_rtc/use_global"), true).toBool();
705 std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()); 717 Settings::values.custom_rtc.SetGlobal(custom_rtc_global);
706 } else { 718 if (global || !custom_rtc_global) {
707 Settings::values.custom_rtc = std::nullopt; 719 if (custom_rtc_enabled) {
720 Settings::values.custom_rtc.SetValue(
721 std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()));
722 } else {
723 Settings::values.custom_rtc.SetValue(std::nullopt);
724 }
708 } 725 }
709 726
710 Settings::values.sound_index = ReadSetting(QStringLiteral("sound_index"), 1).toInt(); 727 ReadSettingGlobal(Settings::values.sound_index, QStringLiteral("sound_index"), 1);
711 728
712 qt_config->endGroup(); 729 qt_config->endGroup();
713} 730}
@@ -720,8 +737,6 @@ void Config::ReadUIValues() {
720 .toString(); 737 .toString();
721 UISettings::values.enable_discord_presence = 738 UISettings::values.enable_discord_presence =
722 ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool(); 739 ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool();
723 UISettings::values.screenshot_resolution_factor =
724 static_cast<u16>(ReadSetting(QStringLiteral("screenshot_resolution_factor"), 0).toUInt());
725 UISettings::values.select_user_on_boot = 740 UISettings::values.select_user_on_boot =
726 ReadSetting(QStringLiteral("select_user_on_boot"), false).toBool(); 741 ReadSetting(QStringLiteral("select_user_on_boot"), false).toBool();
727 742
@@ -803,18 +818,20 @@ void Config::ReadWebServiceValues() {
803} 818}
804 819
805void Config::ReadValues() { 820void Config::ReadValues() {
806 ReadControlValues(); 821 if (global) {
822 ReadControlValues();
823 ReadDataStorageValues();
824 ReadDebuggingValues();
825 ReadDisabledAddOnValues();
826 ReadServiceValues();
827 ReadUIValues();
828 ReadWebServiceValues();
829 ReadMiscellaneousValues();
830 }
807 ReadCoreValues(); 831 ReadCoreValues();
808 ReadRendererValues(); 832 ReadRendererValues();
809 ReadAudioValues(); 833 ReadAudioValues();
810 ReadDataStorageValues();
811 ReadSystemValues(); 834 ReadSystemValues();
812 ReadMiscellaneousValues();
813 ReadDebuggingValues();
814 ReadWebServiceValues();
815 ReadServiceValues();
816 ReadDisabledAddOnValues();
817 ReadUIValues();
818} 835}
819 836
820void Config::SavePlayerValues() { 837void Config::SavePlayerValues() {
@@ -901,30 +918,35 @@ void Config::SaveTouchscreenValues() {
901} 918}
902 919
903void Config::SaveValues() { 920void Config::SaveValues() {
904 SaveControlValues(); 921 if (global) {
922 SaveControlValues();
923 SaveDataStorageValues();
924 SaveDebuggingValues();
925 SaveDisabledAddOnValues();
926 SaveServiceValues();
927 SaveUIValues();
928 SaveWebServiceValues();
929 SaveMiscellaneousValues();
930 }
905 SaveCoreValues(); 931 SaveCoreValues();
906 SaveRendererValues(); 932 SaveRendererValues();
907 SaveAudioValues(); 933 SaveAudioValues();
908 SaveDataStorageValues();
909 SaveSystemValues(); 934 SaveSystemValues();
910 SaveMiscellaneousValues();
911 SaveDebuggingValues();
912 SaveWebServiceValues();
913 SaveServiceValues();
914 SaveDisabledAddOnValues();
915 SaveUIValues();
916} 935}
917 936
918void Config::SaveAudioValues() { 937void Config::SaveAudioValues() {
919 qt_config->beginGroup(QStringLiteral("Audio")); 938 qt_config->beginGroup(QStringLiteral("Audio"));
920 939
921 WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id), 940 if (global) {
922 QStringLiteral("auto")); 941 WriteSetting(QStringLiteral("output_engine"),
923 WriteSetting(QStringLiteral("enable_audio_stretching"), 942 QString::fromStdString(Settings::values.sink_id), QStringLiteral("auto"));
924 Settings::values.enable_audio_stretching, true); 943 WriteSetting(QStringLiteral("output_device"),
925 WriteSetting(QStringLiteral("output_device"), 944 QString::fromStdString(Settings::values.audio_device_id),
926 QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto")); 945 QStringLiteral("auto"));
927 WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f); 946 }
947 WriteSettingGlobal(QStringLiteral("enable_audio_stretching"),
948 Settings::values.enable_audio_stretching, true);
949 WriteSettingGlobal(QStringLiteral("volume"), Settings::values.volume, 1.0f);
928 950
929 qt_config->endGroup(); 951 qt_config->endGroup();
930} 952}
@@ -947,6 +969,7 @@ void Config::SaveControlValues() {
947 WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port, 969 WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port,
948 InputCommon::CemuhookUDP::DEFAULT_PORT); 970 InputCommon::CemuhookUDP::DEFAULT_PORT);
949 WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0); 971 WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0);
972 WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
950 973
951 qt_config->endGroup(); 974 qt_config->endGroup();
952} 975}
@@ -954,7 +977,7 @@ void Config::SaveControlValues() {
954void Config::SaveCoreValues() { 977void Config::SaveCoreValues() {
955 qt_config->beginGroup(QStringLiteral("Core")); 978 qt_config->beginGroup(QStringLiteral("Core"));
956 979
957 WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false); 980 WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
958 981
959 qt_config->endGroup(); 982 qt_config->endGroup();
960} 983}
@@ -1011,6 +1034,7 @@ void Config::SaveDebuggingValues() {
1011 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); 1034 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
1012 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); 1035 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
1013 WriteSetting(QStringLiteral("disable_cpu_opt"), Settings::values.disable_cpu_opt, false); 1036 WriteSetting(QStringLiteral("disable_cpu_opt"), Settings::values.disable_cpu_opt, false);
1037 WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false);
1014 1038
1015 qt_config->endGroup(); 1039 qt_config->endGroup();
1016} 1040}
@@ -1076,31 +1100,34 @@ void Config::SavePathValues() {
1076void Config::SaveRendererValues() { 1100void Config::SaveRendererValues() {
1077 qt_config->beginGroup(QStringLiteral("Renderer")); 1101 qt_config->beginGroup(QStringLiteral("Renderer"));
1078 1102
1079 WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0); 1103 WriteSettingGlobal(QStringLiteral("backend"),
1104 static_cast<int>(Settings::values.renderer_backend.GetValue(global)),
1105 Settings::values.renderer_backend.UsingGlobal(), 0);
1080 WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false); 1106 WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false);
1081 WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); 1107 WriteSettingGlobal(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
1082 WriteSetting(QStringLiteral("resolution_factor"), 1108 WriteSettingGlobal(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
1083 static_cast<double>(Settings::values.resolution_factor), 1.0); 1109 WriteSettingGlobal(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
1084 WriteSetting(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0); 1110 WriteSettingGlobal(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
1085 WriteSetting(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0); 1111 WriteSettingGlobal(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
1086 WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); 1112 WriteSettingGlobal(QStringLiteral("use_disk_shader_cache"),
1087 WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); 1113 Settings::values.use_disk_shader_cache, true);
1088 WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, 1114 WriteSettingGlobal(QStringLiteral("gpu_accuracy"),
1089 true); 1115 static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)),
1090 WriteSetting(QStringLiteral("gpu_accuracy"), static_cast<int>(Settings::values.gpu_accuracy), 1116 Settings::values.gpu_accuracy.UsingGlobal(), 0);
1091 0); 1117 WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"),
1092 WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), 1118 Settings::values.use_asynchronous_gpu_emulation, false);
1093 Settings::values.use_asynchronous_gpu_emulation, false); 1119 WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
1094 WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); 1120 WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
1095 WriteSetting(QStringLiteral("use_assembly_shaders"), Settings::values.use_assembly_shaders, 1121 Settings::values.use_assembly_shaders, false);
1096 false); 1122 WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
1097 WriteSetting(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, true); 1123 true);
1098 WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); 1124 WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode,
1125 false);
1099 1126
1100 // Cast to double because Qt's written float values are not human-readable 1127 // Cast to double because Qt's written float values are not human-readable
1101 WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0); 1128 WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0);
1102 WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0); 1129 WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0);
1103 WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0); 1130 WriteSettingGlobal(QStringLiteral("bg_blue"), Settings::values.bg_blue, 0.0);
1104 1131
1105 qt_config->endGroup(); 1132 qt_config->endGroup();
1106} 1133}
@@ -1128,23 +1155,28 @@ void Config::SaveShortcutValues() {
1128void Config::SaveSystemValues() { 1155void Config::SaveSystemValues() {
1129 qt_config->beginGroup(QStringLiteral("System")); 1156 qt_config->beginGroup(QStringLiteral("System"));
1130 1157
1131 WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
1132 WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0); 1158 WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0);
1133 WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1); 1159 WriteSettingGlobal(QStringLiteral("language_index"), Settings::values.language_index, 1);
1134 WriteSetting(QStringLiteral("region_index"), Settings::values.region_index, 1); 1160 WriteSettingGlobal(QStringLiteral("region_index"), Settings::values.region_index, 1);
1135 WriteSetting(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0); 1161 WriteSettingGlobal(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
1136 1162
1137 WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false); 1163 WriteSettingGlobal(QStringLiteral("rng_seed_enabled"),
1138 WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0); 1164 Settings::values.rng_seed.GetValue(global).has_value(),
1139 1165 Settings::values.rng_seed.UsingGlobal(), false);
1140 WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(), 1166 WriteSettingGlobal(QStringLiteral("rng_seed"),
1141 false); 1167 Settings::values.rng_seed.GetValue(global).value_or(0),
1142 WriteSetting(QStringLiteral("custom_rtc"), 1168 Settings::values.rng_seed.UsingGlobal(), 0);
1143 QVariant::fromValue<long long>( 1169
1144 Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()), 1170 WriteSettingGlobal(QStringLiteral("custom_rtc_enabled"),
1145 0); 1171 Settings::values.custom_rtc.GetValue(global).has_value(),
1146 1172 Settings::values.custom_rtc.UsingGlobal(), false);
1147 WriteSetting(QStringLiteral("sound_index"), Settings::values.sound_index, 1); 1173 WriteSettingGlobal(
1174 QStringLiteral("custom_rtc"),
1175 QVariant::fromValue<long long>(
1176 Settings::values.custom_rtc.GetValue(global).value_or(std::chrono::seconds{}).count()),
1177 Settings::values.custom_rtc.UsingGlobal(), 0);
1178
1179 WriteSettingGlobal(QStringLiteral("sound_index"), Settings::values.sound_index, 1);
1148 1180
1149 qt_config->endGroup(); 1181 qt_config->endGroup();
1150} 1182}
@@ -1156,8 +1188,6 @@ void Config::SaveUIValues() {
1156 QString::fromUtf8(UISettings::themes[0].second)); 1188 QString::fromUtf8(UISettings::themes[0].second));
1157 WriteSetting(QStringLiteral("enable_discord_presence"), 1189 WriteSetting(QStringLiteral("enable_discord_presence"),
1158 UISettings::values.enable_discord_presence, true); 1190 UISettings::values.enable_discord_presence, true);
1159 WriteSetting(QStringLiteral("screenshot_resolution_factor"),
1160 UISettings::values.screenshot_resolution_factor, 0);
1161 WriteSetting(QStringLiteral("select_user_on_boot"), UISettings::values.select_user_on_boot, 1191 WriteSetting(QStringLiteral("select_user_on_boot"), UISettings::values.select_user_on_boot,
1162 false); 1192 false);
1163 1193
@@ -1238,6 +1268,34 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)
1238 return result; 1268 return result;
1239} 1269}
1240 1270
1271template <typename Type>
1272void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name) {
1273 const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
1274 setting.SetGlobal(use_global);
1275 if (global || !use_global) {
1276 setting.SetValue(ReadSetting(name).value<Type>());
1277 }
1278}
1279
1280template <typename Type>
1281void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
1282 const QVariant& default_value) {
1283 const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
1284 setting.SetGlobal(use_global);
1285 if (global || !use_global) {
1286 setting.SetValue(ReadSetting(name, default_value).value<Type>());
1287 }
1288}
1289
1290template <typename Type>
1291void Config::ReadSettingGlobal(Type& setting, const QString& name,
1292 const QVariant& default_value) const {
1293 const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
1294 if (global || !use_global) {
1295 setting = ReadSetting(name, default_value).value<Type>();
1296 }
1297}
1298
1241void Config::WriteSetting(const QString& name, const QVariant& value) { 1299void Config::WriteSetting(const QString& name, const QVariant& value) {
1242 qt_config->setValue(name, value); 1300 qt_config->setValue(name, value);
1243} 1301}
@@ -1248,6 +1306,40 @@ void Config::WriteSetting(const QString& name, const QVariant& value,
1248 qt_config->setValue(name, value); 1306 qt_config->setValue(name, value);
1249} 1307}
1250 1308
1309template <typename Type>
1310void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting) {
1311 if (!global) {
1312 qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
1313 }
1314 if (global || !setting.UsingGlobal()) {
1315 qt_config->setValue(name, setting.GetValue(global));
1316 }
1317}
1318
1319template <typename Type>
1320void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
1321 const QVariant& default_value) {
1322 if (!global) {
1323 qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
1324 }
1325 if (global || !setting.UsingGlobal()) {
1326 qt_config->setValue(name + QStringLiteral("/default"),
1327 setting.GetValue(global) == default_value.value<Type>());
1328 qt_config->setValue(name, setting.GetValue(global));
1329 }
1330}
1331
1332void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
1333 const QVariant& default_value) {
1334 if (!global) {
1335 qt_config->setValue(name + QStringLiteral("/use_global"), use_global);
1336 }
1337 if (global || !use_global) {
1338 qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
1339 qt_config->setValue(name, value);
1340 }
1341}
1342
1251void Config::Reload() { 1343void Config::Reload() {
1252 ReadValues(); 1344 ReadValues();
1253 // To apply default value changes 1345 // To apply default value changes
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 5cd2a5feb..681f0bca5 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include <QMetaType>
10#include <QVariant> 11#include <QVariant>
11#include "core/settings.h" 12#include "core/settings.h"
12#include "yuzu/uisettings.h" 13#include "yuzu/uisettings.h"
@@ -15,7 +16,7 @@ class QSettings;
15 16
16class Config { 17class Config {
17public: 18public:
18 Config(); 19 explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true);
19 ~Config(); 20 ~Config();
20 21
21 void Reload(); 22 void Reload();
@@ -27,7 +28,7 @@ public:
27 default_mouse_buttons; 28 default_mouse_buttons;
28 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; 29 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
29 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; 30 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
30 static const std::array<UISettings::Shortcut, 15> default_hotkeys; 31 static const std::array<UISettings::Shortcut, 16> default_hotkeys;
31 32
32private: 33private:
33 void ReadValues(); 34 void ReadValues();
@@ -82,9 +83,33 @@ private:
82 83
83 QVariant ReadSetting(const QString& name) const; 84 QVariant ReadSetting(const QString& name) const;
84 QVariant ReadSetting(const QString& name, const QVariant& default_value) const; 85 QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
86 // Templated ReadSettingGlobal functions will also look for the use_global setting and set
87 // both the value and the global state properly
88 template <typename Type>
89 void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name);
90 template <typename Type>
91 void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
92 const QVariant& default_value);
93 template <typename Type>
94 void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const;
95 // Templated WriteSettingGlobal functions will also write the global state if needed and will
96 // skip writing the actual setting if it defers to the global value
85 void WriteSetting(const QString& name, const QVariant& value); 97 void WriteSetting(const QString& name, const QVariant& value);
86 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); 98 void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
99 template <typename Type>
100 void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting);
101 template <typename Type>
102 void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
103 const QVariant& default_value);
104 void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
105 const QVariant& default_value);
87 106
88 std::unique_ptr<QSettings> qt_config; 107 std::unique_ptr<QSettings> qt_config;
89 std::string qt_config_loc; 108 std::string qt_config_loc;
109
110 bool global;
90}; 111};
112
113// These metatype declarations cannot be in core/settings.h because core is devoid of QT
114Q_DECLARE_METATYPE(Settings::RendererBackend);
115Q_DECLARE_METATYPE(Settings::GPUAccuracy);
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp
new file mode 100644
index 000000000..bb47c3933
--- /dev/null
+++ b/src/yuzu/configuration/configuration_shared.cpp
@@ -0,0 +1,76 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QCheckBox>
6#include <QComboBox>
7#include "core/settings.h"
8#include "yuzu/configuration/configuration_shared.h"
9#include "yuzu/configuration/configure_per_game.h"
10
11void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting,
12 const QCheckBox* checkbox) {
13 if (checkbox->checkState() == Qt::PartiallyChecked) {
14 setting->SetGlobal(true);
15 } else {
16 setting->SetGlobal(false);
17 setting->SetValue(checkbox->checkState() == Qt::Checked);
18 }
19}
20
21void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<int>* setting,
22 const QComboBox* combobox) {
23 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
24 setting->SetGlobal(true);
25 } else {
26 setting->SetGlobal(false);
27 setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET);
28 }
29}
30
31void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
32 const QComboBox* combobox) {
33 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
34 setting->SetGlobal(true);
35 } else {
36 setting->SetGlobal(false);
37 setting->SetValue(static_cast<Settings::RendererBackend>(
38 combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET));
39 }
40}
41
42void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
43 const Settings::Setting<bool>* setting) {
44 if (setting->UsingGlobal()) {
45 checkbox->setCheckState(Qt::PartiallyChecked);
46 } else {
47 checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
48 }
49}
50
51void ConfigurationShared::SetPerGameSetting(QComboBox* combobox,
52 const Settings::Setting<int>* setting) {
53 combobox->setCurrentIndex(setting->UsingGlobal()
54 ? ConfigurationShared::USE_GLOBAL_INDEX
55 : setting->GetValue() + ConfigurationShared::USE_GLOBAL_OFFSET);
56}
57
58void ConfigurationShared::SetPerGameSetting(
59 QComboBox* combobox, const Settings::Setting<Settings::RendererBackend>* setting) {
60 combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
61 : static_cast<int>(setting->GetValue()) +
62 ConfigurationShared::USE_GLOBAL_OFFSET);
63}
64
65void ConfigurationShared::SetPerGameSetting(
66 QComboBox* combobox, const Settings::Setting<Settings::GPUAccuracy>* setting) {
67 combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
68 : static_cast<int>(setting->GetValue()) +
69 ConfigurationShared::USE_GLOBAL_OFFSET);
70}
71
72void ConfigurationShared::InsertGlobalItem(QComboBox* combobox) {
73 const QString use_global_text = ConfigurePerGame::tr("Use global configuration");
74 combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
75 combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
76}
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
new file mode 100644
index 000000000..b11b1b950
--- /dev/null
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -0,0 +1,36 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QCheckBox>
8#include <QComboBox>
9#include <QString>
10#include "core/settings.h"
11
12namespace ConfigurationShared {
13
14constexpr int USE_GLOBAL_INDEX = 0;
15constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1;
16constexpr int USE_GLOBAL_OFFSET = 2;
17
18// Global-aware apply and set functions
19
20void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox);
21void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox);
22void ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
23 const QComboBox* combobox);
24void ApplyPerGameSetting(Settings::Setting<Settings::GPUAccuracy>* setting,
25 const QComboBox* combobox);
26
27void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting);
28void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<int>* setting);
29void SetPerGameSetting(QComboBox* combobox,
30 const Settings::Setting<Settings::RendererBackend>* setting);
31void SetPerGameSetting(QComboBox* combobox,
32 const Settings::Setting<Settings::GPUAccuracy>* setting);
33
34void InsertGlobalItem(QComboBox* combobox);
35
36} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index f370c690f..cc021beec 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -11,6 +11,7 @@
11#include "core/core.h" 11#include "core/core.h"
12#include "core/settings.h" 12#include "core/settings.h"
13#include "ui_configure_audio.h" 13#include "ui_configure_audio.h"
14#include "yuzu/configuration/configuration_shared.h"
14#include "yuzu/configuration/configure_audio.h" 15#include "yuzu/configuration/configure_audio.h"
15 16
16ConfigureAudio::ConfigureAudio(QWidget* parent) 17ConfigureAudio::ConfigureAudio(QWidget* parent)
@@ -24,6 +25,11 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
24 connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this, 25 connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
25 &ConfigureAudio::UpdateAudioDevices); 26 &ConfigureAudio::UpdateAudioDevices);
26 27
28 ui->volume_label->setVisible(Settings::configuring_global);
29 ui->volume_combo_box->setVisible(!Settings::configuring_global);
30
31 SetupPerGameUI();
32
27 SetConfiguration(); 33 SetConfiguration();
28 34
29 const bool is_powered_on = Core::System::GetInstance().IsPoweredOn(); 35 const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
@@ -41,8 +47,22 @@ void ConfigureAudio::SetConfiguration() {
41 47
42 SetAudioDeviceFromDeviceID(); 48 SetAudioDeviceFromDeviceID();
43 49
44 ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); 50 ui->volume_slider->setValue(Settings::values.volume.GetValue() * ui->volume_slider->maximum());
45 ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum()); 51
52 if (Settings::configuring_global) {
53 ui->toggle_audio_stretching->setChecked(
54 Settings::values.enable_audio_stretching.GetValue());
55 } else {
56 ConfigurationShared::SetPerGameSetting(ui->toggle_audio_stretching,
57 &Settings::values.enable_audio_stretching);
58 if (Settings::values.volume.UsingGlobal()) {
59 ui->volume_combo_box->setCurrentIndex(0);
60 ui->volume_slider->setEnabled(false);
61 } else {
62 ui->volume_combo_box->setCurrentIndex(1);
63 ui->volume_slider->setEnabled(true);
64 }
65 }
46 SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); 66 SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
47} 67}
48 68
@@ -80,15 +100,36 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
80} 100}
81 101
82void ConfigureAudio::ApplyConfiguration() { 102void ConfigureAudio::ApplyConfiguration() {
83 Settings::values.sink_id = 103 if (Settings::configuring_global) {
84 ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) 104 Settings::values.sink_id =
85 .toStdString(); 105 ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
86 Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked(); 106 .toStdString();
87 Settings::values.audio_device_id = 107 Settings::values.audio_device_id =
88 ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) 108 ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
89 .toStdString(); 109 .toStdString();
90 Settings::values.volume = 110
91 static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum(); 111 // Guard if during game and set to game-specific value
112 if (Settings::values.enable_audio_stretching.UsingGlobal()) {
113 Settings::values.enable_audio_stretching.SetValue(
114 ui->toggle_audio_stretching->isChecked());
115 }
116 if (Settings::values.volume.UsingGlobal()) {
117 Settings::values.volume.SetValue(
118 static_cast<float>(ui->volume_slider->sliderPosition()) /
119 ui->volume_slider->maximum());
120 }
121 } else {
122 ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching,
123 ui->toggle_audio_stretching);
124 if (ui->volume_combo_box->currentIndex() == 0) {
125 Settings::values.volume.SetGlobal(true);
126 } else {
127 Settings::values.volume.SetGlobal(false);
128 Settings::values.volume.SetValue(
129 static_cast<float>(ui->volume_slider->sliderPosition()) /
130 ui->volume_slider->maximum());
131 }
132 }
92} 133}
93 134
94void ConfigureAudio::changeEvent(QEvent* event) { 135void ConfigureAudio::changeEvent(QEvent* event) {
@@ -122,3 +163,22 @@ void ConfigureAudio::RetranslateUI() {
122 ui->retranslateUi(this); 163 ui->retranslateUi(this);
123 SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); 164 SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
124} 165}
166
167void ConfigureAudio::SetupPerGameUI() {
168 if (Settings::configuring_global) {
169 ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
170 ui->toggle_audio_stretching->setEnabled(
171 Settings::values.enable_audio_stretching.UsingGlobal());
172
173 return;
174 }
175
176 ui->toggle_audio_stretching->setTristate(true);
177 connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
178 this, [this](int index) { ui->volume_slider->setEnabled(index == 1); });
179
180 ui->output_sink_combo_box->setVisible(false);
181 ui->output_sink_label->setVisible(false);
182 ui->audio_device_combo_box->setVisible(false);
183 ui->audio_device_label->setVisible(false);
184}
diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h
index ea83bd72d..d84f4a682 100644
--- a/src/yuzu/configuration/configure_audio.h
+++ b/src/yuzu/configuration/configure_audio.h
@@ -34,5 +34,7 @@ private:
34 void SetAudioDeviceFromDeviceID(); 34 void SetAudioDeviceFromDeviceID();
35 void SetVolumeIndicatorText(int percentage); 35 void SetVolumeIndicatorText(int percentage);
36 36
37 void SetupPerGameUI();
38
37 std::unique_ptr<Ui::ConfigureAudio> ui; 39 std::unique_ptr<Ui::ConfigureAudio> ui;
38}; 40};
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui
index a098b9acc..862ccb988 100644
--- a/src/yuzu/configuration/configure_audio.ui
+++ b/src/yuzu/configuration/configure_audio.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>188</width> 9 <width>367</width>
10 <height>246</height> 10 <height>368</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <layout class="QVBoxLayout"> 13 <layout class="QVBoxLayout">
@@ -18,9 +18,9 @@
18 </property> 18 </property>
19 <layout class="QVBoxLayout"> 19 <layout class="QVBoxLayout">
20 <item> 20 <item>
21 <layout class="QHBoxLayout"> 21 <layout class="QHBoxLayout" name="_3">
22 <item> 22 <item>
23 <widget class="QLabel" name="label_1"> 23 <widget class="QLabel" name="output_sink_label">
24 <property name="text"> 24 <property name="text">
25 <string>Output Engine:</string> 25 <string>Output Engine:</string>
26 </property> 26 </property>
@@ -31,20 +31,20 @@
31 </item> 31 </item>
32 </layout> 32 </layout>
33 </item> 33 </item>
34 <item>
35 <widget class="QCheckBox" name="toggle_audio_stretching">
36 <property name="toolTip">
37 <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
38 </property>
39 <property name="text">
40 <string>Enable audio stretching</string>
41 </property>
42 </widget>
43 </item>
44 <item> 34 <item>
45 <layout class="QHBoxLayout"> 35 <widget class="QCheckBox" name="toggle_audio_stretching">
36 <property name="toolTip">
37 <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
38 </property>
39 <property name="text">
40 <string>Enable audio stretching</string>
41 </property>
42 </widget>
43 </item>
44 <item>
45 <layout class="QHBoxLayout" name="_2">
46 <item> 46 <item>
47 <widget class="QLabel" name="label_2"> 47 <widget class="QLabel" name="audio_device_label">
48 <property name="text"> 48 <property name="text">
49 <string>Audio Device:</string> 49 <string>Audio Device:</string>
50 </property> 50 </property>
@@ -61,7 +61,21 @@
61 <number>0</number> 61 <number>0</number>
62 </property> 62 </property>
63 <item> 63 <item>
64 <widget class="QLabel" name="label_3"> 64 <widget class="QComboBox" name="volume_combo_box">
65 <item>
66 <property name="text">
67 <string>Use global volume</string>
68 </property>
69 </item>
70 <item>
71 <property name="text">
72 <string>Set volume:</string>
73 </property>
74 </item>
75 </widget>
76 </item>
77 <item>
78 <widget class="QLabel" name="volume_label">
65 <property name="text"> 79 <property name="text">
66 <string>Volume:</string> 80 <string>Volume:</string>
67 </property> 81 </property>
@@ -74,7 +88,7 @@
74 </property> 88 </property>
75 <property name="sizeHint" stdset="0"> 89 <property name="sizeHint" stdset="0">
76 <size> 90 <size>
77 <width>40</width> 91 <width>30</width>
78 <height>20</height> 92 <height>20</height>
79 </size> 93 </size>
80 </property> 94 </property>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index c2026763e..2c77441fd 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -39,6 +39,8 @@ void ConfigureDebug::SetConfiguration() {
39 ui->disable_cpu_opt->setChecked(Settings::values.disable_cpu_opt); 39 ui->disable_cpu_opt->setChecked(Settings::values.disable_cpu_opt);
40 ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 40 ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn());
41 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); 41 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
42 ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
43 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit);
42} 44}
43 45
44void ConfigureDebug::ApplyConfiguration() { 46void ConfigureDebug::ApplyConfiguration() {
@@ -51,6 +53,7 @@ void ConfigureDebug::ApplyConfiguration() {
51 Settings::values.quest_flag = ui->quest_flag->isChecked(); 53 Settings::values.quest_flag = ui->quest_flag->isChecked();
52 Settings::values.disable_cpu_opt = ui->disable_cpu_opt->isChecked(); 54 Settings::values.disable_cpu_opt = ui->disable_cpu_opt->isChecked();
53 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); 55 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
56 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
54 Debugger::ToggleConsole(); 57 Debugger::ToggleConsole();
55 Log::Filter filter; 58 Log::Filter filter;
56 filter.ParseFilterString(Settings::values.log_filter); 59 filter.ParseFilterString(Settings::values.log_filter);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index e0d4c4a44..46f0208c6 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -148,6 +148,19 @@
148 </property> 148 </property>
149 </widget> 149 </widget>
150 </item> 150 </item>
151 <item>
152 <widget class="QCheckBox" name="disable_macro_jit">
153 <property name="enabled">
154 <bool>true</bool>
155 </property>
156 <property name="whatsThis">
157 <string>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</string>
158 </property>
159 <property name="text">
160 <string>Disable Macro JIT</string>
161 </property>
162 </widget>
163 </item>
151 </layout> 164 </layout>
152 </widget> 165 </widget>
153 </item> 166 </item>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index df4473b46..5918e9972 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -14,6 +14,8 @@
14 14
15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) 15ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
16 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { 16 : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
17 Settings::configuring_global = true;
18
17 ui->setupUi(this); 19 ui->setupUi(this);
18 ui->hotkeysTab->Populate(registry); 20 ui->hotkeysTab->Populate(registry);
19 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); 21 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index cb95423e0..1fb62d1cf 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -7,40 +7,79 @@
7#include "core/core.h" 7#include "core/core.h"
8#include "core/settings.h" 8#include "core/settings.h"
9#include "ui_configure_general.h" 9#include "ui_configure_general.h"
10#include "yuzu/configuration/configuration_shared.h"
10#include "yuzu/configuration/configure_general.h" 11#include "yuzu/configuration/configure_general.h"
11#include "yuzu/uisettings.h" 12#include "yuzu/uisettings.h"
12 13
13ConfigureGeneral::ConfigureGeneral(QWidget* parent) 14ConfigureGeneral::ConfigureGeneral(QWidget* parent)
14 : QWidget(parent), ui(new Ui::ConfigureGeneral) { 15 : QWidget(parent), ui(new Ui::ConfigureGeneral) {
15
16 ui->setupUi(this); 16 ui->setupUi(this);
17 17
18 SetupPerGameUI();
19
18 SetConfiguration(); 20 SetConfiguration();
19 21
20 connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled); 22 connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, [this]() {
23 ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked);
24 });
21} 25}
22 26
23ConfigureGeneral::~ConfigureGeneral() = default; 27ConfigureGeneral::~ConfigureGeneral() = default;
24 28
25void ConfigureGeneral::SetConfiguration() { 29void ConfigureGeneral::SetConfiguration() {
30 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
31
32 ui->use_multi_core->setEnabled(runtime_lock);
33 ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
34
26 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); 35 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
27 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); 36 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
28 ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); 37 ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
29 ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse); 38 ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse);
30 39
31 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); 40 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
32 ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); 41 ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
33 ui->frame_limit->setValue(Settings::values.frame_limit); 42
43 if (!Settings::configuring_global) {
44 if (Settings::values.use_multi_core.UsingGlobal()) {
45 ui->use_multi_core->setCheckState(Qt::PartiallyChecked);
46 }
47 if (Settings::values.use_frame_limit.UsingGlobal()) {
48 ui->toggle_frame_limit->setCheckState(Qt::PartiallyChecked);
49 }
50 }
51
52 ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked &&
53 ui->toggle_frame_limit->isEnabled());
34} 54}
35 55
36void ConfigureGeneral::ApplyConfiguration() { 56void ConfigureGeneral::ApplyConfiguration() {
37 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 57 if (Settings::configuring_global) {
38 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); 58 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
39 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); 59 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
40 UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); 60 UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
61 UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
62
63 // Guard if during game and set to game-specific value
64 if (Settings::values.use_frame_limit.UsingGlobal()) {
65 Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
66 Qt::Checked);
67 Settings::values.frame_limit.SetValue(ui->frame_limit->value());
68 Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked());
69 }
70 } else {
71 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core,
72 ui->use_multi_core);
41 73
42 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); 74 bool global_frame_limit = ui->toggle_frame_limit->checkState() == Qt::PartiallyChecked;
43 Settings::values.frame_limit = ui->frame_limit->value(); 75 Settings::values.use_frame_limit.SetGlobal(global_frame_limit);
76 Settings::values.frame_limit.SetGlobal(global_frame_limit);
77 if (!global_frame_limit) {
78 Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
79 Qt::Checked);
80 Settings::values.frame_limit.SetValue(ui->frame_limit->value());
81 }
82 }
44} 83}
45 84
46void ConfigureGeneral::changeEvent(QEvent* event) { 85void ConfigureGeneral::changeEvent(QEvent* event) {
@@ -54,3 +93,20 @@ void ConfigureGeneral::changeEvent(QEvent* event) {
54void ConfigureGeneral::RetranslateUI() { 93void ConfigureGeneral::RetranslateUI() {
55 ui->retranslateUi(this); 94 ui->retranslateUi(this);
56} 95}
96
97void ConfigureGeneral::SetupPerGameUI() {
98 if (Settings::configuring_global) {
99 ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal());
100 ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
101
102 return;
103 }
104
105 ui->toggle_check_exit->setVisible(false);
106 ui->toggle_user_on_boot->setVisible(false);
107 ui->toggle_background_pause->setVisible(false);
108 ui->toggle_hide_mouse->setVisible(false);
109
110 ui->toggle_frame_limit->setTristate(true);
111 ui->use_multi_core->setTristate(true);
112}
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index ef05ce065..9c785c22e 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -28,5 +28,7 @@ private:
28 28
29 void SetConfiguration(); 29 void SetConfiguration();
30 30
31 void SetupPerGameUI();
32
31 std::unique_ptr<Ui::ConfigureGeneral> ui; 33 std::unique_ptr<Ui::ConfigureGeneral> ui;
32}; 34};
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index fc3b7e65a..2711116a2 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -52,6 +52,13 @@
52 </layout> 52 </layout>
53 </item> 53 </item>
54 <item> 54 <item>
55 <widget class="QCheckBox" name="use_multi_core">
56 <property name="text">
57 <string>Multicore CPU Emulation</string>
58 </property>
59 </widget>
60 </item>
61 <item>
55 <widget class="QCheckBox" name="toggle_check_exit"> 62 <widget class="QCheckBox" name="toggle_check_exit">
56 <property name="text"> 63 <property name="text">
57 <string>Confirm exit while emulation is running</string> 64 <string>Confirm exit while emulation is running</string>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index ea667caef..cb4706bd6 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -13,65 +13,27 @@
13#include "core/core.h" 13#include "core/core.h"
14#include "core/settings.h" 14#include "core/settings.h"
15#include "ui_configure_graphics.h" 15#include "ui_configure_graphics.h"
16#include "yuzu/configuration/configuration_shared.h"
16#include "yuzu/configuration/configure_graphics.h" 17#include "yuzu/configuration/configure_graphics.h"
17 18
18#ifdef HAS_VULKAN 19#ifdef HAS_VULKAN
19#include "video_core/renderer_vulkan/renderer_vulkan.h" 20#include "video_core/renderer_vulkan/renderer_vulkan.h"
20#endif 21#endif
21 22
22namespace {
23enum class Resolution : int {
24 Auto,
25 Scale1x,
26 Scale2x,
27 Scale3x,
28 Scale4x,
29};
30
31float ToResolutionFactor(Resolution option) {
32 switch (option) {
33 case Resolution::Auto:
34 return 0.f;
35 case Resolution::Scale1x:
36 return 1.f;
37 case Resolution::Scale2x:
38 return 2.f;
39 case Resolution::Scale3x:
40 return 3.f;
41 case Resolution::Scale4x:
42 return 4.f;
43 }
44 return 0.f;
45}
46
47Resolution FromResolutionFactor(float factor) {
48 if (factor == 0.f) {
49 return Resolution::Auto;
50 } else if (factor == 1.f) {
51 return Resolution::Scale1x;
52 } else if (factor == 2.f) {
53 return Resolution::Scale2x;
54 } else if (factor == 3.f) {
55 return Resolution::Scale3x;
56 } else if (factor == 4.f) {
57 return Resolution::Scale4x;
58 }
59 return Resolution::Auto;
60}
61} // Anonymous namespace
62
63ConfigureGraphics::ConfigureGraphics(QWidget* parent) 23ConfigureGraphics::ConfigureGraphics(QWidget* parent)
64 : QWidget(parent), ui(new Ui::ConfigureGraphics) { 24 : QWidget(parent), ui(new Ui::ConfigureGraphics) {
65 vulkan_device = Settings::values.vulkan_device; 25 vulkan_device = Settings::values.vulkan_device.GetValue();
66 RetrieveVulkanDevices(); 26 RetrieveVulkanDevices();
67 27
68 ui->setupUi(this); 28 ui->setupUi(this);
69 29
30 SetupPerGameUI();
31
70 SetConfiguration(); 32 SetConfiguration();
71 33
72 connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 34 connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this,
73 [this] { UpdateDeviceComboBox(); }); 35 [this] { UpdateDeviceComboBox(); });
74 connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, 36 connect(ui->device, qOverload<int>(&QComboBox::activated), this,
75 [this](int device) { UpdateDeviceSelection(device); }); 37 [this](int device) { UpdateDeviceSelection(device); });
76 38
77 connect(ui->bg_button, &QPushButton::clicked, this, [this] { 39 connect(ui->bg_button, &QPushButton::clicked, this, [this] {
@@ -81,6 +43,9 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
81 } 43 }
82 UpdateBackgroundColorButton(new_bg_color); 44 UpdateBackgroundColorButton(new_bg_color);
83 }); 45 });
46
47 ui->bg_label->setVisible(Settings::configuring_global);
48 ui->bg_combobox->setVisible(!Settings::configuring_global);
84} 49}
85 50
86void ConfigureGraphics::UpdateDeviceSelection(int device) { 51void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -98,31 +63,95 @@ void ConfigureGraphics::SetConfiguration() {
98 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); 63 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
99 64
100 ui->api->setEnabled(runtime_lock); 65 ui->api->setEnabled(runtime_lock);
101 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend));
102 ui->resolution_factor_combobox->setCurrentIndex(
103 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
104 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio);
105 ui->use_disk_shader_cache->setEnabled(runtime_lock);
106 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
107 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); 66 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
108 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); 67 ui->use_disk_shader_cache->setEnabled(runtime_lock);
109 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 68
110 Settings::values.bg_blue)); 69 if (Settings::configuring_global) {
70 ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
71 ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
72 ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
73 ui->use_asynchronous_gpu_emulation->setChecked(
74 Settings::values.use_asynchronous_gpu_emulation.GetValue());
75 } else {
76 ConfigurationShared::SetPerGameSetting(ui->use_disk_shader_cache,
77 &Settings::values.use_disk_shader_cache);
78 ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_gpu_emulation,
79 &Settings::values.use_asynchronous_gpu_emulation);
80
81 ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
82 ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
83 &Settings::values.aspect_ratio);
84
85 ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
86 ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
87 }
88
89 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(),
90 Settings::values.bg_green.GetValue(),
91 Settings::values.bg_blue.GetValue()));
111 UpdateDeviceComboBox(); 92 UpdateDeviceComboBox();
112} 93}
113 94
114void ConfigureGraphics::ApplyConfiguration() { 95void ConfigureGraphics::ApplyConfiguration() {
115 Settings::values.renderer_backend = GetCurrentGraphicsBackend(); 96 if (Settings::configuring_global) {
116 Settings::values.vulkan_device = vulkan_device; 97 // Guard if during game and set to game-specific value
117 Settings::values.resolution_factor = 98 if (Settings::values.renderer_backend.UsingGlobal()) {
118 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); 99 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
119 Settings::values.aspect_ratio = ui->aspect_ratio_combobox->currentIndex(); 100 }
120 Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); 101 if (Settings::values.vulkan_device.UsingGlobal()) {
121 Settings::values.use_asynchronous_gpu_emulation = 102 Settings::values.vulkan_device.SetValue(vulkan_device);
122 ui->use_asynchronous_gpu_emulation->isChecked(); 103 }
123 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 104 if (Settings::values.aspect_ratio.UsingGlobal()) {
124 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 105 Settings::values.aspect_ratio.SetValue(ui->aspect_ratio_combobox->currentIndex());
125 Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); 106 }
107 if (Settings::values.use_disk_shader_cache.UsingGlobal()) {
108 Settings::values.use_disk_shader_cache.SetValue(ui->use_disk_shader_cache->isChecked());
109 }
110 if (Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()) {
111 Settings::values.use_asynchronous_gpu_emulation.SetValue(
112 ui->use_asynchronous_gpu_emulation->isChecked());
113 }
114 if (Settings::values.bg_red.UsingGlobal()) {
115 Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
116 Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
117 Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
118 }
119 } else {
120 if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
121 Settings::values.renderer_backend.SetGlobal(true);
122 Settings::values.vulkan_device.SetGlobal(true);
123 } else {
124 Settings::values.renderer_backend.SetGlobal(false);
125 Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
126 if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) {
127 Settings::values.vulkan_device.SetGlobal(false);
128 Settings::values.vulkan_device.SetValue(vulkan_device);
129 } else {
130 Settings::values.vulkan_device.SetGlobal(true);
131 }
132 }
133
134 ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio,
135 ui->aspect_ratio_combobox);
136
137 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
138 ui->use_disk_shader_cache);
139 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
140 ui->use_asynchronous_gpu_emulation);
141
142 if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
143 Settings::values.bg_red.SetGlobal(true);
144 Settings::values.bg_green.SetGlobal(true);
145 Settings::values.bg_blue.SetGlobal(true);
146 } else {
147 Settings::values.bg_red.SetGlobal(false);
148 Settings::values.bg_green.SetGlobal(false);
149 Settings::values.bg_blue.SetGlobal(false);
150 Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
151 Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
152 Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
153 }
154 }
126} 155}
127 156
128void ConfigureGraphics::changeEvent(QEvent* event) { 157void ConfigureGraphics::changeEvent(QEvent* event) {
@@ -151,19 +180,27 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
151 ui->device->clear(); 180 ui->device->clear();
152 181
153 bool enabled = false; 182 bool enabled = false;
183
184 if (!Settings::configuring_global &&
185 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
186 vulkan_device = Settings::values.vulkan_device.GetValue();
187 }
154 switch (GetCurrentGraphicsBackend()) { 188 switch (GetCurrentGraphicsBackend()) {
155 case Settings::RendererBackend::OpenGL: 189 case Settings::RendererBackend::OpenGL:
156 ui->device->addItem(tr("OpenGL Graphics Device")); 190 ui->device->addItem(tr("OpenGL Graphics Device"));
157 enabled = false; 191 enabled = false;
158 break; 192 break;
159 case Settings::RendererBackend::Vulkan: 193 case Settings::RendererBackend::Vulkan:
160 for (const auto device : vulkan_devices) { 194 for (const auto& device : vulkan_devices) {
161 ui->device->addItem(device); 195 ui->device->addItem(device);
162 } 196 }
163 ui->device->setCurrentIndex(vulkan_device); 197 ui->device->setCurrentIndex(vulkan_device);
164 enabled = !vulkan_devices.empty(); 198 enabled = !vulkan_devices.empty();
165 break; 199 break;
166 } 200 }
201 // If in per-game config and use global is selected, don't enable.
202 enabled &= !(!Settings::configuring_global &&
203 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX);
167 ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn()); 204 ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
168} 205}
169 206
@@ -177,5 +214,37 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
177} 214}
178 215
179Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 216Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
180 return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); 217 if (Settings::configuring_global) {
218 return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
219 }
220
221 if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
222 Settings::values.renderer_backend.SetGlobal(true);
223 return Settings::values.renderer_backend.GetValue();
224 }
225 Settings::values.renderer_backend.SetGlobal(false);
226 return static_cast<Settings::RendererBackend>(ui->api->currentIndex() -
227 ConfigurationShared::USE_GLOBAL_OFFSET);
228}
229
230void ConfigureGraphics::SetupPerGameUI() {
231 if (Settings::configuring_global) {
232 ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
233 ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal());
234 ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
235 ui->use_asynchronous_gpu_emulation->setEnabled(
236 Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
237 ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
238 ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
239
240 return;
241 }
242
243 connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
244 [this](int index) { ui->bg_button->setEnabled(index == 1); });
245
246 ui->use_disk_shader_cache->setTristate(true);
247 ui->use_asynchronous_gpu_emulation->setTristate(true);
248 ConfigurationShared::InsertGlobalItem(ui->aspect_ratio_combobox);
249 ConfigurationShared::InsertGlobalItem(ui->api);
181} 250}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 7e0596d9c..24f01c739 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -35,6 +35,8 @@ private:
35 35
36 void RetrieveVulkanDevices(); 36 void RetrieveVulkanDevices();
37 37
38 void SetupPerGameUI();
39
38 Settings::RendererBackend GetCurrentGraphicsBackend() const; 40 Settings::RendererBackend GetCurrentGraphicsBackend() const;
39 41
40 std::unique_ptr<Ui::ConfigureGraphics> ui; 42 std::unique_ptr<Ui::ConfigureGraphics> ui;
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index c816d6108..62418fc14 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -85,39 +85,34 @@
85 </widget> 85 </widget>
86 </item> 86 </item>
87 <item> 87 <item>
88 <layout class="QHBoxLayout" name="horizontalLayout_2"> 88 <layout class="QHBoxLayout" name="horizontalLayout_6">
89 <item> 89 <item>
90 <widget class="QLabel" name="label"> 90 <widget class="QLabel" name="ar_label">
91 <property name="text"> 91 <property name="text">
92 <string>Internal Resolution:</string> 92 <string>Aspect Ratio:</string>
93 </property> 93 </property>
94 </widget> 94 </widget>
95 </item> 95 </item>
96 <item> 96 <item>
97 <widget class="QComboBox" name="resolution_factor_combobox"> 97 <widget class="QComboBox" name="aspect_ratio_combobox">
98 <item>
99 <property name="text">
100 <string>Auto (Window Size)</string>
101 </property>
102 </item>
103 <item> 98 <item>
104 <property name="text"> 99 <property name="text">
105 <string>Native (1280x720)</string> 100 <string>Default (16:9)</string>
106 </property> 101 </property>
107 </item> 102 </item>
108 <item> 103 <item>
109 <property name="text"> 104 <property name="text">
110 <string>2x Native (2560x1440)</string> 105 <string>Force 4:3</string>
111 </property> 106 </property>
112 </item> 107 </item>
113 <item> 108 <item>
114 <property name="text"> 109 <property name="text">
115 <string>3x Native (3840x2160)</string> 110 <string>Force 21:9</string>
116 </property> 111 </property>
117 </item> 112 </item>
118 <item> 113 <item>
119 <property name="text"> 114 <property name="text">
120 <string>4x Native (5120x2880)</string> 115 <string>Stretch to Window</string>
121 </property> 116 </property>
122 </item> 117 </item>
123 </widget> 118 </widget>
@@ -125,42 +120,30 @@
125 </layout> 120 </layout>
126 </item> 121 </item>
127 <item> 122 <item>
128 <layout class="QHBoxLayout" name="horizontalLayout_6"> 123 <layout class="QHBoxLayout" name="horizontalLayout_3">
129 <item> 124 <item>
130 <widget class="QLabel" name="ar_label"> 125 <widget class="QComboBox" name="bg_combobox">
131 <property name="text"> 126 <property name="currentText">
132 <string>Aspect Ratio:</string> 127 <string>Use global background color</string>
128 </property>
129 <property name="currentIndex">
130 <number>0</number>
131 </property>
132 <property name="maxVisibleItems">
133 <number>10</number>
133 </property> 134 </property>
134 </widget>
135 </item>
136 <item>
137 <widget class="QComboBox" name="aspect_ratio_combobox">
138 <item>
139 <property name="text">
140 <string>Default (16:9)</string>
141 </property>
142 </item>
143 <item>
144 <property name="text">
145 <string>Force 4:3</string>
146 </property>
147 </item>
148 <item> 135 <item>
149 <property name="text"> 136 <property name="text">
150 <string>Force 21:9</string> 137 <string>Use global background color</string>
151 </property> 138 </property>
152 </item> 139 </item>
153 <item> 140 <item>
154 <property name="text"> 141 <property name="text">
155 <string>Stretch to Window</string> 142 <string>Set background color:</string>
156 </property> 143 </property>
157 </item> 144 </item>
158 </widget> 145 </widget>
159 </item> 146 </item>
160 </layout>
161 </item>
162 <item>
163 <layout class="QHBoxLayout" name="horizontalLayout_3">
164 <item> 147 <item>
165 <widget class="QLabel" name="bg_label"> 148 <widget class="QLabel" name="bg_label">
166 <property name="text"> 149 <property name="text">
@@ -169,6 +152,19 @@
169 </widget> 152 </widget>
170 </item> 153 </item>
171 <item> 154 <item>
155 <spacer name="horizontalSpacer">
156 <property name="orientation">
157 <enum>Qt::Horizontal</enum>
158 </property>
159 <property name="sizeHint" stdset="0">
160 <size>
161 <width>40</width>
162 <height>20</height>
163 </size>
164 </property>
165 </spacer>
166 </item>
167 <item>
172 <widget class="QPushButton" name="bg_button"> 168 <widget class="QPushButton" name="bg_button">
173 <property name="maximumSize"> 169 <property name="maximumSize">
174 <size> 170 <size>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 37aadf7f8..7c0fa7ec5 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -5,6 +5,7 @@
5#include "core/core.h" 5#include "core/core.h"
6#include "core/settings.h" 6#include "core/settings.h"
7#include "ui_configure_graphics_advanced.h" 7#include "ui_configure_graphics_advanced.h"
8#include "yuzu/configuration/configuration_shared.h"
8#include "yuzu/configuration/configure_graphics_advanced.h" 9#include "yuzu/configuration/configure_graphics_advanced.h"
9 10
10ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent) 11ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
@@ -12,8 +13,7 @@ ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
12 13
13 ui->setupUi(this); 14 ui->setupUi(this);
14 15
15 // TODO: Remove this after assembly shaders are fully integrated 16 SetupPerGameUI();
16 ui->use_assembly_shaders->setVisible(false);
17 17
18 SetConfiguration(); 18 SetConfiguration();
19} 19}
@@ -22,26 +22,81 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
22 22
23void ConfigureGraphicsAdvanced::SetConfiguration() { 23void ConfigureGraphicsAdvanced::SetConfiguration() {
24 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); 24 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
25 ui->gpu_accuracy->setCurrentIndex(static_cast<int>(Settings::values.gpu_accuracy));
26 ui->use_vsync->setEnabled(runtime_lock); 25 ui->use_vsync->setEnabled(runtime_lock);
27 ui->use_vsync->setChecked(Settings::values.use_vsync);
28 ui->use_assembly_shaders->setEnabled(runtime_lock); 26 ui->use_assembly_shaders->setEnabled(runtime_lock);
29 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders);
30 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time);
31 ui->force_30fps_mode->setEnabled(runtime_lock); 27 ui->force_30fps_mode->setEnabled(runtime_lock);
32 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
33 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); 28 ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
34 ui->anisotropic_filtering_combobox->setCurrentIndex(Settings::values.max_anisotropy); 29
30 if (Settings::configuring_global) {
31 ui->gpu_accuracy->setCurrentIndex(
32 static_cast<int>(Settings::values.gpu_accuracy.GetValue()));
33 ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
34 ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
35 ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
36 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue());
37 ui->anisotropic_filtering_combobox->setCurrentIndex(
38 Settings::values.max_anisotropy.GetValue());
39 } else {
40 ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
41 ConfigurationShared::SetPerGameSetting(ui->use_vsync, &Settings::values.use_vsync);
42 ConfigurationShared::SetPerGameSetting(ui->use_assembly_shaders,
43 &Settings::values.use_assembly_shaders);
44 ConfigurationShared::SetPerGameSetting(ui->use_fast_gpu_time,
45 &Settings::values.use_fast_gpu_time);
46 ConfigurationShared::SetPerGameSetting(ui->force_30fps_mode,
47 &Settings::values.force_30fps_mode);
48 ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
49 &Settings::values.max_anisotropy);
50 }
35} 51}
36 52
37void ConfigureGraphicsAdvanced::ApplyConfiguration() { 53void ConfigureGraphicsAdvanced::ApplyConfiguration() {
38 auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(ui->gpu_accuracy->currentIndex()); 54 // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots)
39 Settings::values.gpu_accuracy = gpu_accuracy; 55 const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(
40 Settings::values.use_vsync = ui->use_vsync->isChecked(); 56 ui->gpu_accuracy->currentIndex() -
41 Settings::values.use_assembly_shaders = ui->use_assembly_shaders->isChecked(); 57 ((Settings::configuring_global) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
42 Settings::values.use_fast_gpu_time = ui->use_fast_gpu_time->isChecked(); 58
43 Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); 59 if (Settings::configuring_global) {
44 Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex(); 60 // Must guard in case of a during-game configuration when set to be game-specific.
61 if (Settings::values.gpu_accuracy.UsingGlobal()) {
62 Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
63 }
64 if (Settings::values.use_vsync.UsingGlobal()) {
65 Settings::values.use_vsync.SetValue(ui->use_vsync->isChecked());
66 }
67 if (Settings::values.use_assembly_shaders.UsingGlobal()) {
68 Settings::values.use_assembly_shaders.SetValue(ui->use_assembly_shaders->isChecked());
69 }
70 if (Settings::values.use_fast_gpu_time.UsingGlobal()) {
71 Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked());
72 }
73 if (Settings::values.force_30fps_mode.UsingGlobal()) {
74 Settings::values.force_30fps_mode.SetValue(ui->force_30fps_mode->isChecked());
75 }
76 if (Settings::values.max_anisotropy.UsingGlobal()) {
77 Settings::values.max_anisotropy.SetValue(
78 ui->anisotropic_filtering_combobox->currentIndex());
79 }
80 } else {
81 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
82 ui->anisotropic_filtering_combobox);
83 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync);
84 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders,
85 ui->use_assembly_shaders);
86 ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
87 ui->use_fast_gpu_time);
88 ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode,
89 ui->force_30fps_mode);
90 ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
91 ui->anisotropic_filtering_combobox);
92
93 if (ui->gpu_accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
94 Settings::values.gpu_accuracy.SetGlobal(true);
95 } else {
96 Settings::values.gpu_accuracy.SetGlobal(false);
97 Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
98 }
99 }
45} 100}
46 101
47void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { 102void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -55,3 +110,25 @@ void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
55void ConfigureGraphicsAdvanced::RetranslateUI() { 110void ConfigureGraphicsAdvanced::RetranslateUI() {
56 ui->retranslateUi(this); 111 ui->retranslateUi(this);
57} 112}
113
114void ConfigureGraphicsAdvanced::SetupPerGameUI() {
115 // Disable if not global (only happens during game)
116 if (Settings::configuring_global) {
117 ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
118 ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
119 ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
120 ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
121 ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal());
122 ui->anisotropic_filtering_combobox->setEnabled(
123 Settings::values.max_anisotropy.UsingGlobal());
124
125 return;
126 }
127
128 ConfigurationShared::InsertGlobalItem(ui->gpu_accuracy);
129 ui->use_vsync->setTristate(true);
130 ui->use_assembly_shaders->setTristate(true);
131 ui->use_fast_gpu_time->setTristate(true);
132 ui->force_30fps_mode->setTristate(true);
133 ConfigurationShared::InsertGlobalItem(ui->anisotropic_filtering_combobox);
134}
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index bbc9d4355..c043588ff 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -26,5 +26,7 @@ private:
26 26
27 void SetConfiguration(); 27 void SetConfiguration();
28 28
29 void SetupPerGameUI();
30
29 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui; 31 std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
30}; 32};
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index e4eb5594b..00433926d 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -70,6 +70,20 @@ static QString ButtonToText(const Common::ParamPackage& param) {
70 return GetKeyName(param.Get("code", 0)); 70 return GetKeyName(param.Get("code", 0));
71 } 71 }
72 72
73 if (param.Get("engine", "") == "gcpad") {
74 if (param.Has("axis")) {
75 const QString axis_str = QString::fromStdString(param.Get("axis", ""));
76 const QString direction_str = QString::fromStdString(param.Get("direction", ""));
77
78 return QObject::tr("GC Axis %1%2").arg(axis_str, direction_str);
79 }
80 if (param.Has("button")) {
81 const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
82 return QObject::tr("GC Button %1").arg(button_str);
83 }
84 return GetKeyName(param.Get("code", 0));
85 }
86
73 if (param.Get("engine", "") == "sdl") { 87 if (param.Get("engine", "") == "sdl") {
74 if (param.Has("hat")) { 88 if (param.Has("hat")) {
75 const QString hat_str = QString::fromStdString(param.Get("hat", "")); 89 const QString hat_str = QString::fromStdString(param.Get("hat", ""));
@@ -126,6 +140,25 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string
126 return {}; 140 return {};
127 } 141 }
128 142
143 if (param.Get("engine", "") == "gcpad") {
144 if (dir == "modifier") {
145 return QObject::tr("[unused]");
146 }
147
148 if (dir == "left" || dir == "right") {
149 const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
150
151 return QObject::tr("GC Axis %1").arg(axis_x_str);
152 }
153
154 if (dir == "up" || dir == "down") {
155 const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
156
157 return QObject::tr("GC Axis %1").arg(axis_y_str);
158 }
159
160 return {};
161 }
129 return QObject::tr("[unknown]"); 162 return QObject::tr("[unknown]");
130} 163}
131 164
@@ -332,7 +365,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
332 365
333 connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] { 366 connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] {
334 const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value(); 367 const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value();
335 if (analogs_param[analog_id].Get("engine", "") == "sdl") { 368 if (analogs_param[analog_id].Get("engine", "") == "sdl" ||
369 analogs_param[analog_id].Get("engine", "") == "gcpad") {
336 analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( 370 analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
337 tr("Deadzone: %1%").arg(slider_value)); 371 tr("Deadzone: %1%").arg(slider_value));
338 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); 372 analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
@@ -352,6 +386,20 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
352 386
353 connect(poll_timer.get(), &QTimer::timeout, [this] { 387 connect(poll_timer.get(), &QTimer::timeout, [this] {
354 Common::ParamPackage params; 388 Common::ParamPackage params;
389 if (InputCommon::GetGCButtons()->IsPolling()) {
390 params = InputCommon::GetGCButtons()->GetNextInput();
391 if (params.Has("engine")) {
392 SetPollingResult(params, false);
393 return;
394 }
395 }
396 if (InputCommon::GetGCAnalogs()->IsPolling()) {
397 params = InputCommon::GetGCAnalogs()->GetNextInput();
398 if (params.Has("engine")) {
399 SetPollingResult(params, false);
400 return;
401 }
402 }
355 for (auto& poller : device_pollers) { 403 for (auto& poller : device_pollers) {
356 params = poller->GetNextInput(); 404 params = poller->GetNextInput();
357 if (params.Has("engine")) { 405 if (params.Has("engine")) {
@@ -480,7 +528,9 @@ void ConfigureInputPlayer::RestoreDefaults() {
480 SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); 528 SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
481 } 529 }
482 } 530 }
531
483 UpdateButtonLabels(); 532 UpdateButtonLabels();
533 ApplyConfiguration();
484} 534}
485 535
486void ConfigureInputPlayer::ClearAll() { 536void ConfigureInputPlayer::ClearAll() {
@@ -505,6 +555,7 @@ void ConfigureInputPlayer::ClearAll() {
505 } 555 }
506 556
507 UpdateButtonLabels(); 557 UpdateButtonLabels();
558 ApplyConfiguration();
508} 559}
509 560
510void ConfigureInputPlayer::UpdateButtonLabels() { 561void ConfigureInputPlayer::UpdateButtonLabels() {
@@ -531,7 +582,7 @@ void ConfigureInputPlayer::UpdateButtonLabels() {
531 analog_map_deadzone_and_modifier_slider_label[analog_id]; 582 analog_map_deadzone_and_modifier_slider_label[analog_id];
532 583
533 if (param.Has("engine")) { 584 if (param.Has("engine")) {
534 if (param.Get("engine", "") == "sdl") { 585 if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") {
535 if (!param.Has("deadzone")) { 586 if (!param.Has("deadzone")) {
536 param.Set("deadzone", 0.1f); 587 param.Set("deadzone", 0.1f);
537 } 588 }
@@ -580,6 +631,11 @@ void ConfigureInputPlayer::HandleClick(
580 631
581 grabKeyboard(); 632 grabKeyboard();
582 grabMouse(); 633 grabMouse();
634 if (type == InputCommon::Polling::DeviceType::Button) {
635 InputCommon::GetGCButtons()->BeginConfiguration();
636 } else {
637 InputCommon::GetGCAnalogs()->BeginConfiguration();
638 }
583 timeout_timer->start(5000); // Cancel after 5 seconds 639 timeout_timer->start(5000); // Cancel after 5 seconds
584 poll_timer->start(200); // Check for new inputs every 200ms 640 poll_timer->start(200); // Check for new inputs every 200ms
585} 641}
@@ -593,6 +649,9 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
593 poller->Stop(); 649 poller->Stop();
594 } 650 }
595 651
652 InputCommon::GetGCButtons()->EndConfiguration();
653 InputCommon::GetGCAnalogs()->EndConfiguration();
654
596 if (!abort) { 655 if (!abort) {
597 (*input_setter)(params); 656 (*input_setter)(params);
598 } 657 }
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
new file mode 100644
index 000000000..1e49f0787
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -0,0 +1,140 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7#include <utility>
8
9#include <QCheckBox>
10#include <QHeaderView>
11#include <QMenu>
12#include <QStandardItemModel>
13#include <QString>
14#include <QTimer>
15#include <QTreeView>
16
17#include "common/common_paths.h"
18#include "common/file_util.h"
19#include "core/file_sys/control_metadata.h"
20#include "core/file_sys/patch_manager.h"
21#include "core/file_sys/xts_archive.h"
22#include "core/loader/loader.h"
23#include "ui_configure_per_game.h"
24#include "yuzu/configuration/config.h"
25#include "yuzu/configuration/configure_input.h"
26#include "yuzu/configuration/configure_per_game.h"
27#include "yuzu/uisettings.h"
28#include "yuzu/util/util.h"
29
30ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id)
31 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) {
32 game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false);
33
34 Settings::configuring_global = false;
35
36 ui->setupUi(this);
37 setFocusPolicy(Qt::ClickFocus);
38 setWindowTitle(tr("Properties"));
39
40 ui->addonsTab->SetTitleId(title_id);
41
42 scene = new QGraphicsScene;
43 ui->icon_view->setScene(scene);
44
45 LoadConfiguration();
46}
47
48ConfigurePerGame::~ConfigurePerGame() = default;
49
50void ConfigurePerGame::ApplyConfiguration() {
51 ui->addonsTab->ApplyConfiguration();
52 ui->generalTab->ApplyConfiguration();
53 ui->systemTab->ApplyConfiguration();
54 ui->graphicsTab->ApplyConfiguration();
55 ui->graphicsAdvancedTab->ApplyConfiguration();
56 ui->audioTab->ApplyConfiguration();
57
58 Settings::Apply();
59 Settings::LogSettings();
60
61 game_config->Save();
62}
63
64void ConfigurePerGame::changeEvent(QEvent* event) {
65 if (event->type() == QEvent::LanguageChange) {
66 RetranslateUI();
67 }
68
69 QDialog::changeEvent(event);
70}
71
72void ConfigurePerGame::RetranslateUI() {
73 ui->retranslateUi(this);
74}
75
76void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file) {
77 this->file = std::move(file);
78 LoadConfiguration();
79}
80
81void ConfigurePerGame::LoadConfiguration() {
82 if (file == nullptr) {
83 return;
84 }
85
86 ui->addonsTab->LoadFromFile(file);
87
88 ui->display_title_id->setText(
89 QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
90
91 FileSys::PatchManager pm{title_id};
92 const auto control = pm.GetControlMetadata();
93 const auto loader = Loader::GetLoader(file);
94
95 if (control.first != nullptr) {
96 ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
97 ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName()));
98 ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName()));
99 } else {
100 std::string title;
101 if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
102 ui->display_name->setText(QString::fromStdString(title));
103
104 FileSys::NACP nacp;
105 if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success)
106 ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName()));
107
108 ui->display_version->setText(QStringLiteral("1.0.0"));
109 }
110
111 if (control.second != nullptr) {
112 scene->clear();
113
114 QPixmap map;
115 const auto bytes = control.second->ReadAllBytes();
116 map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
117
118 scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
119 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
120 } else {
121 std::vector<u8> bytes;
122 if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) {
123 scene->clear();
124
125 QPixmap map;
126 map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
127
128 scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
129 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
130 }
131 }
132
133 ui->display_filename->setText(QString::fromStdString(file->GetName()));
134
135 ui->display_format->setText(
136 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())));
137
138 const auto valueText = ReadableByteSize(file->GetSize());
139 ui->display_size->setText(valueText);
140}
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
new file mode 100644
index 000000000..5f9a08cef
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -0,0 +1,51 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <vector>
9
10#include <QDialog>
11#include <QList>
12
13#include "core/file_sys/vfs_types.h"
14#include "yuzu/configuration/config.h"
15
16class QGraphicsScene;
17class QStandardItem;
18class QStandardItemModel;
19class QTreeView;
20class QVBoxLayout;
21
22namespace Ui {
23class ConfigurePerGame;
24}
25
26class ConfigurePerGame : public QDialog {
27 Q_OBJECT
28
29public:
30 explicit ConfigurePerGame(QWidget* parent, u64 title_id);
31 ~ConfigurePerGame() override;
32
33 /// Save all button configurations to settings file
34 void ApplyConfiguration();
35
36 void LoadFromFile(FileSys::VirtualFile file);
37
38private:
39 void changeEvent(QEvent* event) override;
40 void RetranslateUI();
41
42 void LoadConfiguration();
43
44 std::unique_ptr<Ui::ConfigurePerGame> ui;
45 FileSys::VirtualFile file;
46 u64 title_id;
47
48 QGraphicsScene* scene;
49
50 std::unique_ptr<Config> game_config;
51};
diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui
new file mode 100644
index 000000000..d2057c4ab
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_game.ui
@@ -0,0 +1,350 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigurePerGame</class>
4 <widget class="QDialog" name="ConfigurePerGame">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>800</width>
10 <height>600</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Dialog</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_3">
17 <item>
18 <layout class="QHBoxLayout" name="horizontalLayout">
19 <item>
20 <widget class="QGroupBox" name="groupBox">
21 <property name="sizePolicy">
22 <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
23 <horstretch>0</horstretch>
24 <verstretch>0</verstretch>
25 </sizepolicy>
26 </property>
27 <property name="title">
28 <string>Info</string>
29 </property>
30 <layout class="QVBoxLayout" name="verticalLayout">
31 <item alignment="Qt::AlignHCenter">
32 <widget class="QGraphicsView" name="icon_view">
33 <property name="sizePolicy">
34 <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
35 <horstretch>0</horstretch>
36 <verstretch>0</verstretch>
37 </sizepolicy>
38 </property>
39 <property name="minimumSize">
40 <size>
41 <width>256</width>
42 <height>256</height>
43 </size>
44 </property>
45 <property name="maximumSize">
46 <size>
47 <width>256</width>
48 <height>256</height>
49 </size>
50 </property>
51 <property name="verticalScrollBarPolicy">
52 <enum>Qt::ScrollBarAlwaysOff</enum>
53 </property>
54 <property name="horizontalScrollBarPolicy">
55 <enum>Qt::ScrollBarAlwaysOff</enum>
56 </property>
57 <property name="interactive">
58 <bool>false</bool>
59 </property>
60 </widget>
61 </item>
62 <item>
63 <layout class="QGridLayout" name="gridLayout_2">
64 <item row="6" column="1">
65 <widget class="QLineEdit" name="display_size">
66 <property name="enabled">
67 <bool>true</bool>
68 </property>
69 <property name="readOnly">
70 <bool>true</bool>
71 </property>
72 </widget>
73 </item>
74 <item row="3" column="1">
75 <widget class="QLineEdit" name="display_version">
76 <property name="enabled">
77 <bool>true</bool>
78 </property>
79 <property name="readOnly">
80 <bool>true</bool>
81 </property>
82 </widget>
83 </item>
84 <item row="1" column="0">
85 <widget class="QLabel" name="label">
86 <property name="text">
87 <string>Name</string>
88 </property>
89 </widget>
90 </item>
91 <item row="4" column="0">
92 <widget class="QLabel" name="label_4">
93 <property name="text">
94 <string>Title ID</string>
95 </property>
96 </widget>
97 </item>
98 <item row="4" column="1">
99 <widget class="QLineEdit" name="display_title_id">
100 <property name="enabled">
101 <bool>true</bool>
102 </property>
103 <property name="readOnly">
104 <bool>true</bool>
105 </property>
106 </widget>
107 </item>
108 <item row="7" column="1">
109 <widget class="QLineEdit" name="display_filename">
110 <property name="enabled">
111 <bool>true</bool>
112 </property>
113 <property name="readOnly">
114 <bool>true</bool>
115 </property>
116 </widget>
117 </item>
118 <item row="5" column="1">
119 <widget class="QLineEdit" name="display_format">
120 <property name="enabled">
121 <bool>true</bool>
122 </property>
123 <property name="readOnly">
124 <bool>true</bool>
125 </property>
126 </widget>
127 </item>
128 <item row="7" column="0">
129 <widget class="QLabel" name="label_7">
130 <property name="text">
131 <string>Filename</string>
132 </property>
133 </widget>
134 </item>
135 <item row="1" column="1">
136 <widget class="QLineEdit" name="display_name">
137 <property name="enabled">
138 <bool>true</bool>
139 </property>
140 <property name="readOnly">
141 <bool>true</bool>
142 </property>
143 </widget>
144 </item>
145 <item row="2" column="1">
146 <widget class="QLineEdit" name="display_developer">
147 <property name="enabled">
148 <bool>true</bool>
149 </property>
150 <property name="readOnly">
151 <bool>true</bool>
152 </property>
153 </widget>
154 </item>
155 <item row="5" column="0">
156 <widget class="QLabel" name="label_5">
157 <property name="text">
158 <string>Format</string>
159 </property>
160 </widget>
161 </item>
162 <item row="3" column="0">
163 <widget class="QLabel" name="label_3">
164 <property name="text">
165 <string>Version</string>
166 </property>
167 </widget>
168 </item>
169 <item row="6" column="0">
170 <widget class="QLabel" name="label_6">
171 <property name="text">
172 <string>Size</string>
173 </property>
174 </widget>
175 </item>
176 <item row="2" column="0">
177 <widget class="QLabel" name="label_2">
178 <property name="text">
179 <string>Developer</string>
180 </property>
181 </widget>
182 </item>
183 </layout>
184 </item>
185 <item>
186 <spacer name="verticalSpacer">
187 <property name="orientation">
188 <enum>Qt::Vertical</enum>
189 </property>
190 <property name="sizeHint" stdset="0">
191 <size>
192 <width>20</width>
193 <height>40</height>
194 </size>
195 </property>
196 </spacer>
197 </item>
198 </layout>
199 </widget>
200 </item>
201 <item>
202 <layout class="QVBoxLayout" name="VerticalLayout">
203 <item>
204 <layout class="QVBoxLayout" name="verticalLayout_2"/>
205 </item>
206 <item>
207 <widget class="QTabWidget" name="tabWidget">
208 <property name="enabled">
209 <bool>true</bool>
210 </property>
211 <property name="currentIndex">
212 <number>0</number>
213 </property>
214 <property name="usesScrollButtons">
215 <bool>true</bool>
216 </property>
217 <property name="documentMode">
218 <bool>false</bool>
219 </property>
220 <property name="tabsClosable">
221 <bool>false</bool>
222 </property>
223 <widget class="ConfigurePerGameAddons" name="addonsTab">
224 <attribute name="title">
225 <string>Add-Ons</string>
226 </attribute>
227 </widget>
228 <widget class="ConfigureGeneral" name="generalTab">
229 <attribute name="title">
230 <string>General</string>
231 </attribute>
232 </widget>
233 <widget class="ConfigureSystem" name="systemTab">
234 <attribute name="title">
235 <string>System</string>
236 </attribute>
237 </widget>
238 <widget class="ConfigureGraphics" name="graphicsTab">
239 <attribute name="title">
240 <string>Graphics</string>
241 </attribute>
242 </widget>
243 <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab">
244 <attribute name="title">
245 <string>Adv. Graphics</string>
246 </attribute>
247 </widget>
248 <widget class="ConfigureAudio" name="audioTab">
249 <attribute name="title">
250 <string>Audio</string>
251 </attribute>
252 </widget>
253 </widget>
254 </item>
255 </layout>
256 </item>
257 </layout>
258 </item>
259 <item>
260 <widget class="QDialogButtonBox" name="buttonBox">
261 <property name="sizePolicy">
262 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
263 <horstretch>0</horstretch>
264 <verstretch>0</verstretch>
265 </sizepolicy>
266 </property>
267 <property name="orientation">
268 <enum>Qt::Horizontal</enum>
269 </property>
270 <property name="standardButtons">
271 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
272 </property>
273 </widget>
274 </item>
275 </layout>
276 </widget>
277 <customwidgets>
278 <customwidget>
279 <class>ConfigureGeneral</class>
280 <extends>QWidget</extends>
281 <header>configuration/configure_general.h</header>
282 <container>1</container>
283 </customwidget>
284 <customwidget>
285 <class>ConfigureSystem</class>
286 <extends>QWidget</extends>
287 <header>configuration/configure_system.h</header>
288 <container>1</container>
289 </customwidget>
290 <customwidget>
291 <class>ConfigureAudio</class>
292 <extends>QWidget</extends>
293 <header>configuration/configure_audio.h</header>
294 <container>1</container>
295 </customwidget>
296 <customwidget>
297 <class>ConfigureGraphics</class>
298 <extends>QWidget</extends>
299 <header>configuration/configure_graphics.h</header>
300 <container>1</container>
301 </customwidget>
302 <customwidget>
303 <class>ConfigureGraphicsAdvanced</class>
304 <extends>QWidget</extends>
305 <header>configuration/configure_graphics_advanced.h</header>
306 <container>1</container>
307 </customwidget>
308 <customwidget>
309 <class>ConfigurePerGameAddons</class>
310 <extends>QWidget</extends>
311 <header>configuration/configure_per_game_addons.h</header>
312 <container>1</container>
313 </customwidget>
314 </customwidgets>
315 <resources/>
316 <connections>
317 <connection>
318 <sender>buttonBox</sender>
319 <signal>accepted()</signal>
320 <receiver>ConfigurePerGame</receiver>
321 <slot>accept()</slot>
322 <hints>
323 <hint type="sourcelabel">
324 <x>248</x>
325 <y>254</y>
326 </hint>
327 <hint type="destinationlabel">
328 <x>157</x>
329 <y>274</y>
330 </hint>
331 </hints>
332 </connection>
333 <connection>
334 <sender>buttonBox</sender>
335 <signal>rejected()</signal>
336 <receiver>ConfigurePerGame</receiver>
337 <slot>reject()</slot>
338 <hints>
339 <hint type="sourcelabel">
340 <x>316</x>
341 <y>260</y>
342 </hint>
343 <hint type="destinationlabel">
344 <x>286</x>
345 <y>274</y>
346 </hint>
347 </hints>
348 </connection>
349 </connections>
350</ui>
diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index d7f259f12..478d5d3a1 100644
--- a/src/yuzu/configuration/configure_per_general.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -15,23 +15,20 @@
15 15
16#include "common/common_paths.h" 16#include "common/common_paths.h"
17#include "common/file_util.h" 17#include "common/file_util.h"
18#include "core/file_sys/control_metadata.h" 18#include "core/core.h"
19#include "core/file_sys/patch_manager.h" 19#include "core/file_sys/patch_manager.h"
20#include "core/file_sys/xts_archive.h" 20#include "core/file_sys/xts_archive.h"
21#include "core/loader/loader.h" 21#include "core/loader/loader.h"
22#include "ui_configure_per_general.h" 22#include "ui_configure_per_game_addons.h"
23#include "yuzu/configuration/config.h" 23#include "yuzu/configuration/config.h"
24#include "yuzu/configuration/configure_input.h" 24#include "yuzu/configuration/configure_input.h"
25#include "yuzu/configuration/configure_per_general.h" 25#include "yuzu/configuration/configure_per_game_addons.h"
26#include "yuzu/uisettings.h" 26#include "yuzu/uisettings.h"
27#include "yuzu/util/util.h" 27#include "yuzu/util/util.h"
28 28
29ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id) 29ConfigurePerGameAddons::ConfigurePerGameAddons(QWidget* parent)
30 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGameGeneral>()), title_id(title_id) { 30 : QWidget(parent), ui(new Ui::ConfigurePerGameAddons) {
31
32 ui->setupUi(this); 31 ui->setupUi(this);
33 setFocusPolicy(Qt::ClickFocus);
34 setWindowTitle(tr("Properties"));
35 32
36 layout = new QVBoxLayout; 33 layout = new QVBoxLayout;
37 tree_view = new QTreeView; 34 tree_view = new QTreeView;
@@ -52,7 +49,7 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)
52 item_model->setHeaderData(1, Qt::Horizontal, tr("Version")); 49 item_model->setHeaderData(1, Qt::Horizontal, tr("Version"));
53 50
54 // We must register all custom types with the Qt Automoc system so that we are able to use it 51 // We must register all custom types with the Qt Automoc system so that we are able to use it
55 // with signals/slots. In this case, QList falls under the umbrells of custom types. 52 // with signals/slots. In this case, QList falls under the umbrella of custom types.
56 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); 53 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
57 54
58 layout->setContentsMargins(0, 0, 0, 0); 55 layout->setContentsMargins(0, 0, 0, 0);
@@ -61,18 +58,15 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)
61 58
62 ui->scrollArea->setLayout(layout); 59 ui->scrollArea->setLayout(layout);
63 60
64 scene = new QGraphicsScene; 61 ui->scrollArea->setEnabled(!Core::System::GetInstance().IsPoweredOn());
65 ui->icon_view->setScene(scene);
66 62
67 connect(item_model, &QStandardItemModel::itemChanged, 63 connect(item_model, &QStandardItemModel::itemChanged,
68 [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); 64 [] { UISettings::values.is_game_list_reload_pending.exchange(true); });
69
70 LoadConfiguration();
71} 65}
72 66
73ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default; 67ConfigurePerGameAddons::~ConfigurePerGameAddons() = default;
74 68
75void ConfigurePerGameGeneral::ApplyConfiguration() { 69void ConfigurePerGameAddons::ApplyConfiguration() {
76 std::vector<std::string> disabled_addons; 70 std::vector<std::string> disabled_addons;
77 71
78 for (const auto& item : list_items) { 72 for (const auto& item : list_items) {
@@ -92,72 +86,35 @@ void ConfigurePerGameGeneral::ApplyConfiguration() {
92 Settings::values.disabled_addons[title_id] = disabled_addons; 86 Settings::values.disabled_addons[title_id] = disabled_addons;
93} 87}
94 88
95void ConfigurePerGameGeneral::changeEvent(QEvent* event) { 89void ConfigurePerGameAddons::LoadFromFile(FileSys::VirtualFile file) {
90 this->file = std::move(file);
91 LoadConfiguration();
92}
93
94void ConfigurePerGameAddons::SetTitleId(u64 id) {
95 this->title_id = id;
96}
97
98void ConfigurePerGameAddons::changeEvent(QEvent* event) {
96 if (event->type() == QEvent::LanguageChange) { 99 if (event->type() == QEvent::LanguageChange) {
97 RetranslateUI(); 100 RetranslateUI();
98 } 101 }
99 102
100 QDialog::changeEvent(event); 103 QWidget::changeEvent(event);
101} 104}
102 105
103void ConfigurePerGameGeneral::RetranslateUI() { 106void ConfigurePerGameAddons::RetranslateUI() {
104 ui->retranslateUi(this); 107 ui->retranslateUi(this);
105} 108}
106 109
107void ConfigurePerGameGeneral::LoadFromFile(FileSys::VirtualFile file) { 110void ConfigurePerGameAddons::LoadConfiguration() {
108 this->file = std::move(file);
109 LoadConfiguration();
110}
111
112void ConfigurePerGameGeneral::LoadConfiguration() {
113 if (file == nullptr) { 111 if (file == nullptr) {
114 return; 112 return;
115 } 113 }
116 114
117 ui->display_title_id->setText(QString::fromStdString(fmt::format("{:016X}", title_id)));
118
119 FileSys::PatchManager pm{title_id}; 115 FileSys::PatchManager pm{title_id};
120 const auto control = pm.GetControlMetadata();
121 const auto loader = Loader::GetLoader(file); 116 const auto loader = Loader::GetLoader(file);
122 117
123 if (control.first != nullptr) {
124 ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
125 ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName()));
126 ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName()));
127 } else {
128 std::string title;
129 if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
130 ui->display_name->setText(QString::fromStdString(title));
131
132 FileSys::NACP nacp;
133 if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success)
134 ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName()));
135
136 ui->display_version->setText(QStringLiteral("1.0.0"));
137 }
138
139 if (control.second != nullptr) {
140 scene->clear();
141
142 QPixmap map;
143 const auto bytes = control.second->ReadAllBytes();
144 map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
145
146 scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
147 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
148 } else {
149 std::vector<u8> bytes;
150 if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) {
151 scene->clear();
152
153 QPixmap map;
154 map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
155
156 scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
157 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
158 }
159 }
160
161 FileSys::VirtualFile update_raw; 118 FileSys::VirtualFile update_raw;
162 loader->ReadUpdateRaw(update_raw); 119 loader->ReadUpdateRaw(update_raw);
163 120
@@ -182,12 +139,4 @@ void ConfigurePerGameGeneral::LoadConfiguration() {
182 } 139 }
183 140
184 tree_view->setColumnWidth(0, 5 * tree_view->width() / 16); 141 tree_view->setColumnWidth(0, 5 * tree_view->width() / 16);
185
186 ui->display_filename->setText(QString::fromStdString(file->GetName()));
187
188 ui->display_format->setText(
189 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())));
190
191 const auto valueText = ReadableByteSize(file->GetSize());
192 ui->display_size->setText(valueText);
193} 142}
diff --git a/src/yuzu/configuration/configure_per_general.h b/src/yuzu/configuration/configure_per_game_addons.h
index a3b2cdeff..a00ec3539 100644
--- a/src/yuzu/configuration/configure_per_general.h
+++ b/src/yuzu/configuration/configure_per_game_addons.h
@@ -1,4 +1,4 @@
1// Copyright 2016 Citra Emulator Project 1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
@@ -7,7 +7,6 @@
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9 9
10#include <QDialog>
11#include <QList> 10#include <QList>
12 11
13#include "core/file_sys/vfs_types.h" 12#include "core/file_sys/vfs_types.h"
@@ -19,35 +18,36 @@ class QTreeView;
19class QVBoxLayout; 18class QVBoxLayout;
20 19
21namespace Ui { 20namespace Ui {
22class ConfigurePerGameGeneral; 21class ConfigurePerGameAddons;
23} 22}
24 23
25class ConfigurePerGameGeneral : public QDialog { 24class ConfigurePerGameAddons : public QWidget {
26 Q_OBJECT 25 Q_OBJECT
27 26
28public: 27public:
29 explicit ConfigurePerGameGeneral(QWidget* parent, u64 title_id); 28 explicit ConfigurePerGameAddons(QWidget* parent = nullptr);
30 ~ConfigurePerGameGeneral() override; 29 ~ConfigurePerGameAddons() override;
31 30
32 /// Save all button configurations to settings file 31 /// Save all button configurations to settings file
33 void ApplyConfiguration(); 32 void ApplyConfiguration();
34 33
35 void LoadFromFile(FileSys::VirtualFile file); 34 void LoadFromFile(FileSys::VirtualFile file);
36 35
36 void SetTitleId(u64 id);
37
37private: 38private:
38 void changeEvent(QEvent* event) override; 39 void changeEvent(QEvent* event) override;
39 void RetranslateUI(); 40 void RetranslateUI();
40 41
41 void LoadConfiguration(); 42 void LoadConfiguration();
42 43
43 std::unique_ptr<Ui::ConfigurePerGameGeneral> ui; 44 std::unique_ptr<Ui::ConfigurePerGameAddons> ui;
44 FileSys::VirtualFile file; 45 FileSys::VirtualFile file;
45 u64 title_id; 46 u64 title_id;
46 47
47 QVBoxLayout* layout; 48 QVBoxLayout* layout;
48 QTreeView* tree_view; 49 QTreeView* tree_view;
49 QStandardItemModel* item_model; 50 QStandardItemModel* item_model;
50 QGraphicsScene* scene;
51 51
52 std::vector<QList<QStandardItem*>> list_items; 52 std::vector<QList<QStandardItem*>> list_items;
53}; 53};
diff --git a/src/yuzu/configuration/configure_per_game_addons.ui b/src/yuzu/configuration/configure_per_game_addons.ui
new file mode 100644
index 000000000..aefdebfcd
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_game_addons.ui
@@ -0,0 +1,38 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigurePerGameAddons</class>
4 <widget class="QWidget" name="ConfigurePerGameAddons">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>400</width>
10 <height>300</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QGridLayout" name="gridLayout">
17 <item row="0" column="0">
18 <widget class="QScrollArea" name="scrollArea">
19 <property name="widgetResizable">
20 <bool>true</bool>
21 </property>
22 <widget class="QWidget" name="scrollAreaWidgetContents">
23 <property name="geometry">
24 <rect>
25 <x>0</x>
26 <y>0</y>
27 <width>380</width>
28 <height>280</height>
29 </rect>
30 </property>
31 </widget>
32 </widget>
33 </item>
34 </layout>
35 </widget>
36 <resources/>
37 <connections/>
38</ui>
diff --git a/src/yuzu/configuration/configure_per_general.ui b/src/yuzu/configuration/configure_per_general.ui
deleted file mode 100644
index 8fdd96fa4..000000000
--- a/src/yuzu/configuration/configure_per_general.ui
+++ /dev/null
@@ -1,276 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigurePerGameGeneral</class>
4 <widget class="QDialog" name="ConfigurePerGameGeneral">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>400</width>
10 <height>520</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>ConfigurePerGameGeneral</string>
15 </property>
16 <layout class="QHBoxLayout" name="HorizontalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="VerticalLayout">
19 <item>
20 <widget class="QGroupBox" name="GeneralGroupBox">
21 <property name="title">
22 <string>Info</string>
23 </property>
24 <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
25 <item>
26 <layout class="QGridLayout" name="gridLayout_2">
27 <item row="6" column="1" colspan="2">
28 <widget class="QLineEdit" name="display_filename">
29 <property name="enabled">
30 <bool>true</bool>
31 </property>
32 <property name="readOnly">
33 <bool>true</bool>
34 </property>
35 </widget>
36 </item>
37 <item row="0" column="1">
38 <widget class="QLineEdit" name="display_name">
39 <property name="enabled">
40 <bool>true</bool>
41 </property>
42 <property name="readOnly">
43 <bool>true</bool>
44 </property>
45 </widget>
46 </item>
47 <item row="1" column="0">
48 <widget class="QLabel" name="label_2">
49 <property name="text">
50 <string>Developer</string>
51 </property>
52 </widget>
53 </item>
54 <item row="5" column="1" colspan="2">
55 <widget class="QLineEdit" name="display_size">
56 <property name="enabled">
57 <bool>true</bool>
58 </property>
59 <property name="readOnly">
60 <bool>true</bool>
61 </property>
62 </widget>
63 </item>
64 <item row="0" column="0">
65 <widget class="QLabel" name="label">
66 <property name="text">
67 <string>Name</string>
68 </property>
69 </widget>
70 </item>
71 <item row="6" column="0">
72 <widget class="QLabel" name="label_7">
73 <property name="text">
74 <string>Filename</string>
75 </property>
76 </widget>
77 </item>
78 <item row="2" column="0">
79 <widget class="QLabel" name="label_3">
80 <property name="text">
81 <string>Version</string>
82 </property>
83 </widget>
84 </item>
85 <item row="4" column="0">
86 <widget class="QLabel" name="label_5">
87 <property name="text">
88 <string>Format</string>
89 </property>
90 </widget>
91 </item>
92 <item row="2" column="1">
93 <widget class="QLineEdit" name="display_version">
94 <property name="enabled">
95 <bool>true</bool>
96 </property>
97 <property name="readOnly">
98 <bool>true</bool>
99 </property>
100 </widget>
101 </item>
102 <item row="4" column="1">
103 <widget class="QLineEdit" name="display_format">
104 <property name="enabled">
105 <bool>true</bool>
106 </property>
107 <property name="readOnly">
108 <bool>true</bool>
109 </property>
110 </widget>
111 </item>
112 <item row="5" column="0">
113 <widget class="QLabel" name="label_6">
114 <property name="text">
115 <string>Size</string>
116 </property>
117 </widget>
118 </item>
119 <item row="1" column="1">
120 <widget class="QLineEdit" name="display_developer">
121 <property name="enabled">
122 <bool>true</bool>
123 </property>
124 <property name="readOnly">
125 <bool>true</bool>
126 </property>
127 </widget>
128 </item>
129 <item row="3" column="0">
130 <widget class="QLabel" name="label_4">
131 <property name="text">
132 <string>Title ID</string>
133 </property>
134 </widget>
135 </item>
136 <item row="3" column="1">
137 <widget class="QLineEdit" name="display_title_id">
138 <property name="enabled">
139 <bool>true</bool>
140 </property>
141 <property name="readOnly">
142 <bool>true</bool>
143 </property>
144 </widget>
145 </item>
146 <item row="0" column="2" rowspan="5">
147 <widget class="QGraphicsView" name="icon_view">
148 <property name="sizePolicy">
149 <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
150 <horstretch>0</horstretch>
151 <verstretch>0</verstretch>
152 </sizepolicy>
153 </property>
154 <property name="minimumSize">
155 <size>
156 <width>128</width>
157 <height>128</height>
158 </size>
159 </property>
160 <property name="maximumSize">
161 <size>
162 <width>128</width>
163 <height>128</height>
164 </size>
165 </property>
166 <property name="verticalScrollBarPolicy">
167 <enum>Qt::ScrollBarAlwaysOff</enum>
168 </property>
169 <property name="horizontalScrollBarPolicy">
170 <enum>Qt::ScrollBarAlwaysOff</enum>
171 </property>
172 <property name="sizeAdjustPolicy">
173 <enum>QAbstractScrollArea::AdjustToContents</enum>
174 </property>
175 <property name="interactive">
176 <bool>false</bool>
177 </property>
178 </widget>
179 </item>
180 </layout>
181 </item>
182 </layout>
183 </widget>
184 </item>
185 <item>
186 <widget class="QGroupBox" name="PerformanceGroupBox">
187 <property name="title">
188 <string>Add-Ons</string>
189 </property>
190 <layout class="QHBoxLayout" name="PerformanceHorizontalLayout">
191 <item>
192 <widget class="QScrollArea" name="scrollArea">
193 <property name="widgetResizable">
194 <bool>true</bool>
195 </property>
196 <widget class="QWidget" name="scrollAreaWidgetContents">
197 <property name="geometry">
198 <rect>
199 <x>0</x>
200 <y>0</y>
201 <width>350</width>
202 <height>169</height>
203 </rect>
204 </property>
205 </widget>
206 </widget>
207 </item>
208 <item>
209 <layout class="QVBoxLayout" name="PerformanceVerticalLayout"/>
210 </item>
211 </layout>
212 </widget>
213 </item>
214 <item>
215 <spacer name="verticalSpacer">
216 <property name="orientation">
217 <enum>Qt::Vertical</enum>
218 </property>
219 <property name="sizeType">
220 <enum>QSizePolicy::Fixed</enum>
221 </property>
222 <property name="sizeHint" stdset="0">
223 <size>
224 <width>20</width>
225 <height>40</height>
226 </size>
227 </property>
228 </spacer>
229 </item>
230 <item>
231 <widget class="QDialogButtonBox" name="buttonBox">
232 <property name="standardButtons">
233 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
234 </property>
235 </widget>
236 </item>
237 </layout>
238 </item>
239 </layout>
240 </widget>
241 <resources/>
242 <connections>
243 <connection>
244 <sender>buttonBox</sender>
245 <signal>accepted()</signal>
246 <receiver>ConfigurePerGameGeneral</receiver>
247 <slot>accept()</slot>
248 <hints>
249 <hint type="sourcelabel">
250 <x>269</x>
251 <y>567</y>
252 </hint>
253 <hint type="destinationlabel">
254 <x>269</x>
255 <y>294</y>
256 </hint>
257 </hints>
258 </connection>
259 <connection>
260 <sender>buttonBox</sender>
261 <signal>rejected()</signal>
262 <receiver>ConfigurePerGameGeneral</receiver>
263 <slot>reject()</slot>
264 <hints>
265 <hint type="sourcelabel">
266 <x>269</x>
267 <y>567</y>
268 </hint>
269 <hint type="destinationlabel">
270 <x>269</x>
271 <y>294</y>
272 </hint>
273 </hints>
274 </connection>
275 </connections>
276</ui>
diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_service.cpp
index 06566e981..0de7a4f0b 100644
--- a/src/yuzu/configuration/configure_service.cpp
+++ b/src/yuzu/configuration/configure_service.cpp
@@ -68,6 +68,7 @@ void ConfigureService::SetConfiguration() {
68} 68}
69 69
70std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { 70std::pair<QString, QString> ConfigureService::BCATDownloadEvents() {
71#ifdef YUZU_ENABLE_BOXCAT
71 std::optional<std::string> global; 72 std::optional<std::string> global;
72 std::map<std::string, Service::BCAT::EventStatus> map; 73 std::map<std::string, Service::BCAT::EventStatus> map;
73 const auto res = Service::BCAT::Boxcat::GetStatus(global, map); 74 const auto res = Service::BCAT::Boxcat::GetStatus(global, map);
@@ -105,7 +106,10 @@ std::pair<QString, QString> ConfigureService::BCATDownloadEvents() {
105 .arg(QString::fromStdString(key)) 106 .arg(QString::fromStdString(key))
106 .arg(FormatEventStatusString(value)); 107 .arg(FormatEventStatusString(value));
107 } 108 }
108 return {QStringLiteral("Current Boxcat Events"), std::move(out)}; 109 return {tr("Current Boxcat Events"), std::move(out)};
110#else
111 return {tr("Current Boxcat Events"), tr("There are currently no events on boxcat.")};
112#endif
109} 113}
110 114
111void ConfigureService::OnBCATImplChanged() { 115void ConfigureService::OnBCATImplChanged() {
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 10315e7a6..68e02738b 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -14,6 +14,7 @@
14#include "core/core.h" 14#include "core/core.h"
15#include "core/settings.h" 15#include "core/settings.h"
16#include "ui_configure_system.h" 16#include "ui_configure_system.h"
17#include "yuzu/configuration/configuration_shared.h"
17#include "yuzu/configuration/configure_system.h" 18#include "yuzu/configuration/configure_system.h"
18 19
19ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { 20ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) {
@@ -21,20 +22,25 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
21 connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, 22 connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
22 &ConfigureSystem::RefreshConsoleID); 23 &ConfigureSystem::RefreshConsoleID);
23 24
24 connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) { 25 connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](int state) {
25 ui->rng_seed_edit->setEnabled(checked); 26 ui->rng_seed_edit->setEnabled(state == Qt::Checked);
26 if (!checked) { 27 if (state != Qt::Checked) {
27 ui->rng_seed_edit->setText(QStringLiteral("00000000")); 28 ui->rng_seed_edit->setText(QStringLiteral("00000000"));
28 } 29 }
29 }); 30 });
30 31
31 connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) { 32 connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](int state) {
32 ui->custom_rtc_edit->setEnabled(checked); 33 ui->custom_rtc_edit->setEnabled(state == Qt::Checked);
33 if (!checked) { 34 if (state != Qt::Checked) {
34 ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime()); 35 ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime());
35 } 36 }
36 }); 37 });
37 38
39 ui->label_console_id->setVisible(Settings::configuring_global);
40 ui->button_regenerate_console_id->setVisible(Settings::configuring_global);
41
42 SetupPerGameUI();
43
38 SetConfiguration(); 44 SetConfiguration();
39} 45}
40 46
@@ -54,26 +60,58 @@ void ConfigureSystem::RetranslateUI() {
54 60
55void ConfigureSystem::SetConfiguration() { 61void ConfigureSystem::SetConfiguration() {
56 enabled = !Core::System::GetInstance().IsPoweredOn(); 62 enabled = !Core::System::GetInstance().IsPoweredOn();
63 const auto rng_seed =
64 QStringLiteral("%1")
65 .arg(Settings::values.rng_seed.GetValue().value_or(0), 8, 16, QLatin1Char{'0'})
66 .toUpper();
67 const auto rtc_time = Settings::values.custom_rtc.GetValue().value_or(
68 std::chrono::seconds(QDateTime::currentSecsSinceEpoch()));
57 69
58 ui->combo_language->setCurrentIndex(Settings::values.language_index); 70 if (Settings::configuring_global) {
59 ui->combo_region->setCurrentIndex(Settings::values.region_index); 71 ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
60 ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index); 72 ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue());
61 ui->combo_sound->setCurrentIndex(Settings::values.sound_index); 73 ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue());
62 74 ui->combo_sound->setCurrentIndex(Settings::values.sound_index.GetValue());
63 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value()); 75
64 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value()); 76 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.GetValue().has_value());
65 77 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value() &&
66 const auto rng_seed = QStringLiteral("%1") 78 Settings::values.rng_seed.UsingGlobal());
67 .arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}) 79 ui->rng_seed_edit->setText(rng_seed);
68 .toUpper(); 80
69 ui->rng_seed_edit->setText(rng_seed); 81 ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.GetValue().has_value());
70 82 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value() &&
71 ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); 83 Settings::values.rng_seed.UsingGlobal());
72 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); 84 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
85 } else {
86 ConfigurationShared::SetPerGameSetting(ui->combo_language,
87 &Settings::values.language_index);
88 ConfigurationShared::SetPerGameSetting(ui->combo_region, &Settings::values.region_index);
89 ConfigurationShared::SetPerGameSetting(ui->combo_time_zone,
90 &Settings::values.time_zone_index);
91 ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index);
92
93 if (Settings::values.rng_seed.UsingGlobal()) {
94 ui->rng_seed_checkbox->setCheckState(Qt::PartiallyChecked);
95 } else {
96 ui->rng_seed_checkbox->setCheckState(
97 Settings::values.rng_seed.GetValue().has_value() ? Qt::Checked : Qt::Unchecked);
98 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value());
99 if (Settings::values.rng_seed.GetValue().has_value()) {
100 ui->rng_seed_edit->setText(rng_seed);
101 }
102 }
73 103
74 const auto rtc_time = Settings::values.custom_rtc.value_or( 104 if (Settings::values.custom_rtc.UsingGlobal()) {
75 std::chrono::seconds(QDateTime::currentSecsSinceEpoch())); 105 ui->custom_rtc_checkbox->setCheckState(Qt::PartiallyChecked);
76 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); 106 } else {
107 ui->custom_rtc_checkbox->setCheckState(
108 Settings::values.custom_rtc.GetValue().has_value() ? Qt::Checked : Qt::Unchecked);
109 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value());
110 if (Settings::values.custom_rtc.GetValue().has_value()) {
111 ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
112 }
113 }
114 }
77} 115}
78 116
79void ConfigureSystem::ReadSystemSettings() {} 117void ConfigureSystem::ReadSystemSettings() {}
@@ -83,22 +121,78 @@ void ConfigureSystem::ApplyConfiguration() {
83 return; 121 return;
84 } 122 }
85 123
86 Settings::values.language_index = ui->combo_language->currentIndex(); 124 if (Settings::configuring_global) {
87 Settings::values.region_index = ui->combo_region->currentIndex(); 125 // Guard if during game and set to game-specific value
88 Settings::values.time_zone_index = ui->combo_time_zone->currentIndex(); 126 if (Settings::values.language_index.UsingGlobal()) {
89 Settings::values.sound_index = ui->combo_sound->currentIndex(); 127 Settings::values.language_index.SetValue(ui->combo_language->currentIndex());
128 }
129 if (Settings::values.region_index.UsingGlobal()) {
130 Settings::values.region_index.SetValue(ui->combo_region->currentIndex());
131 }
132 if (Settings::values.time_zone_index.UsingGlobal()) {
133 Settings::values.time_zone_index.SetValue(ui->combo_time_zone->currentIndex());
134 }
135 if (Settings::values.sound_index.UsingGlobal()) {
136 Settings::values.sound_index.SetValue(ui->combo_sound->currentIndex());
137 }
138
139 if (Settings::values.rng_seed.UsingGlobal()) {
140 if (ui->rng_seed_checkbox->isChecked()) {
141 Settings::values.rng_seed.SetValue(
142 ui->rng_seed_edit->text().toULongLong(nullptr, 16));
143 } else {
144 Settings::values.rng_seed.SetValue(std::nullopt);
145 }
146 }
90 147
91 if (ui->rng_seed_checkbox->isChecked()) { 148 if (Settings::values.custom_rtc.UsingGlobal()) {
92 Settings::values.rng_seed = ui->rng_seed_edit->text().toULongLong(nullptr, 16); 149 if (ui->custom_rtc_checkbox->isChecked()) {
150 Settings::values.custom_rtc.SetValue(
151 std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
152 } else {
153 Settings::values.custom_rtc.SetValue(std::nullopt);
154 }
155 }
93 } else { 156 } else {
94 Settings::values.rng_seed = std::nullopt; 157 ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index,
95 } 158 ui->combo_language);
159 ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region);
160 ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index,
161 ui->combo_time_zone);
162 ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound);
163
164 switch (ui->rng_seed_checkbox->checkState()) {
165 case Qt::Checked:
166 Settings::values.rng_seed.SetGlobal(false);
167 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toULongLong(nullptr, 16));
168 break;
169 case Qt::Unchecked:
170 Settings::values.rng_seed.SetGlobal(false);
171 Settings::values.rng_seed.SetValue(std::nullopt);
172 break;
173 case Qt::PartiallyChecked:
174 Settings::values.rng_seed.SetGlobal(false);
175 Settings::values.rng_seed.SetValue(std::nullopt);
176 Settings::values.rng_seed.SetGlobal(true);
177 break;
178 }
96 179
97 if (ui->custom_rtc_checkbox->isChecked()) { 180 switch (ui->custom_rtc_checkbox->checkState()) {
98 Settings::values.custom_rtc = 181 case Qt::Checked:
99 std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()); 182 Settings::values.custom_rtc.SetGlobal(false);
100 } else { 183 Settings::values.custom_rtc.SetValue(
101 Settings::values.custom_rtc = std::nullopt; 184 std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
185 break;
186 case Qt::Unchecked:
187 Settings::values.custom_rtc.SetGlobal(false);
188 Settings::values.custom_rtc.SetValue(std::nullopt);
189 break;
190 case Qt::PartiallyChecked:
191 Settings::values.custom_rtc.SetGlobal(false);
192 Settings::values.custom_rtc.SetValue(std::nullopt);
193 Settings::values.custom_rtc.SetGlobal(true);
194 break;
195 }
102 } 196 }
103 197
104 Settings::Apply(); 198 Settings::Apply();
@@ -120,3 +214,25 @@ void ConfigureSystem::RefreshConsoleID() {
120 ui->label_console_id->setText( 214 ui->label_console_id->setText(
121 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); 215 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
122} 216}
217
218void ConfigureSystem::SetupPerGameUI() {
219 if (Settings::configuring_global) {
220 ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal());
221 ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal());
222 ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal());
223 ui->combo_sound->setEnabled(Settings::values.sound_index.UsingGlobal());
224 ui->rng_seed_checkbox->setEnabled(Settings::values.rng_seed.UsingGlobal());
225 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.UsingGlobal());
226 ui->custom_rtc_checkbox->setEnabled(Settings::values.custom_rtc.UsingGlobal());
227 ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.UsingGlobal());
228
229 return;
230 }
231
232 ConfigurationShared::InsertGlobalItem(ui->combo_language);
233 ConfigurationShared::InsertGlobalItem(ui->combo_region);
234 ConfigurationShared::InsertGlobalItem(ui->combo_time_zone);
235 ConfigurationShared::InsertGlobalItem(ui->combo_sound);
236 ui->rng_seed_checkbox->setTristate(true);
237 ui->custom_rtc_checkbox->setTristate(true);
238}
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index 26d42d5c5..f317ef8b5 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -32,6 +32,8 @@ private:
32 32
33 void RefreshConsoleID(); 33 void RefreshConsoleID();
34 34
35 void SetupPerGameUI();
36
35 std::unique_ptr<Ui::ConfigureSystem> ui; 37 std::unique_ptr<Ui::ConfigureSystem> ui;
36 bool enabled = false; 38 bool enabled = false;
37 39
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index c1ea25fb8..9bb0a0109 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -2,10 +2,13 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <fmt/format.h>
6
5#include "yuzu/debugger/wait_tree.h" 7#include "yuzu/debugger/wait_tree.h"
6#include "yuzu/util/util.h" 8#include "yuzu/util/util.h"
7 9
8#include "common/assert.h" 10#include "common/assert.h"
11#include "core/arm/arm_interface.h"
9#include "core/core.h" 12#include "core/core.h"
10#include "core/hle/kernel/handle_table.h" 13#include "core/hle/kernel/handle_table.h"
11#include "core/hle/kernel/mutex.h" 14#include "core/hle/kernel/mutex.h"
@@ -59,8 +62,10 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList()
59 std::size_t row = 0; 62 std::size_t row = 0;
60 auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) { 63 auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) {
61 for (std::size_t i = 0; i < threads.size(); ++i) { 64 for (std::size_t i = 0; i < threads.size(); ++i) {
62 item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i])); 65 if (!threads[i]->IsHLEThread()) {
63 item_list.back()->row = row; 66 item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i]));
67 item_list.back()->row = row;
68 }
64 ++row; 69 ++row;
65 } 70 }
66 }; 71 };
@@ -114,20 +119,21 @@ QString WaitTreeCallstack::GetText() const {
114std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { 119std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const {
115 std::vector<std::unique_ptr<WaitTreeItem>> list; 120 std::vector<std::unique_ptr<WaitTreeItem>> list;
116 121
117 constexpr std::size_t BaseRegister = 29; 122 if (thread.IsHLEThread()) {
118 auto& memory = Core::System::GetInstance().Memory(); 123 return list;
119 u64 base_pointer = thread.GetContext64().cpu_registers[BaseRegister]; 124 }
120 125
121 while (base_pointer != 0) { 126 if (thread.GetOwnerProcess() == nullptr || !thread.GetOwnerProcess()->Is64BitProcess()) {
122 const u64 lr = memory.Read64(base_pointer + sizeof(u64)); 127 return list;
123 if (lr == 0) { 128 }
124 break;
125 }
126 129
127 list.push_back(std::make_unique<WaitTreeText>( 130 auto backtrace = Core::ARM_Interface::GetBacktraceFromContext(Core::System::GetInstance(),
128 tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'}))); 131 thread.GetContext64());
129 132
130 base_pointer = memory.Read64(base_pointer); 133 for (auto& entry : backtrace) {
134 std::string s = fmt::format("{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
135 entry.original_address, entry.offset, entry.name);
136 list.push_back(std::make_unique<WaitTreeText>(QString::fromStdString(s)));
131 } 137 }
132 138
133 return list; 139 return list;
@@ -206,7 +212,15 @@ QString WaitTreeThread::GetText() const {
206 status = tr("running"); 212 status = tr("running");
207 break; 213 break;
208 case Kernel::ThreadStatus::Ready: 214 case Kernel::ThreadStatus::Ready:
209 status = tr("ready"); 215 if (!thread.IsPaused()) {
216 if (thread.WasRunning()) {
217 status = tr("running");
218 } else {
219 status = tr("ready");
220 }
221 } else {
222 status = tr("paused");
223 }
210 break; 224 break;
211 case Kernel::ThreadStatus::Paused: 225 case Kernel::ThreadStatus::Paused:
212 status = tr("paused"); 226 status = tr("paused");
@@ -254,7 +268,15 @@ QColor WaitTreeThread::GetColor() const {
254 case Kernel::ThreadStatus::Running: 268 case Kernel::ThreadStatus::Running:
255 return QColor(Qt::GlobalColor::darkGreen); 269 return QColor(Qt::GlobalColor::darkGreen);
256 case Kernel::ThreadStatus::Ready: 270 case Kernel::ThreadStatus::Ready:
257 return QColor(Qt::GlobalColor::darkBlue); 271 if (!thread.IsPaused()) {
272 if (thread.WasRunning()) {
273 return QColor(Qt::GlobalColor::darkGreen);
274 } else {
275 return QColor(Qt::GlobalColor::darkBlue);
276 }
277 } else {
278 return QColor(Qt::GlobalColor::lightGray);
279 }
258 case Kernel::ThreadStatus::Paused: 280 case Kernel::ThreadStatus::Paused:
259 return QColor(Qt::GlobalColor::lightGray); 281 return QColor(Qt::GlobalColor::lightGray);
260 case Kernel::ThreadStatus::WaitHLEEvent: 282 case Kernel::ThreadStatus::WaitHLEEvent:
@@ -319,7 +341,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
319 341
320 if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { 342 if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) {
321 list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetSynchronizationObjects(), 343 list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetSynchronizationObjects(),
322 thread.IsSleepingOnWait())); 344 thread.IsWaitingSync()));
323 } 345 }
324 346
325 list.push_back(std::make_unique<WaitTreeCallstack>(thread)); 347 list.push_back(std::make_unique<WaitTreeCallstack>(thread));
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 270cccc77..4d501a8f9 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -16,7 +16,7 @@
16#include "applets/software_keyboard.h" 16#include "applets/software_keyboard.h"
17#include "applets/web_browser.h" 17#include "applets/web_browser.h"
18#include "configuration/configure_input.h" 18#include "configuration/configure_input.h"
19#include "configuration/configure_per_general.h" 19#include "configuration/configure_per_game.h"
20#include "core/file_sys/vfs.h" 20#include "core/file_sys/vfs.h"
21#include "core/file_sys/vfs_real.h" 21#include "core/file_sys/vfs_real.h"
22#include "core/frontend/applets/general_frontend.h" 22#include "core/frontend/applets/general_frontend.h"
@@ -56,6 +56,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
56#include <QShortcut> 56#include <QShortcut>
57#include <QStatusBar> 57#include <QStatusBar>
58#include <QSysInfo> 58#include <QSysInfo>
59#include <QUrl>
59#include <QtConcurrent/QtConcurrent> 60#include <QtConcurrent/QtConcurrent>
60 61
61#include <fmt/format.h> 62#include <fmt/format.h>
@@ -217,7 +218,20 @@ GMainWindow::GMainWindow()
217 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", yuzu_build_version, Common::g_scm_branch, 218 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", yuzu_build_version, Common::g_scm_branch,
218 Common::g_scm_desc); 219 Common::g_scm_desc);
219#ifdef ARCHITECTURE_x86_64 220#ifdef ARCHITECTURE_x86_64
220 LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string); 221 const auto& caps = Common::GetCPUCaps();
222 std::string cpu_string = caps.cpu_string;
223 if (caps.avx || caps.avx2 || caps.avx512) {
224 cpu_string += " | AVX";
225 if (caps.avx512) {
226 cpu_string += "512";
227 } else if (caps.avx2) {
228 cpu_string += '2';
229 }
230 if (caps.fma || caps.fma4) {
231 cpu_string += " | FMA";
232 }
233 }
234 LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
221#endif 235#endif
222 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); 236 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
223 LOG_INFO(Frontend, "Host RAM: {:.2f} GB", 237 LOG_INFO(Frontend, "Host RAM: {:.2f} GB",
@@ -520,14 +534,36 @@ void GMainWindow::InitializeWidgets() {
520 if (emulation_running) { 534 if (emulation_running) {
521 return; 535 return;
522 } 536 }
523 Settings::values.use_asynchronous_gpu_emulation = 537 bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
524 !Settings::values.use_asynchronous_gpu_emulation; 538 Settings::values.use_multi_core.GetValue();
525 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); 539 Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
540 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
526 Settings::Apply(); 541 Settings::Apply();
527 }); 542 });
528 async_status_button->setText(tr("ASYNC")); 543 async_status_button->setText(tr("ASYNC"));
529 async_status_button->setCheckable(true); 544 async_status_button->setCheckable(true);
530 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); 545 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
546
547 // Setup Multicore button
548 multicore_status_button = new QPushButton();
549 multicore_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
550 multicore_status_button->setFocusPolicy(Qt::NoFocus);
551 connect(multicore_status_button, &QPushButton::clicked, [&] {
552 if (emulation_running) {
553 return;
554 }
555 Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue());
556 bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
557 Settings::values.use_multi_core.GetValue();
558 Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
559 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
560 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
561 Settings::Apply();
562 });
563 multicore_status_button->setText(tr("MULTICORE"));
564 multicore_status_button->setCheckable(true);
565 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
566 statusBar()->insertPermanentWidget(0, multicore_status_button);
531 statusBar()->insertPermanentWidget(0, async_status_button); 567 statusBar()->insertPermanentWidget(0, async_status_button);
532 568
533 // Setup Renderer API button 569 // Setup Renderer API button
@@ -545,16 +581,16 @@ void GMainWindow::InitializeWidgets() {
545 renderer_status_button->setCheckable(false); 581 renderer_status_button->setCheckable(false);
546 renderer_status_button->setDisabled(true); 582 renderer_status_button->setDisabled(true);
547#else 583#else
548 renderer_status_button->setChecked(Settings::values.renderer_backend == 584 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
549 Settings::RendererBackend::Vulkan); 585 Settings::RendererBackend::Vulkan);
550 connect(renderer_status_button, &QPushButton::clicked, [=] { 586 connect(renderer_status_button, &QPushButton::clicked, [=] {
551 if (emulation_running) { 587 if (emulation_running) {
552 return; 588 return;
553 } 589 }
554 if (renderer_status_button->isChecked()) { 590 if (renderer_status_button->isChecked()) {
555 Settings::values.renderer_backend = Settings::RendererBackend::Vulkan; 591 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Vulkan);
556 } else { 592 } else {
557 Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; 593 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL);
558 } 594 }
559 595
560 Settings::Apply(); 596 Settings::Apply();
@@ -653,6 +689,11 @@ void GMainWindow::InitializeHotkeys() {
653 ui.action_Capture_Screenshot->setShortcutContext( 689 ui.action_Capture_Screenshot->setShortcutContext(
654 hotkey_registry.GetShortcutContext(main_window, capture_screenshot)); 690 hotkey_registry.GetShortcutContext(main_window, capture_screenshot));
655 691
692 ui.action_Fullscreen->setShortcut(
693 hotkey_registry.GetHotkey(main_window, fullscreen, this)->key());
694 ui.action_Fullscreen->setShortcutContext(
695 hotkey_registry.GetShortcutContext(main_window, fullscreen));
696
656 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this), 697 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this),
657 &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile); 698 &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile);
658 connect( 699 connect(
@@ -686,24 +727,24 @@ void GMainWindow::InitializeHotkeys() {
686 }); 727 });
687 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this), 728 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this),
688 &QShortcut::activated, this, [&] { 729 &QShortcut::activated, this, [&] {
689 Settings::values.use_frame_limit = !Settings::values.use_frame_limit; 730 Settings::values.use_frame_limit.SetValue(
731 !Settings::values.use_frame_limit.GetValue());
690 UpdateStatusBar(); 732 UpdateStatusBar();
691 }); 733 });
692 // TODO: Remove this comment/static whenever the next major release of 734 constexpr u16 SPEED_LIMIT_STEP = 5;
693 // MSVC occurs and we make it a requirement (see:
694 // https://developercommunity.visualstudio.com/content/problem/93922/constexprs-are-trying-to-be-captured-in-lambda-fun.html)
695 static constexpr u16 SPEED_LIMIT_STEP = 5;
696 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this), 735 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this),
697 &QShortcut::activated, this, [&] { 736 &QShortcut::activated, this, [&] {
698 if (Settings::values.frame_limit < 9999 - SPEED_LIMIT_STEP) { 737 if (Settings::values.frame_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) {
699 Settings::values.frame_limit += SPEED_LIMIT_STEP; 738 Settings::values.frame_limit.SetValue(SPEED_LIMIT_STEP +
739 Settings::values.frame_limit.GetValue());
700 UpdateStatusBar(); 740 UpdateStatusBar();
701 } 741 }
702 }); 742 });
703 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this), 743 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this),
704 &QShortcut::activated, this, [&] { 744 &QShortcut::activated, this, [&] {
705 if (Settings::values.frame_limit > SPEED_LIMIT_STEP) { 745 if (Settings::values.frame_limit.GetValue() > SPEED_LIMIT_STEP) {
706 Settings::values.frame_limit -= SPEED_LIMIT_STEP; 746 Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() -
747 SPEED_LIMIT_STEP);
707 UpdateStatusBar(); 748 UpdateStatusBar();
708 } 749 }
709 }); 750 });
@@ -715,7 +756,7 @@ void GMainWindow::InitializeHotkeys() {
715 }); 756 });
716 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), 757 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this),
717 &QShortcut::activated, this, [&] { 758 &QShortcut::activated, this, [&] {
718 if (emu_thread->IsRunning()) { 759 if (emu_thread != nullptr && emu_thread->IsRunning()) {
719 OnCaptureScreenshot(); 760 OnCaptureScreenshot();
720 } 761 }
721 }); 762 });
@@ -726,6 +767,9 @@ void GMainWindow::InitializeHotkeys() {
726 Settings::values.use_docked_mode); 767 Settings::values.use_docked_mode);
727 dock_status_button->setChecked(Settings::values.use_docked_mode); 768 dock_status_button->setChecked(Settings::values.use_docked_mode);
728 }); 769 });
770 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this),
771 &QShortcut::activated, this,
772 [] { Settings::values.audio_muted = !Settings::values.audio_muted; });
729} 773}
730 774
731void GMainWindow::SetDefaultUIGeometry() { 775void GMainWindow::SetDefaultUIGeometry() {
@@ -826,6 +870,10 @@ void GMainWindow::ConnectMenuEvents() {
826 connect(ui.action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame); 870 connect(ui.action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame);
827 connect(ui.action_Report_Compatibility, &QAction::triggered, this, 871 connect(ui.action_Report_Compatibility, &QAction::triggered, this,
828 &GMainWindow::OnMenuReportCompatibility); 872 &GMainWindow::OnMenuReportCompatibility);
873 connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage);
874 connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this,
875 &GMainWindow::OnOpenQuickstartGuide);
876 connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ);
829 connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); }); 877 connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); });
830 connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); 878 connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
831 879
@@ -839,10 +887,6 @@ void GMainWindow::ConnectMenuEvents() {
839 connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize); 887 connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize);
840 888
841 // Fullscreen 889 // Fullscreen
842 ui.action_Fullscreen->setShortcut(
843 hotkey_registry
844 .GetHotkey(QStringLiteral("Main Window"), QStringLiteral("Fullscreen"), this)
845 ->key());
846 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 890 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
847 891
848 // Movie 892 // Movie
@@ -910,6 +954,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
910 nullptr, // E-Commerce 954 nullptr, // E-Commerce
911 }); 955 });
912 956
957 system.RegisterHostThread();
958
913 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; 959 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
914 960
915 const auto drd_callout = 961 const auto drd_callout =
@@ -996,6 +1042,17 @@ void GMainWindow::BootGame(const QString& filename) {
996 LOG_INFO(Frontend, "yuzu starting..."); 1042 LOG_INFO(Frontend, "yuzu starting...");
997 StoreRecentFile(filename); // Put the filename on top of the list 1043 StoreRecentFile(filename); // Put the filename on top of the list
998 1044
1045 u64 title_id{0};
1046
1047 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
1048 const auto loader = Loader::GetLoader(v_file);
1049 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
1050 // Load per game settings
1051 Config per_game_config(fmt::format("{:016X}.ini", title_id), false);
1052 }
1053
1054 Settings::LogSettings();
1055
999 if (UISettings::values.select_user_on_boot) { 1056 if (UISettings::values.select_user_on_boot) {
1000 SelectAndSetCurrentUser(); 1057 SelectAndSetCurrentUser();
1001 } 1058 }
@@ -1020,12 +1077,14 @@ void GMainWindow::BootGame(const QString& filename) {
1020 &LoadingScreen::OnLoadProgress, Qt::QueuedConnection); 1077 &LoadingScreen::OnLoadProgress, Qt::QueuedConnection);
1021 1078
1022 // Update the GUI 1079 // Update the GUI
1080 UpdateStatusButtons();
1023 if (ui.action_Single_Window_Mode->isChecked()) { 1081 if (ui.action_Single_Window_Mode->isChecked()) {
1024 game_list->hide(); 1082 game_list->hide();
1025 game_list_placeholder->hide(); 1083 game_list_placeholder->hide();
1026 } 1084 }
1027 status_bar_update_timer.start(2000); 1085 status_bar_update_timer.start(2000);
1028 async_status_button->setDisabled(true); 1086 async_status_button->setDisabled(true);
1087 multicore_status_button->setDisabled(true);
1029 renderer_status_button->setDisabled(true); 1088 renderer_status_button->setDisabled(true);
1030 1089
1031 if (UISettings::values.hide_mouse) { 1090 if (UISettings::values.hide_mouse) {
@@ -1034,20 +1093,20 @@ void GMainWindow::BootGame(const QString& filename) {
1034 ui.centralwidget->setMouseTracking(true); 1093 ui.centralwidget->setMouseTracking(true);
1035 } 1094 }
1036 1095
1037 const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
1038
1039 std::string title_name; 1096 std::string title_name;
1097 std::string title_version;
1040 const auto res = Core::System::GetInstance().GetGameName(title_name); 1098 const auto res = Core::System::GetInstance().GetGameName(title_name);
1041 if (res != Loader::ResultStatus::Success) {
1042 const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata();
1043 if (metadata.first != nullptr)
1044 title_name = metadata.first->GetApplicationName();
1045 1099
1046 if (title_name.empty()) 1100 const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata();
1047 title_name = FileUtil::GetFilename(filename.toStdString()); 1101 if (metadata.first != nullptr) {
1102 title_version = metadata.first->GetVersionString();
1103 title_name = metadata.first->GetApplicationName();
1104 }
1105 if (res != Loader::ResultStatus::Success || title_name.empty()) {
1106 title_name = FileUtil::GetFilename(filename.toStdString());
1048 } 1107 }
1049 LOG_INFO(Frontend, "Booting game: {:016X} | {}", title_id, title_name); 1108 LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
1050 UpdateWindowTitle(QString::fromStdString(title_name)); 1109 UpdateWindowTitle(title_name, title_version);
1051 1110
1052 loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); 1111 loading_screen->Prepare(Core::System::GetInstance().GetAppLoader());
1053 loading_screen->show(); 1112 loading_screen->show();
@@ -1113,6 +1172,7 @@ void GMainWindow::ShutdownGame() {
1113 game_fps_label->setVisible(false); 1172 game_fps_label->setVisible(false);
1114 emu_frametime_label->setVisible(false); 1173 emu_frametime_label->setVisible(false);
1115 async_status_button->setEnabled(true); 1174 async_status_button->setEnabled(true);
1175 multicore_status_button->setEnabled(true);
1116#ifdef HAS_VULKAN 1176#ifdef HAS_VULKAN
1117 renderer_status_button->setEnabled(true); 1177 renderer_status_button->setEnabled(true);
1118#endif 1178#endif
@@ -1474,7 +1534,7 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
1474 return; 1534 return;
1475 } 1535 }
1476 1536
1477 ConfigurePerGameGeneral dialog(this, title_id); 1537 ConfigurePerGame dialog(this, title_id);
1478 dialog.LoadFromFile(v_file); 1538 dialog.LoadFromFile(v_file);
1479 auto result = dialog.exec(); 1539 auto result = dialog.exec();
1480 if (result == QDialog::Accepted) { 1540 if (result == QDialog::Accepted) {
@@ -1485,7 +1545,14 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
1485 game_list->PopulateAsync(UISettings::values.game_dirs); 1545 game_list->PopulateAsync(UISettings::values.game_dirs);
1486 } 1546 }
1487 1547
1488 config->Save(); 1548 // Do not cause the global config to write local settings into the config file
1549 Settings::RestoreGlobalState();
1550
1551 if (!Core::System::GetInstance().IsPoweredOn()) {
1552 config->Save();
1553 }
1554 } else {
1555 Settings::RestoreGlobalState();
1489 } 1556 }
1490} 1557}
1491 1558
@@ -1772,6 +1839,9 @@ void GMainWindow::OnStopGame() {
1772 } 1839 }
1773 1840
1774 ShutdownGame(); 1841 ShutdownGame();
1842
1843 Settings::RestoreGlobalState();
1844 UpdateStatusButtons();
1775} 1845}
1776 1846
1777void GMainWindow::OnLoadComplete() { 1847void GMainWindow::OnLoadComplete() {
@@ -1797,6 +1867,26 @@ void GMainWindow::OnMenuReportCompatibility() {
1797 } 1867 }
1798} 1868}
1799 1869
1870void GMainWindow::OpenURL(const QUrl& url) {
1871 const bool open = QDesktopServices::openUrl(url);
1872 if (!open) {
1873 QMessageBox::warning(this, tr("Error opening URL"),
1874 tr("Unable to open the URL \"%1\".").arg(url.toString()));
1875 }
1876}
1877
1878void GMainWindow::OnOpenModsPage() {
1879 OpenURL(QUrl(QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods")));
1880}
1881
1882void GMainWindow::OnOpenQuickstartGuide() {
1883 OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/help/quickstart/")));
1884}
1885
1886void GMainWindow::OnOpenFAQ() {
1887 OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/wiki/faq/")));
1888}
1889
1800void GMainWindow::ToggleFullscreen() { 1890void GMainWindow::ToggleFullscreen() {
1801 if (!emulation_running) { 1891 if (!emulation_running) {
1802 return; 1892 return;
@@ -1859,7 +1949,7 @@ void GMainWindow::ToggleWindowMode() {
1859 1949
1860void GMainWindow::ResetWindowSize() { 1950void GMainWindow::ResetWindowSize() {
1861 const auto aspect_ratio = Layout::EmulationAspectRatio( 1951 const auto aspect_ratio = Layout::EmulationAspectRatio(
1862 static_cast<Layout::AspectRatio>(Settings::values.aspect_ratio), 1952 static_cast<Layout::AspectRatio>(Settings::values.aspect_ratio.GetValue()),
1863 static_cast<float>(Layout::ScreenUndocked::Height) / Layout::ScreenUndocked::Width); 1953 static_cast<float>(Layout::ScreenUndocked::Height) / Layout::ScreenUndocked::Width);
1864 if (!ui.action_Single_Window_Mode->isChecked()) { 1954 if (!ui.action_Single_Window_Mode->isChecked()) {
1865 render_window->resize(Layout::ScreenUndocked::Height / aspect_ratio, 1955 render_window->resize(Layout::ScreenUndocked::Height / aspect_ratio,
@@ -1907,12 +1997,7 @@ void GMainWindow::OnConfigure() {
1907 ui.centralwidget->setMouseTracking(false); 1997 ui.centralwidget->setMouseTracking(false);
1908 } 1998 }
1909 1999
1910 dock_status_button->setChecked(Settings::values.use_docked_mode); 2000 UpdateStatusButtons();
1911 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
1912#ifdef HAS_VULKAN
1913 renderer_status_button->setChecked(Settings::values.renderer_backend ==
1914 Settings::RendererBackend::Vulkan);
1915#endif
1916} 2001}
1917 2002
1918void GMainWindow::OnLoadAmiibo() { 2003void GMainWindow::OnLoadAmiibo() {
@@ -1995,7 +2080,8 @@ void GMainWindow::OnCaptureScreenshot() {
1995 OnStartGame(); 2080 OnStartGame();
1996} 2081}
1997 2082
1998void GMainWindow::UpdateWindowTitle(const QString& title_name) { 2083void GMainWindow::UpdateWindowTitle(const std::string& title_name,
2084 const std::string& title_version) {
1999 const auto full_name = std::string(Common::g_build_fullname); 2085 const auto full_name = std::string(Common::g_build_fullname);
2000 const auto branch_name = std::string(Common::g_scm_branch); 2086 const auto branch_name = std::string(Common::g_scm_branch);
2001 const auto description = std::string(Common::g_scm_desc); 2087 const auto description = std::string(Common::g_scm_desc);
@@ -2004,7 +2090,7 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) {
2004 const auto date = 2090 const auto date =
2005 QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString(); 2091 QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString();
2006 2092
2007 if (title_name.isEmpty()) { 2093 if (title_name.empty()) {
2008 const auto fmt = std::string(Common::g_title_bar_format_idle); 2094 const auto fmt = std::string(Common::g_title_bar_format_idle);
2009 setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt, 2095 setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt,
2010 full_name, branch_name, description, 2096 full_name, branch_name, description,
@@ -2012,8 +2098,8 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) {
2012 } else { 2098 } else {
2013 const auto fmt = std::string(Common::g_title_bar_format_running); 2099 const auto fmt = std::string(Common::g_title_bar_format_running);
2014 setWindowTitle(QString::fromStdString( 2100 setWindowTitle(QString::fromStdString(
2015 fmt::format(fmt.empty() ? "yuzu {0}| {3} | {1}-{2}" : fmt, full_name, branch_name, 2101 fmt::format(fmt.empty() ? "yuzu {0}| {3} | {6} | {1}-{2}" : fmt, full_name, branch_name,
2016 description, title_name.toStdString(), date, build_id))); 2102 description, title_name, date, build_id, title_version)));
2017 } 2103 }
2018} 2104}
2019 2105
@@ -2025,21 +2111,34 @@ void GMainWindow::UpdateStatusBar() {
2025 2111
2026 auto results = Core::System::GetInstance().GetAndResetPerfStats(); 2112 auto results = Core::System::GetInstance().GetAndResetPerfStats();
2027 2113
2028 if (Settings::values.use_frame_limit) { 2114 if (Settings::values.use_frame_limit.GetValue()) {
2029 emu_speed_label->setText(tr("Speed: %1% / %2%") 2115 emu_speed_label->setText(tr("Speed: %1% / %2%")
2030 .arg(results.emulation_speed * 100.0, 0, 'f', 0) 2116 .arg(results.emulation_speed * 100.0, 0, 'f', 0)
2031 .arg(Settings::values.frame_limit)); 2117 .arg(Settings::values.frame_limit.GetValue()));
2032 } else { 2118 } else {
2033 emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); 2119 emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
2034 } 2120 }
2035 game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0)); 2121 game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0));
2036 emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); 2122 emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2));
2037 2123
2038 emu_speed_label->setVisible(true); 2124 emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue());
2039 game_fps_label->setVisible(true); 2125 game_fps_label->setVisible(true);
2040 emu_frametime_label->setVisible(true); 2126 emu_frametime_label->setVisible(true);
2041} 2127}
2042 2128
2129void GMainWindow::UpdateStatusButtons() {
2130 dock_status_button->setChecked(Settings::values.use_docked_mode);
2131 multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
2132 Settings::values.use_asynchronous_gpu_emulation.SetValue(
2133 Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
2134 Settings::values.use_multi_core.GetValue());
2135 async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
2136#ifdef HAS_VULKAN
2137 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
2138 Settings::RendererBackend::Vulkan);
2139#endif
2140}
2141
2043void GMainWindow::HideMouseCursor() { 2142void GMainWindow::HideMouseCursor() {
2044 if (emu_thread == nullptr || UISettings::values.hide_mouse == false) { 2143 if (emu_thread == nullptr || UISettings::values.hide_mouse == false) {
2045 mouse_hide_timer.stop(); 2144 mouse_hide_timer.stop();
@@ -2123,6 +2222,9 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
2123 if (answer == QMessageBox::Yes) { 2222 if (answer == QMessageBox::Yes) {
2124 if (emu_thread) { 2223 if (emu_thread) {
2125 ShutdownGame(); 2224 ShutdownGame();
2225
2226 Settings::RestoreGlobalState();
2227 UpdateStatusButtons();
2126 } 2228 }
2127 } else { 2229 } else {
2128 // Only show the message if the game is still running. 2230 // Only show the message if the game is still running.
@@ -2154,7 +2256,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
2154 "title.keys_autogenerated"); 2256 "title.keys_autogenerated");
2155 } 2257 }
2156 2258
2157 Core::Crypto::KeyManager keys{}; 2259 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
2158 if (keys.BaseDeriveNecessary()) { 2260 if (keys.BaseDeriveNecessary()) {
2159 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( 2261 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
2160 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)}; 2262 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)};
@@ -2285,9 +2387,13 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
2285 hotkey_registry.SaveHotkeys(); 2387 hotkey_registry.SaveHotkeys();
2286 2388
2287 // Shutdown session if the emu thread is active... 2389 // Shutdown session if the emu thread is active...
2288 if (emu_thread != nullptr) 2390 if (emu_thread != nullptr) {
2289 ShutdownGame(); 2391 ShutdownGame();
2290 2392
2393 Settings::RestoreGlobalState();
2394 UpdateStatusButtons();
2395 }
2396
2291 render_window->close(); 2397 render_window->close();
2292 2398
2293 QWidget::closeEvent(event); 2399 QWidget::closeEvent(event);
@@ -2467,8 +2573,6 @@ int main(int argc, char* argv[]) {
2467 QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window, 2573 QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window,
2468 &GMainWindow::OnAppFocusStateChanged); 2574 &GMainWindow::OnAppFocusStateChanged);
2469 2575
2470 Settings::LogSettings();
2471
2472 int result = app.exec(); 2576 int result = app.exec();
2473 detached_tasks.WaitForAllTasks(); 2577 detached_tasks.WaitForAllTasks();
2474 return result; 2578 return result;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 4f4c8ddbe..8e3d39c38 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -181,6 +181,9 @@ private slots:
181 void OnPauseGame(); 181 void OnPauseGame();
182 void OnStopGame(); 182 void OnStopGame();
183 void OnMenuReportCompatibility(); 183 void OnMenuReportCompatibility();
184 void OnOpenModsPage();
185 void OnOpenQuickstartGuide();
186 void OnOpenFAQ();
184 /// Called whenever a user selects a game in the game list widget. 187 /// Called whenever a user selects a game in the game list widget.
185 void OnGameListLoadFile(QString game_path); 188 void OnGameListLoadFile(QString game_path);
186 void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); 189 void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
@@ -215,10 +218,13 @@ private slots:
215 218
216private: 219private:
217 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); 220 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
218 void UpdateWindowTitle(const QString& title_name = {}); 221 void UpdateWindowTitle(const std::string& title_name = {},
222 const std::string& title_version = {});
219 void UpdateStatusBar(); 223 void UpdateStatusBar();
224 void UpdateStatusButtons();
220 void HideMouseCursor(); 225 void HideMouseCursor();
221 void ShowMouseCursor(); 226 void ShowMouseCursor();
227 void OpenURL(const QUrl& url);
222 228
223 Ui::MainWindow ui; 229 Ui::MainWindow ui;
224 230
@@ -234,6 +240,7 @@ private:
234 QLabel* game_fps_label = nullptr; 240 QLabel* game_fps_label = nullptr;
235 QLabel* emu_frametime_label = nullptr; 241 QLabel* emu_frametime_label = nullptr;
236 QPushButton* async_status_button = nullptr; 242 QPushButton* async_status_button = nullptr;
243 QPushButton* multicore_status_button = nullptr;
237 QPushButton* renderer_status_button = nullptr; 244 QPushButton* renderer_status_button = nullptr;
238 QPushButton* dock_status_button = nullptr; 245 QPushButton* dock_status_button = nullptr;
239 QTimer status_bar_update_timer; 246 QTimer status_bar_update_timer;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 97c90f50b..bee6e107e 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -113,6 +113,9 @@
113 <string>&amp;Help</string> 113 <string>&amp;Help</string>
114 </property> 114 </property>
115 <addaction name="action_Report_Compatibility"/> 115 <addaction name="action_Report_Compatibility"/>
116 <addaction name="action_Open_Mods_Page"/>
117 <addaction name="action_Open_Quickstart_Guide"/>
118 <addaction name="action_Open_FAQ"/>
116 <addaction name="separator"/> 119 <addaction name="separator"/>
117 <addaction name="action_About"/> 120 <addaction name="action_About"/>
118 </widget> 121 </widget>
@@ -256,6 +259,21 @@
256 <bool>false</bool> 259 <bool>false</bool>
257 </property> 260 </property>
258 </action> 261 </action>
262 <action name="action_Open_Mods_Page">
263 <property name="text">
264 <string>Open Mods Page</string>
265 </property>
266 </action>
267 <action name="action_Open_Quickstart_Guide">
268 <property name="text">
269 <string>Open Quickstart Guide</string>
270 </property>
271 </action>
272 <action name="action_Open_FAQ">
273 <property name="text">
274 <string>FAQ</string>
275 </property>
276 </action>
259 <action name="action_Open_yuzu_Folder"> 277 <action name="action_Open_yuzu_Folder">
260 <property name="text"> 278 <property name="text">
261 <string>Open yuzu Folder</string> 279 <string>Open yuzu Folder</string>
diff --git a/src/yuzu/yuzu.rc b/src/yuzu/yuzu.rc
index 1b253653f..4a3645a71 100644
--- a/src/yuzu/yuzu.rc
+++ b/src/yuzu/yuzu.rc
@@ -16,4 +16,4 @@ IDI_ICON1 ICON "../../dist/yuzu.ico"
16// RT_MANIFEST 16// RT_MANIFEST
17// 17//
18 18
191 RT_MANIFEST "../../dist/yuzu.manifest" 190 RT_MANIFEST "../../dist/yuzu.manifest"
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index c20d48c42..23763144f 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -354,65 +354,72 @@ void Config::ReadValues() {
354 354
355 const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); 355 const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
356 if (rng_seed_enabled) { 356 if (rng_seed_enabled) {
357 Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0); 357 Settings::values.rng_seed.SetValue(sdl2_config->GetInteger("System", "rng_seed", 0));
358 } else { 358 } else {
359 Settings::values.rng_seed = std::nullopt; 359 Settings::values.rng_seed.SetValue(std::nullopt);
360 } 360 }
361 361
362 const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false); 362 const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false);
363 if (custom_rtc_enabled) { 363 if (custom_rtc_enabled) {
364 Settings::values.custom_rtc = 364 Settings::values.custom_rtc.SetValue(
365 std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)); 365 std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)));
366 } else { 366 } else {
367 Settings::values.custom_rtc = std::nullopt; 367 Settings::values.custom_rtc.SetValue(std::nullopt);
368 } 368 }
369 369
370 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1); 370 Settings::values.language_index.SetValue(
371 Settings::values.time_zone_index = sdl2_config->GetInteger("System", "time_zone_index", 0); 371 sdl2_config->GetInteger("System", "language_index", 1));
372 Settings::values.time_zone_index.SetValue(
373 sdl2_config->GetInteger("System", "time_zone_index", 0));
372 374
373 // Core 375 // Core
374 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); 376 Settings::values.use_multi_core.SetValue(
377 sdl2_config->GetBoolean("Core", "use_multi_core", false));
375 378
376 // Renderer 379 // Renderer
377 const int renderer_backend = sdl2_config->GetInteger( 380 const int renderer_backend = sdl2_config->GetInteger(
378 "Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL)); 381 "Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL));
379 Settings::values.renderer_backend = static_cast<Settings::RendererBackend>(renderer_backend); 382 Settings::values.renderer_backend.SetValue(
383 static_cast<Settings::RendererBackend>(renderer_backend));
380 Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false); 384 Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false);
381 Settings::values.vulkan_device = sdl2_config->GetInteger("Renderer", "vulkan_device", 0); 385 Settings::values.vulkan_device.SetValue(
382 386 sdl2_config->GetInteger("Renderer", "vulkan_device", 0));
383 Settings::values.resolution_factor = 387
384 static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); 388 Settings::values.aspect_ratio.SetValue(
385 Settings::values.aspect_ratio = 389 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)));
386 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)); 390 Settings::values.max_anisotropy.SetValue(
387 Settings::values.max_anisotropy = 391 static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)));
388 static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)); 392 Settings::values.use_frame_limit.SetValue(
389 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); 393 sdl2_config->GetBoolean("Renderer", "use_frame_limit", true));
390 Settings::values.frame_limit = 394 Settings::values.frame_limit.SetValue(
391 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); 395 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)));
392 Settings::values.use_disk_shader_cache = 396 Settings::values.use_disk_shader_cache.SetValue(
393 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); 397 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false));
394 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); 398 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
395 Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); 399 Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level));
396 Settings::values.use_asynchronous_gpu_emulation = 400 Settings::values.use_asynchronous_gpu_emulation.SetValue(
397 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); 401 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false));
398 Settings::values.use_vsync = 402 Settings::values.use_vsync.SetValue(
399 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)); 403 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)));
400 Settings::values.use_assembly_shaders = 404 Settings::values.use_assembly_shaders.SetValue(
401 sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", false); 405 sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", false));
402 Settings::values.use_fast_gpu_time = 406 Settings::values.use_fast_gpu_time.SetValue(
403 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true); 407 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true));
404 408
405 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); 409 Settings::values.bg_red.SetValue(
406 Settings::values.bg_green = 410 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)));
407 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)); 411 Settings::values.bg_green.SetValue(
408 Settings::values.bg_blue = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)); 412 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)));
413 Settings::values.bg_blue.SetValue(
414 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)));
409 415
410 // Audio 416 // Audio
411 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); 417 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
412 Settings::values.enable_audio_stretching = 418 Settings::values.enable_audio_stretching.SetValue(
413 sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); 419 sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true));
414 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); 420 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
415 Settings::values.volume = static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1)); 421 Settings::values.volume.SetValue(
422 static_cast<float>(sdl2_config->GetReal("Audio", "volume", 1)));
416 423
417 // Miscellaneous 424 // Miscellaneous
418 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 425 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
@@ -432,6 +439,8 @@ void Config::ReadValues() {
432 Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false); 439 Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false);
433 Settings::values.disable_cpu_opt = 440 Settings::values.disable_cpu_opt =
434 sdl2_config->GetBoolean("Debugging", "disable_cpu_opt", false); 441 sdl2_config->GetBoolean("Debugging", "disable_cpu_opt", false);
442 Settings::values.disable_macro_jit =
443 sdl2_config->GetBoolean("Debugging", "disable_macro_jit", false);
435 444
436 const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); 445 const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
437 std::stringstream ss(title_list); 446 std::stringstream ss(title_list);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index abc6e6e65..45c07ed5d 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -117,11 +117,6 @@ use_hw_renderer =
117# 0: Interpreter (slow), 1 (default): JIT (fast) 117# 0: Interpreter (slow), 1 (default): JIT (fast)
118use_shader_jit = 118use_shader_jit =
119 119
120# Resolution scale factor
121# 0: Auto (scales resolution to window size), 1: Native Switch screen resolution, Otherwise a scale
122# factor for the Switch resolution
123resolution_factor =
124
125# Aspect ratio 120# Aspect ratio
126# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window 121# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window
127aspect_ratio = 122aspect_ratio =
@@ -291,6 +286,8 @@ quest_flag =
291# Determines whether or not JIT CPU optimizations are enabled 286# Determines whether or not JIT CPU optimizations are enabled
292# false: Optimizations Enabled, true: Optimizations Disabled 287# false: Optimizations Enabled, true: Optimizations Disabled
293disable_cpu_opt = 288disable_cpu_opt =
289# Enables/Disables the macro JIT compiler
290disable_macro_jit=false
294 291
295[WebService] 292[WebService]
296# Whether or not to enable telemetry 293# Whether or not to enable telemetry
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 411e7e647..e78025737 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -98,6 +98,9 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen)
98 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); 98 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
99 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); 99 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
100 SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); 100 SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
101 if (Settings::values.renderer_debug) {
102 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
103 }
101 SDL_GL_SetSwapInterval(0); 104 SDL_GL_SetSwapInterval(0);
102 105
103 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, 106 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
@@ -162,7 +165,7 @@ std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateShared
162 165
163void EmuWindow_SDL2_GL::Present() { 166void EmuWindow_SDL2_GL::Present() {
164 SDL_GL_MakeCurrent(render_window, window_context); 167 SDL_GL_MakeCurrent(render_window, window_context);
165 SDL_GL_SetSwapInterval(Settings::values.use_vsync ? 1 : 0); 168 SDL_GL_SetSwapInterval(Settings::values.use_vsync.GetValue() ? 1 : 0);
166 while (IsOpen()) { 169 while (IsOpen()) {
167 system.Renderer().TryPresent(100); 170 system.Renderer().TryPresent(100);
168 SDL_GL_SwapWindow(render_window); 171 SDL_GL_SwapWindow(render_window);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 4d2ea7e9e..512b060a7 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono>
5#include <iostream> 6#include <iostream>
6#include <memory> 7#include <memory>
7#include <string> 8#include <string>
@@ -180,7 +181,7 @@ int main(int argc, char** argv) {
180 Core::System& system{Core::System::GetInstance()}; 181 Core::System& system{Core::System::GetInstance()};
181 182
182 std::unique_ptr<EmuWindow_SDL2> emu_window; 183 std::unique_ptr<EmuWindow_SDL2> emu_window;
183 switch (Settings::values.renderer_backend) { 184 switch (Settings::values.renderer_backend.GetValue()) {
184 case Settings::RendererBackend::OpenGL: 185 case Settings::RendererBackend::OpenGL:
185 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen); 186 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen);
186 break; 187 break;
@@ -236,9 +237,11 @@ int main(int argc, char** argv) {
236 system.Renderer().Rasterizer().LoadDiskResources(); 237 system.Renderer().Rasterizer().LoadDiskResources();
237 238
238 std::thread render_thread([&emu_window] { emu_window->Present(); }); 239 std::thread render_thread([&emu_window] { emu_window->Present(); });
240 system.Run();
239 while (emu_window->IsOpen()) { 241 while (emu_window->IsOpen()) {
240 system.RunLoop(); 242 std::this_thread::sleep_for(std::chrono::milliseconds(1));
241 } 243 }
244 system.Pause();
242 render_thread.join(); 245 render_thread.join();
243 246
244 system.Shutdown(); 247 system.Shutdown();
diff --git a/src/yuzu_cmd/yuzu.rc b/src/yuzu_cmd/yuzu.rc
index 7de8ef3d9..0cde75e2f 100644
--- a/src/yuzu_cmd/yuzu.rc
+++ b/src/yuzu_cmd/yuzu.rc
@@ -14,4 +14,4 @@ YUZU_ICON ICON "../../dist/yuzu.ico"
14// RT_MANIFEST 14// RT_MANIFEST
15// 15//
16 16
171 RT_MANIFEST "../../dist/yuzu.manifest" 170 RT_MANIFEST "../../dist/yuzu.manifest"
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index 3be58b15d..acb22885e 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -81,6 +81,9 @@ void Config::ReadValues() {
81 Settings::values.touchscreen.diameter_x = 15; 81 Settings::values.touchscreen.diameter_x = 15;
82 Settings::values.touchscreen.diameter_y = 15; 82 Settings::values.touchscreen.diameter_y = 15;
83 83
84 Settings::values.use_docked_mode =
85 sdl2_config->GetBoolean("Controls", "use_docked_mode", false);
86
84 // Data Storage 87 // Data Storage
85 Settings::values.use_virtual_sd = 88 Settings::values.use_virtual_sd =
86 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); 89 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
@@ -92,59 +95,59 @@ void Config::ReadValues() {
92 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 95 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
93 96
94 // System 97 // System
95 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
96
97 Settings::values.current_user = std::clamp<int>( 98 Settings::values.current_user = std::clamp<int>(
98 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); 99 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
99 100
100 const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); 101 const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
101 if (rng_seed_enabled) { 102 if (rng_seed_enabled) {
102 Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0); 103 Settings::values.rng_seed.SetValue(sdl2_config->GetInteger("System", "rng_seed", 0));
103 } else { 104 } else {
104 Settings::values.rng_seed = std::nullopt; 105 Settings::values.rng_seed.SetValue(std::nullopt);
105 } 106 }
106 107
107 const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false); 108 const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false);
108 if (custom_rtc_enabled) { 109 if (custom_rtc_enabled) {
109 Settings::values.custom_rtc = 110 Settings::values.custom_rtc.SetValue(
110 std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)); 111 std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0)));
111 } else { 112 } else {
112 Settings::values.custom_rtc = std::nullopt; 113 Settings::values.custom_rtc.SetValue(std::nullopt);
113 } 114 }
114 115
115 // Core 116 // Core
116 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); 117 Settings::values.use_multi_core.SetValue(
118 sdl2_config->GetBoolean("Core", "use_multi_core", false));
117 119
118 // Renderer 120 // Renderer
119 Settings::values.resolution_factor = 121 Settings::values.aspect_ratio.SetValue(
120 static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); 122 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)));
121 Settings::values.aspect_ratio = 123 Settings::values.max_anisotropy.SetValue(
122 static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)); 124 static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)));
123 Settings::values.max_anisotropy = 125 Settings::values.use_frame_limit.SetValue(false);
124 static_cast<int>(sdl2_config->GetInteger("Renderer", "max_anisotropy", 0)); 126 Settings::values.frame_limit.SetValue(100);
125 Settings::values.use_frame_limit = false; 127 Settings::values.use_disk_shader_cache.SetValue(
126 Settings::values.frame_limit = 100; 128 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false));
127 Settings::values.use_disk_shader_cache =
128 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false);
129 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); 129 const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
130 Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); 130 Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level));
131 Settings::values.use_asynchronous_gpu_emulation = 131 Settings::values.use_asynchronous_gpu_emulation.SetValue(
132 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); 132 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false));
133 Settings::values.use_fast_gpu_time = 133 Settings::values.use_fast_gpu_time.SetValue(
134 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true); 134 sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true));
135 135
136 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); 136 Settings::values.bg_red.SetValue(
137 Settings::values.bg_green = 137 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)));
138 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)); 138 Settings::values.bg_green.SetValue(
139 Settings::values.bg_blue = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)); 139 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0)));
140 Settings::values.bg_blue.SetValue(
141 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0)));
140 142
141 // Audio 143 // Audio
142 Settings::values.sink_id = "null"; 144 Settings::values.sink_id = "null";
143 Settings::values.enable_audio_stretching = false; 145 Settings::values.enable_audio_stretching.SetValue(false);
144 Settings::values.audio_device_id = "auto"; 146 Settings::values.audio_device_id = "auto";
145 Settings::values.volume = 0; 147 Settings::values.volume.SetValue(0);
146 148
147 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1); 149 Settings::values.language_index.SetValue(
150 sdl2_config->GetInteger("System", "language_index", 1));
148 151
149 // Miscellaneous 152 // Miscellaneous
150 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 153 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
diff --git a/src/yuzu_tester/default_ini.h b/src/yuzu_tester/default_ini.h
index ca203b64d..41bbbbf60 100644
--- a/src/yuzu_tester/default_ini.h
+++ b/src/yuzu_tester/default_ini.h
@@ -21,11 +21,6 @@ use_hw_renderer =
21# 0: Interpreter (slow), 1 (default): JIT (fast) 21# 0: Interpreter (slow), 1 (default): JIT (fast)
22use_shader_jit = 22use_shader_jit =
23 23
24# Resolution scale factor
25# 0: Auto (scales resolution to window size), 1: Native Switch screen resolution, Otherwise a scale
26# factor for the Switch resolution
27resolution_factor =
28
29# Aspect ratio 24# Aspect ratio
30# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window 25# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window
31aspect_ratio = 26aspect_ratio =
diff --git a/src/yuzu_tester/service/yuzutest.cpp b/src/yuzu_tester/service/yuzutest.cpp
index 85d3f436b..2d3f6e3a7 100644
--- a/src/yuzu_tester/service/yuzutest.cpp
+++ b/src/yuzu_tester/service/yuzutest.cpp
@@ -53,7 +53,7 @@ private:
53 53
54 IPC::ResponseBuilder rb{ctx, 3}; 54 IPC::ResponseBuilder rb{ctx, 3};
55 rb.Push(RESULT_SUCCESS); 55 rb.Push(RESULT_SUCCESS);
56 rb.Push<u32>(write_size); 56 rb.Push<u32>(static_cast<u32>(write_size));
57 } 57 }
58 58
59 void StartIndividual(Kernel::HLERequestContext& ctx) { 59 void StartIndividual(Kernel::HLERequestContext& ctx) {
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 676e70ebd..083667baf 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono>
5#include <iostream> 6#include <iostream>
6#include <memory> 7#include <memory>
7#include <string> 8#include <string>
@@ -255,9 +256,11 @@ int main(int argc, char** argv) {
255 system.GPU().Start(); 256 system.GPU().Start();
256 system.Renderer().Rasterizer().LoadDiskResources(); 257 system.Renderer().Rasterizer().LoadDiskResources();
257 258
259 system.Run();
258 while (!finished) { 260 while (!finished) {
259 system.RunLoop(); 261 std::this_thread::sleep_for(std::chrono::milliseconds(1));
260 } 262 }
263 system.Pause();
261 264
262 detached_tasks.WaitForAllTasks(); 265 detached_tasks.WaitForAllTasks();
263 return return_value; 266 return return_value;
diff --git a/src/yuzu_tester/yuzu.rc b/src/yuzu_tester/yuzu.rc
index 7de8ef3d9..0cde75e2f 100644
--- a/src/yuzu_tester/yuzu.rc
+++ b/src/yuzu_tester/yuzu.rc
@@ -14,4 +14,4 @@ YUZU_ICON ICON "../../dist/yuzu.ico"
14// RT_MANIFEST 14// RT_MANIFEST
15// 15//
16 16
171 RT_MANIFEST "../../dist/yuzu.manifest" 170 RT_MANIFEST "../../dist/yuzu.manifest"