Compare commits

...

322 Commits

Author SHA1 Message Date
ReinUsesLisp 76f178ba6e shader/video: Partially implement VMNMX
Implements the common usages for VMNMX. Inputs with a different size
than 32 bits are not supported and sign mismatches aren't supported
either.

VMNMX works as follows:
It grabs Ra and Rb and applies a maximum/minimum on them (this is
defined by .MX), having in mind the input sign. This result can then be
saturated. After the intermediate result is calculated, it applies
another operation on it using Rc. These operations are merges,
accumulations or another min/max pass.

This instruction allows to implement with a more flexible approach GCN's
min3 and max3 instructions (for instance).
2020-04-12 00:34:42 -03:00
ReinUsesLisp 08470d261d shader_bytecode: Fix I2I_IMM encoding 2020-03-28 18:49:07 -03:00
bunnei ffe1e2b5ec Merge pull request #3562 from perillamint/vrsvc
am: Implement VR related APIs
2020-03-28 13:32:52 -04:00
bunnei 997a802bd6 Merge pull request #3569 from bunnei/hid-stub
services: hid: Stub InitializeSevenSixAxisSensor.
2020-03-28 04:10:45 -04:00
bunnei 75795a9a63 Merge pull request #3573 from FearlessTobi/port-5089
Port citra-emu/citra#5089: "Set render window's focus policy to Qt::StrongFocus"
2020-03-28 01:48:46 -04:00
bunnei 9bd9980372 Merge pull request #3576 from FearlessTobi/port-4906
Port citra-emu/citra#4906: "citra_qt: fix the stuck in fullscreen mode"
2020-03-28 00:28:31 -04:00
bunnei 6286b8dedd Merge pull request #3574 from FearlessTobi/port-5093
Port citra-emu/citra#5093: "Replace non-commercial icons"
2020-03-27 21:43:30 -04:00
FearlessTobi 8ba06aa4e1 yuzu: fix the stuck in fullscreen mode bug
Co-Authored-By: Valentin Vanelslande <vvanelslandedev@gmail.com>
2020-03-28 02:31:35 +01:00
Guo Yunhe dd236c6c1d Replace non-commercial icons with free icons from icons8 (#5093) 2020-03-27 22:31:54 +01:00
Vitor K 10ba8d16be Set render window to also accept focus via tabbing (Qt::StrongFocus) (#5089) 2020-03-27 22:25:01 +01:00
Fernando Sahmkow 7a2f60df26 Merge pull request #3565 from ReinUsesLisp/image-format
engines/const_buffer_engine_interface: Store image format and types
2020-03-27 14:08:54 -04:00
bunnei 8a6a25e4b6 services: hid: Stub InitializeSevenSixAxisSensor.
- Used by Super Smash Bros. Ultimate v7.0.0.
2020-03-27 10:48:01 -04:00
perillamint a429644672 am: Implement VR related APIs
Implement (and stub) VR related APIs in AM sysmodule.

This fixes issue #2938
2020-03-27 18:22:28 +09:00
ReinUsesLisp cedbe925cd engines/const_buffer_engine_interface: Store image format type
This information is required to properly implement SULD.B. It might also
be handy for all image operations, since it would allow us to implement
them on devices that require the image format to be specified (on
desktop, this would be AMD on OpenGL and Intel on OpenGL and Vulkan).
2020-03-27 00:36:22 -03:00
Mat M e84b760016 Merge pull request #3564 from makigumo/maxwell_to_vk_sscaled
maxwell_to_vk: implement signedscaled vertex formats
2020-03-26 21:05:52 -04:00
Dan 744b207d92 maxwell_to_vk: implement signedscaled vertex formats 2020-03-27 00:14:19 +01:00
bunnei 950b6dbc80 Merge pull request #3453 from FearlessTobi/remove-pause-lock
yuzu: Remove exit lock for game pausing
2020-03-26 16:42:57 -04:00
bunnei 3194f14aca Merge pull request #3550 from hughesjs/hughesjs-readme-changes-1
Minor Readme Changes
2020-03-26 00:50:56 -04:00
bunnei 8244536f7a Merge pull request #3548 from jroweboy/use-plugins-dir
Use the correct directory for Qt Plugins
2020-03-26 00:50:06 -04:00
bunnei 23c7dda710 Merge pull request #3544 from makigumo/myfork/patch-2
xmad: fix clang build error
2020-03-25 19:29:16 -04:00
bunnei e6aff11057 Merge pull request #3520 from ReinUsesLisp/legacy-varyings
gl_shader_decompiler: Implement legacy varyings
2020-03-25 19:27:51 -04:00
James Hughes aa41fcc04e Update README.md 2020-03-24 22:30:37 +00:00
James Hughes ac4154bfde Minor ReadMe Changes
Added Discord shield and direct link to contribution guide
2020-03-24 22:26:07 +00:00
bunnei f8382c9d9d Merge pull request #3524 from FearlessTobi/port-5106
Port citra-emu/citra#5106: "gdbstub: Ensure gdbstub doesn't drop packets crucial to initialization"
2020-03-24 16:50:58 -04:00
James Rowe 6ca8637d4c Use the correct directory for Qt Plugins 2020-03-23 18:51:46 -06:00
Fernando Sahmkow 497f593525 Merge pull request #3543 from ReinUsesLisp/gl-depth-range
gl_rasterizer: Use transformed viewport for depth ranges
2020-03-23 12:00:21 -04:00
Fernando Sahmkow 7981910746 Merge pull request #3542 from namkazt/patch-10
Implement MME shadow RAM
2020-03-23 12:00:01 -04:00
bunnei dc4415811c Merge pull request #3546 from FearlessTobi/pointer-buffer-size
sm/controller: Increase PointerBufferSize
2020-03-22 23:31:08 -04:00
FearlessTobi 4afebf26b6 sm/controller: Increase PointerBufferSize
This increases the PointerBufferSize as a lager one is required by some services.
This change is still not hw-accurate, but it is proven to work in Ryujinx.

Instead of using a hardcoded size, we should figure out the specific values for each service in the future. Some of them can be taken from Atmosphere: https://github.com/Atmosphere-NX/Atmosphere/search?q=PointerBufferSize.
2020-03-23 03:19:30 +01:00
makigumo 5a5c6d4ed8 xmad: fix clang build error 2020-03-23 00:09:31 +01:00
bunnei e731c4b991 Merge pull request #3477 from FearlessTobi/webapplet-shit
core/web_browser: Allow WebApplet to exit gracefully when an error occurs
2020-03-22 13:11:02 -04:00
namkazy fc37672f26 apply replay logic to all writes. remove replay from MacroInterpreter::Send (@fincs) 2020-03-22 22:25:44 +07:00
FearlessTobi 977418c65b core/web_browser: Allow WebApplet to exit gracefully when an error occurs
Currently, yuzu just freezes when an error occurs while Initializing the WebApplet.
From a user perspective, this obviously isn't great as the game just softlocks.
With this change, yuzu will call the Finalize method, so to the game it seems like as the user just exited the WebApplet normally.

This works around https://github.com/yuzu-emu/yuzu/issues/2852.
2020-03-22 16:01:13 +01:00
namkazy f66743cd0c maxwell_3d: change declaration order 2020-03-22 13:41:16 +07:00
namkazy d4e93cf38c maxwell_3d: init shadow_state 2020-03-22 13:35:11 +07:00
ReinUsesLisp bdcedc8506 gl_rasterizer: Use transformed viewport for depth ranges
Implement depth ranges using the transformed viewport instead of the
generic one. This matches the current Vulkan implementation but doesn't
support negative depth ranges. An update to glad is required for this.
2020-03-22 03:26:07 -03:00
namkazy 22f4268c2f maxwell_3d: this seem more correct. 2020-03-22 12:02:54 +07:00
namkazy 7051dc1902 maxwell_3d: update comments for shadow ram usage 2020-03-22 11:35:26 +07:00
Nguyen Dac Nam 01af036c1f marco_interpreter: write hw value when shadow ram requested 2020-03-22 10:53:41 +07:00
Nguyen Dac Nam 63c2635e6f maxwell_3d: track shadow ram ctrl and hw reg value 2020-03-22 10:53:41 +07:00
Nguyen Dac Nam dbfbe352e0 maxwell_3d: implement MME shadow RAM 2020-03-22 10:53:35 +07:00
bunnei e5bb5d13c4 Merge pull request #3531 from makigumo/yuzu_master
set: implement GetRegionCode
2020-03-21 22:49:28 -04:00
bunnei e70451d967 Merge pull request #3525 from FearlessTobi/linux-compile-error
input_common/udp: Fix Linux build by using a backwards compatible way of error checking
2020-03-21 16:04:45 -04:00
bunnei 81fa492825 Merge pull request #3526 from FearlessTobi/bcat-disable
bcat: Disable Boxcat backend by default
2020-03-20 13:01:28 -04:00
bunnei bdddbe2daa Merge pull request #3505 from namkazt/patch-8
shader_decode: implement XMAD mode CSfu
2020-03-19 17:41:01 -04:00
Dan 06dea163fa set: implement GetRegionCode 2020-03-19 10:37:42 +01:00
bunnei bc681dc555 Merge pull request #3527 from FearlessTobi/output-mode
yuzu: Save sound output mode and set it to Stereo by default
2020-03-18 23:19:22 -04:00
Mat M 9418b983bd Merge pull request #3535 from ReinUsesLisp/gcc-warnings
video_core: Silence misc warnings
2020-03-18 20:09:32 -04:00
bunnei 76d6178e4a Merge pull request #3534 from ReinUsesLisp/oob-time-zone
time_zone_content_manager: Fix out of bounds read
2020-03-18 19:12:05 -04:00
ReinUsesLisp 38c1e77f01 vk_texture_cache: Silence misc warnings 2020-03-18 20:03:19 -03:00
ReinUsesLisp b6b2e31e5e vk_staging_buffer_pool: Silence unused constant warning 2020-03-18 20:03:19 -03:00
ReinUsesLisp fc51ece7bf vk_rasterizer: Remove unused variable 2020-03-18 20:03:19 -03:00
ReinUsesLisp 98d85cdc20 vk_pipeline_cache: Remove unused variable 2020-03-18 20:03:19 -03:00
ReinUsesLisp dab450ec46 maxwell_to_vk: Sielence -Wswitch warning 2020-03-18 20:03:19 -03:00
ReinUsesLisp 351816ac38 gl_shader_decompiler: Remove deprecated function and its usages 2020-03-18 20:03:19 -03:00
ReinUsesLisp acf328a71f gl_rasterizer: Silence misc warnings 2020-03-18 20:03:19 -03:00
ReinUsesLisp 9f46066bda kepler_compute: Remove unused variables 2020-03-18 20:03:19 -03:00
ReinUsesLisp ba9674862d microprofile: Silence sign comparison warning 2020-03-18 20:03:19 -03:00
ReinUsesLisp ac7ee21331 time_zone_content_manager: Fix out of bounds read
There were cases where raw_data didn't contain enough
space to hold the zero terminator.

This was caught with -fsanitize=address.
2020-03-18 19:06:16 -03:00
Mat M 56ea0f8acb Merge pull request #3530 from ReinUsesLisp/fix-clang
astc/input_common: Fix clang build issues
2020-03-18 04:28:55 -04:00
ReinUsesLisp 716d6aee30 input_common/udp: Fix clang build issues 2020-03-18 04:30:26 -03:00
ReinUsesLisp 664fa4ea06 astc: Fix clang build issues 2020-03-18 04:30:25 -03:00
ReinUsesLisp f5658a9fda gl_shader_decompiler: Don't redeclare gl_VertexID and gl_InstanceID 2020-03-18 01:28:41 -03:00
Mat M edb9cccb36 Merge pull request #3510 from FernandoS27/dirty-write
DirtyFlags: relax need to set render_targets as dirty
2020-03-17 17:29:22 -04:00
Mat M f54d2d3114 Merge pull request #3509 from ReinUsesLisp/astc-opts
astc: General changes and optimizations
2020-03-17 17:28:49 -04:00
Mat M d787856621 Merge pull request #3518 from ReinUsesLisp/scissor-clears
vk_rasterizer: Implement scissor clears and layered clears
2020-03-17 17:27:15 -04:00
Mat M 9fdfd58f9f Merge pull request #3519 from ReinUsesLisp/int-formats
maxwell_to_vk: Implement RG32 and RGB32 integer vertex formats
2020-03-17 17:26:16 -04:00
FearlessTobi cdeadd448b yuzu: Save sound output mode and set it to Stereo by default 2020-03-17 19:03:00 +01:00
bunnei 1c45c8086e Merge pull request #3498 from ReinUsesLisp/texel-fetch-glsl
gl_shader_decompiler: Add layer component to texelFetch
2020-03-17 10:53:38 -04:00
FearlessTobi 2fd3b328ae bcat: Disable Boxcat backend by default
This commit disables the Boxcat backend by default for new users of yuzu.

There's several reasons as to why this is done:
1. Boxcat currently only actually has an impact on 3 games and doesn't influence any core mechanics of them
2. It causes a plethora of issues when enabled such as games like Crash Team Racing, Diablo 3 and Tales of Vesperia not booting at all or hanging
3. It causes https://github.com/yuzu-emu/yuzu/issues/2957 to happen. This makes the configuration menu totally unusable for many Linux users of yuzu

I think those points show that currently the negative impact of Boxcat outweighs its benefits and should therefore be disabled by default.
For users who are eager to use the extra features provided by it, they can still just turn it on in the settings.
2020-03-17 15:24:26 +01:00
FearlessTobi 230ac6a4e8 input_common/udp: Fix Linux build by using a backwards compatible way of error checking
Should fix https://github.com/yuzu-emu/yuzu/issues/3487.

error_code::failed is a function which has been introduced in Boost 1.69.
This version of boost hasn't landed in most major distros yet.
2020-03-17 12:29:25 +01:00
Gauvain "GovanifY" Roussel-Tarbouriech eae2ed6b07 gdbstub: small logic bug fix with defer_start 2020-03-17 11:18:39 +01:00
Gauvain "GovanifY" Roussel-Tarbouriech 38036eb1c8 gdbstub: Ensure gdbstub doesn't drop packets crucial to initialization 2020-03-17 11:18:13 +01:00
bunnei e8ded20d24 Merge pull request #3521 from ReinUsesLisp/nsight-debug
renderer_opengl: Detect Nvidia Nsight as a debugging tool
2020-03-16 22:52:42 -04:00
ReinUsesLisp 53d673a7d3 renderer_opengl: Move some logic to an anonymous namespace 2020-03-16 04:03:34 -03:00
ReinUsesLisp 311d2fc768 renderer_opengl: Detect Nvidia Nsight as a debugging tool
Use getenv to detect Nsight.
2020-03-16 03:59:08 -03:00
Rodrigo Locatti b16c8e0e8d Merge pull request #3515 from ReinUsesLisp/vertex-vk-assert
vk_rasterizer: Fix vertex range assert
2020-03-15 21:26:54 -03:00
Rodrigo Locatti 7cc46a6faa Merge pull request #3501 from ReinUsesLisp/rgba16-snorm
video_core: Implement RGBA16_SNORM
2020-03-15 21:24:53 -03:00
Rodrigo Locatti ddafc99776 Merge pull request #3502 from namkazt/patch-3
shader_decode: Reimplement BFE instructions
2020-03-15 21:23:04 -03:00
Rodrigo Locatti d64edf21bb Merge pull request #3503 from makigumo/patch-2
maxwell_to_vk: add vertex format eA2B10G10R10UnormPack32
2020-03-15 21:21:38 -03:00
ReinUsesLisp 5afc397d52 gl_shader_decompiler: Implement legacy varyings
Legacy varyings are special attributes carried over in hardware from
the OpenGL 1 and OpenGL 2 days. These were generally used instead of the
generic attributes we use today. They are deprecated or removed from
most APIs, but Nvidia still ships them in hardware.

To implement these, this commit maps them 1:1 to OpenGL compatibility.
2020-03-15 21:03:59 -03:00
ReinUsesLisp 6442e02c5d shader/shader_ir: Track usage in input attribute and of legacy varyings 2020-03-15 21:01:52 -03:00
ReinUsesLisp 8e6e55d6f8 shader/shader_ir: Fix clip distance usage stores 2020-03-15 20:53:14 -03:00
ReinUsesLisp 464bd5fad7 shader/shader_ir: Change declare output attribute to a switch 2020-03-15 20:49:35 -03:00
Rodrigo Locatti 86b1f15d9a Merge pull request #3512 from bunnei/fix-renderdoc
renderer_opengl: Keep frames synchronized when using a GPU debugger.
2020-03-15 19:28:43 -03:00
ReinUsesLisp 52acb7f9a0 maxwell_to_vk: Implement RG32 and RGB32 integer vertex formats 2020-03-15 18:51:49 -03:00
Rodrigo Locatti d91a880f11 Merge pull request #3516 from makigumo/patch-3
vk_shader_decompiler: fix linux build
2020-03-15 18:43:40 -03:00
ReinUsesLisp 71cc772988 vk_rasterizer: Implement layered clears 2020-03-15 18:37:19 -03:00
makigumo f91046bf8d vk_shader_decompiler: fix linux build 2020-03-15 18:00:14 +01:00
ReinUsesLisp a7131af7d6 vk_rasterizer: Fix vertex range assert
End can be equal to start in CalculateVertexArraysSize. This is quite
common when the vertex size is zero.
2020-03-15 04:04:17 -03:00
ReinUsesLisp 8baf98e439 vk_rasterizer: Reimplement clears with vkCmdClearAttachments 2020-03-15 03:40:41 -03:00
bunnei c5afe93dcc renderer_opengl: Keep presentation frames in lock-step when GPU debugging.
- Fixes renderdoc with OpenGL renderer.
2020-03-14 17:45:01 -04:00
bunnei 4373fa8042 gl_device: Add option to check GL_EXT_debug_tool. 2020-03-14 17:39:29 -04:00
bunnei 4dfd5c84ea Merge pull request #3508 from FernandoS27/page-table
PageTable: move backing addresses to a children class as the CPU page table does not need them.
2020-03-14 16:50:27 -04:00
Fernando Sahmkow 380fc8d2e1 DirtyFlags: relax need to set render_targets as dirty
The texture cache already takes care of setting a render target to dirty 
when invalidated.
2020-03-14 11:47:33 -04:00
Fernando Sahmkow c51dbf8038 Merge pull request #3500 from ReinUsesLisp/incompatible-types
texture_cache: Report incompatible textures as black
2020-03-14 09:49:05 -04:00
Fernando Sahmkow 41905ee467 Merge pull request #3499 from ReinUsesLisp/depth-2d-array
texture_cache/surface_params: Force depth=1 on 2D textures
2020-03-14 09:48:39 -04:00
Fernando Sahmkow 35145bd529 Merge pull request #3490 from ReinUsesLisp/transform-feedbacks
video_core: Initial implementation of transform feedbacks
2020-03-14 09:48:15 -04:00
Fernando Sahmkow 27cbb75e7c PageTable: move backing addresses to a children class as the CPU page table does not need them.
This PR aims to reduce the memory usage in the CPU page table by moving
GPU specific parameters into a child class. This saves 1Gb of Memory for
most games.
2020-03-14 09:43:57 -04:00
ReinUsesLisp 42cb8f1124 astc: Fix typos from search and replace 2020-03-14 01:05:20 -03:00
ReinUsesLisp 9b8fb3c756 astc: Minor changes to InputBitStream 2020-03-14 00:45:54 -03:00
ReinUsesLisp d71d7d917e astc: Pass val in Replicate by copy 2020-03-14 00:13:58 -03:00
ReinUsesLisp 134f3ff9b4 astc: Call std::vector:reserve on decodedClolorValues to avoid reallocating 2020-03-14 00:09:56 -03:00
Nguyen Dac Nam 3287b1247d clang-format 2020-03-14 10:07:40 +07:00
Nguyen Dac Nam 240d45830d nit 2020-03-14 09:57:24 +07:00
ReinUsesLisp 3377b78ea7 astc: Call std::vector::reserve on texelWeightValues to avoid reallocating 2020-03-13 23:52:51 -03:00
ReinUsesLisp 801fd04f75 astc: Create a LUT at compile time for encoding values 2020-03-13 23:40:02 -03:00
ReinUsesLisp e183820956 astc: Make IntegerEncodedValue a trivial structure 2020-03-13 22:49:28 -03:00
ReinUsesLisp 70a31eda62 astc: Make IntegerEncodedValue constructor constexpr 2020-03-13 22:36:45 -03:00
ReinUsesLisp 5ed377b989 astc: Make IntegerEncodedValue trivially copyable 2020-03-13 22:30:31 -03:00
ReinUsesLisp e7d97605e8 astc: Rename C types to common_types 2020-03-13 22:28:51 -03:00
ReinUsesLisp 835a3d09c6 astc: Move Popcnt to an anonymous namespace and make it constexpr 2020-03-13 22:26:48 -03:00
ReinUsesLisp 731a9a322e astc: Use common types instead of stdint.h integer types 2020-03-13 22:22:27 -03:00
ReinUsesLisp d3dc4e399c astc: Use 'enum class' instead of 'enum' for EIntegerEncoding 2020-03-13 22:20:12 -03:00
ReinUsesLisp 69c7a01f88 vk/gl_shader_decompiler: Silence assertion on compute 2020-03-13 18:33:05 -03:00
ReinUsesLisp 62560f1e63 vk_shader_decompiler: Fix default varying regression 2020-03-13 18:33:05 -03:00
ReinUsesLisp afebdda203 maxwell_3d: Add padding words to XFB entries
Use INSERT_UNION_PADDING_WORDS instead of alignas to ensure a size
requirement.
2020-03-13 18:33:05 -03:00
ReinUsesLisp 4bc4851d45 gl_shader_decompiler: Fix implicit conversion errors 2020-03-13 18:33:05 -03:00
Rodrigo Locatti 47459f6a36 vk_shader_decompiler: Fix implicit type conversion
Co-Authored-By: Mat M. <mathew1800@gmail.com>
2020-03-13 18:33:05 -03:00
ReinUsesLisp 2fae1e6205 vk_rasterizer: Implement transform feedback binding zero 2020-03-13 18:33:05 -03:00
ReinUsesLisp b67360c0f8 vk_shader_decompiler: Add XFB decorations to generic varyings 2020-03-13 18:33:05 -03:00
ReinUsesLisp 8d5bdcb17b vk_device: Enable VK_EXT_transform_feedback when available 2020-03-13 18:33:05 -03:00
ReinUsesLisp c320702092 vk_device: Shrink formatless capability name size 2020-03-13 18:33:05 -03:00
ReinUsesLisp ae6189d7c2 shader/transform_feedback: Expose buffer stride 2020-03-13 18:33:05 -03:00
ReinUsesLisp 7acebd7eb6 vk_shader_decompiler: Use registry for specialization 2020-03-13 18:33:05 -03:00
ReinUsesLisp 8e9f23f393 gl_rasterizer: Implement transform feedback bindings 2020-03-13 18:33:04 -03:00
ReinUsesLisp 4d711dface gl_shader_decompiler: Decorate output attributes with XFB layout
We sometimes have to slice attributes in different parts. This is needed
for example in instances where the game feedbacks 3 components but
writes 4 from the shader (something that is possible with
GL_NV_transform_feedback).
2020-03-13 18:33:04 -03:00
ReinUsesLisp 3dcaa84ba4 shader/transform_feedback: Add host API friendly TFB builder 2020-03-13 18:33:04 -03:00
Fernando Sahmkow 666d431ad8 Merge pull request #3473 from ReinUsesLisp/shader-purge
gl_shader_cache: Rework shader cache and store texture arrays
2020-03-13 16:26:24 -04:00
Rodrigo Locatti 244fe13219 Merge branch 'master' into shader-purge 2020-03-13 16:44:06 -03:00
bunnei b30b1f741d Merge pull request #3491 from ReinUsesLisp/polygon-modes
gl_rasterizer: Implement polygon modes and fill rectangles
2020-03-13 10:08:57 -04:00
Nguyen Dac Nam 829f424618 nit & remove some optional param 2020-03-13 20:47:38 +07:00
Nguyen Dac Nam a166217480 shader_decode: implement XMAD mode CSfu 2020-03-13 19:01:49 +07:00
makigumo 753bc2026f fix formatting 2020-03-13 11:37:24 +01:00
makigumo 54681909be maxwell_to_vk: add vertex format eA2B10G10R10UnormPack32 2020-03-13 11:26:13 +01:00
Nguyen Dac Nam 00607fe1e0 clang-format 2020-03-13 15:38:57 +07:00
Nguyen Dac Nam 325977c0c6 Apply suggestions from code review
Co-Authored-By: Mat M. <mathew1800@gmail.com>
2020-03-13 15:35:15 +07:00
Nguyen Dac Nam 70ff82f72d shader_decode: BFE add ref of reverse parallel method. 2020-03-13 14:20:18 +07:00
Nguyen Dac Nam 96a4abe12d shader_decode: implement BREV on BFE
Implement reverse parallel follow: https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
2020-03-13 14:13:31 +07:00
Nguyen Dac Nam 93547cac68 shader_bytecode: update BFE instructions struct. 2020-03-13 12:52:16 +07:00
Nguyen Dac Nam 911c56ccef node_helper: add IBitfieldExtract case 2020-03-13 12:50:32 +07:00
Nguyen Dac Nam 465ba30d08 shader_decode: Reimplement BFE instructions 2020-03-13 12:48:01 +07:00
ReinUsesLisp e24197bb3f gl_shader_decompiler: Initialize gl_Position on vertex shaders 2020-03-12 23:31:06 -03:00
Fernando Sahmkow 00e9ba0603 Merge pull request #3483 from namkazt/patch-1
vk_rasterizer: fix mistype on SetupGraphicsImages
2020-03-12 22:10:48 -04:00
Fernando Sahmkow f159a12820 Merge pull request #3480 from ReinUsesLisp/vk-disabled-ubo
vk_rasterizer: Support disabled uniform buffers
2020-03-12 22:09:49 -04:00
ReinUsesLisp 3a10016e38 gl_shader_decompiler: Add missing {} on smem GLSL emission 2020-03-12 21:50:37 -03:00
ReinUsesLisp 4dcca90ef4 video_core: Implement RGBA16_SNORM
Implement RGBA16_SNORM with the current API. Nothing special here.
2020-03-12 21:42:33 -03:00
ReinUsesLisp e22816a5bb texture_cache: Report incompatible textures as black
Some games bind incompatible texture types to certain types.
For example Astral Chain binds a 2D texture with 1 layer (non-array) to
a cubemap slot (that's how it's used in the shader). After testing this
in hardware, the expected "undefined behavior" is to report all pixels
as black.

We already have a path for reporting black textures in the texture
cache. When textures types are incompatible, this commit binds these
kind of textures. This is done on the API agnostic texture cache so no
extra code has to be inserted on OpenGL or Vulkan.

As a side effect, this fixes invalidations of ASTC textures on Astral
Chain. This happened because yuzu detected a cube texture and forced
6 faces, generating a texture larger than what the TIC reported.
2020-03-12 18:22:05 -03:00
ReinUsesLisp daae6a323b texture_cache/surface_params: Force depth=1 on 2D textures
Sometimes games will sample a 2D array TIC with a 2D access in the
shader. This causes bad interactions with the rest of the texture cache.
To emulate what the game wants to do, force a depth=1 on 2D textures
(not 2D arrays) and let the texture cache handle the rest.
2020-03-12 18:11:42 -03:00
ReinUsesLisp 38fe070d78 gl_shader_decompiler: Add layer component to texelFetch
TexelFetch was not emitting the array component generating invalid GLSL.
2020-03-12 18:10:29 -03:00
bunnei ca2d228c9d Merge pull request #3497 from FernandoS27/microprogfile-extend
Small corrections and features to microprofile
2020-03-12 12:14:03 -04:00
bunnei c21dc36cda Merge pull request #3496 from vitor-k/remove-enum
framebuffer_layout.h: drop the use of enum for screen dimensions
2020-03-12 12:00:39 -04:00
Fernando Sahmkow 95b804ff05 NVFlinger: Do the microprofile Flip after processing a valid frame. 2020-03-12 10:52:44 -04:00
Fernando Sahmkow 448856695a Microprofile: Allow accessing token. 2020-03-12 10:50:48 -04:00
ReinUsesLisp 825d629565 gl_shader_decompiler: Fix regression in render target declarations
A previous commit introduced a way to declare as few render targets as
possible. Turns out this introduced a regression in some games.
2020-03-12 05:01:20 -03:00
bunnei fce33adcf1 Merge pull request #3494 from ReinUsesLisp/fix-cs-pipeline
gl_shader_manager: Fix interaction between graphics and compute
2020-03-11 13:51:54 -04:00
Vitor Kiguchi e891ff9a0c framebuffer_layout.h: drop the use of enum for screen dimensions.
+clang format
2020-03-11 14:22:28 -03:00
ReinUsesLisp 8357908099 gl_shader_manager: Fix interaction between graphics and compute
After a compute shader was set to the pipeline, no graphics shader was
invoked again. To address this use glUseProgram to bind compute shaders
(without state tracking) and call glUseProgram(0) when transitioning out
of it back to the graphics pipeline.
2020-03-11 01:04:52 -03:00
bunnei 503ebe9b96 Merge pull request #3458 from FearlessTobi/voice-issues
cubeb_sink: Don't discard other channels when performing downmixing
2020-03-10 22:18:37 -04:00
ReinUsesLisp e4bc3c3342 gl_rasterizer: Implement polygon modes and fill rectangles 2020-03-09 20:39:58 -03:00
ReinUsesLisp eb5861e0a2 engines/maxwell_3d: Add TFB registers and store them in shader registry 2020-03-09 18:40:53 -03:00
ReinUsesLisp b1acb4f73f shader/registry: Address feedback 2020-03-09 18:40:53 -03:00
ReinUsesLisp b1061afed9 gl_shader_decompiler: Add identifier to decompiled code 2020-03-09 18:40:53 -03:00
ReinUsesLisp e612242977 gl_shader_decompiler: Roll back to GLSL core 430
RenderDoc won't build shaders if we use GLSL compatibility.
2020-03-09 18:40:53 -03:00
ReinUsesLisp 978172530e const_buffer_engine_interface: Store component types
This is required for Vulkan. Sampling integer textures with float
handles is illegal.
2020-03-09 18:40:53 -03:00
ReinUsesLisp 120f688272 yuzu/loading_screen: Remove unused shader progress mode 2020-03-09 18:40:53 -03:00
ReinUsesLisp e1932351a9 gl_shader_cache: Reduce registry consistency to debug assert
Registry consistency is something that practically can't happen and it
has a measurable runtime cost. Reduce it to a DEBUG_ASSERT.
2020-03-09 18:40:07 -03:00
ReinUsesLisp 66a8a3e887 shader/registry: Cache tessellation state 2020-03-09 18:40:07 -03:00
ReinUsesLisp 0528be5c92 shader/registry: Store graphics and compute metadata
Store information GLSL forces us to provide but it's dynamic state in
hardware (workgroup sizes, primitive topology, shared memory size).
2020-03-09 18:40:07 -03:00
ReinUsesLisp e8efd5a901 video_core: Rename "const buffer locker" to "registry" 2020-03-09 18:40:06 -03:00
ReinUsesLisp bd8b9bbcee gl_shader_cache: Rework shader cache and remove post-specializations
Instead of pre-specializing shaders and then post-specializing them,
drop the later and only "specialize" the shader while decoding it.
2020-03-09 18:40:06 -03:00
Rodrigo Locatti 22e825a3bc Merge pull request #3301 from ReinUsesLisp/state-tracker
video_core: Remove gl_state and use a state tracker based on dirty flags
2020-03-09 18:34:37 -03:00
bunnei c281173df6 Merge pull request #3486 from ReinUsesLisp/fix-anisotropy-hack
textures: Fix anisotropy hack
2020-03-08 16:28:07 -04:00
ReinUsesLisp 1aa75b1081 textures: Fix anisotropy hack
Previous code could generate an anisotropy value way higher than x16.
2020-03-08 15:59:38 -03:00
FearlessTobi 59d0d34dce cubeb_sink: Don't discard other channels when performing downmixing
Previously, when performing downmixing, we would discard all channels except the left and right one.
This implementation respects them when mixing down to Stereo.
It is taken from this document: http://www.atsc.org/wp-content/uploads/2015/03/A52-201212-17.pdf.

Fixes Luigis Mansion 3 cutscene and Bayonetta audio.
2020-03-08 03:16:06 -04:00
bunnei 84e9f9f395 Merge pull request #3452 from Morph1984/anisotropic-filtering
frontend/Graphics: Add "Advanced" graphics tab and experimental Anisotropic Filtering support
2020-03-07 22:28:35 -05:00
Nguyen Dac Nam 16cfbb068c vk_reasterizer: fix mistype on SetupGraphicsImages
This should use Maxwell3D engine. Fixed some GPU error on Kirby and maybe other games.
2020-03-08 10:06:59 +07:00
bunnei 662feb8c1c Merge pull request #3481 from ReinUsesLisp/abgr5-storage
maxwell_to_vk: Remove Storage capability for A1B5G5R5U
2020-03-07 19:51:33 -05:00
ReinUsesLisp e4f9ce0379 vk_rasterizer: Support disabled uniform buffers 2020-03-06 18:47:51 -03:00
ReinUsesLisp aa6fe3f1aa maxwell_to_vk: Remove Storage capability for A1B5G5R5U 2020-03-06 18:47:27 -03:00
bunnei 49eff536d0 Merge pull request #3463 from ReinUsesLisp/vk-toctou
vk_swapchain: Silence TOCTOU race condition
2020-03-05 19:38:42 -05:00
bunnei 4a8fe67964 Merge pull request #3479 from jroweboy/dont-log-on-no-input
Minor fixes for udp input
2020-03-05 15:09:48 -05:00
bunnei 0361aa1915 Merge pull request #3451 from ReinUsesLisp/indexed-textures
vk_shader_decompiler: Implement indexed textures
2020-03-05 11:42:46 -05:00
bunnei fa1d625eed Merge pull request #3469 from namkazt/patch-1
shader_decode: Fix LD, LDG when track constant buffer
2020-03-04 23:10:01 -05:00
bunnei 1e84d22275 Merge pull request #3478 from bunnei/a32
Refactoring to boot A32 games
2020-03-04 20:37:51 -05:00
James Rowe 002d9508a0 input/udp - Add minor error handling to prevent bad input from crashing 2020-03-03 23:46:05 -07:00
bunnei 67e7186d79 Merge pull request #3455 from ReinUsesLisp/attr-scaled
video_core: Implement more scaled attribute formats
2020-03-03 22:46:20 -05:00
James Rowe fc205a1bc5 Frontend/SDL - Provide proper default for UDP input
When the default file is read in, the settings default value is only used
when the key is missing. As it was, the key existed, but the value was empty string
causing it to accept that as a value to pass into the core
2020-03-03 20:05:42 -07:00
James Rowe 2cdda8c564 input/udp - Dont log on invalid packet received 2020-03-03 19:52:16 -07:00
bunnei dba112e510 core: hle: Implement separate A32/A64 SVC interfaces. 2020-03-02 21:52:03 -05:00
bunnei c083ea7d78 core: Implement separate A32/A64 ARM interfaces. 2020-03-02 21:51:57 -05:00
bunnei 6fc485a607 core: loader: Remove check for 32-bit. 2020-03-02 21:43:15 -05:00
bunnei 64facb403e core: dynarmic: Add CP15 from Citra. 2020-03-02 21:43:15 -05:00
bunnei 08c638f249 Merge pull request #3464 from FernandoS27/jit-fix
ARM_Interface: Cache the JITs instead of deleting/recreating.
2020-03-02 21:41:43 -05:00
bunnei dfa2e336ba Merge pull request #3475 from yuzu-emu/FearlessTobi-readme
Port citra-emu/citra#5097: "Update README.md"
2020-03-01 22:41:41 -05:00
Tobias 6af8ff24c9 Update README.md 2020-03-01 18:03:32 +01:00
Nguyen Dac Nam 85a4222a8c nit: move comment to right place. 2020-02-29 13:50:10 +07:00
bunnei ca7618684c Merge pull request #3448 from bunnei/fix-audio-interp-2
audio_core: interpolate: Improvements to fix audio crackling.
2020-02-28 16:07:10 -05:00
ReinUsesLisp 735c003a70 video_core/dirty_flags: Address feedback 2020-02-28 17:56:43 -03:00
ReinUsesLisp ef7f6eb67d renderer_opengl: Fix edge-case where alpha testing might cull presentation 2020-02-28 17:56:43 -03:00
ReinUsesLisp a6a350ddc3 gl_texture_cache: Remove blending disable on blits
Blending doesn't affect blits. Rasterizer discard does, update the
commentaries.
2020-02-28 17:56:43 -03:00
ReinUsesLisp 887d5288ef gl_rasterizer: Don't disable blending on clears
Blending doesn't affect clears.
2020-02-28 17:56:43 -03:00
ReinUsesLisp ac204754d4 dirty_flags: Deduplicate code between OpenGL and Vulkan 2020-02-28 17:56:43 -03:00
ReinUsesLisp 6669b359a3 vk_rasterizer: Pass Maxwell registers to dynamic updates 2020-02-28 17:56:43 -03:00
ReinUsesLisp 042256c6bb state_tracker: Remove type traits with named structures 2020-02-28 17:56:43 -03:00
ReinUsesLisp 6ac3eb4d87 vk_state_tracker: Implement dirty flags for stencil properties 2020-02-28 17:56:43 -03:00
ReinUsesLisp f9df2c6bcd vk_state_tracker: Implement dirty flags for depth bounds 2020-02-28 17:56:43 -03:00
ReinUsesLisp cd0e28c9ec vk_state_tracker: Implement dirty flags for blend constants 2020-02-28 17:56:43 -03:00
ReinUsesLisp a33870996b vk_state_tracker: Implement dirty flags for depth bias 2020-02-28 17:56:43 -03:00
ReinUsesLisp 42f1874965 vk_state_tracker: Implement dirty flags for scissors 2020-02-28 17:56:43 -03:00
ReinUsesLisp 1bd95a314f vk_state_tracker: Initial implementation
Add support for render targets and viewports.
2020-02-28 17:56:43 -03:00
ReinUsesLisp b1498d2c54 gl_rasterizer: Remove num vertex buffers magic number 2020-02-28 17:56:43 -03:00
ReinUsesLisp 62437943a7 gl_rasterizer: Only apply polygon offset clamp if enabled 2020-02-28 17:56:43 -03:00
ReinUsesLisp 2eeea90713 gl_state_tracker: Implement dirty flags for depth clamp enabling 2020-02-28 17:56:43 -03:00
ReinUsesLisp 3ce66776ec gl_rasterizer: Disable scissor 0 when scissor is not used on clear 2020-02-28 17:56:43 -03:00
ReinUsesLisp 35bb9239ca gl_rasterizer: Notify depth mask changes on clear 2020-02-28 17:56:43 -03:00
ReinUsesLisp 98c8948b23 gl_rasterizer: Minor sort changes to clearing 2020-02-28 17:56:42 -03:00
ReinUsesLisp 15cadc3948 maxwell_3d: Use two tables instead of three for dirty flags 2020-02-28 17:56:42 -03:00
ReinUsesLisp a5bfc0d045 gl_state_tracker: Track state of index buffers 2020-02-28 17:56:42 -03:00
ReinUsesLisp a42a6e1a2c gl_state_tracker: Implement dirty flags for clip control 2020-02-28 17:56:42 -03:00
ReinUsesLisp 4f8d152b18 gl_state_tracker: Implement dirty flags for point sizes 2020-02-28 17:56:42 -03:00
ReinUsesLisp 231601763c gl_state_tracker: Implement dirty flags for fragment color clamp 2020-02-28 17:56:42 -03:00
ReinUsesLisp bf1a1d989f gl_state_tracker: Implement dirty flags for logic op 2020-02-28 17:56:42 -03:00
ReinUsesLisp 13afd0e5b0 gl_state_tracker: Implement dirty flags for sRGB 2020-02-28 17:56:42 -03:00
ReinUsesLisp d8f5c45051 gl_state_tracker: Implement dirty flags for rasterize enable 2020-02-28 17:56:42 -03:00
ReinUsesLisp b727d99441 gl_state_tracker: Implement dirty flags for multisample 2020-02-28 17:56:42 -03:00
ReinUsesLisp 3c22bd92d8 gl_state_tracker: Implement dirty flags for alpha testing 2020-02-28 17:56:42 -03:00
ReinUsesLisp 9e46953580 gl_state_tracker: Implement dirty flags for polygon offsets 2020-02-28 17:56:42 -03:00
ReinUsesLisp 46a1888e02 gl_state_tracker: Implement dirty flags for primitive restart 2020-02-28 17:56:42 -03:00
ReinUsesLisp 37536d7a49 gl_state_tracker: Implement dirty flags for stencil testing 2020-02-28 17:56:42 -03:00
ReinUsesLisp 40a2c57df5 gl_state_tracker: Implement depth dirty flags 2020-02-28 17:56:42 -03:00
ReinUsesLisp b910a83a47 gl_state_tracker: Implement dirty flags for front face and culling 2020-02-28 17:56:42 -03:00
ReinUsesLisp b01dd7d1c8 gl_state_tracker: Implement dirty flags for blending 2020-02-28 17:56:42 -03:00
ReinUsesLisp f7ec078592 gl_state_tracker: Implement dirty flags for clip distances and shaders 2020-02-28 17:56:42 -03:00
ReinUsesLisp 758ad3f75d gl_state_tracker: Add dirty flags for buffers and divisors 2020-02-28 17:56:42 -03:00
ReinUsesLisp 9b08698a0c maxwell_3d: Change write dirty flags to a bitset 2020-02-28 17:56:42 -03:00
ReinUsesLisp 69ad6279e4 gl_state_tracker: Implement dirty flags for vertex formats 2020-02-28 17:56:42 -03:00
ReinUsesLisp 6530144ccb gl_state_tracker: Implement dirty flags for color masks 2020-02-28 17:56:42 -03:00
ReinUsesLisp ba6f390448 gl_state_tracker: Implement dirty flags for scissors 2020-02-28 17:56:42 -03:00
ReinUsesLisp 7f52efdf61 gl_state_tracker: Implement dirty flags for viewports 2020-02-28 17:56:41 -03:00
ReinUsesLisp dacf83ac02 renderer_opengl: Reintroduce dirty flags for render targets 2020-02-28 17:56:41 -03:00
ReinUsesLisp 9e74e6988b maxwell_3d: Flatten cull and front face registers 2020-02-28 17:56:41 -03:00
ReinUsesLisp eed789d0d1 video_core: Reintroduce dirty flags infrastructure 2020-02-28 17:56:41 -03:00
ReinUsesLisp b92dfcd7f2 gl_state: Remove completely 2020-02-28 17:56:35 -03:00
ReinUsesLisp 1c4bf9cbfa gl_state: Remove program tracking 2020-02-28 17:52:14 -03:00
ReinUsesLisp 5ccb07933a gl_state: Remove framebuffer tracking 2020-02-28 17:52:10 -03:00
ReinUsesLisp 17a7fa751b gl_state: Remove image tracking 2020-02-28 17:36:40 -03:00
ReinUsesLisp 9677db03da gl_state: Remove texture and sampler tracking 2020-02-28 17:35:58 -03:00
ReinUsesLisp 1bc0da3dea gl_state: Remove blend state tracking 2020-02-28 17:34:43 -03:00
ReinUsesLisp 7d9a5e9e30 gl_state: Remove stencil test tracking 2020-02-28 17:32:05 -03:00
ReinUsesLisp 07a954e67f gl_state: Remove clip control tracking 2020-02-28 17:31:57 -03:00
ReinUsesLisp 1eee891f6e gl_state: Remove clip distances tracking 2020-02-28 17:26:26 -03:00
ReinUsesLisp e8125af8dd gl_state: Remove rasterizer disable tracking 2020-02-28 17:25:28 -03:00
ReinUsesLisp d3e433a380 gl_state: Remove viewport and depth range tracking 2020-02-28 17:25:18 -03:00
ReinUsesLisp 7c16b3551b gl_state: Remove scissor test tracking 2020-02-28 17:00:23 -03:00
ReinUsesLisp 0914c70b7f gl_state: Remove color mask tracking 2020-02-28 16:59:17 -03:00
ReinUsesLisp 2392b548be gl_state: Remove clamp framebuffer color tracking
This commit doesn't reset it for screen draws because clamping doesn't
change anything there.
2020-02-28 16:58:30 -03:00
ReinUsesLisp f92236976b gl_state: Remove multisample tracking 2020-02-28 16:57:47 -03:00
ReinUsesLisp 04d1134191 gl_state: Remove framebuffer sRGB tracking 2020-02-28 16:55:23 -03:00
ReinUsesLisp d5ab0358b6 gl_state: Remove VAO cache and tracking 2020-02-28 16:54:37 -03:00
ReinUsesLisp 2a662fea36 gl_state: Remove depth clamp tracking 2020-02-28 16:53:35 -03:00
ReinUsesLisp e1a16a52fa gl_state: Remove depth tracking 2020-02-28 16:52:46 -03:00
ReinUsesLisp 0f343d32c4 gl_state: Remove primitive restart tracking 2020-02-28 16:51:45 -03:00
ReinUsesLisp 42708c762e gl_state: Remove logic op tracker 2020-02-28 16:51:23 -03:00
ReinUsesLisp 915d73f3b8 gl_state: Remove blend color tracking 2020-02-28 16:50:58 -03:00
ReinUsesLisp a0321b984f gl_state: Remove polygon offset tracking 2020-02-28 16:49:20 -03:00
ReinUsesLisp f646321dd0 gl_state: Remove alpha test tracking 2020-02-28 16:48:57 -03:00
ReinUsesLisp c8f5f54a44 gl_state: Remove cull mode tracking 2020-02-28 16:48:23 -03:00
ReinUsesLisp 925521da5f gl_state: Remove front face tracking 2020-02-28 16:47:59 -03:00
ReinUsesLisp d2d5554296 gl_state: Remove point size tracking 2020-02-28 16:39:44 -03:00
ReinUsesLisp b95f064b51 gl_rasterizer: Add oglEnablei helper 2020-02-28 16:39:44 -03:00
ReinUsesLisp 1698143a1d gl_rasterizer: Add OpenGL enable/disable helper 2020-02-28 16:39:44 -03:00
ReinUsesLisp 96ac3d518a gl_rasterizer: Remove dirty flags 2020-02-28 16:39:27 -03:00
ReinUsesLisp e38ed26b98 common/math_util: Support float type rectangles 2020-02-28 16:22:11 -03:00
bunnei c7db1ef565 Merge pull request #3470 from bunnei/fix-smash-srgb
renderer_opengl: Fix SRGB presentation frame tracking.
2020-02-28 01:22:00 -05:00
namkazy 1326e326f5 Merge branch 'patch-1' of https://github.com/namkazt/yuzu into patch-2 2020-02-28 13:14:49 +07:00
bunnei 5056d23d0d renderer_opengl: Fix SRGB presentation frame tracking.
- Fixes SRGB in Super Smash Bros. Ultimate.
2020-02-28 01:13:38 -05:00
Nguyen Dac Nam 6c0c2dfabc shader_decode: Fix LD, LDG when track constant buffer 2020-02-28 13:11:19 +07:00
Nguyen Dac Nam 1c385362f5 shader_decode: keep it search on all code
It fixed opcode LD, LDG on Pokemon Sword that can't find the constant buffer. Not sure if it helps any on visual.
2020-02-28 11:59:05 +07:00
Morph 7ee6065178 Create an "Advanced" tab in the graphics configuration tab and add anisotropic filtering levels. 2020-02-27 21:34:00 -05:00
bunnei 969357af1a Merge pull request #3430 from bunnei/split-presenter
Port citra-emu/citra#4940: "Split Presentation thread from Render thread"
2020-02-27 19:51:55 -05:00
bunnei ebbfe73557 renderer_opengl: Reduce swap chain size to 3. 2020-02-27 19:50:17 -05:00
Morph e1efab1f51 AM/ICommonStateGetter: Stub SetLcdBacklighOffEnabled (#3454)
* Stub SetLcdBacklighOffEnabled

Used by Super Smash Bros. Ultimate
We require backlight services to be implemented to turn on/off the backlight.

* Address feedback
2020-02-27 17:49:23 +01:00
Nguyen Dac Nam db2f547434 shader: FMUL switch to using LUT (#3441)
* shader: add FmulPostFactor LUT table

* shader: FMUL apply LUT

* Update src/video_core/engines/shader_bytecode.h

Co-Authored-By: Mat M. <mathew1800@gmail.com>

* nit: mistype

* clang-format & add missing import

* shader: remove post factor LUT.

* shader: move post factor LUT to function and fix incorrect order.

* clang-format

* shader: FMUL: add static to post factor LUT

* nit: typo

Co-authored-by: Mat M. <mathew1800@gmail.com>
2020-02-27 11:14:25 -05:00
bunnei a17214baea renderer_opengl: Use more concise lock syntax. 2020-02-26 18:35:35 -05:00
bunnei aef159354c renderer_opengl: Move Frame/FrameMailbox to OpenGL namespace. 2020-02-26 18:28:50 -05:00
ReinUsesLisp 0aaa69e4d7 vk_swapchain: Silence TOCTOU race condition
It's possible that the window is resized from the moment we ask for its
size to the moment a swapchain is created, causing validation issues.

To workaround this Vulkan issue request the capabilities again just
before creating the swapchain, making the race condition less likely.
2020-02-26 17:07:18 -03:00
Fernando Sahmkow f3d4d4eaa8 ARM_Interface: Cache the JITs instead of deleting/recreating.
This was a bug inherited from citra which was fixed by then at some 
time. This commit corrects such bug and ensures JITs are correctly 
recycled.
2020-02-26 15:53:47 -04:00
bunnei 1f57f679a4 Merge pull request #3440 from namkazt/patch-6
shader: implement LOP3 fast replace for old function
2020-02-26 10:24:35 -05:00
bunnei 01a05b48b7 Merge pull request #3431 from CJBok/npad-fix
InputCommon: analog_from_button get direction implementation
2020-02-25 21:39:26 -05:00
bunnei 795893a9a5 renderer_opengl: Create gl_framebuffer_data if empty. 2020-02-25 21:23:02 -05:00
bunnei c6f78a4a6d frontend: qt: bootmanager: Acquire a shared context in main emu thread. 2020-02-25 21:23:02 -05:00
bunnei e25297536f frontend: qt: bootmanager: Vulkan: Restore support for VK backend. 2020-02-25 21:23:01 -05:00
bunnei 14877b8f35 frontend: qt: bootmanager: OpenGL: Implement separate presentation thread. 2020-02-25 21:23:01 -05:00
bunnei b2a38cce4e frontent: qt: main: Various updates/refactoring for separate presentation thread. 2020-02-25 21:23:00 -05:00
bunnei 667f026c95 core: frontend: Refactor scope_acquire_window_context to scope_acquire_context. 2020-02-25 21:23:00 -05:00
bunnei 2e16c23784 frontend: sdl2: emu_window: Implement separate presentation thread. 2020-02-25 21:23:00 -05:00
bunnei dc672ca4b3 renderer_opengl: Add texture mailbox support for presenter thread. 2020-02-25 21:22:59 -05:00
bunnei add2c38b73 renderer_opengl: Add OGLRenderbuffer to resource/state management. 2020-02-25 21:22:58 -05:00
bunnei 0c82b00dfd core: frontend: emu_window: Add TextureMailbox class. 2020-02-25 21:22:57 -05:00
bunnei 571451bdfe core: settings: Add setting to enable vsync, which is on by default. 2020-02-25 20:57:02 -05:00
Mat M 45ac1c62c6 Merge pull request #3461 from ReinUsesLisp/r32i-rt
video_core/surface: Add R32_SINT render target format
2020-02-25 17:47:14 -05:00
Mat M 00e3eab9c1 Merge pull request #3460 from ReinUsesLisp/unused-format-getter
video_core/gpu: Remove unused functions
2020-02-25 17:46:07 -05:00
ReinUsesLisp 466ce715e4 video_core/surface: Add R32_SINT render target format 2020-02-25 17:19:34 -03:00
ReinUsesLisp 3c648e3e2d video_core/gpu: Remove unused functions 2020-02-25 16:53:47 -03:00
bunnei 78ab2e0474 Merge pull request #3417 from ReinUsesLisp/r32i
texture: Implement R32I
2020-02-25 14:08:45 -05:00
FearlessTobi 7cbe6748c3 yuzu: Remove exit lock for game pausing
This removes the "exit lock" popup from yuzu when pausing a game.

Motivation
The exit lock feature is broken in many ways and doesn't work properly in a lot of games, causing it to appear every time you want to pause the game or stop it, even in places where it wouldn't on Switch.

Additionally, the feature of pausing a game doesn't exist like this on Switch and yuzu should be guaranteed to be deterministic anyway, so pausing the emulation shouldn't be able to interrupt any critical processes in any way.
2020-02-24 17:51:17 +01:00
bunnei e22ad52cdb Merge pull request #3425 from ReinUsesLisp/layered-framebuffer
texture_cache: Implement layered framebuffer attachments
2020-02-24 10:14:50 -05:00
ReinUsesLisp 1e9213632a vk_shader_decompiler: Implement indexed textures
Implement accessing textures through an index. It uses the same
interface as OpenGL, the main difference is that Vulkan bindings are
forced to be arrayed (the binding index doesn't change for stacked
textures in SPIR-V).
2020-02-24 01:26:07 -03:00
ReinUsesLisp 1dda77d392 shader: Simplify indexed sampler usages 2020-02-24 01:26:07 -03:00
ReinUsesLisp e2dd59e341 video_core: Implement more scaler attribute formats
While changing this, fix assert in vk_shader_decompiler. We now know
scaled formats are expected to be float in shaders attributes.
2020-02-24 00:27:37 -03:00
bunnei 2b4cdb73b6 Merge pull request #3424 from ReinUsesLisp/spirv-layer
vk_shader_decompiler: Implement Layer output attribute
2020-02-22 23:45:16 -05:00
bunnei 754aac331f Merge pull request #3422 from ReinUsesLisp/buffer-flush
surface_base: Implement texture buffer flushes
2020-02-22 23:09:50 -05:00
bunnei 3ef5f2017d Merge pull request #3416 from FernandoS27/schedule
Kernel: Refactors and Implement a TimeManager and SchedulerLocks
2020-02-22 22:32:21 -05:00
bunnei 1989e1b9ac audio_core: interpolate: Improvements to fix audio crackling.
- Fixes audio crackling in Crash Team Racing Nitro-Fueled, Super Mario Odyssey, and others.
- Addresses followup issues from #3310.
2020-02-22 22:26:16 -05:00
Nguyen Dac Nam 10d8afb302 nit: add const to where it need. 2020-02-21 21:16:45 +07:00
Nguyen Dac Nam 1956a34ee5 shader: implement LOP3 fast replace for old function
ref: https://devtalk.nvidia.com/default/topic/1070081/cuda-programming-and-performance/reverse-lut-for-lop3-lut/
2020-02-21 19:08:07 +07:00
CJBok 23c4cc80e2 analog_from_button get direction implementation 2020-02-18 06:45:37 +01:00
ReinUsesLisp 6a0220b2e1 texture_cache: Implement layered framebuffer attachments
Layered framebuffer attachments is a feature that allows applications to
write attach layered textures to a single attachment. What layer the
fragments are written to is decided from the shader using gl_Layer.
2020-02-16 04:19:32 -03:00
ReinUsesLisp 1caf3f11c8 vk_shader_decompiler: Implement Layer output attribute
SPIR-V's Layer is GLSL's gl_Layer. It lets the application choose from a
shader stage (vertex, tessellation or geometry) which framebuffer layer
write the output fragments to.
2020-02-16 04:17:37 -03:00
ReinUsesLisp fd62bdf377 surface_base: Implement texture buffer flushes
Implement downloads to guest memory from texture buffers on the generic
cache and OpenGL.
2020-02-16 04:13:27 -03:00
ReinUsesLisp 14c2a4a2ec texture: Implement R32I 2020-02-15 16:26:50 -03:00
199 changed files with 7253 additions and 5240 deletions
+8 -4
View File
@@ -6,9 +6,9 @@ function(copy_yuzu_Qt5_deps target_dir)
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/")
set(PLATFORMS ${DLL_DEST}platforms/)
set(STYLES ${DLL_DEST}styles/)
set(IMAGEFORMATS ${DLL_DEST}imageformats/)
set(PLATFORMS ${DLL_DEST}plugins/platforms/)
set(STYLES ${DLL_DEST}plugins/styles/)
set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/)
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
icudt*.dll
icuin*.dll
@@ -42,11 +42,15 @@ function(copy_yuzu_Qt5_deps target_dir)
icudtl.dat
)
endif ()
windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
qjpeg$<$<CONFIG:Debug>:d>.*
qgif$<$<CONFIG:Debug>:d>.*
)
# Create an empty qt.conf file. Qt will detect that this file exists, and use the folder that its in as the root folder.
# This way it'll look for plugins in the root/plugins/ folder
add_custom_command(TARGET yuzu POST_BUILD
COMMAND ${CMAKE_COMMAND} -E touch ${DLL_DEST}qt.conf
)
endfunction(copy_yuzu_Qt5_deps)
+4 -4
View File
@@ -57,8 +57,6 @@ set(HASH_FILES
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
"${VIDEO_CORE}/shader/decode/arithmetic.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
@@ -91,8 +89,6 @@ set(HASH_FILES
"${VIDEO_CORE}/shader/ast.h"
"${VIDEO_CORE}/shader/compiler_settings.cpp"
"${VIDEO_CORE}/shader/compiler_settings.h"
"${VIDEO_CORE}/shader/const_buffer_locker.cpp"
"${VIDEO_CORE}/shader/const_buffer_locker.h"
"${VIDEO_CORE}/shader/control_flow.cpp"
"${VIDEO_CORE}/shader/control_flow.h"
"${VIDEO_CORE}/shader/decode.cpp"
@@ -101,9 +97,13 @@ set(HASH_FILES
"${VIDEO_CORE}/shader/node.h"
"${VIDEO_CORE}/shader/node_helper.cpp"
"${VIDEO_CORE}/shader/node_helper.h"
"${VIDEO_CORE}/shader/registry.cpp"
"${VIDEO_CORE}/shader/registry.h"
"${VIDEO_CORE}/shader/shader_ir.cpp"
"${VIDEO_CORE}/shader/shader_ir.h"
"${VIDEO_CORE}/shader/track.cpp"
"${VIDEO_CORE}/shader/transform_feedback.cpp"
"${VIDEO_CORE}/shader/transform_feedback.h"
)
set(COMBINED "")
foreach (F IN LISTS HASH_FILES)
+3 -2
View File
@@ -1,7 +1,8 @@
yuzu emulator
=============
[![Travis CI Build Status](https://travis-ci.org/yuzu-emu/yuzu.svg?branch=master)](https://travis-ci.org/yuzu-emu/yuzu)
[![Travis CI Build Status](https://travis-ci.com/yuzu-emu/yuzu.svg?branch=master)](https://travis-ci.com/yuzu-emu/yuzu)
[![Azure Mainline CI Build Status](https://dev.azure.com/yuzu-emu/yuzu/_apis/build/status/yuzu%20mainline?branchName=master)](https://dev.azure.com/yuzu-emu/yuzu/)
[![Discord](https://img.shields.io/discord/398318088170242053?color=%237289DA&label=yuzu&logo=discord&logoColor=white)](https://discord.gg/XQV6dn9)
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
@@ -21,7 +22,7 @@ For development discussion, please join us on [Discord](https://discord.gg/XQV6d
Most of the development happens on GitHub. It's also where [our central repository](https://github.com/yuzu-emu/yuzu) is hosted.
If you want to contribute please take a look at the [Contributor's Guide](CONTRIBUTING.md) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information). You should as well contact any of the developers on Discord in order to know about the current state of the emulator.
If you want to contribute please take a look at the [Contributor's Guide](https://github.com/yuzu-emu/yuzu/wiki/Contributing) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information). You should also contact any of the developers on Discord in order to know about the current state of the emulator.
### Building
+2 -4
View File
@@ -2,8 +2,8 @@ The icons in this folder and its subfolders have the following licenses:
Icon Name | License | Origin/Author
--- | --- | ---
qt_themes/default/icons/16x16/checked.png | Free for non-commercial use
qt_themes/default/icons/16x16/failed.png | Free for non-commercial use
qt_themes/default/icons/16x16/checked.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/failed.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
@@ -11,8 +11,6 @@ qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/checked.png | Free for non-commercial use
qt_themes/qdarkstyle/icons/16x16/failed.png | Free for non-commercial use
qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 B

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 524 B

+4 -3
View File
@@ -243,6 +243,7 @@ typedef uint32_t ThreadIdType;
#define MICROPROFILE_DEFINE_GPU(var, name, color) MicroProfileToken g_mp_##var = MicroProfileGetToken("GPU", name, color, MicroProfileTokenTypeGpu)
#define MICROPROFILE_TOKEN_PASTE0(a, b) a ## b
#define MICROPROFILE_TOKEN_PASTE(a, b) MICROPROFILE_TOKEN_PASTE0(a,b)
#define MICROPROFILE_TOKEN(var) g_mp_##var
#define MICROPROFILE_SCOPE(var) MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(g_mp_##var)
#define MICROPROFILE_SCOPE_TOKEN(token) MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo, __LINE__)(token)
#define MICROPROFILE_SCOPEI(group, name, color) static MicroProfileToken MICROPROFILE_TOKEN_PASTE(g_mp,__LINE__) = MicroProfileGetToken(group, name, color, MicroProfileTokenTypeCpu); MicroProfileScopeHandler MICROPROFILE_TOKEN_PASTE(foo,__LINE__)( MICROPROFILE_TOKEN_PASTE(g_mp,__LINE__))
@@ -827,7 +828,7 @@ inline MicroProfileLogEntry MicroProfileMakeLogIndex(uint64_t nBegin, MicroProfi
MicroProfileLogEntry Entry = (nBegin<<62) | ((0x3fff&nToken)<<48) | (MP_LOG_TICK_MASK&nTick);
int t = MicroProfileLogType(Entry);
uint64_t nTimerIndex = MicroProfileLogTimerIndex(Entry);
MP_ASSERT(t == nBegin);
MP_ASSERT((uint64_t)t == nBegin);
MP_ASSERT(nTimerIndex == (nToken&0x3fff));
return Entry;
@@ -1555,10 +1556,10 @@ void MicroProfileFlip()
pFramePut->nFrameStartCpu = MP_TICK();
pFramePut->nFrameStartGpu = (uint32_t)MicroProfileGpuInsertTimeStamp();
if(pFrameNext->nFrameStartGpu != (uint64_t)-1)
if(pFrameNext->nFrameStartGpu != -1)
pFrameNext->nFrameStartGpu = MicroProfileGpuGetTimeStamp((uint32_t)pFrameNext->nFrameStartGpu);
if(pFrameCurrent->nFrameStartGpu == (uint64_t)-1)
if(pFrameCurrent->nFrameStartGpu == -1)
pFrameCurrent->nFrameStartGpu = pFrameNext->nFrameStartGpu + 1;
uint64_t nFrameStartCpu = pFrameCurrent->nFrameStartCpu;
+2 -2
View File
@@ -343,8 +343,8 @@ The icons used in this project have the following licenses:
Icon Name | License | Origin/Author
--- | --- | ---
checked.png | Free for non-commercial use
failed.png | Free for non-commercial use
checked.png | CC BY-ND 3.0 | https://icons8.com
failed.png | CC BY-ND 3.0 | https://icons8.com
lock.png | CC BY-ND 3.0 | https://icons8.com
plus_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
bad_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
+32 -22
View File
@@ -8,13 +8,14 @@
#include <climits>
#include <cmath>
#include <vector>
#include "audio_core/algorithm/interpolate.h"
#include "common/common_types.h"
#include "common/logging/log.h"
namespace AudioCore {
constexpr std::array<s16, 512> curve_lut0 = {
constexpr std::array<s16, 512> curve_lut0{
6600, 19426, 6722, 3, 6479, 19424, 6845, 9, 6359, 19419, 6968, 15, 6239,
19412, 7093, 22, 6121, 19403, 7219, 28, 6004, 19391, 7345, 34, 5888, 19377,
7472, 41, 5773, 19361, 7600, 48, 5659, 19342, 7728, 55, 5546, 19321, 7857,
@@ -56,7 +57,7 @@ constexpr std::array<s16, 512> curve_lut0 = {
19403, 6121, 22, 7093, 19412, 6239, 15, 6968, 19419, 6359, 9, 6845, 19424,
6479, 3, 6722, 19426, 6600};
constexpr std::array<s16, 512> curve_lut1 = {
constexpr std::array<s16, 512> curve_lut1{
-68, 32639, 69, -5, -200, 32630, 212, -15, -328, 32613, 359, -26, -450,
32586, 512, -36, -568, 32551, 669, -47, -680, 32507, 832, -58, -788, 32454,
1000, -69, -891, 32393, 1174, -80, -990, 32323, 1352, -92, -1084, 32244, 1536,
@@ -98,7 +99,7 @@ constexpr std::array<s16, 512> curve_lut1 = {
32551, -568, -36, 512, 32586, -450, -26, 359, 32613, -328, -15, 212, 32630,
-200, -5, 69, 32639, -68};
constexpr std::array<s16, 512> curve_lut2 = {
constexpr std::array<s16, 512> curve_lut2{
3195, 26287, 3329, -32, 3064, 26281, 3467, -34, 2936, 26270, 3608, -38, 2811,
26253, 3751, -42, 2688, 26230, 3897, -46, 2568, 26202, 4046, -50, 2451, 26169,
4199, -54, 2338, 26130, 4354, -58, 2227, 26085, 4512, -63, 2120, 26035, 4673,
@@ -146,10 +147,10 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
if (ratio <= 0) {
LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio);
ratio = 1.0;
return input;
}
const int step = static_cast<int>(ratio * 0x8000);
const s32 step{static_cast<s32>(ratio * 0x8000)};
const std::array<s16, 512>& lut = [step] {
if (step > 0xaaaa) {
return curve_lut0;
@@ -160,28 +161,37 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
return curve_lut2;
}();
std::vector<s16> output(static_cast<std::size_t>(input.size() / ratio));
int in_offset = 0;
for (std::size_t out_offset = 0; out_offset < output.size(); out_offset += 2) {
const int lut_index = (state.fraction >> 8) * 4;
const std::size_t num_frames{input.size() / 2};
const int l = input[(in_offset + 0) * 2 + 0] * lut[lut_index + 0] +
input[(in_offset + 1) * 2 + 0] * lut[lut_index + 1] +
input[(in_offset + 2) * 2 + 0] * lut[lut_index + 2] +
input[(in_offset + 3) * 2 + 0] * lut[lut_index + 3];
std::vector<s16> output;
output.reserve(static_cast<std::size_t>(input.size() / ratio + InterpolationState::taps));
const int r = input[(in_offset + 0) * 2 + 1] * lut[lut_index + 0] +
input[(in_offset + 1) * 2 + 1] * lut[lut_index + 1] +
input[(in_offset + 2) * 2 + 1] * lut[lut_index + 2] +
input[(in_offset + 3) * 2 + 1] * lut[lut_index + 3];
for (std::size_t frame{}; frame < num_frames; ++frame) {
const std::size_t lut_index{(state.fraction >> 8) * InterpolationState::taps};
const int new_offset = state.fraction + step;
std::rotate(state.history.begin(), state.history.end() - 1, state.history.end());
state.history[0][0] = input[frame * 2 + 0];
state.history[0][1] = input[frame * 2 + 1];
in_offset += new_offset >> 15;
state.fraction = new_offset & 0x7fff;
while (state.position <= 1.0) {
const s32 left{state.history[0][0] * lut[lut_index + 0] +
state.history[1][0] * lut[lut_index + 1] +
state.history[2][0] * lut[lut_index + 2] +
state.history[3][0] * lut[lut_index + 3]};
const s32 right{state.history[0][1] * lut[lut_index + 0] +
state.history[1][1] * lut[lut_index + 1] +
state.history[2][1] * lut[lut_index + 2] +
state.history[3][1] * lut[lut_index + 3]};
const s32 new_offset{state.fraction + step};
output[out_offset + 0] = static_cast<s16>(std::clamp(l >> 15, SHRT_MIN, SHRT_MAX));
output[out_offset + 1] = static_cast<s16>(std::clamp(r >> 15, SHRT_MIN, SHRT_MAX));
state.fraction = new_offset & 0x7fff;
output.emplace_back(static_cast<s16>(std::clamp(left >> 15, SHRT_MIN, SHRT_MAX)));
output.emplace_back(static_cast<s16>(std::clamp(right >> 15, SHRT_MIN, SHRT_MAX)));
state.position += ratio;
}
state.position -= 1.0;
}
return output;
+6 -1
View File
@@ -6,12 +6,17 @@
#include <array>
#include <vector>
#include "common/common_types.h"
namespace AudioCore {
struct InterpolationState {
int fraction = 0;
static constexpr std::size_t taps{4};
static constexpr std::size_t history_size{taps * 2 - 1};
std::array<std::array<s16, 2>, history_size> history{};
double position{};
s32 fraction{};
};
/// Interpolates input signal to produce output signal.
+17 -3
View File
@@ -8,6 +8,7 @@
#include "audio_core/cubeb_sink.h"
#include "audio_core/stream.h"
#include "audio_core/time_stretch.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/ring_buffer.h"
#include "core/settings.h"
@@ -65,12 +66,25 @@ public:
void EnqueueSamples(u32 source_num_channels, const std::vector<s16>& samples) override {
if (source_num_channels > num_channels) {
// Downsample 6 channels to 2
ASSERT_MSG(source_num_channels == 6, "Channel count must be 6");
std::vector<s16> buf;
buf.reserve(samples.size() * num_channels / source_num_channels);
for (std::size_t i = 0; i < samples.size(); i += source_num_channels) {
for (std::size_t ch = 0; ch < num_channels; ch++) {
buf.push_back(samples[i + ch]);
}
// Downmixing implementation taken from the ATSC standard
const s16 left{samples[i + 0]};
const s16 right{samples[i + 1]};
const s16 center{samples[i + 2]};
const s16 surround_left{samples[i + 4]};
const s16 surround_right{samples[i + 5]};
// Not used in the ATSC reference implementation
[[maybe_unused]] const s16 low_frequency_effects { samples[i + 3] };
constexpr s32 clev{707}; // center mixing level coefficient
constexpr s32 slev{707}; // surround mixing level coefficient
buf.push_back(left + (clev * center / 1000) + (slev * surround_left / 1000));
buf.push_back(right + (clev * center / 1000) + (slev * surround_right / 1000));
}
queue.Push(buf);
return;
+4 -4
View File
@@ -38,8 +38,6 @@ add_custom_command(OUTPUT scm_rev.cpp
"${VIDEO_CORE}/renderer_opengl/gl_shader_decompiler.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
"${VIDEO_CORE}/shader/decode/arithmetic.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_half.cpp"
"${VIDEO_CORE}/shader/decode/arithmetic_half_immediate.cpp"
@@ -72,8 +70,6 @@ add_custom_command(OUTPUT scm_rev.cpp
"${VIDEO_CORE}/shader/ast.h"
"${VIDEO_CORE}/shader/compiler_settings.cpp"
"${VIDEO_CORE}/shader/compiler_settings.h"
"${VIDEO_CORE}/shader/const_buffer_locker.cpp"
"${VIDEO_CORE}/shader/const_buffer_locker.h"
"${VIDEO_CORE}/shader/control_flow.cpp"
"${VIDEO_CORE}/shader/control_flow.h"
"${VIDEO_CORE}/shader/decode.cpp"
@@ -82,9 +78,13 @@ add_custom_command(OUTPUT scm_rev.cpp
"${VIDEO_CORE}/shader/node.h"
"${VIDEO_CORE}/shader/node_helper.cpp"
"${VIDEO_CORE}/shader/node_helper.h"
"${VIDEO_CORE}/shader/registry.cpp"
"${VIDEO_CORE}/shader/registry.h"
"${VIDEO_CORE}/shader/shader_ir.cpp"
"${VIDEO_CORE}/shader/shader_ir.h"
"${VIDEO_CORE}/shader/track.cpp"
"${VIDEO_CORE}/shader/transform_feedback.cpp"
"${VIDEO_CORE}/shader/transform_feedback.h"
# and also check that the scm_rev files haven't changed
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in"
"${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h"
+14 -2
View File
@@ -24,17 +24,29 @@ struct Rectangle {
: left(left), top(top), right(right), bottom(bottom) {}
T GetWidth() const {
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
if constexpr (std::is_floating_point_v<T>) {
return std::abs(right - left);
} else {
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
}
}
T GetHeight() const {
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
if constexpr (std::is_floating_point_v<T>) {
return std::abs(bottom - top);
} else {
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
}
}
Rectangle<T> TranslateX(const T x) const {
return Rectangle{left + x, top, right + x, bottom};
}
Rectangle<T> TranslateY(const T y) const {
return Rectangle{left, top + y, right, bottom + y};
}
Rectangle<T> Scale(const float s) const {
return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
static_cast<T>(top + GetHeight() * s)};
+11 -1
View File
@@ -16,7 +16,6 @@ void PageTable::Resize(std::size_t address_space_width_in_bits) {
pointers.resize(num_page_table_entries);
attributes.resize(num_page_table_entries);
backing_addr.resize(num_page_table_entries);
// The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
// vector size is subsequently decreased (via resize), the vector might not automatically
@@ -25,6 +24,17 @@ void PageTable::Resize(std::size_t address_space_width_in_bits) {
pointers.shrink_to_fit();
attributes.shrink_to_fit();
}
BackingPageTable::BackingPageTable(std::size_t page_size_in_bits) : PageTable{page_size_in_bits} {}
BackingPageTable::~BackingPageTable() = default;
void BackingPageTable::Resize(std::size_t address_space_width_in_bits) {
PageTable::Resize(address_space_width_in_bits);
const std::size_t num_page_table_entries = 1ULL
<< (address_space_width_in_bits - page_size_in_bits);
backing_addr.resize(num_page_table_entries);
backing_addr.shrink_to_fit();
}
+13 -2
View File
@@ -76,9 +76,20 @@ struct PageTable {
*/
std::vector<PageType> attributes;
std::vector<u64> backing_addr;
const std::size_t page_size_in_bits{};
};
/**
* A more advanced Page Table with the ability to save a backing address when using it
* depends on another MMU.
*/
struct BackingPageTable : PageTable {
explicit BackingPageTable(std::size_t page_size_in_bits);
~BackingPageTable();
void Resize(std::size_t address_space_width_in_bits);
std::vector<u64> backing_addr;
};
} // namespace Common
+8 -4
View File
@@ -131,8 +131,8 @@ add_library(core STATIC
frontend/framebuffer_layout.cpp
frontend/framebuffer_layout.h
frontend/input.h
frontend/scope_acquire_window_context.cpp
frontend/scope_acquire_window_context.h
frontend/scope_acquire_context.cpp
frontend/scope_acquire_context.h
gdbstub/gdbstub.cpp
gdbstub/gdbstub.h
hardware_interrupt_manager.cpp
@@ -595,8 +595,12 @@ endif()
if (ARCHITECTURE_x86_64)
target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic.cpp
arm/dynarmic/arm_dynarmic.h
arm/dynarmic/arm_dynarmic_32.cpp
arm/dynarmic/arm_dynarmic_32.h
arm/dynarmic/arm_dynarmic_64.cpp
arm/dynarmic/arm_dynarmic_64.h
arm/dynarmic/arm_dynarmic_cp15.cpp
arm/dynarmic/arm_dynarmic_cp15.h
)
target_link_libraries(core PRIVATE dynarmic)
endif()
+19 -13
View File
@@ -25,7 +25,20 @@ public:
explicit ARM_Interface(System& system_) : system{system_} {}
virtual ~ARM_Interface() = default;
struct ThreadContext {
struct ThreadContext32 {
std::array<u32, 16> cpu_registers;
u32 cpsr;
std::array<u8, 4> padding;
std::array<u64, 32> fprs;
u32 fpscr;
u32 fpexc;
u32 tpidr;
};
// Internally within the kernel, it expects the AArch32 version of the
// thread context to be 344 bytes in size.
static_assert(sizeof(ThreadContext32) == 0x158);
struct ThreadContext64 {
std::array<u64, 31> cpu_registers;
u64 sp;
u64 pc;
@@ -38,7 +51,7 @@ public:
};
// Internally within the kernel, it expects the AArch64 version of the
// thread context to be 800 bytes in size.
static_assert(sizeof(ThreadContext) == 0x320);
static_assert(sizeof(ThreadContext64) == 0x320);
/// Runs the CPU until an event happens
virtual void Run() = 0;
@@ -130,17 +143,10 @@ public:
*/
virtual void SetTPIDR_EL0(u64 value) = 0;
/**
* Saves the current CPU context
* @param ctx Thread context to save
*/
virtual void SaveContext(ThreadContext& ctx) = 0;
/**
* Loads a CPU context
* @param ctx Thread context to load
*/
virtual void LoadContext(const ThreadContext& ctx) = 0;
virtual void SaveContext(ThreadContext32& ctx) = 0;
virtual void SaveContext(ThreadContext64& ctx) = 0;
virtual void LoadContext(const ThreadContext32& ctx) = 0;
virtual void LoadContext(const ThreadContext64& ctx) = 0;
/// Clears the exclusive monitor's state.
virtual void ClearExclusiveState() = 0;
+208
View File
@@ -0,0 +1,208 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <memory>
#include <dynarmic/A32/a32.h>
#include <dynarmic/A32/config.h>
#include <dynarmic/A32/context.h>
#include "common/microprofile.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
#include "core/core.h"
#include "core/core_manager.h"
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
namespace Core {
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent) : parent(parent) {}
u8 MemoryRead8(u32 vaddr) override {
return parent.system.Memory().Read8(vaddr);
}
u16 MemoryRead16(u32 vaddr) override {
return parent.system.Memory().Read16(vaddr);
}
u32 MemoryRead32(u32 vaddr) override {
return parent.system.Memory().Read32(vaddr);
}
u64 MemoryRead64(u32 vaddr) override {
return parent.system.Memory().Read64(vaddr);
}
void MemoryWrite8(u32 vaddr, u8 value) override {
parent.system.Memory().Write8(vaddr, value);
}
void MemoryWrite16(u32 vaddr, u16 value) override {
parent.system.Memory().Write16(vaddr, value);
}
void MemoryWrite32(u32 vaddr, u32 value) override {
parent.system.Memory().Write32(vaddr, value);
}
void MemoryWrite64(u32 vaddr, u64 value) override {
parent.system.Memory().Write64(vaddr, value);
}
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
UNIMPLEMENTED();
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
switch (exception) {
case Dynarmic::A32::Exception::UndefinedInstruction:
case Dynarmic::A32::Exception::UnpredictableInstruction:
break;
case Dynarmic::A32::Exception::Breakpoint:
break;
}
LOG_CRITICAL(HW_GPU, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
UNIMPLEMENTED();
}
void CallSVC(u32 swi) override {
Kernel::CallSVC(parent.system, swi);
}
void AddTicks(u64 ticks) override {
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
// if not all cores are doing a similar amount of work. Instead of doing this, we should
// device a way so that timing is consistent across all cores without increasing the ticks 4
// times.
u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES;
// Always execute at least one tick.
amortized_ticks = std::max<u64>(amortized_ticks, 1);
parent.system.CoreTiming().AddTicks(amortized_ticks);
num_interpreted_instructions = 0;
}
u64 GetTicksRemaining() override {
return std::max(parent.system.CoreTiming().GetDowncount(), {});
}
ARM_Dynarmic_32& parent;
std::size_t num_interpreted_instructions{};
u64 tpidrro_el0{};
u64 tpidr_el0{};
};
std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table,
std::size_t address_space_bits) const {
Dynarmic::A32::UserConfig config;
config.callbacks = cb.get();
// TODO(bunnei): Implement page table for 32-bit
// config.page_table = &page_table.pointers;
config.coprocessors[15] = std::make_shared<DynarmicCP15>((u32*)&CP15_regs[0]);
config.define_unpredictable_behaviour = true;
return std::make_unique<Dynarmic::A32::Jit>(config);
}
MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_32, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
void ARM_Dynarmic_32::Run() {
MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_32);
jit->Run();
}
void ARM_Dynarmic_32::Step() {
cb->InterpreterFallback(jit->Regs()[15], 1);
}
ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor,
std::size_t core_index)
: ARM_Interface{system},
cb(std::make_unique<DynarmicCallbacks32>(*this)), core_index{core_index},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
void ARM_Dynarmic_32::SetPC(u64 pc) {
jit->Regs()[15] = static_cast<u32>(pc);
}
u64 ARM_Dynarmic_32::GetPC() const {
return jit->Regs()[15];
}
u64 ARM_Dynarmic_32::GetReg(int index) const {
return jit->Regs()[index];
}
void ARM_Dynarmic_32::SetReg(int index, u64 value) {
jit->Regs()[index] = static_cast<u32>(value);
}
u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
return {};
}
void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {}
u32 ARM_Dynarmic_32::GetPSTATE() const {
return jit->Cpsr();
}
void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) {
jit->SetCpsr(cpsr);
}
u64 ARM_Dynarmic_32::GetTlsAddress() const {
return CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)];
}
void ARM_Dynarmic_32::SetTlsAddress(VAddr address) {
CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)] = static_cast<u32>(address);
}
u64 ARM_Dynarmic_32::GetTPIDR_EL0() const {
return cb->tpidr_el0;
}
void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
cb->tpidr_el0 = value;
}
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
Dynarmic::A32::Context context;
jit->SaveContext(context);
ctx.cpu_registers = context.Regs();
ctx.cpsr = context.Cpsr();
}
void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
Dynarmic::A32::Context context;
context.Regs() = ctx.cpu_registers;
context.SetCpsr(ctx.cpsr);
jit->LoadContext(context);
}
void ARM_Dynarmic_32::PrepareReschedule() {
jit->HaltExecution();
}
void ARM_Dynarmic_32::ClearInstructionCache() {
jit->ClearCache();
}
void ARM_Dynarmic_32::ClearExclusiveState() {}
void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
std::size_t new_address_space_size_in_bits) {
auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
auto iter = jit_cache.find(key);
if (iter != jit_cache.end()) {
jit = iter->second;
return;
}
jit = MakeJit(page_table, new_address_space_size_in_bits);
jit_cache.emplace(key, jit);
}
} // namespace Core
+77
View File
@@ -0,0 +1,77 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <unordered_map>
#include <dynarmic/A32/a32.h>
#include <dynarmic/A64/a64.h>
#include <dynarmic/A64/exclusive_monitor.h>
#include "common/common_types.h"
#include "common/hash.h"
#include "core/arm/arm_interface.h"
#include "core/arm/exclusive_monitor.h"
namespace Memory {
class Memory;
}
namespace Core {
class DynarmicCallbacks32;
class DynarmicExclusiveMonitor;
class System;
class ARM_Dynarmic_32 final : public ARM_Interface {
public:
ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ARM_Dynarmic_32() override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
u64 GetReg(int index) const override;
void SetReg(int index, u64 value) override;
u128 GetVectorReg(int index) const override;
void SetVectorReg(int index, u128 value) override;
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
void Run() override;
void Step() override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
u64 GetTPIDR_EL0() const override;
void SaveContext(ThreadContext32& ctx) override;
void SaveContext(ThreadContext64& ctx) override {}
void LoadContext(const ThreadContext32& ctx) override;
void LoadContext(const ThreadContext64& ctx) override {}
void PrepareReschedule() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) override;
private:
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable& page_table,
std::size_t address_space_bits) const;
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
using JitCacheType =
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
friend class DynarmicCallbacks32;
std::unique_ptr<DynarmicCallbacks32> cb;
JitCacheType jit_cache;
std::shared_ptr<Dynarmic::A32::Jit> jit;
std::size_t core_index;
DynarmicExclusiveMonitor& exclusive_monitor;
std::array<u32, 84> CP15_regs{};
};
} // namespace Core
@@ -8,7 +8,7 @@
#include <dynarmic/A64/config.h>
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#include "core/core.h"
#include "core/core_manager.h"
#include "core/core_timing.h"
@@ -25,9 +25,9 @@ namespace Core {
using Vector = Dynarmic::A64::Vector;
class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent) : parent(parent) {}
u8 MemoryRead8(u64 vaddr) override {
return parent.system.Memory().Read8(vaddr);
@@ -68,7 +68,7 @@ public:
LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc,
num_instructions, MemoryReadCode(pc));
ARM_Interface::ThreadContext ctx;
ARM_Interface::ThreadContext64 ctx;
parent.SaveContext(ctx);
parent.inner_unicorn.LoadContext(ctx);
parent.inner_unicorn.ExecuteInstructions(num_instructions);
@@ -90,7 +90,7 @@ public:
parent.jit->HaltExecution();
parent.SetPC(pc);
Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread();
parent.SaveContext(thread->GetContext());
parent.SaveContext(thread->GetContext64());
GDBStub::Break();
GDBStub::SendTrap(thread, 5);
return;
@@ -126,14 +126,14 @@ public:
return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks());
}
ARM_Dynarmic& parent;
ARM_Dynarmic_64& parent;
std::size_t num_interpreted_instructions = 0;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
};
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& page_table,
std::size_t address_space_bits) const {
std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table,
std::size_t address_space_bits) const {
Dynarmic::A64::UserConfig config;
// Callbacks
@@ -159,79 +159,79 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& pag
// Unpredictable instructions
config.define_unpredictable_behaviour = true;
return std::make_unique<Dynarmic::A64::Jit>(config);
return std::make_shared<Dynarmic::A64::Jit>(config);
}
MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_64, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
void ARM_Dynarmic::Run() {
MICROPROFILE_SCOPE(ARM_Jit_Dynarmic);
void ARM_Dynarmic_64::Run() {
MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_64);
jit->Run();
}
void ARM_Dynarmic::Step() {
void ARM_Dynarmic_64::Step() {
cb->InterpreterFallback(jit->GetPC(), 1);
}
ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
std::size_t core_index)
ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor,
std::size_t core_index)
: ARM_Interface{system},
cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
cb(std::make_unique<DynarmicCallbacks64>(*this)), inner_unicorn{system},
core_index{core_index}, exclusive_monitor{
dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
ARM_Dynarmic::~ARM_Dynarmic() = default;
ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
void ARM_Dynarmic::SetPC(u64 pc) {
void ARM_Dynarmic_64::SetPC(u64 pc) {
jit->SetPC(pc);
}
u64 ARM_Dynarmic::GetPC() const {
u64 ARM_Dynarmic_64::GetPC() const {
return jit->GetPC();
}
u64 ARM_Dynarmic::GetReg(int index) const {
u64 ARM_Dynarmic_64::GetReg(int index) const {
return jit->GetRegister(index);
}
void ARM_Dynarmic::SetReg(int index, u64 value) {
void ARM_Dynarmic_64::SetReg(int index, u64 value) {
jit->SetRegister(index, value);
}
u128 ARM_Dynarmic::GetVectorReg(int index) const {
u128 ARM_Dynarmic_64::GetVectorReg(int index) const {
return jit->GetVector(index);
}
void ARM_Dynarmic::SetVectorReg(int index, u128 value) {
void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) {
jit->SetVector(index, value);
}
u32 ARM_Dynarmic::GetPSTATE() const {
u32 ARM_Dynarmic_64::GetPSTATE() const {
return jit->GetPstate();
}
void ARM_Dynarmic::SetPSTATE(u32 pstate) {
void ARM_Dynarmic_64::SetPSTATE(u32 pstate) {
jit->SetPstate(pstate);
}
u64 ARM_Dynarmic::GetTlsAddress() const {
u64 ARM_Dynarmic_64::GetTlsAddress() const {
return cb->tpidrro_el0;
}
void ARM_Dynarmic::SetTlsAddress(VAddr address) {
void ARM_Dynarmic_64::SetTlsAddress(VAddr address) {
cb->tpidrro_el0 = address;
}
u64 ARM_Dynarmic::GetTPIDR_EL0() const {
u64 ARM_Dynarmic_64::GetTPIDR_EL0() const {
return cb->tpidr_el0;
}
void ARM_Dynarmic::SetTPIDR_EL0(u64 value) {
void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
cb->tpidr_el0 = value;
}
void ARM_Dynarmic::SaveContext(ThreadContext& ctx) {
void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
ctx.pc = jit->GetPC();
@@ -242,7 +242,7 @@ void ARM_Dynarmic::SaveContext(ThreadContext& ctx) {
ctx.tpidr = cb->tpidr_el0;
}
void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
jit->SetRegisters(ctx.cpu_registers);
jit->SetSP(ctx.sp);
jit->SetPC(ctx.pc);
@@ -253,25 +253,32 @@ void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
SetTPIDR_EL0(ctx.tpidr);
}
void ARM_Dynarmic::PrepareReschedule() {
void ARM_Dynarmic_64::PrepareReschedule() {
jit->HaltExecution();
}
void ARM_Dynarmic::ClearInstructionCache() {
void ARM_Dynarmic_64::ClearInstructionCache() {
jit->ClearCache();
}
void ARM_Dynarmic::ClearExclusiveState() {
void ARM_Dynarmic_64::ClearExclusiveState() {
jit->ClearExclusiveState();
}
void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table,
std::size_t new_address_space_size_in_bits) {
void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
std::size_t new_address_space_size_in_bits) {
auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
auto iter = jit_cache.find(key);
if (iter != jit_cache.end()) {
jit = iter->second;
return;
}
jit = MakeJit(page_table, new_address_space_size_in_bits);
jit_cache.emplace(key, jit);
}
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count)
: monitor(core_count), memory{memory_} {}
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count)
: monitor(core_count), memory{memory} {}
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
@@ -5,9 +5,12 @@
#pragma once
#include <memory>
#include <unordered_map>
#include <dynarmic/A64/a64.h>
#include <dynarmic/A64/exclusive_monitor.h>
#include "common/common_types.h"
#include "common/hash.h"
#include "core/arm/arm_interface.h"
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
@@ -18,14 +21,14 @@ class Memory;
namespace Core {
class ARM_Dynarmic_Callbacks;
class DynarmicCallbacks64;
class DynarmicExclusiveMonitor;
class System;
class ARM_Dynarmic final : public ARM_Interface {
class ARM_Dynarmic_64 final : public ARM_Interface {
public:
ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ARM_Dynarmic() override;
ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ARM_Dynarmic_64() override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
@@ -42,8 +45,10 @@ public:
void SetTPIDR_EL0(u64 value) override;
u64 GetTPIDR_EL0() const override;
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;
void SaveContext(ThreadContext32& ctx) override {}
void SaveContext(ThreadContext64& ctx) override;
void LoadContext(const ThreadContext32& ctx) override {}
void LoadContext(const ThreadContext64& ctx) override;
void PrepareReschedule() override;
void ClearExclusiveState() override;
@@ -53,12 +58,17 @@ public:
std::size_t new_address_space_size_in_bits) override;
private:
std::unique_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
std::size_t address_space_bits) const;
friend class ARM_Dynarmic_Callbacks;
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
std::unique_ptr<Dynarmic::A64::Jit> jit;
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
using JitCacheType =
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;
friend class DynarmicCallbacks64;
std::unique_ptr<DynarmicCallbacks64> cb;
JitCacheType jit_cache;
std::shared_ptr<Dynarmic::A64::Jit> jit;
ARM_Unicorn inner_unicorn;
std::size_t core_index;
@@ -67,7 +77,7 @@ private:
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
public:
explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count);
explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count);
~DynarmicExclusiveMonitor() override;
void SetExclusive(std::size_t core_index, VAddr addr) override;
@@ -80,7 +90,7 @@ public:
bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override;
private:
friend class ARM_Dynarmic;
friend class ARM_Dynarmic_64;
Dynarmic::A64::ExclusiveMonitor monitor;
Memory::Memory& memory;
};
@@ -0,0 +1,80 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
using Callback = Dynarmic::A32::Coprocessor::Callback;
using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigned opc1,
CoprocReg CRd, CoprocReg CRn,
CoprocReg CRm, unsigned opc2) {
return {};
}
CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
CoprocReg CRm, unsigned opc2) {
// TODO(merry): Privileged CP15 registers
if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C5 && opc2 == 4) {
// This is a dummy write, we ignore the value written here.
return &CP15[static_cast<std::size_t>(CP15Register::CP15_FLUSH_PREFETCH_BUFFER)];
}
if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C10) {
switch (opc2) {
case 4:
// This is a dummy write, we ignore the value written here.
return &CP15[static_cast<std::size_t>(CP15Register::CP15_DATA_SYNC_BARRIER)];
case 5:
// This is a dummy write, we ignore the value written here.
return &CP15[static_cast<std::size_t>(CP15Register::CP15_DATA_MEMORY_BARRIER)];
default:
return {};
}
}
if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0 && opc2 == 2) {
return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_UPRW)];
}
return {};
}
CallbackOrAccessTwoWords DynarmicCP15::CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) {
return {};
}
CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn,
CoprocReg CRm, unsigned opc2) {
// TODO(merry): Privileged CP15 registers
if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0) {
switch (opc2) {
case 2:
return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_UPRW)];
case 3:
return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)];
default:
return {};
}
}
return {};
}
CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {
return {};
}
std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd,
std::optional<u8> option) {
return {};
}
std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
std::optional<u8> option) {
return {};
}
+152
View File
@@ -0,0 +1,152 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <optional>
#include <dynarmic/A32/coprocessor.h>
#include "common/common_types.h"
enum class CP15Register {
// c0 - Information registers
CP15_MAIN_ID,
CP15_CACHE_TYPE,
CP15_TCM_STATUS,
CP15_TLB_TYPE,
CP15_CPU_ID,
CP15_PROCESSOR_FEATURE_0,
CP15_PROCESSOR_FEATURE_1,
CP15_DEBUG_FEATURE_0,
CP15_AUXILIARY_FEATURE_0,
CP15_MEMORY_MODEL_FEATURE_0,
CP15_MEMORY_MODEL_FEATURE_1,
CP15_MEMORY_MODEL_FEATURE_2,
CP15_MEMORY_MODEL_FEATURE_3,
CP15_ISA_FEATURE_0,
CP15_ISA_FEATURE_1,
CP15_ISA_FEATURE_2,
CP15_ISA_FEATURE_3,
CP15_ISA_FEATURE_4,
// c1 - Control registers
CP15_CONTROL,
CP15_AUXILIARY_CONTROL,
CP15_COPROCESSOR_ACCESS_CONTROL,
// c2 - Translation table registers
CP15_TRANSLATION_BASE_TABLE_0,
CP15_TRANSLATION_BASE_TABLE_1,
CP15_TRANSLATION_BASE_CONTROL,
CP15_DOMAIN_ACCESS_CONTROL,
CP15_RESERVED,
// c5 - Fault status registers
CP15_FAULT_STATUS,
CP15_INSTR_FAULT_STATUS,
CP15_COMBINED_DATA_FSR = CP15_FAULT_STATUS,
CP15_INST_FSR,
// c6 - Fault Address registers
CP15_FAULT_ADDRESS,
CP15_COMBINED_DATA_FAR = CP15_FAULT_ADDRESS,
CP15_WFAR,
CP15_IFAR,
// c7 - Cache operation registers
CP15_WAIT_FOR_INTERRUPT,
CP15_PHYS_ADDRESS,
CP15_INVALIDATE_INSTR_CACHE,
CP15_INVALIDATE_INSTR_CACHE_USING_MVA,
CP15_INVALIDATE_INSTR_CACHE_USING_INDEX,
CP15_FLUSH_PREFETCH_BUFFER,
CP15_FLUSH_BRANCH_TARGET_CACHE,
CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY,
CP15_INVALIDATE_DATA_CACHE,
CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
CP15_INVALIDATE_DATA_AND_INSTR_CACHE,
CP15_CLEAN_DATA_CACHE,
CP15_CLEAN_DATA_CACHE_LINE_USING_MVA,
CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX,
CP15_DATA_SYNC_BARRIER,
CP15_DATA_MEMORY_BARRIER,
CP15_CLEAN_AND_INVALIDATE_DATA_CACHE,
CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
// c8 - TLB operations
CP15_INVALIDATE_ITLB,
CP15_INVALIDATE_ITLB_SINGLE_ENTRY,
CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH,
CP15_INVALIDATE_ITLB_ENTRY_ON_MVA,
CP15_INVALIDATE_DTLB,
CP15_INVALIDATE_DTLB_SINGLE_ENTRY,
CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH,
CP15_INVALIDATE_DTLB_ENTRY_ON_MVA,
CP15_INVALIDATE_UTLB,
CP15_INVALIDATE_UTLB_SINGLE_ENTRY,
CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH,
CP15_INVALIDATE_UTLB_ENTRY_ON_MVA,
// c9 - Data cache lockdown register
CP15_DATA_CACHE_LOCKDOWN,
// c10 - TLB/Memory map registers
CP15_TLB_LOCKDOWN,
CP15_PRIMARY_REGION_REMAP,
CP15_NORMAL_REGION_REMAP,
// c13 - Thread related registers
CP15_PID,
CP15_CONTEXT_ID,
CP15_THREAD_UPRW, // Thread ID register - User/Privileged Read/Write
CP15_THREAD_URO, // Thread ID register - User Read Only (Privileged R/W)
CP15_THREAD_PRW, // Thread ID register - Privileged R/W only.
// c15 - Performance and TLB lockdown registers
CP15_PERFORMANCE_MONITOR_CONTROL,
CP15_CYCLE_COUNTER,
CP15_COUNT_0,
CP15_COUNT_1,
CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY,
CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY,
CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS,
CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS,
CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE,
CP15_TLB_DEBUG_CONTROL,
// Skyeye defined
CP15_TLB_FAULT_ADDR,
CP15_TLB_FAULT_STATUS,
// Not an actual register.
// All registers should be defined above this.
CP15_REGISTER_COUNT,
};
class DynarmicCP15 final : public Dynarmic::A32::Coprocessor {
public:
using CoprocReg = Dynarmic::A32::CoprocReg;
explicit DynarmicCP15(u32* cp15) : CP15(cp15){};
std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd,
CoprocReg CRn, CoprocReg CRm,
unsigned opc2) override;
CallbackOrAccessOneWord CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
CoprocReg CRm, unsigned opc2) override;
CallbackOrAccessTwoWords CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) override;
CallbackOrAccessOneWord CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm,
unsigned opc2) override;
CallbackOrAccessTwoWords CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) override;
std::optional<Callback> CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd,
std::optional<u8> option) override;
std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
std::optional<u8> option) override;
private:
u32* CP15{};
};
+1 -1
View File
@@ -3,7 +3,7 @@
// Refer to the license.txt file included.
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#endif
#include "core/arm/exclusive_monitor.h"
#include "core/memory.h"
+4 -4
View File
@@ -53,7 +53,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
void* user_data) {
auto* const system = static_cast<System*>(user_data);
ARM_Interface::ThreadContext ctx{};
ARM_Interface::ThreadContext64 ctx{};
system->CurrentArmInterface().SaveContext(ctx);
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr,
ctx.pc, ctx.cpu_registers[30]);
@@ -179,7 +179,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
}
Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread();
SaveContext(thread->GetContext());
SaveContext(thread->GetContext64());
if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false;
GDBStub::Break();
@@ -188,7 +188,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
}
}
void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
void ARM_Unicorn::SaveContext(ThreadContext64& ctx) {
int uregs[32];
void* tregs[32];
@@ -215,7 +215,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
}
void ARM_Unicorn::LoadContext(const ThreadContext& ctx) {
void ARM_Unicorn::LoadContext(const ThreadContext64& ctx) {
int uregs[32];
void* tregs[32];
+5 -2
View File
@@ -30,8 +30,6 @@ public:
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
u64 GetTPIDR_EL0() const override;
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;
void ClearExclusiveState() override;
void ExecuteInstructions(std::size_t num_instructions);
@@ -41,6 +39,11 @@ public:
void PageTableChanged(Common::PageTable&, std::size_t) override {}
void RecordBreak(GDBStub::BreakpointAddress bkpt);
void SaveContext(ThreadContext32& ctx) override {}
void SaveContext(ThreadContext64& ctx) override;
void LoadContext(const ThreadContext32& ctx) override {}
void LoadContext(const ThreadContext64& ctx) override;
private:
static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
+5 -1
View File
@@ -24,6 +24,7 @@
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hardware_interrupt_manager.h"
#include "core/hle/kernel/client_port.h"
@@ -165,7 +166,7 @@ struct System::Impl {
service_manager = std::make_shared<Service::SM::ServiceManager>();
Service::Init(service_manager, system);
GDBStub::Init();
GDBStub::DeferStart();
renderer = VideoCore::CreateRenderer(emu_window, system);
if (!renderer->Init()) {
@@ -173,6 +174,7 @@ struct System::Impl {
}
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
gpu_core = VideoCore::CreateGPU(system);
renderer->Rasterizer().SetupDirtyFlags();
is_powered_on = true;
exit_lock = false;
@@ -184,6 +186,8 @@ struct System::Impl {
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath) {
Core::Frontend::ScopeAcquireContext acquire_context{emu_window};
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
-3
View File
@@ -6,9 +6,6 @@
#include <mutex>
#include "common/logging/log.h"
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_dynarmic.h"
#endif
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
#include "core/core.h"
-3
View File
@@ -26,9 +26,6 @@ public:
/// Releases (dunno if this is the "right" word) the context from the caller thread
virtual void DoneCurrent() = 0;
/// Swap buffers to display the next frame
virtual void SwapBuffers() = 0;
};
/**
+2 -2
View File
@@ -48,8 +48,8 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
u32 width, height;
if (Settings::values.use_docked_mode) {
width = ScreenDocked::WidthDocked * res_scale;
height = ScreenDocked::HeightDocked * res_scale;
width = ScreenDocked::Width * res_scale;
height = ScreenDocked::Height * res_scale;
} else {
width = ScreenUndocked::Width * res_scale;
height = ScreenUndocked::Height * res_scale;
+9 -8
View File
@@ -8,15 +8,15 @@
namespace Layout {
enum ScreenUndocked : u32 {
Width = 1280,
Height = 720,
};
namespace ScreenUndocked {
constexpr u32 Width = 1280;
constexpr u32 Height = 720;
} // namespace ScreenUndocked
enum ScreenDocked : u32 {
WidthDocked = 1920,
HeightDocked = 1080,
};
namespace ScreenDocked {
constexpr u32 Width = 1920;
constexpr u32 Height = 1080;
} // namespace ScreenDocked
enum class AspectRatio {
Default,
@@ -29,6 +29,7 @@ enum class AspectRatio {
struct FramebufferLayout {
u32 width{ScreenUndocked::Width};
u32 height{ScreenUndocked::Height};
bool is_srgb{};
Common::Rectangle<u32> screen;
@@ -0,0 +1,18 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/frontend/emu_window.h"
#include "core/frontend/scope_acquire_context.h"
namespace Core::Frontend {
ScopeAcquireContext::ScopeAcquireContext(Core::Frontend::GraphicsContext& context)
: context{context} {
context.MakeCurrent();
}
ScopeAcquireContext::~ScopeAcquireContext() {
context.DoneCurrent();
}
} // namespace Core::Frontend
@@ -8,16 +8,16 @@
namespace Core::Frontend {
class EmuWindow;
class GraphicsContext;
/// Helper class to acquire/release window context within a given scope
class ScopeAcquireWindowContext : NonCopyable {
class ScopeAcquireContext : NonCopyable {
public:
explicit ScopeAcquireWindowContext(Core::Frontend::EmuWindow& window);
~ScopeAcquireWindowContext();
explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context);
~ScopeAcquireContext();
private:
Core::Frontend::EmuWindow& emu_window;
Core::Frontend::GraphicsContext& context;
};
} // namespace Core::Frontend
@@ -1,18 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/frontend/emu_window.h"
#include "core/frontend/scope_acquire_window_context.h"
namespace Core::Frontend {
ScopeAcquireWindowContext::ScopeAcquireWindowContext(Core::Frontend::EmuWindow& emu_window_)
: emu_window{emu_window_} {
emu_window.MakeCurrent();
}
ScopeAcquireWindowContext::~ScopeAcquireWindowContext() {
emu_window.DoneCurrent();
}
} // namespace Core::Frontend
+16 -7
View File
@@ -141,6 +141,7 @@ constexpr char target_xml[] =
)";
int gdbserver_socket = -1;
bool defer_start = false;
u8 command_buffer[GDB_BUFFER_SIZE];
u32 command_length;
@@ -217,7 +218,7 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) {
return 0;
}
const auto& thread_context = thread->GetContext();
const auto& thread_context = thread->GetContext64();
if (id < SP_REGISTER) {
return thread_context.cpu_registers[id];
@@ -239,7 +240,7 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr)
return;
}
auto& thread_context = thread->GetContext();
auto& thread_context = thread->GetContext64();
if (id < SP_REGISTER) {
thread_context.cpu_registers[id] = val;
@@ -259,7 +260,7 @@ static u128 FpuRead(std::size_t id, Kernel::Thread* thread = nullptr) {
return u128{0};
}
auto& thread_context = thread->GetContext();
auto& thread_context = thread->GetContext64();
if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
return thread_context.vector_registers[id - UC_ARM64_REG_Q0];
@@ -275,7 +276,7 @@ static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr)
return;
}
auto& thread_context = thread->GetContext();
auto& thread_context = thread->GetContext64();
if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val;
@@ -916,7 +917,7 @@ static void WriteRegister() {
// Update ARM context, skipping scheduler - no running threads at this point
Core::System::GetInstance()
.ArmInterface(current_core)
.LoadContext(current_thread->GetContext());
.LoadContext(current_thread->GetContext64());
SendReply("OK");
}
@@ -947,7 +948,7 @@ static void WriteRegisters() {
// Update ARM context, skipping scheduler - no running threads at this point
Core::System::GetInstance()
.ArmInterface(current_core)
.LoadContext(current_thread->GetContext());
.LoadContext(current_thread->GetContext64());
SendReply("OK");
}
@@ -1019,7 +1020,7 @@ static void Step() {
// Update ARM context, skipping scheduler - no running threads at this point
Core::System::GetInstance()
.ArmInterface(current_core)
.LoadContext(current_thread->GetContext());
.LoadContext(current_thread->GetContext64());
}
step_loop = true;
halt_loop = true;
@@ -1166,6 +1167,9 @@ static void RemoveBreakpoint() {
void HandlePacket() {
if (!IsConnected()) {
if (defer_start) {
ToggleServer(true);
}
return;
}
@@ -1256,6 +1260,10 @@ void ToggleServer(bool status) {
}
}
void DeferStart() {
defer_start = true;
}
static void Init(u16 port) {
if (!server_enabled) {
// Set the halt loop to false in case the user enabled the gdbstub mid-execution.
@@ -1341,6 +1349,7 @@ void Shutdown() {
if (!server_enabled) {
return;
}
defer_start = false;
LOG_INFO(Debug_GDBStub, "Stopping GDB ...");
if (gdbserver_socket != -1) {
+7
View File
@@ -43,6 +43,13 @@ void ToggleServer(bool status);
/// Start the gdbstub server.
void Init();
/**
* Defer initialization of the gdbstub to the first packet processing functions.
* This avoids a case where the gdbstub thread is frozen after initialization
* and fails to respond in time to packets.
*/
void DeferStart();
/// Stop gdbstub server.
void Shutdown();
+4
View File
@@ -186,6 +186,10 @@ struct KernelCore::Impl {
return;
}
for (auto& core : cores) {
core.SetIs64Bit(process->Is64BitProcess());
}
system.Memory().SetCurrentPageTable(*process);
}
+16 -3
View File
@@ -5,7 +5,8 @@
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#endif
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
@@ -20,13 +21,17 @@ PhysicalCore::PhysicalCore(Core::System& system, std::size_t id,
Core::ExclusiveMonitor& exclusive_monitor)
: core_index{id} {
#ifdef ARCHITECTURE_x86_64
arm_interface = std::make_unique<Core::ARM_Dynarmic>(system, exclusive_monitor, core_index);
arm_interface_32 =
std::make_unique<Core::ARM_Dynarmic_32>(system, exclusive_monitor, core_index);
arm_interface_64 =
std::make_unique<Core::ARM_Dynarmic_64>(system, exclusive_monitor, core_index);
#else
arm_interface = std::make_shared<Core::ARM_Unicorn>(system);
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index);
scheduler = std::make_unique<Kernel::Scheduler>(system, core_index);
}
PhysicalCore::~PhysicalCore() = default;
@@ -48,4 +53,12 @@ void PhysicalCore::Shutdown() {
scheduler->Shutdown();
}
void PhysicalCore::SetIs64Bit(bool is_64_bit) {
if (is_64_bit) {
arm_interface = arm_interface_64.get();
} else {
arm_interface = arm_interface_32.get();
}
}
} // namespace Kernel
+5 -1
View File
@@ -68,10 +68,14 @@ public:
return *scheduler;
}
void SetIs64Bit(bool is_64_bit);
private:
std::size_t core_index;
std::unique_ptr<Core::ARM_Interface> arm_interface;
std::unique_ptr<Core::ARM_Interface> arm_interface_32;
std::unique_ptr<Core::ARM_Interface> arm_interface_64;
std::unique_ptr<Kernel::Scheduler> scheduler;
Core::ARM_Interface* arm_interface{};
};
} // namespace Kernel
+2 -1
View File
@@ -42,7 +42,8 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
// Register 1 must be a handle to the main thread
const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
thread->GetContext().cpu_registers[1] = thread_handle;
thread->GetContext32().cpu_registers[1] = thread_handle;
thread->GetContext64().cpu_registers[1] = thread_handle;
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
thread->ResumeFromWait();
+12 -9
View File
@@ -383,8 +383,8 @@ void GlobalScheduler::Unlock() {
// TODO(Blinkhawk): Setup the interrupts and change context on current core.
}
Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id)
: system(system), cpu_core(cpu_core), core_id(core_id) {}
Scheduler::Scheduler(Core::System& system, std::size_t core_id)
: system{system}, core_id{core_id} {}
Scheduler::~Scheduler() = default;
@@ -422,9 +422,10 @@ void Scheduler::UnloadThread() {
// Save context for previous thread
if (previous_thread) {
cpu_core.SaveContext(previous_thread->GetContext());
system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32());
system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified.
previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0());
if (previous_thread->GetStatus() == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
@@ -451,9 +452,10 @@ void Scheduler::SwitchContext() {
// Save context for previous thread
if (previous_thread) {
cpu_core.SaveContext(previous_thread->GetContext());
system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32());
system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified.
previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0());
if (previous_thread->GetStatus() == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
@@ -481,9 +483,10 @@ void Scheduler::SwitchContext() {
system.Kernel().MakeCurrentProcess(thread_owner_process);
}
cpu_core.LoadContext(new_thread->GetContext());
cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
system.ArmInterface(core_id).LoadContext(new_thread->GetContext32());
system.ArmInterface(core_id).LoadContext(new_thread->GetContext64());
system.ArmInterface(core_id).SetTlsAddress(new_thread->GetTLSAddress());
system.ArmInterface(core_id).SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
} else {
current_thread = nullptr;
// Note: We do not reset the current process and current page table when idling because
+1 -2
View File
@@ -181,7 +181,7 @@ private:
class Scheduler final {
public:
explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id);
explicit Scheduler(Core::System& system, std::size_t core_id);
~Scheduler();
/// Returns whether there are any threads that are ready to run.
@@ -235,7 +235,6 @@ private:
std::shared_ptr<Thread> selected_thread = nullptr;
Core::System& system;
Core::ARM_Interface& cpu_core;
u64 last_context_switch_time = 0;
u64 idle_selection_count = 0;
const std::size_t core_id;
+264 -65
View File
@@ -187,6 +187,13 @@ static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_s
return RESULT_SUCCESS;
}
static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
VAddr temp_heap_addr{};
const ResultCode result{SetHeapSize(system, &temp_heap_addr, heap_size)};
*heap_addr = static_cast<u32>(temp_heap_addr);
return result;
}
static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) {
LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
@@ -371,6 +378,12 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
return RESULT_SUCCESS;
}
static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
u32 port_name_address) {
return ConnectToNamedPort(system, out_handle, port_name_address);
}
/// Makes a blocking IPC call to an OS service.
static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
@@ -390,6 +403,10 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
return session->SendSyncRequest(SharedFrom(thread), system.Memory());
}
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
return SendSyncRequest(system, handle);
}
/// Get the ID for the specified thread.
static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
@@ -405,6 +422,17 @@ static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle threa
return RESULT_SUCCESS;
}
static ResultCode GetThreadId32(Core::System& system, u32* thread_id_low, u32* thread_id_high,
Handle thread_handle) {
u64 thread_id{};
const ResultCode result{GetThreadId(system, &thread_id, thread_handle)};
*thread_id_low = static_cast<u32>(thread_id >> 32);
*thread_id_high = static_cast<u32>(thread_id & std::numeric_limits<u32>::max());
return result;
}
/// Gets the ID of the specified process or a specified thread's owning process.
static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
@@ -479,6 +507,12 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
return result;
}
static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
s32 handle_count, u32 timeout_high, Handle* index) {
const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds);
}
/// Resumes a thread waiting on WaitSynchronization
static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
@@ -917,6 +951,18 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
}
}
static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
u32 info_id, u32 handle, u32 sub_id_high) {
const u64 sub_id{static_cast<u64>(sub_id_low | (static_cast<u64>(sub_id_high) << 32))};
u64 res_value{};
const ResultCode result{GetInfo(system, &res_value, info_id, handle, sub_id)};
*result_high = static_cast<u32>(res_value >> 32);
*result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max());
return result;
}
/// Maps memory at a desired address
static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
@@ -1058,7 +1104,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, H
return ERR_BUSY;
}
Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
Core::ARM_Interface::ThreadContext64 ctx = thread->GetContext64();
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
ctx.pstate &= 0xFF0FFE20;
@@ -1088,6 +1134,10 @@ static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle
return RESULT_SUCCESS;
}
static ResultCode GetThreadPriority32(Core::System& system, u32* priority, Handle handle) {
return GetThreadPriority(system, priority, handle);
}
/// Sets the priority for the specified thread
static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) {
LOG_TRACE(Kernel_SVC, "called");
@@ -1259,6 +1309,11 @@ static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
query_address);
}
static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address,
u32 page_info_address, u32 query_address) {
return QueryMemory(system, memory_info_address, page_info_address, query_address);
}
static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
u64 src_address, u64 size) {
LOG_DEBUG(Kernel_SVC,
@@ -1675,6 +1730,10 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
}
}
static void SignalProcessWideKey32(Core::System& system, u32 condition_variable_addr, s32 target) {
SignalProcessWideKey(system, condition_variable_addr, target);
}
// Wait for an address (via Address Arbiter)
static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value,
s64 timeout) {
@@ -1760,6 +1819,10 @@ static ResultCode CloseHandle(Core::System& system, Handle handle) {
return handle_table.Close(handle);
}
static ResultCode CloseHandle32(Core::System& system, Handle handle) {
return CloseHandle(system, handle);
}
/// Clears the signaled state of an event or process.
static ResultCode ResetSignal(Core::System& system, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
@@ -2317,69 +2380,196 @@ struct FunctionDef {
};
} // namespace
static const FunctionDef SVC_Table[] = {
static const FunctionDef SVC_Table_32[] = {
{0x00, nullptr, "Unknown"},
{0x01, SvcWrap<SetHeapSize>, "SetHeapSize"},
{0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"},
{0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"},
{0x04, SvcWrap<MapMemory>, "MapMemory"},
{0x05, SvcWrap<UnmapMemory>, "UnmapMemory"},
{0x06, SvcWrap<QueryMemory>, "QueryMemory"},
{0x07, SvcWrap<ExitProcess>, "ExitProcess"},
{0x08, SvcWrap<CreateThread>, "CreateThread"},
{0x09, SvcWrap<StartThread>, "StartThread"},
{0x0A, SvcWrap<ExitThread>, "ExitThread"},
{0x0B, SvcWrap<SleepThread>, "SleepThread"},
{0x0C, SvcWrap<GetThreadPriority>, "GetThreadPriority"},
{0x0D, SvcWrap<SetThreadPriority>, "SetThreadPriority"},
{0x0E, SvcWrap<GetThreadCoreMask>, "GetThreadCoreMask"},
{0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"},
{0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
{0x11, SvcWrap<SignalEvent>, "SignalEvent"},
{0x12, SvcWrap<ClearEvent>, "ClearEvent"},
{0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"},
{0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"},
{0x15, SvcWrap<CreateTransferMemory>, "CreateTransferMemory"},
{0x16, SvcWrap<CloseHandle>, "CloseHandle"},
{0x17, SvcWrap<ResetSignal>, "ResetSignal"},
{0x18, SvcWrap<WaitSynchronization>, "WaitSynchronization"},
{0x19, SvcWrap<CancelSynchronization>, "CancelSynchronization"},
{0x1A, SvcWrap<ArbitrateLock>, "ArbitrateLock"},
{0x1B, SvcWrap<ArbitrateUnlock>, "ArbitrateUnlock"},
{0x1C, SvcWrap<WaitProcessWideKeyAtomic>, "WaitProcessWideKeyAtomic"},
{0x1D, SvcWrap<SignalProcessWideKey>, "SignalProcessWideKey"},
{0x1E, SvcWrap<GetSystemTick>, "GetSystemTick"},
{0x1F, SvcWrap<ConnectToNamedPort>, "ConnectToNamedPort"},
{0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"},
{0x02, nullptr, "Unknown"},
{0x03, nullptr, "SetMemoryAttribute32"},
{0x04, nullptr, "MapMemory32"},
{0x05, nullptr, "UnmapMemory32"},
{0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"},
{0x07, nullptr, "ExitProcess32"},
{0x08, nullptr, "CreateThread32"},
{0x09, nullptr, "StartThread32"},
{0x0a, nullptr, "ExitThread32"},
{0x0b, nullptr, "SleepThread32"},
{0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"},
{0x0d, nullptr, "SetThreadPriority32"},
{0x0e, nullptr, "GetThreadCoreMask32"},
{0x0f, nullptr, "SetThreadCoreMask32"},
{0x10, nullptr, "GetCurrentProcessorNumber32"},
{0x11, nullptr, "SignalEvent32"},
{0x12, nullptr, "ClearEvent32"},
{0x13, nullptr, "MapSharedMemory32"},
{0x14, nullptr, "UnmapSharedMemory32"},
{0x15, nullptr, "CreateTransferMemory32"},
{0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"},
{0x17, nullptr, "ResetSignal32"},
{0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"},
{0x19, nullptr, "CancelSynchronization32"},
{0x1a, nullptr, "ArbitrateLock32"},
{0x1b, nullptr, "ArbitrateUnlock32"},
{0x1c, nullptr, "WaitProcessWideKeyAtomic32"},
{0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"},
{0x1e, nullptr, "GetSystemTick32"},
{0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"},
{0x20, nullptr, "Unknown"},
{0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"},
{0x22, nullptr, "SendSyncRequestWithUserBuffer32"},
{0x23, nullptr, "Unknown"},
{0x24, nullptr, "GetProcessId32"},
{0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"},
{0x26, nullptr, "Break32"},
{0x27, nullptr, "OutputDebugString32"},
{0x28, nullptr, "Unknown"},
{0x29, SvcWrap32<GetInfo32>, "GetInfo32"},
{0x2a, nullptr, "Unknown"},
{0x2b, nullptr, "Unknown"},
{0x2c, nullptr, "MapPhysicalMemory32"},
{0x2d, nullptr, "UnmapPhysicalMemory32"},
{0x2e, nullptr, "Unknown"},
{0x2f, nullptr, "Unknown"},
{0x30, nullptr, "Unknown"},
{0x31, nullptr, "Unknown"},
{0x32, nullptr, "SetThreadActivity32"},
{0x33, nullptr, "GetThreadContext32"},
{0x34, nullptr, "WaitForAddress32"},
{0x35, nullptr, "SignalToAddress32"},
{0x36, nullptr, "Unknown"},
{0x37, nullptr, "Unknown"},
{0x38, nullptr, "Unknown"},
{0x39, nullptr, "Unknown"},
{0x3a, nullptr, "Unknown"},
{0x3b, nullptr, "Unknown"},
{0x3c, nullptr, "Unknown"},
{0x3d, nullptr, "Unknown"},
{0x3e, nullptr, "Unknown"},
{0x3f, nullptr, "Unknown"},
{0x40, nullptr, "CreateSession32"},
{0x41, nullptr, "AcceptSession32"},
{0x42, nullptr, "Unknown"},
{0x43, nullptr, "ReplyAndReceive32"},
{0x44, nullptr, "Unknown"},
{0x45, nullptr, "CreateEvent32"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
{0x48, nullptr, "Unknown"},
{0x49, nullptr, "Unknown"},
{0x4a, nullptr, "Unknown"},
{0x4b, nullptr, "Unknown"},
{0x4c, nullptr, "Unknown"},
{0x4d, nullptr, "Unknown"},
{0x4e, nullptr, "Unknown"},
{0x4f, nullptr, "Unknown"},
{0x50, nullptr, "Unknown"},
{0x51, nullptr, "Unknown"},
{0x52, nullptr, "Unknown"},
{0x53, nullptr, "Unknown"},
{0x54, nullptr, "Unknown"},
{0x55, nullptr, "Unknown"},
{0x56, nullptr, "Unknown"},
{0x57, nullptr, "Unknown"},
{0x58, nullptr, "Unknown"},
{0x59, nullptr, "Unknown"},
{0x5a, nullptr, "Unknown"},
{0x5b, nullptr, "Unknown"},
{0x5c, nullptr, "Unknown"},
{0x5d, nullptr, "Unknown"},
{0x5e, nullptr, "Unknown"},
{0x5F, nullptr, "FlushProcessDataCache32"},
{0x60, nullptr, "Unknown"},
{0x61, nullptr, "Unknown"},
{0x62, nullptr, "Unknown"},
{0x63, nullptr, "Unknown"},
{0x64, nullptr, "Unknown"},
{0x65, nullptr, "GetProcessList32"},
{0x66, nullptr, "Unknown"},
{0x67, nullptr, "Unknown"},
{0x68, nullptr, "Unknown"},
{0x69, nullptr, "Unknown"},
{0x6A, nullptr, "Unknown"},
{0x6B, nullptr, "Unknown"},
{0x6C, nullptr, "Unknown"},
{0x6D, nullptr, "Unknown"},
{0x6E, nullptr, "Unknown"},
{0x6f, nullptr, "GetSystemInfo32"},
{0x70, nullptr, "CreatePort32"},
{0x71, nullptr, "ManageNamedPort32"},
{0x72, nullptr, "ConnectToPort32"},
{0x73, nullptr, "SetProcessMemoryPermission32"},
{0x74, nullptr, "Unknown"},
{0x75, nullptr, "Unknown"},
{0x76, nullptr, "Unknown"},
{0x77, nullptr, "MapProcessCodeMemory32"},
{0x78, nullptr, "UnmapProcessCodeMemory32"},
{0x79, nullptr, "Unknown"},
{0x7A, nullptr, "Unknown"},
{0x7B, nullptr, "TerminateProcess32"},
};
static const FunctionDef SVC_Table_64[] = {
{0x00, nullptr, "Unknown"},
{0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
{0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
{0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
{0x04, SvcWrap64<MapMemory>, "MapMemory"},
{0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"},
{0x06, SvcWrap64<QueryMemory>, "QueryMemory"},
{0x07, SvcWrap64<ExitProcess>, "ExitProcess"},
{0x08, SvcWrap64<CreateThread>, "CreateThread"},
{0x09, SvcWrap64<StartThread>, "StartThread"},
{0x0A, SvcWrap64<ExitThread>, "ExitThread"},
{0x0B, SvcWrap64<SleepThread>, "SleepThread"},
{0x0C, SvcWrap64<GetThreadPriority>, "GetThreadPriority"},
{0x0D, SvcWrap64<SetThreadPriority>, "SetThreadPriority"},
{0x0E, SvcWrap64<GetThreadCoreMask>, "GetThreadCoreMask"},
{0x0F, SvcWrap64<SetThreadCoreMask>, "SetThreadCoreMask"},
{0x10, SvcWrap64<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
{0x11, SvcWrap64<SignalEvent>, "SignalEvent"},
{0x12, SvcWrap64<ClearEvent>, "ClearEvent"},
{0x13, SvcWrap64<MapSharedMemory>, "MapSharedMemory"},
{0x14, SvcWrap64<UnmapSharedMemory>, "UnmapSharedMemory"},
{0x15, SvcWrap64<CreateTransferMemory>, "CreateTransferMemory"},
{0x16, SvcWrap64<CloseHandle>, "CloseHandle"},
{0x17, SvcWrap64<ResetSignal>, "ResetSignal"},
{0x18, SvcWrap64<WaitSynchronization>, "WaitSynchronization"},
{0x19, SvcWrap64<CancelSynchronization>, "CancelSynchronization"},
{0x1A, SvcWrap64<ArbitrateLock>, "ArbitrateLock"},
{0x1B, SvcWrap64<ArbitrateUnlock>, "ArbitrateUnlock"},
{0x1C, SvcWrap64<WaitProcessWideKeyAtomic>, "WaitProcessWideKeyAtomic"},
{0x1D, SvcWrap64<SignalProcessWideKey>, "SignalProcessWideKey"},
{0x1E, SvcWrap64<GetSystemTick>, "GetSystemTick"},
{0x1F, SvcWrap64<ConnectToNamedPort>, "ConnectToNamedPort"},
{0x20, nullptr, "SendSyncRequestLight"},
{0x21, SvcWrap<SendSyncRequest>, "SendSyncRequest"},
{0x21, SvcWrap64<SendSyncRequest>, "SendSyncRequest"},
{0x22, nullptr, "SendSyncRequestWithUserBuffer"},
{0x23, nullptr, "SendAsyncRequestWithUserBuffer"},
{0x24, SvcWrap<GetProcessId>, "GetProcessId"},
{0x25, SvcWrap<GetThreadId>, "GetThreadId"},
{0x26, SvcWrap<Break>, "Break"},
{0x27, SvcWrap<OutputDebugString>, "OutputDebugString"},
{0x24, SvcWrap64<GetProcessId>, "GetProcessId"},
{0x25, SvcWrap64<GetThreadId>, "GetThreadId"},
{0x26, SvcWrap64<Break>, "Break"},
{0x27, SvcWrap64<OutputDebugString>, "OutputDebugString"},
{0x28, nullptr, "ReturnFromException"},
{0x29, SvcWrap<GetInfo>, "GetInfo"},
{0x29, SvcWrap64<GetInfo>, "GetInfo"},
{0x2A, nullptr, "FlushEntireDataCache"},
{0x2B, nullptr, "FlushDataCache"},
{0x2C, SvcWrap<MapPhysicalMemory>, "MapPhysicalMemory"},
{0x2D, SvcWrap<UnmapPhysicalMemory>, "UnmapPhysicalMemory"},
{0x2C, SvcWrap64<MapPhysicalMemory>, "MapPhysicalMemory"},
{0x2D, SvcWrap64<UnmapPhysicalMemory>, "UnmapPhysicalMemory"},
{0x2E, nullptr, "GetFutureThreadInfo"},
{0x2F, nullptr, "GetLastThreadInfo"},
{0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
{0x31, SvcWrap<GetResourceLimitCurrentValue>, "GetResourceLimitCurrentValue"},
{0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"},
{0x33, SvcWrap<GetThreadContext>, "GetThreadContext"},
{0x34, SvcWrap<WaitForAddress>, "WaitForAddress"},
{0x35, SvcWrap<SignalToAddress>, "SignalToAddress"},
{0x30, SvcWrap64<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
{0x31, SvcWrap64<GetResourceLimitCurrentValue>, "GetResourceLimitCurrentValue"},
{0x32, SvcWrap64<SetThreadActivity>, "SetThreadActivity"},
{0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"},
{0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
{0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
{0x36, nullptr, "SynchronizePreemptionState"},
{0x37, nullptr, "Unknown"},
{0x38, nullptr, "Unknown"},
{0x39, nullptr, "Unknown"},
{0x3A, nullptr, "Unknown"},
{0x3B, nullptr, "Unknown"},
{0x3C, SvcWrap<KernelDebug>, "KernelDebug"},
{0x3D, SvcWrap<ChangeKernelTraceState>, "ChangeKernelTraceState"},
{0x3C, SvcWrap64<KernelDebug>, "KernelDebug"},
{0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
{0x3E, nullptr, "Unknown"},
{0x3F, nullptr, "Unknown"},
{0x40, nullptr, "CreateSession"},
@@ -2387,7 +2577,7 @@ static const FunctionDef SVC_Table[] = {
{0x42, nullptr, "ReplyAndReceiveLight"},
{0x43, nullptr, "ReplyAndReceive"},
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
{0x45, SvcWrap<CreateEvent>, "CreateEvent"},
{0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
@@ -2398,9 +2588,9 @@ static const FunctionDef SVC_Table[] = {
{0x4D, nullptr, "SleepSystem"},
{0x4E, nullptr, "ReadWriteRegister"},
{0x4F, nullptr, "SetProcessActivity"},
{0x50, SvcWrap<CreateSharedMemory>, "CreateSharedMemory"},
{0x51, SvcWrap<MapTransferMemory>, "MapTransferMemory"},
{0x52, SvcWrap<UnmapTransferMemory>, "UnmapTransferMemory"},
{0x50, SvcWrap64<CreateSharedMemory>, "CreateSharedMemory"},
{0x51, SvcWrap64<MapTransferMemory>, "MapTransferMemory"},
{0x52, SvcWrap64<UnmapTransferMemory>, "UnmapTransferMemory"},
{0x53, nullptr, "CreateInterruptEvent"},
{0x54, nullptr, "QueryPhysicalAddress"},
{0x55, nullptr, "QueryIoMapping"},
@@ -2419,8 +2609,8 @@ static const FunctionDef SVC_Table[] = {
{0x62, nullptr, "TerminateDebugProcess"},
{0x63, nullptr, "GetDebugEvent"},
{0x64, nullptr, "ContinueDebugEvent"},
{0x65, SvcWrap<GetProcessList>, "GetProcessList"},
{0x66, SvcWrap<GetThreadList>, "GetThreadList"},
{0x65, SvcWrap64<GetProcessList>, "GetProcessList"},
{0x66, SvcWrap64<GetThreadList>, "GetThreadList"},
{0x67, nullptr, "GetDebugThreadContext"},
{0x68, nullptr, "SetDebugThreadContext"},
{0x69, nullptr, "QueryDebugProcessMemory"},
@@ -2436,24 +2626,32 @@ static const FunctionDef SVC_Table[] = {
{0x73, nullptr, "SetProcessMemoryPermission"},
{0x74, nullptr, "MapProcessMemory"},
{0x75, nullptr, "UnmapProcessMemory"},
{0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"},
{0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"},
{0x78, SvcWrap<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
{0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
{0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
{0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
{0x79, nullptr, "CreateProcess"},
{0x7A, nullptr, "StartProcess"},
{0x7B, nullptr, "TerminateProcess"},
{0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
{0x7D, SvcWrap<CreateResourceLimit>, "CreateResourceLimit"},
{0x7E, SvcWrap<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"},
{0x7C, SvcWrap64<GetProcessInfo>, "GetProcessInfo"},
{0x7D, SvcWrap64<CreateResourceLimit>, "CreateResourceLimit"},
{0x7E, SvcWrap64<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"},
{0x7F, nullptr, "CallSecureMonitor"},
};
static const FunctionDef* GetSVCInfo(u32 func_num) {
if (func_num >= std::size(SVC_Table)) {
static const FunctionDef* GetSVCInfo32(u32 func_num) {
if (func_num >= std::size(SVC_Table_32)) {
LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num);
return nullptr;
}
return &SVC_Table[func_num];
return &SVC_Table_32[func_num];
}
static const FunctionDef* GetSVCInfo64(u32 func_num) {
if (func_num >= std::size(SVC_Table_64)) {
LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num);
return nullptr;
}
return &SVC_Table_64[func_num];
}
MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
@@ -2464,7 +2662,8 @@ void CallSVC(Core::System& system, u32 immediate) {
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard lock{HLE::g_hle_lock};
const FunctionDef* info = GetSVCInfo(immediate);
const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
: GetSVCInfo32(immediate);
if (info) {
if (info->func) {
info->func(system);
+116 -42
View File
@@ -15,6 +15,10 @@ static inline u64 Param(const Core::System& system, int n) {
return system.CurrentArmInterface().GetReg(n);
}
static inline u32 Param32(const Core::System& system, int n) {
return static_cast<u32>(system.CurrentArmInterface().GetReg(n));
}
/**
* HLE a function return from the current ARM userland process
* @param system System context
@@ -24,40 +28,44 @@ static inline void FuncReturn(Core::System& system, u64 result) {
system.CurrentArmInterface().SetReg(0, result);
}
static inline void FuncReturn32(Core::System& system, u32 result) {
system.CurrentArmInterface().SetReg(0, (u64)result);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type ResultCode
template <ResultCode func(Core::System&, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0)).raw);
}
template <ResultCode func(Core::System&, u64, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw);
}
template <ResultCode func(Core::System&, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
}
template <ResultCode func(Core::System&, u32, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(
system,
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
}
template <ResultCode func(Core::System&, u32, u64, u64, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
Param(system, 2), Param(system, 3))
.raw);
}
template <ResultCode func(Core::System&, u32*)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u32 param = 0;
const u32 retval = func(system, &param).raw;
system.CurrentArmInterface().SetReg(1, param);
@@ -65,7 +73,7 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u32*, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
system.CurrentArmInterface().SetReg(1, param_1);
@@ -73,7 +81,7 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u32*, u32*)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
const u32 retval = func(system, &param_1, &param_2).raw;
@@ -86,7 +94,7 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u32*, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1)).raw;
system.CurrentArmInterface().SetReg(1, param_1);
@@ -94,7 +102,7 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u32*, u64, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval =
func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw;
@@ -104,7 +112,7 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u64*, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
@@ -113,12 +121,12 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u64, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
}
template <ResultCode func(Core::System&, u64*, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1)).raw;
@@ -127,7 +135,7 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u64*, u32, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1)),
static_cast<u32>(Param(system, 2)))
@@ -138,19 +146,19 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u32, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
}
template <ResultCode func(Core::System&, u32, u32, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
static_cast<u32>(Param(system, 1)), Param(system, 2))
.raw);
}
template <ResultCode func(Core::System&, u32, u32*, u64*)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
u64 param_2 = 0;
const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
@@ -161,54 +169,54 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u64, u64, u32, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
.raw);
}
template <ResultCode func(Core::System&, u64, u64, u32, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), Param(system, 3))
.raw);
}
template <ResultCode func(Core::System&, u32, u64, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
static_cast<u32>(Param(system, 2)))
.raw);
}
template <ResultCode func(Core::System&, u64, u64, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw);
}
template <ResultCode func(Core::System&, u64, u64, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(
system,
func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
}
template <ResultCode func(Core::System&, u32, u64, u64, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
Param(system, 2), static_cast<u32>(Param(system, 3)))
.raw);
}
template <ResultCode func(Core::System&, u32, u64, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(
system,
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);
}
template <ResultCode func(Core::System&, u32*, u64, u64, s64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
static_cast<s64>(Param(system, 3)))
@@ -219,14 +227,14 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u64, u64, u32, s64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
.raw);
}
template <ResultCode func(Core::System&, u64*, u64, u64, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval =
func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw;
@@ -236,7 +244,7 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3),
static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5)))
@@ -247,7 +255,7 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u32*, u64, u64, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2),
static_cast<u32>(Param(system, 3)))
@@ -258,7 +266,7 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
static_cast<u32>(Param(system, 3)))
@@ -269,14 +277,14 @@ void SvcWrap(Core::System& system) {
}
template <ResultCode func(Core::System&, u64, u32, s32, s64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
.raw);
}
template <ResultCode func(Core::System&, u64, u32, s32, s32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
.raw);
@@ -286,7 +294,7 @@ void SvcWrap(Core::System& system) {
// Function wrappers that return type u32
template <u32 func(Core::System&)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system));
}
@@ -294,7 +302,7 @@ void SvcWrap(Core::System& system) {
// Function wrappers that return type u64
template <u64 func(Core::System&)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system));
}
@@ -302,44 +310,110 @@ void SvcWrap(Core::System& system) {
/// Function wrappers that return type void
template <void func(Core::System&)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
func(system);
}
template <void func(Core::System&, u32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)));
}
template <void func(Core::System&, u32, u64, u64, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2),
Param(system, 3));
}
template <void func(Core::System&, s64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
func(system, static_cast<s64>(Param(system, 0)));
}
template <void func(Core::System&, u64, s32)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
func(system, Param(system, 0), static_cast<s32>(Param(system, 1)));
}
template <void func(Core::System&, u64, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
func(system, Param(system, 0), Param(system, 1));
}
template <void func(Core::System&, u64, u64, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
func(system, Param(system, 0), Param(system, 1), Param(system, 2));
}
template <void func(Core::System&, u32, u64, u64)>
void SvcWrap(Core::System& system) {
void SvcWrap64(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2));
}
// Used by QueryMemory32
template <ResultCode func(Core::System&, u32, u32, u32)>
void SvcWrap32(Core::System& system) {
FuncReturn32(system,
func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw);
}
// Used by GetInfo32
template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)>
void SvcWrap32(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
const u32 retval = func(system, &param_1, &param_2, Param32(system, 0), Param32(system, 1),
Param32(system, 2), Param32(system, 3))
.raw;
system.CurrentArmInterface().SetReg(1, param_1);
system.CurrentArmInterface().SetReg(2, param_2);
FuncReturn(system, retval);
}
// Used by GetThreadPriority32, ConnectToNamedPort32
template <ResultCode func(Core::System&, u32*, u32)>
void SvcWrap32(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param32(system, 1)).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
// Used by GetThreadId32
template <ResultCode func(Core::System&, u32*, u32*, u32)>
void SvcWrap32(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
const u32 retval = func(system, &param_1, &param_2, Param32(system, 1)).raw;
system.CurrentArmInterface().SetReg(1, param_1);
system.CurrentArmInterface().SetReg(2, param_2);
FuncReturn(system, retval);
}
// Used by SignalProcessWideKey32
template <void func(Core::System&, u32, s32)>
void SvcWrap32(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1)));
}
// Used by SendSyncRequest32
template <ResultCode func(Core::System&, u32)>
void SvcWrap32(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
}
// Used by WaitSynchronization32
template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)>
void SvcWrap32(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2),
Param32(system, 3), &param_1)
.raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
} // namespace Kernel
+17 -14
View File
@@ -133,15 +133,16 @@ void Thread::CancelWait() {
ResumeFromWait();
}
/**
* Resets a thread context, making it ready to be scheduled and run by the CPU
* @param context Thread context to reset
* @param stack_top Address of the top of the stack
* @param entry_point Address of entry point for execution
* @param arg User argument for thread
*/
static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top,
VAddr entry_point, u64 arg) {
static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top,
u32 entry_point, u32 arg) {
context = {};
context.cpu_registers[0] = arg;
context.cpu_registers[15] = entry_point;
context.cpu_registers[13] = stack_top;
}
static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, VAddr stack_top,
VAddr entry_point, u64 arg) {
context = {};
context.cpu_registers[0] = arg;
context.pc = entry_point;
@@ -198,9 +199,9 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
thread->owner_process->RegisterThread(thread.get());
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
ResetThreadContext(thread->context, stack_top, entry_point, arg);
ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top),
static_cast<u32>(entry_point), static_cast<u32>(arg));
ResetThreadContext64(thread->context_64, stack_top, entry_point, arg);
return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
}
@@ -213,11 +214,13 @@ void Thread::SetPriority(u32 priority) {
}
void Thread::SetWaitSynchronizationResult(ResultCode result) {
context.cpu_registers[0] = result.raw;
context_32.cpu_registers[0] = result.raw;
context_64.cpu_registers[0] = result.raw;
}
void Thread::SetWaitSynchronizationOutput(s32 output) {
context.cpu_registers[1] = output;
context_32.cpu_registers[1] = output;
context_64.cpu_registers[1] = output;
}
s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const {
+16 -6
View File
@@ -102,7 +102,8 @@ public:
using MutexWaitingThreads = std::vector<std::shared_ptr<Thread>>;
using ThreadContext = Core::ARM_Interface::ThreadContext;
using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>;
@@ -273,12 +274,20 @@ public:
return status == ThreadStatus::WaitSynch;
}
ThreadContext& GetContext() {
return context;
ThreadContext32& GetContext32() {
return context_32;
}
const ThreadContext& GetContext() const {
return context;
const ThreadContext32& GetContext32() const {
return context_32;
}
ThreadContext64& GetContext64() {
return context_64;
}
const ThreadContext64& GetContext64() const {
return context_64;
}
ThreadStatus GetStatus() const {
@@ -466,7 +475,8 @@ private:
void AdjustSchedulingOnPriority(u32 old_priority);
void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core);
Core::ARM_Interface::ThreadContext context{};
ThreadContext32 context_32{};
ThreadContext64 context_64{};
u64 thread_id = 0;
+52 -5
View File
@@ -52,6 +52,11 @@ enum class LaunchParameterKind : u32 {
AccountPreselectedUser = 2,
};
enum class VrMode : u8 {
Disabled = 0,
Enabled = 1,
};
constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
struct LaunchParameterAccountPreselectedUser {
@@ -605,11 +610,11 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system,
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
{31, nullptr, "GetReaderLockAccessorEx"},
{40, nullptr, "GetCradleFwVersion"},
{50, nullptr, "IsVrModeEnabled"},
{51, nullptr, "SetVrModeEnabled"},
{52, nullptr, "SwitchLcdBacklight"},
{50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
{51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
{52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
{53, nullptr, "BeginVrModeEx"},
{54, nullptr, "EndVrModeEx"},
{54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"},
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
@@ -636,7 +641,6 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode
}
@@ -660,6 +664,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
return;
}
rb.Push(RESULT_SUCCESS);
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
}
@@ -672,6 +677,48 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
rb.Push(static_cast<u8>(FocusState::InFocus));
}
void ICommonStateGetter::IsVrModeEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(VrMode::Disabled);
}
void ICommonStateGetter::SetVrModeEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto is_vr_mode_enabled = rp.Pop<bool>();
LOG_WARNING(Service_AM, "(STUBBED) called. is_vr_mode_enabled={}", is_vr_mode_enabled);
IPC::ResponseBuilder rb{ctx, 2};
if (!is_vr_mode_enabled) {
rb.Push(RESULT_SUCCESS);
} else {
// TODO: Find better error code for this
UNIMPLEMENTED_MSG("is_vr_mode_enabled={}", is_vr_mode_enabled);
rb.Push(RESULT_UNKNOWN);
}
}
void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto is_lcd_backlight_off_enabled = rp.Pop<bool>();
LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}",
is_lcd_backlight_off_enabled);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ICommonStateGetter::EndVrModeEx(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
+4
View File
@@ -182,6 +182,10 @@ private:
void GetOperationMode(Kernel::HLERequestContext& ctx);
void GetPerformanceMode(Kernel::HLERequestContext& ctx);
void GetBootMode(Kernel::HLERequestContext& ctx);
void IsVrModeEnabled(Kernel::HLERequestContext& ctx);
void SetVrModeEnabled(Kernel::HLERequestContext& ctx);
void SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx);
void EndVrModeEx(Kernel::HLERequestContext& ctx);
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
void SetCpuBoostMode(Kernel::HLERequestContext& ctx);
@@ -254,6 +254,12 @@ void WebBrowser::Execute() {
if (status != RESULT_SUCCESS) {
complete = true;
// This is a workaround in order not to softlock yuzu when an error happens during the
// webapplet init. In order to avoid an svcBreak, the status is set to RESULT_SUCCESS
Finalize();
status = RESULT_SUCCESS;
return;
}
@@ -287,13 +287,13 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
Input::AnalogDirection::DOWN));
pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
pad_state.r_stick_right.Assign(
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
+8 -1
View File
@@ -235,7 +235,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{303, nullptr, "ActivateSevenSixAxisSensor"},
{304, nullptr, "StartSevenSixAxisSensor"},
{305, nullptr, "StopSevenSixAxisSensor"},
{306, nullptr, "InitializeSevenSixAxisSensor"},
{306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
{307, nullptr, "FinalizeSevenSixAxisSensor"},
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
@@ -853,6 +853,13 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
class HidDbg final : public ServiceFramework<HidDbg> {
public:
explicit HidDbg() : ServiceFramework{"hid:dbg"} {
+1
View File
@@ -128,6 +128,7 @@ private:
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
std::shared_ptr<IAppletResource> applet_resource;
Core::System& system;
+2 -2
View File
@@ -191,8 +191,6 @@ void NVFlinger::Compose() {
// Search for a queued buffer and acquire it
auto buffer = buffer_queue.AcquireBuffer();
MicroProfileFlip();
if (!buffer) {
continue;
}
@@ -206,6 +204,8 @@ void NVFlinger::Compose() {
gpu.WaitFence(fence.id, fence.value);
}
MicroProfileFlip();
// Now send the buffer to the GPU for drawing.
// TODO(Subv): Support more than just disp0. The display device selection is probably based
// on which display we're drawing (Default, Internal, External, etc)
+9 -1
View File
@@ -111,6 +111,14 @@ void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
rb.PushEnum(available_language_codes[Settings::values.language_index]);
}
void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SET, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(Settings::values.region_index);
}
SET::SET() : ServiceFramework("set") {
// clang-format off
static const FunctionInfo functions[] = {
@@ -118,7 +126,7 @@ SET::SET() : ServiceFramework("set") {
{1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"},
{2, &SET::MakeLanguageCode, "MakeLanguageCode"},
{3, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"},
{4, nullptr, "GetRegionCode"},
{4, &SET::GetRegionCode, "GetRegionCode"},
{5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"},
{6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
{7, nullptr, "GetKeyCodeMap"},
+1
View File
@@ -43,6 +43,7 @@ private:
void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx);
void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx);
void GetQuestFlag(Kernel::HLERequestContext& ctx);
void GetRegionCode(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Set
+1 -1
View File
@@ -44,7 +44,7 @@ void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u16>(0x500);
rb.Push<u16>(0x1000);
}
Controller::Controller() : ServiceFramework("IpcController") {
@@ -53,7 +53,7 @@ static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
return {};
}
std::vector<char> raw_data(binary_list->GetSize());
std::vector<char> raw_data(binary_list->GetSize() + 1);
binary_list->ReadBytes<char>(raw_data.data(), binary_list->GetSize());
std::stringstream data_stream{raw_data.data()};
@@ -129,12 +129,6 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
}
metadata.Print();
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit ||
arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
return {ResultStatus::Error32BitISA, {}};
}
if (process.LoadFromMetadata(metadata).IsError()) {
return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
}
+1 -1
View File
@@ -111,7 +111,7 @@ json GetProcessorStateDataAuto(Core::System& system) {
const auto& vm_manager{process->VMManager()};
auto& arm{system.CurrentArmInterface()};
Core::ARM_Interface::ThreadContext context{};
Core::ARM_Interface::ThreadContext64 context{};
arm.SaveContext(context);
return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32",
+2
View File
@@ -86,6 +86,7 @@ void LogSettings() {
LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
LogSetting("System_CurrentUser", Settings::values.current_user);
LogSetting("System_LanguageIndex", Settings::values.language_index);
LogSetting("System_RegionIndex", Settings::values.region_index);
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
@@ -94,6 +95,7 @@ void LogSettings() {
LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
LogSetting("Renderer_UseAsynchronousGpuEmulation",
Settings::values.use_asynchronous_gpu_emulation);
LogSetting("Renderer_UseVsync", Settings::values.use_vsync);
LogSetting("Audio_OutputEngine", Settings::values.sink_id);
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
+4
View File
@@ -387,6 +387,8 @@ struct Values {
s32 current_user;
s32 language_index;
s32 region_index;
s32 sound_index;
// Controls
std::array<PlayerInput, 10> players;
@@ -430,11 +432,13 @@ struct Values {
float resolution_factor;
int aspect_ratio;
int max_anisotropy;
bool use_frame_limit;
u16 frame_limit;
bool use_disk_shader_cache;
bool use_accurate_gpu_emulation;
bool use_asynchronous_gpu_emulation;
bool use_vsync;
bool force_30fps_mode;
float bg_red;
+1
View File
@@ -188,6 +188,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
Settings::values.use_accurate_gpu_emulation);
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
Settings::values.use_asynchronous_gpu_emulation);
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync);
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
}
+14
View File
@@ -34,6 +34,20 @@ public:
y * coef * (x == 0 ? 1.0f : SQRT_HALF));
}
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
switch (direction) {
case Input::AnalogDirection::RIGHT:
return right->GetStatus();
case Input::AnalogDirection::LEFT:
return left->GetStatus();
case Input::AnalogDirection::UP:
return up->GetStatus();
case Input::AnalogDirection::DOWN:
return down->GetStatus();
}
return false;
}
private:
Button up;
Button down;
+13 -4
View File
@@ -32,8 +32,16 @@ public:
SocketCallback callback)
: callback(std::move(callback)), timer(io_service),
socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id),
pad_index(pad_index),
send_endpoint(udp::endpoint(boost::asio::ip::make_address_v4(host), port)) {}
pad_index(pad_index) {
boost::system::error_code ec{};
auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
if (ec.value() != boost::system::errc::success) {
LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided to socket", host);
ipv4 = boost::asio::ip::address_v4{};
}
send_endpoint = {udp::endpoint(ipv4, port)};
}
void Stop() {
io_service.stop();
@@ -85,17 +93,18 @@ private:
}
void HandleSend(const boost::system::error_code& error) {
boost::system::error_code _ignored{};
// Send a request for getting port info for the pad
Request::PortInfo port_info{1, {pad_index, 0, 0, 0}};
const auto port_message = Request::Create(port_info, client_id);
std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE);
socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint);
socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored);
// Send a request for getting pad data for the pad
Request::PadData pad_data{Request::PadData::Flags::Id, pad_index, EMPTY_MAC_ADDRESS};
const auto pad_message = Request::Create(pad_data, client_id);
std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE);
socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint);
socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored);
StartSend(timer.expiry());
}
-1
View File
@@ -31,7 +31,6 @@ namespace Response {
*/
std::optional<Type> Validate(u8* data, std::size_t size) {
if (size < sizeof(Header)) {
LOG_DEBUG(Input, "Invalid UDP packet received");
return std::nullopt;
}
Header header{};
+2 -1
View File
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <mutex>
#include <optional>
#include <tuple>
#include "common/param_package.h"
@@ -44,7 +45,7 @@ public:
std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override {
{
std::lock_guard guard(status->update_mutex);
status->touch_calibration.emplace();
status->touch_calibration = DeviceStatus::CalibrationData{};
// These default values work well for DS4 but probably not other touch inputs
status->touch_calibration->min_x = params.Get("min_x", 100);
status->touch_calibration->min_y = params.Get("min_y", 50);
+10 -6
View File
@@ -2,6 +2,8 @@ add_library(video_core STATIC
buffer_cache/buffer_block.h
buffer_cache/buffer_cache.h
buffer_cache/map_interval.h
dirty_flags.cpp
dirty_flags.h
dma_pusher.cpp
dma_pusher.h
engines/const_buffer_engine_interface.h
@@ -63,14 +65,12 @@ add_library(video_core STATIC
renderer_opengl/gl_shader_decompiler.h
renderer_opengl/gl_shader_disk_cache.cpp
renderer_opengl/gl_shader_disk_cache.h
renderer_opengl/gl_shader_gen.cpp
renderer_opengl/gl_shader_gen.h
renderer_opengl/gl_shader_manager.cpp
renderer_opengl/gl_shader_manager.h
renderer_opengl/gl_shader_util.cpp
renderer_opengl/gl_shader_util.h
renderer_opengl/gl_state.cpp
renderer_opengl/gl_state.h
renderer_opengl/gl_state_tracker.cpp
renderer_opengl/gl_state_tracker.h
renderer_opengl/gl_stream_buffer.cpp
renderer_opengl/gl_stream_buffer.h
renderer_opengl/gl_texture_cache.cpp
@@ -116,8 +116,6 @@ add_library(video_core STATIC
shader/ast.h
shader/compiler_settings.cpp
shader/compiler_settings.h
shader/const_buffer_locker.cpp
shader/const_buffer_locker.h
shader/control_flow.cpp
shader/control_flow.h
shader/decode.cpp
@@ -126,9 +124,13 @@ add_library(video_core STATIC
shader/node_helper.cpp
shader/node_helper.h
shader/node.h
shader/registry.cpp
shader/registry.h
shader/shader_ir.cpp
shader/shader_ir.h
shader/track.cpp
shader/transform_feedback.cpp
shader/transform_feedback.h
surface.cpp
surface.h
texture_cache/format_lookup_table.cpp
@@ -198,6 +200,8 @@ if (ENABLE_VULKAN)
renderer_vulkan/vk_shader_util.h
renderer_vulkan/vk_staging_buffer_pool.cpp
renderer_vulkan/vk_staging_buffer_pool.h
renderer_vulkan/vk_state_tracker.cpp
renderer_vulkan/vk_state_tracker.h
renderer_vulkan/vk_stream_buffer.cpp
renderer_vulkan/vk_stream_buffer.h
renderer_vulkan/vk_swapchain.cpp
+38
View File
@@ -0,0 +1,38 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <cstddef>
#include "common/common_types.h"
#include "video_core/dirty_flags.h"
#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
#define NUM(field_name) (sizeof(::Tegra::Engines::Maxwell3D::Regs::field_name) / sizeof(u32))
namespace VideoCommon::Dirty {
using Tegra::Engines::Maxwell3D;
void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables) {
static constexpr std::size_t num_per_rt = NUM(rt[0]);
static constexpr std::size_t begin = OFF(rt);
static constexpr std::size_t num = num_per_rt * Maxwell3D::Regs::NumRenderTargets;
for (std::size_t rt = 0; rt < Maxwell3D::Regs::NumRenderTargets; ++rt) {
FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt);
}
FillBlock(tables[1], begin, num, RenderTargets);
static constexpr std::array zeta_flags{ZetaBuffer, RenderTargets};
for (std::size_t i = 0; i < std::size(zeta_flags); ++i) {
const u8 flag = zeta_flags[i];
auto& table = tables[i];
table[OFF(zeta_enable)] = flag;
table[OFF(zeta_width)] = flag;
table[OFF(zeta_height)] = flag;
FillBlock(table, OFF(zeta), NUM(zeta), flag);
}
}
} // namespace VideoCommon::Dirty
+49
View File
@@ -0,0 +1,49 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <algorithm>
#include <cstddef>
#include <iterator>
#include "common/common_types.h"
#include "video_core/engines/maxwell_3d.h"
namespace VideoCommon::Dirty {
enum : u8 {
NullEntry = 0,
RenderTargets,
ColorBuffer0,
ColorBuffer1,
ColorBuffer2,
ColorBuffer3,
ColorBuffer4,
ColorBuffer5,
ColorBuffer6,
ColorBuffer7,
ZetaBuffer,
LastCommonEntry,
};
template <typename Integer>
void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Table& table, std::size_t begin,
std::size_t num, Integer dirty_index) {
const auto it = std::begin(table) + begin;
std::fill(it, it + num, static_cast<u8>(dirty_index));
}
template <typename Integer1, typename Integer2>
void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables, std::size_t begin,
std::size_t num, Integer1 index_a, Integer2 index_b) {
FillBlock(tables[0], begin, num, index_a);
FillBlock(tables[1], begin, num, index_b);
}
void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables);
} // namespace VideoCommon::Dirty
+1 -1
View File
@@ -22,7 +22,7 @@ void DmaPusher::DispatchCalls() {
MICROPROFILE_SCOPE(DispatchCalls);
// On entering GPU code, assume all memory may be touched by the ARM core.
gpu.Maxwell3D().dirty.OnMemoryWrite();
gpu.Maxwell3D().OnMemoryWrite();
dma_pushbuffer_subindex = 0;
@@ -16,11 +16,16 @@ namespace Tegra::Engines {
struct SamplerDescriptor {
union {
BitField<0, 20, Tegra::Shader::TextureType> texture_type;
BitField<20, 1, u32> is_array;
BitField<21, 1, u32> is_buffer;
BitField<22, 1, u32> is_shadow;
u32 raw{};
u32 raw = 0;
BitField<0, 2, Tegra::Shader::TextureType> texture_type;
BitField<2, 3, Tegra::Texture::ComponentType> r_type;
BitField<5, 1, u32> is_array;
BitField<6, 1, u32> is_buffer;
BitField<7, 1, u32> is_shadow;
BitField<8, 3, Tegra::Texture::ComponentType> g_type;
BitField<11, 3, Tegra::Texture::ComponentType> b_type;
BitField<14, 3, Tegra::Texture::ComponentType> a_type;
BitField<17, 7, Tegra::Texture::TextureFormat> format;
};
bool operator==(const SamplerDescriptor& rhs) const noexcept {
@@ -31,68 +36,50 @@ struct SamplerDescriptor {
return !operator==(rhs);
}
static SamplerDescriptor FromTicTexture(Tegra::Texture::TextureType tic_texture_type) {
static SamplerDescriptor FromTIC(const Tegra::Texture::TICEntry& tic) {
using Tegra::Shader::TextureType;
SamplerDescriptor result;
switch (tic_texture_type) {
result.format.Assign(tic.format.Value());
result.r_type.Assign(tic.r_type.Value());
result.g_type.Assign(tic.g_type.Value());
result.b_type.Assign(tic.b_type.Value());
result.a_type.Assign(tic.a_type.Value());
switch (tic.texture_type.Value()) {
case Tegra::Texture::TextureType::Texture1D:
result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D);
result.is_array.Assign(0);
result.is_buffer.Assign(0);
result.is_shadow.Assign(0);
result.texture_type.Assign(TextureType::Texture1D);
return result;
case Tegra::Texture::TextureType::Texture2D:
result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
result.is_array.Assign(0);
result.is_buffer.Assign(0);
result.is_shadow.Assign(0);
result.texture_type.Assign(TextureType::Texture2D);
return result;
case Tegra::Texture::TextureType::Texture3D:
result.texture_type.Assign(Tegra::Shader::TextureType::Texture3D);
result.is_array.Assign(0);
result.is_buffer.Assign(0);
result.is_shadow.Assign(0);
result.texture_type.Assign(TextureType::Texture3D);
return result;
case Tegra::Texture::TextureType::TextureCubemap:
result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube);
result.is_array.Assign(0);
result.is_buffer.Assign(0);
result.is_shadow.Assign(0);
result.texture_type.Assign(TextureType::TextureCube);
return result;
case Tegra::Texture::TextureType::Texture1DArray:
result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D);
result.texture_type.Assign(TextureType::Texture1D);
result.is_array.Assign(1);
result.is_buffer.Assign(0);
result.is_shadow.Assign(0);
return result;
case Tegra::Texture::TextureType::Texture2DArray:
result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
result.texture_type.Assign(TextureType::Texture2D);
result.is_array.Assign(1);
result.is_buffer.Assign(0);
result.is_shadow.Assign(0);
return result;
case Tegra::Texture::TextureType::Texture1DBuffer:
result.texture_type.Assign(Tegra::Shader::TextureType::Texture1D);
result.is_array.Assign(0);
result.texture_type.Assign(TextureType::Texture1D);
result.is_buffer.Assign(1);
result.is_shadow.Assign(0);
return result;
case Tegra::Texture::TextureType::Texture2DNoMipmap:
result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
result.is_array.Assign(0);
result.is_buffer.Assign(0);
result.is_shadow.Assign(0);
result.texture_type.Assign(TextureType::Texture2D);
return result;
case Tegra::Texture::TextureType::TextureCubeArray:
result.texture_type.Assign(Tegra::Shader::TextureType::TextureCube);
result.texture_type.Assign(TextureType::TextureCube);
result.is_array.Assign(1);
result.is_buffer.Assign(0);
result.is_shadow.Assign(0);
return result;
default:
result.texture_type.Assign(Tegra::Shader::TextureType::Texture2D);
result.is_array.Assign(0);
result.is_buffer.Assign(0);
result.is_shadow.Assign(0);
result.texture_type.Assign(TextureType::Texture2D);
return result;
}
}
+2 -10
View File
@@ -39,7 +39,7 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
const bool is_last_call = method_call.IsLastCall();
upload_state.ProcessData(method_call.argument, is_last_call);
if (is_last_call) {
system.GPU().Maxwell3D().dirty.OnMemoryWrite();
system.GPU().Maxwell3D().OnMemoryWrite();
}
break;
}
@@ -89,7 +89,7 @@ SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 con
const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
SamplerDescriptor result = SamplerDescriptor::FromTicTexture(tex_info.tic.texture_type.Value());
SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic);
result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
return result;
}
@@ -119,14 +119,6 @@ Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const {
Texture::TICEntry tic_entry;
memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
const auto r_type{tic_entry.r_type.Value()};
const auto g_type{tic_entry.g_type.Value()};
const auto b_type{tic_entry.b_type.Value()};
const auto a_type{tic_entry.a_type.Value()};
// TODO(Subv): Different data types for separate components are not supported
DEBUG_ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
return tic_entry;
}
+1 -1
View File
@@ -34,7 +34,7 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) {
const bool is_last_call = method_call.IsLastCall();
upload_state.ProcessData(method_call.argument, is_last_call);
if (is_last_call) {
system.GPU().Maxwell3D().dirty.OnMemoryWrite();
system.GPU().Maxwell3D().OnMemoryWrite();
}
break;
}
+31 -183
View File
@@ -26,7 +26,8 @@ Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& raste
MemoryManager& memory_manager)
: system{system}, rasterizer{rasterizer}, memory_manager{memory_manager},
macro_interpreter{*this}, upload_state{memory_manager, regs.upload} {
InitDirtySettings();
dirty.flags.flip();
InitializeRegisterDefaults();
}
@@ -75,8 +76,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
regs.stencil_back_mask = 0xFFFFFFFF;
regs.depth_test_func = Regs::ComparisonOp::Always;
regs.cull.front_face = Regs::Cull::FrontFace::CounterClockWise;
regs.cull.cull_face = Regs::Cull::CullFace::Back;
regs.front_face = Regs::FrontFace::CounterClockWise;
regs.cull_face = Regs::CullFace::Back;
// TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
// register carrying a default value. Assume it's OpenGL's default (1).
@@ -95,7 +96,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
regs.rasterize_enable = 1;
regs.rt_separate_frag_data = 1;
regs.framebuffer_srgb = 1;
regs.cull.front_face = Maxwell3D::Regs::Cull::FrontFace::ClockWise;
regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise;
shadow_state = regs;
mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true;
mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true;
@@ -103,164 +106,6 @@ void Maxwell3D::InitializeRegisterDefaults() {
mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true;
}
#define DIRTY_REGS_POS(field_name) static_cast<u8>(offsetof(Maxwell3D::DirtyRegs, field_name))
void Maxwell3D::InitDirtySettings() {
const auto set_block = [this](std::size_t start, std::size_t range, u8 position) {
const auto start_itr = dirty_pointers.begin() + start;
const auto end_itr = start_itr + range;
std::fill(start_itr, end_itr, position);
};
dirty.regs.fill(true);
// Init Render Targets
constexpr u32 registers_per_rt = sizeof(regs.rt[0]) / sizeof(u32);
constexpr u32 rt_start_reg = MAXWELL3D_REG_INDEX(rt);
constexpr u32 rt_end_reg = rt_start_reg + registers_per_rt * 8;
u8 rt_dirty_reg = DIRTY_REGS_POS(render_target);
for (u32 rt_reg = rt_start_reg; rt_reg < rt_end_reg; rt_reg += registers_per_rt) {
set_block(rt_reg, registers_per_rt, rt_dirty_reg);
++rt_dirty_reg;
}
constexpr u32 depth_buffer_flag = DIRTY_REGS_POS(depth_buffer);
dirty_pointers[MAXWELL3D_REG_INDEX(zeta_enable)] = depth_buffer_flag;
dirty_pointers[MAXWELL3D_REG_INDEX(zeta_width)] = depth_buffer_flag;
dirty_pointers[MAXWELL3D_REG_INDEX(zeta_height)] = depth_buffer_flag;
constexpr u32 registers_in_zeta = sizeof(regs.zeta) / sizeof(u32);
constexpr u32 zeta_reg = MAXWELL3D_REG_INDEX(zeta);
set_block(zeta_reg, registers_in_zeta, depth_buffer_flag);
// Init Vertex Arrays
constexpr u32 vertex_array_start = MAXWELL3D_REG_INDEX(vertex_array);
constexpr u32 vertex_array_size = sizeof(regs.vertex_array[0]) / sizeof(u32);
constexpr u32 vertex_array_end = vertex_array_start + vertex_array_size * Regs::NumVertexArrays;
u8 va_dirty_reg = DIRTY_REGS_POS(vertex_array);
u8 vi_dirty_reg = DIRTY_REGS_POS(vertex_instance);
for (u32 vertex_reg = vertex_array_start; vertex_reg < vertex_array_end;
vertex_reg += vertex_array_size) {
set_block(vertex_reg, 3, va_dirty_reg);
// The divisor concerns vertex array instances
dirty_pointers[static_cast<std::size_t>(vertex_reg) + 3] = vi_dirty_reg;
++va_dirty_reg;
++vi_dirty_reg;
}
constexpr u32 vertex_limit_start = MAXWELL3D_REG_INDEX(vertex_array_limit);
constexpr u32 vertex_limit_size = sizeof(regs.vertex_array_limit[0]) / sizeof(u32);
constexpr u32 vertex_limit_end = vertex_limit_start + vertex_limit_size * Regs::NumVertexArrays;
va_dirty_reg = DIRTY_REGS_POS(vertex_array);
for (u32 vertex_reg = vertex_limit_start; vertex_reg < vertex_limit_end;
vertex_reg += vertex_limit_size) {
set_block(vertex_reg, vertex_limit_size, va_dirty_reg);
va_dirty_reg++;
}
constexpr u32 vertex_instance_start = MAXWELL3D_REG_INDEX(instanced_arrays);
constexpr u32 vertex_instance_size =
sizeof(regs.instanced_arrays.is_instanced[0]) / sizeof(u32);
constexpr u32 vertex_instance_end =
vertex_instance_start + vertex_instance_size * Regs::NumVertexArrays;
vi_dirty_reg = DIRTY_REGS_POS(vertex_instance);
for (u32 vertex_reg = vertex_instance_start; vertex_reg < vertex_instance_end;
vertex_reg += vertex_instance_size) {
set_block(vertex_reg, vertex_instance_size, vi_dirty_reg);
vi_dirty_reg++;
}
set_block(MAXWELL3D_REG_INDEX(vertex_attrib_format), regs.vertex_attrib_format.size(),
DIRTY_REGS_POS(vertex_attrib_format));
// Init Shaders
constexpr u32 shader_registers_count =
sizeof(regs.shader_config[0]) * Regs::MaxShaderProgram / sizeof(u32);
set_block(MAXWELL3D_REG_INDEX(shader_config[0]), shader_registers_count,
DIRTY_REGS_POS(shaders));
// State
// Viewport
constexpr u8 viewport_dirty_reg = DIRTY_REGS_POS(viewport);
constexpr u32 viewport_start = MAXWELL3D_REG_INDEX(viewports);
constexpr u32 viewport_size = sizeof(regs.viewports) / sizeof(u32);
set_block(viewport_start, viewport_size, viewport_dirty_reg);
constexpr u32 view_volume_start = MAXWELL3D_REG_INDEX(view_volume_clip_control);
constexpr u32 view_volume_size = sizeof(regs.view_volume_clip_control) / sizeof(u32);
set_block(view_volume_start, view_volume_size, viewport_dirty_reg);
// Viewport transformation
constexpr u32 viewport_trans_start = MAXWELL3D_REG_INDEX(viewport_transform);
constexpr u32 viewport_trans_size = sizeof(regs.viewport_transform) / sizeof(u32);
set_block(viewport_trans_start, viewport_trans_size, DIRTY_REGS_POS(viewport_transform));
// Cullmode
constexpr u32 cull_mode_start = MAXWELL3D_REG_INDEX(cull);
constexpr u32 cull_mode_size = sizeof(regs.cull) / sizeof(u32);
set_block(cull_mode_start, cull_mode_size, DIRTY_REGS_POS(cull_mode));
// Screen y control
dirty_pointers[MAXWELL3D_REG_INDEX(screen_y_control)] = DIRTY_REGS_POS(screen_y_control);
// Primitive Restart
constexpr u32 primitive_restart_start = MAXWELL3D_REG_INDEX(primitive_restart);
constexpr u32 primitive_restart_size = sizeof(regs.primitive_restart) / sizeof(u32);
set_block(primitive_restart_start, primitive_restart_size, DIRTY_REGS_POS(primitive_restart));
// Depth Test
constexpr u8 depth_test_dirty_reg = DIRTY_REGS_POS(depth_test);
dirty_pointers[MAXWELL3D_REG_INDEX(depth_test_enable)] = depth_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(depth_write_enabled)] = depth_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(depth_test_func)] = depth_test_dirty_reg;
// Stencil Test
constexpr u32 stencil_test_dirty_reg = DIRTY_REGS_POS(stencil_test);
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_enable)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_func)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_ref)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_mask)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_fail)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_zfail)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_zpass)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_mask)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_two_side_enable)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_func)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_ref)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_mask)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_fail)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_zfail)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_zpass)] = stencil_test_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_mask)] = stencil_test_dirty_reg;
// Color Mask
constexpr u8 color_mask_dirty_reg = DIRTY_REGS_POS(color_mask);
dirty_pointers[MAXWELL3D_REG_INDEX(color_mask_common)] = color_mask_dirty_reg;
set_block(MAXWELL3D_REG_INDEX(color_mask), sizeof(regs.color_mask) / sizeof(u32),
color_mask_dirty_reg);
// Blend State
constexpr u8 blend_state_dirty_reg = DIRTY_REGS_POS(blend_state);
set_block(MAXWELL3D_REG_INDEX(blend_color), sizeof(regs.blend_color) / sizeof(u32),
blend_state_dirty_reg);
dirty_pointers[MAXWELL3D_REG_INDEX(independent_blend_enable)] = blend_state_dirty_reg;
set_block(MAXWELL3D_REG_INDEX(blend), sizeof(regs.blend) / sizeof(u32), blend_state_dirty_reg);
set_block(MAXWELL3D_REG_INDEX(independent_blend), sizeof(regs.independent_blend) / sizeof(u32),
blend_state_dirty_reg);
// Scissor State
constexpr u8 scissor_test_dirty_reg = DIRTY_REGS_POS(scissor_test);
set_block(MAXWELL3D_REG_INDEX(scissor_test), sizeof(regs.scissor_test) / sizeof(u32),
scissor_test_dirty_reg);
// Polygon Offset
constexpr u8 polygon_offset_dirty_reg = DIRTY_REGS_POS(polygon_offset);
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_fill_enable)] = polygon_offset_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_line_enable)] = polygon_offset_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_point_enable)] = polygon_offset_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_units)] = polygon_offset_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_factor)] = polygon_offset_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_clamp)] = polygon_offset_dirty_reg;
// Depth bounds
constexpr u8 depth_bounds_values_dirty_reg = DIRTY_REGS_POS(depth_bounds_values);
dirty_pointers[MAXWELL3D_REG_INDEX(depth_bounds[0])] = depth_bounds_values_dirty_reg;
dirty_pointers[MAXWELL3D_REG_INDEX(depth_bounds[1])] = depth_bounds_values_dirty_reg;
}
void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters) {
// Reset the current macro.
executing_macro = 0;
@@ -317,31 +162,34 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
ASSERT_MSG(method < Regs::NUM_REGS,
"Invalid Maxwell3D register, increase the size of the Regs structure");
if (regs.reg_array[method] != method_call.argument) {
regs.reg_array[method] = method_call.argument;
const std::size_t dirty_reg = dirty_pointers[method];
if (dirty_reg) {
dirty.regs[dirty_reg] = true;
if (dirty_reg >= DIRTY_REGS_POS(vertex_array) &&
dirty_reg < DIRTY_REGS_POS(vertex_array_buffers)) {
dirty.vertex_array_buffers = true;
} else if (dirty_reg >= DIRTY_REGS_POS(vertex_instance) &&
dirty_reg < DIRTY_REGS_POS(vertex_instances)) {
dirty.vertex_instances = true;
} else if (dirty_reg >= DIRTY_REGS_POS(render_target) &&
dirty_reg < DIRTY_REGS_POS(render_settings)) {
dirty.render_settings = true;
}
u32 arg = method_call.argument;
// Keep track of the register value in shadow_state when requested.
if (shadow_state.shadow_ram_control == Regs::ShadowRamControl::Track ||
shadow_state.shadow_ram_control == Regs::ShadowRamControl::TrackWithFilter) {
shadow_state.reg_array[method] = arg;
} else if (shadow_state.shadow_ram_control == Regs::ShadowRamControl::Replay) {
arg = shadow_state.reg_array[method];
}
if (regs.reg_array[method] != arg) {
regs.reg_array[method] = arg;
for (const auto& table : dirty.tables) {
dirty.flags[table[method]] = true;
}
}
switch (method) {
case MAXWELL3D_REG_INDEX(shadow_ram_control): {
shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(method_call.argument);
break;
}
case MAXWELL3D_REG_INDEX(macros.data): {
ProcessMacroUpload(method_call.argument);
ProcessMacroUpload(arg);
break;
}
case MAXWELL3D_REG_INDEX(macros.bind): {
ProcessMacroBind(method_call.argument);
ProcessMacroBind(arg);
break;
}
case MAXWELL3D_REG_INDEX(firmware[4]): {
@@ -417,9 +265,9 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
}
case MAXWELL3D_REG_INDEX(data_upload): {
const bool is_last_call = method_call.IsLastCall();
upload_state.ProcessData(method_call.argument, is_last_call);
upload_state.ProcessData(arg, is_last_call);
if (is_last_call) {
dirty.OnMemoryWrite();
OnMemoryWrite();
}
break;
}
@@ -727,7 +575,7 @@ void Maxwell3D::FinishCBData() {
const u32 id = cb_data_state.id;
memory_manager.WriteBlock(address, cb_data_state.buffer[id].data(), size);
dirty.OnMemoryWrite();
OnMemoryWrite();
cb_data_state.id = null_cb_data;
cb_data_state.current = null_cb_data;
@@ -805,7 +653,7 @@ SamplerDescriptor Maxwell3D::AccessBindlessSampler(ShaderType stage, u64 const_b
const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(tex_info_address)};
const Texture::FullTextureInfo tex_info = GetTextureInfo(tex_handle);
SamplerDescriptor result = SamplerDescriptor::FromTicTexture(tex_info.tic.texture_type.Value());
SamplerDescriptor result = SamplerDescriptor::FromTIC(tex_info.tic);
result.is_shadow.Assign(tex_info.tsc.depth_compare_enabled.Value());
return result;
}
+131 -118
View File
@@ -6,6 +6,7 @@
#include <array>
#include <bitset>
#include <limits>
#include <optional>
#include <type_traits>
#include <unordered_map>
@@ -66,6 +67,7 @@ public:
static constexpr std::size_t NumVaryings = 31;
static constexpr std::size_t NumImages = 8; // TODO(Rodrigo): Investigate this number
static constexpr std::size_t NumClipDistances = 8;
static constexpr std::size_t NumTransformFeedbackBuffers = 4;
static constexpr std::size_t MaxShaderProgram = 6;
static constexpr std::size_t MaxShaderStage = 5;
// Maximum number of const buffers per shader stage.
@@ -431,21 +433,15 @@ public:
GeneratedPrimitives = 0x1F,
};
struct Cull {
enum class FrontFace : u32 {
ClockWise = 0x0900,
CounterClockWise = 0x0901,
};
enum class FrontFace : u32 {
ClockWise = 0x0900,
CounterClockWise = 0x0901,
};
enum class CullFace : u32 {
Front = 0x0404,
Back = 0x0405,
FrontAndBack = 0x0408,
};
u32 enabled;
FrontFace front_face;
CullFace cull_face;
enum class CullFace : u32 {
Front = 0x0404,
Back = 0x0405,
FrontAndBack = 0x0408,
};
struct Blend {
@@ -529,6 +525,23 @@ public:
FractionalEven = 2,
};
enum class PolygonMode : u32 {
Point = 0x1b00,
Line = 0x1b01,
Fill = 0x1b02,
};
enum class ShadowRamControl : u32 {
// write value to shadow ram
Track = 0,
// write value to shadow ram ( with validation ??? )
TrackWithFilter = 1,
// only write to real hw register
Passthrough = 2,
// write value from shadow ram to real hw register
Replay = 3,
};
struct RenderTargetConfig {
u32 address_high;
u32 address_low;
@@ -542,7 +555,7 @@ public:
BitField<12, 1, InvMemoryLayout> type;
} memory_layout;
union {
BitField<0, 16, u32> array_mode;
BitField<0, 16, u32> layers;
BitField<16, 1, u32> volume;
};
u32 layer_stride;
@@ -574,7 +587,7 @@ public:
f32 translate_z;
INSERT_UNION_PADDING_WORDS(2);
Common::Rectangle<s32> GetRect() const {
Common::Rectangle<f32> GetRect() const {
return {
GetX(), // left
GetY() + GetHeight(), // top
@@ -583,20 +596,20 @@ public:
};
};
s32 GetX() const {
return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
f32 GetX() const {
return std::max(0.0f, translate_x - std::fabs(scale_x));
}
s32 GetY() const {
return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
f32 GetY() const {
return std::max(0.0f, translate_y - std::fabs(scale_y));
}
s32 GetWidth() const {
return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
f32 GetWidth() const {
return translate_x + std::fabs(scale_x) - GetX();
}
s32 GetHeight() const {
return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
f32 GetHeight() const {
return translate_y + std::fabs(scale_y) - GetY();
}
};
@@ -626,6 +639,29 @@ public:
float depth_range_far;
};
struct TransformFeedbackBinding {
u32 buffer_enable;
u32 address_high;
u32 address_low;
s32 buffer_size;
s32 buffer_offset;
INSERT_UNION_PADDING_WORDS(3);
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
};
static_assert(sizeof(TransformFeedbackBinding) == 32);
struct TransformFeedbackLayout {
u32 stream;
u32 varying_count;
u32 stride;
INSERT_UNION_PADDING_WORDS(1);
};
static_assert(sizeof(TransformFeedbackLayout) == 16);
bool IsShaderConfigEnabled(std::size_t index) const {
// The VertexB is always enabled.
if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
@@ -634,6 +670,10 @@ public:
return shader_config[index].enable != 0;
}
bool IsShaderConfigEnabled(Regs::ShaderProgram type) const {
return IsShaderConfigEnabled(static_cast<std::size_t>(type));
}
union {
struct {
INSERT_UNION_PADDING_WORDS(0x45);
@@ -645,7 +685,9 @@ public:
u32 bind;
} macros;
INSERT_UNION_PADDING_WORDS(0x17);
ShadowRamControl shadow_ram_control;
INSERT_UNION_PADDING_WORDS(0x16);
Upload::Registers upload;
struct {
@@ -682,7 +724,13 @@ public:
u32 rasterize_enable;
INSERT_UNION_PADDING_WORDS(0xF1);
std::array<TransformFeedbackBinding, NumTransformFeedbackBuffers> tfb_bindings;
INSERT_UNION_PADDING_WORDS(0xC0);
std::array<TransformFeedbackLayout, NumTransformFeedbackBuffers> tfb_layouts;
INSERT_UNION_PADDING_WORDS(0x1);
u32 tfb_enabled;
@@ -710,7 +758,12 @@ public:
s32 clear_stencil;
INSERT_UNION_PADDING_WORDS(0x7);
INSERT_UNION_PADDING_WORDS(0x2);
PolygonMode polygon_mode_front;
PolygonMode polygon_mode_back;
INSERT_UNION_PADDING_WORDS(0x3);
u32 polygon_offset_point_enable;
u32 polygon_offset_line_enable;
@@ -769,7 +822,11 @@ public:
BitField<12, 4, u32> viewport;
} clear_flags;
INSERT_UNION_PADDING_WORDS(0x19);
INSERT_UNION_PADDING_WORDS(0x10);
u32 fill_rectangle;
INSERT_UNION_PADDING_WORDS(0x8);
std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format;
@@ -800,8 +857,12 @@ public:
u32 zeta_width;
u32 zeta_height;
union {
BitField<0, 16, u32> zeta_layers;
BitField<16, 1, u32> zeta_volume;
};
INSERT_UNION_PADDING_WORDS(0x27);
INSERT_UNION_PADDING_WORDS(0x26);
u32 depth_test_enable;
@@ -868,16 +929,7 @@ public:
INSERT_UNION_PADDING_WORDS(0x35);
union {
BitField<0, 1, u32> c0;
BitField<1, 1, u32> c1;
BitField<2, 1, u32> c2;
BitField<3, 1, u32> c3;
BitField<4, 1, u32> c4;
BitField<5, 1, u32> c5;
BitField<6, 1, u32> c6;
BitField<7, 1, u32> c7;
} clip_distance_enabled;
u32 clip_distance_enabled;
u32 samplecnt_enable;
@@ -1056,7 +1108,9 @@ public:
INSERT_UNION_PADDING_WORDS(1);
Cull cull;
u32 cull_test_enabled;
FrontFace front_face;
CullFace cull_face;
u32 pixel_center_integer;
@@ -1195,7 +1249,11 @@ public:
u32 tex_cb_index;
INSERT_UNION_PADDING_WORDS(0x395);
INSERT_UNION_PADDING_WORDS(0x7D);
std::array<std::array<u8, 128>, NumTransformFeedbackBuffers> tfb_varying_locs;
INSERT_UNION_PADDING_WORDS(0x298);
struct {
/// Compressed address of a buffer that holds information about bound SSBOs.
@@ -1218,7 +1276,12 @@ public:
};
std::array<u32, NUM_REGS> reg_array;
};
} regs{};
};
Regs regs{};
/// Store temporary hw register values, used by some calls to restore state after a operation
Regs shadow_state;
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable");
@@ -1234,79 +1297,6 @@ public:
State state{};
struct DirtyRegs {
static constexpr std::size_t NUM_REGS = 256;
static_assert(NUM_REGS - 1 <= std::numeric_limits<u8>::max());
union {
struct {
bool null_dirty;
// Vertex Attributes
bool vertex_attrib_format;
// Vertex Arrays
std::array<bool, 32> vertex_array;
bool vertex_array_buffers;
// Vertex Instances
std::array<bool, 32> vertex_instance;
bool vertex_instances;
// Render Targets
std::array<bool, 8> render_target;
bool depth_buffer;
bool render_settings;
// Shaders
bool shaders;
// Rasterizer State
bool viewport;
bool clip_coefficient;
bool cull_mode;
bool primitive_restart;
bool depth_test;
bool stencil_test;
bool blend_state;
bool scissor_test;
bool transform_feedback;
bool color_mask;
bool polygon_offset;
bool depth_bounds_values;
// Complementary
bool viewport_transform;
bool screen_y_control;
bool memory_general;
};
std::array<bool, NUM_REGS> regs;
};
void ResetVertexArrays() {
vertex_array.fill(true);
vertex_array_buffers = true;
}
void ResetRenderTargets() {
depth_buffer = true;
render_target.fill(true);
render_settings = true;
}
void OnMemoryWrite() {
shaders = true;
memory_general = true;
ResetRenderTargets();
ResetVertexArrays();
}
} dirty{};
/// Reads a register value located at the input method address
u32 GetRegisterValue(u32 method) const;
@@ -1352,6 +1342,11 @@ public:
return execute_on;
}
/// Notify a memory write has happened.
void OnMemoryWrite() {
dirty.flags |= dirty.on_write_stores;
}
enum class MMEDrawMode : u32 {
Undefined,
Array,
@@ -1367,6 +1362,16 @@ public:
u32 gl_end_count{};
} mme_draw;
struct DirtyState {
using Flags = std::bitset<std::numeric_limits<u8>::max()>;
using Table = std::array<u8, Regs::NUM_REGS>;
using Tables = std::array<Table, 2>;
Flags flags;
Flags on_write_stores;
Tables tables{};
} dirty;
private:
void InitializeRegisterDefaults();
@@ -1413,8 +1418,6 @@ private:
/// Retrieves information about a specific TSC entry from the TSC buffer.
Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
void InitDirtySettings();
/**
* Call a macro on this engine.
* @param method Method to call
@@ -1473,6 +1476,7 @@ private:
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(macros, 0x45);
ASSERT_REG_POSITION(shadow_ram_control, 0x49);
ASSERT_REG_POSITION(upload, 0x60);
ASSERT_REG_POSITION(exec_upload, 0x6C);
ASSERT_REG_POSITION(data_upload, 0x6D);
@@ -1481,6 +1485,8 @@ ASSERT_REG_POSITION(tess_mode, 0xC8);
ASSERT_REG_POSITION(tess_level_outer, 0xC9);
ASSERT_REG_POSITION(tess_level_inner, 0xCD);
ASSERT_REG_POSITION(rasterize_enable, 0xDF);
ASSERT_REG_POSITION(tfb_bindings, 0xE0);
ASSERT_REG_POSITION(tfb_layouts, 0x1C0);
ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
ASSERT_REG_POSITION(rt, 0x200);
ASSERT_REG_POSITION(viewport_transform, 0x280);
@@ -1490,6 +1496,8 @@ ASSERT_REG_POSITION(depth_mode, 0x35F);
ASSERT_REG_POSITION(clear_color[0], 0x360);
ASSERT_REG_POSITION(clear_depth, 0x364);
ASSERT_REG_POSITION(clear_stencil, 0x368);
ASSERT_REG_POSITION(polygon_mode_front, 0x36B);
ASSERT_REG_POSITION(polygon_mode_back, 0x36C);
ASSERT_REG_POSITION(polygon_offset_point_enable, 0x370);
ASSERT_REG_POSITION(polygon_offset_line_enable, 0x371);
ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372);
@@ -1503,10 +1511,12 @@ ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
ASSERT_REG_POSITION(depth_bounds, 0x3E7);
ASSERT_REG_POSITION(zeta, 0x3F8);
ASSERT_REG_POSITION(clear_flags, 0x43E);
ASSERT_REG_POSITION(fill_rectangle, 0x44F);
ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
ASSERT_REG_POSITION(rt_control, 0x487);
ASSERT_REG_POSITION(zeta_width, 0x48a);
ASSERT_REG_POSITION(zeta_height, 0x48b);
ASSERT_REG_POSITION(zeta_layers, 0x48c);
ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
@@ -1556,7 +1566,9 @@ ASSERT_REG_POSITION(index_array, 0x5F2);
ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F);
ASSERT_REG_POSITION(instanced_arrays, 0x620);
ASSERT_REG_POSITION(vp_point_size, 0x644);
ASSERT_REG_POSITION(cull, 0x646);
ASSERT_REG_POSITION(cull_test_enabled, 0x646);
ASSERT_REG_POSITION(front_face, 0x647);
ASSERT_REG_POSITION(cull_face, 0x648);
ASSERT_REG_POSITION(pixel_center_integer, 0x649);
ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B);
ASSERT_REG_POSITION(view_volume_clip_control, 0x64F);
@@ -1573,6 +1585,7 @@ ASSERT_REG_POSITION(firmware, 0x8C0);
ASSERT_REG_POSITION(const_buffer, 0x8E0);
ASSERT_REG_POSITION(cb_bind[0], 0x904);
ASSERT_REG_POSITION(tex_cb_index, 0x982);
ASSERT_REG_POSITION(tfb_varying_locs, 0xA00);
ASSERT_REG_POSITION(ssbo_info, 0xD18);
ASSERT_REG_POSITION(tex_info_buffers.address[0], 0xD2A);
ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F);
+1 -1
View File
@@ -57,7 +57,7 @@ void MaxwellDMA::HandleCopy() {
}
// All copies here update the main memory, so mark all rasterizer states as invalid.
system.GPU().Maxwell3D().dirty.OnMemoryWrite();
system.GPU().Maxwell3D().OnMemoryWrite();
if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
// When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
+65 -9
View File
@@ -82,6 +82,10 @@ union Attribute {
Position = 7,
Attribute_0 = 8,
Attribute_31 = 39,
FrontColor = 40,
FrontSecondaryColor = 41,
BackColor = 42,
BackSecondaryColor = 43,
ClipDistances0123 = 44,
ClipDistances4567 = 45,
PointCoord = 46,
@@ -89,6 +93,8 @@ union Attribute {
// shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
// shader.
TessCoordInstanceIDVertexID = 47,
TexCoord_0 = 48,
TexCoord_7 = 55,
// This attribute contains a tuple of (Unk, Unk, Unk, gl_FrontFacing) when inside a fragment
// shader. It is unknown what the other values contain.
FrontFacing = 63,
@@ -296,6 +302,23 @@ enum class VmadShr : u64 {
Shr15 = 2,
};
enum class VmnmxType : u64 {
Bits8,
Bits16,
Bits32,
};
enum class VmnmxOperation : u64 {
Mrg_16H = 0,
Mrg_16L = 1,
Mrg_8B0 = 2,
Mrg_8B2 = 3,
Acc = 4,
Min = 5,
Max = 6,
Nop = 7,
};
enum class XmadMode : u64 {
None = 0,
CLo = 1,
@@ -911,14 +934,9 @@ union Instruction {
} fadd32i;
union {
BitField<20, 8, u64> shift_position;
BitField<28, 8, u64> shift_length;
BitField<48, 1, u64> negate_b;
BitField<49, 1, u64> negate_a;
u64 GetLeftShiftValue() const {
return 32 - (shift_position + shift_length);
}
BitField<40, 1, u64> brev;
BitField<47, 1, u64> rd_cc;
BitField<48, 1, u64> is_signed;
} bfe;
union {
@@ -1661,6 +1679,42 @@ union Instruction {
BitField<47, 1, u64> cc;
} vmad;
union {
BitField<54, 1, u64> is_dest_signed;
BitField<48, 1, u64> is_src_a_signed;
BitField<49, 1, u64> is_src_b_signed;
BitField<37, 2, u64> src_format_a;
BitField<29, 2, u64> src_format_b;
BitField<56, 1, u64> mx;
BitField<55, 1, u64> sat;
BitField<36, 2, u64> selector_a;
BitField<28, 2, u64> selector_b;
BitField<50, 1, u64> is_op_b_register;
BitField<51, 3, VmnmxOperation> operation;
VmnmxType SourceFormatA() const {
switch (src_format_a) {
case 0b11:
return VmnmxType::Bits32;
case 0b10:
return VmnmxType::Bits16;
default:
return VmnmxType::Bits8;
}
}
VmnmxType SourceFormatB() const {
switch (src_format_b) {
case 0b11:
return VmnmxType::Bits32;
case 0b10:
return VmnmxType::Bits16;
default:
return VmnmxType::Bits8;
}
}
} vmnmx;
union {
BitField<20, 16, u64> imm20_16;
BitField<35, 1, u64> high_b_rr; // used on RR
@@ -1772,6 +1826,7 @@ public:
MEMBAR,
VMAD,
VSETP,
VMNMX,
FFMA_IMM, // Fused Multiply and Add
FFMA_CR,
FFMA_RC,
@@ -2077,6 +2132,7 @@ private:
INST("1110111110011---", Id::MEMBAR, Type::Trivial, "MEMBAR"),
INST("01011111--------", Id::VMAD, Type::Video, "VMAD"),
INST("0101000011110---", Id::VSETP, Type::Video, "VSETP"),
INST("0011101---------", Id::VMNMX, Type::Video, "VMNMX"),
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
@@ -2177,7 +2233,7 @@ private:
INST("0011011-11111---", Id::SHF_LEFT_IMM, Type::Shift, "SHF_LEFT_IMM"),
INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"),
INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"),
INST("0011101-11100---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"),
INST("0011100-11100---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"),
INST("0100110010111---", Id::I2F_C, Type::Conversion, "I2F_C"),
INST("0101110010111---", Id::I2F_R, Type::Conversion, "I2F_R"),
INST("0011100-10111---", Id::I2F_IMM, Type::Conversion, "I2F_IMM"),
-65
View File
@@ -140,71 +140,6 @@ void GPU::FlushCommands() {
renderer.Rasterizer().FlushCommands();
}
u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
ASSERT(format != RenderTargetFormat::NONE);
switch (format) {
case RenderTargetFormat::RGBA32_FLOAT:
case RenderTargetFormat::RGBA32_UINT:
return 16;
case RenderTargetFormat::RGBA16_UINT:
case RenderTargetFormat::RGBA16_UNORM:
case RenderTargetFormat::RGBA16_FLOAT:
case RenderTargetFormat::RGBX16_FLOAT:
case RenderTargetFormat::RG32_FLOAT:
case RenderTargetFormat::RG32_UINT:
return 8;
case RenderTargetFormat::RGBA8_UNORM:
case RenderTargetFormat::RGBA8_SNORM:
case RenderTargetFormat::RGBA8_SRGB:
case RenderTargetFormat::RGBA8_UINT:
case RenderTargetFormat::RGB10_A2_UNORM:
case RenderTargetFormat::BGRA8_UNORM:
case RenderTargetFormat::BGRA8_SRGB:
case RenderTargetFormat::RG16_UNORM:
case RenderTargetFormat::RG16_SNORM:
case RenderTargetFormat::RG16_UINT:
case RenderTargetFormat::RG16_SINT:
case RenderTargetFormat::RG16_FLOAT:
case RenderTargetFormat::R32_FLOAT:
case RenderTargetFormat::R11G11B10_FLOAT:
case RenderTargetFormat::R32_UINT:
return 4;
case RenderTargetFormat::R16_UNORM:
case RenderTargetFormat::R16_SNORM:
case RenderTargetFormat::R16_UINT:
case RenderTargetFormat::R16_SINT:
case RenderTargetFormat::R16_FLOAT:
case RenderTargetFormat::RG8_UNORM:
case RenderTargetFormat::RG8_SNORM:
return 2;
case RenderTargetFormat::R8_UNORM:
case RenderTargetFormat::R8_UINT:
return 1;
default:
UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
return 1;
}
}
u32 DepthFormatBytesPerPixel(DepthFormat format) {
switch (format) {
case DepthFormat::Z32_S8_X24_FLOAT:
return 8;
case DepthFormat::Z32_FLOAT:
case DepthFormat::S8_Z24_UNORM:
case DepthFormat::Z24_X8_UNORM:
case DepthFormat::Z24_S8_UNORM:
case DepthFormat::Z24_C8_UNORM:
return 4;
case DepthFormat::Z16_UNORM:
return 2;
default:
UNIMPLEMENTED_MSG("Unimplemented Depth format {}", static_cast<u32>(format));
return 1;
}
}
// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
// So the values you see in docs might be multiplied by 4.
+2 -6
View File
@@ -39,6 +39,7 @@ enum class RenderTargetFormat : u32 {
RGBA32_FLOAT = 0xC0,
RGBA32_UINT = 0xC2,
RGBA16_UNORM = 0xC6,
RGBA16_SNORM = 0xC7,
RGBA16_UINT = 0xC9,
RGBA16_FLOAT = 0xCA,
RG32_FLOAT = 0xCB,
@@ -57,6 +58,7 @@ enum class RenderTargetFormat : u32 {
RG16_UINT = 0xDD,
RG16_FLOAT = 0xDE,
R11G11B10_FLOAT = 0xE0,
R32_SINT = 0xE3,
R32_UINT = 0xE4,
R32_FLOAT = 0xE5,
B5G6R5_UNORM = 0xE8,
@@ -82,12 +84,6 @@ enum class DepthFormat : u32 {
Z32_S8_X24_FLOAT = 0x19,
};
/// Returns the number of bytes per pixel of each rendertarget format.
u32 RenderTargetBytesPerPixel(RenderTargetFormat format);
/// Returns the number of bytes per pixel of each depth format.
u32 DepthFormatBytesPerPixel(DepthFormat format);
struct CommandListHeader;
class DebugContext;
+2 -2
View File
@@ -5,7 +5,7 @@
#include "common/assert.h"
#include "common/microprofile.h"
#include "core/core.h"
#include "core/frontend/scope_acquire_window_context.h"
#include "core/frontend/scope_acquire_context.h"
#include "video_core/dma_pusher.h"
#include "video_core/gpu.h"
#include "video_core/gpu_thread.h"
@@ -27,7 +27,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
return;
}
Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()};
Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()};
CommandDataContainer next;
while (state.is_running) {
+4 -3
View File
@@ -4,13 +4,15 @@
#include <algorithm>
#include <limits>
#include <vector>
#include "common/common_types.h"
#include "video_core/guest_driver.h"
namespace VideoCore {
void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets) {
if (texture_handler_size_deduced) {
void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32> bound_offsets) {
if (texture_handler_size) {
return;
}
const std::size_t size = bound_offsets.size();
@@ -29,7 +31,6 @@ void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32>&& bound_offse
if (min_val > 2) {
return;
}
texture_handler_size_deduced = true;
texture_handler_size = min_texture_handler_size * min_val;
}
+13 -8
View File
@@ -4,6 +4,7 @@
#pragma once
#include <optional>
#include <vector>
#include "common/common_types.h"
@@ -17,25 +18,29 @@ namespace VideoCore {
*/
class GuestDriverProfile {
public:
void DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets);
explicit GuestDriverProfile() = default;
explicit GuestDriverProfile(std::optional<u32> texture_handler_size)
: texture_handler_size{texture_handler_size} {}
void DeduceTextureHandlerSize(std::vector<u32> bound_offsets);
u32 GetTextureHandlerSize() const {
return texture_handler_size;
return texture_handler_size.value_or(default_texture_handler_size);
}
bool TextureHandlerSizeKnown() const {
return texture_handler_size_deduced;
bool IsTextureHandlerSizeKnown() const {
return texture_handler_size.has_value();
}
private:
// Minimum size of texture handler any driver can use.
static constexpr u32 min_texture_handler_size = 4;
// This goes with Vulkan and OpenGL standards but Nvidia GPUs can easily
// use 4 bytes instead. Thus, certain drivers may squish the size.
// This goes with Vulkan and OpenGL standards but Nvidia GPUs can easily use 4 bytes instead.
// Thus, certain drivers may squish the size.
static constexpr u32 default_texture_handler_size = 8;
u32 texture_handler_size = default_texture_handler_size;
bool texture_handler_size_deduced = false;
std::optional<u32> texture_handler_size = default_texture_handler_size;
};
} // namespace VideoCore
+1 -1
View File
@@ -174,7 +174,7 @@ private:
/// End of address space, based on address space in bits.
static constexpr GPUVAddr address_space_end{1ULL << address_space_width};
Common::PageTable page_table{page_bits};
Common::BackingPageTable page_table{page_bits};
VMAMap vma_map;
VideoCore::RasterizerInterface& rasterizer;
+4
View File
@@ -51,6 +51,7 @@ static constexpr ConversionArray morton_to_linear_fns = {
MortonCopy<true, PixelFormat::R8UI>,
MortonCopy<true, PixelFormat::RGBA16F>,
MortonCopy<true, PixelFormat::RGBA16U>,
MortonCopy<true, PixelFormat::RGBA16S>,
MortonCopy<true, PixelFormat::RGBA16UI>,
MortonCopy<true, PixelFormat::R11FG11FB10F>,
MortonCopy<true, PixelFormat::RGBA32UI>,
@@ -85,6 +86,7 @@ static constexpr ConversionArray morton_to_linear_fns = {
MortonCopy<true, PixelFormat::RG32UI>,
MortonCopy<true, PixelFormat::RGBX16F>,
MortonCopy<true, PixelFormat::R32UI>,
MortonCopy<true, PixelFormat::R32I>,
MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
@@ -130,6 +132,7 @@ static constexpr ConversionArray linear_to_morton_fns = {
MortonCopy<false, PixelFormat::R8U>,
MortonCopy<false, PixelFormat::R8UI>,
MortonCopy<false, PixelFormat::RGBA16F>,
MortonCopy<false, PixelFormat::RGBA16S>,
MortonCopy<false, PixelFormat::RGBA16U>,
MortonCopy<false, PixelFormat::RGBA16UI>,
MortonCopy<false, PixelFormat::R11FG11FB10F>,
@@ -166,6 +169,7 @@ static constexpr ConversionArray linear_to_morton_fns = {
MortonCopy<false, PixelFormat::RG32UI>,
MortonCopy<false, PixelFormat::RGBX16F>,
MortonCopy<false, PixelFormat::R32UI>,
MortonCopy<false, PixelFormat::R32I>,
nullptr,
nullptr,
nullptr,
+3 -1
View File
@@ -25,7 +25,6 @@ constexpr std::size_t NumQueryTypes = 1;
enum class LoadCallbackStage {
Prepare,
Decompile,
Build,
Complete,
};
@@ -89,6 +88,9 @@ public:
virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false,
const DiskResourceLoadCallback& callback = {}) {}
/// Initializes renderer dirty flags
virtual void SetupDirtyFlags() {}
/// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
GuestDriverProfile& AccessGuestDriverProfile() {
return guest_driver_profile;
+7 -3
View File
@@ -35,15 +35,19 @@ public:
explicit RendererBase(Core::Frontend::EmuWindow& window);
virtual ~RendererBase();
/// Swap buffers (render frame)
virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
/// Initialize the renderer
virtual bool Init() = 0;
/// Shutdown the renderer
virtual void ShutDown() = 0;
/// Finalize rendering the guest frame and draw into the presentation texture
virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
/// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
/// specific implementation)
virtual void TryPresent(int timeout_ms) = 0;
// Getter/setter functions:
// ------------------------
@@ -11,7 +11,6 @@
#include "common/common_types.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_framebuffer_cache.h"
#include "video_core/renderer_opengl/gl_state.h"
namespace OpenGL {
@@ -36,8 +35,7 @@ OGLFramebuffer FramebufferCacheOpenGL::CreateFramebuffer(const FramebufferCacheK
framebuffer.Create();
// TODO(Rodrigo): Use DSA here after Nvidia fixes their framebuffer DSA bugs.
local_state.draw.draw_framebuffer = framebuffer.handle;
local_state.ApplyFramebufferState();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
if (key.zeta) {
const bool stencil = key.zeta->GetSurfaceParams().type == SurfaceType::DepthStencil;
@@ -13,7 +13,6 @@
#include "common/common_types.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_state.h"
#include "video_core/renderer_opengl/gl_texture_cache.h"
namespace OpenGL {
@@ -63,7 +62,6 @@ public:
private:
OGLFramebuffer CreateFramebuffer(const FramebufferCacheKey& key);
OpenGLState local_state;
std::unordered_map<FramebufferCacheKey, OGLFramebuffer> cache;
};
File diff suppressed because it is too large Load Diff
+42 -29
View File
@@ -30,7 +30,7 @@
#include "video_core/renderer_opengl/gl_shader_cache.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
#include "video_core/renderer_opengl/gl_state.h"
#include "video_core/renderer_opengl/gl_state_tracker.h"
#include "video_core/renderer_opengl/gl_texture_cache.h"
#include "video_core/renderer_opengl/utils.h"
#include "video_core/textures/texture.h"
@@ -55,7 +55,8 @@ struct DrawParameters;
class RasterizerOpenGL : public VideoCore::RasterizerAccelerated {
public:
explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
ScreenInfo& info);
ScreenInfo& info, GLShader::ProgramManager& program_manager,
StateTracker& state_tracker);
~RasterizerOpenGL() override;
void Draw(bool is_indexed, bool is_instanced) override;
@@ -76,6 +77,7 @@ public:
u32 pixel_stride) override;
void LoadDiskResources(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) override;
void SetupDirtyFlags() override;
/// Returns true when there are commands queued to the OpenGL server.
bool AnyCommandQueued() const {
@@ -86,8 +88,7 @@ private:
/// Configures the color and depth framebuffer states.
void ConfigureFramebuffers();
void ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb,
bool using_depth_fb, bool using_stencil_fb);
void ConfigureClearFramebuffer(bool using_color_fb, bool using_depth_fb, bool using_stencil_fb);
/// Configures the current constbuffers to use for the draw command.
void SetupDrawConstBuffers(std::size_t stage_index, const Shader& shader);
@@ -97,7 +98,7 @@ private:
/// Configures a constant buffer.
void SetupConstBuffer(u32 binding, const Tegra::Engines::ConstBufferInfo& buffer,
const GLShader::ConstBufferEntry& entry);
const ConstBufferEntry& entry);
/// Configures the current global memory entries to use for the draw command.
void SetupDrawGlobalMemory(std::size_t stage_index, const Shader& shader);
@@ -106,7 +107,7 @@ private:
void SetupComputeGlobalMemory(const Shader& kernel);
/// Configures a constant buffer.
void SetupGlobalMemory(u32 binding, const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
void SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
std::size_t size);
/// Configures the current textures to use for the draw command.
@@ -117,7 +118,7 @@ private:
/// Configures a texture.
void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
const GLShader::SamplerEntry& entry);
const SamplerEntry& entry);
/// Configures images in a graphics shader.
void SetupDrawImages(std::size_t stage_index, const Shader& shader);
@@ -126,15 +127,16 @@ private:
void SetupComputeImages(const Shader& shader);
/// Configures an image.
void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic,
const GLShader::ImageEntry& entry);
void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic, const ImageEntry& entry);
/// Syncs the viewport and depth range to match the guest state
void SyncViewport(OpenGLState& current_state);
void SyncViewport();
/// Syncs the depth clamp state
void SyncDepthClamp();
/// Syncs the clip enabled status to match the guest state
void SyncClipEnabled(
const std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances>& clip_mask);
void SyncClipEnabled(u32 clip_mask);
/// Syncs the clip coefficients to match the guest state
void SyncClipCoef();
@@ -164,16 +166,16 @@ private:
void SyncMultiSampleState();
/// Syncs the scissor test state to match the guest state
void SyncScissorTest(OpenGLState& current_state);
/// Syncs the transform feedback state to match the guest state
void SyncTransformFeedback();
void SyncScissorTest();
/// Syncs the point state to match the guest state
void SyncPointState();
/// Syncs the rasterizer enable state to match the guest state
void SyncRasterizeEnable(OpenGLState& current_state);
void SyncRasterizeEnable();
/// Syncs polygon modes to match the guest state
void SyncPolygonModes();
/// Syncs Color Mask
void SyncColorMask();
@@ -184,6 +186,15 @@ private:
/// Syncs the alpha test state to match the guest state
void SyncAlphaTest();
/// Syncs the framebuffer sRGB state to match the guest state
void SyncFramebufferSRGB();
/// Begin a transform feedback
void BeginTransformFeedback(GLenum primitive_mode);
/// End a transform feedback
void EndTransformFeedback();
/// Check for extension that are not strictly required but are needed for correct emulation
void CheckExtensions();
@@ -191,18 +202,17 @@ private:
std::size_t CalculateIndexBufferSize() const;
/// Updates and returns a vertex array object representing current vertex format
GLuint SetupVertexFormat();
/// Updates the current vertex format
void SetupVertexFormat();
void SetupVertexBuffer(GLuint vao);
void SetupVertexInstances(GLuint vao);
void SetupVertexBuffer();
void SetupVertexInstances();
GLintptr SetupIndexBuffer();
void SetupShaders(GLenum primitive_mode);
const Device device;
OpenGLState state;
TextureCacheOpenGL texture_cache;
ShaderCacheOpenGL shader_cache;
@@ -212,22 +222,25 @@ private:
Core::System& system;
ScreenInfo& screen_info;
std::unique_ptr<GLShader::ProgramManager> shader_program_manager;
std::map<std::array<Tegra::Engines::Maxwell3D::Regs::VertexAttribute,
Tegra::Engines::Maxwell3D::Regs::NumVertexAttributes>,
OGLVertexArray>
vertex_array_cache;
GLShader::ProgramManager& program_manager;
StateTracker& state_tracker;
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
OGLBufferCache buffer_cache;
VertexArrayPushBuffer vertex_array_pushbuffer;
VertexArrayPushBuffer vertex_array_pushbuffer{state_tracker};
BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers>
transform_feedback_buffers;
std::bitset<Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers>
enabled_transform_feedback_buffers;
/// Number of commands queued to the OpenGL driver. Reseted on flush.
std::size_t num_queued_commands = 0;
u32 last_clip_distance_mask = 0;
};
} // namespace OpenGL
@@ -8,13 +8,29 @@
#include "common/microprofile.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
#include "video_core/renderer_opengl/gl_state.h"
MICROPROFILE_DEFINE(OpenGL_ResourceCreation, "OpenGL", "Resource Creation", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_RGB(128, 128, 192));
namespace OpenGL {
void OGLRenderbuffer::Create() {
if (handle != 0)
return;
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
glCreateRenderbuffers(1, &handle);
}
void OGLRenderbuffer::Release() {
if (handle == 0)
return;
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
glDeleteRenderbuffers(1, &handle);
handle = 0;
}
void OGLTexture::Create(GLenum target) {
if (handle != 0)
return;
@@ -29,7 +45,6 @@ void OGLTexture::Release() {
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
glDeleteTextures(1, &handle);
OpenGLState::GetCurState().UnbindTexture(handle).Apply();
handle = 0;
}
@@ -47,7 +62,6 @@ void OGLTextureView::Release() {
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
glDeleteTextures(1, &handle);
OpenGLState::GetCurState().UnbindTexture(handle).Apply();
handle = 0;
}
@@ -65,7 +79,6 @@ void OGLSampler::Release() {
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
glDeleteSamplers(1, &handle);
OpenGLState::GetCurState().ResetSampler(handle).Apply();
handle = 0;
}
@@ -109,7 +122,6 @@ void OGLProgram::Release() {
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
glDeleteProgram(handle);
OpenGLState::GetCurState().ResetProgram(handle).Apply();
handle = 0;
}
@@ -127,7 +139,6 @@ void OGLPipeline::Release() {
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
glDeleteProgramPipelines(1, &handle);
OpenGLState::GetCurState().ResetPipeline(handle).Apply();
handle = 0;
}
@@ -171,24 +182,6 @@ void OGLSync::Release() {
handle = 0;
}
void OGLVertexArray::Create() {
if (handle != 0)
return;
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
glCreateVertexArrays(1, &handle);
}
void OGLVertexArray::Release() {
if (handle == 0)
return;
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
glDeleteVertexArrays(1, &handle);
OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
handle = 0;
}
void OGLFramebuffer::Create() {
if (handle != 0)
return;
@@ -203,7 +196,6 @@ void OGLFramebuffer::Release() {
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
glDeleteFramebuffers(1, &handle);
OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
handle = 0;
}
@@ -11,6 +11,31 @@
namespace OpenGL {
class OGLRenderbuffer : private NonCopyable {
public:
OGLRenderbuffer() = default;
OGLRenderbuffer(OGLRenderbuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
~OGLRenderbuffer() {
Release();
}
OGLRenderbuffer& operator=(OGLRenderbuffer&& o) noexcept {
Release();
handle = std::exchange(o.handle, 0);
return *this;
}
/// Creates a new internal OpenGL resource and stores the handle
void Create();
/// Deletes the internal OpenGL resource
void Release();
GLuint handle = 0;
};
class OGLTexture : private NonCopyable {
public:
OGLTexture() = default;
@@ -216,31 +241,6 @@ public:
GLsync handle = 0;
};
class OGLVertexArray : private NonCopyable {
public:
OGLVertexArray() = default;
OGLVertexArray(OGLVertexArray&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
~OGLVertexArray() {
Release();
}
OGLVertexArray& operator=(OGLVertexArray&& o) noexcept {
Release();
handle = std::exchange(o.handle, 0);
return *this;
}
/// Creates a new internal OpenGL resource and stores the handle
void Create();
/// Deletes the internal OpenGL resource
void Release();
GLuint handle = 0;
};
class OGLFramebuffer : private NonCopyable {
public:
OGLFramebuffer() = default;
@@ -38,7 +38,7 @@ OGLSampler SamplerCacheOpenGL::CreateSampler(const Tegra::Texture::TSCEntry& tsc
glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY, tsc.GetMaxAnisotropy());
} else if (GLAD_GL_EXT_texture_filter_anisotropic) {
glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, tsc.GetMaxAnisotropy());
} else if (tsc.GetMaxAnisotropy() != 1) {
} else {
LOG_WARNING(Render_OpenGL, "Anisotropy not supported by host GPU driver");
}
+162 -343
View File
@@ -2,12 +2,16 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <atomic>
#include <functional>
#include <mutex>
#include <optional>
#include <string>
#include <thread>
#include <unordered_set>
#include <boost/functional/hash.hpp>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
@@ -22,14 +26,16 @@
#include "video_core/renderer_opengl/gl_shader_cache.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
#include "video_core/renderer_opengl/gl_state_tracker.h"
#include "video_core/renderer_opengl/utils.h"
#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
namespace OpenGL {
using Tegra::Engines::ShaderType;
using VideoCommon::Shader::ConstBufferLocker;
using VideoCommon::Shader::ProgramCode;
using VideoCommon::Shader::Registry;
using VideoCommon::Shader::ShaderIR;
namespace {
@@ -55,7 +61,7 @@ constexpr bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) {
}
/// Calculates the size of a program stream
std::size_t CalculateProgramSize(const GLShader::ProgramCode& program) {
std::size_t CalculateProgramSize(const ProgramCode& program) {
constexpr std::size_t start_offset = 10;
// This is the encoded version of BRA that jumps to itself. All Nvidia
// shaders end with one.
@@ -108,32 +114,9 @@ constexpr GLenum GetGLShaderType(ShaderType shader_type) {
}
}
/// Describes primitive behavior on geometry shaders
constexpr std::pair<const char*, u32> GetPrimitiveDescription(GLenum primitive_mode) {
switch (primitive_mode) {
case GL_POINTS:
return {"points", 1};
case GL_LINES:
case GL_LINE_STRIP:
return {"lines", 2};
case GL_LINES_ADJACENCY:
case GL_LINE_STRIP_ADJACENCY:
return {"lines_adjacency", 4};
case GL_TRIANGLES:
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
return {"triangles", 3};
case GL_TRIANGLES_ADJACENCY:
case GL_TRIANGLE_STRIP_ADJACENCY:
return {"triangles_adjacency", 6};
default:
return {"points", 1};
}
}
/// Hashes one (or two) program streams
u64 GetUniqueIdentifier(ShaderType shader_type, bool is_a, const ProgramCode& code,
const ProgramCode& code_b) {
const ProgramCode& code_b = {}) {
u64 unique_identifier = boost::hash_value(code);
if (is_a) {
// VertexA programs include two programs
@@ -142,24 +125,6 @@ u64 GetUniqueIdentifier(ShaderType shader_type, bool is_a, const ProgramCode& co
return unique_identifier;
}
/// Creates an unspecialized program from code streams
std::string GenerateGLSL(const Device& device, ShaderType shader_type, const ShaderIR& ir,
const std::optional<ShaderIR>& ir_b) {
switch (shader_type) {
case ShaderType::Vertex:
return GLShader::GenerateVertexShader(device, ir, ir_b ? &*ir_b : nullptr);
case ShaderType::Geometry:
return GLShader::GenerateGeometryShader(device, ir);
case ShaderType::Fragment:
return GLShader::GenerateFragmentShader(device, ir);
case ShaderType::Compute:
return GLShader::GenerateComputeShader(device, ir);
default:
UNIMPLEMENTED_MSG("Unimplemented shader_type={}", static_cast<u32>(shader_type));
return {};
}
}
constexpr const char* GetShaderTypeName(ShaderType shader_type) {
switch (shader_type) {
case ShaderType::Vertex:
@@ -195,102 +160,38 @@ constexpr ShaderType GetShaderType(Maxwell::ShaderProgram program_type) {
return {};
}
std::string GetShaderId(u64 unique_identifier, ShaderType shader_type) {
std::string MakeShaderID(u64 unique_identifier, ShaderType shader_type) {
return fmt::format("{}{:016X}", GetShaderTypeName(shader_type), unique_identifier);
}
Tegra::Engines::ConstBufferEngineInterface& GetConstBufferEngineInterface(Core::System& system,
ShaderType shader_type) {
if (shader_type == ShaderType::Compute) {
return system.GPU().KeplerCompute();
} else {
return system.GPU().Maxwell3D();
std::shared_ptr<Registry> MakeRegistry(const ShaderDiskCacheEntry& entry) {
const VideoCore::GuestDriverProfile guest_profile{entry.texture_handler_size};
const VideoCommon::Shader::SerializedRegistryInfo info{guest_profile, entry.bound_buffer,
entry.graphics_info, entry.compute_info};
const auto registry = std::make_shared<Registry>(entry.type, info);
for (const auto& [address, value] : entry.keys) {
const auto [buffer, offset] = address;
registry->InsertKey(buffer, offset, value);
}
}
std::unique_ptr<ConstBufferLocker> MakeLocker(Core::System& system, ShaderType shader_type) {
return std::make_unique<ConstBufferLocker>(shader_type,
GetConstBufferEngineInterface(system, shader_type));
}
void FillLocker(ConstBufferLocker& locker, const ShaderDiskCacheUsage& usage) {
locker.SetBoundBuffer(usage.bound_buffer);
for (const auto& key : usage.keys) {
const auto [buffer, offset] = key.first;
locker.InsertKey(buffer, offset, key.second);
for (const auto& [offset, sampler] : entry.bound_samplers) {
registry->InsertBoundSampler(offset, sampler);
}
for (const auto& [offset, sampler] : usage.bound_samplers) {
locker.InsertBoundSampler(offset, sampler);
}
for (const auto& [key, sampler] : usage.bindless_samplers) {
for (const auto& [key, sampler] : entry.bindless_samplers) {
const auto [buffer, offset] = key;
locker.InsertBindlessSampler(buffer, offset, sampler);
registry->InsertBindlessSampler(buffer, offset, sampler);
}
return registry;
}
CachedProgram BuildShader(const Device& device, u64 unique_identifier, ShaderType shader_type,
const ProgramCode& code, const ProgramCode& code_b,
ConstBufferLocker& locker, const ProgramVariant& variant,
bool hint_retrievable = false) {
LOG_INFO(Render_OpenGL, "called. {}", GetShaderId(unique_identifier, shader_type));
const bool is_compute = shader_type == ShaderType::Compute;
const u32 main_offset = is_compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
const ShaderIR ir(code, main_offset, COMPILER_SETTINGS, locker);
std::optional<ShaderIR> ir_b;
if (!code_b.empty()) {
ir_b.emplace(code_b, main_offset, COMPILER_SETTINGS, locker);
}
std::string source = fmt::format(R"(// {}
#version 430 core
#extension GL_ARB_separate_shader_objects : enable
)",
GetShaderId(unique_identifier, shader_type));
if (device.HasShaderBallot()) {
source += "#extension GL_ARB_shader_ballot : require\n";
}
if (device.HasVertexViewportLayer()) {
source += "#extension GL_ARB_shader_viewport_layer_array : require\n";
}
if (device.HasImageLoadFormatted()) {
source += "#extension GL_EXT_shader_image_load_formatted : require\n";
}
if (device.HasWarpIntrinsics()) {
source += "#extension GL_NV_gpu_shader5 : require\n"
"#extension GL_NV_shader_thread_group : require\n"
"#extension GL_NV_shader_thread_shuffle : require\n";
}
// This pragma stops Nvidia's driver from over optimizing math (probably using fp16 operations)
// on places where we don't want to.
// Thanks to Ryujinx for finding this workaround.
source += "#pragma optionNV(fastmath off)\n";
if (shader_type == ShaderType::Geometry) {
const auto [glsl_topology, max_vertices] = GetPrimitiveDescription(variant.primitive_mode);
source += fmt::format("#define MAX_VERTEX_INPUT {}\n", max_vertices);
source += fmt::format("layout ({}) in;\n", glsl_topology);
}
if (shader_type == ShaderType::Compute) {
if (variant.local_memory_size > 0) {
source += fmt::format("#define LOCAL_MEMORY_SIZE {}\n",
Common::AlignUp(variant.local_memory_size, 4) / 4);
}
source +=
fmt::format("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;\n",
variant.block_x, variant.block_y, variant.block_z);
if (variant.shared_memory_size > 0) {
// shared_memory_size is described in number of words
source += fmt::format("shared uint smem[{}];\n", variant.shared_memory_size);
}
}
source += '\n';
source += GenerateGLSL(device, shader_type, ir, ir_b);
std::shared_ptr<OGLProgram> BuildShader(const Device& device, ShaderType shader_type,
u64 unique_identifier, const ShaderIR& ir,
const Registry& registry, bool hint_retrievable = false) {
const std::string shader_id = MakeShaderID(unique_identifier, shader_type);
LOG_INFO(Render_OpenGL, "{}", shader_id);
const std::string glsl = DecompileShader(device, ir, registry, shader_type, shader_id);
OGLShader shader;
shader.Create(source.c_str(), GetGLShaderType(shader_type));
shader.Create(glsl.c_str(), GetGLShaderType(shader_type));
auto program = std::make_shared<OGLProgram>();
program->Create(true, hint_retrievable, shader.handle);
@@ -298,7 +199,7 @@ CachedProgram BuildShader(const Device& device, u64 unique_identifier, ShaderTyp
}
std::unordered_set<GLenum> GetSupportedFormats() {
GLint num_formats{};
GLint num_formats;
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
std::vector<GLint> formats(num_formats);
@@ -313,115 +214,82 @@ std::unordered_set<GLenum> GetSupportedFormats() {
} // Anonymous namespace
CachedShader::CachedShader(const ShaderParameters& params, ShaderType shader_type,
GLShader::ShaderEntries entries, ProgramCode code, ProgramCode code_b)
: RasterizerCacheObject{params.host_ptr}, system{params.system},
disk_cache{params.disk_cache}, device{params.device}, cpu_addr{params.cpu_addr},
unique_identifier{params.unique_identifier}, shader_type{shader_type},
entries{std::move(entries)}, code{std::move(code)}, code_b{std::move(code_b)} {
if (!params.precompiled_variants) {
return;
}
for (const auto& pair : *params.precompiled_variants) {
auto locker = MakeLocker(system, shader_type);
const auto& usage = pair->first;
FillLocker(*locker, usage);
CachedShader::CachedShader(const u8* host_ptr, VAddr cpu_addr, std::size_t size_in_bytes,
std::shared_ptr<VideoCommon::Shader::Registry> registry,
ShaderEntries entries, std::shared_ptr<OGLProgram> program)
: RasterizerCacheObject{host_ptr}, registry{std::move(registry)}, entries{std::move(entries)},
cpu_addr{cpu_addr}, size_in_bytes{size_in_bytes}, program{std::move(program)} {}
std::unique_ptr<LockerVariant>* locker_variant = nullptr;
const auto it =
std::find_if(locker_variants.begin(), locker_variants.end(), [&](const auto& variant) {
return variant->locker->HasEqualKeys(*locker);
});
if (it == locker_variants.end()) {
locker_variant = &locker_variants.emplace_back();
*locker_variant = std::make_unique<LockerVariant>();
locker_variant->get()->locker = std::move(locker);
} else {
locker_variant = &*it;
}
locker_variant->get()->programs.emplace(usage.variant, pair->second);
}
CachedShader::~CachedShader() = default;
GLuint CachedShader::GetHandle() const {
DEBUG_ASSERT(registry->IsConsistent());
return program->handle;
}
Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params,
Maxwell::ShaderProgram program_type, ProgramCode code,
ProgramCode code_b) {
const auto shader_type = GetShaderType(program_type);
params.disk_cache.SaveRaw(
ShaderDiskCacheRaw(params.unique_identifier, shader_type, code, code_b));
const std::size_t size_in_bytes = code.size() * sizeof(u64);
ConstBufferLocker locker(shader_type, params.system.GPU().Maxwell3D());
const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, locker);
auto registry = std::make_shared<Registry>(shader_type, params.system.GPU().Maxwell3D());
const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
// TODO(Rodrigo): Handle VertexA shaders
// std::optional<ShaderIR> ir_b;
// if (!code_b.empty()) {
// ir_b.emplace(code_b, STAGE_MAIN_OFFSET);
// }
return std::shared_ptr<CachedShader>(new CachedShader(
params, shader_type, GLShader::GetEntries(ir), std::move(code), std::move(code_b)));
auto program = BuildShader(params.device, shader_type, params.unique_identifier, ir, *registry);
ShaderDiskCacheEntry entry;
entry.type = shader_type;
entry.code = std::move(code);
entry.code_b = std::move(code_b);
entry.unique_identifier = params.unique_identifier;
entry.bound_buffer = registry->GetBoundBuffer();
entry.graphics_info = registry->GetGraphicsInfo();
entry.keys = registry->GetKeys();
entry.bound_samplers = registry->GetBoundSamplers();
entry.bindless_samplers = registry->GetBindlessSamplers();
params.disk_cache.SaveEntry(std::move(entry));
return std::shared_ptr<CachedShader>(new CachedShader(params.host_ptr, params.cpu_addr,
size_in_bytes, std::move(registry),
MakeEntries(ir), std::move(program)));
}
Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code) {
params.disk_cache.SaveRaw(
ShaderDiskCacheRaw(params.unique_identifier, ShaderType::Compute, code));
const std::size_t size_in_bytes = code.size() * sizeof(u64);
ConstBufferLocker locker(Tegra::Engines::ShaderType::Compute,
params.system.GPU().KeplerCompute());
const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, locker);
return std::shared_ptr<CachedShader>(new CachedShader(
params, ShaderType::Compute, GLShader::GetEntries(ir), std::move(code), {}));
auto& engine = params.system.GPU().KeplerCompute();
auto registry = std::make_shared<Registry>(ShaderType::Compute, engine);
const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
const u64 uid = params.unique_identifier;
auto program = BuildShader(params.device, ShaderType::Compute, uid, ir, *registry);
ShaderDiskCacheEntry entry;
entry.type = ShaderType::Compute;
entry.code = std::move(code);
entry.unique_identifier = uid;
entry.bound_buffer = registry->GetBoundBuffer();
entry.compute_info = registry->GetComputeInfo();
entry.keys = registry->GetKeys();
entry.bound_samplers = registry->GetBoundSamplers();
entry.bindless_samplers = registry->GetBindlessSamplers();
params.disk_cache.SaveEntry(std::move(entry));
return std::shared_ptr<CachedShader>(new CachedShader(params.host_ptr, params.cpu_addr,
size_in_bytes, std::move(registry),
MakeEntries(ir), std::move(program)));
}
Shader CachedShader::CreateFromCache(const ShaderParameters& params,
const UnspecializedShader& unspecialized) {
return std::shared_ptr<CachedShader>(new CachedShader(params, unspecialized.type,
unspecialized.entries, unspecialized.code,
unspecialized.code_b));
}
GLuint CachedShader::GetHandle(const ProgramVariant& variant) {
EnsureValidLockerVariant();
const auto [entry, is_cache_miss] = curr_locker_variant->programs.try_emplace(variant);
auto& program = entry->second;
if (!is_cache_miss) {
return program->handle;
}
program = BuildShader(device, unique_identifier, shader_type, code, code_b,
*curr_locker_variant->locker, variant);
disk_cache.SaveUsage(GetUsage(variant, *curr_locker_variant->locker));
LabelGLObject(GL_PROGRAM, program->handle, cpu_addr);
return program->handle;
}
bool CachedShader::EnsureValidLockerVariant() {
const auto previous_variant = curr_locker_variant;
if (curr_locker_variant && !curr_locker_variant->locker->IsConsistent()) {
curr_locker_variant = nullptr;
}
if (!curr_locker_variant) {
for (auto& variant : locker_variants) {
if (variant->locker->IsConsistent()) {
curr_locker_variant = variant.get();
}
}
}
if (!curr_locker_variant) {
auto& new_variant = locker_variants.emplace_back();
new_variant = std::make_unique<LockerVariant>();
new_variant->locker = MakeLocker(system, shader_type);
curr_locker_variant = new_variant.get();
}
return previous_variant == curr_locker_variant;
}
ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant,
const ConstBufferLocker& locker) const {
return ShaderDiskCacheUsage{unique_identifier, variant,
locker.GetBoundBuffer(), locker.GetKeys(),
locker.GetBoundSamplers(), locker.GetBindlessSamplers()};
const PrecompiledShader& precompiled_shader,
std::size_t size_in_bytes) {
return std::shared_ptr<CachedShader>(new CachedShader(
params.host_ptr, params.cpu_addr, size_in_bytes, precompiled_shader.registry,
precompiled_shader.entries, precompiled_shader.program));
}
ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
@@ -431,16 +299,12 @@ ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System&
void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) {
const auto transferable = disk_cache.LoadTransferable();
const std::optional transferable = disk_cache.LoadTransferable();
if (!transferable) {
return;
}
const auto [raws, shader_usages] = *transferable;
if (!GenerateUnspecializedShaders(stop_loading, callback, raws) || stop_loading) {
return;
}
const auto dumps = disk_cache.LoadPrecompiled();
const std::vector gl_cache = disk_cache.LoadPrecompiled();
const auto supported_formats = GetSupportedFormats();
// Track if precompiled cache was altered during loading to know if we have to
@@ -449,77 +313,82 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
// Inform the frontend about shader build initialization
if (callback) {
callback(VideoCore::LoadCallbackStage::Build, 0, shader_usages.size());
callback(VideoCore::LoadCallbackStage::Build, 0, transferable->size());
}
std::mutex mutex;
std::size_t built_shaders = 0; // It doesn't have be atomic since it's used behind a mutex
std::atomic_bool compilation_failed = false;
std::atomic_bool gl_cache_failed = false;
const auto Worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
std::size_t end, const std::vector<ShaderDiskCacheUsage>& shader_usages,
const ShaderDumpsMap& dumps) {
const auto find_precompiled = [&gl_cache](u64 id) {
return std::find_if(gl_cache.begin(), gl_cache.end(),
[id](const auto& entry) { return entry.unique_identifier == id; });
};
const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
std::size_t end) {
context->MakeCurrent();
SCOPE_EXIT({ return context->DoneCurrent(); });
for (std::size_t i = begin; i < end; ++i) {
if (stop_loading || compilation_failed) {
if (stop_loading) {
return;
}
const auto& usage{shader_usages[i]};
const auto& unspecialized{unspecialized_shaders.at(usage.unique_identifier)};
const auto dump{dumps.find(usage)};
const auto& entry = (*transferable)[i];
const u64 uid = entry.unique_identifier;
const auto it = find_precompiled(uid);
const auto precompiled_entry = it != gl_cache.end() ? &*it : nullptr;
CachedProgram shader;
if (dump != dumps.end()) {
// If the shader is dumped, attempt to load it with
shader = GeneratePrecompiledProgram(dump->second, supported_formats);
if (!shader) {
compilation_failed = true;
return;
const bool is_compute = entry.type == ShaderType::Compute;
const u32 main_offset = is_compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
auto registry = MakeRegistry(entry);
const ShaderIR ir(entry.code, main_offset, COMPILER_SETTINGS, *registry);
std::shared_ptr<OGLProgram> program;
if (precompiled_entry) {
// If the shader is precompiled, attempt to load it with
program = GeneratePrecompiledProgram(entry, *precompiled_entry, supported_formats);
if (!program) {
gl_cache_failed = true;
}
}
if (!shader) {
auto locker{MakeLocker(system, unspecialized.type)};
FillLocker(*locker, usage);
shader = BuildShader(device, usage.unique_identifier, unspecialized.type,
unspecialized.code, unspecialized.code_b, *locker,
usage.variant, true);
if (!program) {
// Otherwise compile it from GLSL
program = BuildShader(device, entry.type, uid, ir, *registry, true);
}
PrecompiledShader shader;
shader.program = std::move(program);
shader.registry = std::move(registry);
shader.entries = MakeEntries(ir);
std::scoped_lock lock{mutex};
if (callback) {
callback(VideoCore::LoadCallbackStage::Build, ++built_shaders,
shader_usages.size());
transferable->size());
}
precompiled_programs.emplace(usage, std::move(shader));
// TODO(Rodrigo): Is there a better way to do this?
precompiled_variants[usage.unique_identifier].push_back(
precompiled_programs.find(usage));
runtime_cache.emplace(entry.unique_identifier, std::move(shader));
}
};
const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1ULL)};
const std::size_t bucket_size{shader_usages.size() / num_workers};
const std::size_t bucket_size{transferable->size() / num_workers};
std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers);
std::vector<std::thread> threads(num_workers);
for (std::size_t i = 0; i < num_workers; ++i) {
const bool is_last_worker = i + 1 == num_workers;
const std::size_t start{bucket_size * i};
const std::size_t end{is_last_worker ? shader_usages.size() : start + bucket_size};
const std::size_t end{is_last_worker ? transferable->size() : start + bucket_size};
// On some platforms the shared context has to be created from the GUI thread
contexts[i] = emu_window.CreateSharedContext();
threads[i] = std::thread(Worker, contexts[i].get(), start, end, shader_usages, dumps);
threads[i] = std::thread(worker, contexts[i].get(), start, end);
}
for (auto& thread : threads) {
thread.join();
}
if (compilation_failed) {
if (gl_cache_failed) {
// Invalidate the precompiled cache if a shader dumped shader was rejected
disk_cache.InvalidatePrecompiled();
precompiled_cache_altered = true;
@@ -532,11 +401,12 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
// TODO(Rodrigo): Do state tracking for transferable shaders and do a dummy draw
// before precompiling them
for (std::size_t i = 0; i < shader_usages.size(); ++i) {
const auto& usage{shader_usages[i]};
if (dumps.find(usage) == dumps.end()) {
const auto& program{precompiled_programs.at(usage)};
disk_cache.SaveDump(usage, program->handle);
for (std::size_t i = 0; i < transferable->size(); ++i) {
const u64 id = (*transferable)[i].unique_identifier;
const auto it = find_precompiled(id);
if (it == gl_cache.end()) {
const GLuint program = runtime_cache.at(id).program->handle;
disk_cache.SavePrecompiled(id, program);
precompiled_cache_altered = true;
}
}
@@ -546,84 +416,33 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
}
}
const PrecompiledVariants* ShaderCacheOpenGL::GetPrecompiledVariants(u64 unique_identifier) const {
const auto it = precompiled_variants.find(unique_identifier);
return it == precompiled_variants.end() ? nullptr : &it->second;
}
CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram(
const ShaderDiskCacheDump& dump, const std::unordered_set<GLenum>& supported_formats) {
if (supported_formats.find(dump.binary_format) == supported_formats.end()) {
LOG_INFO(Render_OpenGL, "Precompiled cache entry with unsupported format - removing");
std::shared_ptr<OGLProgram> ShaderCacheOpenGL::GeneratePrecompiledProgram(
const ShaderDiskCacheEntry& entry, const ShaderDiskCachePrecompiled& precompiled_entry,
const std::unordered_set<GLenum>& supported_formats) {
if (supported_formats.find(precompiled_entry.binary_format) == supported_formats.end()) {
LOG_INFO(Render_OpenGL, "Precompiled cache entry with unsupported format, removing");
return {};
}
CachedProgram shader = std::make_shared<OGLProgram>();
shader->handle = glCreateProgram();
glProgramParameteri(shader->handle, GL_PROGRAM_SEPARABLE, GL_TRUE);
glProgramBinary(shader->handle, dump.binary_format, dump.binary.data(),
static_cast<GLsizei>(dump.binary.size()));
auto program = std::make_shared<OGLProgram>();
program->handle = glCreateProgram();
glProgramParameteri(program->handle, GL_PROGRAM_SEPARABLE, GL_TRUE);
glProgramBinary(program->handle, precompiled_entry.binary_format,
precompiled_entry.binary.data(),
static_cast<GLsizei>(precompiled_entry.binary.size()));
GLint link_status{};
glGetProgramiv(shader->handle, GL_LINK_STATUS, &link_status);
GLint link_status;
glGetProgramiv(program->handle, GL_LINK_STATUS, &link_status);
if (link_status == GL_FALSE) {
LOG_INFO(Render_OpenGL, "Precompiled cache rejected by the driver - removing");
LOG_INFO(Render_OpenGL, "Precompiled cache rejected by the driver, removing");
return {};
}
return shader;
}
bool ShaderCacheOpenGL::GenerateUnspecializedShaders(
const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback,
const std::vector<ShaderDiskCacheRaw>& raws) {
if (callback) {
callback(VideoCore::LoadCallbackStage::Decompile, 0, raws.size());
}
for (std::size_t i = 0; i < raws.size(); ++i) {
if (stop_loading) {
return false;
}
const auto& raw{raws[i]};
const u64 unique_identifier{raw.GetUniqueIdentifier()};
const u64 calculated_hash{
GetUniqueIdentifier(raw.GetType(), raw.HasProgramA(), raw.GetCode(), raw.GetCodeB())};
if (unique_identifier != calculated_hash) {
LOG_ERROR(Render_OpenGL,
"Invalid hash in entry={:016x} (obtained hash={:016x}) - "
"removing shader cache",
raw.GetUniqueIdentifier(), calculated_hash);
disk_cache.InvalidateTransferable();
return false;
}
const u32 main_offset =
raw.GetType() == ShaderType::Compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
ConstBufferLocker locker(raw.GetType());
const ShaderIR ir(raw.GetCode(), main_offset, COMPILER_SETTINGS, locker);
// TODO(Rodrigo): Handle VertexA shaders
// std::optional<ShaderIR> ir_b;
// if (raw.HasProgramA()) {
// ir_b.emplace(raw.GetProgramCodeB(), main_offset);
// }
UnspecializedShader unspecialized;
unspecialized.entries = GLShader::GetEntries(ir);
unspecialized.type = raw.GetType();
unspecialized.code = raw.GetCode();
unspecialized.code_b = raw.GetCodeB();
unspecialized_shaders.emplace(raw.GetUniqueIdentifier(), unspecialized);
if (callback) {
callback(VideoCore::LoadCallbackStage::Decompile, i, raws.size());
}
}
return true;
return program;
}
Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
if (!system.GPU().Maxwell3D().dirty.shaders) {
if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) {
return last_shaders[static_cast<std::size_t>(program)];
}
@@ -647,17 +466,17 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
const auto unique_identifier = GetUniqueIdentifier(
GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b);
const auto precompiled_variants = GetPrecompiledVariants(unique_identifier);
const auto cpu_addr{*memory_manager.GpuToCpuAddress(address)};
const ShaderParameters params{system, disk_cache, precompiled_variants, device,
const ShaderParameters params{system, disk_cache, device,
cpu_addr, host_ptr, unique_identifier};
const auto found = unspecialized_shaders.find(unique_identifier);
if (found == unspecialized_shaders.end()) {
const auto found = runtime_cache.find(unique_identifier);
if (found == runtime_cache.end()) {
shader = CachedShader::CreateStageFromMemory(params, program, std::move(code),
std::move(code_b));
} else {
shader = CachedShader::CreateFromCache(params, found->second);
const std::size_t size_in_bytes = code.size() * sizeof(u64);
shader = CachedShader::CreateFromCache(params, found->second, size_in_bytes);
}
Register(shader);
@@ -672,19 +491,19 @@ Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) {
return kernel;
}
// No kernel found - create a new one
// No kernel found, create a new one
auto code{GetShaderCode(memory_manager, code_addr, host_ptr)};
const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code, {})};
const auto precompiled_variants = GetPrecompiledVariants(unique_identifier);
const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)};
const auto cpu_addr{*memory_manager.GpuToCpuAddress(code_addr)};
const ShaderParameters params{system, disk_cache, precompiled_variants, device,
const ShaderParameters params{system, disk_cache, device,
cpu_addr, host_ptr, unique_identifier};
const auto found = unspecialized_shaders.find(unique_identifier);
if (found == unspecialized_shaders.end()) {
const auto found = runtime_cache.find(unique_identifier);
if (found == runtime_cache.end()) {
kernel = CachedShader::CreateKernelFromMemory(params, std::move(code));
} else {
kernel = CachedShader::CreateFromCache(params, found->second);
const std::size_t size_in_bytes = code.size() * sizeof(u64);
kernel = CachedShader::CreateFromCache(params, found->second, size_in_bytes);
}
Register(kernel);
@@ -22,7 +22,7 @@
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
#include "video_core/shader/const_buffer_locker.h"
#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
namespace Core {
@@ -41,22 +41,17 @@ class RasterizerOpenGL;
struct UnspecializedShader;
using Shader = std::shared_ptr<CachedShader>;
using CachedProgram = std::shared_ptr<OGLProgram>;
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using PrecompiledPrograms = std::unordered_map<ShaderDiskCacheUsage, CachedProgram>;
using PrecompiledVariants = std::vector<PrecompiledPrograms::iterator>;
struct UnspecializedShader {
GLShader::ShaderEntries entries;
Tegra::Engines::ShaderType type;
ProgramCode code;
ProgramCode code_b;
struct PrecompiledShader {
std::shared_ptr<OGLProgram> program;
std::shared_ptr<VideoCommon::Shader::Registry> registry;
ShaderEntries entries;
};
struct ShaderParameters {
Core::System& system;
ShaderDiskCacheOpenGL& disk_cache;
const PrecompiledVariants* precompiled_variants;
const Device& device;
VAddr cpu_addr;
u8* host_ptr;
@@ -65,61 +60,45 @@ struct ShaderParameters {
class CachedShader final : public RasterizerCacheObject {
public:
~CachedShader();
/// Gets the GL program handle for the shader
GLuint GetHandle() const;
/// Returns the guest CPU address of the shader
VAddr GetCpuAddr() const override {
return cpu_addr;
}
/// Returns the size in bytes of the shader
std::size_t GetSizeInBytes() const override {
return size_in_bytes;
}
/// Gets the shader entries for the shader
const ShaderEntries& GetEntries() const {
return entries;
}
static Shader CreateStageFromMemory(const ShaderParameters& params,
Maxwell::ShaderProgram program_type,
ProgramCode program_code, ProgramCode program_code_b);
static Shader CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code);
static Shader CreateFromCache(const ShaderParameters& params,
const UnspecializedShader& unspecialized);
VAddr GetCpuAddr() const override {
return cpu_addr;
}
std::size_t GetSizeInBytes() const override {
return code.size() * sizeof(u64);
}
/// Gets the shader entries for the shader
const GLShader::ShaderEntries& GetShaderEntries() const {
return entries;
}
/// Gets the GL program handle for the shader
GLuint GetHandle(const ProgramVariant& variant);
const PrecompiledShader& precompiled_shader,
std::size_t size_in_bytes);
private:
struct LockerVariant {
std::unique_ptr<VideoCommon::Shader::ConstBufferLocker> locker;
std::unordered_map<ProgramVariant, CachedProgram> programs;
};
explicit CachedShader(const u8* host_ptr, VAddr cpu_addr, std::size_t size_in_bytes,
std::shared_ptr<VideoCommon::Shader::Registry> registry,
ShaderEntries entries, std::shared_ptr<OGLProgram> program);
explicit CachedShader(const ShaderParameters& params, Tegra::Engines::ShaderType shader_type,
GLShader::ShaderEntries entries, ProgramCode program_code,
ProgramCode program_code_b);
bool EnsureValidLockerVariant();
ShaderDiskCacheUsage GetUsage(const ProgramVariant& variant,
const VideoCommon::Shader::ConstBufferLocker& locker) const;
Core::System& system;
ShaderDiskCacheOpenGL& disk_cache;
const Device& device;
VAddr cpu_addr{};
u64 unique_identifier{};
Tegra::Engines::ShaderType shader_type{};
GLShader::ShaderEntries entries;
ProgramCode code;
ProgramCode code_b;
LockerVariant* curr_locker_variant = nullptr;
std::vector<std::unique_ptr<LockerVariant>> locker_variants;
std::shared_ptr<VideoCommon::Shader::Registry> registry;
ShaderEntries entries;
VAddr cpu_addr = 0;
std::size_t size_in_bytes = 0;
std::shared_ptr<OGLProgram> program;
};
class ShaderCacheOpenGL final : public RasterizerCache<Shader> {
@@ -142,25 +121,15 @@ protected:
void FlushObjectInner(const Shader& object) override {}
private:
bool GenerateUnspecializedShaders(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback,
const std::vector<ShaderDiskCacheRaw>& raws);
CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump,
const std::unordered_set<GLenum>& supported_formats);
const PrecompiledVariants* GetPrecompiledVariants(u64 unique_identifier) const;
std::shared_ptr<OGLProgram> GeneratePrecompiledProgram(
const ShaderDiskCacheEntry& entry, const ShaderDiskCachePrecompiled& precompiled_entry,
const std::unordered_set<GLenum>& supported_formats);
Core::System& system;
Core::Frontend::EmuWindow& emu_window;
const Device& device;
ShaderDiskCacheOpenGL disk_cache;
PrecompiledPrograms precompiled_programs;
std::unordered_map<u64, PrecompiledVariants> precompiled_variants;
std::unordered_map<u64, UnspecializedShader> unspecialized_shaders;
std::unordered_map<u64, PrecompiledShader> runtime_cache;
std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
};
@@ -23,8 +23,9 @@
#include "video_core/shader/ast.h"
#include "video_core/shader/node.h"
#include "video_core/shader/shader_ir.h"
#include "video_core/shader/transform_feedback.h"
namespace OpenGL::GLShader {
namespace OpenGL {
namespace {
@@ -36,6 +37,8 @@ using Tegra::Shader::IpaInterpMode;
using Tegra::Shader::IpaMode;
using Tegra::Shader::IpaSampleMode;
using Tegra::Shader::Register;
using VideoCommon::Shader::BuildTransformFeedback;
using VideoCommon::Shader::Registry;
using namespace std::string_literals;
using namespace VideoCommon::Shader;
@@ -48,6 +51,11 @@ class ExprDecompiler;
enum class Type { Void, Bool, Bool2, Float, Int, Uint, HalfFloat };
constexpr std::array FLOAT_TYPES{"float", "vec2", "vec3", "vec4"};
constexpr std::string_view INPUT_ATTRIBUTE_NAME = "in_attr";
constexpr std::string_view OUTPUT_ATTRIBUTE_NAME = "out_attr";
struct TextureOffset {};
struct TextureDerivates {};
using TextureArgument = std::pair<Type, Node>;
@@ -56,6 +64,25 @@ using TextureIR = std::variant<TextureOffset, TextureDerivates, TextureArgument>
constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float));
constexpr std::string_view CommonDeclarations = R"(#define ftoi floatBitsToInt
#define ftou floatBitsToUint
#define itof intBitsToFloat
#define utof uintBitsToFloat
bvec2 HalfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{
bvec2 is_nan1 = isnan(pair1);
bvec2 is_nan2 = isnan(pair2);
return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || is_nan2.y);
}}
const float fswzadd_modifiers_a[] = float[4](-1.0f, 1.0f, -1.0f, 0.0f );
const float fswzadd_modifiers_b[] = float[4](-1.0f, -1.0f, 1.0f, -1.0f );
layout (std140, binding = {}) uniform vs_config {{
float y_direction;
}};
)";
class ShaderWriter final {
public:
void AddExpression(std::string_view text) {
@@ -269,12 +296,41 @@ const char* GetImageTypeDeclaration(Tegra::Shader::ImageType image_type) {
}
}
/// Describes primitive behavior on geometry shaders
std::pair<const char*, u32> GetPrimitiveDescription(Maxwell::PrimitiveTopology topology) {
switch (topology) {
case Maxwell::PrimitiveTopology::Points:
return {"points", 1};
case Maxwell::PrimitiveTopology::Lines:
case Maxwell::PrimitiveTopology::LineStrip:
return {"lines", 2};
case Maxwell::PrimitiveTopology::LinesAdjacency:
case Maxwell::PrimitiveTopology::LineStripAdjacency:
return {"lines_adjacency", 4};
case Maxwell::PrimitiveTopology::Triangles:
case Maxwell::PrimitiveTopology::TriangleStrip:
case Maxwell::PrimitiveTopology::TriangleFan:
return {"triangles", 3};
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
return {"triangles_adjacency", 6};
default:
UNIMPLEMENTED_MSG("topology={}", static_cast<int>(topology));
return {"points", 1};
}
}
/// Generates code to use for a swizzle operation.
constexpr const char* GetSwizzle(u32 element) {
constexpr const char* GetSwizzle(std::size_t element) {
constexpr std::array swizzle = {".x", ".y", ".z", ".w"};
return swizzle.at(element);
}
constexpr const char* GetColorSwizzle(std::size_t element) {
constexpr std::array swizzle = {".r", ".g", ".b", ".a"};
return swizzle.at(element);
}
/// Translate topology
std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
switch (topology) {
@@ -310,10 +366,19 @@ constexpr bool IsGenericAttribute(Attribute::Index index) {
return index >= Attribute::Index::Attribute_0 && index <= Attribute::Index::Attribute_31;
}
constexpr bool IsLegacyTexCoord(Attribute::Index index) {
return static_cast<int>(index) >= static_cast<int>(Attribute::Index::TexCoord_0) &&
static_cast<int>(index) <= static_cast<int>(Attribute::Index::TexCoord_7);
}
constexpr Attribute::Index ToGenericAttribute(u64 value) {
return static_cast<Attribute::Index>(value + static_cast<u64>(Attribute::Index::Attribute_0));
}
constexpr int GetLegacyTexCoordIndex(Attribute::Index index) {
return static_cast<int>(index) - static_cast<int>(Attribute::Index::TexCoord_0);
}
u32 GetGenericAttributeIndex(Attribute::Index index) {
ASSERT(IsGenericAttribute(index));
return static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
@@ -337,15 +402,66 @@ std::string FlowStackTopName(MetaStackClass stack) {
return fmt::format("{}_flow_stack_top", GetFlowStackPrefix(stack));
}
[[deprecated]] constexpr bool IsVertexShader(ShaderType stage) {
return stage == ShaderType::Vertex;
}
struct GenericVaryingDescription {
std::string name;
u8 first_element = 0;
bool is_scalar = false;
};
class GLSLDecompiler final {
public:
explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderType stage,
std::string suffix)
: device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {}
explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry,
ShaderType stage, std::string_view identifier, std::string_view suffix)
: device{device}, ir{ir}, registry{registry}, stage{stage},
identifier{identifier}, suffix{suffix}, header{ir.GetHeader()} {
if (stage != ShaderType::Compute) {
transform_feedback = BuildTransformFeedback(registry.GetGraphicsInfo());
}
}
void Decompile() {
DeclareHeader();
DeclareVertex();
DeclareGeometry();
DeclareFragment();
DeclareCompute();
DeclareInputAttributes();
DeclareOutputAttributes();
DeclareImages();
DeclareSamplers();
DeclareGlobalMemory();
DeclareConstantBuffers();
DeclareLocalMemory();
DeclareRegisters();
DeclarePredicates();
DeclareInternalFlags();
DeclareCustomVariables();
DeclarePhysicalAttributeReader();
code.AddLine("void main() {{");
++code.scope;
if (stage == ShaderType::Vertex) {
code.AddLine("gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);");
}
if (ir.IsDecompiled()) {
DecompileAST();
} else {
DecompileBranchMode();
}
--code.scope;
code.AddLine("}}");
}
std::string GetResult() {
return code.GetResult();
}
private:
friend class ASTDecompiler;
friend class ExprDecompiler;
void DecompileBranchMode() {
// VM's program counter
@@ -387,46 +503,40 @@ public:
void DecompileAST();
void Decompile() {
DeclareVertex();
DeclareGeometry();
DeclareRegisters();
DeclareCustomVariables();
DeclarePredicates();
DeclareLocalMemory();
DeclareInternalFlags();
DeclareInputAttributes();
DeclareOutputAttributes();
DeclareConstantBuffers();
DeclareGlobalMemory();
DeclareSamplers();
DeclareImages();
DeclarePhysicalAttributeReader();
code.AddLine("void execute_{}() {{", suffix);
++code.scope;
if (ir.IsDecompiled()) {
DecompileAST();
} else {
DecompileBranchMode();
void DeclareHeader() {
if (!identifier.empty()) {
code.AddLine("// {}", identifier);
}
code.AddLine("#version 440 {}", ir.UsesLegacyVaryings() ? "compatibility" : "core");
code.AddLine("#extension GL_ARB_separate_shader_objects : enable");
if (device.HasShaderBallot()) {
code.AddLine("#extension GL_ARB_shader_ballot : require");
}
if (device.HasVertexViewportLayer()) {
code.AddLine("#extension GL_ARB_shader_viewport_layer_array : require");
}
if (device.HasImageLoadFormatted()) {
code.AddLine("#extension GL_EXT_shader_image_load_formatted : require");
}
if (device.HasWarpIntrinsics()) {
code.AddLine("#extension GL_NV_gpu_shader5 : require");
code.AddLine("#extension GL_NV_shader_thread_group : require");
code.AddLine("#extension GL_NV_shader_thread_shuffle : require");
}
// This pragma stops Nvidia's driver from over optimizing math (probably using fp16
// operations) on places where we don't want to.
// Thanks to Ryujinx for finding this workaround.
code.AddLine("#pragma optionNV(fastmath off)");
--code.scope;
code.AddLine("}}");
code.AddNewLine();
code.AddLine(CommonDeclarations, EmulationUniformBlockBinding);
}
std::string GetResult() {
return code.GetResult();
}
private:
friend class ASTDecompiler;
friend class ExprDecompiler;
void DeclareVertex() {
if (!IsVertexShader(stage))
if (stage != ShaderType::Vertex) {
return;
}
DeclareVertexRedeclarations();
}
@@ -436,9 +546,15 @@ private:
return;
}
const auto& info = registry.GetGraphicsInfo();
const auto input_topology = info.primitive_topology;
const auto [glsl_topology, max_vertices] = GetPrimitiveDescription(input_topology);
max_input_vertices = max_vertices;
code.AddLine("layout ({}) in;", glsl_topology);
const auto topology = GetTopologyName(header.common3.output_topology);
const auto max_vertices = header.common4.max_output_vertices.Value();
code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices);
const auto max_output_vertices = header.common4.max_output_vertices.Value();
code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_output_vertices);
code.AddNewLine();
code.AddLine("in gl_PerVertex {{");
@@ -450,11 +566,50 @@ private:
DeclareVertexRedeclarations();
}
void DeclareFragment() {
if (stage != ShaderType::Fragment) {
return;
}
if (ir.UsesLegacyVaryings()) {
code.AddLine("in gl_PerFragment {{");
++code.scope;
code.AddLine("vec4 gl_TexCoord[8];");
code.AddLine("vec4 gl_Color;");
code.AddLine("vec4 gl_SecondaryColor;");
--code.scope;
code.AddLine("}};");
}
for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) {
code.AddLine("layout (location = {}) out vec4 frag_color{};", rt, rt);
}
}
void DeclareCompute() {
if (stage != ShaderType::Compute) {
return;
}
const auto& info = registry.GetComputeInfo();
if (const u32 size = info.shared_memory_size_in_words; size > 0) {
code.AddLine("shared uint smem[{}];", size);
code.AddNewLine();
}
code.AddLine("layout (local_size_x = {}, local_size_y = {}, local_size_z = {}) in;",
info.workgroup_size[0], info.workgroup_size[1], info.workgroup_size[2]);
code.AddNewLine();
}
void DeclareVertexRedeclarations() {
code.AddLine("out gl_PerVertex {{");
++code.scope;
code.AddLine("vec4 gl_Position;");
auto pos_xfb = GetTransformFeedbackDecoration(Attribute::Index::Position);
if (!pos_xfb.empty()) {
pos_xfb = fmt::format("layout ({}) ", pos_xfb);
}
const char* pos_type =
FLOAT_TYPES.at(GetNumComponents(Attribute::Index::Position).value_or(4) - 1);
code.AddLine("{}{} gl_Position;", pos_xfb, pos_type);
for (const auto attribute : ir.GetOutputAttributes()) {
if (attribute == Attribute::Index::ClipDistances0123 ||
@@ -463,14 +618,14 @@ private:
break;
}
}
if (!IsVertexShader(stage) || device.HasVertexViewportLayer()) {
if (stage != ShaderType::Vertex || device.HasVertexViewportLayer()) {
if (ir.UsesLayer()) {
code.AddLine("int gl_Layer;");
}
if (ir.UsesViewportIndex()) {
code.AddLine("int gl_ViewportIndex;");
}
} else if ((ir.UsesLayer() || ir.UsesViewportIndex()) && IsVertexShader(stage) &&
} else if ((ir.UsesLayer() || ir.UsesViewportIndex()) && stage == ShaderType::Vertex &&
!device.HasVertexViewportLayer()) {
LOG_ERROR(
Render_OpenGL,
@@ -481,12 +636,12 @@ private:
code.AddLine("float gl_PointSize;");
}
if (ir.UsesInstanceId()) {
code.AddLine("int gl_InstanceID;");
}
if (ir.UsesVertexId()) {
code.AddLine("int gl_VertexID;");
if (ir.UsesLegacyVaryings()) {
code.AddLine("vec4 gl_TexCoord[8];");
code.AddLine("vec4 gl_FrontColor;");
code.AddLine("vec4 gl_FrontSecondaryColor;");
code.AddLine("vec4 gl_BackColor;");
code.AddLine("vec4 gl_BackSecondaryColor;");
}
--code.scope;
@@ -525,18 +680,16 @@ private:
}
void DeclareLocalMemory() {
u64 local_memory_size = 0;
if (stage == ShaderType::Compute) {
code.AddLine("#ifdef LOCAL_MEMORY_SIZE");
code.AddLine("uint {}[LOCAL_MEMORY_SIZE];", GetLocalMemory());
code.AddLine("#endif");
return;
local_memory_size = registry.GetComputeInfo().local_memory_size_in_words * 4ULL;
} else {
local_memory_size = header.GetLocalMemorySize();
}
const u64 local_memory_size = header.GetLocalMemorySize();
if (local_memory_size == 0) {
return;
}
const auto element_count = Common::AlignUp(local_memory_size, 4) / 4;
const u64 element_count = Common::AlignUp(local_memory_size, 4) / 4;
code.AddLine("uint {}[{}];", GetLocalMemory(), element_count);
code.AddNewLine();
}
@@ -589,7 +742,7 @@ private:
void DeclareInputAttribute(Attribute::Index index, bool skip_unused) {
const u32 location{GetGenericAttributeIndex(index)};
std::string name{GetInputAttribute(index)};
std::string name{GetGenericInputAttribute(index)};
if (stage == ShaderType::Geometry) {
name = "gs_" + name + "[]";
}
@@ -626,9 +779,59 @@ private:
}
}
std::optional<std::size_t> GetNumComponents(Attribute::Index index, u8 element = 0) const {
const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element);
const auto it = transform_feedback.find(location);
if (it == transform_feedback.end()) {
return {};
}
return it->second.components;
}
std::string GetTransformFeedbackDecoration(Attribute::Index index, u8 element = 0) const {
const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element);
const auto it = transform_feedback.find(location);
if (it == transform_feedback.end()) {
return {};
}
const VaryingTFB& tfb = it->second;
return fmt::format("xfb_buffer = {}, xfb_offset = {}, xfb_stride = {}", tfb.buffer,
tfb.offset, tfb.stride);
}
void DeclareOutputAttribute(Attribute::Index index) {
const u32 location{GetGenericAttributeIndex(index)};
code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index));
static constexpr std::string_view swizzle = "xyzw";
u8 element = 0;
while (element < 4) {
auto xfb = GetTransformFeedbackDecoration(index, element);
if (!xfb.empty()) {
xfb = fmt::format(", {}", xfb);
}
const std::size_t remainder = 4 - element;
const std::size_t num_components = GetNumComponents(index, element).value_or(remainder);
const char* const type = FLOAT_TYPES.at(num_components - 1);
const u32 location = GetGenericAttributeIndex(index);
GenericVaryingDescription description;
description.first_element = static_cast<u8>(element);
description.is_scalar = num_components == 1;
description.name = AppendSuffix(location, OUTPUT_ATTRIBUTE_NAME);
if (element != 0 || num_components != 4) {
const std::string_view name_swizzle = swizzle.substr(element, num_components);
description.name = fmt::format("{}_{}", description.name, name_swizzle);
}
for (std::size_t i = 0; i < num_components; ++i) {
const u8 offset = static_cast<u8>(location * 4 + element + i);
varying_description.insert({offset, description});
}
code.AddLine("layout (location = {}, component = {}{}) out {} {};", location, element,
xfb, type, description.name);
element = static_cast<u8>(static_cast<std::size_t>(element) + num_components);
}
}
void DeclareConstantBuffers() {
@@ -925,7 +1128,8 @@ private:
// TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
// set an 0x80000000 index for those and the shader fails to build. Find out why
// this happens and what's its intent.
return fmt::format("gs_{}[{} % MAX_VERTEX_INPUT]", name, Visit(buffer).AsUint());
return fmt::format("gs_{}[{} % {}]", name, Visit(buffer).AsUint(),
max_input_vertices.value());
}
return std::string(name);
};
@@ -943,6 +1147,10 @@ private:
default:
UNREACHABLE();
}
case Attribute::Index::FrontColor:
return {"gl_Color"s + GetSwizzle(element), Type::Float};
case Attribute::Index::FrontSecondaryColor:
return {"gl_SecondaryColor"s + GetSwizzle(element), Type::Float};
case Attribute::Index::PointCoord:
switch (element) {
case 0:
@@ -959,7 +1167,7 @@ private:
// TODO(Subv): Find out what the values are for the first two elements when inside a
// vertex shader, and what's the value of the fourth element when inside a Tess Eval
// shader.
ASSERT(IsVertexShader(stage));
ASSERT(stage == ShaderType::Vertex);
switch (element) {
case 2:
// Config pack's first value is instance_id.
@@ -980,7 +1188,13 @@ private:
return {"0", Type::Int};
default:
if (IsGenericAttribute(attribute)) {
return {GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element),
return {GeometryPass(GetGenericInputAttribute(attribute)) + GetSwizzle(element),
Type::Float};
}
if (IsLegacyTexCoord(attribute)) {
UNIMPLEMENTED_IF(stage == ShaderType::Geometry);
return {fmt::format("gl_TexCoord[{}]{}", GetLegacyTexCoordIndex(attribute),
GetSwizzle(element)),
Type::Float};
}
break;
@@ -1021,21 +1235,22 @@ private:
}
std::optional<Expression> GetOutputAttribute(const AbufNode* abuf) {
const u32 element = abuf->GetElement();
switch (const auto attribute = abuf->GetIndex()) {
case Attribute::Index::Position:
return {{"gl_Position"s + GetSwizzle(abuf->GetElement()), Type::Float}};
return {{"gl_Position"s + GetSwizzle(element), Type::Float}};
case Attribute::Index::LayerViewportPointSize:
switch (abuf->GetElement()) {
switch (element) {
case 0:
UNIMPLEMENTED();
return {};
case 1:
if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) {
return {};
}
return {{"gl_Layer", Type::Int}};
case 2:
if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) {
return {};
}
return {{"gl_ViewportIndex", Type::Int}};
@@ -1043,14 +1258,26 @@ private:
return {{"gl_PointSize", Type::Float}};
}
return {};
case Attribute::Index::FrontColor:
return {{"gl_FrontColor"s + GetSwizzle(element), Type::Float}};
case Attribute::Index::FrontSecondaryColor:
return {{"gl_FrontSecondaryColor"s + GetSwizzle(element), Type::Float}};
case Attribute::Index::BackColor:
return {{"gl_BackColor"s + GetSwizzle(element), Type::Float}};
case Attribute::Index::BackSecondaryColor:
return {{"gl_BackSecondaryColor"s + GetSwizzle(element), Type::Float}};
case Attribute::Index::ClipDistances0123:
return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement()), Type::Float}};
return {{fmt::format("gl_ClipDistance[{}]", element), Type::Float}};
case Attribute::Index::ClipDistances4567:
return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float}};
return {{fmt::format("gl_ClipDistance[{}]", element + 4), Type::Float}};
default:
if (IsGenericAttribute(attribute)) {
return {
{GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()), Type::Float}};
return {{GetGenericOutputAttribute(attribute, element), Type::Float}};
}
if (IsLegacyTexCoord(attribute)) {
return {{fmt::format("gl_TexCoord[{}]{}", GetLegacyTexCoordIndex(attribute),
GetSwizzle(element)),
Type::Float}};
}
UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute));
return {};
@@ -1822,16 +2049,19 @@ private:
expr += GetSampler(meta->sampler);
expr += ", ";
expr += constructors.at(operation.GetOperandsCount() - 1);
expr += constructors.at(operation.GetOperandsCount() + (meta->array ? 1 : 0) - 1);
expr += '(';
for (std::size_t i = 0; i < count; ++i) {
expr += VisitOperand(operation, i).AsInt();
const std::size_t next = i + 1;
if (next == count)
expr += ')';
else if (next < count)
if (i > 0) {
expr += ", ";
}
expr += VisitOperand(operation, i).AsInt();
}
if (meta->array) {
expr += ", ";
expr += Visit(meta->array).AsInt();
}
expr += ')';
if (meta->lod && !meta->sampler.IsBuffer()) {
expr += ", ";
@@ -1945,7 +2175,7 @@ private:
// TODO(Subv): Figure out how dual-source blending is configured in the Switch.
for (u32 component = 0; component < 4; ++component) {
if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
code.AddLine("FragColor{}[{}] = {};", render_target, component,
code.AddLine("frag_color{}{} = {};", render_target, GetColorSwizzle(component),
SafeGetRegister(current_reg).AsFloat());
++current_reg;
}
@@ -2261,27 +2491,34 @@ private:
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));
std::string GetRegister(u32 index) const {
return GetDeclarationWithSuffix(index, "gpr");
return AppendSuffix(index, "gpr");
}
std::string GetCustomVariable(u32 index) const {
return GetDeclarationWithSuffix(index, "custom_var");
return AppendSuffix(index, "custom_var");
}
std::string GetPredicate(Tegra::Shader::Pred pred) const {
return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred");
return AppendSuffix(static_cast<u32>(pred), "pred");
}
std::string GetInputAttribute(Attribute::Index attribute) const {
return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "input_attr");
std::string GetGenericInputAttribute(Attribute::Index attribute) const {
return AppendSuffix(GetGenericAttributeIndex(attribute), INPUT_ATTRIBUTE_NAME);
}
std::string GetOutputAttribute(Attribute::Index attribute) const {
return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "output_attr");
std::unordered_map<u8, GenericVaryingDescription> varying_description;
std::string GetGenericOutputAttribute(Attribute::Index attribute, std::size_t element) const {
const u8 offset = static_cast<u8>(GetGenericAttributeIndex(attribute) * 4 + element);
const auto& description = varying_description.at(offset);
if (description.is_scalar) {
return description.name;
}
return fmt::format("{}[{}]", description.name, element - description.first_element);
}
std::string GetConstBuffer(u32 index) const {
return GetDeclarationWithSuffix(index, "cbuf");
return AppendSuffix(index, "cbuf");
}
std::string GetGlobalMemory(const GlobalMemoryBase& descriptor) const {
@@ -2294,11 +2531,15 @@ private:
}
std::string GetConstBufferBlock(u32 index) const {
return GetDeclarationWithSuffix(index, "cbuf_block");
return AppendSuffix(index, "cbuf_block");
}
std::string GetLocalMemory() const {
return "lmem_" + suffix;
if (suffix.empty()) {
return "lmem";
} else {
return "lmem_" + std::string{suffix};
}
}
std::string GetInternalFlag(InternalFlag flag) const {
@@ -2307,23 +2548,31 @@ private:
const auto index = static_cast<u32>(flag);
ASSERT(index < static_cast<u32>(InternalFlag::Amount));
return fmt::format("{}_{}", InternalFlagNames[index], suffix);
if (suffix.empty()) {
return InternalFlagNames[index];
} else {
return fmt::format("{}_{}", InternalFlagNames[index], suffix);
}
}
std::string GetSampler(const Sampler& sampler) const {
return GetDeclarationWithSuffix(static_cast<u32>(sampler.GetIndex()), "sampler");
return AppendSuffix(static_cast<u32>(sampler.GetIndex()), "sampler");
}
std::string GetImage(const Image& image) const {
return GetDeclarationWithSuffix(static_cast<u32>(image.GetIndex()), "image");
return AppendSuffix(static_cast<u32>(image.GetIndex()), "image");
}
std::string GetDeclarationWithSuffix(u32 index, std::string_view name) const {
return fmt::format("{}_{}_{}", name, index, suffix);
std::string AppendSuffix(u32 index, std::string_view name) const {
if (suffix.empty()) {
return fmt::format("{}{}", name, index);
} else {
return fmt::format("{}{}_{}", name, index, suffix);
}
}
u32 GetNumPhysicalInputAttributes() const {
return IsVertexShader(stage) ? GetNumPhysicalAttributes() : GetNumPhysicalVaryings();
return stage == ShaderType::Vertex ? GetNumPhysicalAttributes() : GetNumPhysicalVaryings();
}
u32 GetNumPhysicalAttributes() const {
@@ -2334,17 +2583,31 @@ private:
return std::min<u32>(device.GetMaxVaryings(), Maxwell::NumVaryings);
}
bool IsRenderTargetEnabled(u32 render_target) const {
for (u32 component = 0; component < 4; ++component) {
if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
return true;
}
}
return false;
}
const Device& device;
const ShaderIR& ir;
const Registry& registry;
const ShaderType stage;
const std::string suffix;
const std::string_view identifier;
const std::string_view suffix;
const Header header;
std::unordered_map<u8, VaryingTFB> transform_feedback;
ShaderWriter code;
std::optional<u32> max_input_vertices;
};
std::string GetFlowVariable(u32 i) {
return fmt::format("flow_var_{}", i);
std::string GetFlowVariable(u32 index) {
return fmt::format("flow_var{}", index);
}
class ExprDecompiler {
@@ -2531,7 +2794,7 @@ void GLSLDecompiler::DecompileAST() {
} // Anonymous namespace
ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir) {
ShaderEntries MakeEntries(const VideoCommon::Shader::ShaderIR& ir) {
ShaderEntries entries;
for (const auto& cbuf : ir.GetConstantBuffers()) {
entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(),
@@ -2547,33 +2810,20 @@ ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir) {
for (const auto& image : ir.GetImages()) {
entries.images.emplace_back(image);
}
entries.clip_distances = ir.GetClipDistances();
const auto clip_distances = ir.GetClipDistances();
for (std::size_t i = 0; i < std::size(clip_distances); ++i) {
entries.clip_distances = (clip_distances[i] ? 1U : 0U) << i;
}
entries.shader_length = ir.GetLength();
return entries;
}
std::string GetCommonDeclarations() {
return R"(#define ftoi floatBitsToInt
#define ftou floatBitsToUint
#define itof intBitsToFloat
#define utof uintBitsToFloat
bvec2 HalfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {
bvec2 is_nan1 = isnan(pair1);
bvec2 is_nan2 = isnan(pair2);
return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || is_nan2.y);
}
const float fswzadd_modifiers_a[] = float[4](-1.0f, 1.0f, -1.0f, 0.0f );
const float fswzadd_modifiers_b[] = float[4](-1.0f, -1.0f, 1.0f, -1.0f );
)";
}
std::string Decompile(const Device& device, const ShaderIR& ir, ShaderType stage,
const std::string& suffix) {
GLSLDecompiler decompiler(device, ir, stage, suffix);
std::string DecompileShader(const Device& device, const ShaderIR& ir, const Registry& registry,
ShaderType stage, std::string_view identifier,
std::string_view suffix) {
GLSLDecompiler decompiler(device, ir, registry, stage, identifier, suffix);
decompiler.Decompile();
return decompiler.GetResult();
}
} // namespace OpenGL::GLShader
} // namespace OpenGL
@@ -6,22 +6,18 @@
#include <array>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "common/common_types.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/shader_type.h"
#include "video_core/shader/registry.h"
#include "video_core/shader/shader_ir.h"
namespace VideoCommon::Shader {
class ShaderIR;
}
namespace OpenGL {
class Device;
}
namespace OpenGL::GLShader {
class Device;
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using SamplerEntry = VideoCommon::Shader::Sampler;
@@ -74,15 +70,15 @@ struct ShaderEntries {
std::vector<GlobalMemoryEntry> global_memory_entries;
std::vector<SamplerEntry> samplers;
std::vector<ImageEntry> images;
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
u32 clip_distances{};
std::size_t shader_length{};
};
ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir);
ShaderEntries MakeEntries(const VideoCommon::Shader::ShaderIR& ir);
std::string GetCommonDeclarations();
std::string DecompileShader(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
const VideoCommon::Shader::Registry& registry,
Tegra::Engines::ShaderType stage, std::string_view identifier,
std::string_view suffix = {});
std::string Decompile(const Device& device, const VideoCommon::Shader::ShaderIR& ir,
Tegra::Engines::ShaderType stage, const std::string& suffix);
} // namespace OpenGL::GLShader
} // namespace OpenGL
@@ -31,32 +31,24 @@ namespace {
using ShaderCacheVersionHash = std::array<u8, 64>;
enum class TransferableEntryKind : u32 {
Raw,
Usage,
};
struct ConstBufferKey {
u32 cbuf{};
u32 offset{};
u32 value{};
u32 cbuf = 0;
u32 offset = 0;
u32 value = 0;
};
struct BoundSamplerKey {
u32 offset{};
Tegra::Engines::SamplerDescriptor sampler{};
u32 offset = 0;
Tegra::Engines::SamplerDescriptor sampler;
};
struct BindlessSamplerKey {
u32 cbuf{};
u32 offset{};
Tegra::Engines::SamplerDescriptor sampler{};
u32 cbuf = 0;
u32 offset = 0;
Tegra::Engines::SamplerDescriptor sampler;
};
constexpr u32 NativeVersion = 12;
// Making sure sizes doesn't change by accident
static_assert(sizeof(ProgramVariant) == 20);
constexpr u32 NativeVersion = 20;
ShaderCacheVersionHash GetShaderCacheVersionHash() {
ShaderCacheVersionHash hash{};
@@ -67,61 +59,124 @@ ShaderCacheVersionHash GetShaderCacheVersionHash() {
} // Anonymous namespace
ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, ShaderType type, ProgramCode code,
ProgramCode code_b)
: unique_identifier{unique_identifier}, type{type}, code{std::move(code)}, code_b{std::move(
code_b)} {}
ShaderDiskCacheEntry::ShaderDiskCacheEntry() = default;
ShaderDiskCacheRaw::ShaderDiskCacheRaw() = default;
ShaderDiskCacheEntry::~ShaderDiskCacheEntry() = default;
ShaderDiskCacheRaw::~ShaderDiskCacheRaw() = default;
bool ShaderDiskCacheRaw::Load(FileUtil::IOFile& file) {
if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64) ||
file.ReadBytes(&type, sizeof(u32)) != sizeof(u32)) {
bool ShaderDiskCacheEntry::Load(FileUtil::IOFile& file) {
if (file.ReadBytes(&type, sizeof(u32)) != sizeof(u32)) {
return false;
}
u32 code_size{};
u32 code_size_b{};
u32 code_size;
u32 code_size_b;
if (file.ReadBytes(&code_size, sizeof(u32)) != sizeof(u32) ||
file.ReadBytes(&code_size_b, sizeof(u32)) != sizeof(u32)) {
return false;
}
code.resize(code_size);
code_b.resize(code_size_b);
if (file.ReadArray(code.data(), code_size) != code_size)
if (file.ReadArray(code.data(), code_size) != code_size) {
return false;
}
if (HasProgramA() && file.ReadArray(code_b.data(), code_size_b) != code_size_b) {
return false;
}
u8 is_texture_handler_size_known;
u32 texture_handler_size_value;
u32 num_keys;
u32 num_bound_samplers;
u32 num_bindless_samplers;
if (file.ReadArray(&unique_identifier, 1) != 1 || file.ReadArray(&bound_buffer, 1) != 1 ||
file.ReadArray(&is_texture_handler_size_known, 1) != 1 ||
file.ReadArray(&texture_handler_size_value, 1) != 1 ||
file.ReadArray(&graphics_info, 1) != 1 || file.ReadArray(&compute_info, 1) != 1 ||
file.ReadArray(&num_keys, 1) != 1 || file.ReadArray(&num_bound_samplers, 1) != 1 ||
file.ReadArray(&num_bindless_samplers, 1) != 1) {
return false;
}
if (is_texture_handler_size_known) {
texture_handler_size = texture_handler_size_value;
}
std::vector<ConstBufferKey> flat_keys(num_keys);
std::vector<BoundSamplerKey> flat_bound_samplers(num_bound_samplers);
std::vector<BindlessSamplerKey> flat_bindless_samplers(num_bindless_samplers);
if (file.ReadArray(flat_keys.data(), flat_keys.size()) != flat_keys.size() ||
file.ReadArray(flat_bound_samplers.data(), flat_bound_samplers.size()) !=
flat_bound_samplers.size() ||
file.ReadArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) !=
flat_bindless_samplers.size()) {
return false;
}
for (const auto& key : flat_keys) {
keys.insert({{key.cbuf, key.offset}, key.value});
}
for (const auto& key : flat_bound_samplers) {
bound_samplers.emplace(key.offset, key.sampler);
}
for (const auto& key : flat_bindless_samplers) {
bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler});
}
return true;
}
bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const {
if (file.WriteObject(unique_identifier) != 1 || file.WriteObject(static_cast<u32>(type)) != 1 ||
bool ShaderDiskCacheEntry::Save(FileUtil::IOFile& file) const {
if (file.WriteObject(static_cast<u32>(type)) != 1 ||
file.WriteObject(static_cast<u32>(code.size())) != 1 ||
file.WriteObject(static_cast<u32>(code_b.size())) != 1) {
return false;
}
if (file.WriteArray(code.data(), code.size()) != code.size())
if (file.WriteArray(code.data(), code.size()) != code.size()) {
return false;
}
if (HasProgramA() && file.WriteArray(code_b.data(), code_b.size()) != code_b.size()) {
return false;
}
return true;
if (file.WriteObject(unique_identifier) != 1 || file.WriteObject(bound_buffer) != 1 ||
file.WriteObject(static_cast<u8>(texture_handler_size.has_value())) != 1 ||
file.WriteObject(texture_handler_size.value_or(0)) != 1 ||
file.WriteObject(graphics_info) != 1 || file.WriteObject(compute_info) != 1 ||
file.WriteObject(static_cast<u32>(keys.size())) != 1 ||
file.WriteObject(static_cast<u32>(bound_samplers.size())) != 1 ||
file.WriteObject(static_cast<u32>(bindless_samplers.size())) != 1) {
return false;
}
std::vector<ConstBufferKey> flat_keys;
flat_keys.reserve(keys.size());
for (const auto& [address, value] : keys) {
flat_keys.push_back(ConstBufferKey{address.first, address.second, value});
}
std::vector<BoundSamplerKey> flat_bound_samplers;
flat_bound_samplers.reserve(bound_samplers.size());
for (const auto& [address, sampler] : bound_samplers) {
flat_bound_samplers.push_back(BoundSamplerKey{address, sampler});
}
std::vector<BindlessSamplerKey> flat_bindless_samplers;
flat_bindless_samplers.reserve(bindless_samplers.size());
for (const auto& [address, sampler] : bindless_samplers) {
flat_bindless_samplers.push_back(
BindlessSamplerKey{address.first, address.second, sampler});
}
return file.WriteArray(flat_keys.data(), flat_keys.size()) == flat_keys.size() &&
file.WriteArray(flat_bound_samplers.data(), flat_bound_samplers.size()) ==
flat_bound_samplers.size() &&
file.WriteArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) ==
flat_bindless_samplers.size();
}
ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {}
ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
ShaderDiskCacheOpenGL::LoadTransferable() {
std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() {
// Skip games without title id
const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
if (!Settings::values.use_disk_shader_cache || !has_title_id) {
@@ -130,17 +185,14 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
FileUtil::IOFile file(GetTransferablePath(), "rb");
if (!file.IsOpen()) {
LOG_INFO(Render_OpenGL, "No transferable shader cache found for game with title id={}",
GetTitleID());
LOG_INFO(Render_OpenGL, "No transferable shader cache found");
is_usable = true;
return {};
}
u32 version{};
if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) {
LOG_ERROR(Render_OpenGL,
"Failed to get transferable cache version for title id={}, skipping",
GetTitleID());
LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it");
return {};
}
@@ -158,105 +210,42 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
}
// Version is valid, load the shaders
constexpr const char error_loading[] = "Failed to load transferable raw entry, skipping";
std::vector<ShaderDiskCacheRaw> raws;
std::vector<ShaderDiskCacheUsage> usages;
std::vector<ShaderDiskCacheEntry> entries;
while (file.Tell() < file.GetSize()) {
TransferableEntryKind kind{};
if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) {
LOG_ERROR(Render_OpenGL, "Failed to read transferable file, skipping");
return {};
}
switch (kind) {
case TransferableEntryKind::Raw: {
ShaderDiskCacheRaw entry;
if (!entry.Load(file)) {
LOG_ERROR(Render_OpenGL, error_loading);
return {};
}
transferable.insert({entry.GetUniqueIdentifier(), {}});
raws.push_back(std::move(entry));
break;
}
case TransferableEntryKind::Usage: {
ShaderDiskCacheUsage usage;
u32 num_keys{};
u32 num_bound_samplers{};
u32 num_bindless_samplers{};
if (file.ReadArray(&usage.unique_identifier, 1) != 1 ||
file.ReadArray(&usage.variant, 1) != 1 ||
file.ReadArray(&usage.bound_buffer, 1) != 1 || file.ReadArray(&num_keys, 1) != 1 ||
file.ReadArray(&num_bound_samplers, 1) != 1 ||
file.ReadArray(&num_bindless_samplers, 1) != 1) {
LOG_ERROR(Render_OpenGL, error_loading);
return {};
}
std::vector<ConstBufferKey> keys(num_keys);
std::vector<BoundSamplerKey> bound_samplers(num_bound_samplers);
std::vector<BindlessSamplerKey> bindless_samplers(num_bindless_samplers);
if (file.ReadArray(keys.data(), keys.size()) != keys.size() ||
file.ReadArray(bound_samplers.data(), bound_samplers.size()) !=
bound_samplers.size() ||
file.ReadArray(bindless_samplers.data(), bindless_samplers.size()) !=
bindless_samplers.size()) {
LOG_ERROR(Render_OpenGL, error_loading);
return {};
}
for (const auto& key : keys) {
usage.keys.insert({{key.cbuf, key.offset}, key.value});
}
for (const auto& key : bound_samplers) {
usage.bound_samplers.emplace(key.offset, key.sampler);
}
for (const auto& key : bindless_samplers) {
usage.bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler});
}
usages.push_back(std::move(usage));
break;
}
default:
LOG_ERROR(Render_OpenGL, "Unknown transferable shader cache entry kind={}, skipping",
static_cast<u32>(kind));
ShaderDiskCacheEntry& entry = entries.emplace_back();
if (!entry.Load(file)) {
LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping");
return {};
}
}
is_usable = true;
return {{std::move(raws), std::move(usages)}};
return {std::move(entries)};
}
std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>
ShaderDiskCacheOpenGL::LoadPrecompiled() {
std::vector<ShaderDiskCachePrecompiled> ShaderDiskCacheOpenGL::LoadPrecompiled() {
if (!is_usable) {
return {};
}
std::string path = GetPrecompiledPath();
FileUtil::IOFile file(path, "rb");
FileUtil::IOFile file(GetPrecompiledPath(), "rb");
if (!file.IsOpen()) {
LOG_INFO(Render_OpenGL, "No precompiled shader cache found for game with title id={}",
GetTitleID());
LOG_INFO(Render_OpenGL, "No precompiled shader cache found");
return {};
}
const auto result = LoadPrecompiledFile(file);
if (!result) {
LOG_INFO(Render_OpenGL,
"Failed to load precompiled cache for game with title id={}, removing",
GetTitleID());
file.Close();
InvalidatePrecompiled();
return {};
if (const auto result = LoadPrecompiledFile(file)) {
return *result;
}
return *result;
LOG_INFO(Render_OpenGL, "Failed to load precompiled cache");
file.Close();
InvalidatePrecompiled();
return {};
}
std::optional<std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>
ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::LoadPrecompiledFile(
FileUtil::IOFile& file) {
// Read compressed file from disk and decompress to virtual precompiled cache file
std::vector<u8> compressed(file.GetSize());
file.ReadBytes(compressed.data(), compressed.size());
@@ -275,58 +264,22 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
return {};
}
ShaderDumpsMap dumps;
std::vector<ShaderDiskCachePrecompiled> entries;
while (precompiled_cache_virtual_file_offset < precompiled_cache_virtual_file.GetSize()) {
u32 num_keys{};
u32 num_bound_samplers{};
u32 num_bindless_samplers{};
ShaderDiskCacheUsage usage;
if (!LoadObjectFromPrecompiled(usage.unique_identifier) ||
!LoadObjectFromPrecompiled(usage.variant) ||
!LoadObjectFromPrecompiled(usage.bound_buffer) ||
!LoadObjectFromPrecompiled(num_keys) ||
!LoadObjectFromPrecompiled(num_bound_samplers) ||
!LoadObjectFromPrecompiled(num_bindless_samplers)) {
return {};
}
std::vector<ConstBufferKey> keys(num_keys);
std::vector<BoundSamplerKey> bound_samplers(num_bound_samplers);
std::vector<BindlessSamplerKey> bindless_samplers(num_bindless_samplers);
if (!LoadArrayFromPrecompiled(keys.data(), keys.size()) ||
!LoadArrayFromPrecompiled(bound_samplers.data(), bound_samplers.size()) !=
bound_samplers.size() ||
!LoadArrayFromPrecompiled(bindless_samplers.data(), bindless_samplers.size()) !=
bindless_samplers.size()) {
return {};
}
for (const auto& key : keys) {
usage.keys.insert({{key.cbuf, key.offset}, key.value});
}
for (const auto& key : bound_samplers) {
usage.bound_samplers.emplace(key.offset, key.sampler);
}
for (const auto& key : bindless_samplers) {
usage.bindless_samplers.insert({{key.cbuf, key.offset}, key.sampler});
}
ShaderDiskCacheDump dump;
if (!LoadObjectFromPrecompiled(dump.binary_format)) {
u32 binary_size;
auto& entry = entries.emplace_back();
if (!LoadObjectFromPrecompiled(entry.unique_identifier) ||
!LoadObjectFromPrecompiled(entry.binary_format) ||
!LoadObjectFromPrecompiled(binary_size)) {
return {};
}
u32 binary_length{};
if (!LoadObjectFromPrecompiled(binary_length)) {
entry.binary.resize(binary_size);
if (!LoadArrayFromPrecompiled(entry.binary.data(), entry.binary.size())) {
return {};
}
dump.binary.resize(binary_length);
if (!LoadArrayFromPrecompiled(dump.binary.data(), dump.binary.size())) {
return {};
}
dumps.emplace(std::move(usage), dump);
}
return dumps;
return entries;
}
void ShaderDiskCacheOpenGL::InvalidateTransferable() {
@@ -346,13 +299,13 @@ void ShaderDiskCacheOpenGL::InvalidatePrecompiled() {
}
}
void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) {
void ShaderDiskCacheOpenGL::SaveEntry(const ShaderDiskCacheEntry& entry) {
if (!is_usable) {
return;
}
const u64 id = entry.GetUniqueIdentifier();
if (transferable.find(id) != transferable.end()) {
const u64 id = entry.unique_identifier;
if (stored_transferable.find(id) != stored_transferable.end()) {
// The shader already exists
return;
}
@@ -361,71 +314,17 @@ void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) {
if (!file.IsOpen()) {
return;
}
if (file.WriteObject(TransferableEntryKind::Raw) != 1 || !entry.Save(file)) {
if (!entry.Save(file)) {
LOG_ERROR(Render_OpenGL, "Failed to save raw transferable cache entry, removing");
file.Close();
InvalidateTransferable();
return;
}
transferable.insert({id, {}});
stored_transferable.insert(id);
}
void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) {
if (!is_usable) {
return;
}
const auto it = transferable.find(usage.unique_identifier);
ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously");
auto& usages{it->second};
if (usages.find(usage) != usages.end()) {
// Skip this variant since the shader is already stored.
return;
}
usages.insert(usage);
FileUtil::IOFile file = AppendTransferableFile();
if (!file.IsOpen())
return;
const auto Close = [&] {
LOG_ERROR(Render_OpenGL, "Failed to save usage transferable cache entry, removing");
file.Close();
InvalidateTransferable();
};
if (file.WriteObject(TransferableEntryKind::Usage) != 1 ||
file.WriteObject(usage.unique_identifier) != 1 || file.WriteObject(usage.variant) != 1 ||
file.WriteObject(usage.bound_buffer) != 1 ||
file.WriteObject(static_cast<u32>(usage.keys.size())) != 1 ||
file.WriteObject(static_cast<u32>(usage.bound_samplers.size())) != 1 ||
file.WriteObject(static_cast<u32>(usage.bindless_samplers.size())) != 1) {
Close();
return;
}
for (const auto& [pair, value] : usage.keys) {
const auto [cbuf, offset] = pair;
if (file.WriteObject(ConstBufferKey{cbuf, offset, value}) != 1) {
Close();
return;
}
}
for (const auto& [offset, sampler] : usage.bound_samplers) {
if (file.WriteObject(BoundSamplerKey{offset, sampler}) != 1) {
Close();
return;
}
}
for (const auto& [pair, sampler] : usage.bindless_samplers) {
const auto [cbuf, offset] = pair;
if (file.WriteObject(BindlessSamplerKey{cbuf, offset, sampler}) != 1) {
Close();
return;
}
}
}
void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint program) {
void ShaderDiskCacheOpenGL::SavePrecompiled(u64 unique_identifier, GLuint program) {
if (!is_usable) {
return;
}
@@ -437,51 +336,19 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p
SavePrecompiledHeaderToVirtualPrecompiledCache();
}
GLint binary_length{};
GLint binary_length;
glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binary_length);
GLenum binary_format{};
GLenum binary_format;
std::vector<u8> binary(binary_length);
glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data());
const auto Close = [&] {
LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016X}, removing",
usage.unique_identifier);
InvalidatePrecompiled();
};
if (!SaveObjectToPrecompiled(usage.unique_identifier) ||
!SaveObjectToPrecompiled(usage.variant) || !SaveObjectToPrecompiled(usage.bound_buffer) ||
!SaveObjectToPrecompiled(static_cast<u32>(usage.keys.size())) ||
!SaveObjectToPrecompiled(static_cast<u32>(usage.bound_samplers.size())) ||
!SaveObjectToPrecompiled(static_cast<u32>(usage.bindless_samplers.size()))) {
Close();
return;
}
for (const auto& [pair, value] : usage.keys) {
const auto [cbuf, offset] = pair;
if (SaveObjectToPrecompiled(ConstBufferKey{cbuf, offset, value}) != 1) {
Close();
return;
}
}
for (const auto& [offset, sampler] : usage.bound_samplers) {
if (SaveObjectToPrecompiled(BoundSamplerKey{offset, sampler}) != 1) {
Close();
return;
}
}
for (const auto& [pair, sampler] : usage.bindless_samplers) {
const auto [cbuf, offset] = pair;
if (SaveObjectToPrecompiled(BindlessSamplerKey{cbuf, offset, sampler}) != 1) {
Close();
return;
}
}
if (!SaveObjectToPrecompiled(static_cast<u32>(binary_format)) ||
!SaveObjectToPrecompiled(static_cast<u32>(binary_length)) ||
if (!SaveObjectToPrecompiled(unique_identifier) || !SaveObjectToPrecompiled(binary_format) ||
!SaveObjectToPrecompiled(static_cast<u32>(binary.size())) ||
!SaveArrayToPrecompiled(binary.data(), binary.size())) {
Close();
LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016X}, removing",
unique_identifier);
InvalidatePrecompiled();
}
}
@@ -534,7 +401,6 @@ void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() {
if (file.WriteBytes(compressed.data(), compressed.size()) != compressed.size()) {
LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version in path={}",
precompiled_path);
return;
}
}
@@ -19,8 +19,7 @@
#include "common/common_types.h"
#include "core/file_sys/vfs_vector.h"
#include "video_core/engines/shader_type.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/shader/const_buffer_locker.h"
#include "video_core/shader/registry.h"
namespace Core {
class System;
@@ -32,139 +31,39 @@ class IOFile;
namespace OpenGL {
struct ShaderDiskCacheUsage;
struct ShaderDiskCacheDump;
using ProgramCode = std::vector<u64>;
using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>;
/// Describes the different variants a program can be compiled with.
struct ProgramVariant final {
ProgramVariant() = default;
/// Graphics constructor.
explicit constexpr ProgramVariant(GLenum primitive_mode) noexcept
: primitive_mode{primitive_mode} {}
/// Compute constructor.
explicit constexpr ProgramVariant(u32 block_x, u32 block_y, u32 block_z, u32 shared_memory_size,
u32 local_memory_size) noexcept
: block_x{block_x}, block_y{static_cast<u16>(block_y)}, block_z{static_cast<u16>(block_z)},
shared_memory_size{shared_memory_size}, local_memory_size{local_memory_size} {}
// Graphics specific parameters.
GLenum primitive_mode{};
// Compute specific parameters.
u32 block_x{};
u16 block_y{};
u16 block_z{};
u32 shared_memory_size{};
u32 local_memory_size{};
bool operator==(const ProgramVariant& rhs) const noexcept {
return std::tie(primitive_mode, block_x, block_y, block_z, shared_memory_size,
local_memory_size) == std::tie(rhs.primitive_mode, rhs.block_x, rhs.block_y,
rhs.block_z, rhs.shared_memory_size,
rhs.local_memory_size);
}
bool operator!=(const ProgramVariant& rhs) const noexcept {
return !operator==(rhs);
}
};
static_assert(std::is_trivially_copyable_v<ProgramVariant>);
/// Describes how a shader is used.
struct ShaderDiskCacheUsage {
u64 unique_identifier{};
ProgramVariant variant;
u32 bound_buffer{};
VideoCommon::Shader::KeyMap keys;
VideoCommon::Shader::BoundSamplerMap bound_samplers;
VideoCommon::Shader::BindlessSamplerMap bindless_samplers;
bool operator==(const ShaderDiskCacheUsage& rhs) const {
return std::tie(unique_identifier, variant, keys, bound_samplers, bindless_samplers) ==
std::tie(rhs.unique_identifier, rhs.variant, rhs.keys, rhs.bound_samplers,
rhs.bindless_samplers);
}
bool operator!=(const ShaderDiskCacheUsage& rhs) const {
return !operator==(rhs);
}
};
} // namespace OpenGL
namespace std {
template <>
struct hash<OpenGL::ProgramVariant> {
std::size_t operator()(const OpenGL::ProgramVariant& variant) const noexcept {
return (static_cast<std::size_t>(variant.primitive_mode) << 6) ^
static_cast<std::size_t>(variant.block_x) ^
(static_cast<std::size_t>(variant.block_y) << 32) ^
(static_cast<std::size_t>(variant.block_z) << 48) ^
(static_cast<std::size_t>(variant.shared_memory_size) << 16) ^
(static_cast<std::size_t>(variant.local_memory_size) << 36);
}
};
template <>
struct hash<OpenGL::ShaderDiskCacheUsage> {
std::size_t operator()(const OpenGL::ShaderDiskCacheUsage& usage) const noexcept {
return static_cast<std::size_t>(usage.unique_identifier) ^
std::hash<OpenGL::ProgramVariant>{}(usage.variant);
}
};
} // namespace std
namespace OpenGL {
/// Describes a shader how it's used by the guest GPU
class ShaderDiskCacheRaw {
public:
explicit ShaderDiskCacheRaw(u64 unique_identifier, Tegra::Engines::ShaderType type,
ProgramCode code, ProgramCode code_b = {});
ShaderDiskCacheRaw();
~ShaderDiskCacheRaw();
/// Describes a shader and how it's used by the guest GPU
struct ShaderDiskCacheEntry {
ShaderDiskCacheEntry();
~ShaderDiskCacheEntry();
bool Load(FileUtil::IOFile& file);
bool Save(FileUtil::IOFile& file) const;
u64 GetUniqueIdentifier() const {
return unique_identifier;
}
bool HasProgramA() const {
return !code.empty() && !code_b.empty();
}
Tegra::Engines::ShaderType GetType() const {
return type;
}
const ProgramCode& GetCode() const {
return code;
}
const ProgramCode& GetCodeB() const {
return code_b;
}
private:
u64 unique_identifier{};
Tegra::Engines::ShaderType type{};
ProgramCode code;
ProgramCode code_b;
u64 unique_identifier = 0;
std::optional<u32> texture_handler_size;
u32 bound_buffer = 0;
VideoCommon::Shader::GraphicsInfo graphics_info;
VideoCommon::Shader::ComputeInfo compute_info;
VideoCommon::Shader::KeyMap keys;
VideoCommon::Shader::BoundSamplerMap bound_samplers;
VideoCommon::Shader::BindlessSamplerMap bindless_samplers;
};
/// Contains an OpenGL dumped binary program
struct ShaderDiskCacheDump {
GLenum binary_format{};
struct ShaderDiskCachePrecompiled {
u64 unique_identifier = 0;
GLenum binary_format = 0;
std::vector<u8> binary;
};
@@ -174,11 +73,10 @@ public:
~ShaderDiskCacheOpenGL();
/// Loads transferable cache. If file has a old version or on failure, it deletes the file.
std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>>
LoadTransferable();
std::optional<std::vector<ShaderDiskCacheEntry>> LoadTransferable();
/// Loads current game's precompiled cache. Invalidates on failure.
std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> LoadPrecompiled();
std::vector<ShaderDiskCachePrecompiled> LoadPrecompiled();
/// Removes the transferable (and precompiled) cache file.
void InvalidateTransferable();
@@ -187,21 +85,18 @@ public:
void InvalidatePrecompiled();
/// Saves a raw dump to the transferable file. Checks for collisions.
void SaveRaw(const ShaderDiskCacheRaw& entry);
/// Saves shader usage to the transferable file. Does not check for collisions.
void SaveUsage(const ShaderDiskCacheUsage& usage);
void SaveEntry(const ShaderDiskCacheEntry& entry);
/// Saves a dump entry to the precompiled file. Does not check for collisions.
void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program);
void SavePrecompiled(u64 unique_identifier, GLuint program);
/// Serializes virtual precompiled shader cache file to real file
void SaveVirtualPrecompiledFile();
private:
/// Loads the transferable cache. Returns empty on failure.
std::optional<std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>
LoadPrecompiledFile(FileUtil::IOFile& file);
std::optional<std::vector<ShaderDiskCachePrecompiled>> LoadPrecompiledFile(
FileUtil::IOFile& file);
/// Opens current game's transferable file and write it's header if it doesn't exist
FileUtil::IOFile AppendTransferableFile() const;
@@ -270,7 +165,7 @@ private:
std::size_t precompiled_cache_virtual_file_offset = 0;
// Stored transferable shaders
std::unordered_map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable;
std::unordered_set<u64> stored_transferable;
// The cache has been loaded at boot
bool is_usable{};
@@ -1,109 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string>
#include <fmt/format.h>
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/shader_type.h"
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/shader/shader_ir.h"
namespace OpenGL::GLShader {
using Tegra::Engines::Maxwell3D;
using Tegra::Engines::ShaderType;
using VideoCommon::Shader::CompileDepth;
using VideoCommon::Shader::CompilerSettings;
using VideoCommon::Shader::ProgramCode;
using VideoCommon::Shader::ShaderIR;
std::string GenerateVertexShader(const Device& device, const ShaderIR& ir, const ShaderIR* ir_b) {
std::string out = GetCommonDeclarations();
out += fmt::format(R"(
layout (std140, binding = {}) uniform vs_config {{
float y_direction;
}};
)",
EmulationUniformBlockBinding);
out += Decompile(device, ir, ShaderType::Vertex, "vertex");
if (ir_b) {
out += Decompile(device, *ir_b, ShaderType::Vertex, "vertex_b");
}
out += R"(
void main() {
gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);
execute_vertex();
)";
if (ir_b) {
out += " execute_vertex_b();";
}
out += "}\n";
return out;
}
std::string GenerateGeometryShader(const Device& device, const ShaderIR& ir) {
std::string out = GetCommonDeclarations();
out += fmt::format(R"(
layout (std140, binding = {}) uniform gs_config {{
float y_direction;
}};
)",
EmulationUniformBlockBinding);
out += Decompile(device, ir, ShaderType::Geometry, "geometry");
out += R"(
void main() {
execute_geometry();
}
)";
return out;
}
std::string GenerateFragmentShader(const Device& device, const ShaderIR& ir) {
std::string out = GetCommonDeclarations();
out += fmt::format(R"(
layout (location = 0) out vec4 FragColor0;
layout (location = 1) out vec4 FragColor1;
layout (location = 2) out vec4 FragColor2;
layout (location = 3) out vec4 FragColor3;
layout (location = 4) out vec4 FragColor4;
layout (location = 5) out vec4 FragColor5;
layout (location = 6) out vec4 FragColor6;
layout (location = 7) out vec4 FragColor7;
layout (std140, binding = {}) uniform fs_config {{
float y_direction;
}};
)",
EmulationUniformBlockBinding);
out += Decompile(device, ir, ShaderType::Fragment, "fragment");
out += R"(
void main() {
execute_fragment();
}
)";
return out;
}
std::string GenerateComputeShader(const Device& device, const ShaderIR& ir) {
std::string out = GetCommonDeclarations();
out += Decompile(device, ir, ShaderType::Compute, "compute");
out += R"(
void main() {
execute_compute();
}
)";
return out;
}
} // namespace OpenGL::GLShader

Some files were not shown because too many files have changed in this diff Show More